summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk63
-rw-r--r--api/4.xml4
-rw-r--r--api/current.xml9405
-rw-r--r--camera/libcameraservice/Android.mk11
-rw-r--r--camera/libcameraservice/CameraHardwareStub.cpp100
-rw-r--r--camera/libcameraservice/CameraHardwareStub.h54
-rw-r--r--camera/libcameraservice/CameraService.cpp493
-rw-r--r--camera/libcameraservice/CameraService.h28
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java566
-rw-r--r--cmds/app_process/app_main.cpp4
-rw-r--r--cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java41
-rw-r--r--cmds/bootanimation/Android.mk5
-rw-r--r--cmds/bootanimation/BootAnimation.cpp40
-rw-r--r--cmds/bootanimation/BootAnimation.h3
-rw-r--r--cmds/bootanimation/bootanimation_main.cpp7
-rw-r--r--cmds/bugreport/Android.mk14
-rw-r--r--cmds/bugreport/bugreport.c56
-rw-r--r--cmds/dumpstate/Android.mk2
-rw-r--r--cmds/dumpstate/dumpstate.c109
-rw-r--r--cmds/dumpsys/Android.mk4
-rw-r--r--cmds/dumpsys/dumpsys.cpp6
-rw-r--r--cmds/keystore/certtool.h14
-rw-r--r--cmds/keystore/keymgmt.c32
-rw-r--r--cmds/keystore/keymgmt.h3
-rw-r--r--cmds/keystore/netkeystore.c107
-rw-r--r--cmds/keystore/netkeystore.h8
-rw-r--r--cmds/keystore/tests/netkeystore_test.c28
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java3
-rw-r--r--cmds/runtime/Android.mk1
-rw-r--r--cmds/runtime/ServiceManager.cpp4
-rw-r--r--cmds/runtime/ServiceManager.h2
-rw-r--r--cmds/runtime/main_runtime.cpp8
-rw-r--r--cmds/service/Android.mk3
-rw-r--r--cmds/service/service.cpp6
-rw-r--r--cmds/servicemanager/service_manager.c1
-rw-r--r--cmds/stagefright/Android.mk41
-rw-r--r--cmds/stagefright/JPEGSource.cpp234
-rw-r--r--cmds/stagefright/JPEGSource.h59
-rw-r--r--cmds/stagefright/SineSource.cpp101
-rw-r--r--cmds/stagefright/SineSource.h39
-rw-r--r--cmds/stagefright/WaveWriter.h71
-rw-r--r--cmds/stagefright/record.cpp265
-rw-r--r--cmds/stagefright/stagefright.cpp324
-rw-r--r--cmds/surfaceflinger/Android.mk1
-rw-r--r--cmds/surfaceflinger/main_surfaceflinger.cpp6
-rw-r--r--cmds/svc/src/com/android/commands/svc/PowerCommand.java6
-rw-r--r--cmds/system_server/Android.mk1
-rw-r--r--cmds/system_server/library/Android.mk1
-rw-r--r--cmds/system_server/library/system_init.cpp10
-rw-r--r--cmds/system_server/system_main.cpp2
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java6
-rw-r--r--core/java/android/accounts/AbstractAccountAuthenticator.java266
-rw-r--r--core/java/android/accounts/Account.aidl19
-rw-r--r--core/java/android/accounts/Account.java84
-rw-r--r--core/java/android/accounts/AccountAuthenticatorActivity.java102
-rw-r--r--core/java/android/accounts/AccountAuthenticatorCache.java60
-rw-r--r--core/java/android/accounts/AccountAuthenticatorResponse.java82
-rw-r--r--core/java/android/accounts/AccountManager.java856
-rw-r--r--core/java/android/accounts/AccountManagerCallback.java20
-rw-r--r--core/java/android/accounts/AccountManagerFuture.java67
-rw-r--r--core/java/android/accounts/AccountManagerResponse.java74
-rw-r--r--core/java/android/accounts/AccountManagerService.java1622
-rw-r--r--core/java/android/accounts/AccountMonitor.java174
-rw-r--r--core/java/android/accounts/AccountMonitorListener.java29
-rw-r--r--core/java/android/accounts/AccountsServiceConstants.java78
-rw-r--r--core/java/android/accounts/AuthenticatorBindHelper.java252
-rw-r--r--core/java/android/accounts/AuthenticatorDescription.aidl19
-rw-r--r--core/java/android/accounts/AuthenticatorDescription.java72
-rw-r--r--core/java/android/accounts/AuthenticatorException.java32
-rw-r--r--core/java/android/accounts/ChooseAccountActivity.java78
-rw-r--r--core/java/android/accounts/Constants.java59
-rw-r--r--core/java/android/accounts/GrantCredentialsPermissionActivity.java172
-rw-r--r--core/java/android/accounts/IAccountAuthenticator.aidl78
-rw-r--r--core/java/android/accounts/IAccountAuthenticatorResponse.aidl27
-rw-r--r--core/java/android/accounts/IAccountManager.aidl62
-rw-r--r--core/java/android/accounts/IAccountManagerResponse.aidl27
-rw-r--r--core/java/android/accounts/IAccountsService.aidl54
-rw-r--r--core/java/android/accounts/NetworkErrorException.java31
-rw-r--r--core/java/android/accounts/OnAccountsUpdatedListener.java29
-rw-r--r--core/java/android/accounts/OperationCanceledException.java31
-rwxr-xr-xcore/java/android/accounts/package.html5
-rw-r--r--core/java/android/app/Activity.java143
-rw-r--r--core/java/android/app/ActivityManager.java129
-rw-r--r--core/java/android/app/ActivityManagerNative.java172
-rw-r--r--core/java/android/app/ActivityThread.java252
-rw-r--r--core/java/android/app/ApplicationContext.java233
-rw-r--r--core/java/android/app/ApplicationErrorReport.java8
-rw-r--r--core/java/android/app/ApplicationThreadNative.java65
-rw-r--r--core/java/android/app/Dialog.java28
-rw-r--r--core/java/android/app/IActivityManager.java32
-rw-r--r--core/java/android/app/IApplicationThread.java8
-rw-r--r--core/java/android/app/ISearchManager.aidl11
-rw-r--r--core/java/android/app/IWallpaperManager.aidl69
-rw-r--r--core/java/android/app/IWallpaperManagerCallback.aidl31
-rw-r--r--core/java/android/app/IWallpaperService.aidl55
-rw-r--r--core/java/android/app/IWallpaperServiceCallback.aidl31
-rw-r--r--core/java/android/app/Instrumentation.java9
-rw-r--r--core/java/android/app/IntentService.java21
-rw-r--r--core/java/android/app/LauncherActivity.java56
-rw-r--r--core/java/android/app/Notification.java11
-rw-r--r--core/java/android/app/NotificationManager.java3
-rw-r--r--core/java/android/app/PendingIntent.java3
-rw-r--r--core/java/android/app/SearchDialog.java4
-rw-r--r--core/java/android/app/SearchManager.java64
-rw-r--r--core/java/android/app/Service.java159
-rw-r--r--core/java/android/app/WallpaperInfo.aidl19
-rw-r--r--core/java/android/app/WallpaperInfo.java200
-rw-r--r--core/java/android/app/WallpaperManager.java469
-rw-r--r--core/java/android/appwidget/AppWidgetHostView.java60
-rw-r--r--core/java/android/backup/BackupDataInput.java7
-rw-r--r--core/java/android/backup/BackupManager.java2
-rw-r--r--core/java/android/backup/IRestoreSession.aidl5
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java124
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java514
-rw-r--r--core/java/android/bluetooth/BluetoothAudioGateway.java60
-rw-r--r--core/java/android/bluetooth/BluetoothClass.java73
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.aidl19
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java635
-rw-r--r--core/java/android/bluetooth/BluetoothError.java42
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java80
-rw-r--r--core/java/android/bluetooth/BluetoothInputStream.java98
-rw-r--r--core/java/android/bluetooth/BluetoothIntent.java59
-rw-r--r--core/java/android/bluetooth/BluetoothOutputStream.java87
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java257
-rw-r--r--core/java/android/bluetooth/BluetoothServerSocket.java106
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java274
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java69
-rw-r--r--core/java/android/bluetooth/Database.java200
-rw-r--r--core/java/android/bluetooth/HeadsetBase.java33
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl63
-rw-r--r--core/java/android/bluetooth/IBluetoothA2dp.aidl14
-rw-r--r--core/java/android/bluetooth/IBluetoothDevice.aidl78
-rw-r--r--core/java/android/bluetooth/IBluetoothDeviceCallback.aidl25
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadset.aidl12
-rw-r--r--core/java/android/bluetooth/IBluetoothPbap.aidl32
-rw-r--r--core/java/android/bluetooth/RfcommSocket.java674
-rw-r--r--core/java/android/content/AbstractCursorEntityIterator.java120
-rw-r--r--core/java/android/content/AbstractSyncableContentProvider.java403
-rw-r--r--core/java/android/content/AbstractTableMerger.java606
-rw-r--r--core/java/android/content/AbstractThreadedSyncAdapter.java223
-rw-r--r--core/java/android/content/ActiveSyncInfo.java9
-rw-r--r--core/java/android/content/AsyncQueryHandler.java78
-rw-r--r--core/java/android/content/ContentProvider.java106
-rw-r--r--core/java/android/content/ContentProviderClient.java135
-rw-r--r--core/java/android/content/ContentProviderNative.java215
-rw-r--r--core/java/android/content/ContentProviderOperation.java561
-rw-r--r--core/java/android/content/ContentProviderResult.java84
-rw-r--r--core/java/android/content/ContentResolver.java448
-rw-r--r--core/java/android/content/ContentService.java124
-rw-r--r--core/java/android/content/Context.java95
-rw-r--r--core/java/android/content/Entity.aidl20
-rw-r--r--core/java/android/content/Entity.java104
-rw-r--r--core/java/android/content/EntityIterator.java51
-rw-r--r--core/java/android/content/IContentProvider.java14
-rw-r--r--core/java/android/content/IContentService.aidl38
-rw-r--r--core/java/android/content/IEntityIterator.java210
-rw-r--r--core/java/android/content/ISyncAdapter.aidl8
-rw-r--r--core/java/android/content/Intent.java174
-rw-r--r--core/java/android/content/OperationApplicationException.java54
-rw-r--r--core/java/android/content/SyncAdapter.java15
-rw-r--r--core/java/android/content/SyncAdapterType.aidl20
-rw-r--r--core/java/android/content/SyncAdapterType.java145
-rw-r--r--core/java/android/content/SyncAdaptersCache.java60
-rw-r--r--core/java/android/content/SyncManager.java680
-rw-r--r--core/java/android/content/SyncResult.java21
-rw-r--r--core/java/android/content/SyncStateContentProviderHelper.java43
-rw-r--r--core/java/android/content/SyncStats.java21
-rw-r--r--core/java/android/content/SyncStatusObserver.java21
-rw-r--r--core/java/android/content/SyncStorageEngine.java514
-rw-r--r--core/java/android/content/SyncableContentProvider.java29
-rw-r--r--core/java/android/content/TempProviderSyncAdapter.java63
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java27
-rwxr-xr-xcore/java/android/content/pm/ConfigurationInfo.java16
-rwxr-xr-xcore/java/android/content/pm/FeatureInfo.aidl19
-rw-r--r--core/java/android/content/pm/FeatureInfo.java101
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl9
-rw-r--r--core/java/android/content/pm/LabeledIntent.java177
-rw-r--r--core/java/android/content/pm/PackageInfo.java7
-rw-r--r--core/java/android/content/pm/PackageManager.java55
-rw-r--r--core/java/android/content/pm/PackageParser.java95
-rw-r--r--core/java/android/content/pm/ProviderInfo.java7
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java242
-rw-r--r--core/java/android/content/pm/ResolveInfo.java39
-rw-r--r--core/java/android/content/pm/ServiceInfo.java6
-rw-r--r--core/java/android/content/res/Configuration.java7
-rw-r--r--core/java/android/content/res/Resources.java4
-rw-r--r--core/java/android/content/res/StringBlock.java34
-rw-r--r--core/java/android/database/AbstractWindowedCursor.java42
-rw-r--r--core/java/android/database/CursorWindow.java51
-rw-r--r--core/java/android/database/DatabaseUtils.java47
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java206
-rw-r--r--core/java/android/database/sqlite/SQLiteDebug.java13
-rw-r--r--core/java/android/database/sqlite/SQLiteQueryBuilder.java15
-rw-r--r--core/java/android/database/sqlite/SQLiteTransactionListener.java21
-rw-r--r--core/java/android/hardware/Camera.java821
-rw-r--r--core/java/android/hardware/SensorListener.java3
-rw-r--r--core/java/android/hardware/SensorManager.java40
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java5
-rwxr-xr-xcore/java/android/inputmethodservice/Keyboard.java2
-rwxr-xr-xcore/java/android/inputmethodservice/KeyboardView.java96
-rw-r--r--core/java/android/net/ConnectivityManager.java59
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/MobileDataStateTracker.java403
-rw-r--r--core/java/android/net/NetworkStateTracker.java73
-rw-r--r--core/java/android/net/NetworkUtils.java3
-rw-r--r--core/java/android/net/Uri.java2
-rw-r--r--core/java/android/net/WebAddress.java2
-rw-r--r--core/java/android/net/http/ConnectionThread.java29
-rw-r--r--core/java/android/net/http/Request.java30
-rw-r--r--core/java/android/net/http/RequestHandle.java2
-rw-r--r--core/java/android/net/http/RequestQueue.java30
-rw-r--r--core/java/android/os/Build.java14
-rw-r--r--core/java/android/os/Debug.java72
-rw-r--r--core/java/android/os/Exec.java63
-rw-r--r--core/java/android/os/FileObserver.java39
-rw-r--r--core/java/android/os/HandlerStateMachine.java8
-rwxr-xr-xcore/java/android/os/IHardwareService.aidl4
-rw-r--r--core/java/android/os/IPowerManager.aidl1
-rw-r--r--core/java/android/os/LatencyTimer.java94
-rw-r--r--core/java/android/os/MemoryFile.java17
-rw-r--r--core/java/android/os/PowerManager.java43
-rw-r--r--core/java/android/os/Process.java8
-rw-r--r--core/java/android/os/RemoteCallbackList.java57
-rw-r--r--core/java/android/os/SystemClock.java8
-rw-r--r--core/java/android/os/SystemProperties.java40
-rw-r--r--core/java/android/os/Vibrator.java7
-rw-r--r--core/java/android/pim/ContactsAsyncHelper.java136
-rw-r--r--core/java/android/pim/RecurrenceSet.java67
-rw-r--r--core/java/android/pim/vcard/Constants.java94
-rw-r--r--core/java/android/pim/vcard/ContactStruct.java1218
-rw-r--r--core/java/android/pim/vcard/EntryCommitter.java48
-rw-r--r--core/java/android/pim/vcard/EntryHandler.java38
-rw-r--r--core/java/android/pim/vcard/VCardBuilder.java64
-rw-r--r--core/java/android/pim/vcard/VCardBuilderCollection.java99
-rw-r--r--core/java/android/pim/vcard/VCardComposer.java1445
-rw-r--r--core/java/android/pim/vcard/VCardConfig.java283
-rw-r--r--core/java/android/pim/vcard/VCardDataBuilder.java320
-rw-r--r--core/java/android/pim/vcard/VCardEntryCounter.java60
-rw-r--r--core/java/android/pim/vcard/VCardParser.java90
-rw-r--r--core/java/android/pim/vcard/VCardParser_V21.java928
-rw-r--r--core/java/android/pim/vcard/VCardParser_V30.java313
-rw-r--r--core/java/android/pim/vcard/VCardSourceDetector.java137
-rw-r--r--core/java/android/pim/vcard/VCardUtils.java764
-rw-r--r--core/java/android/pim/vcard/exception/VCardException.java35
-rw-r--r--core/java/android/pim/vcard/exception/VCardNestedException.java29
-rw-r--r--core/java/android/pim/vcard/exception/VCardNotSupportedException.java33
-rw-r--r--core/java/android/pim/vcard/exception/VCardVersionException.java29
-rw-r--r--core/java/android/pim/vcard/exception/package.html5
-rw-r--r--core/java/android/pim/vcard/package.html5
-rw-r--r--core/java/android/preference/CheckBoxPreference.java14
-rw-r--r--core/java/android/preference/Preference.java6
-rw-r--r--core/java/android/preference/PreferenceManager.java8
-rw-r--r--core/java/android/preference/RingtonePreference.java5
-rw-r--r--core/java/android/preference/VolumePreference.java173
-rw-r--r--core/java/android/provider/Browser.java19
-rw-r--r--core/java/android/provider/Calendar.java74
-rw-r--r--core/java/android/provider/CallLog.java2
-rw-r--r--core/java/android/provider/Checkin.java2
-rw-r--r--core/java/android/provider/Contacts.java455
-rw-r--r--core/java/android/provider/ContactsContract.java2126
-rw-r--r--core/java/android/provider/Downloads.java46
-rw-r--r--core/java/android/provider/DrmStore.java66
-rw-r--r--core/java/android/provider/Gmail.java20
-rw-r--r--core/java/android/provider/Im.java481
-rw-r--r--core/java/android/provider/LiveFolders.java2
-rw-r--r--core/java/android/provider/MediaStore.java6
-rw-r--r--core/java/android/provider/Settings.java398
-rw-r--r--core/java/android/provider/SocialContract.java187
-rw-r--r--core/java/android/provider/SubscribedFeeds.java31
-rw-r--r--core/java/android/provider/SyncConstValue.java11
-rw-r--r--core/java/android/provider/SyncStateContract.java176
-rw-r--r--core/java/android/provider/Telephony.java70
-rw-r--r--core/java/android/server/BluetoothA2dpService.java537
-rw-r--r--core/java/android/server/BluetoothDeviceService.java1263
-rw-r--r--core/java/android/server/BluetoothEventLoop.java377
-rw-r--r--core/java/android/server/BluetoothService.java1269
-rw-r--r--core/java/android/server/search/SearchDialogWrapper.java17
-rw-r--r--core/java/android/server/search/SearchManagerService.java26
-rw-r--r--core/java/android/service/wallpaper/IWallpaperConnection.aidl28
-rw-r--r--core/java/android/service/wallpaper/IWallpaperEngine.aidl26
-rw-r--r--core/java/android/service/wallpaper/IWallpaperService.aidl28
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java740
-rw-r--r--core/java/android/speech/IRecognitionListener.aidl60
-rw-r--r--core/java/android/speech/IRecognitionService.aidl37
-rw-r--r--core/java/android/speech/RecognitionResult.aidl19
-rw-r--r--core/java/android/speech/RecognitionResult.java176
-rw-r--r--core/java/android/speech/RecognitionServiceUtil.java101
-rw-r--r--core/java/android/syncml/pim/PropertyNode.java1
-rw-r--r--core/java/android/syncml/pim/VBuilder.java1
-rw-r--r--core/java/android/syncml/pim/VBuilderCollection.java1
-rw-r--r--core/java/android/syncml/pim/VDataBuilder.java1
-rw-r--r--core/java/android/syncml/pim/VNode.java1
-rw-r--r--core/java/android/syncml/pim/VParser.java1
-rw-r--r--core/java/android/syncml/pim/vcalendar/CalendarStruct.java56
-rw-r--r--core/java/android/syncml/pim/vcalendar/VCalComposer.java189
-rw-r--r--core/java/android/syncml/pim/vcalendar/VCalException.java41
-rw-r--r--core/java/android/syncml/pim/vcalendar/VCalParser.java116
-rw-r--r--core/java/android/syncml/pim/vcalendar/VCalParser_V10.java1628
-rw-r--r--core/java/android/syncml/pim/vcalendar/VCalParser_V20.java186
-rw-r--r--core/java/android/syncml/pim/vcalendar/package.html6
-rw-r--r--core/java/android/syncml/pim/vcard/ContactStruct.java60
-rw-r--r--core/java/android/syncml/pim/vcard/VCardComposer.java7
-rw-r--r--core/java/android/syncml/pim/vcard/VCardDataBuilder.java9
-rw-r--r--core/java/android/syncml/pim/vcard/VCardEntryCounter.java4
-rw-r--r--core/java/android/syncml/pim/vcard/VCardException.java4
-rw-r--r--core/java/android/syncml/pim/vcard/VCardNestedException.java3
-rw-r--r--core/java/android/syncml/pim/vcard/VCardParser.java5
-rw-r--r--core/java/android/syncml/pim/vcard/VCardParser_V21.java3
-rw-r--r--core/java/android/syncml/pim/vcard/VCardParser_V30.java3
-rw-r--r--core/java/android/syncml/pim/vcard/VCardSourceDetector.java4
-rw-r--r--core/java/android/syncml/pim/vcard/VCardVersionException.java5
-rw-r--r--core/java/android/test/AndroidTestCase.java26
-rw-r--r--core/java/android/test/InstrumentationTestCase.java16
-rw-r--r--core/java/android/test/InstrumentationTestSuite.java2
-rw-r--r--core/java/android/text/BoringLayout.java9
-rw-r--r--core/java/android/text/StaticLayout.java8
-rw-r--r--core/java/android/text/TextPaint.java2
-rw-r--r--core/java/android/text/format/DateUtils.java58
-rw-r--r--core/java/android/text/method/ArrowKeyMovementMethod.java81
-rw-r--r--core/java/android/text/method/CharacterPickerDialog.java30
-rw-r--r--core/java/android/text/method/QwertyKeyListener.java2
-rw-r--r--core/java/android/text/style/AbsoluteSizeSpan.java32
-rw-r--r--core/java/android/text/style/ImageSpan.java2
-rw-r--r--core/java/android/text/style/LineHeightSpan.java7
-rw-r--r--core/java/android/util/Config.java8
-rw-r--r--core/java/android/util/EventLog.java25
-rw-r--r--core/java/android/util/MathUtils.java176
-rw-r--r--core/java/android/util/Pair.java76
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java9
-rw-r--r--core/java/android/view/IWindow.aidl9
-rw-r--r--core/java/android/view/IWindowManager.aidl1
-rw-r--r--core/java/android/view/IWindowSession.aidl6
-rw-r--r--core/java/android/view/KeyEvent.java27
-rw-r--r--core/java/android/view/MotionEvent.java864
-rw-r--r--core/java/android/view/RawInputEvent.java18
-rw-r--r--core/java/android/view/Surface.java33
-rw-r--r--core/java/android/view/SurfaceHolder.java18
-rw-r--r--core/java/android/view/SurfaceView.java38
-rw-r--r--core/java/android/view/View.java100
-rw-r--r--core/java/android/view/ViewRoot.java116
-rw-r--r--core/java/android/view/ViewStub.java18
-rw-r--r--core/java/android/view/VolumePanel.java31
-rw-r--r--core/java/android/view/Window.java25
-rw-r--r--core/java/android/view/WindowManager.java31
-rw-r--r--core/java/android/view/WindowManagerPolicy.java26
-rw-r--r--core/java/android/view/animation/Animation.java2
-rw-r--r--core/java/android/view/inputmethod/InputMethodInfo.java4
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java11
-rw-r--r--core/java/android/webkit/BrowserFrame.java91
-rw-r--r--core/java/android/webkit/CacheLoader.java16
-rw-r--r--core/java/android/webkit/CacheManager.java80
-rw-r--r--core/java/android/webkit/CallbackProxy.java330
-rw-r--r--core/java/android/webkit/ContentLoader.java31
-rw-r--r--core/java/android/webkit/CookieManager.java75
-rw-r--r--core/java/android/webkit/CookieSyncManager.java47
-rw-r--r--core/java/android/webkit/DataLoader.java29
-rw-r--r--core/java/android/webkit/DateSorter.java3
-rw-r--r--core/java/android/webkit/DebugFlags.java49
-rw-r--r--core/java/android/webkit/FileLoader.java18
-rw-r--r--core/java/android/webkit/FrameLoader.java76
-rw-r--r--core/java/android/webkit/GearsPermissionsManager.java246
-rwxr-xr-xcore/java/android/webkit/GeolocationPermissions.java259
-rwxr-xr-xcore/java/android/webkit/GeolocationService.java194
-rw-r--r--core/java/android/webkit/GoogleLocationSettingManager.java156
-rw-r--r--core/java/android/webkit/HTML5VideoViewProxy.java232
-rw-r--r--core/java/android/webkit/HttpAuthHandler.java4
-rw-r--r--core/java/android/webkit/HttpDateTime.java42
-rw-r--r--core/java/android/webkit/JWebCoreJavaBridge.java67
-rw-r--r--core/java/android/webkit/LoadListener.java144
-rw-r--r--core/java/android/webkit/MimeTypeMap.java630
-rw-r--r--core/java/android/webkit/MockGeolocation.java59
-rw-r--r--core/java/android/webkit/Network.java30
-rw-r--r--core/java/android/webkit/Plugin.java75
-rw-r--r--core/java/android/webkit/PluginActivity.java67
-rw-r--r--core/java/android/webkit/PluginContentLoader.java96
-rw-r--r--core/java/android/webkit/PluginData.java27
-rw-r--r--core/java/android/webkit/PluginList.java28
-rw-r--r--core/java/android/webkit/PluginManager.java157
-rw-r--r--core/java/android/webkit/PluginStub.java50
-rw-r--r--core/java/android/webkit/PluginUtil.java62
-rw-r--r--core/java/android/webkit/SslErrorHandler.java86
-rw-r--r--core/java/android/webkit/StreamLoader.java9
-rw-r--r--core/java/android/webkit/TextDialog.java593
-rw-r--r--core/java/android/webkit/URLUtil.java29
-rw-r--r--core/java/android/webkit/UrlInterceptHandler.java12
-rw-r--r--core/java/android/webkit/UrlInterceptRegistry.java30
-rw-r--r--core/java/android/webkit/ViewManager.java157
-rw-r--r--core/java/android/webkit/WebBackForwardList.java2
-rw-r--r--core/java/android/webkit/WebChromeClient.java101
-rw-r--r--core/java/android/webkit/WebHistoryItem.java18
-rw-r--r--core/java/android/webkit/WebIconDatabase.java2
-rw-r--r--core/java/android/webkit/WebSettings.java307
-rw-r--r--core/java/android/webkit/WebStorage.java311
-rw-r--r--core/java/android/webkit/WebSyncManager.java10
-rw-r--r--core/java/android/webkit/WebTextView.java789
-rw-r--r--core/java/android/webkit/WebView.java2617
-rw-r--r--core/java/android/webkit/WebViewClient.java44
-rw-r--r--core/java/android/webkit/WebViewCore.java1059
-rw-r--r--core/java/android/webkit/WebViewDatabase.java38
-rw-r--r--core/java/android/webkit/gears/AndroidGpsLocationProvider.java156
-rw-r--r--core/java/android/webkit/gears/AndroidRadioDataProvider.java260
-rw-r--r--core/java/android/webkit/gears/AndroidWifiDataProvider.java140
-rw-r--r--core/java/android/webkit/gears/ApacheHttpRequestAndroid.java1134
-rw-r--r--core/java/android/webkit/gears/DesktopAndroid.java113
-rw-r--r--core/java/android/webkit/gears/NativeDialog.java142
-rw-r--r--core/java/android/webkit/gears/PluginSettings.java79
-rw-r--r--core/java/android/webkit/gears/UrlInterceptHandlerGears.java417
-rw-r--r--core/java/android/webkit/gears/VersionExtractor.java147
-rw-r--r--core/java/android/webkit/gears/ZipInflater.java200
-rw-r--r--core/java/android/webkit/gears/package.html3
-rw-r--r--core/java/android/widget/AbsListView.java63
-rw-r--r--core/java/android/widget/AbsSeekBar.java21
-rw-r--r--core/java/android/widget/AdapterView.java2
-rw-r--r--core/java/android/widget/AlphabetIndexer.java41
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java76
-rw-r--r--core/java/android/widget/ExpandableListView.java10
-rw-r--r--core/java/android/widget/FastScroller.java14
-rw-r--r--core/java/android/widget/LinearLayout.java2
-rw-r--r--core/java/android/widget/ListView.java57
-rw-r--r--core/java/android/widget/MediaController.java47
-rw-r--r--core/java/android/widget/PopupWindow.java1
-rw-r--r--core/java/android/widget/ProgressBar.java1
-rw-r--r--core/java/android/widget/Scroller.java55
-rw-r--r--core/java/android/widget/SimpleCursorTreeAdapter.java114
-rw-r--r--core/java/android/widget/TabHost.java29
-rw-r--r--core/java/android/widget/TabWidget.java91
-rw-r--r--core/java/android/widget/TextView.java14
-rw-r--r--core/java/android/widget/VideoView.java199
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java27
-rwxr-xr-xcore/java/com/android/internal/app/NetInitiatedActivity.java139
-rw-r--r--core/java/com/android/internal/app/ResolverActivity.java67
-rw-r--r--core/java/com/android/internal/app/ShutdownThread.java12
-rw-r--r--core/java/com/android/internal/backup/SystemBackupAgent.java35
-rw-r--r--core/java/com/android/internal/content/SyncStateContentProviderHelper.java122
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java5
-rw-r--r--core/java/com/android/internal/os/HandlerCaller.java20
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java26
-rw-r--r--core/java/com/android/internal/os/SamplingProfilerIntegration.java144
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java91
-rw-r--r--core/java/com/android/internal/service/wallpaper/ImageWallpaper.java148
-rw-r--r--core/java/com/android/internal/view/BaseIWindow.java95
-rw-r--r--core/java/com/android/internal/view/BaseSurfaceHolder.java174
-rw-r--r--core/java/com/android/internal/widget/ContactHeaderWidget.java493
-rw-r--r--core/java/com/android/internal/widget/DialogTitle.java9
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java10
-rw-r--r--core/java/com/android/internal/widget/NumberPicker.java86
-rw-r--r--core/java/com/google/android/gdata/client/QueryParamsImpl.java4
-rw-r--r--core/java/com/google/android/gdata2/client/AndroidGDataClient.java590
-rw-r--r--core/java/com/google/android/gdata2/client/AndroidXmlParserFactory.java31
-rw-r--r--core/java/com/google/android/gdata2/client/QueryParamsImpl.java99
-rw-r--r--core/java/com/google/android/mms/pdu/EncodedStringValue.java12
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java102
-rw-r--r--core/java/com/google/android/net/GoogleHttpClient.java6
-rw-r--r--core/java/com/google/android/net/UrlRules.java11
-rw-r--r--core/jni/.android_server_BluetoothEventLoop.cpp.swpbin0 -> 16384 bytes
-rw-r--r--core/jni/ActivityManager.cpp6
-rw-r--r--core/jni/Android.mk16
-rw-r--r--core/jni/AndroidRuntime.cpp226
-rw-r--r--core/jni/CursorWindow.cpp2
-rw-r--r--core/jni/CursorWindow.h2
-rw-r--r--core/jni/android/graphics/Bitmap.cpp2
-rw-r--r--core/jni/android/graphics/Canvas.cpp3
-rw-r--r--core/jni/android/graphics/ColorFilter.cpp6
-rw-r--r--core/jni/android/graphics/Paint.cpp13
-rw-r--r--core/jni/android/graphics/Path.cpp5
-rw-r--r--core/jni/android/graphics/Region.cpp2
-rw-r--r--core/jni/android/graphics/Typeface.cpp22
-rw-r--r--core/jni/android/opengl/util.cpp141
-rw-r--r--core/jni/android_bluetooth_BluetoothSocket.cpp534
-rw-r--r--core/jni/android_bluetooth_Database.cpp184
-rw-r--r--core/jni/android_bluetooth_RfcommSocket.cpp621
-rw-r--r--core/jni/android_bluetooth_common.cpp332
-rw-r--r--core/jni/android_bluetooth_common.h19
-rw-r--r--core/jni/android_database_CursorWindow.cpp65
-rw-r--r--core/jni/android_database_SQLiteDatabase.cpp5
-rw-r--r--core/jni/android_emoji_EmojiFactory.cpp3
-rw-r--r--core/jni/android_hardware_Camera.cpp2
-rwxr-xr-xcore/jni/android_location_GpsLocationProvider.cpp161
-rw-r--r--core/jni/android_media_AudioRecord.cpp26
-rw-r--r--core/jni/android_media_AudioSystem.cpp166
-rw-r--r--core/jni/android_media_AudioTrack.cpp49
-rw-r--r--core/jni/android_media_ToneGenerator.cpp6
-rw-r--r--core/jni/android_net_NetUtils.cpp12
-rw-r--r--core/jni/android_net_wifi_Wifi.cpp24
-rw-r--r--core/jni/android_os_Debug.cpp12
-rw-r--r--core/jni/android_os_Exec.cpp215
-rw-r--r--core/jni/android_os_MemoryFile.cpp12
-rw-r--r--core/jni/android_os_SystemProperties.cpp109
-rw-r--r--core/jni/android_server_BluetoothA2dpService.cpp318
-rw-r--r--core/jni/android_server_BluetoothDeviceService.cpp1035
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp736
-rw-r--r--core/jni/android_server_BluetoothService.cpp807
-rw-r--r--core/jni/android_util_Binder.cpp10
-rw-r--r--core/jni/android_util_Binder.h2
-rw-r--r--core/jni/android_util_EventLog.cpp92
-rw-r--r--core/jni/android_util_Process.cpp81
-rw-r--r--core/jni/android_view_Surface.cpp307
-rw-r--r--core/jni/com_google_android_gles_jni_EGLImpl.cpp3
-rw-r--r--core/res/AndroidManifest.xml122
-rw-r--r--core/res/res/anim/accelerate_decelerate_interpolator.xml2
-rw-r--r--core/res/res/anim/accelerate_interpolator.xml2
-rw-r--r--core/res/res/anim/activity_close_enter.xml25
-rw-r--r--core/res/res/anim/activity_close_exit.xml24
-rw-r--r--core/res/res/anim/activity_open_enter.xml24
-rw-r--r--core/res/res/anim/activity_open_exit.xml25
-rw-r--r--core/res/res/anim/anticipate_interpolator.xml2
-rw-r--r--core/res/res/anim/anticipate_overshoot_interpolator.xml2
-rw-r--r--core/res/res/anim/bounce_interpolator.xml2
-rw-r--r--core/res/res/anim/decelerate_interpolator.xml2
-rw-r--r--core/res/res/anim/linear_interpolator.xml2
-rw-r--r--core/res/res/anim/overshoot_interpolator.xml2
-rw-r--r--core/res/res/anim/task_close_enter.xml14
-rw-r--r--core/res/res/anim/task_close_exit.xml16
-rw-r--r--core/res/res/anim/task_open_enter.xml16
-rw-r--r--core/res/res/anim/task_open_exit.xml11
-rw-r--r--core/res/res/anim/translucent_enter.xml26
-rw-r--r--core/res/res/anim/translucent_exit.xml26
-rw-r--r--core/res/res/anim/wallpaper_close_enter.xml24
-rw-r--r--core/res/res/anim/wallpaper_close_exit.xml29
-rw-r--r--core/res/res/anim/wallpaper_enter.xml28
-rw-r--r--core/res/res/anim/wallpaper_exit.xml28
-rw-r--r--core/res/res/anim/wallpaper_intra_close_enter.xml31
-rw-r--r--core/res/res/anim/wallpaper_intra_close_exit.xml30
-rw-r--r--core/res/res/anim/wallpaper_intra_open_enter.xml30
-rw-r--r--core/res/res/anim/wallpaper_intra_open_exit.xml31
-rw-r--r--core/res/res/anim/wallpaper_open_enter.xml29
-rw-r--r--core/res/res/anim/wallpaper_open_exit.xml24
-rw-r--r--core/res/res/color/tab_indicator_text.xml4
-rw-r--r--core/res/res/drawable-hdpi/activity_title_bar.9.pngbin0 -> 1697 bytes
-rw-r--r--core/res/res/drawable-hdpi/arrow_down_float.pngbin0 -> 618 bytes
-rw-r--r--core/res/res/drawable-hdpi/arrow_up_float.pngbin0 -> 616 bytes
-rw-r--r--core/res/res/drawable-hdpi/battery_charge_background.pngbin0 -> 4925 bytes
-rw-r--r--core/res/res/drawable-hdpi/battery_charge_fill_empty.9.pngbin0 -> 783 bytes
-rw-r--r--core/res/res/drawable-hdpi/battery_charge_fill_full.9.pngbin0 -> 756 bytes
-rw-r--r--core/res/res/drawable-hdpi/battery_charge_fill_warning.9.pngbin0 -> 819 bytes
-rw-r--r--core/res/res/drawable-hdpi/battery_low_battery.pngbin0 -> 4759 bytes
-rw-r--r--core/res/res/drawable-hdpi/blank_tile.pngbin0 -> 685 bytes
-rw-r--r--core/res/res/drawable-hdpi/bottom_bar.pngbin0 -> 389 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_buttonless_off.pngbin0 -> 895 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_buttonless_on.pngbin0 -> 1027 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_label_background.9.pngbin0 -> 224 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off.pngbin0 -> 1693 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable.pngbin0 -> 1170 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_disable_focused.pngbin0 -> 1568 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_pressed.pngbin0 -> 2413 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_off_selected.pngbin0 -> 2378 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on.pngbin0 -> 2115 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable.pngbin0 -> 1417 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_disable_focused.pngbin0 -> 1788 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_pressed.pngbin0 -> 2586 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_check_on_selected.pngbin0 -> 2546 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_circle_disable.pngbin0 -> 1447 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_circle_disable_focused.pngbin0 -> 2559 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_circle_normal.pngbin0 -> 1960 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_circle_pressed.pngbin0 -> 2634 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_circle_selected.pngbin0 -> 2654 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_close_normal.pngbin0 -> 1884 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_close_pressed.pngbin0 -> 2737 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_close_selected.pngbin0 -> 2807 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_code_lock_default.pngbin0 -> 2269 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_code_lock_touched.pngbin0 -> 3215 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_normal.9.pngbin0 -> 962 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_normal_disable.9.pngbin0 -> 567 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.pngbin0 -> 892 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_pressed.9.pngbin0 -> 1469 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_selected.9.pngbin0 -> 1512 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_small_normal.9.pngbin0 -> 894 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.pngbin0 -> 541 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.pngbin0 -> 830 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_small_pressed.9.pngbin0 -> 1301 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_default_small_selected.9.pngbin0 -> 1323 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dialog_disable.pngbin0 -> 1859 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dialog_normal.pngbin0 -> 1891 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dialog_pressed.pngbin0 -> 2425 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dialog_selected.pngbin0 -> 2577 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dropdown_normal.9.pngbin0 -> 1840 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dropdown_pressed.9.pngbin0 -> 3199 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_dropdown_selected.9.pngbin0 -> 3231 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_erase_default.9.pngbin0 -> 690 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_erase_pressed.9.pngbin0 -> 726 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_erase_selected.9.pngbin0 -> 747 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_global_search_normal.9.pngbin0 -> 1060 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.pngbin0 -> 674 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.pngbin0 -> 968 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.pngbin0 -> 1039 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.pngbin0 -> 638 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.pngbin0 -> 911 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.pngbin0 -> 979 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal.9.pngbin0 -> 1200 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed.9.pngbin0 -> 1706 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_keyboard_key_trans_selected.9.pngbin0 -> 1730 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_media_player.9.pngbin0 -> 940 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_media_player_disabled.9.pngbin0 -> 941 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.pngbin0 -> 1085 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_media_player_pressed.9.pngbin0 -> 861 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_media_player_selected.9.pngbin0 -> 1333 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_minus_default.pngbin0 -> 8289 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_minus_disable.pngbin0 -> 8595 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_minus_disable_focused.pngbin0 -> 6341 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_minus_pressed.pngbin0 -> 6387 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_minus_selected.pngbin0 -> 9023 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_plus_default.pngbin0 -> 8495 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_plus_disable.pngbin0 -> 8831 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_plus_disable_focused.pngbin0 -> 6519 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_plus_pressed.pngbin0 -> 9267 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_plus_selected.pngbin0 -> 9249 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_label_background.9.pngbin0 -> 216 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off.pngbin0 -> 2491 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_pressed.pngbin0 -> 3304 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_off_selected.pngbin0 -> 3267 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on.pngbin0 -> 2777 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_pressed.pngbin0 -> 3454 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_radio_on_selected.pngbin0 -> 3441 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_off_normal.pngbin0 -> 4372 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_off_pressed.pngbin0 -> 6013 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_off_selected.pngbin0 -> 5951 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_on_normal.pngbin0 -> 4988 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_on_pressed.pngbin0 -> 6199 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_rating_star_on_selected.pngbin0 -> 6111 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_default.9.pngbin0 -> 648 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.pngbin0 -> 1347 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_selected.9.pngbin0 -> 1367 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.pngbin0 -> 878 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.pngbin0 -> 1537 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.pngbin0 -> 1590 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_square_overlay_disabled.pngbin0 -> 822 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_square_overlay_disabled_focused.pngbin0 -> 1115 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_square_overlay_normal.pngbin0 -> 858 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_square_overlay_pressed.pngbin0 -> 955 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_square_overlay_selected.pngbin0 -> 971 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_off.pngbin0 -> 2208 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_off_disable.pngbin0 -> 2024 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_off_disable_focused.pngbin0 -> 2602 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_off_pressed.pngbin0 -> 2674 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_off_selected.pngbin0 -> 2447 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_on.pngbin0 -> 2615 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_on_disable.pngbin0 -> 2081 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_on_disable_focused.pngbin0 -> 2360 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_on_pressed.pngbin0 -> 2721 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_big_on_selected.pngbin0 -> 2427 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_star_label_background.9.pngbin0 -> 185 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_off.9.pngbin0 -> 584 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_toggle_on.9.pngbin0 -> 602 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_down_disabled.9.pngbin0 -> 2229 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_down_disabled_focused.9.pngbin0 -> 2669 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_down_normal.9.pngbin0 -> 3215 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_down_pressed.9.pngbin0 -> 4541 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_down_selected.9.pngbin0 -> 4549 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_page_normal.pngbin0 -> 3207 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_page_press.pngbin0 -> 3484 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_up_disabled.9.pngbin0 -> 2327 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_up_disabled_focused.9.pngbin0 -> 2712 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_up_normal.9.pngbin0 -> 3147 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_up_pressed.9.pngbin0 -> 3980 bytes
-rw-r--r--core/res/res/drawable-hdpi/btn_zoom_up_selected.9.pngbin0 -> 3963 bytes
-rw-r--r--core/res/res/drawable-hdpi/button_onoff_indicator_off.pngbin0 -> 431 bytes
-rw-r--r--core/res/res/drawable-hdpi/button_onoff_indicator_on.pngbin0 -> 431 bytes
-rw-r--r--core/res/res/drawable-hdpi/call_contact.pngbin0 -> 1877 bytes
-rw-r--r--core/res/res/drawable-hdpi/checkbox_off_background.pngbin0 -> 803 bytes
-rw-r--r--core/res/res/drawable-hdpi/checkbox_on_background.pngbin0 -> 1212 bytes
-rw-r--r--core/res/res/drawable-hdpi/clock_dial.pngbin0 -> 10519 bytes
-rw-r--r--core/res/res/drawable-hdpi/clock_hand_hour.pngbin0 -> 2196 bytes
-rw-r--r--core/res/res/drawable-hdpi/clock_hand_minute.pngbin0 -> 2518 bytes
-rw-r--r--core/res/res/drawable-hdpi/code_lock_bottom.9.pngbin0 -> 222 bytes
-rw-r--r--core/res/res/drawable-hdpi/code_lock_left.9.pngbin0 -> 168 bytes
-rw-r--r--core/res/res/drawable-hdpi/code_lock_top.9.pngbin0 -> 165 bytes
-rw-r--r--core/res/res/drawable-hdpi/compass_arrow.pngbin0 -> 1033 bytes
-rw-r--r--core/res/res/drawable-hdpi/compass_base.pngbin0 -> 3986 bytes
-rw-r--r--core/res/res/drawable-hdpi/create_contact.pngbin0 -> 2138 bytes
-rw-r--r--core/res/res/drawable-hdpi/dark_header.9.pngbin0 -> 203 bytes
-rw-r--r--core/res/res/drawable-hdpi/dialog_divider_horizontal_light.9.pngbin0 -> 296 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_bright.9.pngbin0 -> 268 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.pngbin0 -> 287 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_dark.9.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.pngbin0 -> 289 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.pngbin0 -> 285 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_horizontal_textfield.9.pngbin0 -> 142 bytes
-rw-r--r--core/res/res/drawable-hdpi/divider_vertical_bright.9.pngbin0 -> 109 bytes
-rw-r--r--core/res/res/drawable-hdpi/editbox_background_focus_yellow.9.pngbin0 -> 1347 bytes
-rw-r--r--core/res/res/drawable-hdpi/editbox_background_normal.9.pngbin0 -> 642 bytes
-rw-r--r--core/res/res/drawable-hdpi/editbox_dropdown_background.9.pngbin0 -> 395 bytes
-rw-r--r--core/res/res/drawable-hdpi/editbox_dropdown_background_dark.9.pngbin0 -> 402 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_angel.pngbin0 -> 1534 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_cool.pngbin0 -> 1321 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_crying.pngbin0 -> 1401 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_foot_in_mouth.pngbin0 -> 1387 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_happy.pngbin0 -> 1486 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_kissing.pngbin0 -> 1156 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_laughing.pngbin0 -> 1382 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_lips_are_sealed.pngbin0 -> 1543 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_money_mouth.pngbin0 -> 1404 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_sad.pngbin0 -> 1455 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_surprised.pngbin0 -> 1252 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_tongue_sticking_out.pngbin0 -> 1577 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_undecided.pngbin0 -> 1400 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_winking.pngbin0 -> 1322 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_wtf.pngbin0 -> 1385 bytes
-rw-r--r--core/res/res/drawable-hdpi/emo_im_yelling.pngbin0 -> 1433 bytes
-rw-r--r--core/res/res/drawable-hdpi/expander_ic_maximized.9.pngbin0 -> 2241 bytes
-rw-r--r--core/res/res/drawable-hdpi/expander_ic_minimized.9.pngbin0 -> 2196 bytes
-rw-r--r--core/res/res/drawable-hdpi/focused_application_background_static.pngbin0 -> 2277 bytes
-rw-r--r--core/res/res/drawable-hdpi/frame_gallery_thumb.9.pngbin0 -> 990 bytes
-rw-r--r--core/res/res/drawable-hdpi/frame_gallery_thumb_pressed.9.pngbin0 -> 976 bytes
-rw-r--r--core/res/res/drawable-hdpi/frame_gallery_thumb_selected.9.pngbin0 -> 783 bytes
-rw-r--r--core/res/res/drawable-hdpi/gallery_selected_default.9.pngbin0 -> 1445 bytes
-rw-r--r--core/res/res/drawable-hdpi/gallery_selected_focused.9.pngbin0 -> 1642 bytes
-rw-r--r--core/res/res/drawable-hdpi/gallery_selected_pressed.9.pngbin0 -> 1664 bytes
-rw-r--r--core/res/res/drawable-hdpi/gallery_unselected_default.9.pngbin0 -> 729 bytes
-rw-r--r--core/res/res/drawable-hdpi/gallery_unselected_pressed.9.pngbin0 -> 874 bytes
-rw-r--r--core/res/res/drawable-hdpi/grid_selector_background_focus.9.pngbin0 -> 1664 bytes
-rw-r--r--core/res/res/drawable-hdpi/grid_selector_background_pressed.9.pngbin0 -> 1540 bytes
-rw-r--r--core/res/res/drawable-hdpi/highlight_disabled.9.pngbin0 -> 1506 bytes
-rw-r--r--core/res/res/drawable-hdpi/highlight_pressed.9.pngbin0 -> 1538 bytes
-rw-r--r--core/res/res/drawable-hdpi/highlight_selected.9.pngbin0 -> 1666 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_round_more_disabled.pngbin0 -> 648 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_round_more_normal.pngbin0 -> 1622 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_search.pngbin0 -> 2619 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_speak_now.pngbin0 -> 1936 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_disabled.pngbin0 -> 655 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_normal.pngbin0 -> 1396 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_disabled.pngbin0 -> 702 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_normal.pngbin0 -> 2055 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_bullet_key_permission.pngbin0 -> 3255 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_contact_picture.pngbin0 -> 1469 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_delete.pngbin0 -> 1445 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_alert.pngbin0 -> 1337 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_dialer.pngbin0 -> 1158 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_email.pngbin0 -> 1621 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_info.pngbin0 -> 1342 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_map.pngbin0 -> 1774 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_menu_generic.pngbin0 -> 5541 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_time.pngbin0 -> 2038 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_dialog_usb.pngbin0 -> 1475 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_emergency.pngbin0 -> 846 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_input_add.pngbin0 -> 686 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_input_delete.pngbin0 -> 1307 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_input_get.pngbin0 -> 976 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_launcher_android.pngbin0 -> 4833 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_airplane_mode.pngbin0 -> 2250 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_airplane_mode_off.pngbin0 -> 3003 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_idle_alarm.pngbin0 -> 965 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_idle_charging.pngbin0 -> 890 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_idle_lock.pngbin0 -> 784 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_idle_low_battery.pngbin0 -> 951 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_lock.pngbin0 -> 1998 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_power_off.pngbin0 -> 3037 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_silent_mode.pngbin0 -> 2242 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_lock_silent_mode_off.pngbin0 -> 2548 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_maps_indicator_current_position.pngbin0 -> 2079 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim1.pngbin0 -> 2073 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim2.pngbin0 -> 2082 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim3.pngbin0 -> 2069 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_ff.pngbin0 -> 1505 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_next.pngbin0 -> 1675 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_pause.pngbin0 -> 755 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_play.pngbin0 -> 1261 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_previous.pngbin0 -> 1713 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_media_rew.pngbin0 -> 1510 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_account_list.pngbin0 -> 3662 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_add.pngbin0 -> 3765 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_agenda.pngbin0 -> 3000 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_allfriends.pngbin0 -> 3153 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.pngbin0 -> 2307 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_archive.pngbin0 -> 2003 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_attachment.pngbin0 -> 3639 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_back.pngbin0 -> 2096 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_block.pngbin0 -> 3774 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_blocked_user.pngbin0 -> 4174 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_call.pngbin0 -> 3391 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_camera.pngbin0 -> 2736 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_cc.pngbin0 -> 3417 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_chat_dashboard.pngbin0 -> 3086 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_clear_playlist.pngbin0 -> 3631 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.pngbin0 -> 4704 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_compass.pngbin0 -> 5882 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_compose.pngbin0 -> 2993 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_crop.pngbin0 -> 2352 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_day.pngbin0 -> 1947 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_delete.pngbin0 -> 2343 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_directions.pngbin0 -> 2447 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_edit.pngbin0 -> 2310 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_emoticons.pngbin0 -> 4286 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_end_conversation.pngbin0 -> 3867 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_forward.pngbin0 -> 1881 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_friendslist.pngbin0 -> 2567 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_gallery.pngbin0 -> 4037 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_goto.pngbin0 -> 2824 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_help.pngbin0 -> 3926 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_home.pngbin0 -> 3259 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_info_details.pngbin0 -> 3080 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_invite.pngbin0 -> 3693 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_login.pngbin0 -> 3638 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_manage.pngbin0 -> 3726 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_mapmode.pngbin0 -> 2985 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_mark.pngbin0 -> 3834 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_month.pngbin0 -> 3849 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_more.pngbin0 -> 4305 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_my_calendar.pngbin0 -> 2926 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_mylocation.pngbin0 -> 3948 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_myplaces.pngbin0 -> 3138 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_notifications.pngbin0 -> 2535 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_play_clip.pngbin0 -> 2247 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_preferences.pngbin0 -> 3341 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_recent_history.pngbin0 -> 4616 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_refresh.pngbin0 -> 4174 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_report_image.pngbin0 -> 3035 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_revert.pngbin0 -> 2806 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_rotate.pngbin0 -> 4672 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_save.pngbin0 -> 2189 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_search.pngbin0 -> 3808 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_send.pngbin0 -> 3065 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_set_as.pngbin0 -> 3187 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_share.pngbin0 -> 3706 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_slideshow.pngbin0 -> 4155 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.pngbin0 -> 3079 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_sort_by_size.pngbin0 -> 1460 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_star.pngbin0 -> 2382 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_start_conversation.pngbin0 -> 2717 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_stop.pngbin0 -> 3253 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_today.pngbin0 -> 2825 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_upload.pngbin0 -> 2250 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_upload_you_tube.pngbin0 -> 3766 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_view.pngbin0 -> 3265 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_week.pngbin0 -> 2570 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_menu_zoom.pngbin0 -> 3993 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_clear_all.pngbin0 -> 2210 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_notification_overlay.9.pngbin0 -> 1014 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_partial_secure.pngbin0 -> 542 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_disk_full.pngbin0 -> 2074 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_reminder.pngbin0 -> 1596 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_1.pngbin0 -> 1612 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_2.pngbin0 -> 1778 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_3.pngbin0 -> 1689 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_4.pngbin0 -> 1649 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_5.pngbin0 -> 1879 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_popup_sync_6.pngbin0 -> 1756 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_search_category_default.pngbin0 -> 3225 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_secure.pngbin0 -> 420 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_text_dot.pngbin0 -> 865 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_vibrate.pngbin0 -> 3273 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume.pngbin0 -> 4283 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume_bluetooth_ad2p.pngbin0 -> 5210 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume_bluetooth_in_call.pngbin0 -> 4376 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume_off.pngbin0 -> 3567 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume_off_small.pngbin0 -> 1414 bytes
-rw-r--r--core/res/res/drawable-hdpi/ic_volume_small.pngbin0 -> 1599 bytes
-rw-r--r--core/res/res/drawable-hdpi/icon_highlight_rectangle.9.pngbin0 -> 1317 bytes
-rw-r--r--core/res/res/drawable-hdpi/icon_highlight_square.9.pngbin0 -> 1528 bytes
-rw-r--r--core/res/res/drawable-hdpi/ime_qwerty.pngbin0 -> 708 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.pngbin0 -> 298 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.pngbin0 -> 298 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.pngbin0 -> 4675 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.pngbin0 -> 5904 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.pngbin0 -> 5742 bytes
-rw-r--r--core/res/res/drawable-hdpi/indicator_input_error.pngbin0 -> 1461 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_accessory_bg_landscape.9.pngbin0 -> 217 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_background.9.pngbin0 -> 206 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.pngbin0 -> 1853 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.pngbin0 -> 2066 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_popup_panel_background.9.pngbin0 -> 1443 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.pngbin0 -> 1677 bytes
-rw-r--r--core/res/res/drawable-hdpi/keyboard_textfield_selected.9.pngbin0 -> 955 bytes
-rw-r--r--core/res/res/drawable-hdpi/light_header.9.pngbin0 -> 202 bytes
-rw-r--r--core/res/res/drawable-hdpi/list_selector_background_disabled.9.pngbin0 -> 4205 bytes
-rw-r--r--core/res/res/drawable-hdpi/list_selector_background_focus.9.pngbin0 -> 4329 bytes
-rw-r--r--core/res/res/drawable-hdpi/list_selector_background_longpress.9.pngbin0 -> 544 bytes
-rw-r--r--core/res/res/drawable-hdpi/list_selector_background_pressed.9.pngbin0 -> 4203 bytes
-rw-r--r--core/res/res/drawable-hdpi/loading_tile.pngbin0 -> 617 bytes
-rw-r--r--core/res/res/drawable-hdpi/maps_google_logo.pngbin0 -> 4169 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_background.9.pngbin0 -> 1307 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.pngbin0 -> 322 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_separator.9.pngbin0 -> 2826 bytes
-rw-r--r--core/res/res/drawable-hdpi/menu_submenu_background.9.pngbin0 -> 2413 bytes
-rw-r--r--core/res/res/drawable-hdpi/menuitem_background_focus.9.pngbin0 -> 4329 bytes
-rw-r--r--core/res/res/drawable-hdpi/menuitem_background_pressed.9.pngbin0 -> 4203 bytes
-rw-r--r--core/res/res/drawable-hdpi/menuitem_background_solid_focused.9.png (renamed from core/res/res/drawable/menuitem_background_solid_focused.9.png)bin165 -> 165 bytes
-rw-r--r--core/res/res/drawable-hdpi/menuitem_background_solid_pressed.9.png (renamed from core/res/res/drawable/menuitem_background_solid_pressed.9.png)bin165 -> 165 bytes
-rw-r--r--core/res/res/drawable-hdpi/menuitem_checkbox_on.pngbin0 -> 328 bytes
-rw-r--r--core/res/res/drawable-hdpi/no_tile_128.pngbin0 -> 1698 bytes
-rw-r--r--core/res/res/drawable-hdpi/panel_background.9.pngbin0 -> 2135 bytes
-rw-r--r--core/res/res/drawable-hdpi/panel_picture_frame_bg_focus_blue.9.pngbin0 -> 2065 bytes
-rw-r--r--core/res/res/drawable-hdpi/panel_picture_frame_bg_normal.9.pngbin0 -> 862 bytes
-rw-r--r--core/res/res/drawable-hdpi/panel_picture_frame_bg_pressed_blue.9.pngbin0 -> 1951 bytes
-rw-r--r--core/res/res/drawable-hdpi/pickerbox_background.pngbin0 -> 1131 bytes
-rw-r--r--core/res/res/drawable-hdpi/pickerbox_selected.9.pngbin0 -> 2129 bytes
-rw-r--r--core/res/res/drawable-hdpi/pickerbox_unselected.9.pngbin0 -> 1419 bytes
-rw-r--r--core/res/res/drawable-hdpi/picture_emergency.pngbin0 -> 1153 bytes
-rw-r--r--core/res/res/drawable-hdpi/picture_frame.9.pngbin0 -> 901 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_bottom_bright.9.pngbin0 -> 1231 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_bottom_dark.9.pngbin0 -> 1336 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_bottom_medium.9.pngbin0 -> 1329 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_center_bright.9.pngbin0 -> 240 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_center_dark.9.pngbin0 -> 255 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_center_medium.9.pngbin0 -> 156 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_full_bright.9.pngbin0 -> 2009 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_full_dark.9.pngbin0 -> 2218 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_inline_error.9.pngbin0 -> 2372 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_inline_error_above.9.pngbin0 -> 2383 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_top_bright.9.pngbin0 -> 1192 bytes
-rw-r--r--core/res/res/drawable-hdpi/popup_top_dark.9.pngbin0 -> 1339 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_away.pngbin0 -> 1306 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_busy.pngbin0 -> 1241 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_invisible.pngbin0 -> 1094 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_offline.pngbin0 -> 1340 bytes
-rw-r--r--core/res/res/drawable-hdpi/presence_online.pngbin0 -> 1265 bytes
-rw-r--r--core/res/res/drawable-hdpi/pressed_application_background_static.pngbin0 -> 1651 bytes
-rw-r--r--core/res/res/drawable-hdpi/progressbar_indeterminate1.pngbin0 -> 779 bytes
-rw-r--r--core/res/res/drawable-hdpi/progressbar_indeterminate2.pngbin0 -> 775 bytes
-rw-r--r--core/res/res/drawable-hdpi/progressbar_indeterminate3.pngbin0 -> 779 bytes
-rw-r--r--core/res/res/drawable-hdpi/radiobutton_off_background.pngbin0 -> 882 bytes
-rw-r--r--core/res/res/drawable-hdpi/radiobutton_on_background.pngbin0 -> 1187 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_big_half.pngbin0 -> 1458 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_big_off.pngbin0 -> 833 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_big_on.pngbin0 -> 2694 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_small_half.pngbin0 -> 866 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_small_off.pngbin0 -> 509 bytes
-rw-r--r--core/res/res/drawable-hdpi/rate_star_small_on.pngbin0 -> 856 bytes
-rw-r--r--core/res/res/drawable-hdpi/reticle.pngbin0 -> 1090 bytes
-rw-r--r--core/res/res/drawable-hdpi/screen_progress_frame.9.pngbin0 -> 370 bytes
-rw-r--r--core/res/res/drawable-hdpi/screen_progress_inner.9.pngbin0 -> 178 bytes
-rw-r--r--core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.pngbin0 -> 4461 bytes
-rw-r--r--core/res/res/drawable-hdpi/scrollbar_handle_horizontal.9.pngbin0 -> 336 bytes
-rw-r--r--core/res/res/drawable-hdpi/scrollbar_handle_vertical.9.pngbin0 -> 308 bytes
-rw-r--r--core/res/res/drawable-hdpi/search_dropdown_background.9.pngbin0 -> 552 bytes
-rw-r--r--core/res/res/drawable-hdpi/search_plate.9.pngbin0 -> 319 bytes
-rw-r--r--core/res/res/drawable-hdpi/search_plate_global.9.pngbin0 -> 301 bytes
-rw-r--r--core/res/res/drawable-hdpi/seek_thumb_normal.pngbin0 -> 1350 bytes
-rw-r--r--core/res/res/drawable-hdpi/seek_thumb_pressed.pngbin0 -> 1626 bytes
-rw-r--r--core/res/res/drawable-hdpi/seek_thumb_selected.pngbin0 -> 1672 bytes
-rw-r--r--core/res/res/drawable-hdpi/settings_header_raw.9.pngbin0 -> 13362 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_black_16.pngbin0 -> 630 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_black_20.pngbin0 -> 847 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_black_48.pngbin0 -> 1812 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_black_76.pngbin0 -> 2663 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_dropdown_background_down.9.pngbin0 -> 592 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_dropdown_background_up.9.pngbin0 -> 574 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_normal.9.pngbin0 -> 911 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_press.9.pngbin0 -> 1954 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_select.9.pngbin0 -> 2082 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_white_16.pngbin0 -> 606 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_white_48.pngbin0 -> 1736 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinner_white_76.pngbin0 -> 2603 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.pngbin0 -> 491 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.pngbin0 -> 489 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.pngbin0 -> 486 bytes
-rw-r--r--core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.pngbin0 -> 451 bytes
-rw-r--r--core/res/res/drawable-hdpi/star_big_off.pngbin0 -> 2473 bytes
-rw-r--r--core/res/res/drawable-hdpi/star_big_on.pngbin0 -> 2980 bytes
-rw-r--r--core/res/res/drawable-hdpi/star_off.pngbin0 -> 891 bytes
-rw-r--r--core/res/res/drawable-hdpi/star_on.pngbin0 -> 1582 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_ecb_mode.pngbin0 -> 537 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_alarm.pngbin0 -> 1382 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_call_mute.pngbin0 -> 1158 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_chat.pngbin0 -> 994 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_disk_full.pngbin0 -> 1084 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_error.pngbin0 -> 1031 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_missed_call.pngbin0 -> 1219 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_more.pngbin0 -> 864 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sdcard.pngbin0 -> 758 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sdcard_usb.pngbin0 -> 1040 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sim_toolkit.pngbin0 -> 1322 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sync.pngbin0 -> 1599 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sync_anim0.pngbin0 -> 1604 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_sync_error.pngbin0 -> 1566 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_voicemail.pngbin0 -> 999 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_wifi_in_range.pngbin0 -> 1457 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_0.pngbin0 -> 1475 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_10.pngbin0 -> 736 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_100.pngbin0 -> 776 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_20.pngbin0 -> 741 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_40.pngbin0 -> 741 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_60.pngbin0 -> 746 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_80.pngbin0 -> 735 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.pngbin0 -> 925 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.pngbin0 -> 934 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.pngbin0 -> 934 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.pngbin0 -> 971 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.pngbin0 -> 975 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.pngbin0 -> 1024 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_battery_unknown.pngbin0 -> 1019 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_bluetooth.pngbin0 -> 918 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.pngbin0 -> 1096 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_connected_1x.pngbin0 -> 1208 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_connected_3g.pngbin0 -> 1170 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_connected_e.pngbin0 -> 1100 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_connected_g.pngbin0 -> 1119 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_in_1x.pngbin0 -> 1214 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_in_3g.pngbin0 -> 1179 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_in_e.pngbin0 -> 1103 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_in_g.pngbin0 -> 1116 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.pngbin0 -> 1215 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.pngbin0 -> 1158 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_inandout_e.pngbin0 -> 1090 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_inandout_g.pngbin0 -> 1106 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_out_1x.pngbin0 -> 1207 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_out_3g.pngbin0 -> 1160 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_out_e.pngbin0 -> 1091 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_out_g.pngbin0 -> 1111 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_data_usb.pngbin0 -> 977 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim0.pngbin0 -> 734 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim1.pngbin0 -> 738 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim2.pngbin0 -> 754 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim3.pngbin0 -> 752 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim4.pngbin0 -> 724 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_download_anim5.pngbin0 -> 744 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_gps_acquiring.pngbin0 -> 716 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_gps_on.pngbin0 -> 1615 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_headset.pngbin0 -> 664 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_no_sim.pngbin0 -> 960 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call.pngbin0 -> 999 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.pngbin0 -> 1067 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call_forward.pngbin0 -> 1054 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.pngbin0 -> 988 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_0.pngbin0 -> 933 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.pngbin0 -> 901 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_1.pngbin0 -> 955 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.pngbin0 -> 929 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_2.pngbin0 -> 956 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.pngbin0 -> 933 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_3.pngbin0 -> 958 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.pngbin0 -> 930 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_4.pngbin0 -> 878 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.pngbin0 -> 856 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.pngbin0 -> 859 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.pngbin0 -> 875 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.pngbin0 -> 889 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.pngbin0 -> 891 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.pngbin0 -> 799 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ringer_silent.pngbin0 -> 1096 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.pngbin0 -> 4980 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.pngbin0 -> 446 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.pngbin0 -> 139 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.pngbin0 -> 446 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_0.pngbin0 -> 751 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_1.pngbin0 -> 777 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_2.pngbin0 -> 787 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_3.pngbin0 -> 789 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_4.pngbin0 -> 706 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_flightmode.pngbin0 -> 1081 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_signal_null.pngbin0 -> 975 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_speakerphone.pngbin0 -> 1157 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_tty_mode.pngbin0 -> 400 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim0.pngbin0 -> 767 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim1.pngbin0 -> 771 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim2.pngbin0 -> 774 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim3.pngbin0 -> 767 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim4.pngbin0 -> 750 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_upload_anim5.pngbin0 -> 762 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_vp_phone_call.pngbin0 -> 1201 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.pngbin0 -> 1067 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.pngbin0 -> 1064 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_warning.pngbin0 -> 1034 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.pngbin0 -> 1033 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.pngbin0 -> 1047 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.pngbin0 -> 1054 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.pngbin0 -> 1074 bytes
-rw-r--r--core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.pngbin0 -> 4484 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_background.pngbin0 -> 839 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_close_on.9.pngbin0 -> 945 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_header_background.9.pngbin0 -> 187 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.pngbin0 -> 322 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_item_background_focus.9.pngbin0 -> 1657 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_item_background_normal.9.pngbin0 -> 197 bytes
-rw-r--r--core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.pngbin0 -> 1531 bytes
-rw-r--r--core/res/res/drawable-hdpi/statusbar_background.pngbin0 -> 743 bytes
-rw-r--r--core/res/res/drawable-hdpi/submenu_arrow_nofocus.pngbin0 -> 251 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_action_add.pngbin0 -> 1575 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_action_call.pngbin0 -> 1446 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_action_chat.pngbin0 -> 1277 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_action_email.pngbin0 -> 1462 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_call_incoming.pngbin0 -> 2046 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_call_missed.pngbin0 -> 1959 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_call_outgoing.pngbin0 -> 1949 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_contact_card.pngbin0 -> 498 bytes
-rw-r--r--core/res/res/drawable-hdpi/sym_def_app_icon.pngbin0 -> 5182 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_focus.9.pngbin0 -> 304 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_focus_bar_left.9.pngbin0 -> 143 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_focus_bar_right.9.pngbin0 -> 143 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_press.9.pngbin0 -> 304 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_press_bar_left.9.pngbin0 -> 146 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_press_bar_right.9.pngbin0 -> 146 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_selected.9.pngbin0 -> 312 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_selected_bar_left.9.pngbin0 -> 150 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_selected_bar_right.9.pngbin0 -> 150 bytes
-rw-r--r--core/res/res/drawable-hdpi/tab_unselected.9.pngbin0 -> 326 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_default.9.pngbin0 -> 997 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled.9.pngbin0 -> 710 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_disabled_selected.9.pngbin0 -> 915 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_pressed.9.pngbin0 -> 1356 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_search_default.9.pngbin0 -> 790 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_search_pressed.9.pngbin0 -> 1311 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_search_selected.9.pngbin0 -> 873 bytes
-rw-r--r--core/res/res/drawable-hdpi/textfield_selected.9.pngbin0 -> 1058 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_down_disabled.9.pngbin0 -> 554 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_down_disabled_focused.9.pngbin0 -> 786 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_down_normal.9.pngbin0 -> 1124 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_down_pressed.9.pngbin0 -> 1710 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_down_selected.9.pngbin0 -> 1738 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_input_disabled.9.pngbin0 -> 391 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_input_normal.9.pngbin0 -> 694 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_input_pressed.9.pngbin0 -> 970 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_input_selected.9.pngbin0 -> 662 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_up_disabled.9.pngbin0 -> 710 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_up_disabled_focused.9.pngbin0 -> 946 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_up_normal.9.pngbin0 -> 1388 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_up_pressed.9.pngbin0 -> 2068 bytes
-rw-r--r--core/res/res/drawable-hdpi/timepicker_up_selected.9.pngbin0 -> 2103 bytes
-rw-r--r--core/res/res/drawable-hdpi/title_bar_portrait.9.pngbin0 -> 355 bytes
-rw-r--r--core/res/res/drawable-hdpi/title_bar_shadow.9.pngbin0 -> 199 bytes
-rw-r--r--core/res/res/drawable-hdpi/title_bar_tall.pngbin0 -> 599 bytes
-rw-r--r--core/res/res/drawable-hdpi/toast_frame.9.pngbin0 -> 2461 bytes
-rw-r--r--core/res/res/drawable-hdpi/unknown_image.pngbin0 -> 148 bytes
-rw-r--r--core/res/res/drawable-hdpi/zoom_plate.9.pngbin0 -> 1662 bytes
-rw-r--r--core/res/res/drawable-land-hdpi/statusbar_background.pngbin0 -> 858 bytes
-rw-r--r--core/res/res/drawable-land-hdpi/title_bar_tall.pngbin0 -> 721 bytes
-rw-r--r--core/res/res/drawable-land-mdpi/statusbar_background.png (renamed from core/res/res/drawable-land/statusbar_background.png)bin4475 -> 4475 bytes
-rw-r--r--core/res/res/drawable-land-mdpi/title_bar_tall.png (renamed from core/res/res/drawable-land/title_bar_tall.png)bin18449 -> 18449 bytes
-rw-r--r--core/res/res/drawable-mdpi/activity_title_bar.9.png (renamed from core/res/res/drawable/activity_title_bar.9.png)bin205 -> 205 bytes
-rw-r--r--core/res/res/drawable-mdpi/arrow_down_float.png (renamed from core/res/res/drawable/arrow_down_float.png)bin3141 -> 3141 bytes
-rw-r--r--core/res/res/drawable-mdpi/arrow_up_float.png (renamed from core/res/res/drawable/arrow_up_float.png)bin3128 -> 3128 bytes
-rw-r--r--core/res/res/drawable-mdpi/battery_charge_background.png (renamed from core/res/res/drawable/battery_charge_background.png)bin4694 -> 4694 bytes
-rw-r--r--core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png (renamed from core/res/res/drawable/battery_charge_fill_empty.9.png)bin3384 -> 3384 bytes
-rw-r--r--core/res/res/drawable-mdpi/battery_charge_fill_full.9.png (renamed from core/res/res/drawable/battery_charge_fill_full.9.png)bin3409 -> 3409 bytes
-rw-r--r--core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png (renamed from core/res/res/drawable/battery_charge_fill_warning.9.png)bin3427 -> 3427 bytes
-rw-r--r--core/res/res/drawable-mdpi/battery_low_battery.png (renamed from core/res/res/drawable/battery_low_battery.png)bin5306 -> 5306 bytes
-rw-r--r--core/res/res/drawable-mdpi/blank_tile.png (renamed from core/res/res/drawable/blank_tile.png)bin557 -> 557 bytes
-rw-r--r--core/res/res/drawable-mdpi/bottom_bar.png (renamed from core/res/res/drawable/bottom_bar.png)bin2426 -> 2426 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_check_buttonless_off.png (renamed from core/res/res/drawable/btn_check_buttonless_off.png)bin608 -> 608 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_check_buttonless_on.png (renamed from core/res/res/drawable/btn_check_buttonless_on.png)bin721 -> 721 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_label_background.9.png (renamed from core/res/res/drawable/btn_check_label_background.9.png)bin178 -> 178 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off.png (renamed from core/res/res/drawable/btn_check_off.png)bin1172 -> 1172 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable.png (renamed from core/res/res/drawable/btn_check_off_disable.png)bin903 -> 903 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_disable_focused.png (renamed from core/res/res/drawable/btn_check_off_disable_focused.png)bin1073 -> 1073 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_pressed.png (renamed from core/res/res/drawable/btn_check_off_pressed.png)bin1630 -> 1630 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_off_selected.png (renamed from core/res/res/drawable/btn_check_off_selected.png)bin1598 -> 1598 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on.png (renamed from core/res/res/drawable/btn_check_on.png)bin1390 -> 1390 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable.png (renamed from core/res/res/drawable/btn_check_on_disable.png)bin973 -> 973 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_disable_focused.png (renamed from core/res/res/drawable/btn_check_on_disable_focused.png)bin1138 -> 1138 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_pressed.png (renamed from core/res/res/drawable/btn_check_on_pressed.png)bin1680 -> 1680 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_check_on_selected.png (renamed from core/res/res/drawable/btn_check_on_selected.png)bin1661 -> 1661 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_circle_disable.png (renamed from core/res/res/drawable/btn_circle_disable.png)bin938 -> 938 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_circle_disable_focused.png (renamed from core/res/res/drawable/btn_circle_disable_focused.png)bin1436 -> 1436 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_circle_normal.png (renamed from core/res/res/drawable/btn_circle_normal.png)bin1249 -> 1249 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_circle_pressed.png (renamed from core/res/res/drawable/btn_circle_pressed.png)bin1613 -> 1613 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_circle_selected.png (renamed from core/res/res/drawable/btn_circle_selected.png)bin1645 -> 1645 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_close_normal.pngbin0 -> 1259 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_close_pressed.pngbin0 -> 1726 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_close_selected.pngbin0 -> 1716 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_code_lock_default.png (renamed from core/res/res/drawable/btn_code_lock_default.png)bin3943 -> 3943 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_code_lock_touched.png (renamed from core/res/res/drawable/btn_code_lock_touched.png)bin4506 -> 4506 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_normal.9.png (renamed from core/res/res/drawable/btn_default_normal.9.png)bin763 -> 763 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_normal_disable.9.png (renamed from core/res/res/drawable/btn_default_normal_disable.9.png)bin474 -> 474 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png (renamed from core/res/res/drawable/btn_default_normal_disable_focused.9.png)bin673 -> 673 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_pressed.9.png (renamed from core/res/res/drawable/btn_default_pressed.9.png)bin1083 -> 1083 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_selected.9.png (renamed from core/res/res/drawable/btn_default_selected.9.png)bin1099 -> 1099 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_small_normal.9.png (renamed from core/res/res/drawable/btn_default_small_normal.9.png)bin679 -> 679 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png (renamed from core/res/res/drawable/btn_default_small_normal_disable.9.png)bin456 -> 456 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png (renamed from core/res/res/drawable/btn_default_small_normal_disable_focused.9.png)bin648 -> 648 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_small_pressed.9.png (renamed from core/res/res/drawable/btn_default_small_pressed.9.png)bin936 -> 936 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_default_small_selected.9.png (renamed from core/res/res/drawable/btn_default_small_selected.9.png)bin964 -> 964 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_dialog_disable.png (renamed from core/res/res/drawable/btn_dialog_disable.png)bin1477 -> 1477 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_dialog_normal.png (renamed from core/res/res/drawable/btn_dialog_normal.png)bin1263 -> 1263 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_dialog_pressed.png (renamed from core/res/res/drawable/btn_dialog_pressed.png)bin1496 -> 1496 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_dialog_selected.png (renamed from core/res/res/drawable/btn_dialog_selected.png)bin1522 -> 1522 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_dropdown_normal.9.png (renamed from core/res/res/drawable/btn_dropdown_normal.9.png)bin1290 -> 1290 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png (renamed from core/res/res/drawable/btn_dropdown_pressed.9.png)bin2025 -> 2025 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_dropdown_selected.9.png (renamed from core/res/res/drawable/btn_dropdown_selected.9.png)bin2014 -> 2014 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_erase_default.9.png (renamed from core/res/res/drawable/btn_erase_default.9.png)bin1468 -> 1468 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_erase_pressed.9.png (renamed from core/res/res/drawable/btn_erase_pressed.9.png)bin1313 -> 1313 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_erase_selected.9.png (renamed from core/res/res/drawable/btn_erase_selected.9.png)bin1184 -> 1184 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_global_search_normal.9.png (renamed from core/res/res/drawable/btn_global_search_normal.9.png)bin676 -> 676 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_normal.9.png (renamed from core/res/res/drawable/btn_keyboard_key_normal.9.png)bin726 -> 726 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png (renamed from core/res/res/drawable/btn_keyboard_key_normal_off.9.png)bin860 -> 860 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png (renamed from core/res/res/drawable/btn_keyboard_key_normal_on.9.png)bin926 -> 926 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_keyboard_key_pressed.9.png (renamed from core/res/res/drawable/btn_keyboard_key_pressed.9.png)bin664 -> 664 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png (renamed from core/res/res/drawable/btn_keyboard_key_pressed_off.9.png)bin836 -> 836 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png (renamed from core/res/res/drawable/btn_keyboard_key_pressed_on.9.png)bin886 -> 886 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal.9.pngbin0 -> 780 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed.9.pngbin0 -> 1018 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_keyboard_key_trans_selected.9.pngbin0 -> 1037 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_media_player.9.png (renamed from core/res/res/drawable/btn_media_player.9.png)bin1677 -> 1677 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_media_player_disabled.9.png (renamed from core/res/res/drawable/btn_media_player_disabled.9.png)bin724 -> 724 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png (renamed from core/res/res/drawable/btn_media_player_disabled_selected.9.png)bin1040 -> 1040 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_media_player_pressed.9.png (renamed from core/res/res/drawable/btn_media_player_pressed.9.png)bin1222 -> 1222 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_media_player_selected.9.png (renamed from core/res/res/drawable/btn_media_player_selected.9.png)bin1481 -> 1481 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_minus_default.png (renamed from core/res/res/drawable/btn_minus_default.png)bin3366 -> 3366 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_minus_disable.png (renamed from core/res/res/drawable/btn_minus_disable.png)bin3564 -> 3564 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_minus_disable_focused.png (renamed from core/res/res/drawable/btn_minus_disable_focused.png)bin3914 -> 3914 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_minus_pressed.png (renamed from core/res/res/drawable/btn_minus_pressed.png)bin3592 -> 3592 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_minus_selected.png (renamed from core/res/res/drawable/btn_minus_selected.png)bin3561 -> 3561 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_plus_default.png (renamed from core/res/res/drawable/btn_plus_default.png)bin3353 -> 3353 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_plus_disable.png (renamed from core/res/res/drawable/btn_plus_disable.png)bin3669 -> 3669 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_plus_disable_focused.png (renamed from core/res/res/drawable/btn_plus_disable_focused.png)bin4031 -> 4031 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_plus_pressed.png (renamed from core/res/res/drawable/btn_plus_pressed.png)bin3721 -> 3721 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_plus_selected.png (renamed from core/res/res/drawable/btn_plus_selected.png)bin3674 -> 3674 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_label_background.9.png (renamed from core/res/res/drawable/btn_radio_label_background.9.png)bin178 -> 178 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off.png (renamed from core/res/res/drawable/btn_radio_off.png)bin1542 -> 1542 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_pressed.png (renamed from core/res/res/drawable/btn_radio_off_pressed.png)bin1928 -> 1928 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_off_selected.png (renamed from core/res/res/drawable/btn_radio_off_selected.png)bin1954 -> 1954 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on.png (renamed from core/res/res/drawable/btn_radio_on.png)bin1692 -> 1692 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_pressed.png (renamed from core/res/res/drawable/btn_radio_on_pressed.png)bin1997 -> 1997 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_radio_on_selected.png (renamed from core/res/res/drawable/btn_radio_on_selected.png)bin2009 -> 2009 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_off_normal.png (renamed from core/res/res/drawable/btn_rating_star_off_normal.png)bin2760 -> 2760 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_off_pressed.png (renamed from core/res/res/drawable/btn_rating_star_off_pressed.png)bin3613 -> 3613 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_off_selected.png (renamed from core/res/res/drawable/btn_rating_star_off_selected.png)bin3622 -> 3622 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_on_normal.png (renamed from core/res/res/drawable/btn_rating_star_on_normal.png)bin3290 -> 3290 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_on_pressed.png (renamed from core/res/res/drawable/btn_rating_star_on_pressed.png)bin3756 -> 3756 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_rating_star_on_selected.png (renamed from core/res/res/drawable/btn_rating_star_on_selected.png)bin3768 -> 3768 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_default.9.png (renamed from core/res/res/drawable/btn_search_dialog_default.9.png)bin3207 -> 3207 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_pressed.9.png (renamed from core/res/res/drawable/btn_search_dialog_pressed.9.png)bin3597 -> 3597 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_selected.9.png (renamed from core/res/res/drawable/btn_search_dialog_selected.9.png)bin3596 -> 3596 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png (renamed from core/res/res/drawable/btn_search_dialog_voice_default.9.png)bin3371 -> 3371 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png (renamed from core/res/res/drawable/btn_search_dialog_voice_pressed.9.png)bin3722 -> 3722 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png (renamed from core/res/res/drawable/btn_search_dialog_voice_selected.9.png)bin3690 -> 3690 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_square_overlay_disabled.png (renamed from core/res/res/drawable/btn_square_overlay_disabled.png)bin653 -> 653 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_square_overlay_disabled_focused.png (renamed from core/res/res/drawable/btn_square_overlay_disabled_focused.png)bin826 -> 826 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_square_overlay_normal.png (renamed from core/res/res/drawable/btn_square_overlay_normal.png)bin910 -> 910 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_square_overlay_pressed.png (renamed from core/res/res/drawable/btn_square_overlay_pressed.png)bin1201 -> 1201 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_square_overlay_selected.png (renamed from core/res/res/drawable/btn_square_overlay_selected.png)bin1237 -> 1237 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_off.png (renamed from core/res/res/drawable/btn_star_big_off.png)bin1316 -> 1316 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_off_disable.png (renamed from core/res/res/drawable/btn_star_big_off_disable.png)bin1886 -> 1886 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_off_disable_focused.png (renamed from core/res/res/drawable/btn_star_big_off_disable_focused.png)bin1868 -> 1868 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_off_pressed.png (renamed from core/res/res/drawable/btn_star_big_off_pressed.png)bin1507 -> 1507 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_off_selected.png (renamed from core/res/res/drawable/btn_star_big_off_selected.png)bin1471 -> 1471 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_on.png (renamed from core/res/res/drawable/btn_star_big_on.png)bin1521 -> 1521 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_on_disable.png (renamed from core/res/res/drawable/btn_star_big_on_disable.png)bin4522 -> 4522 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_on_disable_focused.png (renamed from core/res/res/drawable/btn_star_big_on_disable_focused.png)bin1911 -> 1911 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_on_pressed.png (renamed from core/res/res/drawable/btn_star_big_on_pressed.png)bin1540 -> 1540 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/btn_star_big_on_selected.png (renamed from core/res/res/drawable/btn_star_big_on_selected.png)bin1456 -> 1456 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_star_label_background.9.png (renamed from core/res/res/drawable/btn_star_label_background.9.png)bin153 -> 153 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_off.9.png (renamed from core/res/res/drawable/btn_toggle_off.9.png)bin364 -> 364 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_toggle_on.9.png (renamed from core/res/res/drawable/btn_toggle_on.9.png)bin442 -> 442 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_down_disabled.9.png (renamed from core/res/res/drawable/btn_zoom_down_disabled.9.png)bin1455 -> 1455 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_down_disabled_focused.9.png (renamed from core/res/res/drawable/btn_zoom_down_disabled_focused.9.png)bin1804 -> 1804 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_down_normal.9.png (renamed from core/res/res/drawable/btn_zoom_down_normal.9.png)bin1983 -> 1983 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_down_pressed.9.png (renamed from core/res/res/drawable/btn_zoom_down_pressed.9.png)bin2522 -> 2522 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_down_selected.9.png (renamed from core/res/res/drawable/btn_zoom_down_selected.9.png)bin2532 -> 2532 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_page_normal.png (renamed from core/res/res/drawable/btn_zoom_page_normal.png)bin1991 -> 1991 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_page_press.png (renamed from core/res/res/drawable/btn_zoom_page_press.png)bin2075 -> 2075 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_up_disabled.9.png (renamed from core/res/res/drawable/btn_zoom_up_disabled.9.png)bin1445 -> 1445 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_up_disabled_focused.9.png (renamed from core/res/res/drawable/btn_zoom_up_disabled_focused.9.png)bin1798 -> 1798 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_up_normal.9.png (renamed from core/res/res/drawable/btn_zoom_up_normal.9.png)bin1962 -> 1962 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_up_pressed.9.png (renamed from core/res/res/drawable/btn_zoom_up_pressed.9.png)bin2547 -> 2547 bytes
-rw-r--r--core/res/res/drawable-mdpi/btn_zoom_up_selected.9.png (renamed from core/res/res/drawable/btn_zoom_up_selected.9.png)bin2540 -> 2540 bytes
-rw-r--r--core/res/res/drawable-mdpi/button_onoff_indicator_off.png (renamed from core/res/res/drawable/button_onoff_indicator_off.png)bin298 -> 298 bytes
-rw-r--r--core/res/res/drawable-mdpi/button_onoff_indicator_on.png (renamed from core/res/res/drawable/button_onoff_indicator_on.png)bin380 -> 380 bytes
-rw-r--r--core/res/res/drawable-mdpi/call_contact.png (renamed from core/res/res/drawable/call_contact.png)bin1025 -> 1025 bytes
-rw-r--r--core/res/res/drawable-mdpi/checkbox_off_background.png (renamed from core/res/res/drawable/checkbox_off_background.png)bin3106 -> 3106 bytes
-rw-r--r--core/res/res/drawable-mdpi/checkbox_on_background.png (renamed from core/res/res/drawable/checkbox_on_background.png)bin3354 -> 3354 bytes
-rw-r--r--core/res/res/drawable-mdpi/clock_dial.png (renamed from core/res/res/drawable/clock_dial.png)bin7384 -> 7384 bytes
-rw-r--r--core/res/res/drawable-mdpi/clock_hand_hour.png (renamed from core/res/res/drawable/clock_hand_hour.png)bin1412 -> 1412 bytes
-rw-r--r--core/res/res/drawable-mdpi/clock_hand_minute.png (renamed from core/res/res/drawable/clock_hand_minute.png)bin1550 -> 1550 bytes
-rw-r--r--core/res/res/drawable-mdpi/code_lock_bottom.9.png (renamed from core/res/res/drawable/code_lock_bottom.9.png)bin158 -> 158 bytes
-rw-r--r--core/res/res/drawable-mdpi/code_lock_left.9.png (renamed from core/res/res/drawable/code_lock_left.9.png)bin131 -> 131 bytes
-rw-r--r--core/res/res/drawable-mdpi/code_lock_top.9.png (renamed from core/res/res/drawable/code_lock_top.9.png)bin124 -> 124 bytes
-rw-r--r--core/res/res/drawable-mdpi/compass_arrow.png (renamed from core/res/res/drawable/compass_arrow.png)bin732 -> 732 bytes
-rw-r--r--core/res/res/drawable-mdpi/compass_base.png (renamed from core/res/res/drawable/compass_base.png)bin2508 -> 2508 bytes
-rw-r--r--core/res/res/drawable-mdpi/create_contact.png (renamed from core/res/res/drawable/create_contact.png)bin1132 -> 1132 bytes
-rw-r--r--core/res/res/drawable-mdpi/dark_header.9.pngbin0 -> 161 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/dialog_divider_horizontal_light.9.png (renamed from core/res/res/drawable/dialog_divider_horizontal_light.9.png)bin2921 -> 2921 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_bright.9.pngbin0 -> 119 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.pngbin0 -> 120 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_dark.9.pngbin0 -> 121 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.pngbin0 -> 120 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png (renamed from core/res/res/drawable/divider_horizontal_dim_dark.9.png)bin232 -> 232 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_horizontal_textfield.9.png (renamed from core/res/res/drawable/divider_horizontal_textfield.9.png)bin137 -> 137 bytes
-rw-r--r--core/res/res/drawable-mdpi/divider_vertical_bright.9.pngbin0 -> 119 bytes
-rw-r--r--core/res/res/drawable-mdpi/editbox_background_focus_yellow.9.png (renamed from core/res/res/drawable/editbox_background_focus_yellow.9.png)bin3425 -> 3425 bytes
-rw-r--r--core/res/res/drawable-mdpi/editbox_background_normal.9.png (renamed from core/res/res/drawable/editbox_background_normal.9.png)bin3130 -> 3130 bytes
-rw-r--r--core/res/res/drawable-mdpi/editbox_dropdown_background.9.png (renamed from core/res/res/drawable/editbox_dropdown_background.9.png)bin3229 -> 3229 bytes
-rw-r--r--core/res/res/drawable-mdpi/editbox_dropdown_background_dark.9.png (renamed from core/res/res/drawable/editbox_dropdown_background_dark.9.png)bin367 -> 367 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_angel.png (renamed from core/res/res/drawable/emo_im_angel.png)bin3592 -> 3592 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_cool.png (renamed from core/res/res/drawable/emo_im_cool.png)bin3466 -> 3466 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_crying.png (renamed from core/res/res/drawable/emo_im_crying.png)bin3558 -> 3558 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_foot_in_mouth.png (renamed from core/res/res/drawable/emo_im_foot_in_mouth.png)bin3603 -> 3603 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_happy.png (renamed from core/res/res/drawable/emo_im_happy.png)bin3591 -> 3591 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_kissing.png (renamed from core/res/res/drawable/emo_im_kissing.png)bin3492 -> 3492 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_laughing.png (renamed from core/res/res/drawable/emo_im_laughing.png)bin3624 -> 3624 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_lips_are_sealed.png (renamed from core/res/res/drawable/emo_im_lips_are_sealed.png)bin3670 -> 3670 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_money_mouth.png (renamed from core/res/res/drawable/emo_im_money_mouth.png)bin3649 -> 3649 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_sad.png (renamed from core/res/res/drawable/emo_im_sad.png)bin3572 -> 3572 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_surprised.png (renamed from core/res/res/drawable/emo_im_surprised.png)bin3490 -> 3490 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_tongue_sticking_out.png (renamed from core/res/res/drawable/emo_im_tongue_sticking_out.png)bin3653 -> 3653 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_undecided.png (renamed from core/res/res/drawable/emo_im_undecided.png)bin3552 -> 3552 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_winking.png (renamed from core/res/res/drawable/emo_im_winking.png)bin3568 -> 3568 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_wtf.png (renamed from core/res/res/drawable/emo_im_wtf.png)bin3591 -> 3591 bytes
-rw-r--r--core/res/res/drawable-mdpi/emo_im_yelling.png (renamed from core/res/res/drawable/emo_im_yelling.png)bin3575 -> 3575 bytes
-rw-r--r--core/res/res/drawable-mdpi/expander_ic_maximized.9.png (renamed from core/res/res/drawable/expander_ic_maximized.9.png)bin1150 -> 1150 bytes
-rw-r--r--core/res/res/drawable-mdpi/expander_ic_minimized.9.png (renamed from core/res/res/drawable/expander_ic_minimized.9.png)bin1167 -> 1167 bytes
-rw-r--r--core/res/res/drawable-mdpi/focused_application_background_static.png (renamed from core/res/res/drawable/focused_application_background_static.png)bin3928 -> 3928 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/frame_gallery_thumb.9.png (renamed from core/res/res/drawable/frame_gallery_thumb.9.png)bin925 -> 925 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/frame_gallery_thumb_pressed.9.png (renamed from core/res/res/drawable/frame_gallery_thumb_pressed.9.png)bin1704 -> 1704 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/frame_gallery_thumb_selected.9.png (renamed from core/res/res/drawable/frame_gallery_thumb_selected.9.png)bin621 -> 621 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/gallery_selected_default.9.png (renamed from core/res/res/drawable/gallery_selected_default.9.png)bin1088 -> 1088 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/gallery_selected_focused.9.png (renamed from core/res/res/drawable/gallery_selected_focused.9.png)bin1313 -> 1313 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/gallery_selected_pressed.9.png (renamed from core/res/res/drawable/gallery_selected_pressed.9.png)bin1317 -> 1317 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/gallery_unselected_default.9.png (renamed from core/res/res/drawable/gallery_unselected_default.9.png)bin560 -> 560 bytes
-rw-r--r--core/res/res/drawable-mdpi/gallery_unselected_pressed.9.png (renamed from core/res/res/drawable/gallery_unselected_pressed.9.png)bin686 -> 686 bytes
-rw-r--r--core/res/res/drawable-mdpi/grid_selector_background_focus.9.png (renamed from core/res/res/drawable/grid_selector_background_focus.9.png)bin985 -> 985 bytes
-rw-r--r--core/res/res/drawable-mdpi/grid_selector_background_pressed.9.png (renamed from core/res/res/drawable/grid_selector_background_pressed.9.png)bin920 -> 920 bytes
-rw-r--r--core/res/res/drawable-mdpi/highlight_disabled.9.png (renamed from core/res/res/drawable/highlight_disabled.9.png)bin1291 -> 1291 bytes
-rw-r--r--core/res/res/drawable-mdpi/highlight_pressed.9.png (renamed from core/res/res/drawable/highlight_pressed.9.png)bin1103 -> 1103 bytes
-rw-r--r--core/res/res/drawable-mdpi/highlight_selected.9.png (renamed from core/res/res/drawable/highlight_selected.9.png)bin1145 -> 1145 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png (renamed from core/res/res/drawable/ic_btn_round_more_disabled.png)bin421 -> 421 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_round_more_normal.png (renamed from core/res/res/drawable/ic_btn_round_more_normal.png)bin968 -> 968 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_search.png (renamed from core/res/res/drawable/ic_btn_search.png)bin1390 -> 1390 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_speak_now.png (renamed from core/res/res/drawable/ic_btn_speak_now.png)bin954 -> 954 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_disabled.png (renamed from core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_disabled.png)bin456 -> 456 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_normal.png (renamed from core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_normal.png)bin820 -> 820 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_disabled.png (renamed from core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_disabled.png)bin461 -> 461 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_normal.png (renamed from core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_normal.png)bin839 -> 839 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_bullet_key_permission.png (renamed from core/res/res/drawable/ic_bullet_key_permission.png)bin597 -> 597 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_contact_picture.png (renamed from core/res/res/drawable/ic_contact_picture.png)bin1719 -> 1719 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_delete.png (renamed from core/res/res/drawable/ic_delete.png)bin3440 -> 3440 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_dialog_alert.png (renamed from core/res/res/drawable/ic_dialog_alert.png)bin3645 -> 3645 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_dialog_dialer.png (renamed from core/res/res/drawable/ic_dialog_dialer.png)bin3529 -> 3529 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_dialog_email.png (renamed from core/res/res/drawable/ic_dialog_email.png)bin3726 -> 3726 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_dialog_info.png (renamed from core/res/res/drawable/ic_dialog_info.png)bin3593 -> 3593 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_dialog_map.png (renamed from core/res/res/drawable/ic_dialog_map.png)bin3862 -> 3862 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_dialog_menu_generic.png (renamed from core/res/res/drawable/ic_dialog_menu_generic.png)bin1187 -> 1187 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_dialog_time.png (renamed from core/res/res/drawable/ic_dialog_time.png)bin1490 -> 1490 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_dialog_usb.png (renamed from core/res/res/drawable/ic_dialog_usb.png)bin938 -> 938 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_emergency.png (renamed from core/res/res/drawable/ic_emergency.png)bin605 -> 605 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_input_add.png (renamed from core/res/res/drawable/ic_input_add.png)bin3180 -> 3180 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_input_delete.png (renamed from core/res/res/drawable/ic_input_delete.png)bin3769 -> 3769 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_input_get.png (renamed from core/res/res/drawable/ic_input_get.png)bin995 -> 995 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_launcher_android.png (renamed from core/res/res/drawable/ic_launcher_android.png)bin3019 -> 3019 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_lock_airplane_mode.png (renamed from core/res/res/drawable/ic_lock_airplane_mode.png)bin1119 -> 1119 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_lock_airplane_mode_off.png (renamed from core/res/res/drawable/ic_lock_airplane_mode_off.png)bin1570 -> 1570 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_idle_alarm.png (renamed from core/res/res/drawable/ic_lock_idle_alarm.png)bin640 -> 640 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_lock_idle_charging.png (renamed from core/res/res/drawable/ic_lock_idle_charging.png)bin599 -> 599 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_lock_idle_lock.png (renamed from core/res/res/drawable/ic_lock_idle_lock.png)bin547 -> 547 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_lock_idle_low_battery.png (renamed from core/res/res/drawable/ic_lock_idle_low_battery.png)bin665 -> 665 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_lock.png (renamed from core/res/res/drawable/ic_lock_lock.png)bin1303 -> 1303 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_power_off.png (renamed from core/res/res/drawable/ic_lock_power_off.png)bin1709 -> 1709 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_silent_mode.png (renamed from core/res/res/drawable/ic_lock_silent_mode.png)bin1362 -> 1362 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png (renamed from core/res/res/drawable/ic_lock_silent_mode_off.png)bin1554 -> 1554 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_maps_indicator_current_position.png (renamed from core/res/res/drawable/ic_maps_indicator_current_position.png)bin1205 -> 1205 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim1.png (renamed from core/res/res/drawable/ic_maps_indicator_current_position_anim1.png)bin1301 -> 1301 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim2.png (renamed from core/res/res/drawable/ic_maps_indicator_current_position_anim2.png)bin1300 -> 1300 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim3.png (renamed from core/res/res/drawable/ic_maps_indicator_current_position_anim3.png)bin1201 -> 1201 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_ff.png (renamed from core/res/res/drawable/ic_media_ff.png)bin1159 -> 1159 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_next.png (renamed from core/res/res/drawable/ic_media_next.png)bin1309 -> 1309 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_pause.png (renamed from core/res/res/drawable/ic_media_pause.png)bin512 -> 512 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_play.png (renamed from core/res/res/drawable/ic_media_play.png)bin919 -> 919 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_previous.png (renamed from core/res/res/drawable/ic_media_previous.png)bin1295 -> 1295 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_media_rew.png (renamed from core/res/res/drawable/ic_media_rew.png)bin1192 -> 1192 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_account_list.png (renamed from core/res/res/drawable/ic_menu_account_list.png)bin2394 -> 2394 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_add.png (renamed from core/res/res/drawable/ic_menu_add.png)bin2017 -> 2017 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_agenda.png (renamed from core/res/res/drawable/ic_menu_agenda.png)bin4805 -> 4805 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_allfriends.png (renamed from core/res/res/drawable/ic_menu_allfriends.png)bin2257 -> 2257 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png (renamed from core/res/res/drawable/ic_menu_always_landscape_portrait.png)bin1658 -> 1658 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_archive.png (renamed from core/res/res/drawable/ic_menu_archive.png)bin1354 -> 1354 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_attachment.png (renamed from core/res/res/drawable/ic_menu_attachment.png)bin2247 -> 2247 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_back.png (renamed from core/res/res/drawable/ic_menu_back.png)bin1237 -> 1237 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_block.png (renamed from core/res/res/drawable/ic_menu_block.png)bin2336 -> 2336 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_blocked_user.png (renamed from core/res/res/drawable/ic_menu_blocked_user.png)bin2408 -> 2408 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_call.png (renamed from core/res/res/drawable/ic_menu_call.png)bin1755 -> 1755 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_camera.png (renamed from core/res/res/drawable/ic_menu_camera.png)bin1971 -> 1971 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_cc.png (renamed from core/res/res/drawable/ic_menu_cc.png)bin2046 -> 2046 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png (renamed from core/res/res/drawable/ic_menu_chat_dashboard.png)bin1865 -> 1865 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_clear_playlist.png (renamed from core/res/res/drawable/ic_menu_clear_playlist.png)bin2281 -> 2281 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png (renamed from core/res/res/drawable/ic_menu_close_clear_cancel.png)bin5306 -> 5306 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_compass.png (renamed from core/res/res/drawable/ic_menu_compass.png)bin3943 -> 3943 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_compose.png (renamed from core/res/res/drawable/ic_menu_compose.png)bin2014 -> 2014 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_crop.png (renamed from core/res/res/drawable/ic_menu_crop.png)bin1743 -> 1743 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_day.png (renamed from core/res/res/drawable/ic_menu_day.png)bin3924 -> 3924 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_delete.png (renamed from core/res/res/drawable/ic_menu_delete.png)bin1747 -> 1747 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_directions.png (renamed from core/res/res/drawable/ic_menu_directions.png)bin4383 -> 4383 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_edit.png (renamed from core/res/res/drawable/ic_menu_edit.png)bin1661 -> 1661 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_emoticons.png (renamed from core/res/res/drawable/ic_menu_emoticons.png)bin2620 -> 2620 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_end_conversation.png (renamed from core/res/res/drawable/ic_menu_end_conversation.png)bin1932 -> 1932 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_forward.png (renamed from core/res/res/drawable/ic_menu_forward.png)bin1228 -> 1228 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_friendslist.png (renamed from core/res/res/drawable/ic_menu_friendslist.png)bin1561 -> 1561 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_gallery.png (renamed from core/res/res/drawable/ic_menu_gallery.png)bin2379 -> 2379 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_goto.png (renamed from core/res/res/drawable/ic_menu_goto.png)bin1636 -> 1636 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_help.png (renamed from core/res/res/drawable/ic_menu_help.png)bin5304 -> 5304 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_home.png (renamed from core/res/res/drawable/ic_menu_home.png)bin2048 -> 2048 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_info_details.png (renamed from core/res/res/drawable/ic_menu_info_details.png)bin2128 -> 2128 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_invite.png (renamed from core/res/res/drawable/ic_menu_invite.png)bin2349 -> 2349 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_login.png (renamed from core/res/res/drawable/ic_menu_login.png)bin2466 -> 2466 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_manage.png (renamed from core/res/res/drawable/ic_menu_manage.png)bin5082 -> 5082 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_mapmode.png (renamed from core/res/res/drawable/ic_menu_mapmode.png)bin1923 -> 1923 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_mark.png (renamed from core/res/res/drawable/ic_menu_mark.png)bin2519 -> 2519 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_month.png (renamed from core/res/res/drawable/ic_menu_month.png)bin5123 -> 5123 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_more.png (renamed from core/res/res/drawable/ic_menu_more.png)bin5223 -> 5223 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_my_calendar.png (renamed from core/res/res/drawable/ic_menu_my_calendar.png)bin4469 -> 4469 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_mylocation.png (renamed from core/res/res/drawable/ic_menu_mylocation.png)bin5307 -> 5307 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_myplaces.png (renamed from core/res/res/drawable/ic_menu_myplaces.png)bin2011 -> 2011 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_notifications.png (renamed from core/res/res/drawable/ic_menu_notifications.png)bin1771 -> 1771 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_play_clip.png (renamed from core/res/res/drawable/ic_menu_play_clip.png)bin1471 -> 1471 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_preferences.png (renamed from core/res/res/drawable/ic_menu_preferences.png)bin2144 -> 2144 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_recent_history.png (renamed from core/res/res/drawable/ic_menu_recent_history.png)bin2647 -> 2647 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_refresh.png (renamed from core/res/res/drawable/ic_menu_refresh.png)bin2450 -> 2450 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_report_image.png (renamed from core/res/res/drawable/ic_menu_report_image.png)bin1996 -> 1996 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_revert.png (renamed from core/res/res/drawable/ic_menu_revert.png)bin1731 -> 1731 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_rotate.png (renamed from core/res/res/drawable/ic_menu_rotate.png)bin2477 -> 2477 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_save.png (renamed from core/res/res/drawable/ic_menu_save.png)bin1645 -> 1645 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_search.png (renamed from core/res/res/drawable/ic_menu_search.png)bin5059 -> 5059 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_send.png (renamed from core/res/res/drawable/ic_menu_send.png)bin1966 -> 1966 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_set_as.png (renamed from core/res/res/drawable/ic_menu_set_as.png)bin1828 -> 1828 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_share.png (renamed from core/res/res/drawable/ic_menu_share.png)bin2194 -> 2194 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_slideshow.png (renamed from core/res/res/drawable/ic_menu_slideshow.png)bin2392 -> 2392 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png (renamed from core/res/res/drawable/ic_menu_sort_alphabetically.png)bin2077 -> 2077 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_sort_by_size.png (renamed from core/res/res/drawable/ic_menu_sort_by_size.png)bin1067 -> 1067 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_star.png (renamed from core/res/res/drawable/ic_menu_star.png)bin1608 -> 1608 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_start_conversation.png (renamed from core/res/res/drawable/ic_menu_start_conversation.png)bin1715 -> 1715 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_stop.png (renamed from core/res/res/drawable/ic_menu_stop.png)bin1930 -> 1930 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_today.png (renamed from core/res/res/drawable/ic_menu_today.png)bin4450 -> 4450 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_upload.png (renamed from core/res/res/drawable/ic_menu_upload.png)bin1571 -> 1571 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_upload_you_tube.png (renamed from core/res/res/drawable/ic_menu_upload_you_tube.png)bin2384 -> 2384 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_view.png (renamed from core/res/res/drawable/ic_menu_view.png)bin1929 -> 1929 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_menu_week.png (renamed from core/res/res/drawable/ic_menu_week.png)bin4202 -> 4202 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_menu_zoom.png (renamed from core/res/res/drawable/ic_menu_zoom.png)bin2290 -> 2290 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_clear_all.png (renamed from core/res/res/drawable/ic_notification_clear_all.png)bin1558 -> 1558 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_notification_overlay.9.png (renamed from core/res/res/drawable/ic_notification_overlay.9.png)bin3201 -> 3201 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_partial_secure.png (renamed from core/res/res/drawable/ic_partial_secure.png)bin315 -> 315 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_disk_full.png (renamed from core/res/res/drawable/ic_popup_disk_full.png)bin4135 -> 4135 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_popup_reminder.png (renamed from core/res/res/drawable/ic_popup_reminder.png)bin1288 -> 1288 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_1.png (renamed from core/res/res/drawable/ic_popup_sync_1.png)bin4001 -> 4001 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_2.png (renamed from core/res/res/drawable/ic_popup_sync_2.png)bin4065 -> 4065 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_3.png (renamed from core/res/res/drawable/ic_popup_sync_3.png)bin4029 -> 4029 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_4.png (renamed from core/res/res/drawable/ic_popup_sync_4.png)bin4019 -> 4019 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_5.png (renamed from core/res/res/drawable/ic_popup_sync_5.png)bin4144 -> 4144 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_popup_sync_6.png (renamed from core/res/res/drawable/ic_popup_sync_6.png)bin3956 -> 3956 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_search_category_default.png (renamed from core/res/res/drawable/ic_search_category_default.png)bin1747 -> 1747 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_secure.png (renamed from core/res/res/drawable/ic_secure.png)bin398 -> 398 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_text_dot.png (renamed from core/res/res/drawable/ic_text_dot.png)bin476 -> 476 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_vibrate.png (renamed from core/res/res/drawable/ic_vibrate.png)bin4103 -> 4103 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_volume.png (renamed from core/res/res/drawable/ic_volume.png)bin2669 -> 2669 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_volume_bluetooth_ad2p.png (renamed from core/res/res/drawable/ic_volume_bluetooth_ad2p.png)bin2996 -> 2996 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_volume_bluetooth_in_call.png (renamed from core/res/res/drawable/ic_volume_bluetooth_in_call.png)bin2172 -> 2172 bytes
-rw-r--r--core/res/res/drawable-mdpi/ic_volume_off.png (renamed from core/res/res/drawable/ic_volume_off.png)bin2462 -> 2462 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_volume_off_small.png (renamed from core/res/res/drawable/ic_volume_off_small.png)bin832 -> 832 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/ic_volume_small.png (renamed from core/res/res/drawable/ic_volume_small.png)bin866 -> 866 bytes
-rw-r--r--core/res/res/drawable-mdpi/icon_highlight_rectangle.9.png (renamed from core/res/res/drawable/icon_highlight_rectangle.9.png)bin1022 -> 1022 bytes
-rw-r--r--core/res/res/drawable-mdpi/icon_highlight_square.9.png (renamed from core/res/res/drawable/icon_highlight_square.9.png)bin1408 -> 1408 bytes
-rw-r--r--core/res/res/drawable-mdpi/ime_qwerty.png (renamed from core/res/res/drawable/ime_qwerty.png)bin3052 -> 3052 bytes
-rw-r--r--core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png (renamed from core/res/res/drawable/indicator_code_lock_drag_direction_green_up.png)bin268 -> 268 bytes
-rw-r--r--core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png (renamed from core/res/res/drawable/indicator_code_lock_drag_direction_red_up.png)bin315 -> 315 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png (renamed from core/res/res/drawable/indicator_code_lock_point_area_default.png)bin3939 -> 3939 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png (renamed from core/res/res/drawable/indicator_code_lock_point_area_green.png)bin4201 -> 4201 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png (renamed from core/res/res/drawable/indicator_code_lock_point_area_red.png)bin4462 -> 4462 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/indicator_input_error.png (renamed from core/res/res/drawable/indicator_input_error.png)bin884 -> 884 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_accessory_bg_landscape.9.png (renamed from core/res/res/drawable/keyboard_accessory_bg_landscape.9.png)bin197 -> 197 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_background.9.png (renamed from core/res/res/drawable/keyboard_background.9.png)bin189 -> 189 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_key_feedback_background.9.png (renamed from core/res/res/drawable/keyboard_key_feedback_background.9.png)bin1182 -> 1182 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png (renamed from core/res/res/drawable/keyboard_key_feedback_more_background.9.png)bin1385 -> 1385 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_popup_panel_background.9.png (renamed from core/res/res/drawable/keyboard_popup_panel_background.9.png)bin996 -> 996 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.pngbin0 -> 3734 bytes
-rw-r--r--core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png (renamed from core/res/res/drawable/keyboard_textfield_selected.9.png)bin782 -> 782 bytes
-rw-r--r--core/res/res/drawable-mdpi/light_header.9.pngbin0 -> 162 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_disabled.9.png (renamed from core/res/res/drawable/list_selector_background_disabled.9.png)bin1252 -> 1252 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_focus.9.png (renamed from core/res/res/drawable/list_selector_background_focus.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_longpress.9.png (renamed from core/res/res/drawable/list_selector_background_longpress.9.png)bin3017 -> 3017 bytes
-rw-r--r--core/res/res/drawable-mdpi/list_selector_background_pressed.9.png (renamed from core/res/res/drawable/list_selector_background_pressed.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/loading_tile.png (renamed from core/res/res/drawable/loading_tile.png)bin729 -> 729 bytes
-rw-r--r--core/res/res/drawable-mdpi/maps_google_logo.png (renamed from core/res/res/drawable/maps_google_logo.png)bin2776 -> 2776 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_background.9.png (renamed from core/res/res/drawable/menu_background.9.png)bin3390 -> 3390 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png (renamed from core/res/res/drawable/menu_background_fill_parent_width.9.png)bin2969 -> 2969 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_separator.9.png (renamed from core/res/res/drawable/menu_separator.9.png)bin2823 -> 2823 bytes
-rw-r--r--core/res/res/drawable-mdpi/menu_submenu_background.9.png (renamed from core/res/res/drawable/menu_submenu_background.9.png)bin4394 -> 4394 bytes
-rw-r--r--core/res/res/drawable-mdpi/menuitem_background_focus.9.png (renamed from core/res/res/drawable/menuitem_background_focus.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/menuitem_background_pressed.9.png (renamed from core/res/res/drawable/menuitem_background_pressed.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/menuitem_background_solid_focused.9.pngbin0 -> 165 bytes
-rw-r--r--core/res/res/drawable-mdpi/menuitem_background_solid_pressed.9.pngbin0 -> 165 bytes
-rw-r--r--core/res/res/drawable-mdpi/menuitem_checkbox_on.png (renamed from core/res/res/drawable/menuitem_checkbox_on.png)bin2920 -> 2920 bytes
-rw-r--r--core/res/res/drawable-mdpi/no_tile_128.png (renamed from core/res/res/drawable/no_tile_128.png)bin1392 -> 1392 bytes
-rw-r--r--core/res/res/drawable-mdpi/panel_background.9.png (renamed from core/res/res/drawable/panel_background.9.png)bin1332 -> 1332 bytes
-rw-r--r--core/res/res/drawable-mdpi/panel_picture_frame_bg_focus_blue.9.png (renamed from core/res/res/drawable/panel_picture_frame_bg_focus_blue.9.png)bin5311 -> 5311 bytes
-rw-r--r--core/res/res/drawable-mdpi/panel_picture_frame_bg_normal.9.png (renamed from core/res/res/drawable/panel_picture_frame_bg_normal.9.png)bin3532 -> 3532 bytes
-rw-r--r--core/res/res/drawable-mdpi/panel_picture_frame_bg_pressed_blue.9.png (renamed from core/res/res/drawable/panel_picture_frame_bg_pressed_blue.9.png)bin5111 -> 5111 bytes
-rw-r--r--core/res/res/drawable-mdpi/pickerbox_background.png (renamed from core/res/res/drawable/pickerbox_background.png)bin4226 -> 4226 bytes
-rw-r--r--core/res/res/drawable-mdpi/pickerbox_selected.9.png (renamed from core/res/res/drawable/pickerbox_selected.9.png)bin2155 -> 2155 bytes
-rw-r--r--core/res/res/drawable-mdpi/pickerbox_unselected.9.png (renamed from core/res/res/drawable/pickerbox_unselected.9.png)bin1474 -> 1474 bytes
-rw-r--r--core/res/res/drawable-mdpi/picture_emergency.png (renamed from core/res/res/drawable/picture_emergency.png)bin8339 -> 8339 bytes
-rw-r--r--core/res/res/drawable-mdpi/picture_frame.9.png (renamed from core/res/res/drawable/picture_frame.9.png)bin547 -> 547 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_bottom_bright.9.png (renamed from core/res/res/drawable/popup_bottom_bright.9.png)bin881 -> 881 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_bottom_dark.9.png (renamed from core/res/res/drawable/popup_bottom_dark.9.png)bin975 -> 975 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/popup_bottom_medium.9.png (renamed from core/res/res/drawable/popup_bottom_medium.9.png)bin960 -> 960 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_center_bright.9.png (renamed from core/res/res/drawable/popup_center_bright.9.png)bin196 -> 196 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_center_dark.9.png (renamed from core/res/res/drawable/popup_center_dark.9.png)bin209 -> 209 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/popup_center_medium.9.png (renamed from core/res/res/drawable/popup_center_medium.9.png)bin148 -> 148 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_full_bright.9.png (renamed from core/res/res/drawable/popup_full_bright.9.png)bin1251 -> 1251 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_full_dark.9.png (renamed from core/res/res/drawable/popup_full_dark.9.png)bin1332 -> 1332 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/popup_inline_error.9.png (renamed from core/res/res/drawable/popup_inline_error.9.png)bin1779 -> 1779 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_inline_error_above.9.png (renamed from core/res/res/drawable/popup_inline_error_above.9.png)bin2043 -> 2043 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_top_bright.9.png (renamed from core/res/res/drawable/popup_top_bright.9.png)bin701 -> 701 bytes
-rw-r--r--core/res/res/drawable-mdpi/popup_top_dark.9.png (renamed from core/res/res/drawable/popup_top_dark.9.png)bin815 -> 815 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_away.png (renamed from core/res/res/drawable/presence_away.png)bin810 -> 810 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_busy.png (renamed from core/res/res/drawable/presence_busy.png)bin811 -> 811 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_invisible.png (renamed from core/res/res/drawable/presence_invisible.png)bin693 -> 693 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_offline.png (renamed from core/res/res/drawable/presence_offline.png)bin767 -> 767 bytes
-rw-r--r--core/res/res/drawable-mdpi/presence_online.png (renamed from core/res/res/drawable/presence_online.png)bin812 -> 812 bytes
-rw-r--r--core/res/res/drawable-mdpi/pressed_application_background_static.png (renamed from core/res/res/drawable/pressed_application_background_static.png)bin3902 -> 3902 bytes
-rw-r--r--core/res/res/drawable-mdpi/progressbar_indeterminate1.png (renamed from core/res/res/drawable/progressbar_indeterminate1.png)bin3678 -> 3678 bytes
-rw-r--r--core/res/res/drawable-mdpi/progressbar_indeterminate2.png (renamed from core/res/res/drawable/progressbar_indeterminate2.png)bin3704 -> 3704 bytes
-rw-r--r--core/res/res/drawable-mdpi/progressbar_indeterminate3.png (renamed from core/res/res/drawable/progressbar_indeterminate3.png)bin3752 -> 3752 bytes
-rw-r--r--core/res/res/drawable-mdpi/radiobutton_off_background.png (renamed from core/res/res/drawable/radiobutton_off_background.png)bin3335 -> 3335 bytes
-rw-r--r--core/res/res/drawable-mdpi/radiobutton_on_background.png (renamed from core/res/res/drawable/radiobutton_on_background.png)bin3468 -> 3468 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_big_half.png (renamed from core/res/res/drawable/rate_star_big_half.png)bin818 -> 818 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_big_off.png (renamed from core/res/res/drawable/rate_star_big_off.png)bin527 -> 527 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_big_on.png (renamed from core/res/res/drawable/rate_star_big_on.png)bin992 -> 992 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_small_half.png (renamed from core/res/res/drawable/rate_star_small_half.png)bin540 -> 540 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_small_off.png (renamed from core/res/res/drawable/rate_star_small_off.png)bin447 -> 447 bytes
-rw-r--r--core/res/res/drawable-mdpi/rate_star_small_on.png (renamed from core/res/res/drawable/rate_star_small_on.png)bin570 -> 570 bytes
-rw-r--r--core/res/res/drawable-mdpi/reticle.png (renamed from core/res/res/drawable/reticle.png)bin3226 -> 3226 bytes
-rw-r--r--core/res/res/drawable-mdpi/screen_progress_frame.9.png (renamed from core/res/res/drawable/screen_progress_frame.9.png)bin322 -> 322 bytes
-rw-r--r--core/res/res/drawable-mdpi/screen_progress_inner.9.png (renamed from core/res/res/drawable/screen_progress_inner.9.png)bin185 -> 185 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png (renamed from core/res/res/drawable/scrollbar_handle_accelerated_anim2.9.png)bin1144 -> 1144 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/scrollbar_handle_horizontal.9.png (renamed from core/res/res/drawable/scrollbar_handle_horizontal.9.png)bin266 -> 266 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/scrollbar_handle_vertical.9.png (renamed from core/res/res/drawable/scrollbar_handle_vertical.9.png)bin254 -> 254 bytes
-rw-r--r--core/res/res/drawable-mdpi/search_dropdown_background.9.png (renamed from core/res/res/drawable/search_dropdown_background.9.png)bin3058 -> 3058 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/search_plate.9.png (renamed from core/res/res/drawable/search_plate.9.png)bin2943 -> 2943 bytes
-rw-r--r--core/res/res/drawable-mdpi/search_plate_global.9.png (renamed from core/res/res/drawable/search_plate_global.9.png)bin293 -> 293 bytes
-rw-r--r--core/res/res/drawable-mdpi/seek_thumb_normal.png (renamed from core/res/res/drawable/seek_thumb_normal.png)bin880 -> 880 bytes
-rw-r--r--core/res/res/drawable-mdpi/seek_thumb_pressed.png (renamed from core/res/res/drawable/seek_thumb_pressed.png)bin1073 -> 1073 bytes
-rw-r--r--core/res/res/drawable-mdpi/seek_thumb_selected.png (renamed from core/res/res/drawable/seek_thumb_selected.png)bin1090 -> 1090 bytes
-rw-r--r--core/res/res/drawable-mdpi/settings_header_raw.9.png (renamed from core/res/res/drawable/settings_header_raw.9.png)bin285 -> 285 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_black_16.png (renamed from core/res/res/drawable/spinner_black_16.png)bin291 -> 291 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/spinner_black_20.png (renamed from core/res/res/drawable/spinner_black_20.png)bin523 -> 523 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_black_48.png (renamed from core/res/res/drawable/spinner_black_48.png)bin1022 -> 1022 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_black_76.png (renamed from core/res/res/drawable/spinner_black_76.png)bin1086 -> 1086 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_dropdown_background_down.9.png (renamed from core/res/res/drawable/spinner_dropdown_background_down.9.png)bin350 -> 350 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_dropdown_background_up.9.png (renamed from core/res/res/drawable/spinner_dropdown_background_up.9.png)bin347 -> 347 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_normal.9.png (renamed from core/res/res/drawable/spinner_normal.9.png)bin1135 -> 1135 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_press.9.png (renamed from core/res/res/drawable/spinner_press.9.png)bin1778 -> 1778 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_select.9.png (renamed from core/res/res/drawable/spinner_select.9.png)bin1755 -> 1755 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_white_16.png (renamed from core/res/res/drawable/spinner_white_16.png)bin2968 -> 2968 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_white_48.png (renamed from core/res/res/drawable/spinner_white_48.png)bin782 -> 782 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinner_white_76.png (renamed from core/res/res/drawable/spinner_white_76.png)bin3745 -> 3745 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png (renamed from core/res/res/drawable/spinnerbox_arrow_first.9.png)bin3053 -> 3053 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png (renamed from core/res/res/drawable/spinnerbox_arrow_last.9.png)bin3052 -> 3052 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png (renamed from core/res/res/drawable/spinnerbox_arrow_middle.9.png)bin3020 -> 3020 bytes
-rw-r--r--core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png (renamed from core/res/res/drawable/spinnerbox_arrow_single.9.png)bin3037 -> 3037 bytes
-rw-r--r--core/res/res/drawable-mdpi/star_big_off.png (renamed from core/res/res/drawable/star_big_off.png)bin1508 -> 1508 bytes
-rw-r--r--core/res/res/drawable-mdpi/star_big_on.png (renamed from core/res/res/drawable/star_big_on.png)bin1734 -> 1734 bytes
-rw-r--r--core/res/res/drawable-mdpi/star_off.png (renamed from core/res/res/drawable/star_off.png)bin594 -> 594 bytes
-rw-r--r--core/res/res/drawable-mdpi/star_on.png (renamed from core/res/res/drawable/star_on.png)bin1231 -> 1231 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_ecb_mode.png (renamed from core/res/res/drawable/stat_ecb_mode.png)bin625 -> 625 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_alarm.png (renamed from core/res/res/drawable/stat_notify_alarm.png)bin1035 -> 1035 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_call_mute.png (renamed from core/res/res/drawable/stat_notify_call_mute.png)bin788 -> 788 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_chat.png (renamed from core/res/res/drawable/stat_notify_chat.png)bin806 -> 806 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_notify_disk_full.png (renamed from core/res/res/drawable/stat_notify_disk_full.png)bin842 -> 842 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_error.png (renamed from core/res/res/drawable/stat_notify_error.png)bin704 -> 704 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_missed_call.png (renamed from core/res/res/drawable/stat_notify_missed_call.png)bin875 -> 875 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_more.png (renamed from core/res/res/drawable/stat_notify_more.png)bin786 -> 786 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sdcard.png (renamed from core/res/res/drawable/stat_notify_sdcard.png)bin369 -> 369 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png (renamed from core/res/res/drawable/stat_notify_sdcard_usb.png)bin430 -> 430 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_notify_sim_toolkit.png (renamed from core/res/res/drawable/stat_notify_sim_toolkit.png)bin1063 -> 1063 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sync.png (renamed from core/res/res/drawable/stat_notify_sync.png)bin1076 -> 1076 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sync_anim0.png (renamed from core/res/res/drawable/stat_notify_sync_anim0.png)bin1076 -> 1076 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_sync_error.png (renamed from core/res/res/drawable/stat_notify_sync_error.png)bin1146 -> 1146 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_voicemail.png (renamed from core/res/res/drawable/stat_notify_voicemail.png)bin655 -> 655 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png (renamed from core/res/res/drawable/stat_notify_wifi_in_range.png)bin1075 -> 1075 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_0.png (renamed from core/res/res/drawable/stat_sys_battery_0.png)bin1034 -> 1034 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_battery_10.png (renamed from core/res/res/drawable/stat_sys_battery_10.png)bin738 -> 738 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_100.png (renamed from core/res/res/drawable/stat_sys_battery_100.png)bin738 -> 738 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_20.png (renamed from core/res/res/drawable/stat_sys_battery_20.png)bin746 -> 746 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_40.png (renamed from core/res/res/drawable/stat_sys_battery_40.png)bin769 -> 769 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_60.png (renamed from core/res/res/drawable/stat_sys_battery_60.png)bin762 -> 762 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_80.png (renamed from core/res/res/drawable/stat_sys_battery_80.png)bin724 -> 724 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim0.png)bin854 -> 854 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim1.png)bin864 -> 864 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim2.png)bin875 -> 875 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim3.png)bin888 -> 888 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim4.png)bin879 -> 879 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png (renamed from core/res/res/drawable/stat_sys_battery_charge_anim5.png)bin868 -> 868 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_battery_unknown.png (renamed from core/res/res/drawable/stat_sys_battery_unknown.png)bin895 -> 895 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png (renamed from core/res/res/drawable/stat_sys_data_bluetooth.png)bin818 -> 818 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png (renamed from core/res/res/drawable/stat_sys_data_bluetooth_connected.png)bin967 -> 967 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_connected_1x.pngbin0 -> 892 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png (renamed from core/res/res/drawable/stat_sys_data_connected_3g.png)bin832 -> 832 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_connected_e.png (renamed from core/res/res/drawable/stat_sys_data_connected_e.png)bin833 -> 833 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_connected_g.png (renamed from core/res/res/drawable/stat_sys_data_connected_g.png)bin838 -> 838 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_in_1x.pngbin0 -> 808 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_in_3g.png (renamed from core/res/res/drawable/stat_sys_data_in_3g.png)bin757 -> 757 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_in_e.png (renamed from core/res/res/drawable/stat_sys_data_in_e.png)bin719 -> 719 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_in_g.png (renamed from core/res/res/drawable/stat_sys_data_in_g.png)bin724 -> 724 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.pngbin0 -> 770 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png (renamed from core/res/res/drawable/stat_sys_data_inandout_3g.png)bin709 -> 709 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png (renamed from core/res/res/drawable/stat_sys_data_inandout_e.png)bin682 -> 682 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png (renamed from core/res/res/drawable/stat_sys_data_inandout_g.png)bin683 -> 683 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_out_1x.pngbin0 -> 820 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_out_3g.png (renamed from core/res/res/drawable/stat_sys_data_out_3g.png)bin768 -> 768 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_out_e.png (renamed from core/res/res/drawable/stat_sys_data_out_e.png)bin735 -> 735 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_out_g.png (renamed from core/res/res/drawable/stat_sys_data_out_g.png)bin739 -> 739 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_data_usb.png (renamed from core/res/res/drawable/stat_sys_data_usb.png)bin786 -> 786 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim0.png (renamed from core/res/res/drawable/stat_sys_download_anim0.png)bin645 -> 645 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim1.png (renamed from core/res/res/drawable/stat_sys_download_anim1.png)bin645 -> 645 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim2.png (renamed from core/res/res/drawable/stat_sys_download_anim2.png)bin653 -> 653 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim3.png (renamed from core/res/res/drawable/stat_sys_download_anim3.png)bin659 -> 659 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim4.png (renamed from core/res/res/drawable/stat_sys_download_anim4.png)bin645 -> 645 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_download_anim5.png (renamed from core/res/res/drawable/stat_sys_download_anim5.png)bin626 -> 626 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png (renamed from core/res/res/drawable/stat_sys_gps_acquiring.png)bin595 -> 595 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_gps_on.png (renamed from core/res/res/drawable/stat_sys_gps_on.png)bin1035 -> 1035 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_headset.png (renamed from core/res/res/drawable/stat_sys_headset.png)bin408 -> 408 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_no_sim.png (renamed from core/res/res/drawable/stat_sys_no_sim.png)bin809 -> 809 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_phone_call.png (renamed from core/res/res/drawable/stat_sys_phone_call.png)bin773 -> 773 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png (renamed from core/res/res/drawable/stat_sys_phone_call_bluetooth.png)bin815 -> 815 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_phone_call_forward.png (renamed from core/res/res/drawable/stat_sys_phone_call_forward.png)bin835 -> 835 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png (renamed from core/res/res/drawable/stat_sys_phone_call_on_hold.png)bin754 -> 754 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_0.png (renamed from core/res/res/drawable/stat_sys_r_signal_0.png)bin802 -> 802 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png (renamed from core/res/res/drawable/stat_sys_r_signal_0_cdma.png)bin743 -> 743 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_1.png (renamed from core/res/res/drawable/stat_sys_r_signal_1.png)bin818 -> 818 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png (renamed from core/res/res/drawable/stat_sys_r_signal_1_cdma.png)bin743 -> 743 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_2.png (renamed from core/res/res/drawable/stat_sys_r_signal_2.png)bin802 -> 802 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png (renamed from core/res/res/drawable/stat_sys_r_signal_2_cdma.png)bin736 -> 736 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_3.png (renamed from core/res/res/drawable/stat_sys_r_signal_3.png)bin798 -> 798 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png (renamed from core/res/res/drawable/stat_sys_r_signal_3_cdma.png)bin730 -> 730 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_4.png (renamed from core/res/res/drawable/stat_sys_r_signal_4.png)bin726 -> 726 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png (renamed from core/res/res/drawable/stat_sys_r_signal_4_cdma.png)bin675 -> 675 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png (renamed from core/res/res/drawable/stat_sys_ra_signal_0_cdma.png)bin719 -> 719 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png (renamed from core/res/res/drawable/stat_sys_ra_signal_1_cdma.png)bin725 -> 725 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png (renamed from core/res/res/drawable/stat_sys_ra_signal_2_cdma.png)bin712 -> 712 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png (renamed from core/res/res/drawable/stat_sys_ra_signal_3_cdma.png)bin705 -> 705 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png (renamed from core/res/res/drawable/stat_sys_ra_signal_4_cdma.png)bin637 -> 637 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ringer_silent.png (renamed from core/res/res/drawable/stat_sys_ringer_silent.png)bin767 -> 767 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png (renamed from core/res/res/drawable/stat_sys_ringer_vibrate.png)bin1255 -> 1255 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png (renamed from core/res/res/drawable/stat_sys_roaming_cdma_0.png)bin377 -> 377 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png (renamed from core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png)bin150 -> 150 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png (renamed from core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png)bin377 -> 377 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_0.png (renamed from core/res/res/drawable/stat_sys_signal_0.png)bin587 -> 587 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_1.png (renamed from core/res/res/drawable/stat_sys_signal_1.png)bin597 -> 597 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_2.png (renamed from core/res/res/drawable/stat_sys_signal_2.png)bin595 -> 595 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_3.png (renamed from core/res/res/drawable/stat_sys_signal_3.png)bin594 -> 594 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_4.png (renamed from core/res/res/drawable/stat_sys_signal_4.png)bin532 -> 532 bytes
-rwxr-xr-x[-rw-r--r--]core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png (renamed from core/res/res/drawable/stat_sys_signal_flightmode.png)bin898 -> 898 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_signal_null.png (renamed from core/res/res/drawable/stat_sys_signal_null.png)bin730 -> 730 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_speakerphone.png (renamed from core/res/res/drawable/stat_sys_speakerphone.png)bin978 -> 978 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_tty_mode.png (renamed from core/res/res/drawable/stat_sys_tty_mode.png)bin725 -> 725 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim0.png (renamed from core/res/res/drawable/stat_sys_upload_anim0.png)bin657 -> 657 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim1.png (renamed from core/res/res/drawable/stat_sys_upload_anim1.png)bin653 -> 653 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim2.png (renamed from core/res/res/drawable/stat_sys_upload_anim2.png)bin666 -> 666 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim3.png (renamed from core/res/res/drawable/stat_sys_upload_anim3.png)bin659 -> 659 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim4.png (renamed from core/res/res/drawable/stat_sys_upload_anim4.png)bin641 -> 641 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/stat_sys_upload_anim5.png (renamed from core/res/res/drawable/stat_sys_upload_anim5.png)bin641 -> 641 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png (renamed from core/res/res/drawable/stat_sys_vp_phone_call.png)bin1161 -> 1161 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png (renamed from core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png)bin815 -> 815 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png (renamed from core/res/res/drawable/stat_sys_vp_phone_call_on_hold.png)bin1177 -> 1177 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_warning.png (renamed from core/res/res/drawable/stat_sys_warning.png)bin651 -> 651 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png (renamed from core/res/res/drawable/stat_sys_wifi_signal_0.png)bin743 -> 743 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png (renamed from core/res/res/drawable/stat_sys_wifi_signal_1.png)bin768 -> 768 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png (renamed from core/res/res/drawable/stat_sys_wifi_signal_2.png)bin785 -> 785 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png (renamed from core/res/res/drawable/stat_sys_wifi_signal_3.png)bin807 -> 807 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png (renamed from core/res/res/drawable/stat_sys_wifi_signal_4.png)bin826 -> 826 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_background.png (renamed from core/res/res/drawable/status_bar_background.png)bin3109 -> 3109 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_close_on.9.png (renamed from core/res/res/drawable/status_bar_close_on.9.png)bin646 -> 646 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_header_background.9.png (renamed from core/res/res/drawable/status_bar_header_background.9.png)bin2867 -> 2867 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png (renamed from core/res/res/drawable/status_bar_item_app_background_normal.9.png)bin3013 -> 3013 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png (renamed from core/res/res/drawable/status_bar_item_background_focus.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png (renamed from core/res/res/drawable/status_bar_item_background_normal.9.png)bin186 -> 186 bytes
-rw-r--r--core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png (renamed from core/res/res/drawable/status_bar_item_background_pressed.9.png)bin11006 -> 11006 bytes
-rw-r--r--core/res/res/drawable-mdpi/statusbar_background.png (renamed from core/res/res/drawable/statusbar_background.png)bin3160 -> 3160 bytes
-rw-r--r--core/res/res/drawable-mdpi/submenu_arrow_nofocus.png (renamed from core/res/res/drawable/submenu_arrow_nofocus.png)bin2878 -> 2878 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_action_add.png (renamed from core/res/res/drawable/sym_action_add.png)bin930 -> 930 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_action_call.png (renamed from core/res/res/drawable/sym_action_call.png)bin904 -> 904 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_action_chat.png (renamed from core/res/res/drawable/sym_action_chat.png)bin808 -> 808 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_action_email.png (renamed from core/res/res/drawable/sym_action_email.png)bin791 -> 791 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_call_incoming.png (renamed from core/res/res/drawable/sym_call_incoming.png)bin1200 -> 1200 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_call_missed.png (renamed from core/res/res/drawable/sym_call_missed.png)bin1234 -> 1234 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_call_outgoing.png (renamed from core/res/res/drawable/sym_call_outgoing.png)bin1206 -> 1206 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_contact_card.png (renamed from core/res/res/drawable/sym_contact_card.png)bin3032 -> 3032 bytes
-rw-r--r--core/res/res/drawable-mdpi/sym_def_app_icon.png (renamed from core/res/res/drawable/sym_def_app_icon.png)bin4499 -> 4499 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_focus.9.pngbin0 -> 280 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_focus_bar_left.9.pngbin0 -> 141 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_focus_bar_right.9.pngbin0 -> 141 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_press.9.pngbin0 -> 271 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_press_bar_left.9.pngbin0 -> 141 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_press_bar_right.9.pngbin0 -> 141 bytes
-rw-r--r--core/res/res/drawable-mdpi/tab_selected.9.pngbin0 -> 287 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_selected_bar_left.9.pngbin0 -> 143 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/tab_selected_bar_right.9.pngbin0 -> 143 bytes
-rw-r--r--core/res/res/drawable-mdpi/tab_unselected.9.pngbin0 -> 300 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_default.9.png (renamed from core/res/res/drawable/textfield_default.9.png)bin758 -> 758 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_disabled.9.png (renamed from core/res/res/drawable/textfield_disabled.9.png)bin545 -> 545 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_disabled_selected.9.png (renamed from core/res/res/drawable/textfield_disabled_selected.9.png)bin570 -> 570 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_pressed.9.png (renamed from core/res/res/drawable/textfield_pressed.9.png)bin1040 -> 1040 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/textfield_search_default.9.png (renamed from core/res/res/drawable/textfield_search_default.9.png)bin3361 -> 3361 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_search_pressed.9.png (renamed from core/res/res/drawable/textfield_search_pressed.9.png)bin3586 -> 3586 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/textfield_search_selected.9.png (renamed from core/res/res/drawable/textfield_search_selected.9.png)bin3354 -> 3354 bytes
-rw-r--r--core/res/res/drawable-mdpi/textfield_selected.9.png (renamed from core/res/res/drawable/textfield_selected.9.png)bin790 -> 790 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_down_disabled.9.png (renamed from core/res/res/drawable/timepicker_down_disabled.9.png)bin422 -> 422 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_down_disabled_focused.9.png (renamed from core/res/res/drawable/timepicker_down_disabled_focused.9.png)bin580 -> 580 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_down_normal.9.png (renamed from core/res/res/drawable/timepicker_down_normal.9.png)bin795 -> 795 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_down_pressed.9.png (renamed from core/res/res/drawable/timepicker_down_pressed.9.png)bin1161 -> 1161 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_down_selected.9.png (renamed from core/res/res/drawable/timepicker_down_selected.9.png)bin1170 -> 1170 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_input_disabled.9.png (renamed from core/res/res/drawable/timepicker_input_disabled.9.png)bin280 -> 280 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_input_normal.9.png (renamed from core/res/res/drawable/timepicker_input_normal.9.png)bin582 -> 582 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_input_pressed.9.png (renamed from core/res/res/drawable/timepicker_input_pressed.9.png)bin604 -> 604 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_input_selected.9.png (renamed from core/res/res/drawable/timepicker_input_selected.9.png)bin517 -> 517 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_up_disabled.9.png (renamed from core/res/res/drawable/timepicker_up_disabled.9.png)bin491 -> 491 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_up_disabled_focused.9.png (renamed from core/res/res/drawable/timepicker_up_disabled_focused.9.png)bin728 -> 728 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_up_normal.9.png (renamed from core/res/res/drawable/timepicker_up_normal.9.png)bin989 -> 989 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_up_pressed.9.png (renamed from core/res/res/drawable/timepicker_up_pressed.9.png)bin1433 -> 1433 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/timepicker_up_selected.9.png (renamed from core/res/res/drawable/timepicker_up_selected.9.png)bin1428 -> 1428 bytes
-rw-r--r--core/res/res/drawable-mdpi/title_bar_portrait.9.png (renamed from core/res/res/drawable/title_bar_portrait.9.png)bin4121 -> 4121 bytes
-rw-r--r--core/res/res/drawable-mdpi/title_bar_shadow.9.png (renamed from core/res/res/drawable/title_bar_shadow.9.png)bin178 -> 178 bytes
-rw-r--r--core/res/res/drawable-mdpi/title_bar_tall.png (renamed from core/res/res/drawable/title_bar_tall.png)bin12764 -> 12764 bytes
-rwxr-xr-xcore/res/res/drawable-mdpi/toast_frame.9.png (renamed from core/res/res/drawable/toast_frame.9.png)bin4328 -> 4328 bytes
-rw-r--r--core/res/res/drawable-mdpi/unknown_image.png (renamed from core/res/res/drawable/unknown_image.png)bin208 -> 208 bytes
-rw-r--r--core/res/res/drawable-mdpi/zoom_plate.9.png (renamed from core/res/res/drawable/zoom_plate.9.png)bin2177 -> 2177 bytes
-rw-r--r--core/res/res/drawable/btn_close.xml4
-rw-r--r--core/res/res/drawable/btn_close_normal.pngbin1213 -> 0 bytes
-rw-r--r--core/res/res/drawable/btn_close_pressed.pngbin1585 -> 0 bytes
-rw-r--r--core/res/res/drawable/btn_keyboard_key_trans.xml28
-rw-r--r--core/res/res/drawable/contact_header_bg.9.pngbin0 -> 231 bytes
-rw-r--r--core/res/res/drawable/dark_header.9.pngbin179 -> 0 bytes
-rw-r--r--core/res/res/drawable/dark_header_dither.xml20
-rw-r--r--core/res/res/drawable/divider_horizontal_bright.9.pngbin240 -> 0 bytes
-rw-r--r--core/res/res/drawable/divider_horizontal_bright_opaque.9.pngbin2933 -> 0 bytes
-rw-r--r--core/res/res/drawable/divider_horizontal_dark.9.pngbin232 -> 0 bytes
-rw-r--r--core/res/res/drawable/divider_horizontal_dark_opaque.9.pngbin2941 -> 0 bytes
-rw-r--r--core/res/res/drawable/divider_vertical_bright.9.pngbin130 -> 0 bytes
-rw-r--r--core/res/res/drawable/divider_vertical_bright_opaque.9.pngbin0 -> 120 bytes
-rw-r--r--core/res/res/drawable/divider_vertical_dark.9.pngbin0 -> 121 bytes
-rw-r--r--core/res/res/drawable/divider_vertical_dark_opaque.9.pngbin0 -> 120 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_dark.xml28
-rw-r--r--core/res/res/drawable/fasttrack_badge_dark_normal.9.pngbin0 -> 624 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_dark_pressed.9.pngbin0 -> 846 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_light.xml28
-rw-r--r--core/res/res/drawable/fasttrack_badge_light_normal.9.pngbin0 -> 824 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_light_pressed.9.pngbin0 -> 831 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_middle.xml28
-rw-r--r--core/res/res/drawable/fasttrack_badge_middle_normal.9.pngbin0 -> 606 bytes
-rw-r--r--core/res/res/drawable/fasttrack_badge_middle_pressed.9.pngbin0 -> 818 bytes
-rw-r--r--core/res/res/drawable/ic_contact_picture_2.pngbin0 -> 1894 bytes
-rw-r--r--core/res/res/drawable/ic_contact_picture_3.pngbin0 -> 1364 bytes
-rw-r--r--core/res/res/drawable/light_header.9.pngbin183 -> 0 bytes
-rw-r--r--core/res/res/drawable/light_header_dither.xml20
-rw-r--r--core/res/res/drawable/stat_sys_data_connected_1xrtt.pngbin984 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_connected_evdo.pngbin995 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_connected_h.pngbin0 -> 642 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_data_dormant_1xrtt.pngbin1081 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_data_dormant_evdo.pngbin1008 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_in_1xrtt.pngbin948 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_in_evdo.pngbin967 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_in_h.pngbin0 -> 669 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_inandout_1xrtt.pngbin942 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_inandout_evdo.pngbin960 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_inandout_h.pngbin0 -> 655 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_out_1xrtt.pngbin942 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_out_evdo.pngbin963 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_data_out_h.pngbin0 -> 654 bytes
-rw-r--r--core/res/res/drawable/stat_sys_signal_0_cdma.pngbin701 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_signal_1_cdma.pngbin714 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_signal_2_cdma.pngbin706 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_signal_3_cdma.pngbin702 -> 0 bytes
-rw-r--r--core/res/res/drawable/stat_sys_signal_4_cdma.pngbin621 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_cdma_0.pngbin701 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_cdma_1.pngbin714 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_cdma_2.pngbin706 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_cdma_3.pngbin702 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_cdma_4.pngbin621 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_evdo_0.pngbin912 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_evdo_1.pngbin925 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_evdo_2.pngbin904 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_evdo_3.pngbin907 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/stat_sys_signal_evdo_4.pngbin823 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_focus.9.pngbin3326 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_focus_bar_left.9.pngbin2967 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_focus_bar_right.9.pngbin2949 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_press.9.pngbin3037 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_press_bar_left.9.pngbin2959 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_press_bar_right.9.pngbin2951 -> 0 bytes
-rw-r--r--core/res/res/drawable/tab_selected.9.pngbin657 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_selected_bar_left.9.pngbin2934 -> 0 bytes
-rwxr-xr-xcore/res/res/drawable/tab_selected_bar_right.9.pngbin2935 -> 0 bytes
-rw-r--r--core/res/res/drawable/tab_unselected.9.pngbin825 -> 0 bytes
-rw-r--r--core/res/res/layout-ja/contact_header_name.xml42
-rw-r--r--core/res/res/layout/character_picker.xml26
-rw-r--r--core/res/res/layout/character_picker_button.xml9
-rw-r--r--core/res/res/layout/contact_header.xml68
-rw-r--r--core/res/res/layout/contact_header_name.xml26
-rw-r--r--core/res/res/layout/grant_credentials_permission.xml41
-rw-r--r--core/res/res/layout/keyguard_screen_glogin_unlock.xml1
-rw-r--r--core/res/res/layout/preference_dialog.xml26
-rw-r--r--core/res/res/layout/tab_indicator.xml2
-rw-r--r--core/res/res/layout/zoom_browser_accessory_buttons.xml11
-rw-r--r--core/res/res/raw/loaderror.html1
-rw-r--r--core/res/res/raw/nodomain.html1
-rw-r--r--core/res/res/values-cs/strings.xml73
-rw-r--r--core/res/res/values-da/strings.xml227
-rw-r--r--core/res/res/values-de/strings.xml73
-rw-r--r--core/res/res/values-el/strings.xml227
-rw-r--r--core/res/res/values-en-rUS/donottranslate-names.xml155
-rw-r--r--core/res/res/values-es-rUS/strings.xml227
-rw-r--r--core/res/res/values-es/strings.xml73
-rw-r--r--core/res/res/values-fr/strings.xml73
-rw-r--r--core/res/res/values-it/strings.xml73
-rw-r--r--core/res/res/values-ja/strings.xml73
-rw-r--r--core/res/res/values-ko/strings.xml227
-rw-r--r--core/res/res/values-mcc204-el/strings.xml2
-rw-r--r--core/res/res/values-mcc204-nb/strings.xml19
-rw-r--r--core/res/res/values-mcc230-el/strings.xml2
-rw-r--r--core/res/res/values-mcc230-nb/strings.xml19
-rw-r--r--core/res/res/values-mcc232-el/strings.xml2
-rw-r--r--core/res/res/values-mcc232-nb/strings.xml19
-rw-r--r--core/res/res/values-mcc234-el/strings.xml2
-rw-r--r--core/res/res/values-mcc234-nb/strings.xml19
-rw-r--r--core/res/res/values-mcc260-el/strings.xml2
-rw-r--r--core/res/res/values-mcc260-nb/strings.xml19
-rw-r--r--core/res/res/values-mcc262-el/strings.xml2
-rw-r--r--core/res/res/values-mcc262-nb/strings.xml19
-rw-r--r--core/res/res/values-nb/strings.xml117
-rw-r--r--core/res/res/values-nl/strings.xml73
-rw-r--r--core/res/res/values-pl/strings.xml73
-rw-r--r--core/res/res/values-pt-rPT/strings.xml227
-rw-r--r--core/res/res/values-pt/strings.xml227
-rw-r--r--core/res/res/values-ru/strings.xml227
-rw-r--r--core/res/res/values-sv/strings.xml227
-rw-r--r--core/res/res/values-tr/strings.xml227
-rw-r--r--core/res/res/values-zh-rCN/strings.xml227
-rw-r--r--core/res/res/values-zh-rTW/strings.xml73
-rw-r--r--core/res/res/values/attrs.xml155
-rw-r--r--core/res/res/values/attrs_manifest.xml28
-rw-r--r--core/res/res/values/colors.xml5
-rw-r--r--core/res/res/values/config.xml49
-rw-r--r--core/res/res/values/dimens.xml6
-rw-r--r--core/res/res/values/donottranslate-names.xml11
-rw-r--r--core/res/res/values/public.xml64
-rw-r--r--core/res/res/values/strings.xml172
-rw-r--r--core/res/res/values/styles.xml50
-rw-r--r--core/res/res/values/themes.xml27
-rw-r--r--data/etc/android.hardware.camera.autofocus.xml21
-rw-r--r--data/etc/platform.xml5
-rw-r--r--docs/html/guide/appendix/faq/commontasks.jd13
-rw-r--r--docs/html/guide/developing/device.jd2
-rw-r--r--docs/html/guide/developing/tools/adb.jd2
-rw-r--r--docs/html/guide/developing/tools/aidl.jd1
-rw-r--r--docs/html/guide/developing/tools/ddms.jd13
-rw-r--r--docs/html/guide/topics/manifest/manifest-element.jd7
-rw-r--r--graphics/java/android/graphics/BitmapFactory.java43
-rw-r--r--graphics/java/android/graphics/Color.java196
-rw-r--r--graphics/java/android/graphics/DashPathEffect.java6
-rw-r--r--graphics/java/android/graphics/Typeface.java10
-rw-r--r--graphics/java/android/graphics/drawable/BitmapDrawable.java2
-rw-r--r--graphics/java/android/graphics/drawable/Drawable.java15
-rw-r--r--graphics/java/android/graphics/drawable/DrawableContainer.java2
-rw-r--r--graphics/java/android/graphics/drawable/NinePatchDrawable.java12
-rw-r--r--graphics/java/android/graphics/drawable/StateListDrawable.java2
-rw-r--r--graphics/java/android/renderscript/Allocation.java269
-rw-r--r--graphics/java/android/renderscript/BaseObj.java83
-rw-r--r--graphics/java/android/renderscript/Dimension.java35
-rw-r--r--graphics/java/android/renderscript/Element.java456
-rw-r--r--graphics/java/android/renderscript/Light.java73
-rw-r--r--graphics/java/android/renderscript/Matrix.java192
-rw-r--r--graphics/java/android/renderscript/Primitive.java37
-rw-r--r--graphics/java/android/renderscript/ProgramFragment.java158
-rw-r--r--graphics/java/android/renderscript/ProgramStore.java173
-rw-r--r--graphics/java/android/renderscript/ProgramVertex.java185
-rw-r--r--graphics/java/android/renderscript/RSSurfaceView.java146
-rw-r--r--graphics/java/android/renderscript/RenderScript.java316
-rw-r--r--graphics/java/android/renderscript/Sampler.java108
-rw-r--r--graphics/java/android/renderscript/Script.java107
-rw-r--r--graphics/java/android/renderscript/ScriptC.java161
-rw-r--r--graphics/java/android/renderscript/SimpleMesh.java323
-rw-r--r--graphics/java/android/renderscript/Type.java144
-rw-r--r--graphics/jni/Android.mk45
-rw-r--r--graphics/jni/android_renderscript_RenderScript.cpp1384
-rw-r--r--im/java/android/im/BrandingResourceIDs.java52
-rw-r--r--im/java/android/im/IImPlugin.aidl69
-rw-r--r--im/java/android/im/ImPluginConsts.java27
-rw-r--r--include/android_runtime/AndroidRuntime.h3
-rw-r--r--include/binder/Binder.h104
-rw-r--r--include/binder/BpBinder.h124
-rw-r--r--include/binder/IBinder.h159
-rw-r--r--include/binder/IInterface.h147
-rw-r--r--include/binder/IMemory.h102
-rw-r--r--include/binder/IPCThreadState.h110
-rw-r--r--include/binder/IPermissionController.h56
-rw-r--r--include/binder/IServiceManager.h100
-rw-r--r--include/binder/MemoryBase.h51
-rw-r--r--include/binder/MemoryDealer.h257
-rw-r--r--include/binder/MemoryHeapBase.h98
-rw-r--r--include/binder/MemoryHeapPmem.h80
-rw-r--r--include/binder/Parcel.h223
-rw-r--r--include/binder/Permission.h68
-rw-r--r--include/binder/ProcessState.h117
-rw-r--r--include/media/AudioRecord.h61
-rw-r--r--include/media/AudioSystem.h399
-rw-r--r--include/media/AudioTrack.h29
-rw-r--r--include/media/IAudioFlinger.h48
-rw-r--r--include/media/IAudioFlingerClient.h8
-rw-r--r--include/media/IAudioPolicyService.h90
-rw-r--r--include/media/IAudioRecord.h4
-rw-r--r--include/media/IAudioTrack.h4
-rw-r--r--include/media/IMediaMetadataRetriever.h6
-rw-r--r--include/media/IMediaPlayer.h36
-rw-r--r--include/media/IMediaPlayerClient.h4
-rw-r--r--include/media/IMediaPlayerService.h8
-rw-r--r--include/media/IMediaRecorder.h2
-rw-r--r--include/media/IOMX.h191
-rw-r--r--include/media/MediaMetadataRetrieverInterface.h22
-rw-r--r--include/media/MediaPlayerInterface.h50
-rw-r--r--include/media/Metadata.h133
-rw-r--r--include/media/PVPlayer.h6
-rw-r--r--include/media/ToneGenerator.h3
-rw-r--r--include/media/mediametadataretriever.h15
-rw-r--r--include/media/mediaplayer.h8
-rw-r--r--include/media/mediarecorder.h9
-rw-r--r--include/media/mediascanner.h26
-rw-r--r--include/media/stagefright/AMRExtractor.h54
-rw-r--r--include/media/stagefright/AudioPlayer.h98
-rw-r--r--include/media/stagefright/AudioSource.h51
-rw-r--r--include/media/stagefright/CachingDataSource.h62
-rw-r--r--include/media/stagefright/CameraSource.h72
-rw-r--r--include/media/stagefright/DataSource.h67
-rw-r--r--include/media/stagefright/ESDS.h64
-rw-r--r--include/media/stagefright/FileSource.h49
-rw-r--r--include/media/stagefright/HTTPDataSource.h59
-rw-r--r--include/media/stagefright/HTTPStream.h74
-rw-r--r--include/media/stagefright/MP3Extractor.h55
-rw-r--r--include/media/stagefright/MPEG4Extractor.h68
-rw-r--r--include/media/stagefright/MPEG4Writer.h75
-rw-r--r--include/media/stagefright/MediaBuffer.h113
-rw-r--r--include/media/stagefright/MediaBufferGroup.h58
-rw-r--r--include/media/stagefright/MediaDebug.h20
-rw-r--r--include/media/stagefright/MediaDefs.h40
-rw-r--r--include/media/stagefright/MediaErrors.h43
-rw-r--r--include/media/stagefright/MediaExtractor.h49
-rw-r--r--include/media/stagefright/MediaPlayerImpl.h128
-rw-r--r--include/media/stagefright/MediaSource.h93
-rw-r--r--include/media/stagefright/MetaData.h134
-rw-r--r--include/media/stagefright/MmapSource.h52
-rw-r--r--include/media/stagefright/OMXClient.h45
-rw-r--r--include/media/stagefright/OMXCodec.h242
-rw-r--r--include/media/stagefright/QComHardwareRenderer.h57
-rw-r--r--include/media/stagefright/SampleTable.h109
-rw-r--r--include/media/stagefright/ShoutcastSource.h59
-rw-r--r--include/media/stagefright/SoftwareRenderer.h55
-rw-r--r--include/media/stagefright/TIHardwareRenderer.h59
-rw-r--r--include/media/stagefright/TimeSource.h51
-rw-r--r--include/media/stagefright/TimedEventQueue.h106
-rw-r--r--include/media/stagefright/Utils.h37
-rw-r--r--include/media/stagefright/VideoRenderer.h41
-rw-r--r--include/media/stagefright/string.h54
-rw-r--r--include/private/binder/Static.h39
-rw-r--r--include/private/binder/binder_module.h (renamed from include/private/utils/binder_module.h)0
-rw-r--r--include/private/media/AudioTrackShared.h13
-rw-r--r--include/private/opengles/gl_context.h43
-rw-r--r--include/private/ui/LayerState.h2
-rw-r--r--include/private/ui/RegionHelper.h281
-rw-r--r--include/private/ui/SharedBufferStack.h327
-rw-r--r--include/private/ui/SharedState.h168
-rw-r--r--include/private/ui/SurfaceBuffer.h81
-rw-r--r--include/private/ui/SurfaceFlingerSynchro.h28
-rw-r--r--include/private/ui/android_natives_priv.h62
-rw-r--r--include/private/utils/Static.h23
-rw-r--r--include/private/utils/futex_synchro.h60
-rw-r--r--include/tts/TtsEngine.h6
-rw-r--r--include/ui/BufferMapper.h64
-rw-r--r--include/ui/Camera.h19
-rw-r--r--include/ui/CameraHardwareInterface.h139
-rw-r--r--include/ui/EGLDisplaySurface.h86
-rw-r--r--include/ui/EGLNativeWindowSurface.h59
-rw-r--r--include/ui/EGLUtils.h53
-rw-r--r--include/ui/EventHub.h29
-rw-r--r--include/ui/FramebufferNativeWindow.h87
-rw-r--r--include/ui/ICamera.h6
-rw-r--r--include/ui/ICameraClient.h6
-rw-r--r--include/ui/ICameraService.h4
-rw-r--r--include/ui/IOverlay.h2
-rw-r--r--include/ui/ISurface.h8
-rw-r--r--include/ui/ISurfaceComposer.h44
-rw-r--r--include/ui/ISurfaceFlingerClient.h8
-rw-r--r--include/ui/Overlay.h12
-rw-r--r--include/ui/PixelFormat.h36
-rw-r--r--include/ui/Rect.h8
-rw-r--r--include/ui/Region.h129
-rw-r--r--include/ui/Surface.h216
-rw-r--r--include/ui/SurfaceComposerClient.h73
-rw-r--r--include/ui/egl/android_natives.h263
-rw-r--r--include/utils.h33
-rw-r--r--include/utils/Binder.h103
-rw-r--r--include/utils/BpBinder.h122
-rw-r--r--include/utils/Debug.h33
-rw-r--r--include/utils/Errors.h2
-rw-r--r--include/utils/IBinder.h159
-rw-r--r--include/utils/IInterface.h135
-rw-r--r--include/utils/IMemory.h94
-rw-r--r--include/utils/IPCThreadState.h110
-rw-r--r--include/utils/IPermissionController.h56
-rw-r--r--include/utils/IServiceManager.h98
-rw-r--r--include/utils/KeyedVector.h2
-rw-r--r--include/utils/List.h272
-rw-r--r--include/utils/LogSocket.h20
-rw-r--r--include/utils/MemoryBase.h51
-rw-r--r--include/utils/MemoryDealer.h238
-rw-r--r--include/utils/MemoryHeapBase.h98
-rw-r--r--include/utils/MemoryHeapPmem.h80
-rw-r--r--include/utils/Parcel.h211
-rw-r--r--include/utils/Pipe.h108
-rw-r--r--include/utils/ProcessState.h117
-rw-r--r--include/utils/RefBase.h4
-rw-r--r--include/utils/Singleton.h69
-rw-r--r--include/utils/Socket.h80
-rw-r--r--include/utils/SortedVector.h3
-rw-r--r--include/utils/StringArray.h83
-rw-r--r--include/utils/TextOutput.h4
-rw-r--r--include/utils/TimerProbe.h72
-rw-r--r--include/utils/Timers.h13
-rw-r--r--include/utils/TypeHelpers.h141
-rw-r--r--include/utils/Vector.h3
-rw-r--r--include/utils/VectorImpl.h1
-rw-r--r--include/utils/ZipEntry.h345
-rw-r--r--include/utils/ZipFile.h269
-rw-r--r--include/utils/executablepath.h28
-rw-r--r--include/utils/inet_address.h103
-rw-r--r--include/utils/misc.h2
-rw-r--r--include/utils/ported.h50
-rw-r--r--include/utils/string_array.h135
-rw-r--r--include/utils/threads.h125
-rw-r--r--keystore/java/android/security/CertTool.java245
-rw-r--r--keystore/java/android/security/Keystore.java19
-rw-r--r--keystore/java/android/security/ServiceCommand.java31
-rw-r--r--keystore/jni/cert.c26
-rw-r--r--libs/audioflinger/A2dpAudioInterface.cpp236
-rw-r--r--libs/audioflinger/A2dpAudioInterface.h51
-rw-r--r--libs/audioflinger/Android.mk83
-rw-r--r--libs/audioflinger/AudioDumpInterface.cpp454
-rw-r--r--libs/audioflinger/AudioDumpInterface.h117
-rw-r--r--libs/audioflinger/AudioFlinger.cpp3314
-rw-r--r--libs/audioflinger/AudioFlinger.h541
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.cpp161
-rw-r--r--libs/audioflinger/AudioHardwareGeneric.h49
-rw-r--r--libs/audioflinger/AudioHardwareInterface.cpp109
-rw-r--r--libs/audioflinger/AudioHardwareStub.cpp64
-rw-r--r--libs/audioflinger/AudioHardwareStub.h37
-rw-r--r--libs/audioflinger/AudioMixer.cpp1
-rw-r--r--libs/audioflinger/AudioMixer.h3
-rw-r--r--libs/audioflinger/AudioPolicyManagerGeneric.cpp826
-rw-r--r--libs/audioflinger/AudioPolicyManagerGeneric.h189
-rw-r--r--libs/audioflinger/AudioPolicyService.cpp771
-rw-r--r--libs/audioflinger/AudioPolicyService.h203
-rw-r--r--libs/binder/Android.mk45
-rw-r--r--libs/binder/Binder.cpp255
-rw-r--r--libs/binder/BpBinder.cpp365
-rw-r--r--libs/binder/IInterface.cpp42
-rw-r--r--libs/binder/IMemory.cpp492
-rw-r--r--libs/binder/IPCThreadState.cpp1025
-rw-r--r--libs/binder/IPermissionController.cpp80
-rw-r--r--libs/binder/IServiceManager.cpp229
-rw-r--r--libs/binder/MemoryBase.cpp46
-rw-r--r--libs/binder/MemoryDealer.cpp421
-rw-r--r--libs/binder/MemoryHeapBase.cpp183
-rw-r--r--libs/binder/MemoryHeapPmem.cpp248
-rw-r--r--libs/binder/Parcel.cpp1336
-rw-r--r--libs/binder/Permission.cpp88
-rw-r--r--libs/binder/ProcessState.cpp398
-rw-r--r--libs/binder/Static.cpp53
-rw-r--r--libs/rs/Android.mk115
-rw-r--r--libs/rs/RenderScript.h201
-rw-r--r--libs/rs/RenderScriptEnv.h33
-rw-r--r--libs/rs/java/Android.mk1
-rw-r--r--libs/rs/java/Film/Android.mk25
-rw-r--r--libs/rs/java/Film/AndroidManifest.xml14
-rw-r--r--libs/rs/java/Film/res/drawable/p01.pngbin0 -> 254040 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p02.pngbin0 -> 138822 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p03.pngbin0 -> 152567 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p04.pngbin0 -> 280950 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p05.pngbin0 -> 349923 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p06.pngbin0 -> 120523 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p07.pngbin0 -> 139555 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p08.pngbin0 -> 321251 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p09.pngbin0 -> 281133 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p10.pngbin0 -> 366289 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p11.pngbin0 -> 319259 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p12.pngbin0 -> 368751 bytes
-rw-r--r--libs/rs/java/Film/res/drawable/p13.pngbin0 -> 312561 bytes
-rw-r--r--libs/rs/java/Film/res/raw/filmimage.c110
-rw-r--r--libs/rs/java/Film/res/raw/filmstrip.c94
-rw-r--r--libs/rs/java/Film/src/com/android/film/Film.java90
-rw-r--r--libs/rs/java/Film/src/com/android/film/FilmRS.java256
-rw-r--r--libs/rs/java/Film/src/com/android/film/FilmStripMesh.java254
-rw-r--r--libs/rs/java/Film/src/com/android/film/FilmView.java82
-rw-r--r--libs/rs/java/Fountain/Android.mk25
-rw-r--r--libs/rs/java/Fountain/AndroidManifest.xml13
-rwxr-xr-xlibs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.pngbin0 -> 104862 bytes
-rw-r--r--libs/rs/java/Fountain/res/raw/fountain.c55
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/Fountain.java90
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java116
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/FountainView.java79
-rw-r--r--libs/rs/java/Rollo/Android.mk25
-rw-r--r--libs/rs/java/Rollo/AndroidManifest.xml13
-rw-r--r--libs/rs/java/Rollo/res/raw/browser.pngbin0 -> 5772 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/calendar.pngbin0 -> 4551 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/g1155.pngbin0 -> 5094 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/g2140.pngbin0 -> 3982 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/maps.pngbin0 -> 5374 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/market.pngbin0 -> 4810 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path1920.pngbin0 -> 3812 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path1927.pngbin0 -> 4034 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path3099.pngbin0 -> 4981 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path3950.pngbin0 -> 4873 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path431.pngbin0 -> 3995 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path4481.pngbin0 -> 4113 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path5168.pngbin0 -> 5328 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path676.pngbin0 -> 4351 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path754.pngbin0 -> 3205 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/path815.pngbin0 -> 5536 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/photos.pngbin0 -> 4902 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/polygon2408.pngbin0 -> 3873 bytes
-rw-r--r--libs/rs/java/Rollo/res/raw/rollo.c184
-rw-r--r--libs/rs/java/Rollo/res/raw/rollo2.c155
-rw-r--r--libs/rs/java/Rollo/res/raw/settings.pngbin0 -> 3764 bytes
-rw-r--r--libs/rs/java/Rollo/src/com/android/rollo/Rollo.java90
-rw-r--r--libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java315
-rw-r--r--libs/rs/java/Rollo/src/com/android/rollo/RolloView.java214
-rw-r--r--libs/rs/rs.spec478
-rw-r--r--libs/rs/rsAdapter.cpp237
-rw-r--r--libs/rs/rsAdapter.h96
-rw-r--r--libs/rs/rsAllocation.cpp563
-rw-r--r--libs/rs/rsAllocation.h101
-rw-r--r--libs/rs/rsComponent.cpp101
-rw-r--r--libs/rs/rsComponent.h77
-rw-r--r--libs/rs/rsContext.cpp573
-rw-r--r--libs/rs/rsContext.h213
-rw-r--r--libs/rs/rsDevice.cpp62
-rw-r--r--libs/rs/rsDevice.h48
-rw-r--r--libs/rs/rsElement.cpp421
-rw-r--r--libs/rs/rsElement.h95
-rw-r--r--libs/rs/rsFileA3D.cpp384
-rw-r--r--libs/rs/rsFileA3D.h122
-rw-r--r--libs/rs/rsFileA3DDecls.h44
-rw-r--r--libs/rs/rsHandcode.h47
-rw-r--r--libs/rs/rsLight.cpp128
-rw-r--r--libs/rs/rsLight.h64
-rw-r--r--libs/rs/rsLocklessFifo.cpp258
-rw-r--r--libs/rs/rsLocklessFifo.h91
-rw-r--r--libs/rs/rsMatrix.cpp159
-rw-r--r--libs/rs/rsMatrix.h87
-rw-r--r--libs/rs/rsMesh.cpp46
-rw-r--r--libs/rs/rsMesh.h90
-rw-r--r--libs/rs/rsNoise.cpp256
-rw-r--r--libs/rs/rsNoise.h35
-rw-r--r--libs/rs/rsObjectBase.cpp98
-rw-r--r--libs/rs/rsObjectBase.h118
-rw-r--r--libs/rs/rsProgram.cpp49
-rw-r--r--libs/rs/rsProgram.h56
-rw-r--r--libs/rs/rsProgramFragment.cpp243
-rw-r--r--libs/rs/rsProgramFragment.h93
-rw-r--r--libs/rs/rsProgramFragmentStore.cpp266
-rw-r--r--libs/rs/rsProgramFragmentStore.h82
-rw-r--r--libs/rs/rsProgramVertex.cpp188
-rw-r--r--libs/rs/rsProgramVertex.h81
-rw-r--r--libs/rs/rsSampler.cpp151
-rw-r--r--libs/rs/rsSampler.h87
-rw-r--r--libs/rs/rsScript.cpp95
-rw-r--r--libs/rs/rsScript.h74
-rw-r--r--libs/rs/rsScriptC.cpp446
-rw-r--r--libs/rs/rsScriptC.h104
-rw-r--r--libs/rs/rsScriptC_Lib.cpp1254
-rw-r--r--libs/rs/rsSimpleMesh.cpp142
-rw-r--r--libs/rs/rsSimpleMesh.h68
-rw-r--r--libs/rs/rsThreadIO.cpp61
-rw-r--r--libs/rs/rsThreadIO.h52
-rw-r--r--libs/rs/rsTriangleMesh.cpp287
-rw-r--r--libs/rs/rsTriangleMesh.h83
-rw-r--r--libs/rs/rsType.cpp382
-rw-r--r--libs/rs/rsType.h153
-rw-r--r--libs/rs/rsUtils.h133
-rw-r--r--libs/rs/rsgApi.cpp.rsg1
-rw-r--r--libs/rs/rsgApiFuncDecl.h.rsg1
-rw-r--r--libs/rs/rsgApiReplay.cpp.rsg1
-rw-r--r--libs/rs/rsgApiStructs.h.rsg1
-rw-r--r--libs/rs/rsg_generator.c306
-rw-r--r--libs/rs/spec.h43
-rw-r--r--libs/rs/spec.l165
-rw-r--r--libs/surfaceflinger/Android.mk36
-rw-r--r--libs/surfaceflinger/Buffer.cpp139
-rw-r--r--libs/surfaceflinger/Buffer.h94
-rw-r--r--libs/surfaceflinger/BufferAllocator.cpp127
-rw-r--r--libs/surfaceflinger/BufferAllocator.h96
-rw-r--r--libs/surfaceflinger/CPUGauge.cpp171
-rw-r--r--libs/surfaceflinger/CPUGauge.h74
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp229
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardware.h24
-rw-r--r--libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp2
-rw-r--r--libs/surfaceflinger/GPUHardware/GPUHardware.cpp585
-rw-r--r--libs/surfaceflinger/GPUHardware/GPUHardware.h63
-rw-r--r--libs/surfaceflinger/Layer.cpp646
-rw-r--r--libs/surfaceflinger/Layer.h96
-rw-r--r--libs/surfaceflinger/LayerBase.cpp450
-rw-r--r--libs/surfaceflinger/LayerBase.h168
-rw-r--r--libs/surfaceflinger/LayerBitmap.cpp187
-rw-r--r--libs/surfaceflinger/LayerBitmap.h84
-rw-r--r--libs/surfaceflinger/LayerBlur.cpp85
-rw-r--r--libs/surfaceflinger/LayerBlur.h5
-rw-r--r--libs/surfaceflinger/LayerBuffer.cpp398
-rw-r--r--libs/surfaceflinger/LayerBuffer.h91
-rw-r--r--libs/surfaceflinger/LayerDim.cpp160
-rw-r--r--libs/surfaceflinger/LayerDim.h19
-rw-r--r--libs/surfaceflinger/LayerOrientationAnim.cpp206
-rw-r--r--libs/surfaceflinger/LayerOrientationAnim.h80
-rw-r--r--libs/surfaceflinger/MessageQueue.cpp192
-rw-r--r--libs/surfaceflinger/MessageQueue.h127
-rw-r--r--libs/surfaceflinger/OrientationAnimation.cpp155
-rw-r--r--libs/surfaceflinger/OrientationAnimation.h85
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.cpp908
-rw-r--r--libs/surfaceflinger/SurfaceFlinger.h194
-rw-r--r--libs/surfaceflinger/Tokenizer.cpp5
-rw-r--r--libs/surfaceflinger/Transform.cpp8
-rw-r--r--libs/surfaceflinger/VRamHeap.cpp178
-rw-r--r--libs/surfaceflinger/VRamHeap.h78
-rw-r--r--libs/surfaceflinger/tests/overlays/overlays.cpp6
-rw-r--r--libs/surfaceflinger/tests/resize/Android.mk16
-rw-r--r--libs/surfaceflinger/tests/resize/resize.cpp60
-rw-r--r--libs/ui/Android.mk14
-rw-r--r--libs/ui/BufferMapper.cpp80
-rw-r--r--libs/ui/Camera.cpp4
-rw-r--r--libs/ui/EGLDisplaySurface.cpp519
-rw-r--r--libs/ui/EGLNativeWindowSurface.cpp161
-rw-r--r--libs/ui/EGLUtils.cpp136
-rw-r--r--libs/ui/EventHub.cpp193
-rw-r--r--libs/ui/FramebufferNativeWindow.cpp267
-rw-r--r--libs/ui/ICamera.cpp8
-rw-r--r--libs/ui/ICameraClient.cpp6
-rw-r--r--libs/ui/ICameraService.cpp12
-rw-r--r--libs/ui/IOverlay.cpp10
-rw-r--r--libs/ui/ISurface.cpp36
-rw-r--r--libs/ui/ISurfaceComposer.cpp125
-rw-r--r--libs/ui/ISurfaceFlingerClient.cpp35
-rw-r--r--libs/ui/LayerState.cpp2
-rw-r--r--libs/ui/Overlay.cpp39
-rw-r--r--libs/ui/Region.cpp636
-rw-r--r--libs/ui/SharedBufferStack.cpp352
-rw-r--r--libs/ui/Surface.cpp802
-rw-r--r--libs/ui/SurfaceBuffer.cpp134
-rw-r--r--libs/ui/SurfaceComposerClient.cpp527
-rw-r--r--libs/ui/SurfaceFlingerSynchro.cpp81
-rw-r--r--libs/ui/tests/Android.mk16
-rw-r--r--libs/ui/tests/region.cpp62
-rw-r--r--libs/utils/Android.mk62
-rw-r--r--libs/utils/BackupData.cpp28
-rw-r--r--libs/utils/Binder.cpp242
-rw-r--r--libs/utils/BpBinder.cpp348
-rw-r--r--libs/utils/CallStack.cpp3
-rw-r--r--libs/utils/IDataConnection.cpp89
-rw-r--r--libs/utils/IInterface.cpp35
-rw-r--r--libs/utils/IMemory.cpp486
-rw-r--r--libs/utils/IPCThreadState.cpp1030
-rw-r--r--libs/utils/IPermissionController.cpp86
-rw-r--r--libs/utils/IServiceManager.cpp230
-rw-r--r--libs/utils/InetAddress.cpp236
-rw-r--r--libs/utils/LogSocket.cpp129
-rw-r--r--libs/utils/MemoryBase.cpp46
-rw-r--r--libs/utils/MemoryDealer.cpp409
-rw-r--r--libs/utils/MemoryHeapBase.cpp183
-rw-r--r--libs/utils/MemoryHeapPmem.cpp248
-rw-r--r--libs/utils/Parcel.cpp1363
-rw-r--r--libs/utils/Pipe.cpp465
-rw-r--r--libs/utils/ProcessState.cpp398
-rw-r--r--libs/utils/ResourceTypes.cpp11
-rw-r--r--libs/utils/Socket.cpp388
-rw-r--r--libs/utils/Static.cpp31
-rw-r--r--libs/utils/StringArray.cpp113
-rw-r--r--libs/utils/TextOutput.cpp10
-rw-r--r--libs/utils/Threads.cpp308
-rw-r--r--libs/utils/TimerProbe.cpp131
-rw-r--r--libs/utils/Timers.cpp125
-rw-r--r--libs/utils/ZipEntry.cpp696
-rw-r--r--libs/utils/ZipFile.cpp1296
-rw-r--r--libs/utils/executablepath_darwin.cpp31
-rw-r--r--libs/utils/executablepath_linux.cpp30
-rw-r--r--libs/utils/futex_synchro.c176
-rw-r--r--libs/utils/ported.cpp106
-rw-r--r--location/java/android/location/Geocoder.java6
-rw-r--r--location/java/android/location/GpsStatus.java12
-rw-r--r--location/java/android/location/IGpsStatusListener.aidl1
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rwxr-xr-xlocation/java/android/location/INetInitiatedListener.aidl26
-rw-r--r--location/java/android/location/LocationManager.java163
-rwxr-xr-xlocation/java/com/android/internal/location/GpsLocationProvider.java171
-rwxr-xr-xlocation/java/com/android/internal/location/GpsNetInitiatedHandler.java457
-rw-r--r--location/java/com/android/internal/location/GpsXtraDownloader.java1
-rw-r--r--media/java/android/media/AudioFormat.java56
-rw-r--r--media/java/android/media/AudioManager.java228
-rw-r--r--media/java/android/media/AudioRecord.java44
-rw-r--r--media/java/android/media/AudioService.java859
-rw-r--r--media/java/android/media/AudioSystem.java158
-rw-r--r--media/java/android/media/AudioTrack.java42
-rw-r--r--media/java/android/media/ExifInterface.java28
-rw-r--r--media/java/android/media/IAudioService.aidl24
-rw-r--r--media/java/android/media/JetPlayer.java2
-rw-r--r--media/java/android/media/MediaFile.java8
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java5
-rw-r--r--media/java/android/media/MediaPlayer.java205
-rw-r--r--media/java/android/media/MediaRecorder.java5
-rw-r--r--media/java/android/media/MediaScanner.java66
-rw-r--r--media/java/android/media/MediaScannerClient.java4
-rw-r--r--media/java/android/media/Metadata.java418
-rw-r--r--media/java/android/media/RingtoneManager.java54
-rw-r--r--media/java/android/media/ToneGenerator.java19
-rw-r--r--media/jni/Android.mk6
-rw-r--r--media/jni/android_media_AmrInputStream.cpp7
-rw-r--r--media/jni/android_media_MediaMetadataRetriever.cpp62
-rw-r--r--media/jni/android_media_MediaPlayer.cpp169
-rw-r--r--media/jni/android_media_MediaRecorder.cpp93
-rw-r--r--media/jni/android_media_MediaScanner.cpp54
-rw-r--r--media/jni/android_media_ResampleInputStream.cpp7
-rw-r--r--media/jni/soundpool/Android.mk1
-rw-r--r--media/jni/soundpool/SoundPool.cpp5
-rw-r--r--media/libdrm/mobile2/include/rights/RoManager.h6
-rw-r--r--media/libdrm/mobile2/src/rights/RoManager.cpp6
-rw-r--r--media/libdrm/mobile2/src/util/domcore/NodeIterator.cpp2
-rw-r--r--media/libmedia/Android.mk44
-rw-r--r--media/libmedia/AudioRecord.cpp98
-rw-r--r--media/libmedia/AudioSystem.cpp753
-rw-r--r--media/libmedia/AudioTrack.cpp205
-rw-r--r--media/libmedia/IAudioFlinger.cpp348
-rw-r--r--media/libmedia/IAudioFlingerClient.cpp52
-rw-r--r--media/libmedia/IAudioPolicyService.cpp413
-rw-r--r--media/libmedia/IAudioRecord.cpp8
-rw-r--r--media/libmedia/IAudioTrack.cpp8
-rw-r--r--media/libmedia/IMediaMetadataRetriever.cpp11
-rw-r--r--media/libmedia/IMediaPlayer.cpp60
-rw-r--r--media/libmedia/IMediaPlayerClient.cpp13
-rw-r--r--media/libmedia/IMediaPlayerService.cpp29
-rw-r--r--media/libmedia/IMediaRecorder.cpp10
-rw-r--r--media/libmedia/IOMX.cpp733
-rw-r--r--media/libmedia/JetPlayer.cpp2
-rw-r--r--media/libmedia/Metadata.cpp168
-rw-r--r--media/libmedia/ToneGenerator.cpp33
-rw-r--r--media/libmedia/mediametadataretriever.cpp4
-rw-r--r--media/libmedia/mediaplayer.cpp43
-rw-r--r--media/libmedia/mediarecorder.cpp2
-rw-r--r--media/libmediaplayerservice/Android.mk43
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp337
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h59
-rw-r--r--media/libmediaplayerservice/MediaRecorderClient.cpp8
-rw-r--r--media/libmediaplayerservice/MetadataRetrieverClient.cpp76
-rw-r--r--media/libmediaplayerservice/MetadataRetrieverClient.h7
-rw-r--r--media/libmediaplayerservice/MidiFile.h4
-rw-r--r--media/libmediaplayerservice/MidiMetadataRetriever.cpp91
-rw-r--r--media/libmediaplayerservice/MidiMetadataRetriever.h49
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.cpp208
-rw-r--r--media/libmediaplayerservice/StagefrightPlayer.h60
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.cpp196
-rw-r--r--media/libmediaplayerservice/TestPlayerStub.h119
-rw-r--r--media/libmediaplayerservice/VorbisMetadataRetriever.cpp86
-rw-r--r--media/libmediaplayerservice/VorbisMetadataRetriever.h49
-rw-r--r--media/libmediaplayerservice/VorbisPlayer.h2
-rw-r--r--media/libstagefright/AMRExtractor.cpp242
-rw-r--r--media/libstagefright/Android.mk56
-rw-r--r--media/libstagefright/AudioPlayer.cpp288
-rw-r--r--media/libstagefright/CachingDataSource.cpp157
-rw-r--r--media/libstagefright/CameraSource.cpp224
-rw-r--r--media/libstagefright/DataSource.cpp91
-rw-r--r--media/libstagefright/ESDS.cpp196
-rw-r--r--media/libstagefright/FileSource.cpp48
-rw-r--r--media/libstagefright/HTTPDataSource.cpp172
-rw-r--r--media/libstagefright/HTTPStream.cpp285
-rw-r--r--media/libstagefright/MP3Extractor.cpp520
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp994
-rw-r--r--media/libstagefright/MPEG4Writer.cpp696
-rw-r--r--media/libstagefright/MediaBuffer.cpp172
-rw-r--r--media/libstagefright/MediaBufferGroup.cpp86
-rw-r--r--media/libstagefright/MediaDefs.cpp36
-rw-r--r--media/libstagefright/MediaExtractor.cpp61
-rw-r--r--media/libstagefright/MediaPlayerImpl.cpp647
-rw-r--r--media/libstagefright/MediaSource.cpp60
-rw-r--r--media/libstagefright/MetaData.cpp232
-rw-r--r--media/libstagefright/MmapSource.cpp110
-rw-r--r--media/libstagefright/OMXClient.cpp47
-rw-r--r--media/libstagefright/OMXCodec.cpp2455
-rw-r--r--media/libstagefright/SampleTable.cpp578
-rw-r--r--media/libstagefright/ShoutcastSource.cpp156
-rw-r--r--media/libstagefright/TimeSource.cpp41
-rw-r--r--media/libstagefright/TimedEventQueue.cpp205
-rw-r--r--media/libstagefright/Utils.cpp45
-rw-r--r--media/libstagefright/omx/Android.mk33
-rw-r--r--media/libstagefright/omx/OMX.cpp639
-rw-r--r--media/libstagefright/omx/OMX.h138
-rw-r--r--media/libstagefright/omx/OMXRenderer.h44
-rw-r--r--media/libstagefright/omx/QComHardwareRenderer.cpp137
-rw-r--r--media/libstagefright/omx/SoftwareRenderer.cpp175
-rw-r--r--media/libstagefright/omx/TIHardwareRenderer.cpp130
-rw-r--r--media/libstagefright/string.cpp83
-rw-r--r--media/mediaserver/Android.mk3
-rw-r--r--media/mediaserver/main_mediaserver.cpp8
-rw-r--r--media/sdutils/sdutil.cpp4
-rw-r--r--media/tests/MediaFrameworkTest/res/drawable-hdpi/icon.pngbin0 -> 9734 bytes
-rw-r--r--media/tests/MediaFrameworkTest/res/drawable-mdpi/icon.png (renamed from media/tests/MediaFrameworkTest/res/drawable/icon.png)bin6094 -> 6094 bytes
-rw-r--r--media/tests/MediaFrameworkTest/res/layout/surface_view.xml7
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java4
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java6
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java13
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java56
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java96
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java9
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java70
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java10
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java432
-rw-r--r--media/tests/players/Android.mk29
-rw-r--r--media/tests/players/README8
-rw-r--r--media/tests/players/invoke_mock_media_player.cpp121
-rw-r--r--obex/Android.mk9
-rw-r--r--obex/javax/obex/ApplicationParameter.java134
-rw-r--r--obex/javax/obex/Authenticator.java115
-rw-r--r--obex/javax/obex/BaseStream.java76
-rw-r--r--obex/javax/obex/ClientOperation.java720
-rw-r--r--obex/javax/obex/ClientSession.java523
-rw-r--r--obex/javax/obex/HeaderSet.java628
-rw-r--r--obex/javax/obex/ObexHelper.java995
-rw-r--r--obex/javax/obex/ObexSession.java218
-rw-r--r--obex/javax/obex/ObexTransport.java76
-rw-r--r--obex/javax/obex/Operation.java179
-rw-r--r--obex/javax/obex/PasswordAuthentication.java79
-rw-r--r--obex/javax/obex/PrivateInputStream.java181
-rw-r--r--obex/javax/obex/PrivateOutputStream.java175
-rw-r--r--obex/javax/obex/ResponseCodes.java326
-rw-r--r--obex/javax/obex/ServerOperation.java686
-rw-r--r--obex/javax/obex/ServerRequestHandler.java271
-rw-r--r--obex/javax/obex/ServerSession.java669
-rw-r--r--obex/javax/obex/SessionNotifier.java128
-rw-r--r--opengl/include/EGL/eglext.h24
-rw-r--r--opengl/include/EGL/eglnatives.h271
-rw-r--r--opengl/include/EGL/eglplatform.h5
-rw-r--r--opengl/include/GLES/glplatform.h6
-rw-r--r--opengl/include/GLES2/gl2.h620
-rw-r--r--opengl/include/GLES2/gl2ext.h518
-rw-r--r--opengl/include/GLES2/gl2platform.h29
-rw-r--r--opengl/java/android/opengl/GLSurfaceView.java33
-rw-r--r--opengl/libagl/Android.mk19
-rw-r--r--opengl/libagl/TextureObjectManager.cpp47
-rw-r--r--opengl/libagl/TextureObjectManager.h70
-rw-r--r--opengl/libagl/array.cpp126
-rw-r--r--opengl/libagl/copybit.cpp460
-rw-r--r--opengl/libagl/copybit.h75
-rw-r--r--opengl/libagl/egl.cpp795
-rw-r--r--opengl/libagl/light.cpp11
-rw-r--r--opengl/libagl/matrix.cpp2
-rw-r--r--opengl/libagl/primitives.cpp2
-rw-r--r--opengl/libagl/state.cpp82
-rw-r--r--opengl/libagl/texture.cpp296
-rw-r--r--opengl/libagl/texture.h10
-rw-r--r--opengl/libs/Android.mk78
-rw-r--r--opengl/libs/EGL/Loader.cpp276
-rw-r--r--opengl/libs/EGL/Loader.h90
-rw-r--r--opengl/libs/EGL/egl.cpp1010
-rw-r--r--opengl/libs/EGL/egl_entries.in57
-rw-r--r--opengl/libs/EGL/gpu.cpp217
-rw-r--r--opengl/libs/EGL/hooks.cpp67
-rw-r--r--opengl/libs/GLES2/gl2.cpp111
-rw-r--r--opengl/libs/GLES2/gl2_api.in426
-rw-r--r--opengl/libs/GLES2/gl2_entries.in142
-rw-r--r--opengl/libs/GLES2/gl2ext_api.in105
-rw-r--r--opengl/libs/GLES2/gl2ext_entries.in35
-rw-r--r--opengl/libs/GLES_CM/gl.cpp15
-rw-r--r--opengl/libs/GLES_CM/gl_entries.in (renamed from opengl/libs/gl_entries.in)0
-rw-r--r--opengl/libs/GLES_CM/glext_entries.in (renamed from opengl/libs/glext_entries.in)0
-rw-r--r--opengl/libs/egl_entries.in52
-rw-r--r--opengl/libs/egl_impl.h5
-rw-r--r--opengl/libs/gl_enums.in261
-rw-r--r--opengl/libs/hooks.h27
-rw-r--r--opengl/libs/tools/enumextract.sh32
-rwxr-xr-xopengl/libs/tools/genfiles11
-rwxr-xr-xopengl/libs/tools/glapigen15
-rwxr-xr-xopengl/libs/tools/glentrygen15
-rw-r--r--opengl/tests/angeles/Android.mk2
-rw-r--r--opengl/tests/angeles/app-linux.c223
-rw-r--r--opengl/tests/angeles/app-linux.cpp213
-rw-r--r--opengl/tests/copybits/Android.mk18
-rw-r--r--opengl/tests/copybits/copybits.cpp726
-rw-r--r--opengl/tests/fillrate/Android.mk17
-rw-r--r--opengl/tests/fillrate/fillrate.cpp161
-rw-r--r--opengl/tests/filter/Android.mk4
-rw-r--r--opengl/tests/filter/filter.c130
-rw-r--r--opengl/tests/filter/filter.cpp190
-rw-r--r--opengl/tests/finish/Android.mk4
-rw-r--r--opengl/tests/finish/finish.c224
-rw-r--r--opengl/tests/finish/finish.cpp226
-rw-r--r--opengl/tests/gl2_basic/Android.mk19
-rw-r--r--opengl/tests/gl2_basic/gl2_basic.cpp85
-rw-r--r--opengl/tests/swapinterval/Android.mk17
-rw-r--r--opengl/tests/swapinterval/swapinterval.cpp121
-rw-r--r--opengl/tests/textures/Android.mk4
-rw-r--r--opengl/tests/textures/textures.c109
-rw-r--r--opengl/tests/textures/textures.cpp118
-rw-r--r--packages/SettingsProvider/AndroidManifest.xml3
-rw-r--r--packages/SettingsProvider/etc/bookmarks.xml2
-rw-r--r--packages/SettingsProvider/res/values-da/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-el/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-es-rUS/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-ko/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-nb/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-pt-rPT/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-pt/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-ru/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-sv/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-tr/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values-zh-rCN/strings.xml19
-rw-r--r--packages/SettingsProvider/res/values/defaults.xml2
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java79
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java26
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java8
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java71
-rw-r--r--packages/SubscribedFeedsProvider/AndroidManifest.xml5
-rw-r--r--packages/SubscribedFeedsProvider/res/values-cs/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-de/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-es/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-fr/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-it/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-ja/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-nl/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-pl/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml2
-rw-r--r--packages/SubscribedFeedsProvider/res/values/strings.xml4
-rw-r--r--packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsBroadcastReceiver.java6
-rw-r--r--packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java76
-rw-r--r--packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java26
-rw-r--r--packages/TtsService/jni/android_tts_SynthProxy.cpp37
-rwxr-xr-xpackages/TtsService/src/android/tts/SynthProxy.java16
-rwxr-xr-xpackages/TtsService/src/android/tts/TtsService.java116
-rw-r--r--packages/VpnServices/res/values-cs/strings.xml6
-rw-r--r--packages/VpnServices/res/values-da/strings.xml24
-rw-r--r--packages/VpnServices/res/values-de/strings.xml6
-rw-r--r--packages/VpnServices/res/values-el/strings.xml24
-rw-r--r--packages/VpnServices/res/values-es-rUS/strings.xml24
-rw-r--r--packages/VpnServices/res/values-es/strings.xml6
-rw-r--r--packages/VpnServices/res/values-fr/strings.xml6
-rw-r--r--packages/VpnServices/res/values-it/strings.xml6
-rw-r--r--packages/VpnServices/res/values-ja/strings.xml6
-rw-r--r--packages/VpnServices/res/values-ko/strings.xml24
-rw-r--r--packages/VpnServices/res/values-nb/strings.xml24
-rw-r--r--packages/VpnServices/res/values-nl/strings.xml6
-rw-r--r--packages/VpnServices/res/values-pl/strings.xml6
-rw-r--r--packages/VpnServices/res/values-pt-rPT/strings.xml24
-rw-r--r--packages/VpnServices/res/values-pt/strings.xml24
-rw-r--r--packages/VpnServices/res/values-ru/strings.xml24
-rw-r--r--packages/VpnServices/res/values-sv/strings.xml24
-rw-r--r--packages/VpnServices/res/values-tr/strings.xml24
-rw-r--r--packages/VpnServices/res/values-zh-rCN/strings.xml24
-rw-r--r--packages/VpnServices/res/values-zh-rTW/strings.xml6
-rwxr-xr-xpackages/VpnServices/res/values/strings.xml6
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnService.java24
-rw-r--r--packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java3
-rw-r--r--preloaded-classes1377
-rw-r--r--services/java/com/android/server/AccessibilityManagerService.java5
-rw-r--r--services/java/com/android/server/BackupManagerService.java722
-rw-r--r--services/java/com/android/server/BatteryService.java60
-rw-r--r--services/java/com/android/server/ConnectivityService.java1015
-rw-r--r--services/java/com/android/server/DockObserver.java112
-rwxr-xr-xservices/java/com/android/server/HardwareService.java250
-rw-r--r--services/java/com/android/server/HeadsetObserver.java48
-rw-r--r--services/java/com/android/server/InputDevice.java582
-rw-r--r--services/java/com/android/server/InputMethodManagerService.java62
-rw-r--r--services/java/com/android/server/JournaledFile.java107
-rw-r--r--services/java/com/android/server/KeyInputQueue.java655
-rw-r--r--services/java/com/android/server/LocationManagerService.java158
-rw-r--r--services/java/com/android/server/MasterClearReceiver.java4
-rw-r--r--services/java/com/android/server/MountService.java7
-rwxr-xr-x[-rw-r--r--]services/java/com/android/server/NotificationManagerService.java98
-rw-r--r--services/java/com/android/server/PackageManagerBackupAgent.java7
-rw-r--r--services/java/com/android/server/PackageManagerService.java293
-rw-r--r--services/java/com/android/server/PowerManagerService.java116
-rw-r--r--services/java/com/android/server/SensorService.java21
-rw-r--r--services/java/com/android/server/ShutdownActivity.java46
-rw-r--r--services/java/com/android/server/SystemBackupAgent.java67
-rw-r--r--services/java/com/android/server/SystemServer.java188
-rw-r--r--services/java/com/android/server/TelephonyRegistry.java20
-rw-r--r--services/java/com/android/server/WallpaperManagerService.java702
-rw-r--r--services/java/com/android/server/WallpaperService.java203
-rw-r--r--services/java/com/android/server/WifiService.java324
-rw-r--r--services/java/com/android/server/WindowManagerService.java1420
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java758
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java1
-rw-r--r--services/java/com/android/server/am/ConnectionRecord.java8
-rw-r--r--services/java/com/android/server/am/PendingIntentRecord.java15
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java1
-rw-r--r--services/java/com/android/server/am/ServiceRecord.java134
-rw-r--r--[-rwxr-xr-x]services/java/com/android/server/am/UsageStatsService.java9
-rw-r--r--services/java/com/android/server/status/StatusBarPolicy.java342
-rw-r--r--services/java/com/android/server/status/StatusBarService.java4
-rw-r--r--services/jni/com_android_server_AlarmManagerService.cpp4
-rw-r--r--services/jni/com_android_server_BatteryService.cpp159
-rw-r--r--services/jni/com_android_server_HardwareService.cpp2
-rw-r--r--services/jni/com_android_server_KeyInputQueue.cpp38
-rw-r--r--services/jni/com_android_server_SensorService.cpp10
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java341
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java3
-rw-r--r--telephony/java/android/telephony/ServiceState.java15
-rw-r--r--telephony/java/android/telephony/SmsManager.java79
-rw-r--r--telephony/java/android/telephony/SmsMessage.java28
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java61
-rw-r--r--telephony/java/android/telephony/gsm/SmsMessage.java1
-rw-r--r--telephony/java/com/android/internal/telephony/AdnRecord.java51
-rw-r--r--telephony/java/com/android/internal/telephony/AdnRecordCache.java34
-rw-r--r--telephony/java/com/android/internal/telephony/Call.java4
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfo.java29
-rw-r--r--telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java4
-rw-r--r--telephony/java/com/android/internal/telephony/CommandsInterface.java45
-rw-r--r--telephony/java/com/android/internal/telephony/Connection.java3
-rw-r--r--telephony/java/com/android/internal/telephony/DataConnectionTracker.java238
-rw-r--r--telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java7
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java63
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl54
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl14
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/IccCard.java625
-rw-r--r--telephony/java/com/android/internal/telephony/IccCardStatus.java105
-rw-r--r--telephony/java/com/android/internal/telephony/IccConstants.java4
-rw-r--r--telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java19
-rw-r--r--telephony/java/com/android/internal/telephony/IccProvider.java45
-rw-r--r--telephony/java/com/android/internal/telephony/IccRecords.java18
-rw-r--r--telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java109
-rw-r--r--telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java17
-rw-r--r--telephony/java/com/android/internal/telephony/IccUtils.java11
-rw-r--r--telephony/java/com/android/internal/telephony/MccTable.java659
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java115
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneBase.java312
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneProxy.java37
-rw-r--r--telephony/java/com/android/internal/telephony/RIL.java112
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java8
-rw-r--r--telephony/java/com/android/internal/telephony/RetryManager.java392
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java167
-rw-r--r--telephony/java/com/android/internal/telephony/ServiceStateTracker.java93
-rw-r--r--telephony/java/com/android/internal/telephony/SmsMessageBase.java64
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyIntents.java24
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyProperties.java27
-rwxr-xr-xtelephony/java/com/android/internal/telephony/cdma/CDMAPhone.java718
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCall.java2
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java125
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java18
-rwxr-xr-x[-rw-r--r--]telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java45
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java5
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java275
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java120
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java482
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/EriManager.java66
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/FeatureCode.java310
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimCard.java497
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimRecords.java58
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java16
-rwxr-xr-x[-rw-r--r--]telephony/java/com/android/internal/telephony/cdma/SmsMessage.java107
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java693
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java125
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java8
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/UserData.java59
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/ApnSetting.java5
-rwxr-xr-xtelephony/java/com/android/internal/telephony/gsm/GSMPhone.java429
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmCall.java1
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java18
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmConnection.java8
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java316
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java23
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java205
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/MccTable.java406
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/PdpConnection.java8
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java24
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SIMRecords.java183
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimCard.java484
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimTlv.java8
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java20
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java424
-rw-r--r--telephony/java/com/android/internal/telephony/test/SimulatedCommands.java40
-rw-r--r--test-runner/android/test/AndroidTestRunner.java19
-rw-r--r--test-runner/android/test/InstrumentationTestRunner.java2
-rw-r--r--test-runner/android/test/IsolatedContext.java26
-rw-r--r--test-runner/android/test/ProviderTestCase.java1
-rw-r--r--test-runner/android/test/RenamingDelegatingContext.java35
-rw-r--r--test-runner/android/test/SyncBaseInstrumentation.java33
-rw-r--r--test-runner/android/test/TestRunner.java8
-rw-r--r--test-runner/android/test/TouchUtils.java1
-rw-r--r--test-runner/android/test/mock/MockContentProvider.java32
-rw-r--r--test-runner/android/test/mock/MockPackageManager.java14
-rw-r--r--tests/AndroidTests/AndroidManifest.xml1
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_1.vcf3
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_2.vcf3
-rw-r--r--tests/AndroidTests/res/raw/v21_simple_3.vcf (renamed from tests/AndroidTests/res/raw/v21_simple.vcf)0
-rwxr-xr-xtests/AndroidTests/run_test.sh2
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java182
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/GsmSmsTest.java252
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/MccTableTest.java87
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/SMSTest.java288
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/VCardTests.java773
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java178
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java322
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java923
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java30
-rw-r--r--tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java313
-rw-r--r--tests/BrowserPowerTest/Android.mk30
-rw-r--r--tests/BrowserPowerTest/AndroidManifest.xml46
-rw-r--r--tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java51
-rw-r--r--tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestActivity.java253
-rw-r--r--tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestRunner.java31
-rw-r--r--tests/BrowserTestPlugin/Android.mk36
-rw-r--r--tests/BrowserTestPlugin/AndroidManifest.xml35
-rw-r--r--tests/BrowserTestPlugin/MODULE_LICENSE_APACHE20
-rw-r--r--tests/BrowserTestPlugin/NOTICE190
-rw-r--r--tests/BrowserTestPlugin/jni/Android.mk49
-rw-r--r--tests/BrowserTestPlugin/jni/PluginObject.cpp202
-rw-r--r--tests/BrowserTestPlugin/jni/PluginObject.h87
-rw-r--r--tests/BrowserTestPlugin/jni/event/EventPlugin.cpp198
-rw-r--r--tests/BrowserTestPlugin/jni/event/EventPlugin.h45
-rw-r--r--tests/BrowserTestPlugin/jni/main.cpp280
-rw-r--r--tests/BrowserTestPlugin/jni/main.h31
-rwxr-xr-xtests/BrowserTestPlugin/res/drawable/browser_test_plugin.pngbin0 -> 3610 bytes
-rw-r--r--tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java15
-rw-r--r--tests/CoreTests/android/content/SyncStorageEngineTest.java3
-rw-r--r--tests/CoreTests/android/core/RecurrenceSetTest.java82
-rw-r--r--tests/CoreTests/android/core/RequestAPITest.java24
-rw-r--r--tests/CoreTests/android/webkit/CookieTest.java2
-rw-r--r--tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java77
-rw-r--r--tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java18
-rw-r--r--tests/CoreTests/com/android/internal/telephony/TelephonyTests.java1
-rw-r--r--tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java219
-rw-r--r--tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java4
-rwxr-xr-xtests/DumpRenderTree/assets/run_layout_tests.py23
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java53
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java22
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java8
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java181
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java241
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java112
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java117
-rw-r--r--tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java92
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/big_drawable_background.9.pngbin0 -> 430 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/black_square.pngbin0 -> 116 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/black_square_stretchable.9.pngbin0 -> 135 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/drawable_background.9.pngbin0 -> 354 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_pause_1.pngbin0 -> 668 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_backward_1.pngbin0 -> 1296 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_forward_1.pngbin0 -> 1247 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/big_drawable_background.9.png (renamed from tests/FrameworkTest/res/drawable/big_drawable_background.9.png)bin330 -> 330 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/black_square.png (renamed from tests/FrameworkTest/res/drawable/black_square.png)bin151 -> 151 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/black_square_stretchable.9.png (renamed from tests/FrameworkTest/res/drawable/black_square_stretchable.9.png)bin175 -> 175 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/drawable_background.9.png (renamed from tests/FrameworkTest/res/drawable/drawable_background.9.png)bin270 -> 270 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_pause_1.png (renamed from tests/FrameworkTest/res/drawable/sym_now_playing_pause_1.png)bin3038 -> 3038 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_backward_1.png (renamed from tests/FrameworkTest/res/drawable/sym_now_playing_skip_backward_1.png)bin3262 -> 3262 bytes
-rw-r--r--tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_forward_1.png (renamed from tests/FrameworkTest/res/drawable/sym_now_playing_skip_forward_1.png)bin3275 -> 3275 bytes
-rw-r--r--tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java35
-rw-r--r--tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java498
-rw-r--r--tests/LowStorageTest/Android.mk25
-rw-r--r--tests/LowStorageTest/AndroidManifest.xml13
-rw-r--r--tests/LowStorageTest/res/layout/main.xml72
-rw-r--r--tests/LowStorageTest/res/values/strings.xml23
-rw-r--r--tests/LowStorageTest/src/com/android/lowstoragetest/LowStorageTest.java132
-rw-r--r--tests/appwidgets/AppWidgetHostTest/res/drawable-hdpi/oh_hai_icon.pngbin0 -> 4912 bytes
-rw-r--r--tests/appwidgets/AppWidgetHostTest/res/drawable-mdpi/oh_hai_icon.png (renamed from tests/appwidgets/AppWidgetHostTest/res/drawable/oh_hai_icon.png)bin2988 -> 2988 bytes
-rw-r--r--tests/backup/AndroidManifest.xml1
-rwxr-xr-xtests/backup/backup_stress_test.sh64
-rwxr-xr-xtests/backup/test_backup.sh66
-rwxr-xr-xtests/backup/test_backup_common.sh33
-rwxr-xr-xtests/backup/test_restore.sh96
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java4
-rw-r--r--tools/aapt/AaptAssets.cpp13
-rw-r--r--tools/aapt/AaptAssets.h23
-rw-r--r--tools/aapt/Android.mk5
-rw-r--r--tools/aapt/Bundle.h10
-rw-r--r--tools/aapt/Command.cpp251
-rw-r--r--tools/aapt/Images.cpp5
-rw-r--r--tools/aapt/Main.cpp22
-rw-r--r--tools/aapt/Main.h12
-rw-r--r--tools/aapt/Package.cpp12
-rw-r--r--tools/aapt/Resource.cpp390
-rw-r--r--tools/aapt/ResourceTable.cpp87
-rw-r--r--tools/aapt/ResourceTable.h10
-rw-r--r--tools/aapt/SourcePos.cpp2
-rw-r--r--tools/aapt/XMLNode.cpp10
-rw-r--r--tools/aapt/ZipEntry.cpp696
-rw-r--r--tools/aapt/ZipEntry.h345
-rw-r--r--tools/aapt/ZipFile.cpp1297
-rw-r--r--tools/aapt/ZipFile.h270
-rwxr-xr-xtools/aidl/AST.cpp17
-rw-r--r--tools/aidl/generate_java.cpp7
-rw-r--r--tools/aidl/options.h1
-rw-r--r--tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java61
-rw-r--r--tools/layoutlib/bridge/src/android/webkit/WebView.java7
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java65
-rw-r--r--tools/localize/Perforce.cpp3
-rw-r--r--tools/localize/SourcePos.cpp1
-rw-r--r--tools/localize/XMLHandler.h1
-rw-r--r--tools/localize/file_utils.cpp3
-rw-r--r--tools/localize/file_utils.h1
-rw-r--r--tools/localize/localize.cpp1
-rw-r--r--tools/localize/localize_test.cpp1
-rw-r--r--tools/localize/merge_res_and_xliff_test.cpp1
-rw-r--r--vpn/java/android/net/vpn/VpnManager.java21
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl2
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java7
-rw-r--r--wifi/java/android/net/wifi/WifiNative.java2
-rw-r--r--wifi/java/android/net/wifi/WifiStateTracker.java65
2785 files changed, 153241 insertions, 54903 deletions
diff --git a/Android.mk b/Android.mk
index 16d77e4..138ff09 100644
--- a/Android.mk
+++ b/Android.mk
@@ -65,8 +65,11 @@ endif
## READ ME: ########################################################
LOCAL_SRC_FILES += \
core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
- core/java/android/accessibilityservice/IEventListener.aidl \
- core/java/android/accounts/IAccountsService.aidl \
+ core/java/android/accessibilityservice/IEventListener.aidl \
+ core/java/android/accounts/IAccountManager.aidl \
+ core/java/android/accounts/IAccountManagerResponse.aidl \
+ core/java/android/accounts/IAccountAuthenticator.aidl \
+ core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
core/java/android/app/IActivityController.aidl \
core/java/android/app/IActivityPendingResult.aidl \
core/java/android/app/IActivityWatcher.aidl \
@@ -80,15 +83,15 @@ LOCAL_SRC_FILES += \
core/java/android/app/IStatusBar.aidl \
core/java/android/app/IThumbnailReceiver.aidl \
core/java/android/app/ITransientNotification.aidl \
- core/java/android/app/IWallpaperService.aidl \
- core/java/android/app/IWallpaperServiceCallback.aidl \
+ core/java/android/app/IWallpaperManager.aidl \
+ core/java/android/app/IWallpaperManagerCallback.aidl \
core/java/android/backup/IBackupManager.aidl \
core/java/android/backup/IRestoreObserver.aidl \
core/java/android/backup/IRestoreSession.aidl \
+ core/java/android/bluetooth/IBluetooth.aidl \
core/java/android/bluetooth/IBluetoothA2dp.aidl \
- core/java/android/bluetooth/IBluetoothDevice.aidl \
- core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadset.aidl \
+ core/java/android/bluetooth/IBluetoothPbap.aidl \
core/java/android/content/IContentService.aidl \
core/java/android/content/IIntentReceiver.aidl \
core/java/android/content/IIntentSender.aidl \
@@ -111,6 +114,9 @@ LOCAL_SRC_FILES += \
core/java/android/os/IParentalControlCallback.aidl \
core/java/android/os/IPermissionController.aidl \
core/java/android/os/IPowerManager.aidl \
+ core/java/android/service/wallpaper/IWallpaperConnection.aidl \
+ core/java/android/service/wallpaper/IWallpaperEngine.aidl \
+ core/java/android/service/wallpaper/IWallpaperService.aidl \
core/java/android/text/IClipboard.aidl \
core/java/android/view/accessibility/IAccessibilityManager.aidl \
core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
@@ -120,8 +126,6 @@ LOCAL_SRC_FILES += \
core/java/android/view/IWindow.aidl \
core/java/android/view/IWindowManager.aidl \
core/java/android/view/IWindowSession.aidl \
- core/java/android/speech/IRecognitionListener.aidl \
- core/java/android/speech/IRecognitionService.aidl \
core/java/android/speech/tts/ITts.aidl \
core/java/android/speech/tts/ITtsCallback.aidl \
core/java/com/android/internal/app/IBatteryStats.aidl \
@@ -137,13 +141,13 @@ LOCAL_SRC_FILES += \
core/java/com/android/internal/view/IInputMethodClient.aidl \
core/java/com/android/internal/view/IInputMethodManager.aidl \
core/java/com/android/internal/view/IInputMethodSession.aidl \
- im/java/android/im/IImPlugin.aidl \
location/java/android/location/IGeocodeProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
location/java/android/location/ILocationProvider.aidl \
+ location/java/android/location/INetInitiatedListener.aidl \
media/java/android/media/IAudioService.aidl \
media/java/android/media/IMediaScannerListener.aidl \
media/java/android/media/IMediaScannerService.aidl \
@@ -195,7 +199,10 @@ framework_built := $(LOCAL_BUILT_MODULE)
# relative to the root of the build tree.
# ============================================================
aidl_files := \
- frameworks/base/core/java/android/accounts/IAccountsService.aidl \
+ frameworks/base/core/java/android/accounts/IAccountManager.aidl \
+ frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \
+ frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl \
+ frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \
frameworks/base/core/java/android/app/Notification.aidl \
frameworks/base/core/java/android/app/PendingIntent.aidl \
frameworks/base/core/java/android/content/ComponentName.aidl \
@@ -221,7 +228,6 @@ aidl_files := \
frameworks/base/graphics/java/android/graphics/Bitmap.aidl \
frameworks/base/graphics/java/android/graphics/Rect.aidl \
frameworks/base/graphics/java/android/graphics/Region.aidl \
- frameworks/base/im/java/android/im/IImPlugin.aidl \
frameworks/base/location/java/android/location/Criteria.aidl \
frameworks/base/location/java/android/location/Location.aidl \
frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
@@ -374,6 +380,34 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-hdf sdk.rel.id $(framework_docs_SDK_REL_ID) \
-hdf sdk.current $(framework_docs_SDK_CURRENT_DIR)
+# ==== the api stubs and current.xml ===========================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
+LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
+LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
+LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
+
+LOCAL_MODULE := api-stubs
+
+LOCAL_DROIDDOC_OPTIONS:=\
+ $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
+ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \
+ -apixml $(INTERNAL_PLATFORM_API_FILE) \
+ -nodocs
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=build/tools/droiddoc/templates-sdk
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-sdk
+
+include $(BUILD_DROIDDOC)
+
+$(full_target): $(framework_built)
+$(INTERNAL_PLATFORM_API_FILE): $(full_target)
+$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
+
# ==== static html in the sdk ==================================
include $(CLEAR_VARS)
@@ -392,10 +426,7 @@ LOCAL_DROIDDOC_OPTIONS:=\
-title "Android SDK" \
-proofread $(OUT_DOCS)/$(LOCAL_MODULE)-proofread.txt \
-todo $(OUT_DOCS)/$(LOCAL_MODULE)-docs-todo.html \
- -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src \
- -apixml $(INTERNAL_PLATFORM_API_FILE) \
-sdkvalues $(OUT_DOCS) \
- -warning 3 \
-hdf android.whichdoc offline
ifeq ($(framework_docs_SDK_PREVIEW),true)
@@ -415,8 +446,6 @@ $(static_doc_index_redirect): \
$(full_target): $(static_doc_index_redirect)
$(full_target): $(framework_built)
-$(INTERNAL_PLATFORM_API_FILE): $(full_target)
-$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
# ==== docs for the web (on the google app engine server) =======================
@@ -501,5 +530,3 @@ include $(BUILD_JAVA_LIBRARY)
ifeq (,$(ONE_SHOT_MAKEFILE))
include $(call first-makefiles-under,$(LOCAL_PATH))
endif
-
-
diff --git a/api/4.xml b/api/4.xml
index fc54859..bca9816 100644
--- a/api/4.xml
+++ b/api/4.xml
@@ -275281,7 +275281,7 @@
extends="java.lang.Enum"
abstract="false"
static="false"
- final="true"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -277334,7 +277334,7 @@
<package name="java.util.concurrent.locks"
>
<class name="AbstractQueuedSynchronizer"
- extends="java.lang.Object"
+ extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
abstract="true"
static="false"
final="false"
diff --git a/api/current.xml b/api/current.xml
index fc54859..d6b272f 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -122,6 +122,28 @@
visibility="public"
>
</field>
+<field name="ACCOUNT_MANAGER_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.ACCOUNT_MANAGER_SERVICE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATE_ACCOUNTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.AUTHENTICATE_ACCOUNTS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BATTERY_STATS"
type="java.lang.String"
transient="false"
@@ -155,6 +177,17 @@
visibility="public"
>
</field>
+<field name="BIND_WALLPAPER"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.BIND_WALLPAPER&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BLUETOOTH"
type="java.lang.String"
transient="false"
@@ -573,6 +606,17 @@
visibility="public"
>
</field>
+<field name="MANAGE_ACCOUNTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.MANAGE_ACCOUNTS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MANAGE_APP_TOKENS"
type="java.lang.String"
transient="false"
@@ -1057,6 +1101,17 @@
visibility="public"
>
</field>
+<field name="USE_CREDENTIALS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.permission.USE_CREDENTIALS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="VIBRATE"
type="java.lang.String"
transient="false"
@@ -1604,6 +1659,17 @@
visibility="public"
>
</field>
+<field name="accountType"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843407"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="action"
type="int"
transient="false"
@@ -2594,6 +2660,17 @@
visibility="public"
>
</field>
+<field name="contentAuthority"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843408"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="contentDescription"
type="int"
transient="false"
@@ -4618,6 +4695,17 @@
visibility="public"
>
</field>
+<field name="killAfterRestore"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843420"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="label"
type="int"
transient="false"
@@ -6367,6 +6455,17 @@
visibility="public"
>
</field>
+<field name="required"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843406"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="resizeable"
type="int"
transient="false"
@@ -6389,6 +6488,17 @@
visibility="public"
>
</field>
+<field name="restoreNeedsApplication"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843421"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="right"
type="int"
transient="false"
@@ -7412,6 +7522,17 @@
visibility="public"
>
</field>
+<field name="supportsUploading"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843419"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="syncable"
type="int"
transient="false"
@@ -8314,6 +8435,17 @@
visibility="public"
>
</field>
+<field name="userVisible"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843409"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="value"
type="int"
transient="false"
@@ -8479,6 +8611,94 @@
visibility="public"
>
</field>
+<field name="wallpaperCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843413"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843414"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperIntraCloseEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843417"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperIntraCloseExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843418"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperIntraOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843415"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperIntraOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843416"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperOpenEnterAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843411"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="wallpaperOpenExitAnimation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843412"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="webViewStyle"
type="int"
transient="false"
@@ -8677,6 +8897,17 @@
visibility="public"
>
</field>
+<field name="windowShowWallpaper"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843410"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="windowSoftInputMode"
type="int"
transient="false"
@@ -10384,6 +10615,17 @@
visibility="public"
>
</field>
+<field name="screen_background_dark_transparent"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="17301673"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="screen_background_light"
type="int"
transient="false"
@@ -11988,6 +12230,17 @@
visibility="public"
>
</field>
+<field name="Animation_InputMethod"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973910"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Animation_Toast"
type="int"
transient="false"
@@ -12516,6 +12769,39 @@
visibility="public"
>
</field>
+<field name="Theme_Wallpaper"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973918"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Wallpaper_NoTitleBar"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973919"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="Theme_Wallpaper_NoTitleBar_Fullscreen"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16973920"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="Widget"
type="int"
transient="false"
@@ -13250,6 +13536,1962 @@
</field>
</class>
</package>
+<package name="android.accounts"
+>
+<class name="AbstractAccountAuthenticator"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AbstractAccountAuthenticator"
+ type="android.accounts.AbstractAccountAuthenticator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</constructor>
+<method name="addAccount"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="confirmCredentials"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="confirmPassword"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="editProperties"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getAccountRemovalAllowed"
+ return="android.os.Bundle"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="getAuthToken"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="getAuthTokenLabel"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+</method>
+<method name="getIAccountAuthenticator"
+ return="android.accounts.IAccountAuthenticator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasFeatures"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<exception name="NetworkErrorException" type="android.accounts.NetworkErrorException">
+</exception>
+</method>
+<method name="updateCredentials"
+ return="android.os.Bundle"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.AccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="Account"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="Account"
+ type="android.accounts.Account"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="Account"
+ type="android.accounts.Account"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="in" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AccountAuthenticatorActivity"
+ extends="android.app.Activity"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AccountAuthenticatorActivity"
+ type="android.accounts.AccountAuthenticatorActivity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="setAccountAuthenticatorResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="result" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="AccountAuthenticatorResponse"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="AccountAuthenticatorResponse"
+ type="android.accounts.AccountAuthenticatorResponse"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+</constructor>
+<constructor name="AccountAuthenticatorResponse"
+ type="android.accounts.AccountAuthenticatorResponse"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onError"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="errorCode" type="int">
+</parameter>
+<parameter name="errorMessage" type="java.lang.String">
+</parameter>
+</method>
+<method name="onRequestContinued"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="result" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AccountManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="addAccount"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="addAccountOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="addAccountExplicitly"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="addOnAccountsUpdatedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.accounts.OnAccountsUpdatedListener">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+<parameter name="updateImmediately" type="boolean">
+</parameter>
+</method>
+<method name="blockingGetAuthToken"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="notifyAuthFailure" type="boolean">
+</parameter>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="clearPassword"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="confirmCredentials"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="confirmPassword"
+ return="android.accounts.AccountManagerFuture&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="editProperties"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="get"
+ return="android.accounts.AccountManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getAccounts"
+ return="android.accounts.Account[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAccountsByType"
+ return="android.accounts.Account[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
+<method name="getAccountsByTypeAndFeatures"
+ return="android.accounts.AccountManagerFuture&lt;android.accounts.Account[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.accounts.Account[]&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthToken"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthToken"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="notifyAuthFailure" type="boolean">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthTokenByFeatures"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<parameter name="activityForPrompting" type="android.app.Activity">
+</parameter>
+<parameter name="addAccountOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="getAuthenticatorTypes"
+ return="android.accounts.AuthenticatorDescription[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPassword"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+</method>
+<method name="getUserData"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="invalidateAuthToken"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+</method>
+<method name="peekAuthToken"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+</method>
+<method name="removeAccount"
+ return="android.accounts.AccountManagerFuture&lt;java.lang.Boolean&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;java.lang.Boolean&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+<method name="removeOnAccountsUpdatedListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.accounts.OnAccountsUpdatedListener">
+</parameter>
+</method>
+<method name="setAuthToken"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="authToken" type="java.lang.String">
+</parameter>
+</method>
+<method name="setPassword"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+</method>
+<method name="setUserData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="updateCredentials"
+ return="android.accounts.AccountManagerFuture&lt;android.os.Bundle&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="loginOptions" type="android.os.Bundle">
+</parameter>
+<parameter name="activity" type="android.app.Activity">
+</parameter>
+<parameter name="callback" type="android.accounts.AccountManagerCallback&lt;android.os.Bundle&gt;">
+</parameter>
+<parameter name="handler" type="android.os.Handler">
+</parameter>
+</method>
+</class>
+<interface name="AccountManagerCallback"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="run"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="future" type="android.accounts.AccountManagerFuture&lt;V&gt;">
+</parameter>
+</method>
+</interface>
+<interface name="AccountManagerFuture"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.util.concurrent.Future">
+</implements>
+<method name="get"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<exception name="ExecutionException" type="java.util.concurrent.ExecutionException">
+</exception>
+<exception name="InterruptedException" type="java.lang.InterruptedException">
+</exception>
+</method>
+<method name="get"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="ExecutionException" type="java.util.concurrent.ExecutionException">
+</exception>
+<exception name="InterruptedException" type="java.lang.InterruptedException">
+</exception>
+<exception name="TimeoutException" type="java.util.concurrent.TimeoutException">
+</exception>
+</method>
+<method name="getResult"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+<method name="getResult"
+ return="V"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="AuthenticatorException" type="android.accounts.AuthenticatorException">
+</exception>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+<exception name="OperationCanceledException" type="android.accounts.OperationCanceledException">
+</exception>
+</method>
+</interface>
+<class name="AuthenticatorDescription"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="AuthenticatorDescription"
+ type="android.accounts.AuthenticatorDescription"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+<parameter name="labelId" type="int">
+</parameter>
+<parameter name="iconId" type="int">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newKey"
+ return="android.accounts.AuthenticatorDescription"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="type" type="java.lang.String">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="iconId"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="labelId"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="type"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AuthenticatorException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="AuthenticatorException"
+ type="android.accounts.AuthenticatorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+<class name="ChooseAccountActivity"
+ extends="android.app.ListActivity"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ChooseAccountActivity"
+ type="android.accounts.ChooseAccountActivity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onCreate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="savedInstanceState" type="android.os.Bundle">
+</parameter>
+</method>
+</class>
+<class name="Constants"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<field name="ACCOUNTS_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accounts&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_AUTHENTICATOR_RESPONSE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountAuthenticatorResponse&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_MANAGER_RESPONSE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountManagerResponse&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_NAME_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authAccount&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_TYPE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;accountType&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATOR_ATTRIBUTES_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;account-authenticator&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATOR_INTENT_ACTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.accounts.AccountAuthenticator&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATOR_META_DATA_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.accounts.AccountAuthenticator&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHENTICATOR_TYPES_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authenticator_types&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTHTOKEN_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authtoken&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTH_FAILED_MESSAGE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authFailedMessage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="AUTH_TOKEN_LABEL_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;authTokenLabelKey&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="BOOLEAN_RESULT_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;booleanResult&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_BAD_ARGUMENTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_BAD_REQUEST"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_CANCELED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_INVALID_RESPONSE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;errorCode&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_NETWORK_ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_REMOTE_EXCEPTION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CODE_UNSUPPORTED_OPERATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_MESSAGE_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;errorMessage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INTENT_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;intent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="LOGIN_ACCOUNTS_CHANGED_ACTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.accounts.LOGIN_ACCOUNTS_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PASSWORD_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;password&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="USERDATA_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;userdata&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="IAccountAuthenticator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.IInterface">
+</implements>
+<method name="addAccount"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="requiredFeatures" type="java.lang.String[]">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="confirmCredentials"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="confirmPassword"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="password" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="editProperties"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getAccountRemovalAllowed"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getAuthToken"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getAuthTokenLabel"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="hasFeatures"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="features" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="updateCredentials"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="response" type="android.accounts.IAccountAuthenticatorResponse">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authTokenType" type="java.lang.String">
+</parameter>
+<parameter name="options" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
+<class name="IAccountAuthenticator.Stub"
+ extends="android.os.Binder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.accounts.IAccountAuthenticator">
+</implements>
+<constructor name="IAccountAuthenticator.Stub"
+ type="android.accounts.IAccountAuthenticator.Stub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="asBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="asInterface"
+ return="android.accounts.IAccountAuthenticator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="android.os.IBinder">
+</parameter>
+</method>
+<method name="onTransact"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+<parameter name="data" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<interface name="IAccountAuthenticatorResponse"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.IInterface">
+</implements>
+<method name="onError"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="errorCode" type="int">
+</parameter>
+<parameter name="errorMessage" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="onRequestContinued"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="onResult"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="android.os.Bundle">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
+<class name="IAccountAuthenticatorResponse.Stub"
+ extends="android.os.Binder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.accounts.IAccountAuthenticatorResponse">
+</implements>
+<constructor name="IAccountAuthenticatorResponse.Stub"
+ type="android.accounts.IAccountAuthenticatorResponse.Stub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="asBinder"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="asInterface"
+ return="android.accounts.IAccountAuthenticatorResponse"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="obj" type="android.os.IBinder">
+</parameter>
+</method>
+<method name="onTransact"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="code" type="int">
+</parameter>
+<parameter name="data" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<class name="NetworkErrorException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="NetworkErrorException"
+ type="android.accounts.NetworkErrorException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+<interface name="OnAccountsUpdatedListener"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onAccountsUpdated"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="accounts" type="android.accounts.Account[]">
+</parameter>
+</method>
+</interface>
+<class name="OperationCanceledException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="OperationCanceledException"
+ type="android.accounts.OperationCanceledException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+</class>
+</package>
<package name="android.app"
>
<class name="Activity"
@@ -13790,6 +16032,17 @@
<parameter name="data" type="android.content.Intent">
</parameter>
</method>
+<method name="onAttachedToWindow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onChildTitleChanged"
return="void"
abstract="false"
@@ -13993,6 +16246,17 @@
visibility="protected"
>
</method>
+<method name="onDetachedFromWindow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -14796,6 +17060,27 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="startActivity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="fillInIntent" type="android.content.Intent">
+</parameter>
+<parameter name="flagsMask" type="int">
+</parameter>
+<parameter name="flagsValues" type="int">
+</parameter>
+<exception name="PendingIntent.CanceledException" type="android.app.PendingIntent.CanceledException">
+</exception>
+</method>
<method name="startActivityForResult"
return="void"
abstract="false"
@@ -14811,6 +17096,29 @@
<parameter name="requestCode" type="int">
</parameter>
</method>
+<method name="startActivityForResult"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="fillInIntent" type="android.content.Intent">
+</parameter>
+<parameter name="flagsMask" type="int">
+</parameter>
+<parameter name="flagsValues" type="int">
+</parameter>
+<exception name="PendingIntent.CanceledException" type="android.app.PendingIntent.CanceledException">
+</exception>
+</method>
<method name="startActivityFromChild"
return="void"
abstract="false"
@@ -14828,6 +17136,31 @@
<parameter name="requestCode" type="int">
</parameter>
</method>
+<method name="startActivityFromChild"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="child" type="android.app.Activity">
+</parameter>
+<parameter name="intent" type="android.app.PendingIntent">
+</parameter>
+<parameter name="requestCode" type="int">
+</parameter>
+<parameter name="fillInIntent" type="android.content.Intent">
+</parameter>
+<parameter name="flagsMask" type="int">
+</parameter>
+<parameter name="flagsValues" type="int">
+</parameter>
+<exception name="PendingIntent.CanceledException" type="android.app.PendingIntent.CanceledException">
+</exception>
+</method>
<method name="startActivityIfNeeded"
return="boolean"
abstract="false"
@@ -14914,6 +17247,23 @@
<parameter name="get" type="boolean">
</parameter>
</method>
+<method name="triggerSearch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="java.lang.String">
+</parameter>
+<parameter name="appSearchData" type="android.os.Bundle">
+</parameter>
+<parameter name="globalSearch" type="boolean">
+</parameter>
+</method>
<method name="unregisterForContextMenu"
return="void"
abstract="false"
@@ -15147,6 +17497,21 @@
visibility="public"
>
</method>
+<method name="getRunningServiceControlPanel"
+ return="android.app.PendingIntent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="service" type="android.content.ComponentName">
+</parameter>
+<exception name="SecurityException" type="java.lang.SecurityException">
+</exception>
+</method>
<method name="getRunningServices"
return="java.util.List&lt;android.app.ActivityManager.RunningServiceInfo&gt;"
abstract="false"
@@ -15716,6 +18081,39 @@
visibility="public"
>
</field>
+<field name="REASON_PROVIDER_IN_USE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REASON_SERVICE_IN_USE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="REASON_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="importance"
type="int"
transient="false"
@@ -15726,6 +18124,36 @@
visibility="public"
>
</field>
+<field name="importanceReasonCode"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="importanceReasonComponent"
+ type="android.content.ComponentName"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="importanceReasonPid"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="lru"
type="int"
transient="false"
@@ -15767,6 +18195,16 @@
visibility="public"
>
</field>
+<field name="uid"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ActivityManager.RunningServiceInfo"
extends="java.lang.Object"
@@ -15835,6 +18273,50 @@
visibility="public"
>
</field>
+<field name="FLAG_FOREGROUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_PERSISTENT_PROCESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_STARTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SYSTEM_PROCESS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="activeSince"
type="long"
transient="false"
@@ -15855,6 +18337,26 @@
visibility="public"
>
</field>
+<field name="clientLabel"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="clientPackage"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="crashCount"
type="int"
transient="false"
@@ -15865,6 +18367,16 @@
visibility="public"
>
</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="foreground"
type="boolean"
transient="false"
@@ -15935,6 +18447,16 @@
visibility="public"
>
</field>
+<field name="uid"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ActivityManager.RunningTaskInfo"
extends="java.lang.Object"
@@ -17506,6 +20028,17 @@
visibility="public"
>
</method>
+<method name="onAttachedToWindow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onContentChanged"
return="void"
abstract="false"
@@ -17614,6 +20147,17 @@
<parameter name="featureId" type="int">
</parameter>
</method>
+<method name="onDetachedFromWindow"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onKeyDown"
return="boolean"
abstract="false"
@@ -19397,6 +21941,19 @@
<parameter name="intent" type="android.content.Intent">
</parameter>
</method>
+<method name="setIntentRedelivery"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="enabled" type="boolean">
+</parameter>
+</method>
</class>
<class name="KeyguardManager"
extends="java.lang.Object"
@@ -19560,6 +22117,30 @@
visibility="public"
>
</method>
+<method name="onQueryPackageManager"
+ return="java.util.List&lt;android.content.pm.ResolveInfo&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="queryIntent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onSetContentView"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
</class>
<class name="LauncherActivity.IconResizer"
extends="java.lang.Object"
@@ -20095,6 +22676,17 @@
visibility="public"
>
</field>
+<field name="FLAG_FOREGROUND_SERVICE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_INSISTENT"
type="int"
transient="false"
@@ -21136,6 +23728,25 @@
visibility="public"
>
</method>
+<method name="triggerSearch"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="query" type="java.lang.String">
+</parameter>
+<parameter name="launchActivity" type="android.content.ComponentName">
+</parameter>
+<parameter name="appSearchData" type="android.os.Bundle">
+</parameter>
+<parameter name="globalSearch" type="boolean">
+</parameter>
+</method>
<field name="ACTION_KEY"
type="java.lang.String"
transient="false"
@@ -21608,11 +24219,28 @@
synchronized="false"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+<parameter name="startId" type="int">
+</parameter>
+</method>
+<method name="onStartCommand"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
<parameter name="intent" type="android.content.Intent">
</parameter>
+<parameter name="flags" type="int">
+</parameter>
<parameter name="startId" type="int">
</parameter>
</method>
@@ -21636,12 +24264,40 @@
synchronized="false"
static="false"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="isForeground" type="boolean">
</parameter>
</method>
+<method name="startForeground"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+<parameter name="notification" type="android.app.Notification">
+</parameter>
+</method>
+<method name="stopForeground"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="removeNotification" type="boolean">
+</parameter>
+</method>
<method name="stopSelf"
return="void"
abstract="false"
@@ -21679,6 +24335,83 @@
<parameter name="startId" type="int">
</parameter>
</method>
+<field name="START_CONTINUATION_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_FLAG_REDELIVERY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_FLAG_RETRY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_NOT_STICKY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_REDELIVER_INTENT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_STICKY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="START_STICKY_COMPATIBILITY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="TabActivity"
extends="android.app.ActivityGroup"
@@ -21868,6 +24601,175 @@
</parameter>
</method>
</interface>
+<class name="WallpaperManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="clear"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="clearWallpaperOffsets"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="windowToken" type="android.os.IBinder">
+</parameter>
+</method>
+<method name="getDesiredMinimumHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDesiredMinimumWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDrawable"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getInstance"
+ return="android.app.WallpaperManager"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="peekDrawable"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setBitmap"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bitmap" type="android.graphics.Bitmap">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="resid" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setStream"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="data" type="java.io.InputStream">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setWallpaperOffsets"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="windowToken" type="android.os.IBinder">
+</parameter>
+<parameter name="xOffset" type="float">
+</parameter>
+<parameter name="yOffset" type="float">
+</parameter>
+</method>
+<method name="suggestDesiredDimensions"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="minimumWidth" type="int">
+</parameter>
+<parameter name="minimumHeight" type="int">
+</parameter>
+</method>
+</class>
</package>
<package name="android.appwidget"
>
@@ -22614,8 +25516,598 @@
</field>
</class>
</package>
+<package name="android.bluetooth"
+>
+<class name="BluetoothAdapter"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="disable"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="enable"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAddress"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRemoteDevice"
+ return="android.bluetooth.BluetoothDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="address" type="java.lang.String">
+</parameter>
+</method>
+<method name="getScanMode"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getState"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="listenUsingRfcommOn"
+ return="android.bluetooth.BluetoothServerSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="channel" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="setName"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="setScanMode"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mode" type="int">
+</parameter>
+</method>
+<field name="ACTION_SCAN_MODE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.action.SCAN_MODE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_STATE_CHANGED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.action.STATE_CHANGED&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PREVIOUS_SCAN_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.extra.PREVIOUS_SCAN_MODE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_PREVIOUS_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.extra.PREVIOUS_STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_SCAN_MODE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.extra.SCAN_MODE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_STATE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.bluetooth.intent.extra.STATE&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCAN_MODE_CONNECTABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="51"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCAN_MODE_CONNECTABLE_DISCOVERABLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="53"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCAN_MODE_NONE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="50"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_OFF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="40"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="42"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_TURNING_OFF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="43"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="STATE_TURNING_ON"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="41"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="BluetoothDevice"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="createRfcommSocket"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="channel" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAddress"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="out" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="ERROR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="BluetoothServerSocket"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<method name="accept"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="accept"
+ return="android.bluetooth.BluetoothSocket"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timeout" type="int">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
+<class name="BluetoothSocket"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="java.io.Closeable">
+</implements>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="connect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getInputStream"
+ return="java.io.InputStream"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getOutputStream"
+ return="java.io.OutputStream"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+<method name="getRemoteDevice"
+ return="android.bluetooth.BluetoothDevice"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+</package>
<package name="android.content"
>
+<class name="AbstractCursorEntityIterator"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.content.EntityIterator">
+</implements>
+<constructor name="AbstractCursorEntityIterator"
+ type="android.content.AbstractCursorEntityIterator"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="db" type="android.database.sqlite.SQLiteDatabase">
+</parameter>
+<parameter name="entityCursor" type="android.database.Cursor">
+</parameter>
+</constructor>
+<method name="close"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasNext"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newEntityFromCursorLocked"
+ return="android.content.Entity"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+</method>
+<method name="next"
+ return="android.content.Entity"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="reset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
<class name="ActivityNotFoundException"
extends="java.lang.RuntimeException"
abstract="false"
@@ -22738,6 +26230,23 @@
<parameter name="cursor" type="android.database.Cursor">
</parameter>
</method>
+<method name="onQueryEntitiesComplete"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="token" type="int">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+<parameter name="iterator" type="android.content.EntityIterator">
+</parameter>
+</method>
<method name="onUpdateComplete"
return="void"
abstract="false"
@@ -22820,6 +26329,29 @@
<parameter name="orderBy" type="java.lang.String">
</parameter>
</method>
+<method name="startQueryEntities"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="token" type="int">
+</parameter>
+<parameter name="cookie" type="java.lang.Object">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="orderBy" type="java.lang.String">
+</parameter>
+</method>
<method name="startUpdate"
return="void"
abstract="false"
@@ -23445,6 +26977,21 @@
visibility="public"
>
</constructor>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
<method name="attachInfo"
return="void"
abstract="false"
@@ -23564,6 +27111,21 @@
<parameter name="values" type="android.content.ContentValues">
</parameter>
</method>
+<method name="insertEntity"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+</method>
<method name="isTemporary"
return="boolean"
abstract="false"
@@ -23682,6 +27244,25 @@
<parameter name="sortOrder" type="java.lang.String">
</parameter>
</method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+</method>
<method name="setPathPermissions"
return="void"
abstract="false"
@@ -23740,6 +27321,695 @@
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
</method>
+<method name="updateEntity"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+</method>
+</class>
+<class name="ContentProviderClient"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="bulkInsert"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="initialValues" type="android.content.ContentValues[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="delete"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getLocalContentProvider"
+ return="android.content.ContentProvider"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getType"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="insert"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="initialValues" type="android.content.ContentValues">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="insertEntity"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="openAssetFile"
+ return="android.content.res.AssetFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mode" type="java.lang.String">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="openFile"
+ return="android.os.ParcelFileDescriptor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="mode" type="java.lang.String">
+</parameter>
+<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="query"
+ return="android.database.Cursor"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="projection" type="java.lang.String[]">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="release"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="update"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="updateEntity"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="entity" type="android.content.Entity">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
+<class name="ContentProviderOperation"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="apply"
+ return="android.content.ContentProviderResult"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProvider">
+</parameter>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getUri"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isReadOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isWriteOperation"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isYieldAllowed"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newAssertQuery"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newDelete"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newInsert"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="newUpdate"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="resolveSelectionArgsBackReferences"
+ return="java.lang.String[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+</method>
+<method name="resolveValueBackReferences"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backRefs" type="android.content.ContentProviderResult[]">
+</parameter>
+<parameter name="numBackRefs" type="int">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ContentProviderOperation.Builder"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="build"
+ return="android.content.ContentProviderOperation"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="withExpectedCount"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="count" type="int">
+</parameter>
+</method>
+<method name="withSelection"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+</method>
+<method name="withSelectionBackReference"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="selectionArgIndex" type="int">
+</parameter>
+<parameter name="previousResult" type="int">
+</parameter>
+</method>
+<method name="withValue"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="value" type="java.lang.Object">
+</parameter>
+</method>
+<method name="withValueBackReference"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="key" type="java.lang.String">
+</parameter>
+<parameter name="previousResult" type="int">
+</parameter>
+</method>
+<method name="withValueBackReferences"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="backReferences" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="withValues"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="withYieldAllowed"
+ return="android.content.ContentProviderOperation.Builder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="yieldAllowed" type="boolean">
+</parameter>
+</method>
+</class>
+<class name="ContentProviderResult"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</constructor>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="count" type="int">
+</parameter>
+</constructor>
+<constructor name="ContentProviderResult"
+ type="android.content.ContentProviderResult"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="count"
+ type="java.lang.Integer"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="uri"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="ContentQueryMap"
extends="java.util.Observable"
@@ -23843,6 +28113,66 @@
<parameter name="context" type="android.content.Context">
</parameter>
</constructor>
+<method name="acquireContentProviderClient"
+ return="android.content.ContentProviderClient"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+</method>
+<method name="acquireContentProviderClient"
+ return="android.content.ContentProviderClient"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="addStatusChangeListener"
+ return="java.lang.Object"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="mask" type="int">
+</parameter>
+<parameter name="callback" type="android.content.SyncStatusObserver">
+</parameter>
+</method>
+<method name="applyBatch"
+ return="android.content.ContentProviderResult[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="operations" type="java.util.ArrayList&lt;android.content.ContentProviderOperation&gt;">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
<method name="bulkInsert"
return="int"
abstract="false"
@@ -23865,12 +28195,27 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="uri" type="android.net.Uri">
</parameter>
</method>
+<method name="cancelSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
<method name="delete"
return="int"
abstract="false"
@@ -23888,6 +28233,58 @@
<parameter name="selectionArgs" type="java.lang.String[]">
</parameter>
</method>
+<method name="getIsSyncable"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
+<method name="getMasterSyncAutomatically"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSyncAdapterTypes"
+ return="android.content.SyncAdapterType[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSyncAutomatically"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
<method name="getType"
return="java.lang.String"
abstract="false"
@@ -23916,6 +28313,36 @@
<parameter name="values" type="android.content.ContentValues">
</parameter>
</method>
+<method name="isSyncActive"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
+<method name="isSyncPending"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+</method>
<method name="notifyChange"
return="void"
abstract="false"
@@ -24050,6 +28477,27 @@
<parameter name="sortOrder" type="java.lang.String">
</parameter>
</method>
+<method name="queryEntities"
+ return="android.content.EntityIterator"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="selection" type="java.lang.String">
+</parameter>
+<parameter name="selectionArgs" type="java.lang.String[]">
+</parameter>
+<parameter name="sortOrder" type="java.lang.String">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
<method name="registerContentObserver"
return="void"
abstract="false"
@@ -24067,6 +28515,83 @@
<parameter name="observer" type="android.database.ContentObserver">
</parameter>
</method>
+<method name="removeStatusChangeListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="handle" type="java.lang.Object">
+</parameter>
+</method>
+<method name="requestSync"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="extras" type="android.os.Bundle">
+</parameter>
+</method>
+<method name="setIsSyncable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="syncable" type="int">
+</parameter>
+</method>
+<method name="setMasterSyncAutomatically"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sync" type="boolean">
+</parameter>
+</method>
+<method name="setSyncAutomatically"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="sync" type="boolean">
+</parameter>
+</method>
<method name="startSync"
return="void"
abstract="false"
@@ -24074,7 +28599,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="uri" type="android.net.Uri">
@@ -24189,7 +28714,7 @@
value="&quot;account&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -24222,6 +28747,28 @@
value="&quot;force&quot;"
static="true"
final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYNC_EXTRAS_INITIALIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;initialize&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SYNC_EXTRAS_MANUAL"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;force&quot;"
+ static="true"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -24900,7 +29447,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<exception name="IOException" type="java.io.IOException">
@@ -25349,7 +29896,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -25360,7 +29907,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -25371,7 +29918,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -25523,7 +30070,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -25690,7 +30237,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="bitmap" type="android.graphics.Bitmap">
@@ -25705,7 +30252,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="data" type="java.io.InputStream">
@@ -25806,6 +30353,17 @@
visibility="public"
>
</field>
+<field name="ACCOUNT_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;account&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTIVITY_SERVICE"
type="java.lang.String"
transient="false"
@@ -25861,6 +30419,17 @@
visibility="public"
>
</field>
+<field name="BLUETOOTH_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;bluetooth&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="CLIPBOARD_SERVICE"
type="java.lang.String"
transient="false"
@@ -27298,6 +31867,199 @@
</parameter>
</method>
</interface>
+<class name="Entity"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="Entity"
+ type="android.content.Entity"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</constructor>
+<method name="addSubValue"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</method>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEntityValues"
+ return="android.content.ContentValues"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSubValues"
+ return="java.util.ArrayList&lt;android.content.Entity.NamedContentValues&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="Entity.NamedContentValues"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Entity.NamedContentValues"
+ type="android.content.Entity.NamedContentValues"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="values" type="android.content.ContentValues">
+</parameter>
+</constructor>
+<field name="uri"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="values"
+ type="android.content.ContentValues"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="EntityIterator"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="close"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="hasNext"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="next"
+ return="android.content.Entity"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="reset"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</interface>
<class name="Intent"
extends="java.lang.Object"
abstract="false"
@@ -29467,6 +34229,17 @@
visibility="public"
>
</field>
+<field name="ACTION_REMOTE_INTENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.action.REMOTE_INTENT&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_RUN"
type="java.lang.String"
transient="false"
@@ -29950,6 +34723,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_CHANGED_COMPONENT_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.changed_component_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_DATA_REMOVED"
type="java.lang.String"
transient="false"
@@ -29983,6 +34767,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_INITIAL_INTENTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.INITIAL_INTENTS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_INTENT"
type="java.lang.String"
transient="false"
@@ -30016,6 +34811,17 @@
visibility="public"
>
</field>
+<field name="EXTRA_REMOTE_INTENT_TOKEN"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.intent.extra.remote_intent_token&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_REPLACING"
type="java.lang.String"
transient="false"
@@ -31622,6 +36428,88 @@
</parameter>
</method>
</class>
+<class name="OperationApplicationException"
+ extends="java.lang.Exception"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cause" type="java.lang.Throwable">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="numSuccessfulYieldPoints" type="int">
+</parameter>
+</constructor>
+<constructor name="OperationApplicationException"
+ type="android.content.OperationApplicationException"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="numSuccessfulYieldPoints" type="int">
+</parameter>
+</constructor>
+<method name="getNumSuccessfulYieldPoints"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
<class name="ReceiverCallNotAllowedException"
extends="android.util.AndroidRuntimeException"
abstract="false"
@@ -32112,6 +37000,167 @@
</parameter>
</method>
</interface>
+<class name="SyncAdapterType"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="SyncAdapterType"
+ type="android.content.SyncAdapterType"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+<parameter name="userVisible" type="boolean">
+</parameter>
+<parameter name="supportsUploading" type="boolean">
+</parameter>
+</constructor>
+<constructor name="SyncAdapterType"
+ type="android.content.SyncAdapterType"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isUserVisible"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="newKey"
+ return="android.content.SyncAdapterType"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="authority" type="java.lang.String">
+</parameter>
+<parameter name="accountType" type="java.lang.String">
+</parameter>
+</method>
+<method name="supportsUploading"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="accountType"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="authority"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="isKey"
+ type="boolean"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="SyncStatusObserver"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onStatusChanged"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="which" type="int">
+</parameter>
+</method>
+</interface>
<class name="UriMatcher"
extends="java.lang.Object"
abstract="false"
@@ -33326,6 +38375,134 @@
>
</field>
</class>
+<class name="FeatureInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="FeatureInfo"
+ type="android.content.pm.FeatureInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<constructor name="FeatureInfo"
+ type="android.content.pm.FeatureInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="orig" type="android.content.pm.FeatureInfo">
+</parameter>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGlEsVersion"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_REQUIRED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="GL_ES_VERSION_UNDEFINED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="name"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="reqGlEsVersion"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="InstrumentationInfo"
extends="android.content.pm.PackageItemInfo"
abstract="false"
@@ -33436,6 +38613,155 @@
>
</field>
</class>
+<class name="LabeledIntent"
+ extends="android.content.Intent"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="LabeledIntent"
+ type="android.content.pm.LabeledIntent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="origIntent" type="android.content.Intent">
+</parameter>
+<parameter name="sourcePackage" type="java.lang.String">
+</parameter>
+<parameter name="labelRes" type="int">
+</parameter>
+<parameter name="icon" type="int">
+</parameter>
+</constructor>
+<constructor name="LabeledIntent"
+ type="android.content.pm.LabeledIntent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="origIntent" type="android.content.Intent">
+</parameter>
+<parameter name="sourcePackage" type="java.lang.String">
+</parameter>
+<parameter name="nonLocalizedLabel" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="int">
+</parameter>
+</constructor>
+<constructor name="LabeledIntent"
+ type="android.content.pm.LabeledIntent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sourcePackage" type="java.lang.String">
+</parameter>
+<parameter name="labelRes" type="int">
+</parameter>
+<parameter name="icon" type="int">
+</parameter>
+</constructor>
+<constructor name="LabeledIntent"
+ type="android.content.pm.LabeledIntent"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sourcePackage" type="java.lang.String">
+</parameter>
+<parameter name="nonLocalizedLabel" type="java.lang.CharSequence">
+</parameter>
+<parameter name="icon" type="int">
+</parameter>
+</constructor>
+<method name="getIconResource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getLabelResource"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getNonLocalizedLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSourcePackage"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="loadIcon"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<method name="loadLabel"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pm" type="android.content.pm.PackageManager">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="PackageInfo"
extends="java.lang.Object"
abstract="false"
@@ -33587,6 +38913,17 @@
visibility="public"
>
</field>
+<field name="reqFeatures"
+ type="android.content.pm.FeatureInfo[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="requestedPermissions"
type="java.lang.String[]"
transient="false"
@@ -33971,6 +39308,21 @@
<parameter name="pkg2" type="java.lang.String">
</parameter>
</method>
+<method name="checkSignatures"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uid1" type="int">
+</parameter>
+<parameter name="uid2" type="int">
+</parameter>
+</method>
<method name="clearPackagePreferredActivities"
return="void"
abstract="true"
@@ -34182,6 +39534,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="getInstallerPackageName"
+ return="java.lang.String"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
<method name="getInstrumentationInfo"
return="android.content.pm.InstrumentationInfo"
abstract="true"
@@ -34428,6 +39793,17 @@
<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
</exception>
</method>
+<method name="getSystemAvailableFeatures"
+ return="android.content.pm.FeatureInfo[]"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSystemSharedLibraryNames"
return="java.lang.String[]"
abstract="true"
@@ -35570,7 +40946,7 @@
volatile="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -35823,6 +41199,16 @@
visibility="public"
>
</field>
+<field name="resolvePackageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="serviceInfo"
type="android.content.pm.ServiceInfo"
transient="false"
@@ -35919,6 +41305,21 @@
visibility="public"
>
</method>
+<method name="dump"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pw" type="android.util.Printer">
+</parameter>
+<parameter name="prefix" type="java.lang.String">
+</parameter>
+</method>
<field name="CREATOR"
type="android.os.Parcelable.Creator"
transient="false"
@@ -39384,6 +44785,32 @@
<parameter name="columnIndex" type="int">
</parameter>
</method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
<method name="isNull"
return="boolean"
abstract="false"
@@ -39397,6 +44824,19 @@
<parameter name="columnIndex" type="int">
</parameter>
</method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
<method name="setWindow"
return="void"
abstract="false"
@@ -40477,6 +45917,36 @@
<parameter name="col" type="int">
</parameter>
</method>
+<method name="isFloat"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
+<method name="isLong"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
<method name="isNull"
return="boolean"
abstract="false"
@@ -40492,6 +45962,21 @@
<parameter name="col" type="int">
</parameter>
</method>
+<method name="isString"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="row" type="int">
+</parameter>
+<parameter name="col" type="int">
+</parameter>
+</method>
<method name="newFromParcel"
return="android.database.CursorWindow"
abstract="false"
@@ -41687,6 +47172,21 @@
<exception name="FileNotFoundException" type="java.io.FileNotFoundException">
</exception>
</method>
+<method name="readExceptionWithOperationApplicationExceptionFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+<exception name="OperationApplicationException" type="android.content.OperationApplicationException">
+</exception>
+</method>
<method name="sqlEscapeString"
return="java.lang.String"
abstract="false"
@@ -42724,6 +48224,19 @@
visibility="public"
>
</method>
+<method name="beginTransactionWithListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="transactionListener" type="android.database.sqlite.SQLiteTransactionListener">
+</parameter>
+</method>
<method name="close"
return="void"
abstract="false"
@@ -43395,6 +48908,19 @@
visibility="public"
>
</method>
+<method name="yieldIfContendedSafely"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="sleepAfterYieldDelay" type="long">
+</parameter>
+</method>
<field name="CREATE_IF_NECESSARY"
type="int"
transient="false"
@@ -44342,6 +49868,47 @@
>
</method>
</class>
+<interface name="SQLiteTransactionListener"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onBegin"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onCommit"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onRollback"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
</package>
<package name="android.gesture"
>
@@ -56128,6 +61695,21 @@
visibility="public"
>
</method>
+<method name="setGammaForText"
+ return="void"
+ abstract="false"
+ native="true"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="blackGamma" type="float">
+</parameter>
+<parameter name="whiteGamma" type="float">
+</parameter>
+</method>
<field name="BOLD"
type="int"
transient="false"
@@ -57029,6 +62611,27 @@
<parameter name="srcName" type="java.lang.String">
</parameter>
</method>
+<method name="createFromResourceStream"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="res" type="android.content.res.Resources">
+</parameter>
+<parameter name="value" type="android.util.TypedValue">
+</parameter>
+<parameter name="is" type="java.io.InputStream">
+</parameter>
+<parameter name="srcName" type="java.lang.String">
+</parameter>
+<parameter name="opts" type="android.graphics.BitmapFactory.Options">
+</parameter>
+</method>
<method name="createFromStream"
return="android.graphics.drawable.Drawable"
abstract="false"
@@ -60038,6 +65641,19 @@
<exception name="IOException" type="java.io.IOException">
</exception>
</method>
+<method name="setZoomCallback"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="cb" type="android.hardware.Camera.ZoomCallback">
+</parameter>
+</method>
<method name="startPreview"
return="void"
abstract="false"
@@ -60077,6 +65693,25 @@
<parameter name="jpeg" type="android.hardware.Camera.PictureCallback">
</parameter>
</method>
+<method name="takePicture"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="shutter" type="android.hardware.Camera.ShutterCallback">
+</parameter>
+<parameter name="raw" type="android.hardware.Camera.PictureCallback">
+</parameter>
+<parameter name="postview" type="android.hardware.Camera.PictureCallback">
+</parameter>
+<parameter name="jpeg" type="android.hardware.Camera.PictureCallback">
+</parameter>
+</method>
<field name="CAMERA_ERROR_SERVER_DIED"
type="int"
transient="false"
@@ -60178,6 +65813,39 @@
<parameter name="key" type="java.lang.String">
</parameter>
</method>
+<method name="getAntibanding"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getColorEffect"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFlashMode"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getInt"
return="int"
abstract="false"
@@ -60191,6 +65859,39 @@
<parameter name="key" type="java.lang.String">
</parameter>
</method>
+<method name="getJpegQuality"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getJpegThumbnailQuality"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getJpegThumbnailSize"
+ return="android.hardware.Camera.Size"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getPictureFormat"
return="int"
abstract="false"
@@ -60246,6 +65947,138 @@
visibility="public"
>
</method>
+<method name="getSceneMode"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedAntibanding"
+ return="java.util.List&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedColorEffects"
+ return="java.util.List&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedFlashModes"
+ return="java.util.List&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedPictureFormats"
+ return="java.util.List&lt;java.lang.Integer&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedPictureSizes"
+ return="java.util.List&lt;android.hardware.Camera.Size&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedPreviewFormats"
+ return="java.util.List&lt;java.lang.Integer&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedPreviewFrameRates"
+ return="java.util.List&lt;java.lang.Integer&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedPreviewSizes"
+ return="java.util.List&lt;android.hardware.Camera.Size&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedSceneModes"
+ return="java.util.List&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getSupportedWhiteBalance"
+ return="java.util.List&lt;java.lang.String&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getWhiteBalance"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="remove"
return="void"
abstract="false"
@@ -60259,6 +66092,17 @@
<parameter name="key" type="java.lang.String">
</parameter>
</method>
+<method name="removeGpsData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="set"
return="void"
abstract="false"
@@ -60289,6 +66133,138 @@
<parameter name="value" type="int">
</parameter>
</method>
+<method name="setAntibanding"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="antibanding" type="java.lang.String">
+</parameter>
+</method>
+<method name="setColorEffect"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setFlashMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setGpsAltitude"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="altitude" type="double">
+</parameter>
+</method>
+<method name="setGpsLatitude"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="latitude" type="double">
+</parameter>
+</method>
+<method name="setGpsLongitude"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="longitude" type="double">
+</parameter>
+</method>
+<method name="setGpsTimestamp"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timestamp" type="long">
+</parameter>
+</method>
+<method name="setJpegQuality"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="quality" type="int">
+</parameter>
+</method>
+<method name="setJpegThumbnailQuality"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="quality" type="int">
+</parameter>
+</method>
+<method name="setJpegThumbnailSize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
+</method>
<method name="setPictureFormat"
return="void"
abstract="false"
@@ -60358,6 +66334,45 @@
<parameter name="height" type="int">
</parameter>
</method>
+<method name="setRotation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="rotation" type="int">
+</parameter>
+</method>
+<method name="setSceneMode"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
+<method name="setWhiteBalance"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="value" type="java.lang.String">
+</parameter>
+</method>
<method name="unflatten"
return="void"
abstract="false"
@@ -60371,6 +66386,446 @@
<parameter name="flattened" type="java.lang.String">
</parameter>
</method>
+<field name="ANTIBANDING_50HZ"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;50hz&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ANTIBANDING_60HZ"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;60hz&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ANTIBANDING_AUTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ANTIBANDING_OFF"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;off&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_AQUA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;aqua&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_BLACKBOARD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;blackboard&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_MONO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;mono&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_NEGATIVE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;negative&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_NONE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;none&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_POSTERIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;posterize&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_SEPIA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sepia&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_SOLARIZE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;solarize&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EFFECT_WHITEBOARD"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;whiteboard&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLASH_MODE_AUTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLASH_MODE_OFF"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;off&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLASH_MODE_ON"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;on&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLASH_MODE_RED_EYE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;red-eye&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_ACTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;action&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_AUTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_BEACH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;beach&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_CANDLELIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;candlelight&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_FIREWORKS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;fireworks&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_LANDSCAPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;landscape&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_NIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;night&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_NIGHT_PORTRAIT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;night-portrait&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_PARTY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;party&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_PORTRAIT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;portrait&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_SNOW"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;snow&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_SPORTS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sports&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_STEADYPHOTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;steadyphoto&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_SUNSET"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;sunset&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="SCENE_MODE_THEATRE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;theatre&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_AUTO"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;auto&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_CLOUDY_DAYLIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;cloudy-daylight&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_DAYLIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;daylight&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_FLUORESCENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;fluorescent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_INCANDESCENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;incandescent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_SHADE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;shade&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_TWILIGHT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;twilight&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="WHITE_BALANCE_WARM_FLUORESCENT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;warm-fluorescent&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<interface name="Camera.PictureCallback"
abstract="true"
@@ -60478,6 +66933,29 @@
>
</field>
</class>
+<interface name="Camera.ZoomCallback"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onZoomUpdate"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="zoomLevel" type="int">
+</parameter>
+<parameter name="camera" type="android.hardware.Camera">
+</parameter>
+</method>
+</interface>
<class name="GeomagneticField"
extends="java.lang.Object"
abstract="false"
@@ -65545,6 +72023,29 @@
</parameter>
</method>
</interface>
+<interface name="GpsStatus.NmeaListener"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="onNmeaReceived"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="timestamp" type="long">
+</parameter>
+<parameter name="nmea" type="java.lang.String">
+</parameter>
+</method>
+</interface>
<class name="Location"
extends="java.lang.Object"
abstract="false"
@@ -66148,6 +72649,19 @@
<parameter name="listener" type="android.location.GpsStatus.Listener">
</parameter>
</method>
+<method name="addNmeaListener"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.location.GpsStatus.NmeaListener">
+</parameter>
+</method>
<method name="addProximityAlert"
return="void"
abstract="false"
@@ -66358,6 +72872,19 @@
<parameter name="listener" type="android.location.GpsStatus.Listener">
</parameter>
</method>
+<method name="removeNmeaListener"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="listener" type="android.location.GpsStatus.NmeaListener">
+</parameter>
+</method>
<method name="removeProximityAlert"
return="void"
abstract="false"
@@ -66842,7 +73369,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -66853,7 +73380,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -66864,7 +73391,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -66875,6 +73402,380 @@
value="3"
static="true"
final="true"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_INVALID"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_BACK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_BACK_PROCESSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="512"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_FRONT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_FRONT_PROCESSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_LEFT_PROCESSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_MONO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_PRESSURE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_RIGHT_PROCESSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_STEREO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_VOICE_DNLINK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32768"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_VOICE_UPLINK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16384"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_X_AXIS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2048"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_Y_AXIS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4096"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_IN_Z_AXIS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8192"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_5POINT1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="252"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_7POINT1"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1020"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_BACK_CENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1024"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_BACK_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_BACK_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="128"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_FRONT_CENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_FRONT_LEFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_FRONT_LEFT_OF_CENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="256"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_FRONT_RIGHT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_FRONT_RIGHT_OF_CENTER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="512"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_LOW_FREQUENCY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_MONO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_QUAD"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="204"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_STEREO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="CHANNEL_OUT_SURROUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1052"
+ static="true"
+ final="true"
deprecated="not deprecated"
visibility="public"
>
@@ -66992,6 +73893,19 @@
visibility="public"
>
</method>
+<method name="getParameters"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keys" type="java.lang.String">
+</parameter>
+</method>
<method name="getRingerMode"
return="int"
abstract="false"
@@ -67110,6 +74024,17 @@
visibility="public"
>
</method>
+<method name="isWiredHeadsetOn"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="loadSoundEffects"
return="void"
abstract="false"
@@ -67156,7 +74081,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="on" type="boolean">
@@ -67201,6 +74126,19 @@
<parameter name="mode" type="int">
</parameter>
</method>
+<method name="setParameters"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="keyValuePairs" type="java.lang.String">
+</parameter>
+</method>
<method name="setRingerMode"
return="void"
abstract="false"
@@ -67306,6 +74244,19 @@
<parameter name="vibrateSetting" type="int">
</parameter>
</method>
+<method name="setWiredHeadsetOn"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="on" type="boolean">
+</parameter>
+</method>
<method name="shouldVibrate"
return="boolean"
abstract="false"
@@ -67678,7 +74629,7 @@
value="-1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67700,7 +74651,7 @@
value="16"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67711,7 +74662,7 @@
value="4"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67722,7 +74673,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67733,7 +74684,7 @@
value="8"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67744,7 +74695,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -67759,6 +74710,17 @@
visibility="public"
>
</field>
+<field name="STREAM_DTMF"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="STREAM_MUSIC"
type="int"
transient="false"
@@ -69494,6 +76456,21 @@
visibility="public"
>
</method>
+<method name="invoke"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="request" type="android.os.Parcel">
+</parameter>
+<parameter name="reply" type="android.os.Parcel">
+</parameter>
+</method>
<method name="isLooping"
return="boolean"
abstract="false"
@@ -69516,6 +76493,17 @@
visibility="public"
>
</method>
+<method name="newRequest"
+ return="android.os.Parcel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="pause"
return="void"
abstract="false"
@@ -69921,6 +76909,17 @@
visibility="public"
>
</field>
+<field name="MEDIA_INFO_METADATA_UPDATE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="802"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="MEDIA_INFO_NOT_SEEKABLE"
type="int"
transient="false"
@@ -71685,6 +78684,19 @@
<method name="startTone"
return="boolean"
abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="toneType" type="int">
+</parameter>
+</method>
+<method name="startTone"
+ return="boolean"
+ abstract="false"
native="true"
synchronized="false"
static="false"
@@ -71694,6 +78706,8 @@
>
<parameter name="toneType" type="int">
</parameter>
+<parameter name="durationMs" type="int">
+</parameter>
</method>
<method name="stopTone"
return="void"
@@ -90140,6 +97154,17 @@
visibility="public"
>
</field>
+<field name="ECLAIR"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="10000"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="Bundle"
extends="java.lang.Object"
@@ -92201,6 +99226,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.os.Parcelable">
+</implements>
<constructor name="Debug.MemoryInfo"
type="android.os.Debug.MemoryInfo"
static="false"
@@ -92209,6 +99236,55 @@
visibility="public"
>
</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="readFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="source" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="dalvikPrivateDirty"
type="int"
transient="false"
@@ -100567,7 +107643,7 @@
abstract="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="AUTHORITY"
@@ -100577,7 +107653,7 @@
value="&quot;contacts&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100587,7 +107663,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100598,7 +107674,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100609,7 +107685,7 @@
value="3"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100620,7 +107696,7 @@
value="4"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100631,7 +107707,7 @@
value="5"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100642,7 +107718,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100652,7 +107728,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -100668,7 +107744,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -100687,7 +107763,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="encodedString" type="java.lang.String">
@@ -100700,7 +107776,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="protocolString" type="java.lang.String">
@@ -100713,7 +107789,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="protocol" type="int">
@@ -100726,7 +107802,7 @@
synchronized="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -100745,7 +107821,7 @@
value="&quot;vnd.android.cursor.item/email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100756,7 +107832,7 @@
value="&quot;vnd.android.cursor.dir/email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100766,7 +107842,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100777,7 +107853,7 @@
value="&quot;vnd.android.cursor.item/jabber-im&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100788,7 +107864,7 @@
value="&quot;vnd.android.cursor.item/postal-address&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100799,7 +107875,7 @@
value="&quot;vnd.android.cursor.dir/postal-address&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100810,7 +107886,7 @@
value="&quot;vnd.android.cursor.dir/contact-methods&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100820,7 +107896,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100831,7 +107907,7 @@
value="&quot;name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100842,7 +107918,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100853,7 +107929,7 @@
value="&quot;data&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100864,7 +107940,7 @@
value="&quot;aux_data&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100875,7 +107951,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100886,7 +107962,7 @@
value="5"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100897,7 +107973,7 @@
value="6"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100908,7 +107984,7 @@
value="7"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100919,7 +107995,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100930,7 +108006,7 @@
value="4"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100941,7 +108017,7 @@
value="3"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100952,7 +108028,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100961,7 +108037,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="AUX_DATA"
@@ -100971,7 +108047,7 @@
value="&quot;aux_data&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100982,7 +108058,7 @@
value="&quot;data&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -100993,7 +108069,7 @@
value="&quot;isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101004,7 +108080,7 @@
value="&quot;kind&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101015,7 +108091,7 @@
value="&quot;label&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101026,7 +108102,7 @@
value="&quot;type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101037,7 +108113,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101048,7 +108124,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101059,7 +108135,7 @@
value="3"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101070,7 +108146,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101080,7 +108156,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -101094,7 +108170,7 @@
value="&quot;vnd.android.cursor.item/contact_extensions&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101105,7 +108181,7 @@
value="&quot;vnd.android.cursor.dir/contact_extensions&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101115,7 +108191,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101126,7 +108202,7 @@
value="&quot;person, name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101137,7 +108213,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101146,7 +108222,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="NAME"
@@ -101156,7 +108232,7 @@
value="&quot;name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101167,7 +108243,7 @@
value="&quot;value&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101177,7 +108253,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -101191,7 +108267,7 @@
value="&quot;groupmembership&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101202,7 +108278,7 @@
value="&quot;vnd.android.cursor.item/contactsgroupmembership&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101213,7 +108289,7 @@
value="&quot;vnd.android.cursor.dir/contactsgroupmembership&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101223,7 +108299,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101234,7 +108310,7 @@
value="&quot;group_id ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101245,7 +108321,7 @@
value="&quot;group_id&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101256,7 +108332,18 @@
value="&quot;group_sync_account&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="GROUP_SYNC_ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;group_sync_account_type&quot;"
+ static="true"
+ final="true"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101267,7 +108354,7 @@
value="&quot;group_sync_id&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101278,7 +108365,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101288,7 +108375,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101298,7 +108385,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -101312,7 +108399,7 @@
value="&quot;vnd.android.cursor.item/contactsgroup&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101323,7 +108410,7 @@
value="&quot;vnd.android.cursor.dir/contactsgroup&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101333,7 +108420,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101344,7 +108431,7 @@
value="&quot;name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101354,7 +108441,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101365,7 +108452,7 @@
value="&quot;Starred in Android&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101376,7 +108463,7 @@
value="&quot;Contacts&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101385,7 +108472,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="NAME"
@@ -101395,7 +108482,7 @@
value="&quot;name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101406,7 +108493,7 @@
value="&quot;notes&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101417,7 +108504,7 @@
value="&quot;should_sync&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101428,7 +108515,7 @@
value="&quot;system_id&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101438,14 +108525,14 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="Contacts.Intents"
type="android.provider.Contacts.Intents"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</constructor>
@@ -101456,7 +108543,7 @@
value="&quot;com.android.contacts.action.ATTACH_IMAGE&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101467,7 +108554,7 @@
value="&quot;com.android.contacts.action.CREATE_DESCRIPTION&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101478,7 +108565,7 @@
value="&quot;com.android.contacts.action.FORCE_CREATE&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101489,7 +108576,7 @@
value="&quot;android.provider.Contacts.SEARCH_SUGGESTION_CLICKED&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101500,7 +108587,7 @@
value="&quot;android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101511,7 +108598,7 @@
value="&quot;android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101522,7 +108609,7 @@
value="&quot;com.android.contacts.action.SHOW_OR_CREATE_CONTACT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101532,14 +108619,14 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="Contacts.Intents.Insert"
type="android.provider.Contacts.Intents.Insert"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</constructor>
@@ -101550,7 +108637,7 @@
value="&quot;android.intent.action.INSERT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101561,7 +108648,7 @@
value="&quot;company&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101572,7 +108659,7 @@
value="&quot;email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101583,7 +108670,7 @@
value="&quot;email_isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101594,7 +108681,7 @@
value="&quot;email_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101605,7 +108692,7 @@
value="&quot;full_mode&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101616,7 +108703,7 @@
value="&quot;im_handle&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101627,7 +108714,7 @@
value="&quot;im_isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101638,7 +108725,7 @@
value="&quot;im_protocol&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101649,7 +108736,7 @@
value="&quot;job_title&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101660,7 +108747,7 @@
value="&quot;name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101671,7 +108758,7 @@
value="&quot;notes&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101682,7 +108769,7 @@
value="&quot;phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101693,7 +108780,7 @@
value="&quot;phonetic_name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101704,7 +108791,7 @@
value="&quot;phone_isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101715,7 +108802,7 @@
value="&quot;phone_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101726,7 +108813,7 @@
value="&quot;postal&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101737,7 +108824,7 @@
value="&quot;postal_isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101748,7 +108835,7 @@
value="&quot;postal_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101759,7 +108846,7 @@
value="&quot;secondary_email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101770,7 +108857,7 @@
value="&quot;secondary_email_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101781,7 +108868,7 @@
value="&quot;secondary_phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101792,7 +108879,7 @@
value="&quot;secondary_phone_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101803,7 +108890,7 @@
value="&quot;tertiary_email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101814,7 +108901,7 @@
value="&quot;tertiary_email_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101825,7 +108912,7 @@
value="&quot;tertiary_phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101836,7 +108923,7 @@
value="&quot;tertiary_phone_type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101846,14 +108933,14 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="Contacts.Intents.UI"
type="android.provider.Contacts.Intents.UI"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</constructor>
@@ -101864,7 +108951,7 @@
value="&quot;com.android.contacts.action.FILTER_CONTACTS&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101875,7 +108962,7 @@
value="&quot;com.android.contacts.extra.FILTER_TEXT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101886,7 +108973,7 @@
value="&quot;com.android.contacts.extra.GROUP&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101897,7 +108984,7 @@
value="&quot;com.android.contacts.action.LIST_ALL_CONTACTS&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101908,7 +108995,7 @@
value="&quot;com.android.contacts.action.LIST_CONTACTS_WITH_PHONES&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101919,7 +109006,7 @@
value="&quot;com.android.contacts.action.LIST_DEFAULT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101930,7 +109017,7 @@
value="&quot;com.android.contacts.action.LIST_FREQUENT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101941,7 +109028,7 @@
value="&quot;com.android.contacts.action.LIST_GROUP&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101952,7 +109039,7 @@
value="&quot;com.android.contacts.action.LIST_STARRED&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101963,7 +109050,7 @@
value="&quot;com.android.contacts.action.LIST_STREQUENT&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101974,7 +109061,7 @@
value="&quot;com.android.contacts.extra.TITLE_EXTRA&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -101983,7 +109070,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="COMPANY"
@@ -101993,7 +109080,7 @@
value="&quot;company&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102004,7 +109091,7 @@
value="&quot;isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102015,7 +109102,7 @@
value="&quot;label&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102026,7 +109113,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102037,7 +109124,7 @@
value="&quot;title&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102048,7 +109135,7 @@
value="&quot;type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102059,7 +109146,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102070,7 +109157,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102081,7 +109168,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102091,7 +109178,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102105,7 +109192,7 @@
synchronized="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -102122,7 +109209,7 @@
value="&quot;organizations&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102132,7 +109219,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102143,7 +109230,7 @@
value="&quot;company, title, isprimary ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102153,7 +109240,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102171,7 +109258,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102188,7 +109275,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102205,7 +109292,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102220,7 +109307,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102235,7 +109322,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -102254,7 +109341,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102269,7 +109356,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
@@ -102284,7 +109371,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="resolver" type="android.content.ContentResolver">
@@ -102299,7 +109386,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
@@ -102315,7 +109402,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102326,7 +109413,7 @@
value="&quot;vnd.android.cursor.item/person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102337,7 +109424,7 @@
value="&quot;vnd.android.cursor.dir/person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102347,7 +109434,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102358,7 +109445,7 @@
value="&quot;name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102368,7 +109455,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102379,7 +109466,7 @@
value="&quot;primary_email&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102390,7 +109477,7 @@
value="&quot;primary_organization&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102401,7 +109488,7 @@
value="&quot;primary_phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102411,7 +109498,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102427,7 +109514,7 @@
value="&quot;contact_methods&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102438,7 +109525,7 @@
value="&quot;data ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102448,7 +109535,7 @@
abstract="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102462,7 +109549,7 @@
value="&quot;extensions&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102473,7 +109560,7 @@
value="&quot;name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102484,7 +109571,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102494,7 +109581,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102510,7 +109597,7 @@
value="&quot;phones&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102521,7 +109608,7 @@
value="&quot;number ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102530,7 +109617,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="CUSTOM_RINGTONE"
@@ -102540,7 +109627,7 @@
value="&quot;custom_ringtone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102551,7 +109638,7 @@
value="&quot;display_name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102562,7 +109649,7 @@
value="&quot;last_time_contacted&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102573,7 +109660,7 @@
value="&quot;name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102584,7 +109671,7 @@
value="&quot;notes&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102595,7 +109682,7 @@
value="&quot;phonetic_name&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102606,7 +109693,7 @@
value="&quot;photo_version&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102617,7 +109704,7 @@
value="&quot;send_to_voicemail&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102628,7 +109715,7 @@
value="&quot;starred&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102639,7 +109726,7 @@
value="&quot;times_contacted&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102649,7 +109736,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102665,7 +109752,7 @@
synchronized="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -102684,7 +109771,7 @@
synchronized="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -102700,7 +109787,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102711,7 +109798,7 @@
value="&quot;vnd.android.cursor.item/phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102722,7 +109809,7 @@
value="&quot;vnd.android.cursor.dir/phone&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102732,7 +109819,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102743,7 +109830,7 @@
value="&quot;name ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102754,7 +109841,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102763,7 +109850,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="ISPRIMARY"
@@ -102773,7 +109860,7 @@
value="&quot;isprimary&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102784,7 +109871,7 @@
value="&quot;label&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102795,7 +109882,7 @@
value="&quot;number&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102806,7 +109893,7 @@
value="&quot;number_key&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102817,7 +109904,7 @@
value="&quot;type&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102828,7 +109915,7 @@
value="0"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102839,7 +109926,7 @@
value="5"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102850,7 +109937,7 @@
value="4"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102861,7 +109948,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102872,7 +109959,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102883,7 +109970,7 @@
value="7"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102894,7 +109981,7 @@
value="6"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102905,7 +109992,7 @@
value="3"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102915,7 +110002,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -102929,7 +110016,7 @@
value="&quot;photo&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102939,7 +110026,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102950,7 +110037,7 @@
value="&quot;person ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102959,7 +110046,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="DATA"
@@ -102969,7 +110056,7 @@
value="&quot;data&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102980,7 +110067,7 @@
value="&quot;download_required&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -102991,7 +110078,7 @@
value="&quot;exists_on_server&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103002,7 +110089,7 @@
value="&quot;local_version&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103013,7 +110100,7 @@
value="&quot;person&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103024,7 +110111,7 @@
value="&quot;sync_error&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103033,7 +110120,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="IM_ACCOUNT"
@@ -103043,7 +110130,7 @@
value="&quot;im_account&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103054,7 +110141,7 @@
value="&quot;im_handle&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103065,7 +110152,7 @@
value="&quot;im_protocol&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103075,7 +110162,7 @@
abstract="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<implements name="android.provider.BaseColumns">
@@ -103089,7 +110176,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
@@ -103106,7 +110193,7 @@
synchronized="false"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="cr" type="android.content.ContentResolver">
@@ -103125,7 +110212,7 @@
value="&quot;settings&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103135,7 +110222,7 @@
volatile="false"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103146,7 +110233,7 @@
value="&quot;key ASC&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103157,7 +110244,7 @@
value="&quot;syncEverything&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103166,7 +110253,7 @@
abstract="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<field name="KEY"
@@ -103176,7 +110263,7 @@
value="&quot;key&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103187,7 +110274,7 @@
value="&quot;value&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -103198,7 +110285,18 @@
value="&quot;_sync_account&quot;"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
+ visibility="public"
+>
+</field>
+<field name="_SYNC_ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;_sync_account_type&quot;"
+ static="true"
+ final="true"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -105770,6 +112868,17 @@
visibility="public"
>
</constructor>
+<field name="ACTION_ACCESSIBILITY_SETTINGS"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;android.settings.ACCESSIBILITY_SETTINGS&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_AIRPLANE_MODE_SETTINGS"
type="java.lang.String"
transient="false"
@@ -107209,6 +114318,17 @@
visibility="public"
>
</field>
+<field name="ALARM_ALERT"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;alarm_alert&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ALWAYS_FINISH_ACTIVITIES"
type="java.lang.String"
transient="false"
@@ -107329,6 +114449,16 @@
visibility="public"
>
</field>
+<field name="DEFAULT_ALARM_ALERT_URI"
+ type="android.net.Uri"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="DEFAULT_NOTIFICATION_URI"
type="android.net.Uri"
transient="false"
@@ -108252,6 +115382,244 @@
>
</field>
</class>
+<class name="SyncStateContract"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SyncStateContract"
+ type="android.provider.SyncStateContract"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<interface name="SyncStateContract.Columns"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.BaseColumns">
+</implements>
+<field name="ACCOUNT_NAME"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;account_name&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACCOUNT_TYPE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;account_type&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DATA"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;data&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</interface>
+<class name="SyncStateContract.Constants"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.provider.SyncStateContract.Columns">
+</implements>
+<constructor name="SyncStateContract.Constants"
+ type="android.provider.SyncStateContract.Constants"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<field name="CONTENT_DIRECTORY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;syncstate&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="SyncStateContract.Helpers"
+ extends="java.lang.Object"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="SyncStateContract.Helpers"
+ type="android.provider.SyncStateContract.Helpers"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="get"
+ return="byte[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProviderClient">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="getWithUri"
+ return="android.util.Pair&lt;android.net.Uri, byte[]&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProviderClient">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="insert"
+ return="android.net.Uri"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProviderClient">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="newSetOperation"
+ return="android.content.ContentProviderOperation"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+</method>
+<method name="newUpdateOperation"
+ return="android.content.ContentProviderOperation"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+</method>
+<method name="set"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProviderClient">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="account" type="android.accounts.Account">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+<method name="update"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="provider" type="android.content.ContentProviderClient">
+</parameter>
+<parameter name="uri" type="android.net.Uri">
+</parameter>
+<parameter name="data" type="byte[]">
+</parameter>
+<exception name="RemoteException" type="android.os.RemoteException">
+</exception>
+</method>
+</class>
<class name="UserDictionary"
extends="java.lang.Object"
abstract="false"
@@ -110368,7 +117736,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="asu" type="int">
@@ -111729,6 +119097,17 @@
visibility="public"
>
</method>
+<method name="hasIccCard"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isNetworkRoaming"
return="boolean"
abstract="false"
@@ -113455,6 +120834,19 @@
synchronized="false"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="instrumentation" type="android.app.Instrumentation">
+</parameter>
+</method>
+<method name="setInstrumentation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -113704,6 +121096,19 @@
synchronized="false"
static="false"
final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
+<parameter name="instrumentation" type="android.app.Instrumentation">
+</parameter>
+</method>
+<method name="injectInstrumentation"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -115200,7 +122605,7 @@
>
<parameter name="uri" type="android.net.Uri">
</parameter>
-<parameter name="account" type="java.lang.String">
+<parameter name="accountName" type="java.lang.String">
</parameter>
<parameter name="authority" type="java.lang.String">
</parameter>
@@ -117216,6 +124621,21 @@
<parameter name="pkg2" type="java.lang.String">
</parameter>
</method>
+<method name="checkSignatures"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="uid1" type="int">
+</parameter>
+<parameter name="uid2" type="int">
+</parameter>
+</method>
<method name="clearPackagePreferredActivities"
return="void"
abstract="false"
@@ -117427,6 +124847,19 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="getInstallerPackageName"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.String">
+</parameter>
+</method>
<method name="getInstrumentationInfo"
return="android.content.pm.InstrumentationInfo"
abstract="false"
@@ -117656,6 +125089,17 @@
<exception name="PackageManager.NameNotFoundException" type="android.content.pm.PackageManager.NameNotFoundException">
</exception>
</method>
+<method name="getSystemAvailableFeatures"
+ return="android.content.pm.FeatureInfo[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSystemSharedLibraryNames"
return="java.lang.String[]"
abstract="false"
@@ -122715,6 +130159,16 @@
visibility="public"
>
</field>
+<field name="density"
+ type="float"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="drawableState"
type="int[]"
transient="false"
@@ -123926,6 +131380,27 @@
<parameter name="flags" type="int">
</parameter>
</method>
+<method name="formatDateRange"
+ return="java.util.Formatter"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+<parameter name="formatter" type="java.util.Formatter">
+</parameter>
+<parameter name="startMillis" type="long">
+</parameter>
+<parameter name="endMillis" type="long">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
<method name="formatDateTime"
return="java.lang.String"
abstract="false"
@@ -127692,6 +135167,18 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="size" type="int">
+</parameter>
+<parameter name="dip" type="boolean">
+</parameter>
+</constructor>
+<constructor name="AbsoluteSizeSpan"
+ type="android.text.style.AbsoluteSizeSpan"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
<parameter name="src" type="android.os.Parcel">
</parameter>
</constructor>
@@ -127706,6 +135193,17 @@
visibility="public"
>
</method>
+<method name="getDip"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSize"
return="int"
abstract="false"
@@ -129096,6 +136594,41 @@
</parameter>
</method>
</interface>
+<interface name="LineHeightSpan.WithDensity"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.text.style.LineHeightSpan">
+</implements>
+<method name="chooseHeight"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="start" type="int">
+</parameter>
+<parameter name="end" type="int">
+</parameter>
+<parameter name="spanstartv" type="int">
+</parameter>
+<parameter name="v" type="int">
+</parameter>
+<parameter name="fm" type="android.graphics.Paint.FontMetricsInt">
+</parameter>
+<parameter name="paint" type="android.text.TextPaint">
+</parameter>
+</method>
+</interface>
<class name="MaskFilterSpan"
extends="android.text.style.CharacterStyle"
abstract="false"
@@ -132387,6 +139920,62 @@
>
</method>
</class>
+<class name="Pair"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="Pair"
+ type="android.util.Pair"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="first" type="F">
+</parameter>
+<parameter name="second" type="S">
+</parameter>
+</constructor>
+<method name="create"
+ return="android.util.Pair&lt;A, B&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="a" type="A">
+</parameter>
+<parameter name="b" type="B">
+</parameter>
+</method>
+<field name="first"
+ type="F"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="second"
+ type="S"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
<class name="PrintStreamPrinter"
extends="java.lang.Object"
abstract="false"
@@ -135357,6 +142946,17 @@
visibility="public"
>
</field>
+<field name="VIRTUAL_KEY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="InflateException"
extends="java.lang.RuntimeException"
@@ -136197,6 +143797,17 @@
visibility="public"
>
</method>
+<method name="isCanceled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="isModifierKey"
return="boolean"
abstract="false"
@@ -136312,6 +143923,17 @@
visibility="public"
>
</field>
+<field name="FLAG_CANCELED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_EDITOR_ACTION"
type="int"
transient="false"
@@ -136356,6 +143978,17 @@
visibility="public"
>
</field>
+<field name="FLAG_VIRTUAL_HARD_KEY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_WOKE_HERE"
type="int"
transient="false"
@@ -138719,6 +146352,19 @@
visibility="public"
>
</method>
+<method name="findPointerIndex"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerId" type="int">
+</parameter>
+</method>
<method name="getAction"
return="int"
abstract="false"
@@ -138800,6 +146446,34 @@
<parameter name="pos" type="int">
</parameter>
</method>
+<method name="getHistoricalPressure"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+<parameter name="pos" type="int">
+</parameter>
+</method>
+<method name="getHistoricalSize"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pos" type="int">
+</parameter>
+</method>
<method name="getHistoricalSize"
return="float"
abstract="false"
@@ -138810,6 +146484,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="pointerIndex" type="int">
+</parameter>
<parameter name="pos" type="int">
</parameter>
</method>
@@ -138826,6 +146502,21 @@
<parameter name="pos" type="int">
</parameter>
</method>
+<method name="getHistoricalX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+<parameter name="pos" type="int">
+</parameter>
+</method>
<method name="getHistoricalY"
return="float"
abstract="false"
@@ -138839,6 +146530,21 @@
<parameter name="pos" type="int">
</parameter>
</method>
+<method name="getHistoricalY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+<parameter name="pos" type="int">
+</parameter>
+</method>
<method name="getHistorySize"
return="int"
abstract="false"
@@ -138861,6 +146567,30 @@
visibility="public"
>
</method>
+<method name="getPointerCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPointerId"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
<method name="getPressure"
return="float"
abstract="false"
@@ -138872,6 +146602,19 @@
visibility="public"
>
</method>
+<method name="getPressure"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
<method name="getRawX"
return="float"
abstract="false"
@@ -138905,6 +146648,19 @@
visibility="public"
>
</method>
+<method name="getSize"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
<method name="getX"
return="float"
abstract="false"
@@ -138916,6 +146672,19 @@
visibility="public"
>
</method>
+<method name="getX"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
<method name="getXPrecision"
return="float"
abstract="false"
@@ -138938,6 +146707,19 @@
visibility="public"
>
</method>
+<method name="getY"
+ return="float"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="pointerIndex" type="int">
+</parameter>
+</method>
<method name="getYPrecision"
return="float"
abstract="false"
@@ -139000,6 +146782,43 @@
</parameter>
<parameter name="action" type="int">
</parameter>
+<parameter name="pointers" type="int">
+</parameter>
+<parameter name="x" type="float">
+</parameter>
+<parameter name="y" type="float">
+</parameter>
+<parameter name="pressure" type="float">
+</parameter>
+<parameter name="size" type="float">
+</parameter>
+<parameter name="metaState" type="int">
+</parameter>
+<parameter name="xPrecision" type="float">
+</parameter>
+<parameter name="yPrecision" type="float">
+</parameter>
+<parameter name="deviceId" type="int">
+</parameter>
+<parameter name="edgeFlags" type="int">
+</parameter>
+</method>
+<method name="obtain"
+ return="android.view.MotionEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="downTime" type="long">
+</parameter>
+<parameter name="eventTime" type="long">
+</parameter>
+<parameter name="action" type="int">
+</parameter>
<parameter name="x" type="float">
</parameter>
<parameter name="y" type="float">
@@ -139020,6 +146839,19 @@
<parameter name="o" type="android.view.MotionEvent">
</parameter>
</method>
+<method name="obtainNoHistory"
+ return="android.view.MotionEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="android.view.MotionEvent">
+</parameter>
+</method>
<method name="offsetLocation"
return="void"
abstract="false"
@@ -139124,6 +146956,17 @@
visibility="public"
>
</field>
+<field name="ACTION_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="255"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_MOVE"
type="int"
transient="false"
@@ -139146,6 +146989,116 @@
visibility="public"
>
</field>
+<field name="ACTION_POINTER_1_DOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_1_UP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_2_DOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="261"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_2_UP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="262"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_3_DOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="517"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_3_UP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="518"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_DOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_ID_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="65280"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_ID_SHIFT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ACTION_POINTER_UP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTION_UP"
type="int"
transient="false"
@@ -139675,6 +147628,8 @@
>
<parameter name="dirty" type="android.graphics.Rect">
</parameter>
+<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
+</exception>
<exception name="Surface.OutOfResourcesException" type="android.view.Surface.OutOfResourcesException">
</exception>
</method>
@@ -139946,7 +147901,7 @@
value="40"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -139957,7 +147912,7 @@
value="16"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -140307,7 +148262,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -140318,7 +148273,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -148960,6 +156915,17 @@
<parameter name="event" type="android.view.MotionEvent">
</parameter>
</method>
+<method name="onAttachedToWindow"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onContentChanged"
return="void"
abstract="true"
@@ -148999,6 +156965,17 @@
<parameter name="featureId" type="int">
</parameter>
</method>
+<method name="onDetachedFromWindow"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="onMenuItemSelected"
return="boolean"
abstract="true"
@@ -149616,6 +157593,28 @@
visibility="public"
>
</field>
+<field name="FLAG_SHOW_WALLPAPER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1048576"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FLAG_SHOW_WHEN_LOCKED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="524288"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FLAG_TOUCHABLE_WHEN_WAKING"
type="int"
transient="false"
@@ -149711,7 +157710,7 @@
value="2"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -149722,7 +157721,7 @@
value="1"
static="true"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</field>
@@ -150166,6 +158165,17 @@
visibility="public"
>
</field>
+<field name="TYPE_WALLPAPER"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2013"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="alpha"
type="float"
transient="false"
@@ -156602,6 +164612,29 @@
<parameter name="contentLength" type="long">
</parameter>
</method>
+<method name="onExceededDatabaseQuota"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
+<parameter name="databaseIdentifier" type="java.lang.String">
+</parameter>
+<parameter name="currentQuota" type="long">
+</parameter>
+<parameter name="estimatedSize" type="long">
+</parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
+<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
+</parameter>
+</method>
<method name="onFormResubmission"
return="void"
abstract="false"
@@ -157423,14 +165456,14 @@
abstract="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="Plugin"
type="android.webkit.Plugin"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="name" type="java.lang.String">
@@ -157449,7 +165482,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -157462,7 +165495,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157473,7 +165506,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157484,7 +165517,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157495,7 +165528,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157506,7 +165539,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="handler" type="android.webkit.Plugin.PreferencesClickHandler">
@@ -157519,7 +165552,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="description" type="java.lang.String">
@@ -157532,7 +165565,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="fileName" type="java.lang.String">
@@ -157545,7 +165578,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="name" type="java.lang.String">
@@ -157558,7 +165591,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="path" type="java.lang.String">
@@ -157591,14 +165624,14 @@
abstract="false"
static="false"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="PluginData"
type="android.webkit.PluginData"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="stream" type="java.io.InputStream">
@@ -157617,7 +165650,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157628,7 +165661,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157639,7 +165672,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157650,7 +165683,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157660,14 +165693,14 @@
abstract="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="PluginList"
type="android.webkit.PluginList"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</constructor>
@@ -157678,7 +165711,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="plugin" type="android.webkit.Plugin">
@@ -157691,7 +165724,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157702,7 +165735,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -157713,7 +165746,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="context" type="android.content.Context">
@@ -157728,13 +165761,58 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="plugin" type="android.webkit.Plugin">
</parameter>
</method>
</class>
+<class name="PluginStub"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="PluginStub"
+ type="android.webkit.PluginStub"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="npp" type="int">
+</parameter>
+</constructor>
+<method name="getEmbeddedView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+<method name="getFullScreenView"
+ return="android.view.View"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="context" type="android.content.Context">
+</parameter>
+</method>
+</class>
<class name="SslErrorHandler"
extends="android.os.Handler"
abstract="false"
@@ -158005,7 +166083,7 @@
abstract="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<method name="getPluginData"
@@ -158015,7 +166093,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -158044,7 +166122,7 @@
abstract="false"
static="false"
final="true"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<constructor name="UrlInterceptRegistry"
@@ -158062,7 +166140,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="url" type="java.lang.String">
@@ -158092,7 +166170,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="handler" type="android.webkit.UrlInterceptHandler">
@@ -158105,7 +166183,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="disabled" type="boolean">
@@ -158118,7 +166196,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="handler" type="android.webkit.UrlInterceptHandler">
@@ -158131,7 +166209,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -158243,6 +166321,29 @@
<parameter name="resultMsg" type="android.os.Message">
</parameter>
</method>
+<method name="onExceededDatabaseQuota"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
+<parameter name="databaseIdentifier" type="java.lang.String">
+</parameter>
+<parameter name="currentQuota" type="long">
+</parameter>
+<parameter name="estimatedSize" type="long">
+</parameter>
+<parameter name="totalUsedQuota" type="long">
+</parameter>
+<parameter name="quotaUpdater" type="android.webkit.WebStorage.QuotaUpdater">
+</parameter>
+</method>
<method name="onJsAlert"
return="boolean"
abstract="false"
@@ -158628,6 +166729,28 @@
visibility="public"
>
</method>
+<method name="getDatabaseEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDatabasePath"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDefaultFixedFontSize"
return="int"
abstract="false"
@@ -158866,7 +166989,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -158968,6 +167091,32 @@
<parameter name="font" type="java.lang.String">
</parameter>
</method>
+<method name="setDatabaseEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="flag" type="boolean">
+</parameter>
+</method>
+<method name="setDatabasePath"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="databasePath" type="java.lang.String">
+</parameter>
+</method>
<method name="setDefaultFixedFontSize"
return="void"
abstract="false"
@@ -159300,7 +167449,7 @@
synchronized="true"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="use" type="boolean">
@@ -159522,6 +167671,44 @@
>
</method>
</class>
+<class name="WebStorage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="WebStorage"
+ type="android.webkit.WebStorage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+</class>
+<interface name="WebStorage.QuotaUpdater"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="updateQuota"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="newQuota" type="long">
+</parameter>
+</method>
+</interface>
<class name="WebSyncManager"
extends="java.lang.Object"
abstract="true"
@@ -160018,7 +168205,7 @@
synchronized="true"
static="true"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -160289,7 +168476,7 @@
visibility="public"
>
</method>
-<method name="refreshPlugins"
+<method name="postUrl"
return="void"
abstract="false"
native="false"
@@ -160299,6 +168486,21 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="url" type="java.lang.String">
+</parameter>
+<parameter name="postData" type="byte[]">
+</parameter>
+</method>
+<method name="refreshPlugins"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
<parameter name="reloadOpenPages" type="boolean">
</parameter>
</method>
@@ -161058,6 +169260,171 @@
<parameter name="url" type="java.lang.String">
</parameter>
</method>
+<field name="ERROR_AUTHENTICATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_BAD_URL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-12"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_CONNECT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-6"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FAILED_SSL_HANDSHAKE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-11"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-13"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_FILE_NOT_FOUND"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-14"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_HOST_LOOKUP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_IO"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-7"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_PROXY_AUTHENTICATION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-5"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_REDIRECT_LOOP"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-9"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_TIMEOUT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_TOO_MANY_REQUESTS"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-15"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNKNOWN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNSUPPORTED_AUTH_SCHEME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-3"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="ERROR_UNSUPPORTED_SCHEME"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-10"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
</class>
<class name="WebViewDatabase"
extends="java.lang.Object"
@@ -163625,6 +171992,17 @@
visibility="public"
>
</method>
+<method name="getDropDownBackground"
+ return="android.graphics.drawable.Drawable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDropDownHeight"
return="int"
abstract="false"
@@ -163636,6 +172014,28 @@
visibility="public"
>
</method>
+<method name="getDropDownHorizontalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getDropDownVerticalOffset"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getDropDownWidth"
return="int"
abstract="false"
@@ -163859,6 +172259,32 @@
<parameter name="id" type="int">
</parameter>
</method>
+<method name="setDropDownBackgroundDrawable"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="d" type="android.graphics.drawable.Drawable">
+</parameter>
+</method>
+<method name="setDropDownBackgroundResource"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="id" type="int">
+</parameter>
+</method>
<method name="setDropDownHeight"
return="void"
abstract="false"
@@ -163872,6 +172298,32 @@
<parameter name="height" type="int">
</parameter>
</method>
+<method name="setDropDownHorizontalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
+<method name="setDropDownVerticalOffset"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="offset" type="int">
+</parameter>
+</method>
<method name="setDropDownWidth"
return="void"
abstract="false"
@@ -169636,6 +178088,39 @@
deprecated="not deprecated"
visibility="public"
>
+<method name="canPause"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="canSeekBackward"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="canSeekForward"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getBufferPercentage"
return="int"
abstract="true"
@@ -173572,6 +182057,30 @@
<parameter name="isExpanded" type="boolean">
</parameter>
</method>
+<method name="getViewBinder"
+ return="android.widget.SimpleCursorTreeAdapter.ViewBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setViewBinder"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewBinder" type="android.widget.SimpleCursorTreeAdapter.ViewBinder">
+</parameter>
+</method>
<method name="setViewImage"
return="void"
abstract="false"
@@ -173587,7 +182096,47 @@
<parameter name="value" type="java.lang.String">
</parameter>
</method>
+<method name="setViewText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.widget.TextView">
+</parameter>
+<parameter name="text" type="java.lang.String">
+</parameter>
+</method>
</class>
+<interface name="SimpleCursorTreeAdapter.ViewBinder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="setViewValue"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+</interface>
<class name="SimpleExpandableListAdapter"
extends="android.widget.BaseExpandableListAdapter"
abstract="false"
@@ -177900,6 +186449,39 @@
<parameter name="defStyle" type="int">
</parameter>
</constructor>
+<method name="canPause"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="canSeekBackward"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="canSeekForward"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getBufferPercentage"
return="int"
abstract="false"
@@ -186532,7 +195114,7 @@
<method name="valid"
return="boolean"
abstract="false"
- native="true"
+ native="false"
synchronized="false"
static="false"
final="false"
@@ -191655,7 +200237,7 @@
return="java.nio.channels.FileChannel"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="false"
final="true"
deprecated="not deprecated"
@@ -200525,7 +209107,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="i" type="int">
+<parameter name="value" type="int">
</parameter>
</method>
<method name="toString"
@@ -201325,10 +209907,10 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="y" type="double">
-</parameter>
<parameter name="x" type="double">
</parameter>
+<parameter name="y" type="double">
+</parameter>
</method>
<method name="cbrt"
return="double"
@@ -257911,7 +266493,7 @@
</parameter>
<parameter name="buffer" type="java.lang.StringBuffer">
</parameter>
-<parameter name="field" type="java.text.FieldPosition">
+<parameter name="fieldPos" type="java.text.FieldPosition">
</parameter>
</method>
<method name="get2DigitYearStart"
@@ -264451,7 +273033,7 @@
return="boolean"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -264477,7 +273059,7 @@
return="boolean"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -264501,7 +273083,7 @@
return="java.util.Set&lt;java.util.Map.Entry&lt;K, V&gt;&gt;"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -264536,7 +273118,7 @@
return="java.util.Set&lt;K&gt;"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -264621,7 +273203,7 @@
return="java.util.Collection&lt;V&gt;"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="false"
final="false"
deprecated="not deprecated"
@@ -264996,7 +273578,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="s" type="int">
+<parameter name="initialCapacity" type="int">
</parameter>
</constructor>
<constructor name="LinkedHashMap"
@@ -265006,9 +273588,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="s" type="int">
+<parameter name="initialCapacity" type="int">
</parameter>
-<parameter name="lf" type="float">
+<parameter name="loadFactor" type="float">
</parameter>
</constructor>
<constructor name="LinkedHashMap"
@@ -265018,11 +273600,11 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="s" type="int">
+<parameter name="initialCapacity" type="int">
</parameter>
-<parameter name="lf" type="float">
+<parameter name="loadFactor" type="float">
</parameter>
-<parameter name="order" type="boolean">
+<parameter name="accessOrder" type="boolean">
</parameter>
</constructor>
<constructor name="LinkedHashMap"
@@ -265032,7 +273614,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="m" type="java.util.Map&lt;? extends K, ? extends V&gt;">
+<parameter name="map" type="java.util.Map&lt;? extends K, ? extends V&gt;">
</parameter>
</constructor>
<method name="removeEldestEntry"
@@ -266685,7 +275267,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -266698,7 +275280,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -266709,7 +275291,7 @@
return="int"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -266744,7 +275326,7 @@
return="boolean"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -266779,7 +275361,7 @@
return="void"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -269124,7 +277706,7 @@
return="E"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -269580,6 +278162,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="name" type="java.lang.String">
+</parameter>
<parameter name="isDaemon" type="boolean">
</parameter>
</constructor>
@@ -269590,6 +278174,8 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="name" type="java.lang.String">
+</parameter>
</constructor>
<constructor name="Timer"
type="java.util.Timer"
@@ -269598,8 +278184,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="name" type="java.lang.String">
-</parameter>
<parameter name="isDaemon" type="boolean">
</parameter>
</constructor>
@@ -269610,8 +278194,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="name" type="java.lang.String">
-</parameter>
</constructor>
<method name="cancel"
return="void"
@@ -270490,7 +279072,7 @@
return="E"
abstract="false"
native="false"
- synchronized="true"
+ synchronized="false"
static="false"
final="false"
deprecated="not deprecated"
@@ -270970,7 +279552,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="offer"
@@ -270983,7 +279565,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<parameter name="timeout" type="long">
</parameter>
@@ -271041,7 +279623,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<exception name="InterruptedException" type="java.lang.InterruptedException">
</exception>
@@ -271101,7 +279683,20 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
+</parameter>
+</method>
+<method name="contains"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="drainTo"
@@ -271142,7 +279737,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="offer"
@@ -271155,7 +279750,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<parameter name="timeout" type="long">
</parameter>
@@ -271191,7 +279786,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<exception name="InterruptedException" type="java.lang.InterruptedException">
</exception>
@@ -271207,6 +279802,19 @@
visibility="public"
>
</method>
+<method name="remove"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="o" type="java.lang.Object">
+</parameter>
+</method>
<method name="take"
return="E"
abstract="true"
@@ -271424,7 +280032,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="java.util.Map&lt;? extends K, ? extends V&gt;">
+<parameter name="m" type="java.util.Map&lt;? extends K, ? extends V&gt;">
</parameter>
</constructor>
<method name="contains"
@@ -271587,7 +280195,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="peek"
@@ -271737,7 +280345,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="array" type="E[]">
+<parameter name="toCopyIn" type="E[]">
</parameter>
</constructor>
<method name="add"
@@ -271765,7 +280373,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="e" type="E">
+<parameter name="element" type="E">
</parameter>
</method>
<method name="addAll"
@@ -271893,9 +280501,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="indexOf"
@@ -271908,7 +280514,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
</parameter>
</method>
<method name="isEmpty"
@@ -271943,9 +280551,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="e" type="E">
-</parameter>
-<parameter name="index" type="int">
+<parameter name="o" type="java.lang.Object">
</parameter>
</method>
<method name="lastIndexOf"
@@ -271958,7 +280564,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="java.lang.Object">
+<parameter name="e" type="E">
+</parameter>
+<parameter name="index" type="int">
</parameter>
</method>
<method name="listIterator"
@@ -272049,7 +280657,7 @@
>
<parameter name="index" type="int">
</parameter>
-<parameter name="e" type="E">
+<parameter name="element" type="E">
</parameter>
</method>
<method name="size"
@@ -272413,7 +281021,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="offer"
@@ -272426,7 +281034,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<parameter name="timeout" type="long">
</parameter>
@@ -272454,12 +281062,6 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="time" type="long">
-</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
-</parameter>
-<exception name="InterruptedException" type="java.lang.InterruptedException">
-</exception>
</method>
<method name="poll"
return="E"
@@ -272471,6 +281073,12 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="timeout" type="long">
+</parameter>
+<parameter name="unit" type="java.util.concurrent.TimeUnit">
+</parameter>
+<exception name="InterruptedException" type="java.lang.InterruptedException">
+</exception>
</method>
<method name="put"
return="void"
@@ -272482,7 +281090,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="remainingCapacity"
@@ -273553,7 +282161,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<parameter name="timeout" type="long">
</parameter>
@@ -273572,7 +282180,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="peek"
@@ -273624,7 +282232,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<exception name="InterruptedException" type="java.lang.InterruptedException">
</exception>
@@ -273777,7 +282385,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="offer"
@@ -273790,7 +282398,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
<parameter name="timeout" type="long">
</parameter>
@@ -273846,7 +282454,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="remainingCapacity"
@@ -274576,7 +283184,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="o" type="E">
+<parameter name="e" type="E">
</parameter>
</method>
<method name="peek"
@@ -275281,7 +283889,7 @@
extends="java.lang.Enum"
abstract="false"
static="false"
- final="true"
+ final="false"
deprecated="not deprecated"
visibility="public"
>
@@ -275295,9 +283903,9 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="duration" type="long">
+<parameter name="sourceDuration" type="long">
</parameter>
-<parameter name="unit" type="java.util.concurrent.TimeUnit">
+<parameter name="sourceUnit" type="java.util.concurrent.TimeUnit">
</parameter>
</method>
<method name="sleep"
@@ -277333,12 +285941,55 @@
</package>
<package name="java.util.concurrent.locks"
>
-<class name="AbstractQueuedSynchronizer"
+<class name="AbstractOwnableSynchronizer"
extends="java.lang.Object"
abstract="true"
static="false"
final="false"
deprecated="not deprecated"
+ visibility=""
+>
+<implements name="java.io.Serializable">
+</implements>
+<constructor name="AbstractOwnableSynchronizer"
+ type="java.util.concurrent.locks.AbstractOwnableSynchronizer"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</constructor>
+<method name="getExclusiveOwnerThread"
+ return="java.lang.Thread"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="setExclusiveOwnerThread"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+<parameter name="t" type="java.lang.Thread">
+</parameter>
+</method>
+</class>
+<class name="AbstractQueuedSynchronizer"
+ extends="java.util.concurrent.locks.AbstractOwnableSynchronizer"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
visibility="public"
>
<implements name="java.io.Serializable">
@@ -284906,7 +293557,11 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="val" type="int">
+<parameter name="buf" type="byte[]">
+</parameter>
+<parameter name="off" type="int">
+</parameter>
+<parameter name="nbytes" type="int">
</parameter>
</method>
<method name="update"
@@ -284919,11 +293574,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="buf" type="byte[]">
-</parameter>
-<parameter name="off" type="int">
-</parameter>
-<parameter name="nbytes" type="int">
+<parameter name="val" type="int">
</parameter>
</method>
</interface>
@@ -303751,7 +312402,7 @@
return="javax.net.ServerSocketFactory"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="true"
final="false"
deprecated="not deprecated"
@@ -304541,7 +313192,7 @@
return="javax.net.SocketFactory"
abstract="false"
native="false"
- synchronized="false"
+ synchronized="true"
static="true"
final="false"
deprecated="not deprecated"
diff --git a/camera/libcameraservice/Android.mk b/camera/libcameraservice/Android.mk
index 96cc512..ecaebff 100644
--- a/camera/libcameraservice/Android.mk
+++ b/camera/libcameraservice/Android.mk
@@ -25,6 +25,10 @@ LOCAL_SRC_FILES:= \
LOCAL_MODULE:= libcamerastub
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_CFLAGS += -DSINGLE_PROCESS
+endif
+
LOCAL_SHARED_LIBRARIES:= libui
include $(BUILD_STATIC_LIBRARY)
@@ -42,12 +46,17 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES:= \
libui \
libutils \
+ libbinder \
libcutils \
libmedia
LOCAL_MODULE:= libcameraservice
-LOCAL_CFLAGS+=-DLOG_TAG=\"CameraService\"
+LOCAL_CFLAGS += -DLOG_TAG=\"CameraService\"
+
+ifeq ($(TARGET_SIMULATOR),true)
+LOCAL_CFLAGS += -DSINGLE_PROCESS
+endif
ifeq ($(USE_CAMERA_STUB), true)
LOCAL_STATIC_LIBRARIES += libcamerastub
diff --git a/camera/libcameraservice/CameraHardwareStub.cpp b/camera/libcameraservice/CameraHardwareStub.cpp
index a7af57c..24496bb 100644
--- a/camera/libcameraservice/CameraHardwareStub.cpp
+++ b/camera/libcameraservice/CameraHardwareStub.cpp
@@ -33,13 +33,11 @@ CameraHardwareStub::CameraHardwareStub()
mRawHeap(0),
mFakeCamera(0),
mPreviewFrameSize(0),
- mRawPictureCallback(0),
- mJpegPictureCallback(0),
- mPictureCallbackCookie(0),
- mPreviewCallback(0),
- mPreviewCallbackCookie(0),
- mAutoFocusCallback(0),
- mAutoFocusCallbackCookie(0),
+ mNotifyCb(0),
+ mDataCb(0),
+ mDataCbTimestamp(0),
+ mCallbackCookie(0),
+ mMsgEnabled(0),
mCurrentPreviewFrame(0)
{
initDefaultParameters();
@@ -112,6 +110,36 @@ sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const
return mRawHeap;
}
+void CameraHardwareStub::setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user)
+{
+ Mutex::Autolock lock(mLock);
+ mNotifyCb = notify_cb;
+ mDataCb = data_cb;
+ mDataCbTimestamp = data_cb_timestamp;
+ mCallbackCookie = user;
+}
+
+void CameraHardwareStub::enableMsgType(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ mMsgEnabled |= msgType;
+}
+
+void CameraHardwareStub::disableMsgType(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ mMsgEnabled &= ~msgType;
+}
+
+bool CameraHardwareStub::msgTypeEnabled(int32_t msgType)
+{
+ Mutex::Autolock lock(mLock);
+ return (mMsgEnabled & msgType);
+}
+
// ---------------------------------------------------------------------------
int CameraHardwareStub::previewThread()
@@ -150,7 +178,8 @@ int CameraHardwareStub::previewThread()
//LOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame);
// Notify the client of a new frame.
- mPreviewCallback(buffer, mPreviewCallbackCookie);
+ if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)
+ mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);
// Advance the buffer pointer.
mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;
@@ -162,15 +191,13 @@ int CameraHardwareStub::previewThread()
return NO_ERROR;
}
-status_t CameraHardwareStub::startPreview(preview_callback cb, void* user)
+status_t CameraHardwareStub::startPreview()
{
Mutex::Autolock lock(mLock);
if (mPreviewThread != 0) {
// already running
return INVALID_OPERATION;
}
- mPreviewCallback = cb;
- mPreviewCallbackCookie = user;
mPreviewThread = new PreviewThread(this);
return NO_ERROR;
}
@@ -197,7 +224,7 @@ bool CameraHardwareStub::previewEnabled() {
return mPreviewThread != 0;
}
-status_t CameraHardwareStub::startRecording(recording_callback cb, void* user)
+status_t CameraHardwareStub::startRecording()
{
return UNKNOWN_ERROR;
}
@@ -225,25 +252,14 @@ int CameraHardwareStub::beginAutoFocusThread(void *cookie)
int CameraHardwareStub::autoFocusThread()
{
- if (mAutoFocusCallback != NULL) {
- mAutoFocusCallback(true, mAutoFocusCallbackCookie);
- mAutoFocusCallback = NULL;
- return NO_ERROR;
- }
- return UNKNOWN_ERROR;
+ if (mMsgEnabled & CAMERA_MSG_FOCUS)
+ mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie);
+ return NO_ERROR;
}
-status_t CameraHardwareStub::autoFocus(autofocus_callback af_cb,
- void *user)
+status_t CameraHardwareStub::autoFocus()
{
Mutex::Autolock lock(mLock);
-
- if (mAutoFocusCallback != NULL) {
- return mAutoFocusCallback == af_cb ? NO_ERROR : INVALID_OPERATION;
- }
-
- mAutoFocusCallback = af_cb;
- mAutoFocusCallbackCookie = user;
if (createThread(beginAutoFocusThread, this) == false)
return UNKNOWN_ERROR;
return NO_ERROR;
@@ -257,10 +273,10 @@ status_t CameraHardwareStub::autoFocus(autofocus_callback af_cb,
int CameraHardwareStub::pictureThread()
{
- if (mShutterCallback)
- mShutterCallback(mPictureCallbackCookie);
+ if (mMsgEnabled & CAMERA_MSG_SHUTTER)
+ mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie);
- if (mRawPictureCallback) {
+ if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) {
//FIXME: use a canned YUV image!
// In the meantime just make another fake camera picture.
int w, h;
@@ -268,42 +284,28 @@ int CameraHardwareStub::pictureThread()
sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * 2 * h);
FakeCamera cam(w, h);
cam.getNextFrameAsYuv422((uint8_t *)mRawHeap->base());
- if (mRawPictureCallback)
- mRawPictureCallback(mem, mPictureCallbackCookie);
+ mDataCb(CAMERA_MSG_RAW_IMAGE, mem, mCallbackCookie);
}
- if (mJpegPictureCallback) {
+ if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) {
sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize);
sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize);
memcpy(heap->base(), kCannedJpeg, kCannedJpegSize);
- if (mJpegPictureCallback)
- mJpegPictureCallback(mem, mPictureCallbackCookie);
+ mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, mCallbackCookie);
}
return NO_ERROR;
}
-status_t CameraHardwareStub::takePicture(shutter_callback shutter_cb,
- raw_callback raw_cb,
- jpeg_callback jpeg_cb,
- void* user)
+status_t CameraHardwareStub::takePicture()
{
stopPreview();
- mShutterCallback = shutter_cb;
- mRawPictureCallback = raw_cb;
- mJpegPictureCallback = jpeg_cb;
- mPictureCallbackCookie = user;
if (createThread(beginPictureThread, this) == false)
return -1;
return NO_ERROR;
}
-status_t CameraHardwareStub::cancelPicture(bool cancel_shutter,
- bool cancel_raw,
- bool cancel_jpeg)
+status_t CameraHardwareStub::cancelPicture()
{
- if (cancel_shutter) mShutterCallback = NULL;
- if (cancel_raw) mRawPictureCallback = NULL;
- if (cancel_jpeg) mJpegPictureCallback = NULL;
return NO_ERROR;
}
diff --git a/camera/libcameraservice/CameraHardwareStub.h b/camera/libcameraservice/CameraHardwareStub.h
index 0d26d47..000906a 100644
--- a/camera/libcameraservice/CameraHardwareStub.h
+++ b/camera/libcameraservice/CameraHardwareStub.h
@@ -21,8 +21,8 @@
#include "FakeCamera.h"
#include <utils/threads.h>
#include <ui/CameraHardwareInterface.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
#include <utils/threads.h>
namespace android {
@@ -32,23 +32,27 @@ public:
virtual sp<IMemoryHeap> getPreviewHeap() const;
virtual sp<IMemoryHeap> getRawHeap() const;
- virtual status_t startPreview(preview_callback cb, void* user);
+ virtual void setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user);
+
+ virtual void enableMsgType(int32_t msgType);
+ virtual void disableMsgType(int32_t msgType);
+ virtual bool msgTypeEnabled(int32_t msgType);
+
+ virtual status_t startPreview();
virtual void stopPreview();
virtual bool previewEnabled();
- virtual status_t startRecording(recording_callback cb, void* user);
+ virtual status_t startRecording();
virtual void stopRecording();
virtual bool recordingEnabled();
virtual void releaseRecordingFrame(const sp<IMemory>& mem);
- virtual status_t autoFocus(autofocus_callback, void *user);
- virtual status_t takePicture(shutter_callback,
- raw_callback,
- jpeg_callback,
- void* user);
- virtual status_t cancelPicture(bool cancel_shutter,
- bool cancel_raw,
- bool cancel_jpeg);
+ virtual status_t autoFocus();
+ virtual status_t takePicture();
+ virtual status_t cancelPicture();
virtual status_t dump(int fd, const Vector<String16>& args) const;
virtual status_t setParameters(const CameraParameters& params);
virtual CameraParameters getParameters() const;
@@ -67,8 +71,15 @@ private:
class PreviewThread : public Thread {
CameraHardwareStub* mHardware;
public:
- PreviewThread(CameraHardwareStub* hw)
- : Thread(false), mHardware(hw) { }
+ PreviewThread(CameraHardwareStub* hw) :
+#ifdef SINGLE_PROCESS
+ // In single process mode this thread needs to be a java thread,
+ // since we won't be calling through the binder.
+ Thread(true),
+#else
+ Thread(false),
+#endif
+ mHardware(hw) { }
virtual void onFirstRef() {
run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY);
}
@@ -102,18 +113,15 @@ private:
bool mPreviewRunning;
int mPreviewFrameSize;
- shutter_callback mShutterCallback;
- raw_callback mRawPictureCallback;
- jpeg_callback mJpegPictureCallback;
- void *mPictureCallbackCookie;
-
// protected by mLock
sp<PreviewThread> mPreviewThread;
- preview_callback mPreviewCallback;
- void *mPreviewCallbackCookie;
- autofocus_callback mAutoFocusCallback;
- void *mAutoFocusCallbackCookie;
+ notify_callback mNotifyCb;
+ data_callback mDataCb;
+ data_callback_timestamp mDataCbTimestamp;
+ void *mCallbackCookie;
+
+ int32_t mMsgEnabled;
// only used from PreviewThread
int mCurrentPreviewFrame;
diff --git a/camera/libcameraservice/CameraService.cpp b/camera/libcameraservice/CameraService.cpp
index e4b6791..3c2dc2b 100644
--- a/camera/libcameraservice/CameraService.cpp
+++ b/camera/libcameraservice/CameraService.cpp
@@ -20,12 +20,12 @@
#define LOG_TAG "CameraService"
#include <utils/Log.h>
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/Errors.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
#include <ui/ICameraService.h>
#include <media/mediaplayer.h>
@@ -33,7 +33,6 @@
#include "CameraService.h"
#include <cutils/atomic.h>
-#include <cutils/properties.h>
namespace android {
@@ -60,6 +59,7 @@ extern "C" {
#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */
#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0
#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0
+#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
static int debug_frame_cnt;
@@ -199,13 +199,7 @@ static sp<MediaPlayer> newMediaPlayer(const char *file)
{
sp<MediaPlayer> mp = new MediaPlayer();
if (mp->setDataSource(file) == NO_ERROR) {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.camera.sound.forced", value, "0");
- if (atoi(value)) {
- mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
- } else {
- mp->setAudioStreamType(AudioSystem::SYSTEM);
- }
+ mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
mp->prepare();
} else {
mp.clear();
@@ -225,8 +219,20 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
mHardware = openCameraHardware();
mUseOverlay = mHardware->useOverlay();
+ mHardware->setCallbacks(notifyCallback,
+ dataCallback,
+ dataCallbackTimestamp,
+ mCameraService.get());
+
+ // Enable zoom, error, and focus messages by default
+ mHardware->enableMsgType(CAMERA_MSG_ERROR |
+ CAMERA_MSG_ZOOM |
+ CAMERA_MSG_FOCUS);
+
mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
+ mOverlayW = 0;
+ mOverlayH = 0;
// Callback is disabled by default
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
@@ -396,9 +402,20 @@ void CameraService::Client::disconnect()
// idle state.
mHardware->stopPreview();
// Cancel all picture callbacks.
- mHardware->cancelPicture(true, true, true);
+ mHardware->disableMsgType(CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+ mHardware->cancelPicture();
+ // Turn off remaining messages.
+ mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
// Release the hardware resources.
mHardware->release();
+ // Release the held overlay resources.
+ if (mUseOverlay)
+ {
+ mOverlayRef = 0;
+ }
mHardware.clear();
mCameraService->removeClient(mCameraClient);
@@ -420,11 +437,21 @@ status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
result = NO_ERROR;
// asBinder() is safe on NULL (returns NULL)
if (surface->asBinder() != mSurface->asBinder()) {
- if (mSurface != 0 && !mUseOverlay) {
+ if (mSurface != 0) {
LOGD("clearing old preview surface %p", mSurface.get());
- mSurface->unregisterBuffers();
+ if ( !mUseOverlay)
+ {
+ mSurface->unregisterBuffers();
+ }
+ else
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ }
}
mSurface = surface;
+ mOverlayRef = 0;
// If preview has been already started, set overlay or register preview
// buffers now.
if (mHardware->previewEnabled()) {
@@ -446,6 +473,13 @@ void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
Mutex::Autolock lock(mLock);
if (checkPid() != NO_ERROR) return;
mPreviewCallbackFlag = callback_flag;
+
+ if(mUseOverlay) {
+ if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
+ mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ else
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
}
// start preview mode
@@ -504,7 +538,7 @@ status_t CameraService::Client::startRecordingMode()
}
// start recording mode
- ret = mHardware->startRecording(recordingCallback, mCameraService.get());
+ ret = mHardware->startRecording();
if (ret != NO_ERROR) {
LOGE("mHardware->startRecording() failed with status %d", ret);
}
@@ -518,27 +552,35 @@ status_t CameraService::Client::setOverlay()
CameraParameters params(mHardware->getParameters());
params.getPreviewSize(&w, &h);
- const char *format = params.getPreviewFormat();
- int fmt;
- if (!strcmp(format, "yuv422i"))
- fmt = OVERLAY_FORMAT_YCbCr_422_I;
- else if (!strcmp(format, "rgb565"))
- fmt = OVERLAY_FORMAT_RGB_565;
- else {
- LOGE("Invalid preview format for overlays");
- return -EINVAL;
+ if ( w != mOverlayW || h != mOverlayH )
+ {
+ // Force the destruction of any previous overlay
+ sp<Overlay> dummy;
+ mHardware->setOverlay( dummy );
+ mOverlayRef = 0;
}
status_t ret = NO_ERROR;
if (mSurface != 0) {
- sp<OverlayRef> ref = mSurface->createOverlay(w, h, fmt);
- ret = mHardware->setOverlay(new Overlay(ref));
+ if (mOverlayRef.get() == NULL) {
+ mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT);
+ if ( mOverlayRef.get() == NULL )
+ {
+ LOGE("Overlay Creation Failed!");
+ return -EINVAL;
+ }
+ ret = mHardware->setOverlay(new Overlay(mOverlayRef));
+ }
} else {
ret = mHardware->setOverlay(NULL);
}
if (ret != NO_ERROR) {
LOGE("mHardware->setOverlay() failed with status %d\n", ret);
}
+
+ mOverlayW = w;
+ mOverlayH = h;
+
return ret;
}
@@ -588,10 +630,10 @@ status_t CameraService::Client::startPreviewMode()
ret = setOverlay();
}
if (ret != NO_ERROR) return ret;
- ret = mHardware->startPreview(NULL, mCameraService.get());
+ ret = mHardware->startPreview();
} else {
- ret = mHardware->startPreview(previewCallback,
- mCameraService.get());
+ mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ ret = mHardware->startPreview();
if (ret != NO_ERROR) return ret;
// If preview display has been set, register preview buffers now.
if (mSurface != 0) {
@@ -618,6 +660,9 @@ status_t CameraService::Client::startRecording()
mMediaPlayerBeep->seekTo(0);
mMediaPlayerBeep->start();
}
+
+ mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
+
return startCameraMode(CAMERA_RECORDING_MODE);
}
@@ -635,6 +680,7 @@ void CameraService::Client::stopPreview()
}
mHardware->stopPreview();
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
LOGD("stopPreview(), hardware stopped OK");
if (mSurface != 0 && !mUseOverlay) {
@@ -660,8 +706,11 @@ void CameraService::Client::stopRecording()
mMediaPlayerBeep->seekTo(0);
mMediaPlayerBeep->start();
}
+
mHardware->stopRecording();
+ mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
LOGD("stopRecording(), hardware stopped OK");
+
mPreviewBuffer.clear();
}
@@ -749,65 +798,6 @@ static void dump_to_file(const char *fname,
}
#endif
-// preview callback - frame buffer update
-void CameraService::Client::previewCallback(const sp<IMemory>& mem, void* user)
-{
- LOGV("previewCallback()");
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
-#if DEBUG_HEAP_LEAKS && 0 // debugging
- if (gWeakHeap == NULL) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- if (gWeakHeap != heap) {
- LOGD("SETTING PREVIEW HEAP");
- heap->trackMe(true, true);
- gWeakHeap = heap;
- }
- }
-#endif
-
-#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
- {
- if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/preview.yuv",
- (uint8_t *)heap->base() + offset, size);
- }
- }
-#endif
-
- // The strong pointer guarantees the client will exist, but no lock is held.
- client->postPreviewFrame(mem);
-
-#if DEBUG_CLIENT_REFERENCES
- //**** if the client's refcount is 1, then we are about to destroy it here,
- // which is bad--print all refcounts.
- if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (PREVIEW) THIS WILL CAUSE A LOCKUP!");
- client->printRefs();
- }
-#endif
-}
-
-// recording callback
-void CameraService::Client::recordingCallback(nsecs_t timestamp, const sp<IMemory>& mem, void* user)
-{
- LOGV("recordingCallback");
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
- // The strong pointer guarantees the client will exist, but no lock is held.
- client->postRecordingFrame(timestamp, mem);
-}
-
// take a picture - image is returned in callback
status_t CameraService::Client::autoFocus()
{
@@ -822,8 +812,7 @@ status_t CameraService::Client::autoFocus()
return INVALID_OPERATION;
}
- return mHardware->autoFocus(autoFocusCallback,
- mCameraService.get());
+ return mHardware->autoFocus();
}
// take a picture - image is returned in callback
@@ -840,38 +829,36 @@ status_t CameraService::Client::takePicture()
return INVALID_OPERATION;
}
- return mHardware->takePicture(shutterCallback,
- yuvPictureCallback,
- jpegPictureCallback,
- mCameraService.get());
+ mHardware->enableMsgType(CAMERA_MSG_SHUTTER |
+ CAMERA_MSG_POSTVIEW_FRAME |
+ CAMERA_MSG_RAW_IMAGE |
+ CAMERA_MSG_COMPRESSED_IMAGE);
+
+ return mHardware->takePicture();
}
-// picture callback - snapshot taken
-void CameraService::Client::shutterCallback(void *user)
+// snapshot taken
+void CameraService::Client::handleShutter()
{
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
- }
-
// Play shutter sound.
- if (client->mMediaPlayerClick.get() != NULL) {
- client->mMediaPlayerClick->seekTo(0);
- client->mMediaPlayerClick->start();
+ if (mMediaPlayerClick.get() != NULL) {
+ mMediaPlayerClick->seekTo(0);
+ mMediaPlayerClick->start();
}
// Screen goes black after the buffer is unregistered.
- if (client->mSurface != 0 && !client->mUseOverlay) {
- client->mSurface->unregisterBuffers();
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->unregisterBuffers();
}
- client->postShutter();
+ mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
+ mHardware->disableMsgType(CAMERA_MSG_SHUTTER);
// It takes some time before yuvPicture callback to be called.
// Register the buffer for raw image here to reduce latency.
- if (client->mSurface != 0 && !client->mUseOverlay) {
+ if (mSurface != 0 && !mUseOverlay) {
int w, h;
- CameraParameters params(client->mHardware->getParameters());
+ CameraParameters params(mHardware->getParameters());
params.getPictureSize(&w, &h);
uint32_t transform = 0;
if (params.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
@@ -879,26 +866,92 @@ void CameraService::Client::shutterCallback(void *user)
transform = ISurface::BufferHeap::ROT_90;
}
ISurface::BufferHeap buffers(w, h, w, h,
- PIXEL_FORMAT_YCbCr_420_SP, transform, 0, client->mHardware->getRawHeap());
+ PIXEL_FORMAT_YCbCr_420_SP, transform, 0, mHardware->getRawHeap());
- client->mSurface->registerBuffers(buffers);
+ mSurface->registerBuffers(buffers);
}
}
-// picture callback - raw image ready
-void CameraService::Client::yuvPictureCallback(const sp<IMemory>& mem,
- void *user)
+// preview callback - frame buffer update
+void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
{
- sp<Client> client = getClientFromCookie(user);
- if (client == 0) {
- return;
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+
+#if DEBUG_HEAP_LEAKS && 0 // debugging
+ if (gWeakHeap == NULL) {
+ if (gWeakHeap != heap) {
+ LOGD("SETTING PREVIEW HEAP");
+ heap->trackMe(true, true);
+ gWeakHeap = heap;
+ }
+ }
+#endif
+#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
+ {
+ if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
+ dump_to_file("/data/preview.yuv",
+ (uint8_t *)heap->base() + offset, size);
+ }
+ }
+#endif
+
+ if (!mUseOverlay)
+ {
+ Mutex::Autolock surfaceLock(mSurfaceLock);
+ if (mSurface != NULL) {
+ mSurface->postBuffer(offset);
+ }
}
- if (mem == NULL) {
- client->postRaw(NULL);
- client->postError(UNKNOWN_ERROR);
+
+ // Is the callback enabled or not?
+ if (!(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
+ // If the enable bit is off, the copy-out and one-shot bits are ignored
+ LOGV("frame callback is diabled");
return;
}
+ // Is the received frame copied out or not?
+ if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
+ LOGV("frame is copied out");
+ copyFrameAndPostCopiedFrame(heap, offset, size);
+ } else {
+ LOGV("frame is directly sent out without copying");
+ mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
+ }
+
+ // Is this is one-shot only?
+ if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) {
+ LOGV("One-shot only, thus clear the bits and disable frame callback");
+ mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
+ FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
+ FRAME_CALLBACK_FLAG_ENABLE_MASK);
+ if (mUseOverlay)
+ mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
+ }
+}
+
+// picture callback - postview image ready
+void CameraService::Client::handlePostview(const sp<IMemory>& mem)
+{
+#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only
+ {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ dump_to_file("/data/postview.yuv",
+ (uint8_t *)heap->base() + offset, size);
+ }
+#endif
+
+ mCameraClient->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
+ mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
+}
+
+// picture callback - raw image ready
+void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
+{
ssize_t offset;
size_t size;
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
@@ -906,80 +959,128 @@ void CameraService::Client::yuvPictureCallback(const sp<IMemory>& mem,
gWeakHeap = heap; // debugging
#endif
- //LOGV("yuvPictureCallback(%d, %d, %p)", offset, size, user);
+ //LOGV("handleRawPicture(%d, %d)", offset, size);
#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only
dump_to_file("/data/photo.yuv",
(uint8_t *)heap->base() + offset, size);
#endif
// Put the YUV version of the snapshot in the preview display.
- if (client->mSurface != 0 && !client->mUseOverlay) {
- client->mSurface->postBuffer(offset);
+ if (mSurface != 0 && !mUseOverlay) {
+ mSurface->postBuffer(offset);
+ }
+
+ mCameraClient->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
+ mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);
+}
+
+// picture callback - compressed picture ready
+void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)
+{
+#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
+ {
+ ssize_t offset;
+ size_t size;
+ sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+ dump_to_file("/data/photo.jpg",
+ (uint8_t *)heap->base() + offset, size);
+ }
+#endif
+
+ mCameraClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
+ mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
+}
+
+void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user)
+{
+ LOGV("notifyCallback(%d)", msgType);
+
+ sp<Client> client = getClientFromCookie(user);
+ if (client == 0) {
+ return;
}
- client->postRaw(mem);
+ switch (msgType) {
+ case CAMERA_MSG_SHUTTER:
+ client->handleShutter();
+ break;
+ default:
+ client->mCameraClient->notifyCallback(msgType, ext1, ext2);
+ break;
+ }
#if DEBUG_CLIENT_REFERENCES
- //**** if the client's refcount is 1, then we are about to destroy it here,
- // which is bad--print all refcounts.
if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (RAW) THIS WILL CAUSE A LOCKUP!");
+ LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!");
client->printRefs();
}
#endif
}
-// picture callback - jpeg ready
-void CameraService::Client::jpegPictureCallback(const sp<IMemory>& mem, void *user)
+void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
{
+ LOGV("dataCallback(%d)", msgType);
+
sp<Client> client = getClientFromCookie(user);
if (client == 0) {
return;
}
- if (mem == NULL) {
- client->postJpeg(NULL);
- client->postError(UNKNOWN_ERROR);
+
+ if (dataPtr == NULL) {
+ LOGE("Null data returned in data callback");
+ client->mCameraClient->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ client->mCameraClient->dataCallback(msgType, NULL);
return;
}
- /** We absolutely CANNOT call into user code with a lock held **/
-
-#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
- {
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- dump_to_file("/data/photo.jpg",
- (uint8_t *)heap->base() + offset, size);
+ switch (msgType) {
+ case CAMERA_MSG_PREVIEW_FRAME:
+ client->handlePreviewData(dataPtr);
+ break;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ client->handlePostview(dataPtr);
+ break;
+ case CAMERA_MSG_RAW_IMAGE:
+ client->handleRawPicture(dataPtr);
+ break;
+ case CAMERA_MSG_COMPRESSED_IMAGE:
+ client->handleCompressedPicture(dataPtr);
+ break;
+ default:
+ client->mCameraClient->dataCallback(msgType, dataPtr);
+ break;
}
-#endif
-
- client->postJpeg(mem);
#if DEBUG_CLIENT_REFERENCES
- //**** if the client's refcount is 1, then we are about to destroy it here,
- // which is bad--print all refcounts.
if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (JPEG) THIS WILL CAUSE A LOCKUP!");
+ LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
client->printRefs();
}
#endif
}
-void CameraService::Client::autoFocusCallback(bool focused, void *user)
+void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& dataPtr, void* user)
{
- LOGV("autoFocusCallback");
+ LOGV("dataCallbackTimestamp(%d)", msgType);
sp<Client> client = getClientFromCookie(user);
if (client == 0) {
return;
}
- client->postAutoFocus(focused);
+ if (dataPtr == NULL) {
+ LOGE("Null data returned in data with timestamp callback");
+ client->mCameraClient->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
+ client->mCameraClient->dataCallbackTimestamp(0, msgType, NULL);
+ return;
+ }
+
+ client->mCameraClient->dataCallbackTimestamp(timestamp, msgType, dataPtr);
#if DEBUG_CLIENT_REFERENCES
if (client->getStrongCount() == 1) {
- LOGE("++++++++++++++++ (AUTOFOCUS) THIS WILL CAUSE A LOCKUP!");
+ LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!");
client->printRefs();
}
#endif
@@ -1019,30 +1120,6 @@ String8 CameraService::Client::getParameters() const
return params;
}
-void CameraService::Client::postAutoFocus(bool focused)
-{
- LOGV("postAutoFocus");
- mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, (int32_t)focused, 0);
-}
-
-void CameraService::Client::postShutter()
-{
- LOGD("postShutter");
- mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
-}
-
-void CameraService::Client::postRaw(const sp<IMemory>& mem)
-{
- LOGD("postRaw");
- mCameraClient->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
-}
-
-void CameraService::Client::postJpeg(const sp<IMemory>& mem)
-{
- LOGD("postJpeg");
- mCameraClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
-}
-
void CameraService::Client::copyFrameAndPostCopiedFrame(sp<IMemoryHeap> heap, size_t offset, size_t size)
{
LOGV("copyFrameAndPostCopiedFrame");
@@ -1071,64 +1148,6 @@ void CameraService::Client::copyFrameAndPostCopiedFrame(sp<IMemoryHeap> heap, si
mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
}
-void CameraService::Client::postRecordingFrame(nsecs_t timestamp, const sp<IMemory>& frame)
-{
- LOGV("postRecordingFrame");
- if (frame == 0) {
- LOGW("frame is a null pointer");
- return;
- }
- mCameraClient->dataCallbackTimestamp(timestamp, CAMERA_MSG_VIDEO_FRAME, frame);
-}
-
-void CameraService::Client::postPreviewFrame(const sp<IMemory>& mem)
-{
- LOGV("postPreviewFrame");
- if (mem == 0) {
- LOGW("mem is a null pointer");
- return;
- }
-
- ssize_t offset;
- size_t size;
- sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
- {
- Mutex::Autolock surfaceLock(mSurfaceLock);
- if (mSurface != NULL) {
- mSurface->postBuffer(offset);
- }
- }
-
- // Is the callback enabled or not?
- if (!(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
- // If the enable bit is off, the copy-out and one-shot bits are ignored
- LOGV("frame callback is diabled");
- return;
- }
-
- // Is the received frame copied out or not?
- if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
- LOGV("frame is copied out");
- copyFrameAndPostCopiedFrame(heap, offset, size);
- } else {
- LOGV("frame is directly sent out without copying");
- mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
- }
-
- // Is this is one-shot only?
- if (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) {
- LOGV("One-shot only, thus clear the bits and disable frame callback");
- mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
- FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
- FRAME_CALLBACK_FLAG_ENABLE_MASK);
- }
-}
-
-void CameraService::Client::postError(status_t error)
-{
- mCameraClient->notifyCallback(CAMERA_MSG_ERROR, error, 0);
-}
-
status_t CameraService::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
@@ -1160,12 +1179,6 @@ status_t CameraService::dump(int fd, const Vector<String16>& args)
}
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t CameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/camera/libcameraservice/CameraService.h b/camera/libcameraservice/CameraService.h
index ea93789..f8c7216 100644
--- a/camera/libcameraservice/CameraService.h
+++ b/camera/libcameraservice/CameraService.h
@@ -132,22 +132,20 @@ private:
status_t checkPid();
- static void recordingCallback(nsecs_t timestamp, const sp<IMemory>& mem, void* user);
- static void previewCallback(const sp<IMemory>& mem, void* user);
- static void shutterCallback(void *user);
- static void yuvPictureCallback(const sp<IMemory>& mem, void* user);
- static void jpegPictureCallback(const sp<IMemory>& mem, void* user);
- static void autoFocusCallback(bool focused, void* user);
+ static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user);
+ static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+ static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+ const sp<IMemory>& dataPtr, void* user);
+
static sp<Client> getClientFromCookie(void* user);
- void postShutter();
- void postRaw(const sp<IMemory>& mem);
- void postJpeg(const sp<IMemory>& mem);
- void postPreviewFrame(const sp<IMemory>& mem);
- void postRecordingFrame(nsecs_t timestamp, const sp<IMemory>& frame);
+ void handlePreviewData(const sp<IMemory>&);
+ void handleShutter();
+ void handlePostview(const sp<IMemory>&);
+ void handleRawPicture(const sp<IMemory>&);
+ void handleCompressedPicture(const sp<IMemory>&);
+
void copyFrameAndPostCopiedFrame(sp<IMemoryHeap> heap, size_t offset, size_t size);
- void postError(status_t error);
- void postAutoFocus(bool focused);
// camera operation mode
enum camera_mode {
@@ -189,6 +187,10 @@ private:
sp<CameraHardwareInterface> mHardware;
pid_t mClientPid;
bool mUseOverlay;
+
+ sp<OverlayRef> mOverlayRef;
+ int mOverlayW;
+ int mOverlayH;
};
// ----------------------------------------------------------------------------
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3782136..0b4f25e 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -23,16 +23,19 @@ import android.app.IActivityManager;
import android.app.IInstrumentationWatcher;
import android.app.Instrumentation;
import android.content.ComponentName;
+import android.content.IIntentReceiver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.AndroidException;
import android.view.IWindowManager;
import java.io.File;
import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Set;
@@ -45,16 +48,29 @@ public class Am {
private boolean mDebugOption = false;
+ // These are magic strings understood by the Eclipse plugin.
+ private static final String FATAL_ERROR_CODE = "Error type 1";
+ private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
+ private static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
/**
* Command-line entry point.
*
* @param args The command-line arguments
*/
public static void main(String[] args) {
- (new Am()).run(args);
+ try {
+ (new Am()).run(args);
+ } catch (IllegalArgumentException e) {
+ showUsage();
+ System.err.println("Error: " + e.getMessage());
+ } catch (Exception e) {
+ System.err.println(e.toString());
+ System.exit(1);
+ }
}
- private void run(String[] args) {
+ private void run(String[] args) throws Exception {
if (args.length < 1) {
showUsage();
return;
@@ -62,16 +78,14 @@ public class Am {
mAm = ActivityManagerNative.getDefault();
if (mAm == null) {
- System.err.println("Error type 2");
- System.err.println("Error: Unable to connect to activity manager; is the system running?");
- showUsage();
- return;
+ System.err.println(NO_SYSTEM_ERROR_CODE);
+ throw new AndroidException("Can't connect to activity manager; is the system running?");
}
mArgs = args;
-
String op = args[0];
mNextArg = 1;
+
if (op.equals("start")) {
runStart();
} else if (op.equals("instrument")) {
@@ -81,13 +95,11 @@ public class Am {
} else if (op.equals("profile")) {
runProfile();
} else {
- System.err.println("Error: Unknown command: " + op);
- showUsage();
- return;
+ throw new IllegalArgumentException("Unknown command: " + op);
}
}
- private Intent makeIntent() {
+ private Intent makeIntent() throws URISyntaxException {
Intent intent = new Intent();
boolean hasIntentInfo = false;
@@ -95,186 +107,146 @@ public class Am {
Uri data = null;
String type = null;
- try {
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-a")) {
- intent.setAction(nextOptionData());
- hasIntentInfo = true;
- } else if (opt.equals("-d")) {
- data = Uri.parse(nextOptionData());
- hasIntentInfo = true;
- } else if (opt.equals("-t")) {
- type = nextOptionData();
- hasIntentInfo = true;
- } else if (opt.equals("-c")) {
- intent.addCategory(nextOptionData());
- hasIntentInfo = true;
- } else if (opt.equals("-e") || opt.equals("--es")) {
- String key = nextOptionData();
- String value = nextOptionData();
- intent.putExtra(key, value);
- hasIntentInfo = true;
- } else if (opt.equals("--ei")) {
- String key = nextOptionData();
- String value = nextOptionData();
- intent.putExtra(key, Integer.valueOf(value));
- hasIntentInfo = true;
- } else if (opt.equals("--ez")) {
- String key = nextOptionData();
- String value = nextOptionData();
- intent.putExtra(key, Boolean.valueOf(value));
- hasIntentInfo = true;
- } else if (opt.equals("-n")) {
- String str = nextOptionData();
- ComponentName cn = ComponentName.unflattenFromString(str);
- if (cn == null) {
- System.err.println("Error: Bad component name: " + str);
- showUsage();
- return null;
- }
- intent.setComponent(cn);
- hasIntentInfo = true;
- } else if (opt.equals("-f")) {
- String str = nextOptionData();
- intent.setFlags(Integer.decode(str).intValue());
- } else if (opt.equals("-D")) {
- mDebugOption = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- showUsage();
- return null;
- }
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-a")) {
+ intent.setAction(nextArgRequired());
+ hasIntentInfo = true;
+ } else if (opt.equals("-d")) {
+ data = Uri.parse(nextArgRequired());
+ hasIntentInfo = true;
+ } else if (opt.equals("-t")) {
+ type = nextArgRequired();
+ hasIntentInfo = true;
+ } else if (opt.equals("-c")) {
+ intent.addCategory(nextArgRequired());
+ hasIntentInfo = true;
+ } else if (opt.equals("-e") || opt.equals("--es")) {
+ String key = nextArgRequired();
+ String value = nextArgRequired();
+ intent.putExtra(key, value);
+ hasIntentInfo = true;
+ } else if (opt.equals("--ei")) {
+ String key = nextArgRequired();
+ String value = nextArgRequired();
+ intent.putExtra(key, Integer.valueOf(value));
+ hasIntentInfo = true;
+ } else if (opt.equals("--ez")) {
+ String key = nextArgRequired();
+ String value = nextArgRequired();
+ intent.putExtra(key, Boolean.valueOf(value));
+ hasIntentInfo = true;
+ } else if (opt.equals("-n")) {
+ String str = nextArgRequired();
+ ComponentName cn = ComponentName.unflattenFromString(str);
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + str);
+ intent.setComponent(cn);
+ hasIntentInfo = true;
+ } else if (opt.equals("-f")) {
+ String str = nextArgRequired();
+ intent.setFlags(Integer.decode(str).intValue());
+ } else if (opt.equals("-D")) {
+ mDebugOption = true;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return null;
}
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- showUsage();
- return null;
}
intent.setDataAndType(data, type);
String uri = nextArg();
if (uri != null) {
- try {
- Intent oldIntent = intent;
- try {
- intent = Intent.getIntent(uri);
- } catch (java.net.URISyntaxException ex) {
- System.err.println("Bad URI: " + uri);
- showUsage();
- return null;
- }
- if (oldIntent.getAction() != null) {
- intent.setAction(oldIntent.getAction());
- }
- if (oldIntent.getData() != null || oldIntent.getType() != null) {
- intent.setDataAndType(oldIntent.getData(), oldIntent.getType());
- }
- Set cats = oldIntent.getCategories();
- if (cats != null) {
- Iterator it = cats.iterator();
- while (it.hasNext()) {
- intent.addCategory((String)it.next());
- }
+ Intent oldIntent = intent;
+ intent = Intent.getIntent(uri);
+ if (oldIntent.getAction() != null) {
+ intent.setAction(oldIntent.getAction());
+ }
+ if (oldIntent.getData() != null || oldIntent.getType() != null) {
+ intent.setDataAndType(oldIntent.getData(), oldIntent.getType());
+ }
+ Set cats = oldIntent.getCategories();
+ if (cats != null) {
+ Iterator it = cats.iterator();
+ while (it.hasNext()) {
+ intent.addCategory((String)it.next());
}
- } catch (RuntimeException ex) {
- System.err.println("Error creating from URI: " + ex.toString());
- showUsage();
- return null;
}
- } else if (!hasIntentInfo) {
- System.err.println("Error: No intent supplied");
- showUsage();
- return null;
+ hasIntentInfo = true;
}
+ if (!hasIntentInfo) throw new IllegalArgumentException("No intent supplied");
return intent;
}
- private void runStart() {
+ private void runStart() throws Exception {
Intent intent = makeIntent();
-
- if (intent != null) {
- System.out.println("Starting: " + intent);
- try {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- // XXX should do something to determine the MIME type.
- int res = mAm.startActivity(null, intent, intent.getType(),
- null, 0, null, null, 0, false, mDebugOption);
- switch (res) {
- case IActivityManager.START_SUCCESS:
- break;
- case IActivityManager.START_SWITCHES_CANCELED:
- System.err.println(
- "Warning: Activity not started because the "
- + " current activity is being kept for the user.");
- break;
- case IActivityManager.START_DELIVERED_TO_TOP:
- System.err.println(
- "Warning: Activity not started, intent has "
- + "been delivered to currently running "
- + "top-most instance.");
- break;
- case IActivityManager.START_RETURN_INTENT_TO_CALLER:
- System.err.println(
- "Warning: Activity not started because intent "
- + "should be handled by the caller");
- break;
- case IActivityManager.START_TASK_TO_FRONT:
- System.err.println(
- "Warning: Activity not started, its current "
- + "task has been brought to the front");
- break;
- case IActivityManager.START_INTENT_NOT_RESOLVED:
- System.err.println(
- "Error: Activity not started, unable to "
- + "resolve " + intent.toString());
- break;
- case IActivityManager.START_CLASS_NOT_FOUND:
- System.err.println("Error type 3");
- System.err.println("Error: Activity class " +
- intent.getComponent().toShortString()
- + " does not exist.");
- break;
- case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
- System.err.println(
- "Error: Activity not started, you requested to "
- + "both forward and receive its result");
- break;
- case IActivityManager.START_PERMISSION_DENIED:
- System.err.println(
- "Error: Activity not started, you do not "
- + "have permission to access it.");
- break;
- default:
- System.err.println(
- "Error: Activity not started, unknown error "
- + "code " + res);
- break;
- }
- } catch (RemoteException e) {
- System.err.println("Error type 1");
+ System.out.println("Starting: " + intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ // XXX should do something to determine the MIME type.
+ int res = mAm.startActivity(null, intent, intent.getType(),
+ null, 0, null, null, 0, false, mDebugOption);
+ switch (res) {
+ case IActivityManager.START_SUCCESS:
+ break;
+ case IActivityManager.START_SWITCHES_CANCELED:
+ System.err.println(
+ "Warning: Activity not started because the "
+ + " current activity is being kept for the user.");
+ break;
+ case IActivityManager.START_DELIVERED_TO_TOP:
+ System.err.println(
+ "Warning: Activity not started, intent has "
+ + "been delivered to currently running "
+ + "top-most instance.");
+ break;
+ case IActivityManager.START_RETURN_INTENT_TO_CALLER:
+ System.err.println(
+ "Warning: Activity not started because intent "
+ + "should be handled by the caller");
+ break;
+ case IActivityManager.START_TASK_TO_FRONT:
+ System.err.println(
+ "Warning: Activity not started, its current "
+ + "task has been brought to the front");
+ break;
+ case IActivityManager.START_INTENT_NOT_RESOLVED:
System.err.println(
"Error: Activity not started, unable to "
- + "call on to activity manager service");
- }
+ + "resolve " + intent.toString());
+ break;
+ case IActivityManager.START_CLASS_NOT_FOUND:
+ System.err.println(NO_CLASS_ERROR_CODE);
+ System.err.println("Error: Activity class " +
+ intent.getComponent().toShortString()
+ + " does not exist.");
+ break;
+ case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+ System.err.println(
+ "Error: Activity not started, you requested to "
+ + "both forward and receive its result");
+ break;
+ case IActivityManager.START_PERMISSION_DENIED:
+ System.err.println(
+ "Error: Activity not started, you do not "
+ + "have permission to access it.");
+ break;
+ default:
+ System.err.println(
+ "Error: Activity not started, unknown error code " + res);
+ break;
}
}
- private void sendBroadcast() {
+ private void sendBroadcast() throws Exception {
Intent intent = makeIntent();
-
- if (intent != null) {
- System.out.println("Broadcasting: " + intent);
- try {
- mAm.broadcastIntent(null, intent, null, null, 0, null, null,
- null, true, false);
- } catch (RemoteException e) {
- }
- }
+ IntentReceiver receiver = new IntentReceiver();
+ System.out.println("Broadcasting: " + intent);
+ mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, null, true, false);
+ receiver.waitForFinish();
}
- private void runInstrument() {
+ private void runInstrument() throws Exception {
String profileFile = null;
boolean wait = false;
boolean rawMode = false;
@@ -283,46 +255,30 @@ public class Am {
String argKey = null, argValue = null;
IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
- try {
- String opt;
- while ((opt=nextOption()) != null) {
- if (opt.equals("-p")) {
- profileFile = nextOptionData();
- } else if (opt.equals("-w")) {
- wait = true;
- } else if (opt.equals("-r")) {
- rawMode = true;
- } else if (opt.equals("-e")) {
- argKey = nextOptionData();
- argValue = nextOptionData();
- args.putString(argKey, argValue);
- } else if (opt.equals("--no_window_animation")) {
- no_window_animation = true;
- } else {
- System.err.println("Error: Unknown option: " + opt);
- showUsage();
- return;
- }
+ String opt;
+ while ((opt=nextOption()) != null) {
+ if (opt.equals("-p")) {
+ profileFile = nextArgRequired();
+ } else if (opt.equals("-w")) {
+ wait = true;
+ } else if (opt.equals("-r")) {
+ rawMode = true;
+ } else if (opt.equals("-e")) {
+ argKey = nextArgRequired();
+ argValue = nextArgRequired();
+ args.putString(argKey, argValue);
+ } else if (opt.equals("--no_window_animation")) {
+ no_window_animation = true;
+ } else {
+ System.err.println("Error: Unknown option: " + opt);
+ showUsage();
+ return;
}
- } catch (RuntimeException ex) {
- System.err.println("Error: " + ex.toString());
- showUsage();
- return;
}
- String cnArg = nextArg();
- if (cnArg == null) {
- System.err.println("Error: No instrumentation component supplied");
- showUsage();
- return;
- }
-
+ String cnArg = nextArgRequired();
ComponentName cn = ComponentName.unflattenFromString(cnArg);
- if (cn == null) {
- System.err.println("Error: Bad component name: " + cnArg);
- showUsage();
- return;
- }
+ if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
InstrumentationWatcher watcher = null;
if (wait) {
@@ -331,22 +287,13 @@ public class Am {
}
float[] oldAnims = null;
if (no_window_animation) {
- try {
- oldAnims = wm.getAnimationScales();
- wm.setAnimationScale(0, 0.0f);
- wm.setAnimationScale(1, 0.0f);
- } catch (RemoteException e) {
- }
+ oldAnims = wm.getAnimationScales();
+ wm.setAnimationScale(0, 0.0f);
+ wm.setAnimationScale(1, 0.0f);
}
- try {
- if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) {
- System.out.println("INSTRUMENTATION_FAILED: " +
- cn.flattenToString());
- showUsage();
- return;
- }
- } catch (RemoteException e) {
+ if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher)) {
+ throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
}
if (watcher != null) {
@@ -356,9 +303,57 @@ public class Am {
}
if (oldAnims != null) {
+ wm.setAnimationScales(oldAnims);
+ }
+ }
+
+ private void runProfile() throws Exception {
+ String profileFile = null;
+ boolean start = false;
+ String process = nextArgRequired();
+ ParcelFileDescriptor fd = null;
+
+ String cmd = nextArgRequired();
+ if ("start".equals(cmd)) {
+ start = true;
+ profileFile = nextArgRequired();
+ try {
+ fd = ParcelFileDescriptor.open(
+ new File(profileFile),
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE |
+ ParcelFileDescriptor.MODE_READ_WRITE);
+ } catch (FileNotFoundException e) {
+ System.err.println("Error: Unable to open file: " + profileFile);
+ return;
+ }
+ } else if (!"stop".equals(cmd)) {
+ throw new IllegalArgumentException("Profile command " + cmd + " not valid");
+ }
+
+ if (!mAm.profileControl(process, start, profileFile, fd)) {
+ throw new AndroidException("PROFILE FAILED on process " + process);
+ }
+ }
+
+ private class IntentReceiver extends IIntentReceiver.Stub {
+ private boolean mFinished = false;
+
+ public synchronized void performReceive(
+ Intent intent, int rc, String data, Bundle ext, boolean ord) {
+ String line = "Broadcast completed: result=" + rc;
+ if (data != null) line = line + ", data=\"" + data + "\"";
+ if (ext != null) line = line + ", extras: " + ext;
+ System.out.println(line);
+ mFinished = true;
+ notifyAll();
+ }
+
+ public synchronized void waitForFinish() {
try {
- wm.setAnimationScales(oldAnims);
- } catch (RemoteException e) {
+ while (!mFinished) wait();
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
}
}
}
@@ -366,7 +361,7 @@ public class Am {
private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
private boolean mFinished = false;
private boolean mRawMode = false;
-
+
/**
* Set or reset "raw mode". In "raw mode", all bundles are dumped. In "pretty mode",
* if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
@@ -375,7 +370,7 @@ public class Am {
public void setRawOutput(boolean rawMode) {
mRawMode = rawMode;
}
-
+
public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
synchronized (this) {
// pretty printer mode?
@@ -431,6 +426,7 @@ public class Am {
}
wait(1000);
} catch (InterruptedException e) {
+ throw new IllegalStateException(e);
}
}
}
@@ -438,62 +434,11 @@ public class Am {
}
}
- private void runProfile() {
- String profileFile = null;
- boolean start = false;
-
- String process = nextArg();
- if (process == null) {
- System.err.println("Error: No profile process supplied");
- showUsage();
- return;
- }
-
- ParcelFileDescriptor fd = null;
-
- String cmd = nextArg();
- if ("start".equals(cmd)) {
- start = true;
- profileFile = nextArg();
- if (profileFile == null) {
- System.err.println("Error: No profile file path supplied");
- showUsage();
- return;
- }
- try {
- fd = ParcelFileDescriptor.open(
- new File(profileFile),
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE |
- ParcelFileDescriptor.MODE_READ_WRITE);
- } catch (FileNotFoundException e) {
- System.err.println("Error: Unable to open file: " + profileFile);
- return;
- }
- } else if (!"stop".equals(cmd)) {
- System.err.println("Error: Profile command " + cmd + " not valid");
- showUsage();
- return;
- }
-
- try {
- if (!mAm.profileControl(process, start, profileFile, fd)) {
- System.err.println("PROFILE FAILED on process " + process);
- return;
- }
- } catch (IllegalArgumentException e) {
- System.out.println("PROFILE FAILED: " + e.getMessage());
- return;
- } catch (IllegalStateException e) {
- System.out.println("PROFILE FAILED: " + e.getMessage());
- return;
- } catch (RemoteException e) {
- System.out.println("PROFILE FAILED: activity manager gone");
- return;
- }
- }
-
private String nextOption() {
+ if (mCurArgData != null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+ }
if (mNextArg >= mArgs.length) {
return null;
}
@@ -518,41 +463,52 @@ public class Am {
return arg;
}
- private String nextOptionData() {
+ private String nextArg() {
if (mCurArgData != null) {
- return mCurArgData;
- }
- if (mNextArg >= mArgs.length) {
+ String arg = mCurArgData;
+ mCurArgData = null;
+ return arg;
+ } else if (mNextArg < mArgs.length) {
+ return mArgs[mNextArg++];
+ } else {
return null;
}
- String data = mArgs[mNextArg];
- mNextArg++;
- return data;
}
- private String nextArg() {
- if (mNextArg >= mArgs.length) {
- return null;
+ private String nextArgRequired() {
+ String arg = nextArg();
+ if (arg == null) {
+ String prev = mArgs[mNextArg - 1];
+ throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
}
- String arg = mArgs[mNextArg];
- mNextArg++;
return arg;
}
- private void showUsage() {
- System.err.println("usage: am [start|broadcast|instrument|profile]");
- System.err.println(" am start [-D] INTENT");
- System.err.println(" am broadcast INTENT");
- System.err.println(" am instrument [-r] [-e <ARG_NAME> <ARG_VALUE>] [-p <PROF_FILE>]");
- System.err.println(" [-w] <COMPONENT> ");
- System.err.println(" am profile <PROCESS> [start <PROF_FILE>|stop]");
- System.err.println("");
- System.err.println(" INTENT is described with:");
- System.err.println(" [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]");
- System.err.println(" [-c <CATEGORY> [-c <CATEGORY>] ...]");
- System.err.println(" [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]");
- System.err.println(" [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]");
- System.err.println(" [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]");
- System.err.println(" [-n <COMPONENT>] [-f <FLAGS>] [<URI>]");
+ private static void showUsage() {
+ System.err.println(
+ "usage: am [subcommand] [options]\n" +
+ "\n" +
+ " start an Activity: am start [-D] <INTENT>\n" +
+ " -D: enable debugging\n" +
+ "\n" +
+ " send a broadcast Intent: am broadcast <INTENT>\n" +
+ "\n" +
+ " start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
+ " -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT)\n" +
+ " -e <NAME> <VALUE>: set argument <NAME> to <VALUE>\n" +
+ " -p <FILE>: write profiling data to <FILE>\n" +
+ " -w: wait for instrumentation to finish before returning\n" +
+ "\n" +
+ " start profiling: am profile <PROCESS> start <FILE>\n" +
+ " stop profiling: am profile <PROCESS> stop\n" +
+ "\n" +
+ " <INTENT> specifications include these flags:\n" +
+ " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" +
+ " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" +
+ " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" +
+ " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
+ " [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
+ " [-n <COMPONENT>] [-f <FLAGS>] [<URI>]\n"
+ );
}
}
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index d825d5a..7decf9a 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -7,8 +7,8 @@
#define LOG_TAG "appproc"
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <cutils/process_name.h>
#include <cutils/memory.h>
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index ee3ec1a..8c15d0b 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -268,7 +268,7 @@ public final class Bmgr {
private void printRestoreSets(RestoreSet[] sets) {
for (RestoreSet s : sets) {
- System.out.println(" " + s.token + " : " + s.name);
+ System.out.println(" " + Long.toHexString(s.token) + " : " + s.name);
}
}
@@ -294,7 +294,7 @@ public final class Bmgr {
private void doRestore() {
long token;
try {
- token = Long.parseLong(nextArg());
+ token = Long.parseLong(nextArg(), 16);
} catch (NumberFormatException e) {
showUsage();
return;
@@ -311,12 +311,13 @@ public final class Bmgr {
return;
}
RestoreSet[] sets = mRestore.getAvailableRestoreSets();
- for (RestoreSet s : sets) {
- if (s.token == token) {
- System.out.println("Scheduling restore: " + s.name);
- mRestore.performRestore(token, observer);
- didRestore = true;
- break;
+ if (sets != null) {
+ for (RestoreSet s : sets) {
+ if (s.token == token) {
+ System.out.println("Scheduling restore: " + s.name);
+ didRestore = (mRestore.performRestore(token, observer) == 0);
+ break;
+ }
}
}
if (!didRestore) {
@@ -327,21 +328,27 @@ public final class Bmgr {
printRestoreSets(sets);
}
}
+
+ // if we kicked off a restore successfully, we have to wait for it
+ // to complete before we can shut down the restore session safely
+ if (didRestore) {
+ synchronized (observer) {
+ while (!observer.done) {
+ try {
+ observer.wait();
+ } catch (InterruptedException ex) {
+ }
+ }
+ }
+ }
+
+ // once the restore has finished, close down the session and we're done
mRestore.endRestoreSession();
} catch (RemoteException e) {
System.err.println(e.toString());
System.err.println(BMGR_NOT_RUNNING_ERR);
}
- // now wait for it to be done
- synchronized (observer) {
- while (!observer.done) {
- try {
- observer.wait();
- } catch (InterruptedException ex) {
- }
- }
- }
System.out.println("done");
}
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index 9c94c2e..3449de1 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -12,12 +12,13 @@ ifeq ($(TARGET_SIMULATOR),true)
endif
endif
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
libui \
- libcorecg \
- libsgl \
+ libskia \
libEGL \
libGLESv1_CM
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 2fb3f79..99e513c 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#define LOG_TAG "BootAnimation"
-
#include <stdint.h>
#include <sys/types.h>
#include <math.h>
#include <fcntl.h>
#include <utils/misc.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
#include <utils/threads.h>
#include <utils/Atomic.h>
#include <utils/Errors.h>
@@ -35,7 +33,8 @@
#include <ui/DisplayInfo.h>
#include <ui/ISurfaceComposer.h>
#include <ui/ISurfaceFlingerClient.h>
-#include <ui/EGLNativeWindowSurface.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
#include <core/SkBitmap.h>
#include <images/SkImageDecoder.h>
@@ -130,15 +129,19 @@ status_t BootAnimation::readyToRun() {
return -1;
// create the native surface
- sp<Surface> s = session()->createSurface(getpid(), 0, dinfo.w, dinfo.h,
- PIXEL_FORMAT_RGB_565, ISurfaceComposer::eGPU);
+ sp<SurfaceControl> control = session()->createSurface(
+ getpid(), 0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
session()->openTransaction();
- s->setLayer(0x40000000);
+ control->setLayer(0x40000000);
session()->closeTransaction();
+ sp<Surface> s = control->getSurface();
+
// initialize opengl and egl
- const EGLint attribs[] = { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE, 0, EGL_NONE };
+ const EGLint attribs[] = {
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
EGLint w, h, dummy;
EGLint numConfigs;
EGLConfig config;
@@ -148,21 +151,21 @@ status_t BootAnimation::readyToRun() {
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, 0, 0);
- eglChooseConfig(display, attribs, &config, 1, &numConfigs);
-
- mNativeWindowSurface = new EGLNativeWindowSurface(s);
- surface = eglCreateWindowSurface(display, config,
- mNativeWindowSurface.get(), NULL);
-
+ EGLUtils::selectConfigForNativeWindow(display, attribs, s.get(), &config);
+ surface = eglCreateWindowSurface(display, config, s.get(), NULL);
context = eglCreateContext(display, config, NULL, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);
- eglMakeCurrent(display, surface, surface, context);
+
+ if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
+ return NO_INIT;
+
mDisplay = display;
mContext = context;
mSurface = surface;
mWidth = w;
mHeight = h;
+ mFlingerSurfaceControl = control;
mFlingerSurface = s;
// initialize GL
@@ -178,8 +181,8 @@ bool BootAnimation::threadLoop() {
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(mDisplay, mContext);
eglDestroySurface(mDisplay, mSurface);
- mNativeWindowSurface.clear();
mFlingerSurface.clear();
+ mFlingerSurfaceControl.clear();
eglTerminate(mDisplay);
IPCThreadState::self()->stopProcess();
return r;
@@ -200,8 +203,7 @@ bool BootAnimation::android() {
const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);
// draw and update only what we need
- mNativeWindowSurface->setSwapRectangle(updateRect.left,
- updateRect.top, updateRect.width(), updateRect.height());
+ mFlingerSurface->setSwapRectangle(updateRect);
glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),
updateRect.height());
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index 42e9eed..796077d 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -34,7 +34,6 @@ class SkBitmap;
namespace android {
class AssetManager;
-class EGLNativeWindowSurface;
// ---------------------------------------------------------------------------
@@ -68,8 +67,8 @@ private:
EGLDisplay mDisplay;
EGLDisplay mContext;
EGLDisplay mSurface;
+ sp<SurfaceControl> mFlingerSurfaceControl;
sp<Surface> mFlingerSurface;
- sp<EGLNativeWindowSurface> mNativeWindowSurface;
};
// ---------------------------------------------------------------------------
diff --git a/cmds/bootanimation/bootanimation_main.cpp b/cmds/bootanimation/bootanimation_main.cpp
index a8359c4..3c82fe5 100644
--- a/cmds/bootanimation/bootanimation_main.cpp
+++ b/cmds/bootanimation/bootanimation_main.cpp
@@ -18,9 +18,10 @@
#include <cutils/properties.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
#include <utils/Log.h>
#include <utils/threads.h>
diff --git a/cmds/bugreport/Android.mk b/cmds/bugreport/Android.mk
new file mode 100644
index 0000000..631c219
--- /dev/null
+++ b/cmds/bugreport/Android.mk
@@ -0,0 +1,14 @@
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= bugreport.c
+
+LOCAL_MODULE:= bugreport
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/cmds/bugreport/bugreport.c b/cmds/bugreport/bugreport.c
new file mode 100644
index 0000000..4a0b511
--- /dev/null
+++ b/cmds/bugreport/bugreport.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+int main(int argc, char *argv[]) {
+ char buffer[65536];
+ int i, s;
+
+ /* start the dumpstate service */
+ property_set("ctl.start", "dumpstate");
+
+ /* socket will not be available until service starts */
+ for (i = 0; i < 10; i++) {
+ s = socket_local_client("dumpstate",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (s >= 0)
+ break;
+ /* try again in 1 second */
+ sleep(1);
+ }
+
+ if (s < 0) {
+ fprintf(stderr, "Failed to connect to dumpstate service\n");
+ exit(1);
+ }
+
+ while (1) {
+ int length = read(s, buffer, sizeof(buffer));
+ if (length <= 0)
+ break;
+ fwrite(buffer, 1, length, stdout);
+ }
+
+ close(s);
+ return 0;
+}
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index f61d7ec..f8b37a8 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -11,7 +11,7 @@ LOCAL_SHARED_LIBRARIES := libcutils
include $(BUILD_EXECUTABLE)
-COMMANDS = dumpcrash bugreport
+COMMANDS = dumpcrash
SYMLINKS := $(addprefix $(TARGET_OUT_EXECUTABLES)/,$(COMMANDS))
$(SYMLINKS): DUMPSTATE_BINARY := dumpstate
$(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c
index cc951c1..c211b47 100644
--- a/cmds/dumpstate/dumpstate.c
+++ b/cmds/dumpstate/dumpstate.c
@@ -20,10 +20,12 @@
#include <unistd.h>
#include <sys/stat.h>
#include <limits.h>
+#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>
+#include <cutils/sockets.h>
#include "private/android_filesystem_config.h"
#include "dumpstate.h"
@@ -34,6 +36,8 @@ static int end_pattern[] = { 75, 50, 75, 50, 75, 0 };
static struct tm now;
+static void dump_kernel_log(const char *path, const char *title) ;
+
/* dumps the current system state to stdout */
static void dumpstate(int full) {
if (full) {
@@ -101,8 +105,12 @@ static void dumpstate(int full) {
DUMP("/data/system/packages.xml");
PRINT("------ PACKAGE UID ERRORS ------");
DUMP("/data/system/uiderrors.txt");
- PRINT("------ LAST KERNEL LOG ------");
- DUMP("/proc/last_kmsg");
+
+ dump_kernel_log("/data/dontpanic/last_kmsg", "RAMCONSOLE");
+ dump_kernel_log("/data/dontpanic/apanic_console",
+ "PANIC CONSOLE");
+ dump_kernel_log("/data/dontpanic/apanic_threads",
+ "PANIC THREADS");
}
PRINT("========================================================");
PRINT("== build.prop");
@@ -157,11 +165,11 @@ static int check_command_name(const char* name, const char* test) {
int main(int argc, char *argv[]) {
int dumpcrash = check_command_name(argv[0], "dumpcrash");
- int bugreport = check_command_name(argv[0], "bugreport");
int add_date = 0;
char* outfile = 0;
int vibrate = 0;
int compress = 0;
+ int socket = 0;
int c, fd, vibrate_fd, fds[2];
char path[PATH_MAX];
pid_t pid;
@@ -173,31 +181,32 @@ int main(int argc, char *argv[]) {
get_time(&now);
- if (bugreport) {
- do {
- c = getopt(argc, argv, "do:vz");
- if (c == EOF)
+ do {
+ c = getopt(argc, argv, "do:svz");
+ if (c == EOF)
+ break;
+ switch (c) {
+ case 'd':
+ add_date = 1;
break;
- switch (c) {
- case 'd':
- add_date = 1;
- break;
- case 'o':
- outfile = optarg;
- break;
- case 'v':
- vibrate = 1;
- break;
- case 'z':
- compress = 1;
- break;
- case '?':
- fprintf(stderr, "%s: invalid option -%c\n",
- argv[0], optopt);
- exit(1);
- }
- } while (1);
- }
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'v':
+ vibrate = 1;
+ break;
+ case 'z':
+ compress = 1;
+ break;
+ case 's':
+ socket = 1;
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ argv[0], optopt);
+ exit(1);
+ }
+ } while (1);
/* open vibrator before switching user */
if (vibrate) {
@@ -207,14 +216,40 @@ int main(int argc, char *argv[]) {
} else
vibrate_fd = -1;
+#if 0
/* switch to non-root user and group */
setgroups(sizeof(groups)/sizeof(groups[0]), groups);
setuid(AID_SHELL);
+#endif
/* make it safe to use both printf and STDOUT_FILENO */
setvbuf(stdout, 0, _IONBF, 0);
- if (outfile) {
+ if (socket) {
+ struct sockaddr addr;
+ socklen_t alen;
+
+ int s = android_get_control_socket("dumpstate");
+ if (s < 0) {
+ fprintf(stderr, "could not open dumpstate socket\n");
+ exit(1);
+ }
+ if (listen(s, 4) < 0) {
+ fprintf(stderr, "could not listen on dumpstate socket\n");
+ exit(1);
+ }
+
+ alen = sizeof(addr);
+ fd = accept(s, &addr, &alen);
+ if (fd < 0) {
+ fprintf(stderr, "could not accept dumpstate socket\n");
+ exit(1);
+ }
+
+ /* redirect stdout to the socket */
+ dup2(fd, STDOUT_FILENO);
+ close(fd);
+ } else if (outfile) {
if (strlen(outfile) > sizeof(path) - 100)
exit(1);
@@ -295,3 +330,21 @@ int main(int argc, char *argv[]) {
return 0;
}
+static void dump_kernel_log(const char *path, const char *title)
+
+{
+ printf("------ KERNEL %s LOG ------\n", title);
+ if (access(path, R_OK) < 0)
+ printf("%s: %s\n", path, strerror(errno));
+ else {
+ struct stat sbuf;
+
+ if (stat(path, &sbuf) < 0)
+ printf("%s: stat failed (%s)\n", path, strerror(errno));
+ else
+ printf("Harvested %s", ctime(&sbuf.st_ctime));
+
+ DUMP(path);
+ }
+}
+
diff --git a/cmds/dumpsys/Android.mk b/cmds/dumpsys/Android.mk
index 0c623cc..42b1b73 100644
--- a/cmds/dumpsys/Android.mk
+++ b/cmds/dumpsys/Android.mk
@@ -5,7 +5,9 @@ LOCAL_SRC_FILES:= \
dumpsys.cpp
LOCAL_SHARED_LIBRARIES := \
- libutils
+ libutils \
+ libbinder
+
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index a62fe55..945a690 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -6,9 +6,9 @@
#define LOG_TAG "dumpsys"
#include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/TextOutput.h>
#include <utils/Vector.h>
diff --git a/cmds/keystore/certtool.h b/cmds/keystore/certtool.h
index aefad66..9b72bf7 100644
--- a/cmds/keystore/certtool.h
+++ b/cmds/keystore/certtool.h
@@ -38,9 +38,9 @@ static inline int get_cert(const char *certname, unsigned char *value, int *size
int count, fd, ret = -1;
LPC_MARSHAL cmd;
char delimiter[] = "_";
- char *namespace, *keyname;
+ char *p = NULL;
char *context = NULL;
- char cname[CERT_NAME_LEN];
+ char *cname = (char*)cmd.data;
if ((certname == NULL) || (value == NULL)) {
LOGE("get_cert: certname or value is null\n");
@@ -61,12 +61,10 @@ static inline int get_cert(const char *certname, unsigned char *value, int *size
}
cmd.opcode = GET;
- if (((namespace = strtok_r(cname, delimiter, &context)) == NULL) ||
- ((keyname = strtok_r(NULL, delimiter, &context)) == NULL)) {
- goto err;
- }
- if ((cmd.len = snprintf((char*)cmd.data, BUFFER_MAX, "%s %s", namespace, keyname))
- > (2 * MAX_KEY_NAME_LENGTH + 1)) goto err;
+ p = strstr(cname, delimiter);
+ cmd.len = strlen(certname) + 1;
+ if (p == NULL) goto err;
+ *p = 0; // replace the delimiter with \0 .
if (write_marshal(fd, &cmd)) {
LOGE("Incorrect command or command line is too long.\n");
diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c
index 9a1f845..69e0380 100644
--- a/cmds/keystore/keymgmt.c
+++ b/cmds/keystore/keymgmt.c
@@ -192,21 +192,15 @@ static int create_master_key(char *upasswd)
return ret;
}
-static int change_passwd(char *data)
+int change_passwd(char *old_pass, char *new_pass)
{
unsigned char master_key[USER_KEY_LEN];
- char *old_pass, *new_pass = NULL, *p, *delimiter=" ";
- int ret, count = 0;
- char *context = NULL;
-
- old_pass = p = strtok_r(data, delimiter, &context);
- while (p != NULL) {
- count++;
- new_pass = p;
- p = strtok_r(NULL, delimiter, &context);
- }
- if (count != 2) return -1;
- if (strlen(new_pass) < MIN_PASSWD_LENGTH) return -1;
+ int ret;
+
+ if (state == UNINITIALIZED) return -1;
+ if ((strlen(old_pass) < MIN_PASSWD_LENGTH) ||
+ (strlen(new_pass) < MIN_PASSWD_LENGTH)) return -1;
+
if ((ret = get_master_key(old_pass, master_key)) == 0) {
ret = store_master_key(new_pass, master_key);
retry_count = 0;
@@ -336,14 +330,12 @@ int list_keys(const char *namespace, char reply[BUFFER_MAX])
return 0;
}
-int passwd(char *data)
+int new_passwd(char *password)
{
- if (state == UNINITIALIZED) {
- if (strchr(data, ' ')) return -1;
- if (strlen(data) < MIN_PASSWD_LENGTH) return -1;
- return create_master_key(data);
- }
- return change_passwd(data);
+ int passwdlen = strlen(password);
+
+ if ((state != UNINITIALIZED) || (passwdlen < MIN_PASSWD_LENGTH)) return -1;
+ return create_master_key(password);
}
int lock()
diff --git a/cmds/keystore/keymgmt.h b/cmds/keystore/keymgmt.h
index 0e928db..116d7a3 100644
--- a/cmds/keystore/keymgmt.h
+++ b/cmds/keystore/keymgmt.h
@@ -72,7 +72,8 @@ int get_key(const char *namespace, const char *keyname,
unsigned char *data, int *size);
int remove_key(const char *namespace, const char *keyname);
int list_keys(const char *namespace, char reply[BUFFER_MAX]);
-int passwd(char *data);
+int new_passwd(char *password);
+int change_passwd(char *old_pass, char *new_pass);
int lock();
int unlock(char *passwd);
KEYSTORE_STATE get_state();
diff --git a/cmds/keystore/netkeystore.c b/cmds/keystore/netkeystore.c
index 637e0d8..82a92c3 100644
--- a/cmds/keystore/netkeystore.c
+++ b/cmds/keystore/netkeystore.c
@@ -85,28 +85,44 @@ static int check_reset_perm(int uid)
return -1;
}
-static int parse_keyname(char *name, uint32_t len,
- char *namespace, char *keyname)
+/**
+ * The function parse_strings() only handle two or three tokens just for
+ * keystore's need.
+ */
+static int parse_strings(char *data, int data_len, int ntokens, ...)
{
int count = 0;
- char *c = namespace, *p = namespace, *t = name;
-
- if (!name || !namespace || !keyname) return -1;
- while (t < name + len && (*t != 0)) {
- if (*t == ' ') {
- if (c == keyname) return -1;
- *p = count = 0;
- c = p = keyname;
- t++;
- } else {
- if (!isalnum(*t)) return -1;
- *p++ = *t++;
- // also check if the keyname/namespace is too long.
- if (count++ == MAX_KEY_NAME_LENGTH) return -1;
+ va_list args;
+ char *p = data, **q;
+
+ va_start(args, ntokens);
+ q = va_arg(args, char**);
+ *q = p;
+ while (p < (data + data_len)) {
+ if (*(p++) == 0) {
+ if (++count == ntokens) break;
+ if ((q = va_arg(args, char**)) == NULL) break;
+ *q = p;
}
}
- *p = 0;
- return 0;
+ va_end(args);
+ // the first two strings should be null-terminated and the third could
+ // ignore the delimiter.
+ if (count >= 2) {
+ if ((ntokens == 3) || ((ntokens == 2) && (p == (data + data_len)))) {
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int is_alnum_string(char *s)
+{
+ while (*s != 0) {
+ if (!isalnum(*s++)) return 0;
+ }
+ LOGE("The string %s is not an alphanumeric string\n", s);
+ return 1;
}
// args of passwd():
@@ -114,7 +130,17 @@ static int parse_keyname(char *name, uint32_t len,
// oldPassword newPassword - for changing the password
static void do_passwd(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
{
- reply->retcode = passwd((char*)cmd->data);
+ char *p1 = NULL, *p2 = NULL;
+
+ if (strlen((char*)cmd->data) == (cmd->len - 1)) {
+ reply->retcode = new_passwd((char*)cmd->data);
+ } else {
+ if (parse_strings((char *)cmd->data, cmd->len, 2, &p1, &p2) != 0) {
+ reply->retcode = -1;
+ } else {
+ reply->retcode = change_passwd(p1, p2);
+ }
+ }
}
// args of lock():
@@ -150,8 +176,7 @@ static void do_listkeys(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
// namespace keyname
static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
{
- char namespace[MAX_KEY_NAME_LENGTH];
- char keyname[MAX_KEY_NAME_LENGTH];
+ char *namespace = NULL, *keyname = NULL;
if (check_get_perm(cr.uid)) {
LOGE("uid %d doesn't have the permission to get key value\n", cr.uid);
@@ -159,7 +184,8 @@ static void do_get_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
return;
}
- if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+ if (parse_strings((char*)cmd->data, cmd->len, 2, &namespace, &keyname) ||
+ !is_alnum_string(namespace) || !is_alnum_string(keyname)) {
reply->retcode = -1;
} else {
reply->retcode = get_key(namespace, keyname, reply->data,
@@ -182,31 +208,26 @@ static int get_value_index(LPC_MARSHAL *cmd)
// namespace keyname keyvalue
static void do_put_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
{
- char namespace[MAX_KEY_NAME_LENGTH];
- char keyname[MAX_KEY_NAME_LENGTH];
+ char *namespace = NULL, *keyname = NULL;
+ char *value = NULL;
- int p = get_value_index(cmd);
- if (p == -1) {
+ if (parse_strings((char*)cmd->data, cmd->len, 3, &namespace, &keyname, &value) ||
+ !is_alnum_string(namespace) || !is_alnum_string(keyname)) {
reply->retcode = -1;
- } else {
- unsigned char *value;
- if (parse_keyname((char*)cmd->data, p - 1, namespace, keyname)) {
- reply->retcode = -1;
- return;
- }
- value = &cmd->data[p];
- int len = cmd->len - p;
- reply->retcode = put_key(namespace, keyname, value, len);
+ return;
}
+ int len = cmd->len - (value - namespace);
+ reply->retcode = put_key(namespace, keyname, (unsigned char *)value, len);
}
// args of remove_key():
// namespace keyname
static void do_remove_key(LPC_MARSHAL *cmd, LPC_MARSHAL *reply)
{
- char namespace[MAX_KEY_NAME_LENGTH];
- char keyname[MAX_KEY_NAME_LENGTH];
- if (parse_keyname((char*)cmd->data, cmd->len, namespace, keyname)) {
+ char *namespace = NULL, *keyname = NULL;
+
+ if (parse_strings((char*)cmd->data, cmd->len, 2, &namespace, &keyname) ||
+ !is_alnum_string(namespace) || !is_alnum_string(keyname)) {
reply->retcode = -1;
return;
}
@@ -242,6 +263,7 @@ static int set_read_timeout(int socket)
{
struct timeval tv;
tv.tv_sec = READ_TIMEOUT;
+ tv.tv_usec = 0;
if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv))
{
LOGE("setsockopt failed");
@@ -259,8 +281,6 @@ static int append_input_from_file(const char *filename, LPC_MARSHAL *cmd)
fprintf(stderr, "Can not open file %s\n", filename);
return -1;
}
- cmd->data[cmd->len] = ' ';
- cmd->len++;
len = read(fd, cmd->data + cmd->len, BUFFER_MAX - cmd->len);
if (len < 0 || (len == (int)(BUFFER_MAX - cmd->len))) {
ret = -1;
@@ -277,14 +297,15 @@ static int flatten_str_args(int argc, const char **argv, LPC_MARSHAL *cmd)
char *buf = (char*)cmd->data;
buf[0] = 0;
for (i = 0 ; i < argc ; ++i) {
+ // we also include the \0 character in the input.
if (i == 0) {
- len = strlcpy(buf, argv[i], BUFFER_MAX);
+ len = (strlcpy(buf, argv[i], BUFFER_MAX) + 1);
} else {
- len += snprintf(buf + len, BUFFER_MAX - len, " %s", argv[i]);
+ len += (snprintf(buf + len, BUFFER_MAX - len, "%s", argv[i]) + 1);
}
if (len >= BUFFER_MAX) return -1;
}
- if (len) cmd->len = len;
+ if (len) cmd->len = len ;
return 0;
}
diff --git a/cmds/keystore/netkeystore.h b/cmds/keystore/netkeystore.h
index a87a667..d80ddae 100644
--- a/cmds/keystore/netkeystore.h
+++ b/cmds/keystore/netkeystore.h
@@ -19,6 +19,7 @@
#define __NETKEYSTORE_H__
#include <stdio.h>
+#include <arpa/inet.h>
#include <cutils/sockets.h>
#include <cutils/log.h>
@@ -68,6 +69,8 @@ static inline int read_marshal(int s, LPC_MARSHAL *cmd)
LOGE("failed to read header\n");
return -1;
}
+ cmd->len = ntohl(cmd->len);
+ cmd->opcode = ntohl(cmd->opcode);
if (cmd->len > BUFFER_MAX) {
LOGE("invalid size %d\n", cmd->len);
return -1;
@@ -82,11 +85,14 @@ static inline int read_marshal(int s, LPC_MARSHAL *cmd)
static inline int write_marshal(int s, LPC_MARSHAL *cmd)
{
+ int len = cmd->len;
+ cmd->len = htonl(cmd->len);
+ cmd->opcode = htonl(cmd->opcode);
if (writex(s, cmd, 2 * sizeof(uint32_t))) {
LOGE("failed to write marshal header\n");
return -1;
}
- if (writex(s, cmd->data, cmd->len)) {
+ if (writex(s, cmd->data, len)) {
LOGE("failed to write marshal data\n");
return -1;
}
diff --git a/cmds/keystore/tests/netkeystore_test.c b/cmds/keystore/tests/netkeystore_test.c
index e7e686b..00390e0 100644
--- a/cmds/keystore/tests/netkeystore_test.c
+++ b/cmds/keystore/tests/netkeystore_test.c
@@ -43,7 +43,7 @@ typedef struct {
#define FUNC_BODY(x) int test_##x()
#define TEST_PASSWD "12345678"
-#define TEST_NPASSWD "11111111"
+#define TEST_NPASSWD "hello world"
#define TEST_DIR "/data/local/tmp/keystore"
#define READONLY_DIR "/proc/keystore"
#define TEST_NAMESPACE "test"
@@ -83,7 +83,7 @@ FUNC_BODY(reset_keystore)
FUNC_BODY(get_state)
{
if (get_state() != UNINITIALIZED) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
if (get_state() != UNLOCKED) return -1;
lock();
if (get_state() != LOCKED) return -1;
@@ -96,19 +96,17 @@ FUNC_BODY(passwd)
{
char buf[512];
- if (passwd(" 23432dsfsdf") == 0) return -1;
- if (passwd("dsfsdf") == 0) return -1;
- passwd(TEST_PASSWD);
+ if (new_passwd("2d fsdf") == 0) return -1;
+ if (new_passwd("dsfsdf") == 0) return -1;
+ new_passwd(TEST_PASSWD);
lock();
if (unlock("55555555") == 0) return -1;
if (unlock(TEST_PASSWD) != 0) return -1;
// change the password
- sprintf(buf, "%s %s", "klfdjdsklfjg", "abcdefghi");
- if (passwd(buf) == 0) return -1;
+ if (change_passwd("klfdjdsklfjg", "abcdefghi") == 0) return -1;
- sprintf(buf, "%s %s", TEST_PASSWD, TEST_NPASSWD);
- if (passwd(buf) != 0) return -1;
+ if (change_passwd(TEST_PASSWD, TEST_NPASSWD) != 0) return -1;
lock();
if (unlock(TEST_PASSWD) == 0) return -1;
@@ -120,7 +118,7 @@ FUNC_BODY(passwd)
FUNC_BODY(lock)
{
if (lock() == 0) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
if (lock() != 0) return -1;
if (lock() != 0) return -1;
return EXIT_SUCCESS;
@@ -129,7 +127,7 @@ FUNC_BODY(lock)
FUNC_BODY(unlock)
{
int i = MAX_RETRY_COUNT;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
lock();
while (i > 1) {
if (unlock(TEST_NPASSWD) != --i) return -1;
@@ -145,7 +143,7 @@ FUNC_BODY(put_key)
if (put_key(TEST_NAMESPACE, TEST_KEYNAME, (unsigned char *)TEST_KEYVALUE,
strlen(TEST_KEYVALUE)) == 0) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
if (put_key(TEST_NAMESPACE, TEST_KEYNAME, (unsigned char *)TEST_KEYVALUE,
strlen(TEST_KEYVALUE)) != 0) return -1;
@@ -165,7 +163,7 @@ FUNC_BODY(get_key)
if (get_key(TEST_NAMESPACE, TEST_KEYNAME, data, &size) == 0) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
put_key(TEST_NAMESPACE, TEST_KEYNAME, (unsigned char *)TEST_KEYVALUE,
strlen(TEST_KEYVALUE));
if (get_key(TEST_NAMESPACE, TEST_KEYNAME, data, &size) != 0) return -1;
@@ -178,7 +176,7 @@ FUNC_BODY(remove_key)
{
if (remove_key(TEST_NAMESPACE, TEST_KEYNAME) == 0) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
if (remove_key(TEST_NAMESPACE, TEST_KEYNAME) == 0) return -1;
put_key(TEST_NAMESPACE, TEST_KEYNAME, (unsigned char *)TEST_KEYVALUE,
@@ -199,7 +197,7 @@ FUNC_BODY(list_keys)
if (list_keys(TEST_NAMESPACE, reply) == 0) return -1;
- passwd(TEST_PASSWD);
+ new_passwd(TEST_PASSWD);
if (list_keys(buf, reply) == 0) return -1;
if (list_keys(TEST_NAMESPACE, reply) != 0) return -1;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index fd9e708..7adaf57 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -546,6 +546,9 @@ public final class Pm {
case PackageManager.INSTALL_FAILED_CPU_ABI_INCOMPATIBLE:
s = "INSTALL_FAILED_CPU_ABI_INCOMPATIBLE";
break;
+ case PackageManager.INSTALL_FAILED_MISSING_FEATURE:
+ s = "INSTALL_FAILED_MISSING_FEATURE";
+ break;
case PackageManager.INSTALL_PARSE_FAILED_NOT_APK:
s = "INSTALL_PARSE_FAILED_NOT_APK";
break;
diff --git a/cmds/runtime/Android.mk b/cmds/runtime/Android.mk
index 521eb2b..6a72d10 100644
--- a/cmds/runtime/Android.mk
+++ b/cmds/runtime/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libutils \
+ libbinder \
libandroid_runtime \
libcutils \
libui \
diff --git a/cmds/runtime/ServiceManager.cpp b/cmds/runtime/ServiceManager.cpp
index 758a95c..b2bef07 100644
--- a/cmds/runtime/ServiceManager.cpp
+++ b/cmds/runtime/ServiceManager.cpp
@@ -9,9 +9,9 @@
#include <utils/Debug.h>
#include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <utils/String8.h>
-#include <utils/ProcessState.h>
+#include <binder/ProcessState.h>
#include <private/utils/Static.h>
diff --git a/cmds/runtime/ServiceManager.h b/cmds/runtime/ServiceManager.h
index d09cec8..090ca6d 100644
--- a/cmds/runtime/ServiceManager.h
+++ b/cmds/runtime/ServiceManager.h
@@ -4,7 +4,7 @@
#ifndef ANDROID_SERVICE_MANAGER_H
#define ANDROID_SERVICE_MANAGER_H
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <utils/KeyedVector.h>
#include <utils/threads.h>
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 476f38a..21e0e4d 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -7,9 +7,11 @@
#include "ServiceManager.h"
#include "SignalHandler.h"
-#include <utils.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
+#include <utils/threads.h>
+#include <utils/Errors.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
#include <utils/Log.h>
#include <cutils/zygote.h>
diff --git a/cmds/service/Android.mk b/cmds/service/Android.mk
index 8c5005c..275bbb2 100644
--- a/cmds/service/Android.mk
+++ b/cmds/service/Android.mk
@@ -4,8 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
service.cpp
-LOCAL_SHARED_LIBRARIES := \
- libutils
+LOCAL_SHARED_LIBRARIES := libutils libbinder
ifeq ($(TARGET_OS),linux)
LOCAL_CFLAGS += -DXP_UNIX
diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp
index 859a9bf..32db83b 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -3,9 +3,9 @@
*
*/
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/TextOutput.h>
#include <getopt.h>
diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c
index e4aa8b5..f3a4713 100644
--- a/cmds/servicemanager/service_manager.c
+++ b/cmds/servicemanager/service_manager.c
@@ -30,6 +30,7 @@ static struct {
{ AID_MEDIA, "media.audio_flinger" },
{ AID_MEDIA, "media.player" },
{ AID_MEDIA, "media.camera" },
+ { AID_MEDIA, "media.audio_policy" },
{ AID_RADIO, "radio.phone" },
{ AID_RADIO, "radio.sms" },
{ AID_RADIO, "radio.phonesubinfo" },
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
new file mode 100644
index 0000000..51a0649
--- /dev/null
+++ b/cmds/stagefright/Android.mk
@@ -0,0 +1,41 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ JPEGSource.cpp \
+ stagefright.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright
+
+LOCAL_C_INCLUDES:= \
+ frameworks/base/media/libstagefright \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= stagefright
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ SineSource.cpp \
+ record.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libstagefright
+
+LOCAL_C_INCLUDES:= \
+ frameworks/base/media/libstagefright \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= record
+
+include $(BUILD_EXECUTABLE)
diff --git a/cmds/stagefright/JPEGSource.cpp b/cmds/stagefright/JPEGSource.cpp
new file mode 100644
index 0000000..25c772f
--- /dev/null
+++ b/cmds/stagefright/JPEGSource.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+#define LOG_TAG "JPEGSource"
+#include <utils/Log.h>
+
+#include "JPEGSource.h"
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+
+#define JPEG_SOF0 0xC0 /* nStart Of Frame N*/
+#define JPEG_SOF1 0xC1 /* N indicates which compression process*/
+#define JPEG_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use*/
+#define JPEG_SOF3 0xC3
+#define JPEG_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers*/
+#define JPEG_SOF6 0xC6
+#define JPEG_SOF7 0xC7
+#define JPEG_SOF9 0xC9
+#define JPEG_SOF10 0xCA
+#define JPEG_SOF11 0xCB
+#define JPEG_SOF13 0xCD
+#define JPEG_SOF14 0xCE
+#define JPEG_SOF15 0xCF
+#define JPEG_SOI 0xD8 /* nStart Of Image (beginning of datastream)*/
+#define JPEG_EOI 0xD9 /* End Of Image (end of datastream)*/
+#define JPEG_SOS 0xDA /* nStart Of Scan (begins compressed data)*/
+#define JPEG_JFIF 0xE0 /* Jfif marker*/
+#define JPEG_EXIF 0xE1 /* Exif marker*/
+#define JPEG_COM 0xFE /* COMment */
+#define JPEG_DQT 0xDB
+#define JPEG_DHT 0xC4
+#define JPEG_DRI 0xDD
+
+namespace android {
+
+JPEGSource::JPEGSource(const sp<DataSource> &source)
+ : mSource(source),
+ mGroup(NULL),
+ mStarted(false),
+ mSize(0),
+ mWidth(0),
+ mHeight(0),
+ mOffset(0) {
+ CHECK_EQ(parseJPEG(), OK);
+ CHECK(mSource->getSize(&mSize) == OK);
+}
+
+JPEGSource::~JPEGSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t JPEGSource::start(MetaData *) {
+ if (mStarted) {
+ return UNKNOWN_ERROR;
+ }
+
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(mSize));
+
+ mOffset = 0;
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t JPEGSource::stop() {
+ if (!mStarted) {
+ return UNKNOWN_ERROR;
+ }
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> JPEGSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_IMAGE_JPEG);
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
+ meta->setInt32(kKeyMaxInputSize, mSize);
+
+ return meta;
+}
+
+status_t JPEGSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+ return UNKNOWN_ERROR;
+ }
+
+ MediaBuffer *buffer;
+ mGroup->acquire_buffer(&buffer);
+
+ ssize_t n = mSource->read_at(mOffset, buffer->data(), mSize - mOffset);
+
+ if (n <= 0) {
+ buffer->release();
+ buffer = NULL;
+
+ return UNKNOWN_ERROR;
+ }
+
+ buffer->set_range(0, n);
+
+ mOffset += n;
+
+ *out = buffer;
+
+ return OK;
+}
+
+status_t JPEGSource::parseJPEG() {
+ mWidth = 0;
+ mHeight = 0;
+
+ off_t i = 0;
+
+ uint16_t soi;
+ if (!mSource->getUInt16(i, &soi)) {
+ return ERROR_IO;
+ }
+
+ i += 2;
+
+ if (soi != 0xffd8) {
+ return UNKNOWN_ERROR;
+ }
+
+ for (;;) {
+ uint8_t marker;
+ if (mSource->read_at(i++, &marker, 1) != 1) {
+ return ERROR_IO;
+ }
+
+ CHECK_EQ(marker, 0xff);
+
+ if (mSource->read_at(i++, &marker, 1) != 1) {
+ return ERROR_IO;
+ }
+
+ CHECK(marker != 0xff);
+
+ uint16_t chunkSize;
+ if (!mSource->getUInt16(i, &chunkSize)) {
+ return ERROR_IO;
+ }
+
+ i += 2;
+
+ if (chunkSize < 2) {
+ return UNKNOWN_ERROR;
+ }
+
+ switch (marker) {
+ case JPEG_SOS:
+ {
+ return (mWidth > 0 && mHeight > 0) ? OK : UNKNOWN_ERROR;
+ }
+
+ case JPEG_EOI:
+ {
+ return UNKNOWN_ERROR;
+ }
+
+ case JPEG_SOF0:
+ case JPEG_SOF1:
+ case JPEG_SOF3:
+ case JPEG_SOF5:
+ case JPEG_SOF6:
+ case JPEG_SOF7:
+ case JPEG_SOF9:
+ case JPEG_SOF10:
+ case JPEG_SOF11:
+ case JPEG_SOF13:
+ case JPEG_SOF14:
+ case JPEG_SOF15:
+ {
+ uint16_t width, height;
+ if (!mSource->getUInt16(i + 1, &height)
+ || !mSource->getUInt16(i + 3, &width)) {
+ return ERROR_IO;
+ }
+
+ mWidth = width;
+ mHeight = height;
+
+ i += chunkSize - 2;
+ break;
+ }
+
+ default:
+ {
+ // Skip chunk
+
+ i += chunkSize - 2;
+
+ break;
+ }
+ }
+ }
+
+ return OK;
+}
+
+} // namespace android
diff --git a/cmds/stagefright/JPEGSource.h b/cmds/stagefright/JPEGSource.h
new file mode 100644
index 0000000..051c034
--- /dev/null
+++ b/cmds/stagefright/JPEGSource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JPEG_SOURCE_H_
+
+#define JPEG_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class DataSource;
+class MediaBufferGroup;
+
+struct JPEGSource : public MediaSource {
+ // Assumes ownership of "source".
+ JPEGSource(const sp<DataSource> &source);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~JPEGSource();
+
+private:
+ sp<DataSource> mSource;
+ MediaBufferGroup *mGroup;
+ bool mStarted;
+ off_t mSize;
+ int32_t mWidth, mHeight;
+ off_t mOffset;
+
+ status_t parseJPEG();
+
+ JPEGSource(const JPEGSource &);
+ JPEGSource &operator=(const JPEGSource &);
+};
+
+} // namespace android
+
+#endif // JPEG_SOURCE_H_
+
diff --git a/cmds/stagefright/SineSource.cpp b/cmds/stagefright/SineSource.cpp
new file mode 100644
index 0000000..e5a6ccb
--- /dev/null
+++ b/cmds/stagefright/SineSource.cpp
@@ -0,0 +1,101 @@
+#include "SineSource.h"
+
+#include <math.h>
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+SineSource::SineSource(int32_t sampleRate, int32_t numChannels)
+ : mStarted(false),
+ mSampleRate(sampleRate),
+ mNumChannels(numChannels),
+ mPhase(0),
+ mGroup(NULL) {
+ CHECK(numChannels == 1 || numChannels == 2);
+}
+
+SineSource::~SineSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t SineSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(kBufferSize));
+
+ mPhase = 0;
+ mStarted = true;
+
+ return OK;
+}
+
+status_t SineSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> SineSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ meta->setInt32(kKeyChannelCount, mNumChannels);
+ meta->setInt32(kKeySampleRate, mSampleRate);
+
+ return meta;
+}
+
+status_t SineSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+
+ if (err != OK) {
+ return err;
+ }
+
+ size_t frameSize = mNumChannels * sizeof(int16_t);
+ size_t numFramesPerBuffer = buffer->size() / frameSize;
+
+ int16_t *ptr = (int16_t *)buffer->data();
+
+ const double k = kFrequency / mSampleRate * (2.0 * M_PI);
+
+ double x = mPhase * k;
+ for (size_t i = 0; i < numFramesPerBuffer; ++i) {
+ int16_t amplitude = (int16_t)(32767.0 * sin(x));
+
+ *ptr++ = amplitude;
+ if (mNumChannels == 2) {
+ *ptr++ = amplitude;
+ }
+
+ x += k;
+ }
+
+ buffer->meta_data()->setInt32(kKeyTimeUnits, mPhase);
+ buffer->meta_data()->setInt32(kKeyTimeScale, mSampleRate);
+
+ mPhase += numFramesPerBuffer;
+
+ buffer->set_range(0, numFramesPerBuffer * frameSize);
+
+ *out = buffer;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/cmds/stagefright/SineSource.h b/cmds/stagefright/SineSource.h
new file mode 100644
index 0000000..76ab669
--- /dev/null
+++ b/cmds/stagefright/SineSource.h
@@ -0,0 +1,39 @@
+#ifndef SINE_SOURCE_H_
+
+#define SINE_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct SineSource : public MediaSource {
+ SineSource(int32_t sampleRate, int32_t numChannels);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **out, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~SineSource();
+
+private:
+ enum { kBufferSize = 8192 };
+ static const double kFrequency = 500.0;
+
+ bool mStarted;
+ int32_t mSampleRate;
+ int32_t mNumChannels;
+ size_t mPhase;
+
+ MediaBufferGroup *mGroup;
+};
+
+} // namespace android
+
+#endif // SINE_SOURCE_H_
diff --git a/cmds/stagefright/WaveWriter.h b/cmds/stagefright/WaveWriter.h
new file mode 100644
index 0000000..a0eb66e
--- /dev/null
+++ b/cmds/stagefright/WaveWriter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_WAVEWRITER_H_
+
+#define ANDROID_WAVEWRITER_H_
+
+namespace android {
+
+class WaveWriter {
+public:
+ WaveWriter(const char *filename,
+ uint16_t num_channels, uint32_t sampling_rate)
+ : mFile(fopen(filename, "wb")),
+ mTotalBytes(0) {
+ fwrite("RIFFxxxxWAVEfmt \x10\x00\x00\x00\x01\x00", 1, 22, mFile);
+ write_u16(num_channels);
+ write_u32(sampling_rate);
+ write_u32(sampling_rate * num_channels * 2);
+ write_u16(num_channels * 2);
+ write_u16(16);
+ fwrite("dataxxxx", 1, 8, mFile);
+ }
+
+ ~WaveWriter() {
+ fseek(mFile, 40, SEEK_SET);
+ write_u32(mTotalBytes);
+
+ fseek(mFile, 4, SEEK_SET);
+ write_u32(36 + mTotalBytes);
+
+ fclose(mFile);
+ mFile = NULL;
+ }
+
+ void Append(const void *data, size_t size) {
+ fwrite(data, 1, size, mFile);
+ mTotalBytes += size;
+ }
+
+private:
+ void write_u16(uint16_t x) {
+ fputc(x & 0xff, mFile);
+ fputc(x >> 8, mFile);
+ }
+
+ void write_u32(uint32_t x) {
+ write_u16(x & 0xffff);
+ write_u16(x >> 16);
+ }
+
+ FILE *mFile;
+ size_t mTotalBytes;
+};
+
+} // namespace android
+
+#endif // ANDROID_WAVEWRITER_H_
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
new file mode 100644
index 0000000..323d448
--- /dev/null
+++ b/cmds/stagefright/record.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SineSource.h"
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/MediaPlayerInterface.h>
+
+using namespace android;
+
+#if 0
+class DummySource : public MediaSource {
+public:
+ DummySource(int width, int height)
+ : mWidth(width),
+ mHeight(height),
+ mSize((width * height * 3) / 2) {
+ mGroup.add_buffer(new MediaBuffer(mSize));
+ }
+
+ virtual sp<MetaData> getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, mWidth);
+ meta->setInt32(kKeyHeight, mHeight);
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+
+ return meta;
+ }
+
+ virtual status_t start(MetaData *params) {
+ return OK;
+ }
+
+ virtual status_t stop() {
+ return OK;
+ }
+
+ virtual status_t read(
+ MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
+ status_t err = mGroup.acquire_buffer(buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ char x = (char)((double)rand() / RAND_MAX * 255);
+ memset((*buffer)->data(), x, mSize);
+ (*buffer)->set_range(0, mSize);
+
+ return OK;
+ }
+
+protected:
+ virtual ~DummySource() {}
+
+private:
+ MediaBufferGroup mGroup;
+ int mWidth, mHeight;
+ size_t mSize;
+
+ DummySource(const DummySource &);
+ DummySource &operator=(const DummySource &);
+};
+
+sp<MediaSource> createSource(const char *filename) {
+ sp<MediaSource> source;
+
+ sp<MPEG4Extractor> extractor =
+ new MPEG4Extractor(new MmapSource(filename));
+
+ size_t num_tracks = extractor->countTracks();
+
+ sp<MetaData> meta;
+ for (size_t i = 0; i < num_tracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
+ CHECK(meta.get() != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ if (strncasecmp(mime, "video/", 6)) {
+ continue;
+ }
+
+ source = extractor->getTrack(i);
+ break;
+ }
+
+ return source;
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+#if 1
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s filename\n", argv[0]);
+ return 1;
+ }
+
+ OMXClient client;
+ CHECK_EQ(client.connect(), OK);
+
+#if 0
+ sp<MediaSource> source = createSource(argv[1]);
+
+ if (source == NULL) {
+ fprintf(stderr, "Unable to find a suitable video track.\n");
+ return 1;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ sp<OMXCodec> decoder = OMXCodec::Create(
+ client.interface(), meta, false /* createEncoder */, source);
+
+ int width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+#else
+ int width = 320;
+ int height = 240;
+ sp<MediaSource> decoder = new DummySource(width, height);
+#endif
+
+ sp<MetaData> enc_meta = new MetaData;
+ // enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ enc_meta->setInt32(kKeyWidth, width);
+ enc_meta->setInt32(kKeyHeight, height);
+
+ sp<OMXCodec> encoder =
+ OMXCodec::Create(
+ client.interface(), enc_meta, true /* createEncoder */, decoder);
+
+#if 0
+ sp<MPEG4Writer> writer = new MPEG4Writer("/sdcard/output.mp4");
+ writer->addSource(enc_meta, encoder);
+ writer->start();
+ sleep(20);
+ printf("stopping now.\n");
+ writer->stop();
+#else
+ encoder->start();
+
+ MediaBuffer *buffer;
+ while (encoder->read(&buffer) == OK) {
+ printf("got an output frame of size %d\n", buffer->range_length());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ encoder->stop();
+#endif
+
+ client.disconnect();
+#endif
+
+#if 0
+ CameraSource *source = CameraSource::Create();
+ printf("source = %p\n", source);
+
+ for (int i = 0; i < 100; ++i) {
+ MediaBuffer *buffer;
+ status_t err = source->read(&buffer);
+ CHECK_EQ(err, OK);
+
+ printf("got a frame, data=%p, size=%d\n",
+ buffer->data(), buffer->range_length());
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ delete source;
+ source = NULL;
+#endif
+
+ return 0;
+}
+#else
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ OMXClient client;
+ CHECK_EQ(client.connect(), OK);
+
+ const int32_t kSampleRate = 22050;
+ const int32_t kNumChannels = 2;
+ sp<MediaSource> audioSource = new SineSource(kSampleRate, kNumChannels);
+
+#if 0
+ sp<MediaPlayerBase::AudioSink> audioSink;
+ AudioPlayer *player = new AudioPlayer(audioSink);
+ player->setSource(audioSource);
+ player->start();
+
+ sleep(10);
+
+ player->stop();
+#endif
+
+ sp<MetaData> encMeta = new MetaData;
+ encMeta->setCString(kKeyMIMEType,
+ 1 ? MEDIA_MIMETYPE_AUDIO_AMR_WB : MEDIA_MIMETYPE_AUDIO_AAC);
+ encMeta->setInt32(kKeySampleRate, kSampleRate);
+ encMeta->setInt32(kKeyChannelCount, kNumChannels);
+ encMeta->setInt32(kKeyMaxInputSize, 8192);
+
+ sp<MediaSource> encoder =
+ OMXCodec::Create(client.interface(), encMeta, true, audioSource);
+
+ encoder->start();
+
+ int32_t n = 0;
+ status_t err;
+ MediaBuffer *buffer;
+ while ((err = encoder->read(&buffer)) == OK) {
+ printf(".");
+ fflush(stdout);
+
+ buffer->release();
+ buffer = NULL;
+
+ if (++n == 100) {
+ break;
+ }
+ }
+ printf("$\n");
+
+ encoder->stop();
+
+ client.disconnect();
+
+ return 0;
+}
+#endif
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
new file mode 100644
index 0000000..d1926b4
--- /dev/null
+++ b/cmds/stagefright/stagefright.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/time.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+
+#include "JPEGSource.h"
+
+using namespace android;
+
+static long gNumRepetitions;
+static long gMaxNumFrames; // 0 means decode all available.
+static long gReproduceBug; // if not -1.
+
+static int64_t getNowUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+}
+
+static void playSource(OMXClient *client, const sp<MediaSource> &source) {
+ sp<MetaData> meta = source->getFormat();
+
+ sp<OMXCodec> decoder = OMXCodec::Create(
+ client->interface(), meta, false /* createEncoder */, source);
+
+ if (decoder == NULL) {
+ return;
+ }
+
+ decoder->start();
+
+ int n = 0;
+ int64_t startTime = getNowUs();
+
+ long numIterationsLeft = gNumRepetitions;
+ MediaSource::ReadOptions options;
+
+ while (numIterationsLeft-- > 0) {
+ long numFrames = 0;
+
+ MediaBuffer *buffer;
+
+ for (;;) {
+ status_t err = decoder->read(&buffer, &options);
+ options.clearSeekTo();
+
+ if (err != OK) {
+ CHECK_EQ(buffer, NULL);
+ break;
+ }
+
+ if ((n++ % 16) == 0) {
+ printf(".");
+ fflush(stdout);
+ }
+
+ buffer->release();
+ buffer = NULL;
+
+ ++numFrames;
+ if (gMaxNumFrames > 0 && numFrames == gMaxNumFrames) {
+ break;
+ }
+
+ if (gReproduceBug == 1 && numFrames == 40) {
+ printf("seeking past the end now.");
+ options.setSeekTo(0x7fffffffL);
+ } else if (gReproduceBug == 2 && numFrames == 40) {
+ printf("seeking to 5 secs.");
+ options.setSeekTo(5000000);
+ }
+ }
+
+ printf("$");
+ fflush(stdout);
+
+ options.setSeekTo(0);
+ }
+
+ decoder->stop();
+ printf("\n");
+
+ int64_t delay = getNowUs() - startTime;
+ printf("avg. %.2f fps\n", n * 1E6 / delay);
+
+ printf("decoded a total of %d frame(s).\n", n);
+}
+
+static void usage(const char *me) {
+ fprintf(stderr, "usage: %s\n", me);
+ fprintf(stderr, " -h(elp)\n");
+ fprintf(stderr, " -a(udio)\n");
+ fprintf(stderr, " -n repetitions\n");
+ fprintf(stderr, " -l(ist) components\n");
+ fprintf(stderr, " -m max-number-of-frames-to-decode in each pass\n");
+ fprintf(stderr, " -b bug to reproduce\n");
+ fprintf(stderr, " -p(rofiles) dump decoder profiles supported\n");
+}
+
+int main(int argc, char **argv) {
+ android::ProcessState::self()->startThreadPool();
+
+ bool audioOnly = false;
+ bool listComponents = false;
+ bool dumpProfiles = false;
+ gNumRepetitions = 1;
+ gMaxNumFrames = 0;
+ gReproduceBug = -1;
+
+ int res;
+ while ((res = getopt(argc, argv, "han:lm:b:p")) >= 0) {
+ switch (res) {
+ case 'a':
+ {
+ audioOnly = true;
+ break;
+ }
+
+ case 'l':
+ {
+ listComponents = true;
+ break;
+ }
+
+ case 'm':
+ case 'n':
+ case 'b':
+ {
+ char *end;
+ long x = strtol(optarg, &end, 10);
+
+ if (*end != '\0' || end == optarg || x <= 0) {
+ x = 1;
+ }
+
+ if (res == 'n') {
+ gNumRepetitions = x;
+ } else if (res == 'm') {
+ gMaxNumFrames = x;
+ } else {
+ CHECK_EQ(res, 'b');
+ gReproduceBug = x;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ dumpProfiles = true;
+ break;
+ }
+
+ case '?':
+ case 'h':
+ default:
+ {
+ usage(argv[0]);
+ exit(1);
+ break;
+ }
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (dumpProfiles) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service =
+ interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ sp<IOMX> omx = service->createOMX();
+ CHECK(omx.get() != NULL);
+
+ const char *kMimeTypes[] = {
+ MEDIA_MIMETYPE_VIDEO_AVC, MEDIA_MIMETYPE_VIDEO_MPEG4,
+ MEDIA_MIMETYPE_VIDEO_H263
+ };
+
+ for (size_t k = 0; k < sizeof(kMimeTypes) / sizeof(kMimeTypes[0]);
+ ++k) {
+ printf("type '%s':\n", kMimeTypes[k]);
+
+ Vector<CodecCapabilities> results;
+ CHECK_EQ(QueryCodecs(omx, kMimeTypes[k],
+ true, // queryDecoders
+ &results), OK);
+
+ for (size_t i = 0; i < results.size(); ++i) {
+ printf(" decoder '%s' supports ",
+ results[i].mComponentName.string());
+
+ if (results[i].mProfileLevels.size() == 0) {
+ printf("NOTHING.\n");
+ continue;
+ }
+
+ for (size_t j = 0; j < results[i].mProfileLevels.size(); ++j) {
+ const CodecProfileLevel &profileLevel =
+ results[i].mProfileLevels[j];
+
+ printf("%s%ld/%ld", j > 0 ? ", " : "",
+ profileLevel.mProfile, profileLevel.mLevel);
+ }
+
+ printf("\n");
+ }
+ }
+ }
+
+ if (listComponents) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ sp<IOMX> omx = service->createOMX();
+ CHECK(omx.get() != NULL);
+
+ List<String8> list;
+ omx->list_nodes(&list);
+
+ for (List<String8>::iterator it = list.begin();
+ it != list.end(); ++it) {
+ printf("%s\n", (*it).string());
+ }
+ }
+
+ DataSource::RegisterDefaultSniffers();
+
+ OMXClient client;
+ status_t err = client.connect();
+
+ for (int k = 0; k < argc; ++k) {
+ const char *filename = argv[k];
+
+ sp<DataSource> dataSource;
+ if (!strncasecmp("http://", filename, 7)) {
+ dataSource = new HTTPDataSource(filename);
+ dataSource = new CachingDataSource(dataSource, 64 * 1024, 10);
+ } else {
+ dataSource = new MmapSource(filename);
+ }
+
+ bool isJPEG = false;
+
+ size_t len = strlen(filename);
+ if (len >= 4 && !strcasecmp(filename + len - 4, ".jpg")) {
+ isJPEG = true;
+ }
+
+ sp<MediaSource> mediaSource;
+
+ if (isJPEG) {
+ mediaSource = new JPEGSource(dataSource);
+ } else {
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ size_t numTracks = extractor->countTracks();
+
+ sp<MetaData> meta;
+ size_t i;
+ for (i = 0; i < numTracks; ++i) {
+ meta = extractor->getTrackMetaData(i);
+
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
+
+ if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+ break;
+ }
+
+ if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
+ break;
+ }
+ }
+
+ mediaSource = extractor->getTrack(i);
+ }
+
+ playSource(&client, mediaSource);
+ }
+
+ client.disconnect();
+
+ return 0;
+}
diff --git a/cmds/surfaceflinger/Android.mk b/cmds/surfaceflinger/Android.mk
index 37c3d94..bfa58a1 100644
--- a/cmds/surfaceflinger/Android.mk
+++ b/cmds/surfaceflinger/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libsurfaceflinger \
+ libbinder \
libutils
LOCAL_C_INCLUDES := \
diff --git a/cmds/surfaceflinger/main_surfaceflinger.cpp b/cmds/surfaceflinger/main_surfaceflinger.cpp
index 7c89578..d650721 100644
--- a/cmds/surfaceflinger/main_surfaceflinger.cpp
+++ b/cmds/surfaceflinger/main_surfaceflinger.cpp
@@ -1,6 +1,6 @@
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <SurfaceFlinger.h>
diff --git a/cmds/svc/src/com/android/commands/svc/PowerCommand.java b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
index 2b54f54..e021012 100644
--- a/cmds/svc/src/com/android/commands/svc/PowerCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/PowerCommand.java
@@ -16,7 +16,10 @@
package com.android.commands.svc;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.PowerManager;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.os.BatteryManager;
@@ -60,7 +63,10 @@ public class PowerCommand extends Svc.Command {
IPowerManager pm
= IPowerManager.Stub.asInterface(ServiceManager.getService(Context.POWER_SERVICE));
try {
+ IBinder lock = new Binder();
+ pm.acquireWakeLock(PowerManager.FULL_WAKE_LOCK, lock, "svc power");
pm.setStayOnSetting(val);
+ pm.releaseWakeLock(lock);
}
catch (RemoteException e) {
System.err.println("Faild to set setting: " + e);
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
index 0a684e8..ad537977 100644
--- a/cmds/system_server/Android.mk
+++ b/cmds/system_server/Android.mk
@@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libutils \
+ libbinder \
libsystem_server
LOCAL_C_INCLUDES := \
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
index 580331a..1813d3e 100644
--- a/cmds/system_server/library/Android.mk
+++ b/cmds/system_server/library/Android.mk
@@ -20,6 +20,7 @@ LOCAL_SHARED_LIBRARIES := \
libcameraservice \
libmediaplayerservice \
libutils \
+ libbinder \
libcutils
LOCAL_MODULE:= libsystem_server
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
index 73b23e2..1d57fdc 100644
--- a/cmds/system_server/library/system_init.cpp
+++ b/cmds/system_server/library/system_init.cpp
@@ -8,15 +8,16 @@
#define LOG_TAG "sysproc"
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/TextOutput.h>
#include <utils/Log.h>
#include <SurfaceFlinger.h>
#include <AudioFlinger.h>
#include <CameraService.h>
+#include <AudioPolicyService.h>
#include <MediaPlayerService.h>
#include <android_runtime/AndroidRuntime.h>
@@ -80,6 +81,9 @@ extern "C" status_t system_init()
// Start the camera service
CameraService::instantiate();
+
+ // Start the audio policy service
+ AudioPolicyService::instantiate();
}
// And now start the Android runtime. We have to do this bit
diff --git a/cmds/system_server/system_main.cpp b/cmds/system_server/system_main.cpp
index ca16e57..543f650 100644
--- a/cmds/system_server/system_main.cpp
+++ b/cmds/system_server/system_main.cpp
@@ -9,7 +9,7 @@
#define LOG_TAG "sysproc"
-#include <utils/IPCThreadState.h>
+#include <binder/IPCThreadState.h>
#include <utils/Log.h>
#include <private/android_filesystem_config.h>
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 79bd6e7..8c422a2 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -211,8 +211,10 @@ public abstract class AccessibilityService extends Service {
switch (message.what) {
case DO_ON_ACCESSIBILITY_EVENT :
AccessibilityEvent event = (AccessibilityEvent) message.obj;
- mTarget.onAccessibilityEvent(event);
- event.recycle();
+ if (event != null){
+ mTarget.onAccessibilityEvent(event);
+ event.recycle();
+ }
return;
case DO_ON_INTERRUPT :
mTarget.onInterrupt();
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
new file mode 100644
index 0000000..38ae962
--- /dev/null
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.Binder;
+import android.util.Log;
+import android.content.pm.PackageManager;
+import android.content.Context;
+import android.Manifest;
+
+/**
+ * Base class for creating AccountAuthenticators. This implements the IAccountAuthenticator
+ * binder interface and also provides helper libraries to simplify the creation of
+ * AccountAuthenticators.
+ */
+public abstract class AbstractAccountAuthenticator {
+ private final Context mContext;
+
+ public AbstractAccountAuthenticator(Context context) {
+ mContext = context;
+ }
+
+ class Transport extends IAccountAuthenticator.Stub {
+ public void addAccount(IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, String[] requiredFeatures, Bundle options)
+ throws RemoteException {
+ checkBinderPermission();
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.addAccount(
+ new AccountAuthenticatorResponse(response),
+ accountType, authTokenType, requiredFeatures, options);
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "addAccount not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
+
+ public void confirmPassword(IAccountAuthenticatorResponse response,
+ Account account, String password) throws RemoteException {
+ checkBinderPermission();
+ boolean result;
+ try {
+ result = AbstractAccountAuthenticator.this.confirmPassword(
+ new AccountAuthenticatorResponse(response),
+ account, password);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "confirmPassword not supported");
+ return;
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ }
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(Constants.BOOLEAN_RESULT_KEY, result);
+ response.onResult(bundle);
+ }
+
+ public void confirmCredentials(IAccountAuthenticatorResponse response,
+ Account account) throws RemoteException {
+ checkBinderPermission();
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.confirmCredentials(
+ new AccountAuthenticatorResponse(response), account);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "confirmCredentials not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
+
+ public void getAuthTokenLabel(IAccountAuthenticatorResponse response,
+ String authTokenType)
+ throws RemoteException {
+ checkBinderPermission();
+ try {
+ Bundle result = new Bundle();
+ result.putString(Constants.AUTH_TOKEN_LABEL_KEY,
+ AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType));
+ response.onResult(result);
+ } catch (IllegalArgumentException e) {
+ response.onError(Constants.ERROR_CODE_BAD_ARGUMENTS,
+ "unknown authTokenType");
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "getAuthTokenTypeLabel not supported");
+ }
+ }
+
+ public void getAuthToken(IAccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle loginOptions)
+ throws RemoteException {
+ checkBinderPermission();
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this.getAuthToken(
+ new AccountAuthenticatorResponse(response), account,
+ authTokenType, loginOptions);
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "getAuthToken not supported");
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ }
+ }
+
+ public void updateCredentials(IAccountAuthenticatorResponse response, Account account,
+ String authTokenType, Bundle loginOptions) throws RemoteException {
+ checkBinderPermission();
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.updateCredentials(
+ new AccountAuthenticatorResponse(response), account,
+ authTokenType, loginOptions);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "updateCredentials not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
+
+ public void editProperties(IAccountAuthenticatorResponse response,
+ String accountType) throws RemoteException {
+ checkBinderPermission();
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.editProperties(
+ new AccountAuthenticatorResponse(response), accountType);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "editProperties not supported");
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
+
+ public void hasFeatures(IAccountAuthenticatorResponse response,
+ Account account, String[] features) throws RemoteException {
+ checkBinderPermission();
+ final Bundle result;
+ try {
+ result = AbstractAccountAuthenticator.this.hasFeatures(
+ new AccountAuthenticatorResponse(response), account, features);
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "hasFeatures not supported");
+ return;
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ }
+ if (result != null) {
+ response.onResult(result);
+ }
+ }
+
+ public void getAccountRemovalAllowed(IAccountAuthenticatorResponse response,
+ Account account) throws RemoteException {
+ checkBinderPermission();
+ try {
+ final Bundle result = AbstractAccountAuthenticator.this.getAccountRemovalAllowed(
+ new AccountAuthenticatorResponse(response), account);
+ if (result != null) {
+ response.onResult(result);
+ }
+ } catch (UnsupportedOperationException e) {
+ response.onError(Constants.ERROR_CODE_UNSUPPORTED_OPERATION,
+ "getAccountRemovalAllowed not supported");
+ return;
+ } catch (NetworkErrorException e) {
+ response.onError(Constants.ERROR_CODE_NETWORK_ERROR, e.getMessage());
+ return;
+ }
+ }
+ }
+
+ private void checkBinderPermission() {
+ final int uid = Binder.getCallingUid();
+ final String perm = Manifest.permission.ACCOUNT_MANAGER_SERVICE;
+ if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("caller uid " + uid + " lacks " + perm);
+ }
+ }
+
+ Transport mTransport = new Transport();
+
+ /**
+ * @return the IAccountAuthenticator binder transport object
+ */
+ public final IAccountAuthenticator getIAccountAuthenticator()
+ {
+ return mTransport;
+ }
+
+ /**
+ * Returns a Bundle that contains the Intent of the activity that can be used to edit the
+ * properties. In order to indicate success the activity should call response.setResult()
+ * with a non-null Bundle.
+ * @param response used to set the result for the request. If the Constants.INTENT_KEY
+ * is set in the bundle then this response field is to be used for sending future
+ * results if and when the Intent is started.
+ * @param accountType the AccountType whose properties are to be edited.
+ * @return a Bundle containing the result or the Intent to start to continue the request.
+ * If this is null then the request is considered to still be active and the result should
+ * sent later using response.
+ */
+ public abstract Bundle editProperties(AccountAuthenticatorResponse response,
+ String accountType);
+ public abstract Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+ String authTokenType, String[] requiredFeatures, Bundle options)
+ throws NetworkErrorException;
+ /* @deprecated */
+ public abstract boolean confirmPassword(AccountAuthenticatorResponse response,
+ Account account, String password) throws NetworkErrorException;
+ public abstract Bundle confirmCredentials(AccountAuthenticatorResponse response,
+ Account account);
+ public abstract Bundle getAuthToken(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle loginOptions)
+ throws NetworkErrorException;
+ public abstract String getAuthTokenLabel(String authTokenType);
+ public abstract Bundle updateCredentials(AccountAuthenticatorResponse response,
+ Account account, String authTokenType, Bundle loginOptions);
+ public abstract Bundle hasFeatures(AccountAuthenticatorResponse response,
+ Account account, String[] features) throws NetworkErrorException;
+ public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
+ Account account) throws NetworkErrorException {
+ final Bundle result = new Bundle();
+ result.putBoolean(Constants.BOOLEAN_RESULT_KEY, true);
+ return result;
+ }
+}
diff --git a/core/java/android/accounts/Account.aidl b/core/java/android/accounts/Account.aidl
new file mode 100644
index 0000000..8752d99
--- /dev/null
+++ b/core/java/android/accounts/Account.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+parcelable Account;
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
new file mode 100644
index 0000000..7b83a30
--- /dev/null
+++ b/core/java/android/accounts/Account.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+/**
+ * Value type that represents an Account in the {@link AccountManager}. This object is
+ * {@link Parcelable} and also overrides {@link #equals} and {@link #hashCode}, making it
+ * suitable for use as the key of a {@link java.util.Map}
+ */
+public class Account implements Parcelable {
+ public final String name;
+ public final String type;
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof Account)) return false;
+ final Account other = (Account)o;
+ return name.equals(other.name) && type.equals(other.type);
+ }
+
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + name.hashCode();
+ result = 31 * result + type.hashCode();
+ return result;
+ }
+
+ public Account(String name, String type) {
+ if (TextUtils.isEmpty(name)) {
+ throw new IllegalArgumentException("the name must not be empty: " + name);
+ }
+ if (TextUtils.isEmpty(type)) {
+ throw new IllegalArgumentException("the type must not be empty: " + type);
+ }
+ this.name = name;
+ this.type = type;
+ }
+
+ public Account(Parcel in) {
+ this.name = in.readString();
+ this.type = in.readString();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(name);
+ dest.writeString(type);
+ }
+
+ public static final Creator<Account> CREATOR = new Creator<Account>() {
+ public Account createFromParcel(Parcel source) {
+ return new Account(source);
+ }
+
+ public Account[] newArray(int size) {
+ return new Account[size];
+ }
+ };
+
+ public String toString() {
+ return "Account {name=" + name + ", type=" + type + "}";
+ }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorActivity.java b/core/java/android/accounts/AccountAuthenticatorActivity.java
new file mode 100644
index 0000000..0319ab9
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorActivity.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * Base class for implementing an Activity that is used to help implement an
+ * AbstractAccountAuthenticator. If the AbstractAccountAuthenticator needs to return an Intent
+ * that is to be used to launch an Activity that needs to return results to satisfy an
+ * AbstractAccountAuthenticator request, it should store the AccountAuthenticatorResponse
+ * inside of the Intent as follows:
+ * <p>
+ * intent.putExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY, response);
+ * <p>
+ * The activity that it launches should extend the AccountAuthenticatorActivity. If this
+ * activity has a result that satisfies the original request it sets it via:
+ * <p>
+ * setAccountAuthenticatorResult(result)
+ * <p>
+ * This result will be sent as the result of the request when the activity finishes. If this
+ * is never set or if it is set to null then the request will be canceled when the activity
+ * finishes.
+ */
+public class AccountAuthenticatorActivity extends Activity {
+ private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
+ private Bundle mResultBundle = null;
+
+ /**
+ * Set the result that is to be sent as the result of the request that caused this
+ * Activity to be launched. If result is null or this method is never called then
+ * the request will be canceled.
+ * @param result this is returned as the result of the AbstractAccountAuthenticator request
+ */
+ public final void setAccountAuthenticatorResult(Bundle result) {
+ mResultBundle = result;
+ }
+
+ /**
+ * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the
+ * icicle is non-zero.
+ * @param icicle the save instance data of this Activity, may be null
+ */
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ if (icicle == null) {
+ Intent intent = getIntent();
+ mAccountAuthenticatorResponse =
+ intent.getParcelableExtra(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+ } else {
+ mAccountAuthenticatorResponse =
+ icicle.getParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY);
+ }
+
+ if (mAccountAuthenticatorResponse != null) {
+ mAccountAuthenticatorResponse.onRequestContinued();
+ }
+ }
+
+ /**
+ * Saves the AccountAuthenticatorResponse in the instance state.
+ * @param outState where to store any instance data
+ */
+ protected void onSaveInstanceState(Bundle outState) {
+ outState.putParcelable(Constants.ACCOUNT_AUTHENTICATOR_RESPONSE_KEY,
+ mAccountAuthenticatorResponse);
+ super.onSaveInstanceState(outState);
+ }
+
+ /**
+ * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+ */
+ public void finish() {
+ if (mAccountAuthenticatorResponse != null) {
+ // send the result bundle back if set, otherwise send an error.
+ if (mResultBundle != null) {
+ mAccountAuthenticatorResponse.onResult(mResultBundle);
+ } else {
+ mAccountAuthenticatorResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+ }
+ mAccountAuthenticatorResponse = null;
+ }
+ super.finish();
+ }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java
new file mode 100644
index 0000000..82cadd5
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorCache.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.pm.PackageManager;
+import android.content.pm.RegisteredServicesCache;
+import android.content.res.TypedArray;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.text.TextUtils;
+
+/**
+ * A cache of services that export the {@link IAccountAuthenticator} interface. This cache
+ * is built by interrogating the {@link PackageManager} and is updated as packages are added,
+ * removed and changed. The authenticators are referred to by their account type and
+ * are made available via the {@link RegisteredServicesCache#getServiceInfo} method.
+ * @hide
+ */
+/* package private */ class AccountAuthenticatorCache
+ extends RegisteredServicesCache<AuthenticatorDescription> {
+ private static final String TAG = "Account";
+
+ public AccountAuthenticatorCache(Context context) {
+ super(context, Constants.AUTHENTICATOR_INTENT_ACTION,
+ Constants.AUTHENTICATOR_META_DATA_NAME, Constants.AUTHENTICATOR_ATTRIBUTES_NAME);
+ }
+
+ public AuthenticatorDescription parseServiceAttributes(String packageName, AttributeSet attrs) {
+ TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.AccountAuthenticator);
+ try {
+ final String accountType =
+ sa.getString(com.android.internal.R.styleable.AccountAuthenticator_accountType);
+ final int labelId = sa.getResourceId(
+ com.android.internal.R.styleable.AccountAuthenticator_label, 0);
+ final int iconId = sa.getResourceId(
+ com.android.internal.R.styleable.AccountAuthenticator_icon, 0);
+ if (TextUtils.isEmpty(accountType)) {
+ return null;
+ }
+ return new AuthenticatorDescription(accountType, packageName, labelId, iconId);
+ } finally {
+ sa.recycle();
+ }
+ }
+}
diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java
new file mode 100644
index 0000000..7198046
--- /dev/null
+++ b/core/java/android/accounts/AccountAuthenticatorResponse.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.os.RemoteException;
+
+/**
+ * Object that wraps calls to an {@link IAccountAuthenticatorResponse} object.
+ * TODO: this interface is still in flux
+ */
+public class AccountAuthenticatorResponse implements Parcelable {
+ private IAccountAuthenticatorResponse mAccountAuthenticatorResponse;
+
+ public AccountAuthenticatorResponse(IAccountAuthenticatorResponse response) {
+ mAccountAuthenticatorResponse = response;
+ }
+
+ public AccountAuthenticatorResponse(Parcel parcel) {
+ mAccountAuthenticatorResponse =
+ IAccountAuthenticatorResponse.Stub.asInterface(parcel.readStrongBinder());
+ }
+
+ public void onResult(Bundle result) {
+ try {
+ mAccountAuthenticatorResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public void onRequestContinued() {
+ try {
+ mAccountAuthenticatorResponse.onRequestContinued();
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ try {
+ mAccountAuthenticatorResponse.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mAccountAuthenticatorResponse.asBinder());
+ }
+
+ public static final Creator<AccountAuthenticatorResponse> CREATOR =
+ new Creator<AccountAuthenticatorResponse>() {
+ public AccountAuthenticatorResponse createFromParcel(Parcel source) {
+ return new AccountAuthenticatorResponse(source);
+ }
+
+ public AccountAuthenticatorResponse[] newArray(int size) {
+ return new AccountAuthenticatorResponse[size];
+ }
+ };
+}
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
new file mode 100644
index 0000000..d04abe5
--- /dev/null
+++ b/core/java/android/accounts/AccountManager.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.BroadcastReceiver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.TimeUnit;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.android.collect.Maps;
+
+/**
+ * A class that helps with interactions with the AccountManagerService. It provides
+ * methods to allow for account, password, and authtoken management for all accounts on the
+ * device. Some of these calls are implemented with the help of the corresponding
+ * {@link IAccountAuthenticator} services. One accesses the {@link AccountManager} by calling:
+ * AccountManager accountManager = AccountManager.get(context);
+ *
+ * <p>
+ * TODO(fredq) this interface is still in flux
+ */
+public class AccountManager {
+ private static final String TAG = "AccountManager";
+
+ private final Context mContext;
+ private final IAccountManager mService;
+ private final Handler mMainHandler;
+
+ /**
+ * @hide
+ */
+ public AccountManager(Context context, IAccountManager service) {
+ mContext = context;
+ mService = service;
+ mMainHandler = new Handler(mContext.getMainLooper());
+ }
+
+ /**
+ * @hide used for testing only
+ */
+ public AccountManager(Context context, IAccountManager service, Handler handler) {
+ mContext = context;
+ mService = service;
+ mMainHandler = handler;
+ }
+
+ public static AccountManager get(Context context) {
+ return (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
+ }
+
+ public String getPassword(final Account account) {
+ try {
+ return mService.getPassword(account);
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getUserData(final Account account, final String key) {
+ try {
+ return mService.getUserData(account, key);
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public AuthenticatorDescription[] getAuthenticatorTypes() {
+ try {
+ return mService.getAuthenticatorTypes();
+ } catch (RemoteException e) {
+ // will never happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Account[] getAccounts() {
+ try {
+ return mService.getAccounts(null);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public Account[] getAccountsByType(String type) {
+ try {
+ return mService.getAccounts(type);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
+ try {
+ return mService.addAccount(account, password, extras);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public AccountManagerFuture<Boolean> removeAccount(final Account account,
+ AccountManagerCallback<Boolean> callback, Handler handler) {
+ return new Future2Task<Boolean>(handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.removeAccount(mResponse, account);
+ }
+ public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+ if (!bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) {
+ throw new AuthenticatorException("no result in response");
+ }
+ return bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY);
+ }
+ }.start();
+ }
+
+ public void invalidateAuthToken(final String accountType, final String authToken) {
+ try {
+ mService.invalidateAuthToken(accountType, authToken);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String peekAuthToken(final Account account, final String authTokenType) {
+ try {
+ return mService.peekAuthToken(account, authTokenType);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setPassword(final Account account, final String password) {
+ try {
+ mService.setPassword(account, password);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void clearPassword(final Account account) {
+ try {
+ mService.clearPassword(account);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setUserData(final Account account, final String key, final String value) {
+ try {
+ mService.setUserData(account, key, value);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setAuthToken(Account account, final String authTokenType, final String authToken) {
+ try {
+ mService.setAuthToken(account, authTokenType, authToken);
+ } catch (RemoteException e) {
+ // won't ever happen
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String blockingGetAuthToken(Account account, String authTokenType,
+ boolean notifyAuthFailure)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ Bundle bundle = getAuthToken(account, authTokenType, notifyAuthFailure, null /* callback */,
+ null /* handler */).getResult();
+ return bundle.getString(Constants.AUTHTOKEN_KEY);
+ }
+
+ /**
+ * Request the auth token for this account/authTokenType. If this succeeds then the
+ * auth token will then be passed to the activity. If this results in an authentication
+ * failure then a login intent will be returned that can be invoked to prompt the user to
+ * update their credentials. This login activity will return the auth token to the calling
+ * activity. If activity is null then the login intent will not be invoked.
+ *
+ * @param account the account whose auth token should be retrieved
+ * @param authTokenType the auth token type that should be retrieved
+ * @param loginOptions
+ * @param activity the activity to launch the login intent, if necessary, and to which
+ */
+ public AccountManagerFuture<Bundle> getAuthToken(
+ final Account account, final String authTokenType, final Bundle loginOptions,
+ final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+ if (activity == null) throw new IllegalArgumentException("activity is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAuthToken(mResponse, account, authTokenType,
+ false /* notifyOnAuthFailure */, true /* expectActivityLaunch */,
+ loginOptions);
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Bundle> getAuthToken(
+ final Account account, final String authTokenType, final boolean notifyAuthFailure,
+ AccountManagerCallback<Bundle> callback, Handler handler) {
+ if (account == null) throw new IllegalArgumentException("account is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ return new AmsTask(null, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAuthToken(mResponse, account, authTokenType,
+ notifyAuthFailure, false /* expectActivityLaunch */, null /* options */);
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Bundle> addAccount(final String accountType,
+ final String authTokenType, final String[] requiredFeatures,
+ final Bundle addAccountOptions,
+ final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.addAcount(mResponse, accountType, authTokenType,
+ requiredFeatures, activity != null, addAccountOptions);
+ }
+ }.start();
+ }
+
+ /** @deprecated use {@link #confirmCredentials} instead */
+ @Deprecated
+ public AccountManagerFuture<Boolean> confirmPassword(final Account account, final String password,
+ AccountManagerCallback<Boolean> callback, Handler handler) {
+ return new Future2Task<Boolean>(handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.confirmPassword(mResponse, account, password);
+ }
+ public Boolean bundleToResult(Bundle bundle) throws AuthenticatorException {
+ if (!bundle.containsKey(Constants.BOOLEAN_RESULT_KEY)) {
+ throw new AuthenticatorException("no result in response");
+ }
+ return bundle.getBoolean(Constants.BOOLEAN_RESULT_KEY);
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
+ final String type, final String[] features,
+ AccountManagerCallback<Account[]> callback, Handler handler) {
+ if (type == null) throw new IllegalArgumentException("type is null");
+ return new Future2Task<Account[]>(handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.getAccountsByFeatures(mResponse, type, features);
+ }
+ public Account[] bundleToResult(Bundle bundle) throws AuthenticatorException {
+ if (!bundle.containsKey(Constants.ACCOUNTS_KEY)) {
+ throw new AuthenticatorException("no result in response");
+ }
+ final Parcelable[] parcelables = bundle.getParcelableArray(Constants.ACCOUNTS_KEY);
+ Account[] descs = new Account[parcelables.length];
+ for (int i = 0; i < parcelables.length; i++) {
+ descs[i] = (Account) parcelables[i];
+ }
+ return descs;
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Bundle> confirmCredentials(final Account account, final Activity activity,
+ final AccountManagerCallback<Bundle> callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.confirmCredentials(mResponse, account, activity != null);
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Bundle> updateCredentials(final Account account, final String authTokenType,
+ final Bundle loginOptions, final Activity activity,
+ final AccountManagerCallback<Bundle> callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.updateCredentials(mResponse, account, authTokenType, activity != null,
+ loginOptions);
+ }
+ }.start();
+ }
+
+ public AccountManagerFuture<Bundle> editProperties(final String accountType, final Activity activity,
+ final AccountManagerCallback<Bundle> callback,
+ final Handler handler) {
+ return new AmsTask(activity, handler, callback) {
+ public void doWork() throws RemoteException {
+ mService.editProperties(mResponse, accountType, activity != null);
+ }
+ }.start();
+ }
+
+ private void ensureNotOnMainThread() {
+ final Looper looper = Looper.myLooper();
+ if (looper != null && looper == mContext.getMainLooper()) {
+ // We really want to throw an exception here, but GTalkService exercises this
+ // path quite a bit and needs some serious rewrite in order to work properly.
+ //noinspection ThrowableInstanceNeverThrow
+// Log.e(TAG, "calling this from your main thread can lead to deadlock and/or ANRs",
+// new Exception());
+ // TODO(fredq) remove the log and throw this exception when the callers are fixed
+// throw new IllegalStateException(
+// "calling this from your main thread can lead to deadlock");
+ }
+ }
+
+ private void postToHandler(Handler handler, final AccountManagerCallback<Bundle> callback,
+ final AccountManagerFuture<Bundle> future) {
+ handler = handler == null ? mMainHandler : handler;
+ handler.post(new Runnable() {
+ public void run() {
+ callback.run(future);
+ }
+ });
+ }
+
+ private void postToHandler(Handler handler, final OnAccountsUpdatedListener listener,
+ final Account[] accounts) {
+ final Account[] accountsCopy = new Account[accounts.length];
+ // send a copy to make sure that one doesn't
+ // change what another sees
+ System.arraycopy(accounts, 0, accountsCopy, 0, accountsCopy.length);
+ handler = (handler == null) ? mMainHandler : handler;
+ handler.post(new Runnable() {
+ public void run() {
+ listener.onAccountsUpdated(accountsCopy);
+ }
+ });
+ }
+
+ private abstract class AmsTask extends FutureTask<Bundle> implements AccountManagerFuture<Bundle> {
+ final IAccountManagerResponse mResponse;
+ final Handler mHandler;
+ final AccountManagerCallback<Bundle> mCallback;
+ final Activity mActivity;
+ public AmsTask(Activity activity, Handler handler, AccountManagerCallback<Bundle> callback) {
+ super(new Callable<Bundle>() {
+ public Bundle call() throws Exception {
+ throw new IllegalStateException("this should never be called");
+ }
+ });
+
+ mHandler = handler;
+ mCallback = callback;
+ mActivity = activity;
+ mResponse = new Response();
+ }
+
+ public final AccountManagerFuture<Bundle> start() {
+ try {
+ doWork();
+ } catch (RemoteException e) {
+ setException(e);
+ }
+ return this;
+ }
+
+ public abstract void doWork() throws RemoteException;
+
+ private Bundle internalGetResult(Long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ ensureNotOnMainThread();
+ try {
+ if (timeout == null) {
+ return get();
+ } else {
+ return get(timeout, unit);
+ }
+ } catch (CancellationException e) {
+ throw new OperationCanceledException();
+ } catch (TimeoutException e) {
+ // fall through and cancel
+ } catch (InterruptedException e) {
+ // fall through and cancel
+ } catch (ExecutionException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else if (cause instanceof UnsupportedOperationException) {
+ throw new AuthenticatorException(cause);
+ } else if (cause instanceof AuthenticatorException) {
+ throw (AuthenticatorException) cause;
+ } else if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new IllegalStateException(cause);
+ }
+ } finally {
+ cancel(true /* interrupt if running */);
+ }
+ throw new OperationCanceledException();
+ }
+
+ public Bundle getResult()
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(null, null);
+ }
+
+ public Bundle getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(timeout, unit);
+ }
+
+ protected void done() {
+ if (mCallback != null) {
+ postToHandler(mHandler, mCallback, this);
+ }
+ }
+
+ /** Handles the responses from the AccountManager */
+ private class Response extends IAccountManagerResponse.Stub {
+ public void onResult(Bundle bundle) {
+ Intent intent = bundle.getParcelable("intent");
+ if (intent != null && mActivity != null) {
+ // since the user provided an Activity we will silently start intents
+ // that we see
+ mActivity.startActivity(intent);
+ // leave the Future running to wait for the real response to this request
+ } else if (bundle.getBoolean("retry")) {
+ try {
+ doWork();
+ } catch (RemoteException e) {
+ // this will only happen if the system process is dead, which means
+ // we will be dying ourselves
+ }
+ } else {
+ set(bundle);
+ }
+ }
+
+ public void onError(int code, String message) {
+ if (code == Constants.ERROR_CODE_CANCELED) {
+ // the authenticator indicated that this request was canceled, do so now
+ cancel(true /* mayInterruptIfRunning */);
+ return;
+ }
+ setException(convertErrorToException(code, message));
+ }
+ }
+
+ }
+
+ private abstract class BaseFutureTask<T> extends FutureTask<T> {
+ final public IAccountManagerResponse mResponse;
+ final Handler mHandler;
+
+ public BaseFutureTask(Handler handler) {
+ super(new Callable<T>() {
+ public T call() throws Exception {
+ throw new IllegalStateException("this should never be called");
+ }
+ });
+ mHandler = handler;
+ mResponse = new Response();
+ }
+
+ public abstract void doWork() throws RemoteException;
+
+ public abstract T bundleToResult(Bundle bundle) throws AuthenticatorException;
+
+ protected void postRunnableToHandler(Runnable runnable) {
+ Handler handler = (mHandler == null) ? mMainHandler : mHandler;
+ handler.post(runnable);
+ }
+
+ protected void startTask() {
+ try {
+ doWork();
+ } catch (RemoteException e) {
+ setException(e);
+ }
+ }
+
+ protected class Response extends IAccountManagerResponse.Stub {
+ public void onResult(Bundle bundle) {
+ try {
+ T result = bundleToResult(bundle);
+ if (result == null) {
+ return;
+ }
+ set(result);
+ return;
+ } catch (ClassCastException e) {
+ // we will set the exception below
+ } catch (AuthenticatorException e) {
+ // we will set the exception below
+ }
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE, "no result in response");
+ }
+
+ public void onError(int code, String message) {
+ if (code == Constants.ERROR_CODE_CANCELED) {
+ cancel(true /* mayInterruptIfRunning */);
+ return;
+ }
+ setException(convertErrorToException(code, message));
+ }
+ }
+ }
+
+ private abstract class Future2Task<T>
+ extends BaseFutureTask<T> implements AccountManagerFuture<T> {
+ final AccountManagerCallback<T> mCallback;
+ public Future2Task(Handler handler, AccountManagerCallback<T> callback) {
+ super(handler);
+ mCallback = callback;
+ }
+
+ protected void done() {
+ if (mCallback != null) {
+ postRunnableToHandler(new Runnable() {
+ public void run() {
+ mCallback.run(Future2Task.this);
+ }
+ });
+ }
+ }
+
+ public Future2Task<T> start() {
+ startTask();
+ return this;
+ }
+
+ private T internalGetResult(Long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ ensureNotOnMainThread();
+ try {
+ if (timeout == null) {
+ return get();
+ } else {
+ return get(timeout, unit);
+ }
+ } catch (InterruptedException e) {
+ // fall through and cancel
+ } catch (TimeoutException e) {
+ // fall through and cancel
+ } catch (CancellationException e) {
+ // fall through and cancel
+ } catch (ExecutionException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof IOException) {
+ throw (IOException) cause;
+ } else if (cause instanceof UnsupportedOperationException) {
+ throw new AuthenticatorException(cause);
+ } else if (cause instanceof AuthenticatorException) {
+ throw (AuthenticatorException) cause;
+ } else if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new IllegalStateException(cause);
+ }
+ } finally {
+ cancel(true /* interrupt if running */);
+ }
+ throw new OperationCanceledException();
+ }
+
+ public T getResult()
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(null, null);
+ }
+
+ public T getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ return internalGetResult(timeout, unit);
+ }
+
+ }
+
+ private Exception convertErrorToException(int code, String message) {
+ if (code == Constants.ERROR_CODE_NETWORK_ERROR) {
+ return new IOException(message);
+ }
+
+ if (code == Constants.ERROR_CODE_UNSUPPORTED_OPERATION) {
+ return new UnsupportedOperationException(message);
+ }
+
+ if (code == Constants.ERROR_CODE_INVALID_RESPONSE) {
+ return new AuthenticatorException(message);
+ }
+
+ if (code == Constants.ERROR_CODE_BAD_ARGUMENTS) {
+ return new IllegalArgumentException(message);
+ }
+
+ return new AuthenticatorException(message);
+ }
+
+ private class GetAuthTokenByTypeAndFeaturesTask
+ extends AmsTask implements AccountManagerCallback<Bundle> {
+ GetAuthTokenByTypeAndFeaturesTask(final String accountType, final String authTokenType,
+ final String[] features, Activity activityForPrompting,
+ final Bundle addAccountOptions, final Bundle loginOptions,
+ AccountManagerCallback<Bundle> callback, Handler handler) {
+ super(activityForPrompting, handler, callback);
+ if (accountType == null) throw new IllegalArgumentException("account type is null");
+ mAccountType = accountType;
+ mAuthTokenType = authTokenType;
+ mFeatures = features;
+ mAddAccountOptions = addAccountOptions;
+ mLoginOptions = loginOptions;
+ mMyCallback = this;
+ }
+ volatile AccountManagerFuture<Bundle> mFuture = null;
+ final String mAccountType;
+ final String mAuthTokenType;
+ final String[] mFeatures;
+ final Bundle mAddAccountOptions;
+ final Bundle mLoginOptions;
+ final AccountManagerCallback<Bundle> mMyCallback;
+
+ public void doWork() throws RemoteException {
+ getAccountsByTypeAndFeatures(mAccountType, mFeatures,
+ new AccountManagerCallback<Account[]>() {
+ public void run(AccountManagerFuture<Account[]> future) {
+ Account[] accounts;
+ try {
+ accounts = future.getResult();
+ } catch (OperationCanceledException e) {
+ setException(e);
+ return;
+ } catch (IOException e) {
+ setException(e);
+ return;
+ } catch (AuthenticatorException e) {
+ setException(e);
+ return;
+ }
+
+ if (accounts.length == 0) {
+ if (mActivity != null) {
+ // no accounts, add one now. pretend that the user directly
+ // made this request
+ mFuture = addAccount(mAccountType, mAuthTokenType, mFeatures,
+ mAddAccountOptions, mActivity, mMyCallback, mHandler);
+ } else {
+ // send result since we can't prompt to add an account
+ Bundle result = new Bundle();
+ result.putString(Constants.ACCOUNT_NAME_KEY, null);
+ result.putString(Constants.ACCOUNT_TYPE_KEY, null);
+ result.putString(Constants.AUTHTOKEN_KEY, null);
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this will never happen
+ }
+ // we are done
+ }
+ } else if (accounts.length == 1) {
+ // have a single account, return an authtoken for it
+ if (mActivity == null) {
+ mFuture = getAuthToken(accounts[0], mAuthTokenType,
+ false /* notifyAuthFailure */, mMyCallback, mHandler);
+ } else {
+ mFuture = getAuthToken(accounts[0],
+ mAuthTokenType, mLoginOptions,
+ mActivity, mMyCallback, mHandler);
+ }
+ } else {
+ if (mActivity != null) {
+ IAccountManagerResponse chooseResponse =
+ new IAccountManagerResponse.Stub() {
+ public void onResult(Bundle value) throws RemoteException {
+ Account account = new Account(
+ value.getString(Constants.ACCOUNT_NAME_KEY),
+ value.getString(Constants.ACCOUNT_TYPE_KEY));
+ mFuture = getAuthToken(account, mAuthTokenType, mLoginOptions,
+ mActivity, mMyCallback, mHandler);
+ }
+
+ public void onError(int errorCode, String errorMessage)
+ throws RemoteException {
+ mResponse.onError(errorCode, errorMessage);
+ }
+ };
+ // have many accounts, launch the chooser
+ Intent intent = new Intent();
+ intent.setClassName("android",
+ "android.accounts.ChooseAccountActivity");
+ intent.putExtra(Constants.ACCOUNTS_KEY, accounts);
+ intent.putExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY,
+ new AccountManagerResponse(chooseResponse));
+ mActivity.startActivity(intent);
+ // the result will arrive via the IAccountManagerResponse
+ } else {
+ // send result since we can't prompt to select an account
+ Bundle result = new Bundle();
+ result.putString(Constants.ACCOUNTS_KEY, null);
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this will never happen
+ }
+ // we are done
+ }
+ }
+ }}, mHandler);
+ }
+
+
+
+ // TODO(fredq) pass through the calls to our implemention of Future2 to the underlying
+ // future that we create. We need to do things like have cancel cancel the mFuture, if set
+ // or to cause this to be canceled if mFuture isn't set.
+ // Once this is done then getAuthTokenByFeatures can be changed to return a Future2.
+
+ public void run(AccountManagerFuture<Bundle> future) {
+ try {
+ set(future.get());
+ } catch (InterruptedException e) {
+ cancel(true);
+ } catch (CancellationException e) {
+ cancel(true);
+ } catch (ExecutionException e) {
+ setException(e.getCause());
+ }
+ }
+ }
+
+ public void getAuthTokenByFeatures(
+ final String accountType, final String authTokenType, final String[] features,
+ final Activity activityForPrompting, final Bundle addAccountOptions,
+ final Bundle loginOptions,
+ final AccountManagerCallback<Bundle> callback, final Handler handler) {
+ if (accountType == null) throw new IllegalArgumentException("account type is null");
+ if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
+ new GetAuthTokenByTypeAndFeaturesTask(accountType, authTokenType, features,
+ activityForPrompting, addAccountOptions, loginOptions, callback, handler).start();
+ }
+
+ private final HashMap<OnAccountsUpdatedListener, Handler> mAccountsUpdatedListeners =
+ Maps.newHashMap();
+
+ /**
+ * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
+ * so that it can read the updated list of accounts and send them to the listener
+ * in mAccountsUpdatedListeners.
+ */
+ private final BroadcastReceiver mAccountsChangedBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(final Context context, final Intent intent) {
+ final Account[] accounts = getAccounts();
+ // send the result to the listeners
+ synchronized (mAccountsUpdatedListeners) {
+ for (Map.Entry<OnAccountsUpdatedListener, Handler> entry :
+ mAccountsUpdatedListeners.entrySet()) {
+ postToHandler(entry.getValue(), entry.getKey(), accounts);
+ }
+ }
+ }
+ };
+
+ /**
+ * Add a {@link OnAccountsUpdatedListener} to this instance of the {@link AccountManager}.
+ * The listener is guaranteed to be invoked on the thread of the Handler that is passed
+ * in or the main thread's Handler if handler is null.
+ * @param listener the listener to add
+ * @param handler the Handler whose thread will be used to invoke the listener. If null
+ * the AccountManager context's main thread will be used.
+ * @param updateImmediately if true then the listener will be invoked as a result of this
+ * call.
+ * @throws IllegalArgumentException if listener is null
+ * @throws IllegalStateException if listener was already added
+ */
+ public void addOnAccountsUpdatedListener(final OnAccountsUpdatedListener listener,
+ Handler handler, boolean updateImmediately) {
+ if (listener == null) {
+ throw new IllegalArgumentException("the listener is null");
+ }
+ synchronized (mAccountsUpdatedListeners) {
+ if (mAccountsUpdatedListeners.containsKey(listener)) {
+ throw new IllegalStateException("this listener is already added");
+ }
+ final boolean wasEmpty = mAccountsUpdatedListeners.isEmpty();
+
+ mAccountsUpdatedListeners.put(listener, handler);
+
+ if (wasEmpty) {
+ // Register a broadcast receiver to monitor account changes
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+ mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
+ }
+ }
+
+ if (updateImmediately) {
+ postToHandler(handler, listener, getAccounts());
+ }
+ }
+
+ /**
+ * Remove an {@link OnAccountsUpdatedListener} that was previously registered with
+ * {@link #addOnAccountsUpdatedListener}.
+ * @param listener the listener to remove
+ * @throws IllegalArgumentException if listener is null
+ * @throws IllegalStateException if listener was not already added
+ */
+ public void removeOnAccountsUpdatedListener(OnAccountsUpdatedListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("the listener is null");
+ }
+ synchronized (mAccountsUpdatedListeners) {
+ if (mAccountsUpdatedListeners.remove(listener) == null) {
+ throw new IllegalStateException("this listener was not previously added");
+ }
+ if (mAccountsUpdatedListeners.isEmpty()) {
+ mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
+ }
+ }
+ }
+}
diff --git a/core/java/android/accounts/AccountManagerCallback.java b/core/java/android/accounts/AccountManagerCallback.java
new file mode 100644
index 0000000..4aa7169
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerCallback.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public interface AccountManagerCallback<V> {
+ void run(AccountManagerFuture<V> future);
+} \ No newline at end of file
diff --git a/core/java/android/accounts/AccountManagerFuture.java b/core/java/android/accounts/AccountManagerFuture.java
new file mode 100644
index 0000000..74d83eb
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerFuture.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import java.io.IOException;
+
+/**
+ * An extension of {@link java.util.concurrent.Future} that provides wrappers for {@link #get()}
+ * that handle the various
+ * exceptions that {@link #get()} may return and rethrows them as exceptions specific to
+ * {@link android.accounts.AccountManager}.
+ */
+public interface AccountManagerFuture<V> extends Future<V> {
+ /**
+ * Wrapper for {@link java.util.concurrent.Future#get()}. If the get() throws
+ * {@link InterruptedException} then the
+ * {@link AccountManagerFuture} is canceled and
+ * {@link android.accounts.OperationCanceledException} is thrown.
+ * @return the {@link android.os.Bundle} that is returned by get()
+ * @throws android.accounts.OperationCanceledException if get() throws the unchecked
+ * CancellationException
+ * or if the Future was interrupted.
+ */
+ V getResult() throws OperationCanceledException, IOException, AuthenticatorException;
+
+ /**
+ * Wrapper for {@link java.util.concurrent.Future#get()}. If the get() throws
+ * {@link InterruptedException} then the
+ * {@link AccountManagerFuture} is canceled and
+ * {@link android.accounts.OperationCanceledException} is thrown.
+ * @param timeout the maximum time to wait
+ * @param unit the time unit of the timeout argument
+ * @return the {@link android.os.Bundle} that is returned by
+ * {@link java.util.concurrent.Future#get()}
+ * @throws android.accounts.OperationCanceledException if get() throws the unchecked
+ * {@link java.util.concurrent.CancellationException} or if the {@link AccountManagerFuture}
+ * was interrupted.
+ */
+ V getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException;
+
+ /** @deprecated Use {@link #getResult} */
+ @Deprecated
+ V get() throws InterruptedException, ExecutionException;
+
+ /** @deprecated Use {@link #getResult} */
+ @Deprecated
+ V get(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException;
+} \ No newline at end of file
diff --git a/core/java/android/accounts/AccountManagerResponse.java b/core/java/android/accounts/AccountManagerResponse.java
new file mode 100644
index 0000000..25371fd
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerResponse.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * Object that wraps calls to an {@link android.accounts.IAccountManagerResponse} object.
+ * @hide
+ */
+public class AccountManagerResponse implements Parcelable {
+ private IAccountManagerResponse mResponse;
+
+ public AccountManagerResponse(IAccountManagerResponse response) {
+ mResponse = response;
+ }
+
+ public AccountManagerResponse(Parcel parcel) {
+ mResponse =
+ IAccountManagerResponse.Stub.asInterface(parcel.readStrongBinder());
+ }
+
+ public void onResult(Bundle result) {
+ try {
+ mResponse.onResult(result);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ try {
+ mResponse.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ // this should never happen
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeStrongBinder(mResponse.asBinder());
+ }
+
+ public static final Creator<AccountManagerResponse> CREATOR =
+ new Creator<AccountManagerResponse>() {
+ public AccountManagerResponse createFromParcel(Parcel source) {
+ return new AccountManagerResponse(source);
+ }
+
+ public AccountManagerResponse[] newArray(int size) {
+ return new AccountManagerResponse[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
new file mode 100644
index 0000000..8cfb758
--- /dev/null
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -0,0 +1,1622 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.RegisteredServicesCache;
+import android.content.pm.PackageInfo;
+import android.content.pm.ApplicationInfo;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Binder;
+import android.os.SystemProperties;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.app.PendingIntent;
+import android.app.NotificationManager;
+import android.app.Notification;
+import android.app.Activity;
+import android.Manifest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.R;
+
+/**
+ * A system service that provides account, password, and authtoken management for all
+ * accounts on the device. Some of these calls are implemented with the help of the corresponding
+ * {@link IAccountAuthenticator} services. This service is not accessed by users directly,
+ * instead one uses an instance of {@link AccountManager}, which can be accessed as follows:
+ * AccountManager accountManager =
+ * (AccountManager)context.getSystemService(Context.ACCOUNT_SERVICE)
+ * @hide
+ */
+public class AccountManagerService extends IAccountManager.Stub {
+ private static final String TAG = "AccountManagerService";
+
+ private static final int TIMEOUT_DELAY_MS = 1000 * 60;
+ private static final String DATABASE_NAME = "accounts.db";
+ private static final int DATABASE_VERSION = 3;
+
+ private final Context mContext;
+
+ private HandlerThread mMessageThread;
+ private final MessageHandler mMessageHandler;
+
+ // Messages that can be sent on mHandler
+ private static final int MESSAGE_TIMED_OUT = 3;
+ private static final int MESSAGE_CONNECTED = 7;
+ private static final int MESSAGE_DISCONNECTED = 8;
+
+ private final AccountAuthenticatorCache mAuthenticatorCache;
+ private final AuthenticatorBindHelper mBindHelper;
+ private final DatabaseHelper mOpenHelper;
+ private final SimWatcher mSimWatcher;
+
+ private static final String TABLE_ACCOUNTS = "accounts";
+ private static final String ACCOUNTS_ID = "_id";
+ private static final String ACCOUNTS_NAME = "name";
+ private static final String ACCOUNTS_TYPE = "type";
+ private static final String ACCOUNTS_PASSWORD = "password";
+
+ private static final String TABLE_AUTHTOKENS = "authtokens";
+ private static final String AUTHTOKENS_ID = "_id";
+ private static final String AUTHTOKENS_ACCOUNTS_ID = "accounts_id";
+ private static final String AUTHTOKENS_TYPE = "type";
+ private static final String AUTHTOKENS_AUTHTOKEN = "authtoken";
+
+ private static final String TABLE_GRANTS = "grants";
+ private static final String GRANTS_ACCOUNTS_ID = "accounts_id";
+ private static final String GRANTS_AUTH_TOKEN_TYPE = "auth_token_type";
+ private static final String GRANTS_GRANTEE_UID = "uid";
+
+ private static final String TABLE_EXTRAS = "extras";
+ private static final String EXTRAS_ID = "_id";
+ private static final String EXTRAS_ACCOUNTS_ID = "accounts_id";
+ private static final String EXTRAS_KEY = "key";
+ private static final String EXTRAS_VALUE = "value";
+
+ private static final String TABLE_META = "meta";
+ private static final String META_KEY = "key";
+ private static final String META_VALUE = "value";
+
+ private static final String[] ACCOUNT_NAME_TYPE_PROJECTION =
+ new String[]{ACCOUNTS_ID, ACCOUNTS_NAME, ACCOUNTS_TYPE};
+ private static final Intent ACCOUNTS_CHANGED_INTENT;
+
+ private static final String COUNT_OF_MATCHING_GRANTS = ""
+ + "SELECT COUNT(*) FROM " + TABLE_GRANTS + ", " + TABLE_ACCOUNTS
+ + " WHERE " + GRANTS_ACCOUNTS_ID + "=" + ACCOUNTS_ID
+ + " AND " + GRANTS_GRANTEE_UID + "=?"
+ + " AND " + GRANTS_AUTH_TOKEN_TYPE + "=?"
+ + " AND " + ACCOUNTS_NAME + "=?"
+ + " AND " + ACCOUNTS_TYPE + "=?";
+
+ private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<String, Session>();
+ private final AtomicInteger mNotificationIds = new AtomicInteger(1);
+
+ private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
+ mCredentialsPermissionNotificationIds =
+ new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
+ private final HashMap<Account, Integer> mSigninRequiredNotificationIds =
+ new HashMap<Account, Integer>();
+ private static AtomicReference<AccountManagerService> sThis =
+ new AtomicReference<AccountManagerService>();
+
+ private static final boolean isDebuggableMonkeyBuild =
+ SystemProperties.getBoolean("ro.monkey", false)
+ && SystemProperties.getBoolean("ro.debuggable", false);
+
+ static {
+ ACCOUNTS_CHANGED_INTENT = new Intent(Constants.LOGIN_ACCOUNTS_CHANGED_ACTION);
+ ACCOUNTS_CHANGED_INTENT.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ }
+
+ /**
+ * This should only be called by system code. One should only call this after the service
+ * has started.
+ * @return a reference to the AccountManagerService instance
+ * @hide
+ */
+ public static AccountManagerService getSingleton() {
+ return sThis.get();
+ }
+
+ public class AuthTokenKey {
+ public final Account mAccount;
+ public final String mAuthTokenType;
+ private final int mHashCode;
+
+ public AuthTokenKey(Account account, String authTokenType) {
+ mAccount = account;
+ mAuthTokenType = authTokenType;
+ mHashCode = computeHashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof AuthTokenKey)) {
+ return false;
+ }
+ AuthTokenKey other = (AuthTokenKey)o;
+ if (!mAccount.equals(other.mAccount)) {
+ return false;
+ }
+ return (mAuthTokenType == null)
+ ? other.mAuthTokenType == null
+ : mAuthTokenType.equals(other.mAuthTokenType);
+ }
+
+ private int computeHashCode() {
+ int result = 17;
+ result = 31 * result + mAccount.hashCode();
+ result = 31 * result + ((mAuthTokenType == null) ? 0 : mAuthTokenType.hashCode());
+ return result;
+ }
+
+ public int hashCode() {
+ return mHashCode;
+ }
+ }
+
+ public AccountManagerService(Context context) {
+ mContext = context;
+
+ mOpenHelper = new DatabaseHelper(mContext);
+
+ mMessageThread = new HandlerThread("AccountManagerService");
+ mMessageThread.start();
+ mMessageHandler = new MessageHandler(mMessageThread.getLooper());
+
+ mAuthenticatorCache = new AccountAuthenticatorCache(mContext);
+ mBindHelper = new AuthenticatorBindHelper(mContext, mAuthenticatorCache, mMessageHandler,
+ MESSAGE_CONNECTED, MESSAGE_DISCONNECTED);
+
+ mSimWatcher = new SimWatcher(mContext);
+ sThis.set(this);
+ }
+
+ public String getPassword(Account account) {
+ checkAuthenticateAccountsPermission(account);
+
+ long identityToken = clearCallingIdentity();
+ try {
+ return readPasswordFromDatabase(account);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private String readPasswordFromDatabase(Account account) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_PASSWORD},
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[]{account.name, account.type}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ }
+ return null;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public String getUserData(Account account, String key) {
+ checkAuthenticateAccountsPermission(account);
+ long identityToken = clearCallingIdentity();
+ try {
+ return readUserDataFromDatabase(account, key);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private String readUserDataFromDatabase(Account account, String key) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId < 0) {
+ return null;
+ }
+ Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_VALUE},
+ EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
+ new String[]{key}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ }
+ return null;
+ } finally {
+ cursor.close();
+ }
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public AuthenticatorDescription[] getAuthenticatorTypes() {
+ long identityToken = clearCallingIdentity();
+ try {
+ Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>>
+ authenticatorCollection = mAuthenticatorCache.getAllServices();
+ AuthenticatorDescription[] types =
+ new AuthenticatorDescription[authenticatorCollection.size()];
+ int i = 0;
+ for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator
+ : authenticatorCollection) {
+ types[i] = authenticator.type;
+ i++;
+ }
+ return types;
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public Account[] getAccountsByType(String accountType) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+
+ final String selection = accountType == null ? null : (ACCOUNTS_TYPE + "=?");
+ final String[] selectionArgs = accountType == null ? null : new String[]{accountType};
+ Cursor cursor = db.query(TABLE_ACCOUNTS, ACCOUNT_NAME_TYPE_PROJECTION,
+ selection, selectionArgs, null, null, null);
+ try {
+ int i = 0;
+ Account[] accounts = new Account[cursor.getCount()];
+ while (cursor.moveToNext()) {
+ accounts[i] = new Account(cursor.getString(1), cursor.getString(2));
+ i++;
+ }
+ return accounts;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public boolean addAccount(Account account, String password, Bundle extras) {
+ checkAuthenticateAccountsPermission(account);
+
+ // fails if the account already exists
+ long identityToken = clearCallingIdentity();
+ try {
+ return insertAccountIntoDatabase(account, password, extras);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private boolean insertAccountIntoDatabase(Account account, String password, Bundle extras) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long numMatches = DatabaseUtils.longForQuery(db,
+ "select count(*) from " + TABLE_ACCOUNTS
+ + " WHERE " + ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[]{account.name, account.type});
+ if (numMatches > 0) {
+ return false;
+ }
+ ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_NAME, account.name);
+ values.put(ACCOUNTS_TYPE, account.type);
+ values.put(ACCOUNTS_PASSWORD, password);
+ long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+ if (accountId < 0) {
+ return false;
+ }
+ if (extras != null) {
+ for (String key : extras.keySet()) {
+ final String value = extras.getString(key);
+ if (insertExtra(db, accountId, key, value) < 0) {
+ return false;
+ }
+ }
+ }
+ db.setTransactionSuccessful();
+ sendAccountsChangedBroadcast();
+ return true;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
+ ContentValues values = new ContentValues();
+ values.put(EXTRAS_KEY, key);
+ values.put(EXTRAS_ACCOUNTS_ID, accountId);
+ values.put(EXTRAS_VALUE, value);
+ return db.insert(TABLE_EXTRAS, EXTRAS_KEY, values);
+ }
+
+ public void removeAccount(IAccountManagerResponse response, Account account) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new RemoveAccountSession(response, account).bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private class RemoveAccountSession extends Session {
+ final Account mAccount;
+ public RemoveAccountSession(IAccountManagerResponse response, Account account) {
+ super(response, account.type, false /* expectActivityLaunch */);
+ mAccount = account;
+ }
+
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", removeAccount"
+ + ", account " + mAccount;
+ }
+
+ public void run() throws RemoteException {
+ mAuthenticator.getAccountRemovalAllowed(this, mAccount);
+ }
+
+ public void onResult(Bundle result) {
+ if (result != null && result.containsKey(Constants.BOOLEAN_RESULT_KEY)
+ && !result.containsKey(Constants.INTENT_KEY)) {
+ final boolean removalAllowed = result.getBoolean(Constants.BOOLEAN_RESULT_KEY);
+ if (removalAllowed) {
+ removeAccount(mAccount);
+ }
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ Bundle result2 = new Bundle();
+ result2.putBoolean(Constants.BOOLEAN_RESULT_KEY, removalAllowed);
+ try {
+ response.onResult(result2);
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ }
+ super.onResult(result);
+ }
+ }
+
+ private void removeAccount(Account account) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.delete(TABLE_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[]{account.name, account.type});
+ sendAccountsChangedBroadcast();
+ }
+
+ public void invalidateAuthToken(String accountType, String authToken) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ invalidateAuthToken(db, accountType, authToken);
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void invalidateAuthToken(SQLiteDatabase db, String accountType, String authToken) {
+ if (authToken == null || accountType == null) {
+ return;
+ }
+ Cursor cursor = db.rawQuery(
+ "SELECT " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+ + ", " + TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+ + ", " + TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+ + " FROM " + TABLE_ACCOUNTS
+ + " JOIN " + TABLE_AUTHTOKENS
+ + " ON " + TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+ + " = " + AUTHTOKENS_ACCOUNTS_ID
+ + " WHERE " + AUTHTOKENS_AUTHTOKEN + " = ? AND "
+ + TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+ new String[]{authToken, accountType});
+ try {
+ while (cursor.moveToNext()) {
+ long authTokenId = cursor.getLong(0);
+ String accountName = cursor.getString(1);
+ String authTokenType = cursor.getString(2);
+ db.delete(TABLE_AUTHTOKENS, AUTHTOKENS_ID + "=" + authTokenId, null);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private boolean saveAuthTokenToDatabase(Account account, String type, String authToken) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId < 0) {
+ return false;
+ }
+ db.delete(TABLE_AUTHTOKENS,
+ AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+ new String[]{type});
+ ContentValues values = new ContentValues();
+ values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
+ values.put(AUTHTOKENS_TYPE, type);
+ values.put(AUTHTOKENS_AUTHTOKEN, authToken);
+ if (db.insert(TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values) >= 0) {
+ db.setTransactionSuccessful();
+ return true;
+ }
+ return false;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public String readAuthTokenFromDatabase(Account account, String authTokenType) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId < 0) {
+ return null;
+ }
+ return getAuthToken(db, accountId, authTokenType);
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ public String peekAuthToken(Account account, String authTokenType) {
+ checkAuthenticateAccountsPermission(account);
+ long identityToken = clearCallingIdentity();
+ try {
+ return readAuthTokenFromDatabase(account, authTokenType);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void setAuthToken(Account account, String authTokenType, String authToken) {
+ checkAuthenticateAccountsPermission(account);
+ long identityToken = clearCallingIdentity();
+ try {
+ cacheAuthToken(account, authTokenType, authToken);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void setPassword(Account account, String password) {
+ checkAuthenticateAccountsPermission(account);
+ long identityToken = clearCallingIdentity();
+ try {
+ ContentValues values = new ContentValues();
+ values.put(ACCOUNTS_PASSWORD, password);
+ mOpenHelper.getWritableDatabase().update(TABLE_ACCOUNTS, values,
+ ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE+ "=?",
+ new String[]{account.name, account.type});
+ sendAccountsChangedBroadcast();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void sendAccountsChangedBroadcast() {
+ mContext.sendBroadcast(ACCOUNTS_CHANGED_INTENT);
+ }
+
+ public void clearPassword(Account account) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ setPassword(account, null);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void sendResult(IAccountManagerResponse response, Bundle bundle) {
+ if (response != null) {
+ try {
+ response.onResult(bundle);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+ }
+
+ public void setUserData(Account account, String key, String value) {
+ checkAuthenticateAccountsPermission(account);
+ long identityToken = clearCallingIdentity();
+ try {
+ writeUserdataIntoDatabase(account, key, value);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void writeUserdataIntoDatabase(Account account, String key, String value) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId < 0) {
+ return;
+ }
+ long extrasId = getExtrasId(db, accountId, key);
+ if (extrasId < 0 ) {
+ extrasId = insertExtra(db, accountId, key, value);
+ if (extrasId < 0) {
+ return;
+ }
+ } else {
+ ContentValues values = new ContentValues();
+ values.put(EXTRAS_VALUE, value);
+ if (1 != db.update(TABLE_EXTRAS, values, EXTRAS_ID + "=" + extrasId, null)) {
+ return;
+ }
+
+ }
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+
+ private void onResult(IAccountManagerResponse response, Bundle result) {
+ try {
+ response.onResult(result);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote
+ // exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+
+ public void getAuthToken(IAccountManagerResponse response, final Account account,
+ final String authTokenType, final boolean notifyOnAuthFailure,
+ final boolean expectActivityLaunch, final Bundle loginOptions) {
+ checkBinderPermission(Manifest.permission.USE_CREDENTIALS);
+ final int callerUid = Binder.getCallingUid();
+ final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid);
+
+ long identityToken = clearCallingIdentity();
+ try {
+ // if the caller has permission, do the peek. otherwise go the more expensive
+ // route of starting a Session
+ if (permissionGranted) {
+ String authToken = readAuthTokenFromDatabase(account, authTokenType);
+ if (authToken != null) {
+ Bundle result = new Bundle();
+ result.putString(Constants.AUTHTOKEN_KEY, authToken);
+ result.putString(Constants.ACCOUNT_NAME_KEY, account.name);
+ result.putString(Constants.ACCOUNT_TYPE_KEY, account.type);
+ onResult(response, result);
+ return;
+ }
+ }
+
+ new Session(response, account.type, expectActivityLaunch) {
+ protected String toDebugString(long now) {
+ if (loginOptions != null) loginOptions.keySet();
+ return super.toDebugString(now) + ", getAuthToken"
+ + ", " + account
+ + ", authTokenType " + authTokenType
+ + ", loginOptions " + loginOptions
+ + ", notifyOnAuthFailure " + notifyOnAuthFailure;
+ }
+
+ public void run() throws RemoteException {
+ // If the caller doesn't have permission then create and return the
+ // "grant permission" intent instead of the "getAuthToken" intent.
+ if (!permissionGranted) {
+ mAuthenticator.getAuthTokenLabel(this, authTokenType);
+ } else {
+ mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
+ }
+ }
+
+ public void onResult(Bundle result) {
+ if (result != null) {
+ if (result.containsKey(Constants.AUTH_TOKEN_LABEL_KEY)) {
+ Intent intent = newGrantCredentialsPermissionIntent(account, callerUid,
+ new AccountAuthenticatorResponse(this),
+ authTokenType,
+ result.getString(Constants.AUTH_TOKEN_LABEL_KEY));
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(Constants.INTENT_KEY, intent);
+ onResult(bundle);
+ return;
+ }
+ String authToken = result.getString(Constants.AUTHTOKEN_KEY);
+ if (authToken != null) {
+ String name = result.getString(Constants.ACCOUNT_NAME_KEY);
+ String type = result.getString(Constants.ACCOUNT_TYPE_KEY);
+ if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+ "the type and name should not be empty");
+ return;
+ }
+ cacheAuthToken(new Account(name, type), authTokenType, authToken);
+ }
+
+ Intent intent = result.getParcelable(Constants.INTENT_KEY);
+ if (intent != null && notifyOnAuthFailure) {
+ doNotification(
+ account, result.getString(Constants.AUTH_FAILED_MESSAGE_KEY),
+ intent);
+ }
+ }
+ super.onResult(result);
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void createNoCredentialsPermissionNotification(Account account, Intent intent) {
+ int uid = intent.getIntExtra(
+ GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, -1);
+ String authTokenType = intent.getStringExtra(
+ GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE);
+ String authTokenLabel = intent.getStringExtra(
+ GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL);
+
+ Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
+ 0 /* when */);
+ final CharSequence subtitleFormatString =
+ mContext.getText(R.string.permission_request_notification_subtitle);
+ n.setLatestEventInfo(mContext,
+ mContext.getText(R.string.permission_request_notification_title),
+ String.format(subtitleFormatString.toString(), account.name),
+ PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(getCredentialPermissionNotificationId(account, authTokenType, uid), n);
+ }
+
+ private Intent newGrantCredentialsPermissionIntent(Account account, int uid,
+ AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) {
+ RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(account.type));
+ if (serviceInfo == null) {
+ throw new IllegalArgumentException("unknown account type: " + account.type);
+ }
+
+ final Context authContext;
+ try {
+ authContext = mContext.createPackageContext(
+ serviceInfo.type.packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("unknown account type: " + account.type);
+ }
+
+ Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.addCategory(
+ String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid)));
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT, account);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_LABEL, authTokenLabel);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_AUTH_TOKEN_TYPE, authTokenType);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_RESPONSE, response);
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_ACCOUNT_TYPE_LABEL,
+ authContext.getString(serviceInfo.type.labelId));
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_PACKAGES,
+ mContext.getPackageManager().getPackagesForUid(uid));
+ intent.putExtra(GrantCredentialsPermissionActivity.EXTRAS_REQUESTING_UID, uid);
+ return intent;
+ }
+
+ private Integer getCredentialPermissionNotificationId(Account account, String authTokenType,
+ int uid) {
+ Integer id;
+ synchronized(mCredentialsPermissionNotificationIds) {
+ final Pair<Pair<Account, String>, Integer> key =
+ new Pair<Pair<Account, String>, Integer>(
+ new Pair<Account, String>(account, authTokenType), uid);
+ id = mCredentialsPermissionNotificationIds.get(key);
+ if (id == null) {
+ id = mNotificationIds.incrementAndGet();
+ mCredentialsPermissionNotificationIds.put(key, id);
+ }
+ }
+ return id;
+ }
+
+ private Integer getSigninRequiredNotificationId(Account account) {
+ Integer id;
+ synchronized(mSigninRequiredNotificationIds) {
+ id = mSigninRequiredNotificationIds.get(account);
+ if (id == null) {
+ id = mNotificationIds.incrementAndGet();
+ mSigninRequiredNotificationIds.put(account, id);
+ }
+ }
+ return id;
+ }
+
+
+ public void addAcount(final IAccountManagerResponse response, final String accountType,
+ final String authTokenType, final String[] requiredFeatures,
+ final boolean expectActivityLaunch, final Bundle options) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new Session(response, accountType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
+ options);
+ }
+
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", addAccount"
+ + ", accountType " + accountType
+ + ", requiredFeatures "
+ + (requiredFeatures != null
+ ? TextUtils.join(",", requiredFeatures)
+ : null);
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void confirmCredentials(IAccountManagerResponse response,
+ final Account account, final boolean expectActivityLaunch) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new Session(response, account.type, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.confirmCredentials(this, account);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", confirmCredentials"
+ + ", " + account;
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void confirmPassword(IAccountManagerResponse response, final Account account,
+ final String password) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new Session(response, account.type, false /* expectActivityLaunch */) {
+ public void run() throws RemoteException {
+ mAuthenticator.confirmPassword(this, account, password);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", confirmPassword"
+ + ", " + account;
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void updateCredentials(IAccountManagerResponse response, final Account account,
+ final String authTokenType, final boolean expectActivityLaunch,
+ final Bundle loginOptions) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new Session(response, account.type, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
+ }
+ protected String toDebugString(long now) {
+ if (loginOptions != null) loginOptions.keySet();
+ return super.toDebugString(now) + ", updateCredentials"
+ + ", " + account
+ + ", authTokenType " + authTokenType
+ + ", loginOptions " + loginOptions;
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void editProperties(IAccountManagerResponse response, final String accountType,
+ final boolean expectActivityLaunch) {
+ checkManageAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ new Session(response, accountType, expectActivityLaunch) {
+ public void run() throws RemoteException {
+ mAuthenticator.editProperties(this, mAccountType);
+ }
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", editProperties"
+ + ", accountType " + accountType;
+ }
+ }.bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private class GetAccountsByTypeAndFeatureSession extends Session {
+ private final String[] mFeatures;
+ private volatile Account[] mAccountsOfType = null;
+ private volatile ArrayList<Account> mAccountsWithFeatures = null;
+ private volatile int mCurrentAccount = 0;
+
+ public GetAccountsByTypeAndFeatureSession(IAccountManagerResponse response,
+ String type, String[] features) {
+ super(response, type, false /* expectActivityLaunch */);
+ mFeatures = features;
+ }
+
+ public void run() throws RemoteException {
+ mAccountsOfType = getAccountsByType(mAccountType);
+ // check whether each account matches the requested features
+ mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
+ mCurrentAccount = 0;
+
+ checkAccount();
+ }
+
+ public void checkAccount() {
+ if (mCurrentAccount >= mAccountsOfType.length) {
+ sendResult();
+ return;
+ }
+
+ try {
+ mAuthenticator.hasFeatures(this, mAccountsOfType[mCurrentAccount], mFeatures);
+ } catch (RemoteException e) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "remote exception");
+ }
+ }
+
+ public void onResult(Bundle result) {
+ mNumResults++;
+ if (result == null) {
+ onError(Constants.ERROR_CODE_INVALID_RESPONSE, "null bundle");
+ return;
+ }
+ if (result.getBoolean(Constants.BOOLEAN_RESULT_KEY, false)) {
+ mAccountsWithFeatures.add(mAccountsOfType[mCurrentAccount]);
+ }
+ mCurrentAccount++;
+ checkAccount();
+ }
+
+ public void sendResult() {
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ try {
+ Account[] accounts = new Account[mAccountsWithFeatures.size()];
+ for (int i = 0; i < accounts.length; i++) {
+ accounts[i] = mAccountsWithFeatures.get(i);
+ }
+ Bundle result = new Bundle();
+ result.putParcelableArray(Constants.ACCOUNTS_KEY, accounts);
+ response.onResult(result);
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+ }
+
+
+ protected String toDebugString(long now) {
+ return super.toDebugString(now) + ", getAccountsByTypeAndFeatures"
+ + ", " + (mFeatures != null ? TextUtils.join(",", mFeatures) : null);
+ }
+ }
+
+ public Account[] getAccounts(String type) {
+ checkReadAccountsPermission();
+ long identityToken = clearCallingIdentity();
+ try {
+ return getAccountsByType(type);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public void getAccountsByFeatures(IAccountManagerResponse response,
+ String type, String[] features) {
+ checkReadAccountsPermission();
+ if (features != null && type == null) {
+ if (response != null) {
+ try {
+ response.onError(Constants.ERROR_CODE_BAD_ARGUMENTS, "type is null");
+ } catch (RemoteException e) {
+ // ignore this
+ }
+ }
+ return;
+ }
+ long identityToken = clearCallingIdentity();
+ try {
+ if (features == null || features.length == 0) {
+ getAccountsByType(type);
+ return;
+ }
+ new GetAccountsByTypeAndFeatureSession(response, type, features).bind();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private boolean cacheAuthToken(Account account, String authTokenType, String authToken) {
+ return saveAuthTokenToDatabase(account, authTokenType, authToken);
+ }
+
+ private long getAccountId(SQLiteDatabase db, Account account) {
+ Cursor cursor = db.query(TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
+ "name=? AND type=?", new String[]{account.name, account.type}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getLong(0);
+ }
+ return -1;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private long getExtrasId(SQLiteDatabase db, long accountId, String key) {
+ Cursor cursor = db.query(TABLE_EXTRAS, new String[]{EXTRAS_ID},
+ EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
+ new String[]{key}, null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getLong(0);
+ }
+ return -1;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private String getAuthToken(SQLiteDatabase db, long accountId, String authTokenType) {
+ Cursor cursor = db.query(TABLE_AUTHTOKENS, new String[]{AUTHTOKENS_AUTHTOKEN},
+ AUTHTOKENS_ACCOUNTS_ID + "=" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+ new String[]{authTokenType},
+ null, null, null);
+ try {
+ if (cursor.moveToNext()) {
+ return cursor.getString(0);
+ }
+ return null;
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private abstract class Session extends IAccountAuthenticatorResponse.Stub
+ implements AuthenticatorBindHelper.Callback, IBinder.DeathRecipient {
+ IAccountManagerResponse mResponse;
+ final String mAccountType;
+ final boolean mExpectActivityLaunch;
+ final long mCreationTime;
+
+ public int mNumResults = 0;
+ private int mNumRequestContinued = 0;
+ private int mNumErrors = 0;
+
+
+ IAccountAuthenticator mAuthenticator = null;
+
+ public Session(IAccountManagerResponse response, String accountType,
+ boolean expectActivityLaunch) {
+ super();
+ if (response == null) throw new IllegalArgumentException("response is null");
+ if (accountType == null) throw new IllegalArgumentException("accountType is null");
+ mResponse = response;
+ mAccountType = accountType;
+ mExpectActivityLaunch = expectActivityLaunch;
+ mCreationTime = SystemClock.elapsedRealtime();
+ synchronized (mSessions) {
+ mSessions.put(toString(), this);
+ }
+ try {
+ response.asBinder().linkToDeath(this, 0 /* flags */);
+ } catch (RemoteException e) {
+ mResponse = null;
+ binderDied();
+ }
+ }
+
+ IAccountManagerResponse getResponseAndClose() {
+ if (mResponse == null) {
+ // this session has already been closed
+ return null;
+ }
+ IAccountManagerResponse response = mResponse;
+ close(); // this clears mResponse so we need to save the response before this call
+ return response;
+ }
+
+ private void close() {
+ synchronized (mSessions) {
+ if (mSessions.remove(toString()) == null) {
+ // the session was already closed, so bail out now
+ return;
+ }
+ }
+ if (mResponse != null) {
+ // stop listening for response deaths
+ mResponse.asBinder().unlinkToDeath(this, 0 /* flags */);
+
+ // clear this so that we don't accidentally send any further results
+ mResponse = null;
+ }
+ cancelTimeout();
+ unbind();
+ }
+
+ public void binderDied() {
+ mResponse = null;
+ close();
+ }
+
+ protected String toDebugString() {
+ return toDebugString(SystemClock.elapsedRealtime());
+ }
+
+ protected String toDebugString(long now) {
+ return "Session: expectLaunch " + mExpectActivityLaunch
+ + ", connected " + (mAuthenticator != null)
+ + ", stats (" + mNumResults + "/" + mNumRequestContinued
+ + "/" + mNumErrors + ")"
+ + ", lifetime " + ((now - mCreationTime) / 1000.0);
+ }
+
+ void bind() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "initiating bind to authenticator type " + mAccountType);
+ }
+ if (!mBindHelper.bind(mAccountType, this)) {
+ Log.d(TAG, "bind attempt failed for " + toDebugString());
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION, "bind failure");
+ }
+ }
+
+ private void unbind() {
+ if (mAuthenticator != null) {
+ mAuthenticator = null;
+ mBindHelper.unbind(this);
+ }
+ }
+
+ public void scheduleTimeout() {
+ mMessageHandler.sendMessageDelayed(
+ mMessageHandler.obtainMessage(MESSAGE_TIMED_OUT, this), TIMEOUT_DELAY_MS);
+ }
+
+ public void cancelTimeout() {
+ mMessageHandler.removeMessages(MESSAGE_TIMED_OUT, this);
+ }
+
+ public void onConnected(IBinder service) {
+ mAuthenticator = IAccountAuthenticator.Stub.asInterface(service);
+ try {
+ run();
+ } catch (RemoteException e) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "remote exception");
+ }
+ }
+
+ public abstract void run() throws RemoteException;
+
+ public void onDisconnected() {
+ mAuthenticator = null;
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "disconnected");
+ }
+ }
+
+ public void onTimedOut() {
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ onError(Constants.ERROR_CODE_REMOTE_EXCEPTION,
+ "timeout");
+ }
+ }
+
+ public void onResult(Bundle result) {
+ mNumResults++;
+ if (result != null && !TextUtils.isEmpty(result.getString(Constants.AUTHTOKEN_KEY))) {
+ String accountName = result.getString(Constants.ACCOUNT_NAME_KEY);
+ String accountType = result.getString(Constants.ACCOUNT_TYPE_KEY);
+ if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
+ Account account = new Account(accountName, accountType);
+ cancelNotification(getSigninRequiredNotificationId(account));
+ }
+ }
+ IAccountManagerResponse response;
+ if (mExpectActivityLaunch && result != null
+ && result.containsKey(Constants.INTENT_KEY)) {
+ response = mResponse;
+ } else {
+ response = getResponseAndClose();
+ }
+ if (response != null) {
+ try {
+ if (result == null) {
+ response.onError(Constants.ERROR_CODE_INVALID_RESPONSE,
+ "null bundle returned");
+ } else {
+ response.onResult(result);
+ }
+ } catch (RemoteException e) {
+ // if the caller is dead then there is no one to care about remote exceptions
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "failure while notifying response", e);
+ }
+ }
+ }
+ }
+
+ public void onRequestContinued() {
+ mNumRequestContinued++;
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ mNumErrors++;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: " + errorCode + ", " + errorMessage);
+ }
+ IAccountManagerResponse response = getResponseAndClose();
+ if (response != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: responding");
+ }
+ try {
+ response.onError(errorCode, errorMessage);
+ } catch (RemoteException e) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: caught RemoteException while responding", e);
+ }
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Session.onError: already closed");
+ }
+ }
+ }
+ }
+
+ private class MessageHandler extends Handler {
+ MessageHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ if (mBindHelper.handleMessage(msg)) {
+ return;
+ }
+ switch (msg.what) {
+ case MESSAGE_TIMED_OUT:
+ Session session = (Session)msg.obj;
+ session.onTimedOut();
+ break;
+
+ default:
+ throw new IllegalStateException("unhandled message: " + msg.what);
+ }
+ }
+ }
+
+ private class DatabaseHelper extends SQLiteOpenHelper {
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_ACCOUNTS + " ( "
+ + ACCOUNTS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ACCOUNTS_NAME + " TEXT NOT NULL, "
+ + ACCOUNTS_TYPE + " TEXT NOT NULL, "
+ + ACCOUNTS_PASSWORD + " TEXT, "
+ + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " ( "
+ + AUTHTOKENS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + AUTHTOKENS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + AUTHTOKENS_TYPE + " TEXT NOT NULL, "
+ + AUTHTOKENS_AUTHTOKEN + " TEXT, "
+ + "UNIQUE (" + AUTHTOKENS_ACCOUNTS_ID + "," + AUTHTOKENS_TYPE + "))");
+
+ createGrantsTable(db);
+
+ db.execSQL("CREATE TABLE " + TABLE_EXTRAS + " ( "
+ + EXTRAS_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + EXTRAS_ACCOUNTS_ID + " INTEGER, "
+ + EXTRAS_KEY + " TEXT NOT NULL, "
+ + EXTRAS_VALUE + " TEXT, "
+ + "UNIQUE(" + EXTRAS_ACCOUNTS_ID + "," + EXTRAS_KEY + "))");
+
+ db.execSQL("CREATE TABLE " + TABLE_META + " ( "
+ + META_KEY + " TEXT PRIMARY KEY NOT NULL, "
+ + META_VALUE + " TEXT)");
+
+ createAccountsDeletionTrigger(db);
+ }
+
+ private void createAccountsDeletionTrigger(SQLiteDatabase db) {
+ db.execSQL(""
+ + " CREATE TRIGGER " + TABLE_ACCOUNTS + "Delete DELETE ON " + TABLE_ACCOUNTS
+ + " BEGIN"
+ + " DELETE FROM " + TABLE_AUTHTOKENS
+ + " WHERE " + AUTHTOKENS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_EXTRAS
+ + " WHERE " + EXTRAS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " DELETE FROM " + TABLE_GRANTS
+ + " WHERE " + GRANTS_ACCOUNTS_ID + "=OLD." + ACCOUNTS_ID + " ;"
+ + " END");
+ }
+
+ private void createGrantsTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_GRANTS + " ( "
+ + GRANTS_ACCOUNTS_ID + " INTEGER NOT NULL, "
+ + GRANTS_AUTH_TOKEN_TYPE + " STRING NOT NULL, "
+ + GRANTS_GRANTEE_UID + " INTEGER NOT NULL, "
+ + "UNIQUE (" + GRANTS_ACCOUNTS_ID + "," + GRANTS_AUTH_TOKEN_TYPE
+ + "," + GRANTS_GRANTEE_UID + "))");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ Log.e(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
+
+ if (oldVersion == 1) {
+ // no longer need to do anything since the work is done
+ // when upgrading from version 2
+ oldVersion++;
+ }
+
+ if (oldVersion == 2) {
+ createGrantsTable(db);
+ db.execSQL("DROP TRIGGER " + TABLE_ACCOUNTS + "Delete");
+ createAccountsDeletionTrigger(db);
+ oldVersion++;
+ }
+ }
+
+ @Override
+ public void onOpen(SQLiteDatabase db) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "opened database " + DATABASE_NAME);
+ }
+ }
+
+ private void setMetaValue(String key, String value) {
+ ContentValues values = new ContentValues();
+ values.put(META_KEY, key);
+ values.put(META_VALUE, value);
+ mOpenHelper.getWritableDatabase().replace(TABLE_META, META_KEY, values);
+ }
+
+ private String getMetaValue(String key) {
+ Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_META,
+ new String[]{META_VALUE}, META_KEY + "=?", new String[]{key}, null, null, null);
+ try {
+ if (c.moveToNext()) {
+ return c.getString(0);
+ }
+ return null;
+ } finally {
+ c.close();
+ }
+ }
+
+ private class SimWatcher extends BroadcastReceiver {
+ public SimWatcher(Context context) {
+ // Re-scan the SIM card when the SIM state changes, and also if
+ // the disk recovers from a full state (we may have failed to handle
+ // things properly while the disk was full).
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+ context.registerReceiver(this, filter);
+ }
+
+ /**
+ * Compare the IMSI to the one stored in the login service's
+ * database. If they differ, erase all passwords and
+ * authtokens (and store the new IMSI).
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Check IMSI on every update; nothing happens if the IMSI is missing or unchanged.
+ String imsi = ((TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE)).getSubscriberId();
+ if (TextUtils.isEmpty(imsi)) return;
+
+ String storedImsi = getMetaValue("imsi");
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "current IMSI=" + imsi + "; stored IMSI=" + storedImsi);
+ }
+
+ if (!imsi.equals(storedImsi) && !TextUtils.isEmpty(storedImsi)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "wiping all passwords and authtokens");
+ }
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ db.execSQL("DELETE from " + TABLE_AUTHTOKENS);
+ db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''");
+ sendAccountsChangedBroadcast();
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ }
+ setMetaValue("imsi", imsi);
+ }
+ }
+
+ public IBinder onBind(Intent intent) {
+ return asBinder();
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ synchronized (mSessions) {
+ final long now = SystemClock.elapsedRealtime();
+ fout.println("AccountManagerService: " + mSessions.size() + " sessions");
+ for (Session session : mSessions.values()) {
+ fout.println(" " + session.toDebugString(now));
+ }
+ }
+
+ fout.println();
+
+ mAuthenticatorCache.dump(fd, fout, args);
+ }
+
+ private void doNotification(Account account, CharSequence message, Intent intent) {
+ long identityToken = clearCallingIdentity();
+ try {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "doNotification: " + message + " intent:" + intent);
+ }
+
+ if (intent.getComponent() != null &&
+ GrantCredentialsPermissionActivity.class.getName().equals(
+ intent.getComponent().getClassName())) {
+ createNoCredentialsPermissionNotification(account, intent);
+ } else {
+ Notification n = new Notification(android.R.drawable.stat_sys_warning, null,
+ 0 /* when */);
+ n.setLatestEventInfo(mContext, mContext.getText(R.string.notification_title),
+ message, PendingIntent.getActivity(
+ mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT));
+ ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(getSigninRequiredNotificationId(account), n);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void cancelNotification(int id) {
+ long identityToken = clearCallingIdentity();
+ try {
+ ((NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+ .cancel(id);
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ private void checkBinderPermission(String permission) {
+ final int uid = Binder.getCallingUid();
+ if (mContext.checkCallingOrSelfPermission(permission) !=
+ PackageManager.PERMISSION_GRANTED) {
+ String msg = "caller uid " + uid + " lacks " + permission;
+ Log.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "caller uid " + uid + " has " + permission);
+ }
+ }
+
+ private boolean inSystemImage(int callerUid) {
+ String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid);
+ for (String name : packages) {
+ try {
+ PackageInfo packageInfo =
+ mContext.getPackageManager().getPackageInfo(name, 0 /* flags */);
+ if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) {
+ final boolean fromAuthenticator = hasAuthenticatorUid(account.type, callerUid);
+ final boolean hasExplicitGrants = hasExplicitlyGrantedPermission(account, authTokenType);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid "
+ + callerUid + ", account " + account
+ + ": is authenticator? " + fromAuthenticator
+ + ", has explicit permission? " + hasExplicitGrants);
+ }
+ return fromAuthenticator || hasExplicitGrants || inSystemImage(callerUid);
+ }
+
+ private boolean hasAuthenticatorUid(String accountType, int callingUid) {
+ for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo :
+ mAuthenticatorCache.getAllServices()) {
+ if (serviceInfo.type.type.equals(accountType)) {
+ return (serviceInfo.uid == callingUid) ||
+ (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid)
+ == PackageManager.SIGNATURE_MATCH);
+ }
+ }
+ return false;
+ }
+
+ private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType) {
+ if (Binder.getCallingUid() == android.os.Process.SYSTEM_UID) {
+ return true;
+ }
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ String[] args = {String.valueOf(Binder.getCallingUid()), authTokenType,
+ account.name, account.type};
+ final boolean permissionGranted =
+ DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args) != 0;
+ if (!permissionGranted && isDebuggableMonkeyBuild) {
+ // TODO: Skip this check when running automated tests. Replace this
+ // with a more general solution.
+ Log.w(TAG, "no credentials permission for usage of " + account + ", "
+ + authTokenType + " by uid " + Binder.getCallingUid()
+ + " but ignoring since this is a monkey build");
+ return true;
+ }
+ return permissionGranted;
+ }
+
+ private void checkCallingUidAgainstAuthenticator(Account account) {
+ final int uid = Binder.getCallingUid();
+ if (!hasAuthenticatorUid(account.type, uid)) {
+ String msg = "caller uid " + uid + " is different than the authenticator's uid";
+ Log.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "caller uid " + uid + " is the same as the authenticator's uid");
+ }
+ }
+
+ private void checkAuthenticateAccountsPermission(Account account) {
+ checkBinderPermission(Manifest.permission.AUTHENTICATE_ACCOUNTS);
+ checkCallingUidAgainstAuthenticator(account);
+ }
+
+ private void checkReadAccountsPermission() {
+ checkBinderPermission(Manifest.permission.GET_ACCOUNTS);
+ }
+
+ private void checkManageAccountsPermission() {
+ checkBinderPermission(Manifest.permission.MANAGE_ACCOUNTS);
+ }
+
+ /**
+ * Allow callers with the given uid permission to get credentials for account/authTokenType.
+ * <p>
+ * Although this is public it can only be accessed via the AccountManagerService object
+ * which is in the system. This means we don't need to protect it with permissions.
+ * @hide
+ */
+ public void grantAppPermission(Account account, String authTokenType, int uid) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId >= 0) {
+ ContentValues values = new ContentValues();
+ values.put(GRANTS_ACCOUNTS_ID, accountId);
+ values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
+ values.put(GRANTS_GRANTEE_UID, uid);
+ db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
+ db.setTransactionSuccessful();
+ }
+ } finally {
+ db.endTransaction();
+ }
+ cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
+ }
+
+ /**
+ * Don't allow callers with the given uid permission to get credentials for
+ * account/authTokenType.
+ * <p>
+ * Although this is public it can only be accessed via the AccountManagerService object
+ * which is in the system. This means we don't need to protect it with permissions.
+ * @hide
+ */
+ public void revokeAppPermission(Account account, String authTokenType, int uid) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ db.beginTransaction();
+ try {
+ long accountId = getAccountId(db, account);
+ if (accountId >= 0) {
+ db.delete(TABLE_GRANTS,
+ GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
+ + GRANTS_GRANTEE_UID + "=?",
+ new String[]{String.valueOf(accountId), authTokenType,
+ String.valueOf(uid)});
+ db.setTransactionSuccessful();
+ }
+ } finally {
+ db.endTransaction();
+ }
+ cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid));
+ }
+}
diff --git a/core/java/android/accounts/AccountMonitor.java b/core/java/android/accounts/AccountMonitor.java
deleted file mode 100644
index f21385e..0000000
--- a/core/java/android/accounts/AccountMonitor.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.database.SQLException;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.util.Log;
-
-/**
- * A helper class that calls back on the provided
- * AccountMonitorListener with the set of current accounts both when
- * it gets created and whenever the set changes. It does this by
- * binding to the AccountsService and registering to receive the
- * intent broadcast when the set of accounts is changed. The
- * connection to the accounts service is only made when it needs to
- * fetch the current list of accounts (that is, when the
- * AccountMonitor is first created, and when the intent is received).
- */
-public class AccountMonitor extends BroadcastReceiver implements ServiceConnection {
- private final Context mContext;
- private final AccountMonitorListener mListener;
- private boolean mClosed = false;
- private int pending = 0;
-
- // This thread runs in the background and runs the code to update accounts
- // in the listener.
- private class AccountUpdater extends Thread {
- private IBinder mService;
-
- public AccountUpdater(IBinder service) {
- mService = service;
- }
-
- @Override
- public void run() {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- IAccountsService accountsService = IAccountsService.Stub.asInterface(mService);
- String[] accounts = null;
- do {
- try {
- accounts = accountsService.getAccounts();
- } catch (RemoteException e) {
- // if the service was killed then the system will restart it and when it does we
- // will get another onServiceConnected, at which point we will do a notify.
- Log.w("AccountMonitor", "Remote exception when getting accounts", e);
- return;
- }
-
- synchronized (AccountMonitor.this) {
- --pending;
- if (pending == 0) {
- break;
- }
- }
- } while (true);
-
- mContext.unbindService(AccountMonitor.this);
-
- try {
- mListener.onAccountsUpdated(accounts);
- } catch (SQLException e) {
- // Better luck next time. If the problem was disk-full,
- // the STORAGE_OK intent will re-trigger the update.
- Log.e("AccountMonitor", "Can't update accounts", e);
- }
- }
- }
-
- /**
- * Initializes the AccountMonitor and initiates a bind to the
- * AccountsService to get the initial account list. For 1.0,
- * the "list" is always a single account.
- *
- * @param context the context we are running in
- * @param listener the user to notify when the account set changes
- */
- public AccountMonitor(Context context, AccountMonitorListener listener) {
- if (listener == null) {
- throw new IllegalArgumentException("listener is null");
- }
-
- mContext = context;
- mListener = listener;
-
- // Register a broadcast receiver to monitor account changes
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(AccountsServiceConstants.LOGIN_ACCOUNTS_CHANGED_ACTION);
- intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); // To recover from disk-full.
- mContext.registerReceiver(this, intentFilter);
-
- // Send the listener the initial state now.
- notifyListener();
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- notifyListener();
- }
-
- public void onServiceConnected(ComponentName className, IBinder service) {
- // Create a background thread to update the accounts.
- new AccountUpdater(service).start();
- }
-
- public void onServiceDisconnected(ComponentName className) {
- }
-
- private synchronized void notifyListener() {
- if (pending == 0) {
- // initiate the bind
- if (!mContext.bindService(AccountsServiceConstants.SERVICE_INTENT,
- this, Context.BIND_AUTO_CREATE)) {
- // This is normal if GLS isn't part of this build.
- Log.w("AccountMonitor",
- "Couldn't connect to " +
- AccountsServiceConstants.SERVICE_INTENT +
- " (Missing service?)");
- }
- } else {
- // already bound. bindService will not trigger another
- // call to onServiceConnected, so instead we make sure
- // that the existing background thread will call
- // getAccounts() after this function returns, by
- // incrementing pending.
- //
- // Yes, this else clause contains only a comment.
- }
- ++pending;
- }
-
- /**
- * calls close()
- * @throws Throwable
- */
- @Override
- protected void finalize() throws Throwable {
- close();
- super.finalize();
- }
-
- /**
- * Unregisters the account receiver. Consecutive calls to this
- * method are harmless, but also do nothing. Once this call is
- * made no more notifications will occur.
- */
- public synchronized void close() {
- if (!mClosed) {
- mContext.unregisterReceiver(this);
- mClosed = true;
- }
- }
-}
diff --git a/core/java/android/accounts/AccountMonitorListener.java b/core/java/android/accounts/AccountMonitorListener.java
deleted file mode 100644
index d0bd9a9..0000000
--- a/core/java/android/accounts/AccountMonitorListener.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-/**
- * An interface that contains the callback used by the AccountMonitor
- */
-public interface AccountMonitorListener {
- /**
- * This invoked when the AccountMonitor starts up and whenever the account
- * set changes.
- * @param currentAccounts the current accounts
- */
- void onAccountsUpdated(String[] currentAccounts);
-}
diff --git a/core/java/android/accounts/AccountsServiceConstants.java b/core/java/android/accounts/AccountsServiceConstants.java
deleted file mode 100644
index b882e7b..0000000
--- a/core/java/android/accounts/AccountsServiceConstants.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-import android.content.Intent;
-
-/**
- * Miscellaneous constants used by the AccountsService and its
- * clients.
- */
-// TODO: These constants *could* come directly from the
-// IAccountsService interface, but that's not possible since the
-// aidl compiler doesn't let you define constants (yet.)
-public class AccountsServiceConstants {
- /** This class is never instantiated. */
- private AccountsServiceConstants() {
- }
-
- /**
- * Action sent as a broadcast Intent by the AccountsService
- * when accounts are added to and/or removed from the device's
- * database, or when the primary account is changed.
- */
- public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
- "android.accounts.LOGIN_ACCOUNTS_CHANGED";
-
- /**
- * Action sent as a broadcast Intent by the AccountsService
- * when it starts up and no accounts are available (so some should be added).
- */
- public static final String LOGIN_ACCOUNTS_MISSING_ACTION =
- "android.accounts.LOGIN_ACCOUNTS_MISSING";
-
- /**
- * Action on the intent used to bind to the IAccountsService interface. This
- * is used for services that have multiple interfaces (allowing
- * them to differentiate the interface intended, and return the proper
- * Binder.)
- */
- private static final String ACCOUNTS_SERVICE_ACTION = "android.accounts.IAccountsService";
-
- /*
- * The intent uses a component in addition to the action to ensure the actual
- * accounts service is bound to (a malicious third-party app could
- * theoretically have a service with the same action).
- */
- /** The intent used to bind to the accounts service. */
- public static final Intent SERVICE_INTENT =
- new Intent()
- .setClassName("com.google.android.googleapps",
- "com.google.android.googleapps.GoogleLoginService")
- .setAction(ACCOUNTS_SERVICE_ACTION);
-
- /**
- * Checks whether the intent is to bind to the accounts service.
- *
- * @param bindIntent The Intent used to bind to the service.
- * @return Whether the intent is to bind to the accounts service.
- */
- public static final boolean isForAccountsService(Intent bindIntent) {
- String otherAction = bindIntent.getAction();
- return otherAction != null && otherAction.equals(ACCOUNTS_SERVICE_ACTION);
- }
-}
diff --git a/core/java/android/accounts/AuthenticatorBindHelper.java b/core/java/android/accounts/AuthenticatorBindHelper.java
new file mode 100644
index 0000000..91e23ab
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorBindHelper.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+/**
+ * A helper object that simplifies binding to Account Authenticators. It uses the
+ * {@link AccountAuthenticatorCache} to find the component name of the authenticators,
+ * allowing the user to bind by account name. It also allows multiple, simultaneous binds
+ * to the same authenticator, with each bind call guaranteed to return either
+ * {@link Callback#onConnected} or {@link Callback#onDisconnected} if the bind() call
+ * itself succeeds, even if the authenticator is already bound internally.
+ * @hide
+ */
+public class AuthenticatorBindHelper {
+ private static final String TAG = "Accounts";
+ private final Handler mHandler;
+ private final Context mContext;
+ private final int mMessageWhatConnected;
+ private final int mMessageWhatDisconnected;
+ private final Map<String, MyServiceConnection> mServiceConnections = Maps.newHashMap();
+ private final Map<String, ArrayList<Callback>> mServiceUsers = Maps.newHashMap();
+ private final AccountAuthenticatorCache mAuthenticatorCache;
+
+ public AuthenticatorBindHelper(Context context,
+ AccountAuthenticatorCache authenticatorCache, Handler handler,
+ int messageWhatConnected, int messageWhatDisconnected) {
+ mContext = context;
+ mHandler = handler;
+ mAuthenticatorCache = authenticatorCache;
+ mMessageWhatConnected = messageWhatConnected;
+ mMessageWhatDisconnected = messageWhatDisconnected;
+ }
+
+ public interface Callback {
+ void onConnected(IBinder service);
+ void onDisconnected();
+ }
+
+ public boolean bind(String authenticatorType, Callback callback) {
+ // if the authenticator is connecting or connected then return true
+ synchronized (mServiceConnections) {
+ if (mServiceConnections.containsKey(authenticatorType)) {
+ MyServiceConnection connection = mServiceConnections.get(authenticatorType);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "service connection already exists for " + authenticatorType);
+ }
+ mServiceUsers.get(authenticatorType).add(callback);
+ if (connection.mService != null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service is connected, scheduling a connected message for "
+ + authenticatorType);
+ }
+ connection.scheduleCallbackConnectedMessage(callback);
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service is *not* connected, waiting for for "
+ + authenticatorType);
+ }
+ }
+ return true;
+ }
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there is no service connection for " + authenticatorType);
+ }
+
+ // otherwise find the component name for the authenticator and initiate a bind
+ // if no authenticator or the bind fails then return false, otherwise return true
+ AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo =
+ mAuthenticatorCache.getServiceInfo(
+ AuthenticatorDescription.newKey(authenticatorType));
+ if (authenticatorInfo == null) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there is no authenticator for " + authenticatorType
+ + ", bailing out");
+ }
+ return false;
+ }
+
+ MyServiceConnection connection = new MyServiceConnection(authenticatorType);
+
+ Intent intent = new Intent();
+ intent.setAction("android.accounts.AccountAuthenticator");
+ intent.setComponent(authenticatorInfo.componentName);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "performing bindService to " + authenticatorInfo.componentName);
+ }
+ if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "bindService to " + authenticatorInfo.componentName + " failed");
+ }
+ return false;
+ }
+
+ mServiceConnections.put(authenticatorType, connection);
+ mServiceUsers.put(authenticatorType, Lists.newArrayList(callback));
+ return true;
+ }
+ }
+
+ public void unbind(Callback callbackToUnbind) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "unbinding callback " + callbackToUnbind);
+ }
+ synchronized (mServiceConnections) {
+ for (Map.Entry<String, ArrayList<Callback>> entry : mServiceUsers.entrySet()) {
+ final String authenticatorType = entry.getKey();
+ final ArrayList<Callback> serviceUsers = entry.getValue();
+ for (Callback callback : serviceUsers) {
+ if (callback == callbackToUnbind) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found callback in service" + authenticatorType);
+ }
+ serviceUsers.remove(callbackToUnbind);
+ if (serviceUsers.isEmpty()) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "there are no more callbacks for service "
+ + authenticatorType + ", unbinding service");
+ }
+ unbindFromService(authenticatorType);
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "leaving service " + authenticatorType
+ + " around since there are still callbacks using it");
+ }
+ }
+ return;
+ }
+ }
+ }
+ Log.e(TAG, "did not find callback " + callbackToUnbind + " in any of the services");
+ }
+ }
+
+ private void unbindFromService(String authenticatorType) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "unbindService from " + authenticatorType);
+ }
+ mContext.unbindService(mServiceConnections.get(authenticatorType));
+ mServiceUsers.remove(authenticatorType);
+ mServiceConnections.remove(authenticatorType);
+ }
+
+ private class ConnectedMessagePayload {
+ public final IBinder mService;
+ public final Callback mCallback;
+ public ConnectedMessagePayload(IBinder service, Callback callback) {
+ mService = service;
+ mCallback = callback;
+ }
+ }
+
+ private class MyServiceConnection implements ServiceConnection {
+ private final String mAuthenticatorType;
+ private IBinder mService = null;
+
+ public MyServiceConnection(String authenticatorType) {
+ mAuthenticatorType = authenticatorType;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onServiceConnected for account type " + mAuthenticatorType);
+ }
+ // post a message for each service user to tell them that the service is connected
+ synchronized (mServiceConnections) {
+ mService = service;
+ for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service became connected, scheduling a connected "
+ + "message for " + mAuthenticatorType);
+ }
+ scheduleCallbackConnectedMessage(callback);
+ }
+ }
+ }
+
+ private void scheduleCallbackConnectedMessage(Callback callback) {
+ final ConnectedMessagePayload payload =
+ new ConnectedMessagePayload(mService, callback);
+ mHandler.obtainMessage(mMessageWhatConnected, payload).sendToTarget();
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onServiceDisconnected for account type " + mAuthenticatorType);
+ }
+ // post a message for each service user to tell them that the service is disconnected,
+ // and unbind from the service.
+ synchronized (mServiceConnections) {
+ for (Callback callback : mServiceUsers.get(mAuthenticatorType)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the service became disconnected, scheduling a "
+ + "disconnected message for "
+ + mAuthenticatorType);
+ }
+ mHandler.obtainMessage(mMessageWhatDisconnected, callback).sendToTarget();
+ }
+ unbindFromService(mAuthenticatorType);
+ }
+ }
+ }
+
+ boolean handleMessage(Message message) {
+ if (message.what == mMessageWhatConnected) {
+ ConnectedMessagePayload payload = (ConnectedMessagePayload)message.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "notifying callback " + payload.mCallback + " that it is connected");
+ }
+ payload.mCallback.onConnected(payload.mService);
+ return true;
+ } else if (message.what == mMessageWhatDisconnected) {
+ Callback callback = (Callback)message.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "notifying callback " + callback + " that it is disconnected");
+ }
+ callback.onDisconnected();
+ return true;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/accounts/AuthenticatorDescription.aidl b/core/java/android/accounts/AuthenticatorDescription.aidl
new file mode 100644
index 0000000..136361c
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorDescription.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+parcelable AuthenticatorDescription;
diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java
new file mode 100644
index 0000000..672e648
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorDescription.java
@@ -0,0 +1,72 @@
+package android.accounts;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+public class AuthenticatorDescription implements Parcelable {
+ final public String type;
+ final public int labelId;
+ final public int iconId;
+ final public String packageName;
+
+ public AuthenticatorDescription(String type, String packageName, int labelId, int iconId) {
+ if (type == null) throw new IllegalArgumentException("type cannot be null");
+ if (packageName == null) throw new IllegalArgumentException("packageName cannot be null");
+ this.type = type;
+ this.packageName = packageName;
+ this.labelId = labelId;
+ this.iconId = iconId;
+ }
+
+ public static AuthenticatorDescription newKey(String type) {
+ if (type == null) throw new IllegalArgumentException("type cannot be null");
+ return new AuthenticatorDescription(type);
+ }
+
+ private AuthenticatorDescription(String type) {
+ this.type = type;
+ this.packageName = null;
+ this.labelId = 0;
+ this.iconId = 0;
+ }
+
+ private AuthenticatorDescription(Parcel source) {
+ this.type = source.readString();
+ this.packageName = source.readString();
+ this.labelId = source.readInt();
+ this.iconId = source.readInt();
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof AuthenticatorDescription)) return false;
+ final AuthenticatorDescription other = (AuthenticatorDescription) o;
+ return type.equals(other.type);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(type);
+ dest.writeString(packageName);
+ dest.writeInt(labelId);
+ dest.writeInt(iconId);
+ }
+
+ public static final Creator<AuthenticatorDescription> CREATOR =
+ new Creator<AuthenticatorDescription>() {
+ public AuthenticatorDescription createFromParcel(Parcel source) {
+ return new AuthenticatorDescription(source);
+ }
+
+ public AuthenticatorDescription[] newArray(int size) {
+ return new AuthenticatorDescription[size];
+ }
+ };
+}
diff --git a/core/java/android/accounts/AuthenticatorException.java b/core/java/android/accounts/AuthenticatorException.java
new file mode 100644
index 0000000..4023494
--- /dev/null
+++ b/core/java/android/accounts/AuthenticatorException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+public class AuthenticatorException extends Exception {
+ public AuthenticatorException() {
+ super();
+ }
+ public AuthenticatorException(String message) {
+ super(message);
+ }
+ public AuthenticatorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public AuthenticatorException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/accounts/ChooseAccountActivity.java b/core/java/android/accounts/ChooseAccountActivity.java
new file mode 100644
index 0000000..bd6f205
--- /dev/null
+++ b/core/java/android/accounts/ChooseAccountActivity.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.view.View;
+import android.util.Log;
+
+public class ChooseAccountActivity extends ListActivity {
+ private static final String TAG = "AccountManager";
+ private Parcelable[] mAccounts = null;
+ private AccountManagerResponse mAccountManagerResponse = null;
+ private Bundle mResult;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ mAccounts = getIntent().getParcelableArrayExtra(Constants.ACCOUNTS_KEY);
+ mAccountManagerResponse =
+ getIntent().getParcelableExtra(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+ } else {
+ mAccounts = savedInstanceState.getParcelableArray(Constants.ACCOUNTS_KEY);
+ mAccountManagerResponse =
+ savedInstanceState.getParcelable(Constants.ACCOUNT_MANAGER_RESPONSE_KEY);
+ }
+
+ String[] mAccountNames = new String[mAccounts.length];
+ for (int i = 0; i < mAccounts.length; i++) {
+ mAccountNames[i] = ((Account) mAccounts[i]).name;
+ }
+
+ // Use an existing ListAdapter that will map an array
+ // of strings to TextViews
+ setListAdapter(new ArrayAdapter<String>(this,
+ android.R.layout.simple_list_item_1, mAccountNames));
+ getListView().setTextFilterEnabled(true);
+ }
+
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ Account account = (Account) mAccounts[position];
+ Log.d(TAG, "selected account " + account);
+ Bundle bundle = new Bundle();
+ bundle.putString(Constants.ACCOUNT_NAME_KEY, account.name);
+ bundle.putString(Constants.ACCOUNT_TYPE_KEY, account.type);
+ mResult = bundle;
+ finish();
+ }
+
+ public void finish() {
+ if (mAccountManagerResponse != null) {
+ if (mResult != null) {
+ mAccountManagerResponse.onResult(mResult);
+ } else {
+ mAccountManagerResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+ }
+ }
+ super.finish();
+ }
+}
diff --git a/core/java/android/accounts/Constants.java b/core/java/android/accounts/Constants.java
new file mode 100644
index 0000000..15b1773
--- /dev/null
+++ b/core/java/android/accounts/Constants.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class Constants {
+ // this should never be instantiated
+ private Constants() {}
+
+ public static final int ERROR_CODE_REMOTE_EXCEPTION = 1;
+ public static final int ERROR_CODE_NETWORK_ERROR = 3;
+ public static final int ERROR_CODE_CANCELED = 4;
+ public static final int ERROR_CODE_INVALID_RESPONSE = 5;
+ public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
+ public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
+ public static final int ERROR_CODE_BAD_REQUEST = 8;
+
+ public static final String ACCOUNTS_KEY = "accounts";
+ public static final String AUTHENTICATOR_TYPES_KEY = "authenticator_types";
+ public static final String USERDATA_KEY = "userdata";
+ public static final String AUTHTOKEN_KEY = "authtoken";
+ public static final String PASSWORD_KEY = "password";
+ public static final String ACCOUNT_NAME_KEY = "authAccount";
+ public static final String ACCOUNT_TYPE_KEY = "accountType";
+ public static final String ERROR_CODE_KEY = "errorCode";
+ public static final String ERROR_MESSAGE_KEY = "errorMessage";
+ public static final String INTENT_KEY = "intent";
+ public static final String BOOLEAN_RESULT_KEY = "booleanResult";
+ public static final String ACCOUNT_AUTHENTICATOR_RESPONSE_KEY = "accountAuthenticatorResponse";
+ public static final String ACCOUNT_MANAGER_RESPONSE_KEY = "accountManagerResponse";
+ public static final String AUTH_FAILED_MESSAGE_KEY = "authFailedMessage";
+ public static final String AUTH_TOKEN_LABEL_KEY = "authTokenLabelKey";
+
+ public static final String AUTHENTICATOR_INTENT_ACTION =
+ "android.accounts.AccountAuthenticator";
+ public static final String AUTHENTICATOR_META_DATA_NAME =
+ "android.accounts.AccountAuthenticator";
+ public static final String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
+
+ /**
+ * Action sent as a broadcast Intent by the AccountsService
+ * when accounts are added to and/or removed from the device's
+ * database.
+ */
+ public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
+ "android.accounts.LOGIN_ACCOUNTS_CHANGED";
+}
diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
new file mode 100644
index 0000000..e06afb4
--- /dev/null
+++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.view.View;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class GrantCredentialsPermissionActivity extends Activity implements View.OnClickListener {
+ public static final String EXTRAS_ACCOUNT = "account";
+ public static final String EXTRAS_AUTH_TOKEN_LABEL = "authTokenLabel";
+ public static final String EXTRAS_AUTH_TOKEN_TYPE = "authTokenType";
+ public static final String EXTRAS_RESPONSE = "response";
+ public static final String EXTRAS_ACCOUNT_TYPE_LABEL = "accountTypeLabel";
+ public static final String EXTRAS_PACKAGES = "application";
+ public static final String EXTRAS_REQUESTING_UID = "uid";
+ private Account mAccount;
+ private String mAuthTokenType;
+ private int mUid;
+ private Bundle mResultBundle = null;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setContentView(R.layout.grant_credentials_permission);
+ mAccount = getIntent().getExtras().getParcelable(EXTRAS_ACCOUNT);
+ mAuthTokenType = getIntent().getExtras().getString(EXTRAS_AUTH_TOKEN_TYPE);
+ mUid = getIntent().getExtras().getInt(EXTRAS_REQUESTING_UID);
+ final String accountTypeLabel =
+ getIntent().getExtras().getString(EXTRAS_ACCOUNT_TYPE_LABEL);
+ final String[] packages = getIntent().getExtras().getStringArray(EXTRAS_PACKAGES);
+
+ findViewById(R.id.allow).setOnClickListener(this);
+ findViewById(R.id.deny).setOnClickListener(this);
+
+ TextView messageView = (TextView) getWindow().findViewById(R.id.message);
+ String authTokenLabel = getIntent().getExtras().getString(EXTRAS_AUTH_TOKEN_LABEL);
+ if (authTokenLabel.length() == 0) {
+ CharSequence grantCredentialsPermissionFormat = getResources().getText(
+ R.string.grant_credentials_permission_message_desc);
+ messageView.setText(String.format(grantCredentialsPermissionFormat.toString(),
+ mAccount.name, accountTypeLabel));
+ } else {
+ CharSequence grantCredentialsPermissionFormat = getResources().getText(
+ R.string.grant_credentials_permission_message_with_authtokenlabel_desc);
+ messageView.setText(String.format(grantCredentialsPermissionFormat.toString(),
+ authTokenLabel, mAccount.name, accountTypeLabel));
+ }
+
+ String[] packageLabels = new String[packages.length];
+ final PackageManager pm = getPackageManager();
+ for (int i = 0; i < packages.length; i++) {
+ try {
+ packageLabels[i] =
+ pm.getApplicationLabel(pm.getApplicationInfo(packages[i], 0)).toString();
+ } catch (PackageManager.NameNotFoundException e) {
+ packageLabels[i] = packages[i];
+ }
+ }
+ ((ListView) findViewById(R.id.packages_list)).setAdapter(
+ new PackagesArrayAdapter(this, packageLabels));
+ }
+
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.allow:
+ AccountManagerService.getSingleton().grantAppPermission(mAccount, mAuthTokenType,
+ mUid);
+ Intent result = new Intent();
+ result.putExtra("retry", true);
+ setResult(RESULT_OK, result);
+ setAccountAuthenticatorResult(result.getExtras());
+ break;
+
+ case R.id.deny:
+ AccountManagerService.getSingleton().revokeAppPermission(mAccount, mAuthTokenType,
+ mUid);
+ setResult(RESULT_CANCELED);
+ break;
+ }
+ finish();
+ }
+
+ public final void setAccountAuthenticatorResult(Bundle result) {
+ mResultBundle = result;
+ }
+
+ /**
+ * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present.
+ */
+ public void finish() {
+ Intent intent = getIntent();
+ AccountAuthenticatorResponse accountAuthenticatorResponse =
+ intent.getParcelableExtra(EXTRAS_RESPONSE);
+ if (accountAuthenticatorResponse != null) {
+ // send the result bundle back if set, otherwise send an error.
+ if (mResultBundle != null) {
+ accountAuthenticatorResponse.onResult(mResultBundle);
+ } else {
+ accountAuthenticatorResponse.onError(Constants.ERROR_CODE_CANCELED, "canceled");
+ }
+ }
+ super.finish();
+ }
+
+ private static class PackagesArrayAdapter extends ArrayAdapter<String> {
+ protected LayoutInflater mInflater;
+ private static final int mResource = R.layout.simple_list_item_1;
+
+ public PackagesArrayAdapter(Context context, String[] items) {
+ super(context, mResource, items);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ static class ViewHolder {
+ TextView label;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // A ViewHolder keeps references to children views to avoid unneccessary calls
+ // to findViewById() on each row.
+ ViewHolder holder;
+
+ // When convertView is not null, we can reuse it directly, there is no need
+ // to reinflate it. We only inflate a new View when the convertView supplied
+ // by ListView is null.
+ if (convertView == null) {
+ convertView = mInflater.inflate(mResource, null);
+
+ // Creates a ViewHolder and store references to the two children views
+ // we want to bind data to.
+ holder = new ViewHolder();
+ holder.label = (TextView) convertView.findViewById(R.id.text1);
+
+ convertView.setTag(holder);
+ } else {
+ // Get the ViewHolder back to get fast access to the TextView
+ // and the ImageView.
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.label.setText(getItem(position));
+
+ return convertView;
+ }
+ }
+}
diff --git a/core/java/android/accounts/IAccountAuthenticator.aidl b/core/java/android/accounts/IAccountAuthenticator.aidl
new file mode 100644
index 0000000..1592eea
--- /dev/null
+++ b/core/java/android/accounts/IAccountAuthenticator.aidl
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.accounts.IAccountAuthenticatorResponse;
+import android.accounts.Account;
+import android.os.Bundle;
+
+/**
+ * Service that allows the interaction with an authentication server.
+ */
+oneway interface IAccountAuthenticator {
+ /**
+ * prompts the user for account information and adds the result to the IAccountManager
+ */
+ void addAccount(in IAccountAuthenticatorResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, in Bundle options);
+
+ /**
+ * Checks that the account/password combination is valid.
+ * note -- deprecated
+ */
+ void confirmPassword(in IAccountAuthenticatorResponse response,
+ in Account account, String password);
+
+ /**
+ * prompts the user for the credentials of the account
+ */
+ void confirmCredentials(in IAccountAuthenticatorResponse response, in Account account);
+
+ /**
+ * gets the password by either prompting the user or querying the IAccountManager
+ */
+ void getAuthToken(in IAccountAuthenticatorResponse response, in Account account,
+ String authTokenType, in Bundle options);
+
+ /**
+ * Gets the user-visible label of the given authtoken type.
+ */
+ void getAuthTokenLabel(in IAccountAuthenticatorResponse response, String authTokenType);
+
+ /**
+ * prompts the user for a new password and writes it to the IAccountManager
+ */
+ void updateCredentials(in IAccountAuthenticatorResponse response, in Account account,
+ String authTokenType, in Bundle options);
+
+ /**
+ * launches an activity that lets the user edit and set the properties for an authenticator
+ */
+ void editProperties(in IAccountAuthenticatorResponse response, String accountType);
+
+ /**
+ * returns a Bundle where the boolean value BOOLEAN_RESULT_KEY is set if the account has the
+ * specified features
+ */
+ void hasFeatures(in IAccountAuthenticatorResponse response, in Account account,
+ in String[] features);
+
+ /**
+ * Gets whether or not the account is allowed to be removed.
+ */
+ void getAccountRemovalAllowed(in IAccountAuthenticatorResponse response, in Account account);
+}
diff --git a/core/java/android/accounts/IAccountAuthenticatorResponse.aidl b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
new file mode 100644
index 0000000..a9ac2f1
--- /dev/null
+++ b/core/java/android/accounts/IAccountAuthenticatorResponse.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+import android.os.Bundle;
+
+/**
+ * The interface used to return responses from an {@link IAccountAuthenticator}
+ */
+oneway interface IAccountAuthenticatorResponse {
+ void onResult(in Bundle value);
+ void onRequestContinued();
+ void onError(int errorCode, String errorMessage);
+}
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
new file mode 100644
index 0000000..411952b
--- /dev/null
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+import android.accounts.IAccountManagerResponse;
+import android.accounts.Account;
+import android.accounts.AuthenticatorDescription;
+import android.os.Bundle;
+
+
+/**
+ * Central application service that provides account management.
+ * @hide
+ */
+interface IAccountManager {
+ String getPassword(in Account account);
+ String getUserData(in Account account, String key);
+ AuthenticatorDescription[] getAuthenticatorTypes();
+ Account[] getAccounts(String accountType);
+ void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
+ boolean addAccount(in Account account, String password, in Bundle extras);
+ void removeAccount(in IAccountManagerResponse response, in Account account);
+ void invalidateAuthToken(String accountType, String authToken);
+ String peekAuthToken(in Account account, String authTokenType);
+ void setAuthToken(in Account account, String authTokenType, String authToken);
+ void setPassword(in Account account, String password);
+ void clearPassword(in Account account);
+ void setUserData(in Account account, String key, String value);
+
+ void getAuthToken(in IAccountManagerResponse response, in Account account,
+ String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
+ in Bundle options);
+ void addAcount(in IAccountManagerResponse response, String accountType,
+ String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
+ in Bundle options);
+ void updateCredentials(in IAccountManagerResponse response, in Account account,
+ String authTokenType, boolean expectActivityLaunch, in Bundle options);
+ void editProperties(in IAccountManagerResponse response, String accountType,
+ boolean expectActivityLaunch);
+ void confirmCredentials(in IAccountManagerResponse response, in Account account,
+ boolean expectActivityLaunch);
+
+ /*
+ * @deprecated
+ */
+ void confirmPassword(in IAccountManagerResponse response, in Account account,
+ String password);
+}
diff --git a/core/java/android/accounts/IAccountManagerResponse.aidl b/core/java/android/accounts/IAccountManagerResponse.aidl
new file mode 100644
index 0000000..ca1203d
--- /dev/null
+++ b/core/java/android/accounts/IAccountManagerResponse.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+import android.os.Bundle;
+
+/**
+ * The interface used to return responses for asynchronous calls to the {@link IAccountManager}
+ * @hide
+ */
+oneway interface IAccountManagerResponse {
+ void onResult(in Bundle value);
+ void onError(int errorCode, String errorMessage);
+}
diff --git a/core/java/android/accounts/IAccountsService.aidl b/core/java/android/accounts/IAccountsService.aidl
deleted file mode 100644
index dda513c..0000000
--- a/core/java/android/accounts/IAccountsService.aidl
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.accounts;
-
-/**
- * Central application service that allows querying the list of accounts.
- */
-interface IAccountsService {
- /**
- * Gets the list of Accounts the user has previously logged
- * in to. Accounts are of the form "username@domain".
- * <p>
- * This method will return an empty array if the device doesn't
- * know about any accounts (yet).
- *
- * @return The accounts. The array will be zero-length if the
- * AccountsService doesn't know about any accounts yet.
- */
- String[] getAccounts();
-
- /**
- * This is an interim solution for bypassing a forgotten gesture on the
- * unlock screen (it is hidden, please make sure it stays this way!). This
- * will be *removed* when the unlock screen design supports additional
- * authenticators.
- * <p>
- * The user will be presented with username and password fields that are
- * called as parameters to this method. If true is returned, the user is
- * able to define a new gesture and get back into the system. If false, the
- * user can try again.
- *
- * @param username The username entered.
- * @param password The password entered.
- * @return Whether to allow the user to bypass the lock screen and define a
- * new gesture.
- * @hide (The package is already hidden, but just in case someone
- * unhides that, this should not be revealed.)
- */
- boolean shouldUnlock(String username, String password);
-}
diff --git a/core/java/android/accounts/NetworkErrorException.java b/core/java/android/accounts/NetworkErrorException.java
new file mode 100644
index 0000000..f855cc8
--- /dev/null
+++ b/core/java/android/accounts/NetworkErrorException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class NetworkErrorException extends Exception {
+ public NetworkErrorException() {
+ super();
+ }
+ public NetworkErrorException(String message) {
+ super(message);
+ }
+ public NetworkErrorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public NetworkErrorException(Throwable cause) {
+ super(cause);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/accounts/OnAccountsUpdatedListener.java b/core/java/android/accounts/OnAccountsUpdatedListener.java
new file mode 100644
index 0000000..bd249d0
--- /dev/null
+++ b/core/java/android/accounts/OnAccountsUpdatedListener.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+/**
+ * An interface that contains the callback used by the AccountMonitor
+ */
+public interface OnAccountsUpdatedListener {
+ /**
+ * This invoked when the AccountMonitor starts up and whenever the account
+ * set changes.
+ * @param accounts the current accounts
+ */
+ void onAccountsUpdated(Account[] accounts);
+}
diff --git a/core/java/android/accounts/OperationCanceledException.java b/core/java/android/accounts/OperationCanceledException.java
new file mode 100644
index 0000000..2f2c164
--- /dev/null
+++ b/core/java/android/accounts/OperationCanceledException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.accounts;
+
+public class OperationCanceledException extends Exception {
+ public OperationCanceledException() {
+ super();
+ }
+ public OperationCanceledException(String message) {
+ super(message);
+ }
+ public OperationCanceledException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public OperationCanceledException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/accounts/package.html b/core/java/android/accounts/package.html
deleted file mode 100755
index c9f96a6..0000000
--- a/core/java/android/accounts/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<body>
-
-{@hide}
-
-</body>
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f2905a7..8c10091 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1930,11 +1930,32 @@ public class Activity extends ContextThemeWrapper
*
* @see #hasWindowFocus()
* @see #onResume
+ * @see View#onWindowFocusChanged(boolean)
*/
public void onWindowFocusChanged(boolean hasFocus) {
}
/**
+ * Called when the main window associated with the activity has been
+ * attached to the window manager.
+ * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
+ * for more information.
+ * @see View#onAttachedToWindow
+ */
+ public void onAttachedToWindow() {
+ }
+
+ /**
+ * Called when the main window associated with the activity has been
+ * detached from the window manager.
+ * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
+ * for more information.
+ * @see View#onDetachedFromWindow
+ */
+ public void onDetachedFromWindow() {
+ }
+
+ /**
* Returns true if this activity's <em>main</em> window currently has window focus.
* Note that this is not the same as the view itself having focus.
*
@@ -2394,6 +2415,7 @@ public class Activity extends ContextThemeWrapper
*
* @param id The id of the managed dialog.
*
+ * @see Dialog
* @see #onCreateDialog(int)
* @see #onPrepareDialog(int, Dialog)
* @see #dismissDialog(int)
@@ -2535,6 +2557,25 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Similar to {@link #startSearch}, but actually fires off the search query after invoking
+ * the search dialog. Made available for testing purposes.
+ *
+ * @param query The query to trigger. If empty, the request will be ignored.
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ * @param globalSearch If false, this will only launch the search that has been specifically
+ * defined by the application (which is usually defined as a local search). If no default
+ * search is defined in the current application or activity, no search will be launched.
+ * If true, this will always launch a platform-global (e.g. web-based) search instead.
+ */
+ public void triggerSearch(String query, Bundle appSearchData, boolean globalSearch) {
+ ensureSearchManager();
+ mSearchManager.triggerSearch(query, getComponentName(), appSearchData, globalSearch);
+ }
+
+ /**
* Request that key events come to this activity. Use this if your
* activity has no views with focus, but the activity still wants
* a chance to process key events.
@@ -2608,10 +2649,8 @@ public class Activity extends ContextThemeWrapper
}
@Override
- protected void onApplyThemeResource(Resources.Theme theme,
- int resid,
- boolean first)
- {
+ protected void onApplyThemeResource(Resources.Theme theme, int resid,
+ boolean first) {
if (mParent == null) {
super.onApplyThemeResource(theme, resid, first);
} else {
@@ -2682,6 +2721,66 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivityForResult(Intent, int)}, but allowing you
+ * to use a PendingIntent to describe the activity to be started. Note
+ * that the given PendingIntent <em>must</em> have been created with
+ * {@link PendingIntent#getActivity PendingIntent.getActivity}; all other
+ * types will result in an IllegalArgumentException being thrown.
+ *
+ * @param intent The PendingIntent to launch.
+ * @param requestCode If >= 0, this code will be returned in
+ * onActivityResult() when the activity exits.
+ * @param fillInIntent If non-null, this will be provided as the
+ * intent parameter to {@link PendingIntent#send(Context, int, Intent)
+ * PendingIntent.send(Context, int, Intent)}.
+ * @param flagsMask Intent flags in the original PendingIntent that you
+ * would like to change.
+ * @param flagsValues Desired values for any bits set in
+ * <var>flagsMask</var>
+ */
+ public void startActivityForResult(PendingIntent intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues)
+ throws PendingIntent.CanceledException {
+ if (mParent == null) {
+ startActivityForResultInner(intent, requestCode, fillInIntent,
+ flagsMask, flagsValues, this);
+ } else {
+ mParent.startActivityFromChild(this, intent, requestCode,
+ fillInIntent, flagsMask, flagsValues);
+ }
+ }
+
+ private void startActivityForResultInner(PendingIntent intent, int requestCode,
+ Intent fillInIntent, int flagsMask, int flagsValues, Activity activity)
+ throws PendingIntent.CanceledException {
+ try {
+ String resolvedType = null;
+ if (fillInIntent != null) {
+ resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
+ }
+ int result = ActivityManagerNative.getDefault()
+ .startActivityPendingIntent(mMainThread.getApplicationThread(), intent,
+ fillInIntent, resolvedType, mToken, activity.mEmbeddedID,
+ requestCode, flagsMask, flagsValues);
+ if (result == IActivityManager.START_CANCELED) {
+ throw new PendingIntent.CanceledException();
+ }
+ Instrumentation.checkStartActivityResult(result, null);
+ } catch (RemoteException e) {
+ }
+ if (requestCode >= 0) {
+ // If this start is requesting a result, we can avoid making
+ // the activity visible until the result is received. Setting
+ // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
+ // activity hidden during this time, to avoid flickering.
+ // This can only be done when a result is requested because
+ // that guarantees we will get information back when the
+ // activity is finished, no matter what happens to it.
+ mStartedActivity = true;
+ }
+ }
+
+ /**
* Launch a new activity. You will not receive any information about when
* the activity exits. This implementation overrides the base version,
* providing information about
@@ -2705,6 +2804,27 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivity(Intent)}, but taking a PendingIntent
+ * to start; see
+ * {@link #startActivityForResult(PendingIntent, int, Intent, int, int)}
+ * for more information.
+ *
+ * @param intent The PendingIntent to launch.
+ * @param fillInIntent If non-null, this will be provided as the
+ * intent parameter to {@link PendingIntent#send(Context, int, Intent)
+ * PendingIntent.send(Context, int, Intent)}.
+ * @param flagsMask Intent flags in the original PendingIntent that you
+ * would like to change.
+ * @param flagsValues Desired values for any bits set in
+ * <var>flagsMask</var>
+ */
+ public void startActivity(PendingIntent intent,
+ Intent fillInIntent, int flagsMask, int flagsValues)
+ throws PendingIntent.CanceledException {
+ startActivityForResult(intent, -1, fillInIntent, flagsMask, flagsValues);
+ }
+
+ /**
* A special variation to launch an activity only if a new activity
* instance is needed to handle the given Intent. In other words, this is
* just like {@link #startActivityForResult(Intent, int)} except: if you are
@@ -2825,6 +2945,19 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Like {@link #startActivityFromChild(Activity, Intent, int)}, but
+ * taking a PendingIntent; see
+ * {@link #startActivityForResult(PendingIntent, int, Intent, int, int)}
+ * for more information.
+ */
+ public void startActivityFromChild(Activity child, PendingIntent intent,
+ int requestCode, Intent fillInIntent, int flagsMask, int flagsValues)
+ throws PendingIntent.CanceledException {
+ startActivityForResultInner(intent, requestCode, fillInIntent,
+ flagsMask, flagsValues, child);
+ }
+
+ /**
* Call this to set the result that your activity will return to its
* caller.
*
@@ -3255,7 +3388,7 @@ public class Activity extends ContextThemeWrapper
throw new IllegalArgumentException("no ident");
}
}
- mSearchManager.setIdent(ident);
+ mSearchManager.setIdent(ident, getComponentName());
}
@Override
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 07520c9d..e96de07 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -289,6 +289,11 @@ public class ActivityManager {
public int pid;
/**
+ * The UID that owns this service.
+ */
+ public int uid;
+
+ /**
* The name of the process this service runs in.
*/
public String process;
@@ -299,7 +304,7 @@ public class ActivityManager {
public boolean foreground;
/**
- * The time when the service was first made activity, either by someone
+ * The time when the service was first made active, either by someone
* starting or binding to it.
*/
public long activeSince;
@@ -332,6 +337,48 @@ public class ActivityManager {
*/
public long restarting;
+ /**
+ * Bit for {@link #flags}: set if this service has been
+ * explicitly started.
+ */
+ public static final int FLAG_STARTED = 1<<0;
+
+ /**
+ * Bit for {@link #flags}: set if the service has asked to
+ * run as a foreground process.
+ */
+ public static final int FLAG_FOREGROUND = 1<<1;
+
+ /**
+ * Bit for {@link #flags): set if the service is running in a
+ * core system process.
+ */
+ public static final int FLAG_SYSTEM_PROCESS = 1<<2;
+
+ /**
+ * Bit for {@link #flags): set if the service is running in a
+ * persistent process.
+ */
+ public static final int FLAG_PERSISTENT_PROCESS = 1<<3;
+
+ /**
+ * Running flags.
+ */
+ public int flags;
+
+ /**
+ * For special services that are bound to by system code, this is
+ * the package that holds the binding.
+ */
+ public String clientPackage;
+
+ /**
+ * For special services that are bound to by system code, this is
+ * a string resource providing a user-visible label for who the
+ * client is.
+ */
+ public int clientLabel;
+
public RunningServiceInfo() {
}
@@ -342,6 +389,7 @@ public class ActivityManager {
public void writeToParcel(Parcel dest, int flags) {
ComponentName.writeToParcel(service, dest);
dest.writeInt(pid);
+ dest.writeInt(uid);
dest.writeString(process);
dest.writeInt(foreground ? 1 : 0);
dest.writeLong(activeSince);
@@ -350,11 +398,15 @@ public class ActivityManager {
dest.writeInt(crashCount);
dest.writeLong(lastActivityTime);
dest.writeLong(restarting);
+ dest.writeInt(this.flags);
+ dest.writeString(clientPackage);
+ dest.writeInt(clientLabel);
}
public void readFromParcel(Parcel source) {
service = ComponentName.readFromParcel(source);
pid = source.readInt();
+ uid = source.readInt();
process = source.readString();
foreground = source.readInt() != 0;
activeSince = source.readLong();
@@ -363,6 +415,9 @@ public class ActivityManager {
crashCount = source.readInt();
lastActivityTime = source.readLong();
restarting = source.readLong();
+ flags = source.readInt();
+ clientPackage = source.readString();
+ clientLabel = source.readInt();
}
public static final Creator<RunningServiceInfo> CREATOR = new Creator<RunningServiceInfo>() {
@@ -401,6 +456,22 @@ public class ActivityManager {
}
/**
+ * Returns a PendingIntent you can start to show a control panel for the
+ * given running service. If the service does not have a control panel,
+ * null is returned.
+ */
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws SecurityException {
+ try {
+ return ActivityManagerNative.getDefault()
+ .getRunningServiceControlPanel(service);
+ } catch (RemoteException e) {
+ // System dead, we will be dead too soon!
+ return null;
+ }
+ }
+
+ /**
* Information you can retrieve about the available memory through
* {@link ActivityManager#getMemoryInfo}.
*/
@@ -613,6 +684,11 @@ public class ActivityManager {
*/
public int pid;
+ /**
+ * The user id of this process.
+ */
+ public int uid;
+
public String pkgList[];
/**
@@ -666,8 +742,51 @@ public class ActivityManager {
*/
public int lru;
+ /**
+ * Constant for {@link #importanceReasonCode}: nothing special has
+ * been specified for the reason for this level.
+ */
+ public static final int REASON_UNKNOWN = 0;
+
+ /**
+ * Constant for {@link #importanceReasonCode}: one of the application's
+ * content providers is being used by another process. The pid of
+ * the client process is in {@link #importanceReasonPid} and the
+ * target provider in this process is in
+ * {@link #importanceReasonComponent}.
+ */
+ public static final int REASON_PROVIDER_IN_USE = 1;
+
+ /**
+ * Constant for {@link #importanceReasonCode}: one of the application's
+ * content providers is being used by another process. The pid of
+ * the client process is in {@link #importanceReasonPid} and the
+ * target provider in this process is in
+ * {@link #importanceReasonComponent}.
+ */
+ public static final int REASON_SERVICE_IN_USE = 2;
+
+ /**
+ * The reason for {@link #importance}, if any.
+ */
+ public int importanceReasonCode;
+
+ /**
+ * For the specified values of {@link #importanceReasonCode}, this
+ * is the process ID of the other process that is a client of this
+ * process. This will be 0 if no other process is using this one.
+ */
+ public int importanceReasonPid;
+
+ /**
+ * For the specified values of {@link #importanceReasonCode}, this
+ * is the name of the component that is being used in this process.
+ */
+ public ComponentName importanceReasonComponent;
+
public RunningAppProcessInfo() {
importance = IMPORTANCE_FOREGROUND;
+ importanceReasonCode = REASON_UNKNOWN;
}
public RunningAppProcessInfo(String pProcessName, int pPid, String pArr[]) {
@@ -683,17 +802,25 @@ public class ActivityManager {
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(processName);
dest.writeInt(pid);
+ dest.writeInt(uid);
dest.writeStringArray(pkgList);
dest.writeInt(importance);
dest.writeInt(lru);
+ dest.writeInt(importanceReasonCode);
+ dest.writeInt(importanceReasonPid);
+ ComponentName.writeToParcel(importanceReasonComponent, dest);
}
public void readFromParcel(Parcel source) {
processName = source.readString();
pid = source.readInt();
+ uid = source.readInt();
pkgList = source.readStringArray();
importance = source.readInt();
lru = source.readInt();
+ importanceReasonCode = source.readInt();
+ importanceReasonPid = source.readInt();
+ importanceReasonComponent = ComponentName.readFromParcel(source);
}
public static final Creator<RunningAppProcessInfo> CREATOR =
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 447512a..4796e49 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -29,6 +29,7 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -39,9 +40,6 @@ import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
-import java.io.FileNotFoundException;
-import java.io.FileDescriptor;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -146,6 +144,30 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeInt(result);
return true;
}
+
+ case START_ACTIVITY_PENDING_INTENT_TRANSACTION:
+ {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder b = data.readStrongBinder();
+ IApplicationThread app = ApplicationThreadNative.asInterface(b);
+ PendingIntent intent = PendingIntent.CREATOR.createFromParcel(data);
+ Intent fillInIntent = null;
+ if (data.readInt() != 0) {
+ fillInIntent = Intent.CREATOR.createFromParcel(data);
+ }
+ String resolvedType = data.readString();
+ IBinder resultTo = data.readStrongBinder();
+ String resultWho = data.readString();
+ int requestCode = data.readInt();
+ int flagsMask = data.readInt();
+ int flagsValues = data.readInt();
+ int result = startActivityPendingIntent(app, intent,
+ fillInIntent, resolvedType, resultTo, resultWho,
+ requestCode, flagsMask, flagsValues);
+ reply.writeNoException();
+ reply.writeInt(result);
+ return true;
+ }
case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
{
@@ -512,6 +534,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ ComponentName comp = ComponentName.CREATOR.createFromParcel(data);
+ PendingIntent pi = getRunningServiceControlPanel(comp);
+ reply.writeNoException();
+ PendingIntent.writePendingIntentOrNullToParcel(pi, reply);
+ return true;
+ }
+
case START_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
@@ -551,8 +582,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
ComponentName className = ComponentName.readFromParcel(data);
IBinder token = data.readStrongBinder();
- boolean isForeground = data.readInt() != 0;
- setServiceForeground(className, token, isForeground);
+ int id = data.readInt();
+ Notification notification = null;
+ if (data.readInt() != 0) {
+ notification = Notification.CREATOR.createFromParcel(data);
+ }
+ boolean removeNotification = data.readInt() != 0;
+ setServiceForeground(className, token, id, notification, removeNotification);
reply.writeNoException();
return true;
}
@@ -606,7 +642,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case SERVICE_DONE_EXECUTING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
- serviceDoneExecuting(token);
+ int type = data.readInt();
+ int startId = data.readInt();
+ int res = data.readInt();
+ serviceDoneExecuting(token, type, startId, res);
reply.writeNoException();
return true;
}
@@ -935,13 +974,6 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
- case SYSTEM_READY_TRANSACTION: {
- data.enforceInterface(IActivityManager.descriptor);
- systemReady();
- reply.writeNoException();
- return true;
- }
-
case HANDLE_APPLICATION_ERROR_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder app = data.readStrongBinder();
@@ -1102,6 +1134,25 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeNoException();
return true;
}
+
+ case GET_PROCESS_MEMORY_INFO_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int pid = data.readInt();
+ Debug.MemoryInfo mi = new Debug.MemoryInfo();
+ getProcessMemoryInfo(pid, mi);
+ reply.writeNoException();
+ mi.writeToParcel(reply, 0);
+ return true;
+ }
+
+ case KILL_APPLICATION_PROCESS_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String processName = data.readString();
+ int uid = data.readInt();
+ killApplicationProcess(processName, uid);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -1152,6 +1203,34 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
return result;
}
+ public int startActivityPendingIntent(IApplicationThread caller,
+ PendingIntent intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(caller != null ? caller.asBinder() : null);
+ intent.writeToParcel(data, 0);
+ if (fillInIntent != null) {
+ data.writeInt(1);
+ fillInIntent.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeString(resolvedType);
+ data.writeStrongBinder(resultTo);
+ data.writeString(resultWho);
+ data.writeInt(requestCode);
+ data.writeInt(flagsMask);
+ data.writeInt(flagsValues);
+ mRemote.transact(START_ACTIVITY_PENDING_INTENT_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int result = reply.readInt();
+ reply.recycle();
+ data.recycle();
+ return result;
+ }
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) throws RemoteException {
Parcel data = Parcel.obtain();
@@ -1616,6 +1695,21 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws RemoteException
+ {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ service.writeToParcel(data, 0);
+ mRemote.transact(GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION, data, reply, 0);
+ reply.readException();
+ PendingIntent res = PendingIntent.readPendingIntentOrNullFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException
{
@@ -1664,13 +1758,20 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) throws RemoteException {
+ int id, Notification notification, boolean removeNotification) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
ComponentName.writeToParcel(className, data);
data.writeStrongBinder(token);
- data.writeInt(isForeground ? 1 : 0);
+ data.writeInt(id);
+ if (notification != null) {
+ data.writeInt(1);
+ notification.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
+ data.writeInt(removeNotification ? 1 : 0);
mRemote.transact(SET_SERVICE_FOREGROUND_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
@@ -1737,11 +1838,15 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
- public void serviceDoneExecuting(IBinder token) throws RemoteException {
+ public void serviceDoneExecuting(IBinder token, int type, int startId,
+ int res) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
+ data.writeInt(type);
+ data.writeInt(startId);
+ data.writeInt(res);
mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();
@@ -2212,16 +2317,6 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
- public void systemReady() throws RemoteException
- {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(IActivityManager.descriptor);
- mRemote.transact(SYSTEM_READY_TRANSACTION, data, reply, 0);
- reply.readException();
- data.recycle();
- reply.recycle();
- }
public boolean testIsSystemReady()
{
/* this base class version is never called */
@@ -2408,6 +2503,31 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+
+ public void getProcessMemoryInfo(int pid, Debug.MemoryInfo outInfo)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(pid);
+ mRemote.transact(GET_PROCESS_MEMORY_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ }
+
+ public void killApplicationProcess(String processName, int uid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(processName);
+ data.writeInt(uid);
+ mRemote.transact(KILL_APPLICATION_PROCESS_TRANSACTION, data, reply, 0);
+ reply.readException();
+ data.recycle();
+ reply.recycle();
+ }
private IBinder mRemote;
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index e045105..2877aec 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -68,6 +68,7 @@ import android.view.WindowManagerImpl;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.SamplingProfilerIntegration;
import com.android.internal.util.ArrayUtils;
import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
@@ -87,6 +88,8 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Pattern;
+import dalvik.system.SamplingProfiler;
+
final class IntentReceiverLeaked extends AndroidRuntimeException {
public IntentReceiverLeaked(String msg) {
super(msg);
@@ -126,7 +129,7 @@ public final class ActivityThread {
private static final int LOG_ON_PAUSE_CALLED = 30021;
private static final int LOG_ON_RESUME_CALLED = 30022;
-
+
public static final ActivityThread currentActivityThread() {
return (ActivityThread)sThreadLocal.get();
}
@@ -314,7 +317,7 @@ public final class ActivityThread {
public ApplicationInfo getApplicationInfo() {
return mApplicationInfo;
}
-
+
public boolean isSecurityViolation() {
return mSecurityViolation;
}
@@ -322,7 +325,7 @@ public final class ActivityThread {
/**
* Gets the array of shared libraries that are listed as
* used by the given package.
- *
+ *
* @param packageName the name of the package (note: not its
* file name)
* @return null-ok; the array of shared libraries, each one
@@ -350,7 +353,7 @@ public final class ActivityThread {
* result is a single string with the names of the libraries
* separated by colons, or <code>null</code> if both lists
* were <code>null</code> or empty.
- *
+ *
* @param list1 null-ok; the first list
* @param list2 null-ok; the second list
* @return null-ok; the combination
@@ -378,7 +381,7 @@ public final class ActivityThread {
if (dupCheck && ArrayUtils.contains(list1, s)) {
continue;
}
-
+
if (first) {
first = false;
} else {
@@ -390,7 +393,7 @@ public final class ActivityThread {
return result.toString();
}
-
+
public ClassLoader getClassLoader() {
synchronized (this) {
if (mClassLoader != null) {
@@ -428,7 +431,7 @@ public final class ActivityThread {
if ((mSharedLibraries != null) ||
(instrumentationLibs != null)) {
- zip =
+ zip =
combineLibs(mSharedLibraries, instrumentationLibs)
+ ':' + zip;
}
@@ -485,9 +488,9 @@ public final class ActivityThread {
if (mApplication != null) {
return mApplication;
}
-
+
Application app = null;
-
+
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
@@ -510,7 +513,7 @@ public final class ActivityThread {
mActivityThread.mAllApplications.add(app);
return mApplication = app;
}
-
+
public void removeContextRegistrations(Context context,
String who, String what) {
HashMap<BroadcastReceiver, ReceiverDispatcher> rmap =
@@ -643,7 +646,7 @@ public final class ActivityThread {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<ReceiverDispatcher> mDispatcher;
final ReceiverDispatcher mStrongRef;
-
+
InnerReceiver(ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
@@ -661,7 +664,7 @@ public final class ActivityThread {
}
}
}
-
+
final IIntentReceiver.Stub mIIntentReceiver;
final BroadcastReceiver mReceiver;
final Context mContext;
@@ -770,7 +773,7 @@ public final class ActivityThread {
BroadcastReceiver getIntentReceiver() {
return mReceiver;
}
-
+
IIntentReceiver getIIntentReceiver() {
return mIIntentReceiver;
}
@@ -901,7 +904,7 @@ public final class ActivityThread {
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<ServiceDispatcher> mDispatcher;
-
+
InnerConnection(ServiceDispatcher sd) {
mDispatcher = new WeakReference<ServiceDispatcher>(sd);
}
@@ -913,7 +916,7 @@ public final class ActivityThread {
}
}
}
-
+
private final HashMap<ComponentName, ConnectionInfo> mActiveConnections
= new HashMap<ComponentName, ConnectionInfo>();
@@ -965,7 +968,7 @@ public final class ActivityThread {
IServiceConnection getIServiceConnection() {
return mIServiceConnection;
}
-
+
int getFlags() {
return mFlags;
}
@@ -1191,7 +1194,7 @@ public final class ActivityThread {
+ " mode=" + backupMode + "}";
}
}
-
+
private static final class CreateServiceData {
IBinder token;
ServiceInfo info;
@@ -1215,6 +1218,7 @@ public final class ActivityThread {
private static final class ServiceArgsData {
IBinder token;
int startId;
+ int flags;
Intent args;
public String toString() {
return "ServiceArgsData{token=" + token + " startId=" + startId
@@ -1270,10 +1274,10 @@ public final class ActivityThread {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
private static final String TWO_COUNT_COLUMNS = "%17s %8d %17s %8d";
-
+
// Formatting for checkin service - update version if row format changes
private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 1;
-
+
public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges) {
queueOrSendMessage(
@@ -1342,7 +1346,7 @@ public final class ActivityThread {
synchronized (mRelaunchingActivities) {
mRelaunchingActivities.add(r);
}
-
+
queueOrSendMessage(H.RELAUNCH_ACTIVITY, r, configChanges);
}
@@ -1417,10 +1421,11 @@ public final class ActivityThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
- Intent args) {
+ int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.startId = startId;
+ s.flags = flags;
s.args = args;
queueOrSendMessage(H.SERVICE_ARGS, s);
@@ -1461,6 +1466,10 @@ public final class ActivityThread {
queueOrSendMessage(H.EXIT_APPLICATION, null);
}
+ public final void scheduleSuicide() {
+ queueOrSendMessage(H.SUICIDE, null);
+ }
+
public void requestThumbnail(IBinder token) {
queueOrSendMessage(H.REQUEST_THUMBNAIL, token);
}
@@ -1508,7 +1517,7 @@ public final class ActivityThread {
throws RemoteException {
receiver.performReceive(intent, resultCode, dataStr, extras, ordered);
}
-
+
public void scheduleLowMemory() {
queueOrSendMessage(H.LOW_MEMORY, null);
}
@@ -1524,7 +1533,7 @@ public final class ActivityThread {
} catch (RemoteException e) {
}
}
-
+
public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
ProfilerControlData pcd = new ProfilerControlData();
pcd.path = path;
@@ -1543,7 +1552,11 @@ public final class ActivityThread {
Log.w(TAG, "Failed setting process group to " + group, e);
}
}
-
+
+ public void getMemoryInfo(Debug.MemoryInfo outInfo) {
+ Debug.getMemoryInfo(outInfo);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1579,7 +1592,7 @@ public final class ActivityThread {
long sqliteAllocated = SQLiteDebug.getHeapAllocatedSize() / 1024;
SQLiteDebug.PagerStats stats = new SQLiteDebug.PagerStats();
SQLiteDebug.getPagerStats(stats);
-
+
// Check to see if we were called by checkin server. If so, print terse format.
boolean doCheckinFormat = false;
if (args != null) {
@@ -1587,79 +1600,79 @@ public final class ActivityThread {
if ("-c".equals(arg)) doCheckinFormat = true;
}
}
-
+
// For checkin, we print one long comma-separated list of values
if (doCheckinFormat) {
// NOTE: if you change anything significant below, also consider changing
// ACTIVITY_THREAD_CHECKIN_VERSION.
- String processName = (mBoundApplication != null)
+ String processName = (mBoundApplication != null)
? mBoundApplication.processName : "unknown";
-
+
// Header
pw.print(ACTIVITY_THREAD_CHECKIN_VERSION); pw.print(',');
pw.print(Process.myPid()); pw.print(',');
pw.print(processName); pw.print(',');
-
+
// Heap info - max
pw.print(nativeMax); pw.print(',');
pw.print(dalvikMax); pw.print(',');
pw.print("N/A,");
pw.print(nativeMax + dalvikMax); pw.print(',');
-
+
// Heap info - allocated
pw.print(nativeAllocated); pw.print(',');
pw.print(dalvikAllocated); pw.print(',');
pw.print("N/A,");
pw.print(nativeAllocated + dalvikAllocated); pw.print(',');
-
+
// Heap info - free
pw.print(nativeFree); pw.print(',');
pw.print(dalvikFree); pw.print(',');
pw.print("N/A,");
pw.print(nativeFree + dalvikFree); pw.print(',');
-
+
// Heap info - proportional set size
pw.print(memInfo.nativePss); pw.print(',');
pw.print(memInfo.dalvikPss); pw.print(',');
pw.print(memInfo.otherPss); pw.print(',');
pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
-
+
// Heap info - shared
- pw.print(nativeShared); pw.print(',');
- pw.print(dalvikShared); pw.print(',');
- pw.print(otherShared); pw.print(',');
+ pw.print(nativeShared); pw.print(',');
+ pw.print(dalvikShared); pw.print(',');
+ pw.print(otherShared); pw.print(',');
pw.print(nativeShared + dalvikShared + otherShared); pw.print(',');
-
+
// Heap info - private
- pw.print(nativePrivate); pw.print(',');
+ pw.print(nativePrivate); pw.print(',');
pw.print(dalvikPrivate); pw.print(',');
pw.print(otherPrivate); pw.print(',');
pw.print(nativePrivate + dalvikPrivate + otherPrivate); pw.print(',');
-
+
// Object counts
pw.print(viewInstanceCount); pw.print(',');
pw.print(viewRootInstanceCount); pw.print(',');
pw.print(appContextInstanceCount); pw.print(',');
pw.print(activityInstanceCount); pw.print(',');
-
+
pw.print(globalAssetCount); pw.print(',');
pw.print(globalAssetManagerCount); pw.print(',');
pw.print(binderLocalObjectCount); pw.print(',');
pw.print(binderProxyObjectCount); pw.print(',');
-
+
pw.print(binderDeathObjectCount); pw.print(',');
pw.print(openSslSocketCount); pw.print(',');
-
+
// SQL
pw.print(sqliteAllocated); pw.print(',');
- pw.print(stats.databaseBytes / 1024); pw.print(',');
+ pw.print(stats.databaseBytes / 1024); pw.print(',');
pw.print(stats.numPagers); pw.print(',');
pw.print((stats.totalBytes - stats.referencedBytes) / 1024); pw.print(',');
pw.print(stats.referencedBytes / 1024); pw.print('\n');
-
+
return;
}
-
+
// otherwise, show human-readable format
printRow(pw, HEAP_COLUMN, "", "native", "dalvik", "other", "total");
printRow(pw, HEAP_COLUMN, "size:", nativeMax, dalvikMax, "N/A", nativeMax + dalvikMax);
@@ -1692,7 +1705,7 @@ public final class ActivityThread {
printRow(pw, ONE_COUNT_COLUMN, "Death Recipients:", binderDeathObjectCount);
printRow(pw, ONE_COUNT_COLUMN, "OpenSSL Sockets:", openSslSocketCount);
-
+
// SQLite mem info
pw.println(" ");
pw.println(" SQL");
@@ -1701,7 +1714,7 @@ public final class ActivityThread {
printRow(pw, TWO_COUNT_COLUMNS, "numPagers:", stats.numPagers, "inactivePageKB:",
(stats.totalBytes - stats.referencedBytes) / 1024);
printRow(pw, ONE_COUNT_COLUMN, "activePageKB:", stats.referencedBytes / 1024);
-
+
// Asset details.
String assetAlloc = AssetManager.getAssetAllocations();
if (assetAlloc != null) {
@@ -1717,6 +1730,10 @@ public final class ActivityThread {
}
private final class H extends Handler {
+ private H() {
+ SamplingProfiler.getInstance().setEventThread(mLooper.getThread());
+ }
+
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
@@ -1746,7 +1763,8 @@ public final class ActivityThread {
public static final int RELAUNCH_ACTIVITY = 126;
public static final int PROFILER_CONTROL = 127;
public static final int CREATE_BACKUP_AGENT = 128;
- public static final int DESTROY_BACKUP_AGENT = 129;
+ public static final int DESTROY_BACKUP_AGENT = 129;
+ public static final int SUICIDE = 130;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -1780,6 +1798,7 @@ public final class ActivityThread {
case PROFILER_CONTROL: return "PROFILER_CONTROL";
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
+ case SUICIDE: return "SUICIDE";
}
}
return "(unknown)";
@@ -1799,6 +1818,7 @@ public final class ActivityThread {
} break;
case PAUSE_ACTIVITY:
handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);
+ maybeSnapshot();
break;
case PAUSE_ACTIVITY_FINISHING:
handlePauseActivity((IBinder)msg.obj, true, msg.arg1 != 0, msg.arg2);
@@ -1841,6 +1861,7 @@ public final class ActivityThread {
break;
case RECEIVER:
handleReceiver((ReceiverData)msg.obj);
+ maybeSnapshot();
break;
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
@@ -1856,6 +1877,7 @@ public final class ActivityThread {
break;
case STOP_SERVICE:
handleStopService((IBinder)msg.obj);
+ maybeSnapshot();
break;
case REQUEST_THUMBNAIL:
handleRequestThumbnail((IBinder)msg.obj);
@@ -1888,6 +1910,18 @@ public final class ActivityThread {
case DESTROY_BACKUP_AGENT:
handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
break;
+ case SUICIDE:
+ {
+ Process.killProcess(Process.myPid());
+ }
+ break;
+ }
+ }
+
+ void maybeSnapshot() {
+ if (mBoundApplication != null) {
+ SamplingProfilerIntegration.writeSnapshot(
+ mBoundApplication.processName);
}
}
}
@@ -1930,13 +1964,13 @@ public final class ActivityThread {
final private String mResDir;
final private float mScale;
final private int mHash;
-
+
ResourcesKey(String resDir, float scale) {
mResDir = resDir;
mScale = scale;
mHash = mResDir.hashCode() << 2 + (int) (mScale * 2);
}
-
+
@Override
public int hashCode() {
return mHash;
@@ -1987,7 +2021,7 @@ public final class ActivityThread {
final ArrayList<ActivityRecord> mRelaunchingActivities
= new ArrayList<ActivityRecord>();
Configuration mPendingConfiguration = null;
-
+
// These can be accessed by multiple threads; mPackages is the lock.
// XXX For now we keep around information about all packages we have
// seen, not removing entries from this map.
@@ -2122,7 +2156,7 @@ public final class ActivityThread {
return false;
}
}
-
+
ActivityThread() {
}
@@ -2155,11 +2189,11 @@ public final class ActivityThread {
public Application getApplication() {
return mInitialApplication;
}
-
+
public String getProcessName() {
return mBoundApplication.processName;
}
-
+
public ApplicationContext getSystemContext() {
synchronized (this) {
if (mSystemContext == null) {
@@ -2214,7 +2248,7 @@ public final class ActivityThread {
}
return aInfo;
}
-
+
public final Activity startActivityNow(Activity parent, String id,
Intent intent, ActivityInfo activityInfo, IBinder token, Bundle state,
Object lastNonConfigurationInstance) {
@@ -2297,7 +2331,7 @@ public final class ActivityThread {
r.packageInfo = getPackageInfo(aInfo.applicationInfo,
Context.CONTEXT_INCLUDE_CODE);
}
-
+
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
@@ -2329,7 +2363,7 @@ public final class ActivityThread {
try {
Application app = r.packageInfo.makeApplication(false);
-
+
if (localLOGV) Log.v(TAG, "Performing launch of " + r);
if (localLOGV) Log.v(
TAG, r + ": app=" + app
@@ -2348,7 +2382,7 @@ public final class ActivityThread {
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstance,
r.lastNonConfigurationChildInstances, config);
-
+
if (customIntent != null) {
activity.mIntent = customIntent;
}
@@ -2486,7 +2520,7 @@ public final class ActivityThread {
}
}
}
-
+
private final void handleNewIntent(NewIntentData data) {
performNewIntents(data.token, data.intents);
}
@@ -2524,7 +2558,7 @@ public final class ActivityThread {
try {
Application app = packageInfo.makeApplication(false);
-
+
if (localLOGV) Log.v(
TAG, "Performing receive of " + data.intent
+ ": app=" + app
@@ -2581,7 +2615,7 @@ public final class ActivityThread {
+ " already exists");
return;
}
-
+
BackupAgent agent = null;
String classname = data.appInfo.backupAgentName;
if (classname == null) {
@@ -2635,7 +2669,7 @@ public final class ActivityThread {
// Tear down a BackupAgent
private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data);
-
+
PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
String packageName = packageInfo.mPackageName;
BackupAgent agent = mBackupAgents.get(packageName);
@@ -2684,7 +2718,8 @@ public final class ActivityThread {
service.onCreate();
mServices.put(data.token, service);
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2710,7 +2745,7 @@ public final class ActivityThread {
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token);
+ data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2736,7 +2771,7 @@ public final class ActivityThread {
data.token, data.intent, doRebind);
} else {
ActivityManagerNative.getDefault().serviceDoneExecuting(
- data.token);
+ data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2773,9 +2808,10 @@ public final class ActivityThread {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
- s.onStart(data.args, data.startId);
+ int res = s.onStartCommand(data.args, data.flags, data.startId);
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ data.token, 1, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2801,7 +2837,8 @@ public final class ActivityThread {
((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
}
try {
- ActivityManagerNative.getDefault().serviceDoneExecuting(token);
+ ActivityManagerNative.getDefault().serviceDoneExecuting(
+ token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2837,9 +2874,9 @@ public final class ActivityThread {
}
r.activity.performResume();
- EventLog.writeEvent(LOG_ON_RESUME_CALLED,
+ EventLog.writeEvent(LOG_ON_RESUME_CALLED,
r.activity.getComponentName().getClassName());
-
+
r.paused = false;
r.stopped = false;
if (r.activity.mStartedActivity) {
@@ -2875,7 +2912,7 @@ public final class ActivityThread {
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
-
+
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
@@ -2994,7 +3031,7 @@ public final class ActivityThread {
if (userLeaving) {
performUserLeavingActivity(r);
}
-
+
r.activity.mConfigChangeFlags |= configChanges;
Bundle state = performPauseActivity(token, finished, true);
@@ -3171,7 +3208,7 @@ public final class ActivityThread {
+ " win=" + r.window);
updateVisibility(r, show);
-
+
// Tell activity manager we have been stopped.
try {
ActivityManagerNative.getDefault().activityStopped(
@@ -3287,7 +3324,7 @@ public final class ActivityThread {
try {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
- EventLog.writeEvent(LOG_ON_PAUSE_CALLED,
+ EventLog.writeEvent(LOG_ON_PAUSE_CALLED,
r.activity.getComponentName().getClassName());
if (!r.activity.mCalled) {
throw new SuperNotCalledException(
@@ -3344,7 +3381,7 @@ public final class ActivityThread {
+ ": " + e.toString(), e);
}
}
-
+
}
try {
r.activity.mCalled = false;
@@ -3426,7 +3463,7 @@ public final class ActivityThread {
unscheduleGcIdler();
Configuration changedConfig = null;
-
+
// First: make sure we have the most recent configuration and most
// recent version of the activity, or skip it if some previous call
// had taken a more recent version.
@@ -3443,38 +3480,38 @@ public final class ActivityThread {
N--;
}
}
-
+
if (tmp == null) {
return;
}
-
+
if (mPendingConfiguration != null) {
changedConfig = mPendingConfiguration;
mPendingConfiguration = null;
}
}
-
+
// If there was a pending configuration change, execute it first.
if (changedConfig != null) {
handleConfigurationChanged(changedConfig);
}
-
+
ActivityRecord r = mActivities.get(tmp.token);
if (localLOGV) Log.v(TAG, "Handling relaunch of " + r);
if (r == null) {
return;
}
-
+
r.activity.mConfigChangeFlags |= configChanges;
Intent currentIntent = r.activity.mIntent;
-
+
Bundle savedState = null;
if (!r.paused) {
savedState = performPauseActivity(r.token, false, true);
}
-
+
handleDestroyActivity(r.token, false, configChanges, true);
-
+
r.activity = null;
r.window = null;
r.hideForNow = false;
@@ -3498,7 +3535,7 @@ public final class ActivityThread {
if (savedState != null) {
r.state = savedState;
}
-
+
handleLaunchActivity(r, currentIntent);
}
@@ -3528,7 +3565,7 @@ public final class ActivityThread {
boolean allActivities, Configuration newConfig) {
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
-
+
if (mActivities.size() > 0) {
Iterator<ActivityRecord> it = mActivities.values().iterator();
while (it.hasNext()) {
@@ -3569,10 +3606,10 @@ public final class ActivityThread {
for (int i=0; i<N; i++) {
callbacks.add(mAllApplications.get(i));
}
-
+
return callbacks;
}
-
+
private final void performConfigurationChanged(
ComponentCallbacks cb, Configuration config) {
// Only for Activity objects, check that they actually call up to their
@@ -3582,18 +3619,18 @@ public final class ActivityThread {
if (activity != null) {
activity.mCalled = false;
}
-
+
boolean shouldChangeConfig = false;
if ((activity == null) || (activity.mCurrentConfig == null)) {
shouldChangeConfig = true;
} else {
-
+
// If the new config is the same as the config this Activity
// is already running with then don't bother calling
// onConfigurationChanged
int diff = activity.mCurrentConfig.diff(config);
if (diff != 0) {
-
+
// If this activity doesn't handle any of the config changes
// then don't bother calling onConfigurationChanged as we're
// going to destroy it.
@@ -3602,10 +3639,10 @@ public final class ActivityThread {
}
}
}
-
+
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
-
+
if (activity != null) {
if (!activity.mCalled) {
throw new SuperNotCalledException(
@@ -3619,17 +3656,17 @@ public final class ActivityThread {
}
final void handleConfigurationChanged(Configuration config) {
-
+
synchronized (mRelaunchingActivities) {
if (mPendingConfiguration != null) {
config = mPendingConfiguration;
mPendingConfiguration = null;
}
}
-
+
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
-
+
synchronized(mPackages) {
if (mConfiguration == null) {
mConfiguration = new Configuration();
@@ -3664,10 +3701,10 @@ public final class ActivityThread {
}
}
}
-
+
callbacks = collectComponentCallbacksLocked(false, config);
}
-
+
final int N = callbacks.size();
for (int i=0; i<N; i++) {
performConfigurationChanged(callbacks.get(i), config);
@@ -3679,7 +3716,7 @@ public final class ActivityThread {
if (r == null || r.activity == null) {
return;
}
-
+
performConfigurationChanged(r.activity, mConfiguration);
}
@@ -3702,7 +3739,7 @@ public final class ActivityThread {
Debug.stopMethodTracing();
}
}
-
+
final void handleLowMemory() {
ArrayList<ComponentCallbacks> callbacks
= new ArrayList<ComponentCallbacks>();
@@ -3710,7 +3747,7 @@ public final class ActivityThread {
synchronized(mPackages) {
callbacks = collectComponentCallbacksLocked(true, null);
}
-
+
final int N = callbacks.size();
for (int i=0; i<N; i++) {
callbacks.get(i).onLowMemory();
@@ -3721,7 +3758,7 @@ public final class ActivityThread {
int sqliteReleased = SQLiteDatabase.releaseMemory();
EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
}
-
+
// Ask graphics to free up as much as possible (font/image caches)
Canvas.freeCaches();
@@ -3768,7 +3805,7 @@ public final class ActivityThread {
== 0) {
Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
}
-
+
if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
@@ -4193,6 +4230,8 @@ public final class ActivityThread {
}
public static final void main(String[] args) {
+ SamplingProfilerIntegration.start();
+
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
@@ -4208,8 +4247,11 @@ public final class ActivityThread {
thread.detach();
String name;
- if (thread.mInitialApplication != null) name = thread.mInitialApplication.getPackageName();
- else name = "<unknown>";
+ if (thread.mInitialApplication != null) {
+ name = thread.mInitialApplication.getPackageName();
+ } else {
+ name = "<unknown>";
+ }
Log.i(TAG, "Main thread of " + name + " is now exiting");
}
}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 92929ea..487cfda 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -22,8 +22,8 @@ import com.google.android.collect.Maps;
import org.xmlpull.v1.XmlPullParserException;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.IBluetooth;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -40,6 +40,7 @@ import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
@@ -59,8 +60,6 @@ import android.content.res.XmlResourceParser;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.SensorManager;
import android.location.ILocationManager;
@@ -78,7 +77,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -88,14 +86,14 @@ import android.os.FileUtils.FileStatus;
import android.telephony.TelephonyManager;
import android.text.ClipboardManager;
import android.util.AndroidRuntimeException;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
-import android.view.Display;
import android.view.LayoutInflater;
import android.view.WindowManagerImpl;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import android.accounts.AccountManager;
+import android.accounts.IAccountManager;
import java.io.File;
import java.io.FileInputStream;
@@ -155,14 +153,12 @@ class ApplicationContext extends Context {
private final static boolean DEBUG_ICONS = false;
private static final Object sSync = new Object();
+ private static AccountManager sAccountManager;
private static AlarmManager sAlarmManager;
private static PowerManager sPowerManager;
private static ConnectivityManager sConnectivityManager;
private static WifiManager sWifiManager;
private static LocationManager sLocationManager;
- private static boolean sIsBluetoothDeviceCached = false;
- private static BluetoothDevice sBluetoothDevice;
- private static IWallpaperService sWallpaperService;
private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =
new HashMap<File, SharedPreferencesImpl>();
@@ -177,8 +173,8 @@ class ApplicationContext extends Context {
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager = null;
- private AccessibilityManager mAccessibilityManager = null;
private ActivityManager mActivityManager = null;
+ private WallpaperManager mWallpaperManager = null;
private Context mReceiverRestrictedContext = null;
private SearchManager mSearchManager = null;
private SensorManager mSensorManager = null;
@@ -187,6 +183,8 @@ class ApplicationContext extends Context {
private StatusBarManager mStatusBarManager = null;
private TelephonyManager mTelephonyManager = null;
private ClipboardManager mClipboardManager = null;
+ private boolean mIsBluetoothAdapterCached = false;
+ private BluetoothAdapter mBluetoothAdapter;
private boolean mRestricted;
private final Object mSync = new Object();
@@ -198,9 +196,6 @@ class ApplicationContext extends Context {
private File mCacheDir;
- private Drawable mWallpaper;
- private IWallpaperServiceCallback mWallpaperCallback = null;
-
private static long sInstanceCount = 0;
private static final String[] EMPTY_FILE_LIST = {};
@@ -520,127 +515,37 @@ class ApplicationContext extends Context {
@Override
public Drawable getWallpaper() {
- Drawable dr = peekWallpaper();
- return dr != null ? dr : getResources().getDrawable(
- com.android.internal.R.drawable.default_wallpaper);
+ return getWallpaperManager().getDrawable();
}
@Override
- public synchronized Drawable peekWallpaper() {
- if (mWallpaper != null) {
- return mWallpaper;
- }
- mWallpaperCallback = new WallpaperCallback(this);
- mWallpaper = getCurrentWallpaperLocked();
- return mWallpaper;
- }
-
- private Drawable getCurrentWallpaperLocked() {
- try {
- ParcelFileDescriptor fd = getWallpaperService().getWallpaper(mWallpaperCallback);
- if (fd != null) {
- Bitmap bm = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
- if (bm != null) {
- // For now clear the density until we figure out how
- // to deal with it for wallpapers.
- bm.setDensity(0);
- return new BitmapDrawable(getResources(), bm);
- }
- }
- } catch (RemoteException e) {
- }
- return null;
+ public Drawable peekWallpaper() {
+ return getWallpaperManager().peekDrawable();
}
@Override
public int getWallpaperDesiredMinimumWidth() {
- try {
- return getWallpaperService().getWidthHint();
- } catch (RemoteException e) {
- // Shouldn't happen!
- return 0;
- }
+ return getWallpaperManager().getDesiredMinimumWidth();
}
@Override
public int getWallpaperDesiredMinimumHeight() {
- try {
- return getWallpaperService().getHeightHint();
- } catch (RemoteException e) {
- // Shouldn't happen!
- return 0;
- }
+ return getWallpaperManager().getDesiredMinimumHeight();
}
@Override
public void setWallpaper(Bitmap bitmap) throws IOException {
- try {
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- } catch (RemoteException e) {
- }
+ getWallpaperManager().setBitmap(bitmap);
}
@Override
public void setWallpaper(InputStream data) throws IOException {
- try {
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd == null) {
- return;
- }
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(data, fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- } catch (RemoteException e) {
- }
- }
-
- private void setWallpaper(InputStream data, FileOutputStream fos)
- throws IOException {
- byte[] buffer = new byte[32768];
- int amt;
- while ((amt=data.read(buffer)) > 0) {
- fos.write(buffer, 0, amt);
- }
+ getWallpaperManager().setStream(data);
}
@Override
public void clearWallpaper() throws IOException {
- try {
- /* Set the wallpaper to the default values */
- ParcelFileDescriptor fd = getWallpaperService().setWallpaper();
- if (fd != null) {
- FileOutputStream fos = null;
- try {
- fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
- setWallpaper(getResources().openRawResource(
- com.android.internal.R.drawable.default_wallpaper),
- fos);
- } finally {
- if (fos != null) {
- fos.close();
- }
- }
- }
- } catch (RemoteException e) {
- }
+ getWallpaperManager().clear();
}
@Override
@@ -901,8 +806,12 @@ class ApplicationContext extends Context {
}
} else if (ACTIVITY_SERVICE.equals(name)) {
return getActivityManager();
+ } else if (INPUT_METHOD_SERVICE.equals(name)) {
+ return InputMethodManager.getInstance(this);
} else if (ALARM_SERVICE.equals(name)) {
return getAlarmManager();
+ } else if (ACCOUNT_SERVICE.equals(name)) {
+ return getAccountManager();
} else if (POWER_SERVICE.equals(name)) {
return getPowerManager();
} else if (CONNECTIVITY_SERVICE.equals(name)) {
@@ -919,10 +828,10 @@ class ApplicationContext extends Context {
return getLocationManager();
} else if (SEARCH_SERVICE.equals(name)) {
return getSearchManager();
- } else if ( SENSOR_SERVICE.equals(name)) {
+ } else if (SENSOR_SERVICE.equals(name)) {
return getSensorManager();
} else if (BLUETOOTH_SERVICE.equals(name)) {
- return getBluetoothDevice();
+ return getBluetoothAdapter();
} else if (VIBRATOR_SERVICE.equals(name)) {
return getVibrator();
} else if (STATUS_BAR_SERVICE.equals(name)) {
@@ -938,13 +847,24 @@ class ApplicationContext extends Context {
return getTelephonyManager();
} else if (CLIPBOARD_SERVICE.equals(name)) {
return getClipboardManager();
- } else if (INPUT_METHOD_SERVICE.equals(name)) {
- return InputMethodManager.getInstance(this);
+ } else if (WALLPAPER_SERVICE.equals(name)) {
+ return getWallpaperManager();
}
return null;
}
+ private AccountManager getAccountManager() {
+ synchronized (sSync) {
+ if (sAccountManager == null) {
+ IBinder b = ServiceManager.getService(ACCOUNT_SERVICE);
+ IAccountManager service = IAccountManager.Stub.asInterface(b);
+ sAccountManager = new AccountManager(this, service);
+ }
+ }
+ return sAccountManager;
+ }
+
private ActivityManager getActivityManager() {
synchronized (mSync) {
if (mActivityManager == null) {
@@ -1001,8 +921,7 @@ class ApplicationContext extends Context {
return sWifiManager;
}
- private NotificationManager getNotificationManager()
- {
+ private NotificationManager getNotificationManager() {
synchronized (mSync) {
if (mNotificationManager == null) {
mNotificationManager = new NotificationManager(
@@ -1013,6 +932,16 @@ class ApplicationContext extends Context {
return mNotificationManager;
}
+ private WallpaperManager getWallpaperManager() {
+ synchronized (mSync) {
+ if (mWallpaperManager == null) {
+ mWallpaperManager = new WallpaperManager(getOuterContext(),
+ mMainThread.getHandler());
+ }
+ }
+ return mWallpaperManager;
+ }
+
private TelephonyManager getTelephonyManager() {
synchronized (mSync) {
if (mTelephonyManager == null) {
@@ -1052,21 +981,16 @@ class ApplicationContext extends Context {
return mSearchManager;
}
- private BluetoothDevice getBluetoothDevice() {
- if (sIsBluetoothDeviceCached) {
- return sBluetoothDevice;
- }
- synchronized (sSync) {
+ private synchronized BluetoothAdapter getBluetoothAdapter() {
+ if (!mIsBluetoothAdapterCached) {
+ mIsBluetoothAdapterCached = true;
IBinder b = ServiceManager.getService(BLUETOOTH_SERVICE);
- if (b == null) {
- sBluetoothDevice = null;
- } else {
- IBluetoothDevice service = IBluetoothDevice.Stub.asInterface(b);
- sBluetoothDevice = new BluetoothDevice(service);
+ if (b != null) {
+ IBluetooth service = IBluetooth.Stub.asInterface(b);
+ mBluetoothAdapter = new BluetoothAdapter(service);
}
- sIsBluetoothDeviceCached = true;
}
- return sBluetoothDevice;
+ return mBluetoothAdapter;
}
private SensorManager getSensorManager() {
@@ -1087,16 +1011,6 @@ class ApplicationContext extends Context {
return mVibrator;
}
- private IWallpaperService getWallpaperService() {
- synchronized (sSync) {
- if (sWallpaperService == null) {
- IBinder b = ServiceManager.getService(WALLPAPER_SERVICE);
- sWallpaperService = IWallpaperService.Stub.asInterface(b);
- }
- }
- return sWallpaperService;
- }
-
private AudioManager getAudioManager()
{
if (mAudioManager == null) {
@@ -1710,6 +1624,15 @@ class ApplicationContext extends Context {
}
@Override
+ public FeatureInfo[] getSystemAvailableFeatures() {
+ try {
+ return mPM.getSystemAvailableFeatures();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
public int checkPermission(String permName, String pkgName) {
try {
return mPM.checkPermission(permName, pkgName);
@@ -1746,6 +1669,15 @@ class ApplicationContext extends Context {
}
@Override
+ public int checkSignatures(int uid1, int uid2) {
+ try {
+ return mPM.checkUidSignatures(uid1, uid2);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
try {
return mPM.getPackagesForUid(uid);
@@ -2801,25 +2733,4 @@ class ApplicationContext extends Context {
return false;
}
}
-
- private static class WallpaperCallback extends IWallpaperServiceCallback.Stub {
- private WeakReference<ApplicationContext> mContext;
-
- public WallpaperCallback(ApplicationContext context) {
- mContext = new WeakReference<ApplicationContext>(context);
- }
-
- public synchronized void onWallpaperChanged() {
-
- /* The wallpaper has changed but we shouldn't eagerly load the
- * wallpaper as that would be inefficient. Reset the cached wallpaper
- * to null so if the user requests the wallpaper again then we'll
- * fetch it.
- */
- final ApplicationContext applicationContext = mContext.get();
- if (applicationContext != null) {
- applicationContext.mWallpaper = null;
- }
- }
- }
}
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 6b17236..aeae5f9 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -171,6 +171,11 @@ public class ApplicationErrorReport implements Parcelable {
public String throwMethodName;
/**
+ * Line number the exception was thrown from.
+ */
+ public int throwLineNumber;
+
+ /**
* Stack trace.
*/
public String stackTrace;
@@ -190,6 +195,7 @@ public class ApplicationErrorReport implements Parcelable {
throwFileName = in.readString();
throwClassName = in.readString();
throwMethodName = in.readString();
+ throwLineNumber = in.readInt();
stackTrace = in.readString();
}
@@ -202,6 +208,7 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(throwFileName);
dest.writeString(throwClassName);
dest.writeString(throwMethodName);
+ dest.writeInt(throwLineNumber);
dest.writeString(stackTrace);
}
@@ -214,6 +221,7 @@ public class ApplicationErrorReport implements Parcelable {
pw.println(prefix + "throwFileName: " + throwFileName);
pw.println(prefix + "throwClassName: " + throwClassName);
pw.println(prefix + "throwMethodName: " + throwMethodName);
+ pw.println(prefix + "throwLineNumber: " + throwLineNumber);
pw.println(prefix + "stackTrace: " + stackTrace);
}
}
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index a3c6325..928981d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -26,6 +26,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.IBinder;
@@ -206,8 +207,14 @@ public abstract class ApplicationThreadNative extends Binder
data.enforceInterface(IApplicationThread.descriptor);
IBinder token = data.readStrongBinder();
int startId = data.readInt();
- Intent args = Intent.CREATOR.createFromParcel(data);
- scheduleServiceArgs(token, startId, args);
+ int fl = data.readInt();
+ Intent args;
+ if (data.readInt() != 0) {
+ args = Intent.CREATOR.createFromParcel(data);
+ } else {
+ args = null;
+ }
+ scheduleServiceArgs(token, startId, fl, args);
return true;
}
@@ -251,6 +258,13 @@ public abstract class ApplicationThreadNative extends Binder
return true;
}
+ case SCHEDULE_SUICIDE_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ scheduleSuicide();
+ return true;
+ }
+
case REQUEST_THUMBNAIL_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -364,6 +378,16 @@ public abstract class ApplicationThreadNative extends Binder
scheduleDestroyBackupAgent(appInfo);
return true;
}
+
+ case GET_MEMORY_INFO_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ Debug.MemoryInfo mi = new Debug.MemoryInfo();
+ getMemoryInfo(mi);
+ reply.writeNoException();
+ mi.writeToParcel(reply, 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -524,7 +548,8 @@ class ApplicationThreadProxy implements IApplicationThread {
data.writeInterfaceToken(IApplicationThread.descriptor);
app.writeToParcel(data, 0);
data.writeInt(backupMode);
- mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0);
+ mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
data.recycle();
}
@@ -532,7 +557,8 @@ class ApplicationThreadProxy implements IApplicationThread {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
app.writeToParcel(data, 0);
- mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0);
+ mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
data.recycle();
}
@@ -571,12 +597,18 @@ class ApplicationThreadProxy implements IApplicationThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
- Intent args) throws RemoteException {
+ int flags, Intent args) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(startId);
- args.writeToParcel(data, 0);
+ data.writeInt(flags);
+ if (args != null) {
+ data.writeInt(1);
+ args.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
@@ -627,7 +659,15 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
+ public final void scheduleSuicide() throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(SCHEDULE_SUICIDE_TRANSACTION, data, null,
+ IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
public final void requestThumbnail(IBinder token)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -742,5 +782,16 @@ class ApplicationThreadProxy implements IApplicationThread {
IBinder.FLAG_ONEWAY);
data.recycle();
}
+
+ public void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ mRemote.transact(GET_MEMORY_INFO_TRANSACTION, data, reply, 0);
+ reply.readException();
+ outInfo.readFromParcel(reply);
+ data.recycle();
+ reply.recycle();
+ }
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index 9432755..1b96af9 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -21,6 +21,7 @@ import com.android.internal.policy.PolicyManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.ComponentName;
+import android.content.ContextWrapper;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
@@ -576,6 +577,12 @@ public class Dialog implements DialogInterface, Window.Callback,
public void onWindowFocusChanged(boolean hasFocus) {
}
+ public void onAttachedToWindow() {
+ }
+
+ public void onDetachedFromWindow() {
+ }
+
/**
* Called to process key events. You can override this to intercept all
* key events before they are dispatched to the window. Be sure to call
@@ -795,14 +802,31 @@ public class Dialog implements DialogInterface, Window.Callback,
// associate search with owner activity if possible (otherwise it will default to
// global search).
- final ComponentName appName = mOwnerActivity == null ? null
- : mOwnerActivity.getComponentName();
+ final ComponentName appName = getAssociatedActivity();
final boolean globalSearch = (appName == null);
searchManager.startSearch(null, false, appName, null, globalSearch);
dismiss();
return true;
}
+ /**
+ * @return The activity associated with this dialog, or null if there is no assocaited activity.
+ */
+ private ComponentName getAssociatedActivity() {
+ Activity activity = mOwnerActivity;
+ Context context = getContext();
+ while (activity == null && context != null) {
+ if (context instanceof Activity) {
+ activity = (Activity) context; // found it!
+ } else {
+ context = (context instanceof ContextWrapper) ?
+ ((ContextWrapper) context).getBaseContext() : // unwrap one level
+ null; // done
+ }
+ }
+ return activity == null ? null : activity.getComponentName();
+ }
+
/**
* Request that key events come to this dialog. Use this if your
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index f6ef549..8244645 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -30,6 +30,7 @@ import android.content.pm.ProviderInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.Debug;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
@@ -76,10 +77,16 @@ public interface IActivityManager extends IInterface {
public static final int START_CLASS_NOT_FOUND = -2;
public static final int START_FORWARD_AND_REQUEST_CONFLICT = -3;
public static final int START_PERMISSION_DENIED = -4;
+ public static final int START_NOT_ACTIVITY = -5;
+ public static final int START_CANCELED = -6;
public int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo, String resultWho, int requestCode,
boolean onlyIfNeeded, boolean debug) throws RemoteException;
+ public int startActivityPendingIntent(IApplicationThread caller,
+ PendingIntent intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) throws RemoteException;
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) throws RemoteException;
public boolean finishActivity(IBinder token, int code, Intent data)
@@ -125,13 +132,15 @@ public interface IActivityManager extends IInterface {
public void finishOtherInstances(IBinder token, ComponentName className) throws RemoteException;
/* oneway */
public void reportThumbnail(IBinder token,
- Bitmap thumbnail, CharSequence description) throws RemoteException;
+ Bitmap thumbnail, CharSequence description) throws RemoteException;
public ContentProviderHolder getContentProvider(IApplicationThread caller,
- String name) throws RemoteException;
+ String name) throws RemoteException;
public void removeContentProvider(IApplicationThread caller,
- String name) throws RemoteException;
+ String name) throws RemoteException;
public void publishContentProviders(IApplicationThread caller,
- List<ContentProviderHolder> providers) throws RemoteException;
+ List<ContentProviderHolder> providers) throws RemoteException;
+ public PendingIntent getRunningServiceControlPanel(ComponentName service)
+ throws RemoteException;
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType) throws RemoteException;
public int stopService(IApplicationThread caller, Intent service,
@@ -139,7 +148,7 @@ public interface IActivityManager extends IInterface {
public boolean stopServiceToken(ComponentName className, IBinder token,
int startId) throws RemoteException;
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) throws RemoteException;
+ int id, Notification notification, boolean keepNotification) throws RemoteException;
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags) throws RemoteException;
@@ -149,13 +158,15 @@ public interface IActivityManager extends IInterface {
public void unbindFinished(IBinder token, Intent service,
boolean doRebind) throws RemoteException;
/* oneway */
- public void serviceDoneExecuting(IBinder token) throws RemoteException;
+ public void serviceDoneExecuting(IBinder token, int type, int startId,
+ int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
throws RemoteException;
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
+ public void killApplicationProcess(String processName, int uid) throws RemoteException;
public boolean startInstrumentation(ComponentName className, String profileFile,
int flags, Bundle arguments, IInstrumentationWatcher watcher)
@@ -230,7 +241,6 @@ public interface IActivityManager extends IInterface {
// Special low-level communication with activity manager.
public void startRunning(String pkg, String cls, String action,
String data) throws RemoteException;
- public void systemReady() throws RemoteException;
// Returns 1 if the user wants to debug.
public int handleApplicationError(IBinder app,
int flags, /* 1 == can debug */
@@ -271,6 +281,9 @@ public interface IActivityManager extends IInterface {
public void closeSystemDialogs(String reason) throws RemoteException;
+ public void getProcessMemoryInfo(int pid, Debug.MemoryInfo outInfo)
+ throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -362,7 +375,7 @@ public interface IActivityManager extends IInterface {
int PUBLISH_CONTENT_PROVIDERS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int SET_PERSISTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
int FINISH_SUB_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
- int SYSTEM_READY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
+ int GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
int START_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+33;
int STOP_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+34;
int BIND_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+35;
@@ -427,4 +440,7 @@ public interface IActivityManager extends IInterface {
int START_ACTIVITY_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+94;
int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
+ int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
+ int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
+ int START_ACTIVITY_PENDING_INTENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99;
}
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index c915770..8dda898 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.Debug;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.IBinder;
@@ -71,7 +72,8 @@ public interface IApplicationThread extends IInterface {
Intent intent, boolean rebind) throws RemoteException;
void scheduleUnbindService(IBinder token,
Intent intent) throws RemoteException;
- void scheduleServiceArgs(IBinder token, int startId, Intent args) throws RemoteException;
+ void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
+ throws RemoteException;
void scheduleStopService(IBinder token) throws RemoteException;
static final int DEBUG_OFF = 0;
static final int DEBUG_ON = 1;
@@ -81,6 +83,7 @@ public interface IApplicationThread extends IInterface {
IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
Configuration config, Map<String, IBinder> services) throws RemoteException;
void scheduleExit() throws RemoteException;
+ void scheduleSuicide() throws RemoteException;
void requestThumbnail(IBinder token) throws RemoteException;
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
void updateTimeZone() throws RemoteException;
@@ -96,6 +99,7 @@ public interface IApplicationThread extends IInterface {
void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
+ void getMemoryInfo(Debug.MemoryInfo outInfo) throws RemoteException;
String descriptor = "android.app.IApplicationThread";
@@ -129,4 +133,6 @@ public interface IApplicationThread extends IInterface {
int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
+ int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
+ int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
}
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index bd72544..ed5dd3d 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -36,6 +36,17 @@ interface ISearchManager {
boolean globalSearch,
ISearchManagerCallback searchManagerCallback,
int ident);
+
+ void triggerSearch(in String query,
+ in ComponentName launchActivity,
+ in Bundle appSearchData,
+ ISearchManagerCallback searchManagerCallback,
+ boolean globalSearch,
+ int ident);
+
void stopSearch();
+
+
boolean isVisible();
+
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
new file mode 100644
index 0000000..69f64a1
--- /dev/null
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.app.IWallpaperManagerCallback;
+import android.app.WallpaperInfo;
+import android.content.ComponentName;
+
+/** @hide */
+interface IWallpaperManager {
+
+ /**
+ * Set the wallpaper.
+ */
+ ParcelFileDescriptor setWallpaper(String name);
+
+ /**
+ * Set the live wallpaper.
+ */
+ void setWallpaperComponent(in ComponentName name);
+
+ /**
+ * Get the wallpaper.
+ */
+ ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
+ out Bundle outParams);
+
+ /**
+ * Get information about a live wallpaper.
+ */
+ WallpaperInfo getWallpaperInfo();
+
+ /**
+ * Clear the wallpaper.
+ */
+ void clearWallpaper();
+
+ /**
+ * Sets the dimension hint for the wallpaper. These hints indicate the desired
+ * minimum width and height for the wallpaper.
+ */
+ void setDimensionHints(in int width, in int height);
+
+ /**
+ * Returns the desired minimum width for the wallpaper.
+ */
+ int getWidthHint();
+
+ /**
+ * Returns the desired minimum height for the wallpaper.
+ */
+ int getHeightHint();
+}
diff --git a/core/java/android/app/IWallpaperManagerCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl
new file mode 100644
index 0000000..991b2bc
--- /dev/null
+++ b/core/java/android/app/IWallpaperManagerCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+/**
+ * Callback interface used by IWallpaperManager to send asynchronous
+ * notifications back to its clients. Note that this is a
+ * one-way interface so the server does not block waiting for the client.
+ *
+ * @hide
+ */
+oneway interface IWallpaperManagerCallback {
+ /**
+ * Called when the wallpaper has changed
+ */
+ void onWallpaperChanged();
+}
diff --git a/core/java/android/app/IWallpaperService.aidl b/core/java/android/app/IWallpaperService.aidl
deleted file mode 100644
index a332b1a..0000000
--- a/core/java/android/app/IWallpaperService.aidl
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright (c) 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-import android.os.ParcelFileDescriptor;
-import android.app.IWallpaperServiceCallback;
-
-/** @hide */
-interface IWallpaperService {
-
- /**
- * Set the wallpaper.
- */
- ParcelFileDescriptor setWallpaper();
-
- /**
- * Get the wallpaper.
- */
- ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb);
-
- /**
- * Clear the wallpaper.
- */
- void clearWallpaper();
-
- /**
- * Sets the dimension hint for the wallpaper. These hints indicate the desired
- * minimum width and height for the wallpaper.
- */
- void setDimensionHints(in int width, in int height);
-
- /**
- * Returns the desired minimum width for the wallpaper.
- */
- int getWidthHint();
-
- /**
- * Returns the desired minimum height for the wallpaper.
- */
- int getHeightHint();
-}
diff --git a/core/java/android/app/IWallpaperServiceCallback.aidl b/core/java/android/app/IWallpaperServiceCallback.aidl
deleted file mode 100644
index 6086f40..0000000
--- a/core/java/android/app/IWallpaperServiceCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-/**
- * Callback interface used by IWallpaperService to send asynchronous
- * notifications back to its clients. Note that this is a
- * one-way interface so the server does not block waiting for the client.
- *
- * @hide
- */
-oneway interface IWallpaperServiceCallback {
- /**
- * Called when the wallpaper has changed
- */
- void onWallpaperChanged();
-}
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e31f4f8..6d79aee 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1468,7 +1468,7 @@ public class Instrumentation {
mWatcher = watcher;
}
- /*package*/ static void checkStartActivityResult(int res, Intent intent) {
+ /*package*/ static void checkStartActivityResult(int res, Object intent) {
if (res >= IActivityManager.START_SUCCESS) {
return;
}
@@ -1476,10 +1476,10 @@ public class Instrumentation {
switch (res) {
case IActivityManager.START_INTENT_NOT_RESOLVED:
case IActivityManager.START_CLASS_NOT_FOUND:
- if (intent.getComponent() != null)
+ if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
throw new ActivityNotFoundException(
"Unable to find explicit activity class "
- + intent.getComponent().toShortString()
+ + ((Intent)intent).getComponent().toShortString()
+ "; have you declared this activity in your AndroidManifest.xml?");
throw new ActivityNotFoundException(
"No Activity found to handle " + intent);
@@ -1489,6 +1489,9 @@ public class Instrumentation {
case IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
throw new AndroidRuntimeException(
"FORWARD_RESULT_FLAG used while also requesting a result");
+ case IActivityManager.START_NOT_ACTIVITY:
+ throw new IllegalArgumentException(
+ "PendingIntent is not an activity");
default:
throw new AndroidRuntimeException("Unknown error code "
+ res + " when starting " + intent);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 2b12a2a..804c8eb 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -18,6 +18,7 @@ public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
+ private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
@@ -36,6 +37,19 @@ public abstract class IntentService extends Service {
mName = name;
}
+ /**
+ * Control redelivery of intents. If called with true,
+ * {@link #onStartCommand(Intent, int, int)} will return
+ * {@link Service#START_REDELIVER_INTENT} instead of
+ * {@link Service#START_NOT_STICKY}, so that if this service's process
+ * is called while it is executing the Intent in
+ * {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
+ * will be re-delivered to it, to retry its execution.
+ */
+ public void setIntentRedelivery(boolean enabled) {
+ mRedelivery = enabled;
+ }
+
@Override
public void onCreate() {
super.onCreate();
@@ -48,7 +62,6 @@ public abstract class IntentService extends Service {
@Override
public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
@@ -56,6 +69,12 @@ public abstract class IntentService extends Service {
}
@Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
+ }
+
+ @Override
public void onDestroy() {
mServiceLooper.quit();
}
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index d788c43..0ece2fc 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -18,6 +18,7 @@ package android.app;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ComponentInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
@@ -52,9 +53,9 @@ import java.util.List;
*
*/
public abstract class LauncherActivity extends ListActivity {
-
Intent mIntent;
PackageManager mPackageManager;
+ IconResizer mIconResizer;
/**
* An item in the list
@@ -70,13 +71,17 @@ public abstract class LauncherActivity extends ListActivity {
ListItem(PackageManager pm, ResolveInfo resolveInfo, IconResizer resizer) {
this.resolveInfo = resolveInfo;
label = resolveInfo.loadLabel(pm);
- if (label == null && resolveInfo.activityInfo != null) {
+ ComponentInfo ci = resolveInfo.activityInfo;
+ if (ci == null) ci = resolveInfo.serviceInfo;
+ if (label == null && ci != null) {
label = resolveInfo.activityInfo.name;
}
- icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
- packageName = resolveInfo.activityInfo.applicationInfo.packageName;
- className = resolveInfo.activityInfo.name;
+ if (resizer != null) {
+ icon = resizer.createIconThumbnail(resolveInfo.loadIcon(pm));
+ }
+ packageName = ci.applicationInfo.packageName;
+ className = ci.name;
}
public ListItem() {
@@ -90,13 +95,15 @@ public abstract class LauncherActivity extends ListActivity {
private final Object lock = new Object();
private ArrayList<ListItem> mOriginalValues;
+ protected final IconResizer mIconResizer;
protected final LayoutInflater mInflater;
protected List<ListItem> mActivitiesList;
private Filter mFilter;
- public ActivityAdapter() {
+ public ActivityAdapter(IconResizer resizer) {
+ mIconResizer = resizer;
mInflater = (LayoutInflater) LauncherActivity.this.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
mActivitiesList = makeListItems();
@@ -151,6 +158,10 @@ public abstract class LauncherActivity extends ListActivity {
private void bindView(View view, ListItem item) {
TextView text = (TextView) view;
text.setText(item.label);
+ if (item.icon == null) {
+ item.icon = mIconResizer.createIconThumbnail(
+ item.resolveInfo.loadIcon(getPackageManager()));
+ }
text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
}
@@ -325,12 +336,13 @@ public abstract class LauncherActivity extends ListActivity {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarIndeterminateVisibility(true);
- setContentView(com.android.internal.R.layout.activity_list);
-
+ onSetContentView();
+ mIconResizer = new IconResizer();
+
mIntent = new Intent(getTargetIntent());
mIntent.setComponent(null);
- mAdapter = new ActivityAdapter();
+ mAdapter = new ActivityAdapter(mIconResizer);
setListAdapter(mAdapter);
getListView().setTextFilterEnabled(true);
@@ -338,10 +350,17 @@ public abstract class LauncherActivity extends ListActivity {
setProgressBarIndeterminateVisibility(false);
}
+ /**
+ * Override to call setContentView() with your own content view to
+ * customize the list layout.
+ */
+ protected void onSetContentView() {
+ setContentView(com.android.internal.R.layout.activity_list);
+ }
+
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
- Intent intent = ((ActivityAdapter)mAdapter).intentForPosition(position);
-
+ Intent intent = intentForPosition(position);
startActivity(intent);
}
@@ -374,21 +393,26 @@ public abstract class LauncherActivity extends ListActivity {
}
/**
+ * Perform query on package manager for list items. The default
+ * implementation queries for activities.
+ */
+ protected List<ResolveInfo> onQueryPackageManager(Intent queryIntent) {
+ return mPackageManager.queryIntentActivities(queryIntent, /* no flags */ 0);
+ }
+
+ /**
* Perform the query to determine which results to show and return a list of them.
*/
public List<ListItem> makeListItems() {
// Load all matching activities and sort correctly
- List<ResolveInfo> list = mPackageManager.queryIntentActivities(mIntent,
- /* no flags */ 0);
+ List<ResolveInfo> list = onQueryPackageManager(mIntent);
Collections.sort(list, new ResolveInfo.DisplayNameComparator(mPackageManager));
- IconResizer resizer = new IconResizer();
-
ArrayList<ListItem> result = new ArrayList<ListItem>(list.size());
int listSize = list.size();
for (int i = 0; i < listSize; i++) {
ResolveInfo resolveInfo = list.get(i);
- result.add(new ListItem(mPackageManager, resolveInfo, resizer));
+ result.add(new ListItem(mPackageManager, resolveInfo, null));
}
return result;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9834c75..be5a7d3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -257,6 +257,13 @@ public class Notification implements Parcelable
*/
public static final int FLAG_NO_CLEAR = 0x00000020;
+ /**
+ * Bit to be bitwise-ored into the {@link #flags} field that should be
+ * set if this notification represents a currently running service. This
+ * will normally be set for you by {@link Service#startForeground}.
+ */
+ public static final int FLAG_FOREGROUND_SERVICE = 0x00000040;
+
public int flags;
/**
@@ -458,7 +465,9 @@ public class Notification implements Parcelable
sb.append(this.vibrate[i]);
sb.append(',');
}
- sb.append(this.vibrate[N]);
+ if (N != -1) {
+ sb.append(this.vibrate[N]);
+ }
sb.append("]");
} else if ((this.defaults & DEFAULT_VIBRATE) != 0) {
sb.append("default");
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 39edab7..7b51fdf 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -61,7 +61,8 @@ public class NotificationManager
private static INotificationManager sService;
- static private INotificationManager getService()
+ /** @hide */
+ static public INotificationManager getService()
{
if (sService != null) {
return sService;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index f7479bc..18d9b92 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -519,7 +519,8 @@ public final class PendingIntent implements Parcelable {
mTarget = IIntentSender.Stub.asInterface(target);
}
- /*package*/ IIntentSender getTarget() {
+ /** @hide */
+ public IIntentSender getTarget() {
return mTarget;
}
}
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 5844079..f474f06 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -745,7 +745,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
return true;
}
- if (keyCode == KeyEvent.KEYCODE_SEARCH) {
+ if (keyCode == KeyEvent.KEYCODE_SEARCH && event.getRepeatCount() < 1) {
// If the search key is pressed, toggle between global and in-app search. If we are
// currently doing global search and there is no in-app search context to toggle to,
// just don't do anything.
@@ -1120,7 +1120,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
/**
* Launch a search for the text in the query text field.
*/
- protected void launchQuerySearch() {
+ public void launchQuerySearch() {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null);
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 2245562..7e6efec 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -29,6 +29,7 @@ import android.os.ServiceManager;
import android.server.search.SearchableInfo;
import android.util.Log;
import android.view.KeyEvent;
+import android.text.TextUtils;
import java.util.List;
@@ -1636,7 +1637,17 @@ public class SearchManager
private final Context mContext;
+ /**
+ * compact representation of the activity associated with this search manager so
+ * we can say who we are when starting search. the search managerservice, in turn,
+ * uses this to properly handle the back stack.
+ */
private int mIdent;
+
+ /**
+ * The package associated with this seach manager.
+ */
+ private String mAssociatedPackage;
// package private since they are used by the inner class SearchManagerCallback
/* package */ final Handler mHandler;
@@ -1656,11 +1667,15 @@ public class SearchManager
return mIdent != 0;
}
- /*package*/ void setIdent(int ident) {
+ /*package*/ void setIdent(int ident, ComponentName component) {
if (mIdent != 0) {
throw new IllegalStateException("mIdent already set");
}
+ if (component == null) {
+ throw new IllegalArgumentException("component must be non-null");
+ }
mIdent = ident;
+ mAssociatedPackage = component.getPackageName();
}
/**
@@ -1710,12 +1725,55 @@ public class SearchManager
boolean globalSearch) {
if (mIdent == 0) throw new IllegalArgumentException(
"Called from outside of an Activity context");
+ if (!globalSearch && !mAssociatedPackage.equals(launchActivity.getPackageName())) {
+ Log.w(TAG, "invoking app search on a different package " +
+ "not associated with this search manager");
+ }
try {
// activate the search manager and start it up!
mService.startSearch(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch, mSearchManagerCallback, mIdent);
} catch (RemoteException ex) {
- Log.e(TAG, "startSearch() failed: " + ex);
+ Log.e(TAG, "startSearch() failed.", ex);
+ }
+ }
+
+ /**
+ * Similar to {@link #startSearch} but actually fires off the search query after invoking
+ * the search dialog. Made available for testing purposes.
+ *
+ * @param query The query to trigger. If empty, request will be ignored.
+ * @param launchActivity The ComponentName of the activity that has launched this search.
+ * @param appSearchData An application can insert application-specific
+ * context here, in order to improve quality or specificity of its own
+ * searches. This data will be returned with SEARCH intent(s). Null if
+ * no extra data is required.
+ * @param globalSearch If false, this will only launch the search that has been specifically
+ * defined by the application (which is usually defined as a local search). If no default
+ * search is defined in the current application or activity, no search will be launched.
+ * If true, this will always launch a platform-global (e.g. web-based) search instead.
+ *
+ * @see #startSearch
+ */
+ public void triggerSearch(String query,
+ ComponentName launchActivity,
+ Bundle appSearchData,
+ boolean globalSearch) {
+ if (mIdent == 0) throw new IllegalArgumentException(
+ "Called from outside of an Activity context");
+ if (!mAssociatedPackage.equals(launchActivity.getPackageName())) {
+ throw new IllegalArgumentException("invoking app search on a different package " +
+ "not associated with this search manager");
+ }
+ if (query == null || TextUtils.getTrimmedLength(query) == 0) {
+ Log.w(TAG, "triggerSearch called with empty query, ignoring.");
+ return;
+ }
+ try {
+ mService.triggerSearch(query, launchActivity, appSearchData, mSearchManagerCallback,
+ globalSearch, mIdent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "triggerSearch() failed.", ex);
}
}
@@ -1836,6 +1894,7 @@ public class SearchManager
/**
* @deprecated This method is an obsolete internal implementation detail. Do not use.
*/
+ @Deprecated
public void onCancel(DialogInterface dialog) {
throw new UnsupportedOperationException();
}
@@ -1843,6 +1902,7 @@ public class SearchManager
/**
* @deprecated This method is an obsolete internal implementation detail. Do not use.
*/
+ @Deprecated
public void onDismiss(DialogInterface dialog) {
throw new UnsupportedOperationException();
}
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index d2fb605..60c756b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -22,8 +22,10 @@ import android.content.Intent;
import android.content.ContextWrapper;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.RemoteException;
import android.os.IBinder;
+import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -168,20 +170,119 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
+ * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
+ */
+ @Deprecated
+ public void onStart(Intent intent, int startId) {
+ }
+
+ /**
+ * Bits returned by {@link #onStartCommand} describing how to continue
+ * the service if it is killed. May be {@link #START_STICKY},
+ * {@link #START_NOT_STICKY}, {@link #START_REDELIVER_INTENT},
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ */
+ public static final int START_CONTINUATION_MASK = 0xf;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: compatibility
+ * version of {@link #START_STICKY} that does not guarantee that
+ * {@link #onStartCommand} will be called again after being killed.
+ */
+ public static final int START_STICKY_COMPATIBILITY = 0;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then leave it in the started state but
+ * don't retain this delivered intent. Later the system will try to
+ * re-create the service, but it will <em>not</em> call
+ * {@link #onStartCommand} unless there has been a new call to
+ * {@link Context#startService Context.startService(Intent)} with a new
+ * Intent to deliver.
+ *
+ * <p>This mode makes sense for things that will be explicitly started
+ * and stopped to run for arbitrary periods of time, such as a service
+ * performing background music playback.
+ */
+ public static final int START_STICKY = 1;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), and there are no new start intents to
+ * deliver to it, then take the service out of the started state and
+ * don't recreate until a future explicit call to
+ * {@link Context#startService Context.startService(Intent)}.
+ *
+ * <p>This mode makes sense for things that want to do some work as a
+ * result of being started, but can be stopped when under memory pressure
+ * and will explicit start themselves again later to do more work. An
+ * example of such a service would be one that polls for data from
+ * a server: it could schedule an alarm to poll every N minutes by having
+ * the alarm start its service. When its {@link #onStartCommand} is
+ * called from the alarm, it schedules a new alarm for N minutes later,
+ * and spawns a thread to do its networking. If its process is killed
+ * while doing that check, the service will not be restarted until the
+ * alarm goes off.
+ */
+ public static final int START_NOT_STICKY = 2;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then it will be scheduled for a restart
+ * and the last delivered Intent re-delivered to it again via
+ * {@link #onStartCommand}. This Intent will remain scheduled for
+ * redelivery until the service calls {@link #stopSelf(int)} with the
+ * start ID provided to {@link #onStartCommand}.
+ */
+ public static final int START_REDELIVER_INTENT = 3;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * re-delivery of a previously delivered intent, because the service
+ * had previously returned {@link #START_REDELIVER_INTENT} but had been
+ * killed before calling {@link #stopSelf(int)} for that Intent.
+ */
+ public static final int START_FLAG_REDELIVERY = 0x0001;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * a retry because the original attempt never got to or returned from
+ * {@link #onStartCommand(Intent, int, int)}.
+ */
+ public static final int START_FLAG_RETRY = 0x0002;
+
+ /**
* Called by the system every time a client explicitly starts the service by calling
* {@link android.content.Context#startService}, providing the arguments it supplied and a
* unique integer token representing the start request. Do not call this method directly.
- *
+ *
+ * <p>For backwards compatibility, the default implementation calls
+ * {@link #onStart} and returns either {@link #START_STICKY}
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ *
* @param intent The Intent supplied to {@link android.content.Context#startService},
- * as given.
+ * as given. This may be null if the service is being restarted after
+ * its process has gone away, and it had previously returned anything
+ * except {@link #START_STICKY_COMPATIBILITY}.
+ * @param flags Additional data about this start request. Currently either
+ * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.
* @param startId A unique integer representing this specific request to
- * start. Use with {@link #stopSelfResult(int)}.
+ * start. Use with {@link #stopSelfResult(int)}.
+ *
+ * @return The return value indicates what semantics the system should
+ * use for the service's current started state. It may be one of the
+ * constants associated with the {@link #START_CONTINUATION_MASK} bits.
*
* @see #stopSelfResult(int)
*/
- public void onStart(Intent intent, int startId) {
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
-
+
/**
* Called by the system to notify a Service that it is no longer used and is being removed. The
* service should clean up an resources it holds (threads, registered
@@ -304,24 +405,53 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
- * Control whether this service is considered to be a foreground service.
+ * @deprecated This is a now a no-op, use
+ * {@link #startForeground(int, Notification)} instead.
+ */
+ @Deprecated
+ public final void setForeground(boolean isForeground) {
+ Log.w(TAG, "setForeground: ignoring old API call on " + getClass().getName());
+ }
+
+ /**
+ * Make this service run in the foreground, supplying the ongoing
+ * notification to be shown to the user while in this state.
* By default services are background, meaning that if the system needs to
* kill them to reclaim more memory (such as to display a large page in a
* web browser), they can be killed without too much harm. You can set this
- * flag if killing your service would be disruptive to the user: such as
+ * flag if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
*
- * @param isForeground Determines whether this service is considered to
- * be foreground (true) or background (false).
+ * @param id The identifier for this notification as per
+ * {@link NotificationManager#notify(int, Notification)
+ * NotificationManager.notify(int, Notification)}.
+ * @param notification The Notification to be displayed.
+ *
+ * @see #stopForeground(boolean)
*/
- public final void setForeground(boolean isForeground) {
- if (mActivityManager == null) {
- return;
+ public final void startForeground(int id, Notification notification) {
+ try {
+ mActivityManager.setServiceForeground(
+ new ComponentName(this, mClassName), mToken, id,
+ notification, true);
+ } catch (RemoteException ex) {
}
+ }
+
+ /**
+ * Remove this service from foreground state, allowing it to be killed if
+ * more memory is needed.
+ * @param removeNotification If true, the notification previously provided
+ * to {@link #startForeground} will be removed. Otherwise it will remain
+ * until a later call removes it (or the service is destroyed).
+ * @see #startForeground(int, Notification)
+ */
+ public final void stopForeground(boolean removeNotification) {
try {
mActivityManager.setServiceForeground(
- new ComponentName(this, mClassName), mToken, isForeground);
+ new ComponentName(this, mClassName), mToken, 0, null,
+ removeNotification);
} catch (RemoteException ex) {
}
}
@@ -363,6 +493,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
+ mStartCompatibility = getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.ECLAIR;
}
final String getClassName() {
@@ -375,4 +507,5 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
private IBinder mToken = null;
private Application mApplication = null;
private IActivityManager mActivityManager = null;
+ private boolean mStartCompatibility = false;
}
diff --git a/core/java/android/app/WallpaperInfo.aidl b/core/java/android/app/WallpaperInfo.aidl
new file mode 100644
index 0000000..7bbdcae
--- /dev/null
+++ b/core/java/android/app/WallpaperInfo.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.app;
+
+parcelable WallpaperInfo;
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
new file mode 100644
index 0000000..5e44bc7
--- /dev/null
+++ b/core/java/android/app/WallpaperInfo.java
@@ -0,0 +1,200 @@
+package android.app;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.service.wallpaper.WallpaperService;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of a wallpaper service.
+ * @hide Live Wallpaper
+ */
+public final class WallpaperInfo implements Parcelable {
+ static final String TAG = "WallpaperInfo";
+
+ /**
+ * The Service that implements this wallpaper component.
+ */
+ final ResolveInfo mService;
+
+ /**
+ * The wallpaper setting activity's name, to
+ * launch the setting activity of this wallpaper.
+ */
+ final String mSettingsActivityName;
+
+ /**
+ * Constructor.
+ *
+ * @param context The Context in which we are parsing the wallpaper.
+ * @param service The ResolveInfo returned from the package manager about
+ * this wallpaper's component.
+ */
+ public WallpaperInfo(Context context, ResolveInfo service)
+ throws XmlPullParserException, IOException {
+ mService = service;
+ ServiceInfo si = service.serviceInfo;
+
+ PackageManager pm = context.getPackageManager();
+ String settingsActivityComponent = null;
+
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, WallpaperService.SERVICE_META_DATA);
+ if (parser == null) {
+ throw new XmlPullParserException("No "
+ + WallpaperService.SERVICE_META_DATA + " meta-data");
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!"wallpaper".equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with wallpaper tag");
+ }
+
+ TypedArray sa = context.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.Wallpaper);
+ settingsActivityComponent = sa.getString(
+ com.android.internal.R.styleable.Wallpaper_settingsActivity);
+ sa.recycle();
+ } finally {
+ if (parser != null) parser.close();
+ }
+
+ mSettingsActivityName = settingsActivityComponent;
+ }
+
+ WallpaperInfo(Parcel source) {
+ mSettingsActivityName = source.readString();
+ mService = ResolveInfo.CREATOR.createFromParcel(source);
+ }
+
+ /**
+ * Return the .apk package that implements this wallpaper.
+ */
+ public String getPackageName() {
+ return mService.serviceInfo.packageName;
+ }
+
+ /**
+ * Return the class name of the service component that implements
+ * this wallpaper.
+ */
+ public String getServiceName() {
+ return mService.serviceInfo.name;
+ }
+
+ /**
+ * Return the raw information about the Service implementing this
+ * wallpaper. Do not modify the returned object.
+ */
+ public ServiceInfo getServiceInfo() {
+ return mService.serviceInfo;
+ }
+
+ /**
+ * Return the component of the service that implements this wallpaper.
+ */
+ public ComponentName getComponent() {
+ return new ComponentName(mService.serviceInfo.packageName,
+ mService.serviceInfo.name);
+ }
+
+ /**
+ * Load the user-displayed label for this wallpaper.
+ *
+ * @param pm Supply a PackageManager used to load the wallpaper's
+ * resources.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ return mService.loadLabel(pm);
+ }
+
+ /**
+ * Load the user-displayed icon for this wallpaper.
+ *
+ * @param pm Supply a PackageManager used to load the wallpaper's
+ * resources.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ return mService.loadIcon(pm);
+ }
+
+ /**
+ * Return the class name of an activity that provides a settings UI for
+ * the wallpaper. You can launch this activity be starting it with
+ * an {@link android.content.Intent} whose action is MAIN and with an
+ * explicit {@link android.content.ComponentName}
+ * composed of {@link #getPackageName} and the class name returned here.
+ *
+ * <p>A null will be returned if there is no settings activity associated
+ * with the wallpaper.
+ */
+ public String getSettingsActivity() {
+ return mSettingsActivityName;
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + "Service:");
+ mService.dump(pw, prefix + " ");
+ pw.println(prefix + "mSettingsActivityName=" + mSettingsActivityName);
+ }
+
+ @Override
+ public String toString() {
+ return "WallpaperInfo{" + mService.serviceInfo.name
+ + ", settings: "
+ + mSettingsActivityName + "}";
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mSettingsActivityName);
+ mService.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<WallpaperInfo> CREATOR = new Parcelable.Creator<WallpaperInfo>() {
+ public WallpaperInfo createFromParcel(Parcel source) {
+ return new WallpaperInfo(source);
+ }
+
+ public WallpaperInfo[] newArray(int size) {
+ return new WallpaperInfo[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
new file mode 100644
index 0000000..da40c8a
--- /dev/null
+++ b/core/java/android/app/WallpaperManager.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.ViewRoot;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class WallpaperManager {
+ private static String TAG = "WallpaperManager";
+ private static boolean DEBUG = false;
+
+ /**
+ * Launch an activity for the user to pick the current global live
+ * wallpaper.
+ * @hide Live Wallpaper
+ */
+ public static final String ACTION_LIVE_WALLPAPER_CHOOSER
+ = "android.service.wallpaper.LIVE_WALLPAPER_CHOOSER";
+
+ private final Context mContext;
+
+ static class Globals extends IWallpaperManagerCallback.Stub {
+ private IWallpaperManager mService;
+ private Bitmap mWallpaper;
+
+ private static final int MSG_CLEAR_WALLPAPER = 1;
+
+ private final Handler mHandler;
+
+ Globals(Looper looper) {
+ IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
+ mService = IWallpaperManager.Stub.asInterface(b);
+ mHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_CLEAR_WALLPAPER:
+ synchronized (this) {
+ mWallpaper = null;
+ }
+ break;
+ }
+ }
+ };
+ }
+
+ public void onWallpaperChanged() {
+ /* The wallpaper has changed but we shouldn't eagerly load the
+ * wallpaper as that would be inefficient. Reset the cached wallpaper
+ * to null so if the user requests the wallpaper again then we'll
+ * fetch it.
+ */
+ mHandler.sendEmptyMessage(MSG_CLEAR_WALLPAPER);
+ }
+
+ public Bitmap peekWallpaperBitmap(Context context) {
+ synchronized (this) {
+ if (mWallpaper != null) {
+ return mWallpaper;
+ }
+ mWallpaper = getCurrentWallpaperLocked(context);
+ return mWallpaper;
+ }
+ }
+
+ private Bitmap getCurrentWallpaperLocked(Context context) {
+ try {
+ Bundle params = new Bundle();
+ ParcelFileDescriptor fd = mService.getWallpaper(this, params);
+ if (fd != null) {
+ int width = params.getInt("width", 0);
+ int height = params.getInt("height", 0);
+
+ if (width <= 0 || height <= 0) {
+ // Degenerate case: no size requested, just load
+ // bitmap as-is.
+ Bitmap bm = BitmapFactory.decodeFileDescriptor(
+ fd.getFileDescriptor(), null, null);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ if (bm != null) {
+ bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ }
+ return bm;
+ }
+
+ // Load the bitmap with full color depth, to preserve
+ // quality for later processing.
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inDither = false;
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap bm = BitmapFactory.decodeFileDescriptor(
+ fd.getFileDescriptor(), null, options);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ }
+ if (bm == null) {
+ return bm;
+ }
+ bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+
+ // This is the final bitmap we want to return.
+ Bitmap newbm = Bitmap.createBitmap(width, height,
+ bm.getConfig());
+ newbm.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ Canvas c = new Canvas(newbm);
+ c.setDensity(DisplayMetrics.DENSITY_DEVICE);
+ Rect targetRect = new Rect();
+ targetRect.left = targetRect.top = 0;
+ targetRect.right = bm.getWidth();
+ targetRect.bottom = bm.getHeight();
+
+ int deltaw = width - targetRect.right;
+ int deltah = height - targetRect.bottom;
+
+ if (deltaw > 0 || deltah > 0) {
+ // We need to scale up so it covers the entire
+ // area.
+ float scale = 1.0f;
+ if (deltaw > deltah) {
+ scale = width / (float)targetRect.right;
+ } else {
+ scale = height / (float)targetRect.bottom;
+ }
+ targetRect.right = (int)(targetRect.right*scale);
+ targetRect.bottom = (int)(targetRect.bottom*scale);
+ deltaw = width - targetRect.right;
+ deltah = height - targetRect.bottom;
+ }
+
+ targetRect.offset(deltaw/2, deltah/2);
+ Paint paint = new Paint();
+ paint.setFilterBitmap(true);
+ paint.setDither(true);
+ c.drawBitmap(bm, null, targetRect, paint);
+
+ bm.recycle();
+ return newbm;
+ }
+ } catch (RemoteException e) {
+ }
+ return null;
+ }
+ }
+
+ private static Object mSync = new Object();
+ private static Globals sGlobals;
+
+ static void initGlobals(Looper looper) {
+ synchronized (mSync) {
+ if (sGlobals == null) {
+ sGlobals = new Globals(looper);
+ }
+ }
+ }
+
+ /*package*/ WallpaperManager(Context context, Handler handler) {
+ mContext = context;
+ initGlobals(context.getMainLooper());
+ }
+
+ /**
+ * Retrieve a WallpaperManager associated with the given Context.
+ */
+ public static WallpaperManager getInstance(Context context) {
+ return (WallpaperManager)context.getSystemService(
+ Context.WALLPAPER_SERVICE);
+ }
+
+ /** @hide */
+ public IWallpaperManager getIWallpaperManager() {
+ return sGlobals.mService;
+ }
+
+ /**
+ * Like {@link #peekDrawable}, but always returns a valid Drawable. If
+ * no wallpaper is set, the system default wallpaper is returned.
+ *
+ * @return Returns a Drawable object that will draw the wallpaper.
+ */
+ public Drawable getDrawable() {
+ Drawable dr = peekDrawable();
+ return dr != null ? dr : Resources.getSystem().getDrawable(
+ com.android.internal.R.drawable.default_wallpaper);
+ }
+
+ /**
+ * Retrieve the current system wallpaper. This is returned as an
+ * abstract Drawable that you can install in a View to display whatever
+ * wallpaper the user has currently set. If there is no wallpaper set,
+ * a null pointer is returned.
+ *
+ * @return Returns a Drawable object that will draw the wallpaper or a
+ * null pointer if these is none.
+ */
+ public Drawable peekDrawable() {
+ Bitmap bm = sGlobals.peekWallpaperBitmap(mContext);
+ return bm != null ? new BitmapDrawable(mContext.getResources(), bm) : null;
+ }
+
+ /**
+ * If the current wallpaper is a live wallpaper component, return the
+ * information about that wallpaper. Otherwise, if it is a static image,
+ * simply return null.
+ * @hide Live Wallpaper
+ */
+ public WallpaperInfo getWallpaperInfo() {
+ try {
+ return sGlobals.mService.getWallpaperInfo();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to the bitmap in the given resource.
+ * The resource is opened as a raw data stream and copied into the
+ * wallpaper; it must be a valid PNG or JPEG image. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * @param resid The bitmap to save.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setResource(int resid) throws IOException {
+ try {
+ Resources resources = mContext.getResources();
+ /* Set the wallpaper to the default values */
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
+ "res:" + resources.getResourceName(resid));
+ if (fd != null) {
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(resources.openRawResource(resid), fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to a bitmap. The given bitmap is
+ * converted to a PNG and stored as the wallpaper. On success, the intent
+ * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
+ *
+ * @param bitmap The bitmap to save.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setBitmap(Bitmap bitmap) throws IOException {
+ try {
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Change the current system wallpaper to a specific byte stream. The
+ * give InputStream is copied into persistent storage and will now be
+ * used as the wallpaper. Currently it must be either a JPEG or PNG
+ * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+ * is broadcast.
+ *
+ * @param data A stream containing the raw data to install as a wallpaper.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void setStream(InputStream data) throws IOException {
+ try {
+ ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(null);
+ if (fd == null) {
+ return;
+ }
+ FileOutputStream fos = null;
+ try {
+ fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+ setWallpaper(data, fos);
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ private void setWallpaper(InputStream data, FileOutputStream fos)
+ throws IOException {
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=data.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ }
+
+ /**
+ * Returns the desired minimum width for the wallpaper. Callers of
+ * {@link #setBitmap(android.graphics.Bitmap)} or
+ * {@link #setStream(java.io.InputStream)} should check this value
+ * beforehand to make sure the supplied wallpaper respects the desired
+ * minimum width.
+ *
+ * If the returned value is <= 0, the caller should use the width of
+ * the default display instead.
+ *
+ * @return The desired minimum width for the wallpaper. This value should
+ * be honored by applications that set the wallpaper but it is not
+ * mandatory.
+ */
+ public int getDesiredMinimumWidth() {
+ try {
+ return sGlobals.mService.getWidthHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ /**
+ * Returns the desired minimum height for the wallpaper. Callers of
+ * {@link #setBitmap(android.graphics.Bitmap)} or
+ * {@link #setStream(java.io.InputStream)} should check this value
+ * beforehand to make sure the supplied wallpaper respects the desired
+ * minimum height.
+ *
+ * If the returned value is <= 0, the caller should use the height of
+ * the default display instead.
+ *
+ * @return The desired minimum height for the wallpaper. This value should
+ * be honored by applications that set the wallpaper but it is not
+ * mandatory.
+ */
+ public int getDesiredMinimumHeight() {
+ try {
+ return sGlobals.mService.getHeightHint();
+ } catch (RemoteException e) {
+ // Shouldn't happen!
+ return 0;
+ }
+ }
+
+ /**
+ * For use only by the current home application, to specify the size of
+ * wallpaper it would like to use. This allows such applications to have
+ * a virtual wallpaper that is larger than the physical screen, matching
+ * the size of their workspace.
+ * @param minimumWidth Desired minimum width
+ * @param minimumHeight Desired minimum height
+ */
+ public void suggestDesiredDimensions(int minimumWidth, int minimumHeight) {
+ try {
+ sGlobals.mService.setDimensionHints(minimumWidth, minimumHeight);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Set the position of the current wallpaper within any larger space, when
+ * that wallpaper is visible behind the given window. The X and Y offsets
+ * are floating point numbers ranging from 0 to 1, representing where the
+ * wallpaper should be positioned within the screen space. These only
+ * make sense when the wallpaper is larger than the screen.
+ *
+ * @param windowToken The window who these offsets should be associated
+ * with, as returned by {@link android.view.View#getWindowVisibility()
+ * View.getWindowToken()}.
+ * @param xOffset The offset olong the X dimension, from 0 to 1.
+ * @param yOffset The offset along the Y dimension, from 0 to 1.
+ */
+ public void setWallpaperOffsets(IBinder windowToken, float xOffset, float yOffset) {
+ try {
+ ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ windowToken, xOffset, yOffset);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * Clear the offsets previously associated with this window through
+ * {@link #setWallpaperOffsets(IBinder, float, float)}. This reverts
+ * the window to its default state, where it does not cause the wallpaper
+ * to scroll from whatever its last offsets were.
+ *
+ * @param windowToken The window who these offsets should be associated
+ * with, as returned by {@link android.view.View#getWindowVisibility()
+ * View.getWindowToken()}.
+ */
+ public void clearWallpaperOffsets(IBinder windowToken) {
+ try {
+ ViewRoot.getWindowSession(mContext.getMainLooper()).setWallpaperPosition(
+ windowToken, -1, -1);
+ } catch (RemoteException e) {
+ // Ignore.
+ }
+ }
+
+ /**
+ * Remove any currently set wallpaper, reverting to the system's default
+ * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
+ * is broadcast.
+ *
+ * @throws IOException If an error occurs reverting to the default
+ * wallpaper.
+ */
+ public void clear() throws IOException {
+ setResource(com.android.internal.R.drawable.default_wallpaper);
+ }
+}
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 9799ac4..a4c141e 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -24,16 +24,17 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.SystemClock;
+import android.os.Parcelable;
+import android.os.Parcel;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
-import android.widget.FrameLayout.LayoutParams;
/**
* Provides the glue to show AppWidget views. This class offers automatic animation
@@ -108,6 +109,24 @@ public class AppWidgetHostView extends FrameLayout {
return mInfo;
}
+ @Override
+ protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+ final ParcelableSparseArray jail = new ParcelableSparseArray();
+ super.dispatchSaveInstanceState(jail);
+ container.put(generateId(), jail);
+ }
+
+ private int generateId() {
+ final int id = getId();
+ return id == View.NO_ID ? mAppWidgetId : id;
+ }
+
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+ final ParcelableSparseArray jail = (ParcelableSparseArray) container.get(generateId());
+ super.dispatchRestoreInstanceState(jail);
+ }
+
/** {@inheritDoc} */
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
@@ -303,6 +322,7 @@ public class AppWidgetHostView extends FrameLayout {
if (mInfo != null) {
Context theirContext = mContext.createPackageContext(
mInfo.provider.getPackageName(), Context.CONTEXT_RESTRICTED);
+ mRemoteContext = theirContext;
LayoutInflater inflater = (LayoutInflater)
theirContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater = inflater.cloneInContext(theirContext);
@@ -317,8 +337,8 @@ public class AppWidgetHostView extends FrameLayout {
exception = e;
}
- if (exception != null && LOGD) {
- Log.w(TAG, "Error inflating AppWidget " + mInfo, exception);
+ if (exception != null) {
+ Log.w(TAG, "Error inflating AppWidget " + mInfo + ": " + exception.toString());
}
if (defaultView == null) {
@@ -339,4 +359,36 @@ public class AppWidgetHostView extends FrameLayout {
tv.setBackgroundColor(Color.argb(127, 0, 0, 0));
return tv;
}
+
+ private static class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ final int count = size();
+ dest.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ dest.writeInt(keyAt(i));
+ dest.writeParcelable(valueAt(i), 0);
+ }
+ }
+
+ public static final Parcelable.Creator<ParcelableSparseArray> CREATOR =
+ new Parcelable.Creator<ParcelableSparseArray>() {
+ public ParcelableSparseArray createFromParcel(Parcel source) {
+ final ParcelableSparseArray array = new ParcelableSparseArray();
+ final ClassLoader loader = array.getClass().getClassLoader();
+ final int count = source.readInt();
+ for (int i = 0; i < count; i++) {
+ array.put(source.readInt(), source.readParcelable(loader));
+ }
+ return array;
+ }
+
+ public ParcelableSparseArray[] newArray(int size) {
+ return new ParcelableSparseArray[size];
+ }
+ };
+ }
}
diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java
index 69c206c..e67b0be 100644
--- a/core/java/android/backup/BackupDataInput.java
+++ b/core/java/android/backup/BackupDataInput.java
@@ -97,12 +97,7 @@ public class BackupDataInput {
public void skipEntityData() throws IOException {
if (mHeaderReady) {
- int result = skipEntityData_native(mBackupReader);
- if (result >= 0) {
- return;
- } else {
- throw new IOException("result=0x" + Integer.toHexString(result));
- }
+ skipEntityData_native(mBackupReader);
} else {
throw new IllegalStateException("mHeaderReady=false");
}
diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java
index c52fcd2..da1647a 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -43,7 +43,7 @@ public class BackupManager {
private static final String TAG = "BackupManager";
/** @hide TODO: REMOVE THIS */
- public static final boolean EVEN_THINK_ABOUT_DOING_RESTORE = false;
+ public static final boolean EVEN_THINK_ABOUT_DOING_RESTORE = true;
private Context mContext;
private static IBackupManager sService;
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index 2a1fbc1..fd40d98 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -40,6 +40,8 @@ interface IRestoreSession {
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
+ * @return Zero on success; nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
* the restore set that should be used.
* @param observer If non-null, this binder points to an object that will receive
@@ -50,6 +52,9 @@ interface IRestoreSession {
/**
* End this restore session. After this method is called, the IRestoreSession binder
* is no longer valid.
+ *
+ * <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
+ * even if {@link getAvailableRestoreSets} or {@link performRestore} failed.
*/
void endRestoreSession();
}
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 2ea45d5..060f20e 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -25,7 +25,10 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.util.Log;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
/**
* Public API for controlling the Bluetooth A2DP Profile Service.
@@ -39,15 +42,12 @@ import java.util.List;
*
* Currently the BluetoothA2dp service runs in the system server and this
* proxy object will be immediately bound to the service on construction.
- * However this may change in future releases, and error codes such as
- * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the
- * proxy object is not yet attached.
*
* Currently this class provides methods to connect to A2DP audio sinks.
*
* @hide
*/
-public class BluetoothA2dp {
+public final class BluetoothA2dp {
private static final String TAG = "BluetoothA2dp";
private static final boolean DBG = false;
@@ -79,6 +79,7 @@ public class BluetoothA2dp {
/** Default priority for a2dp devices that should not allow incoming
* connections */
public static final int PRIORITY_OFF = 0;
+
private final IBluetoothA2dp mService;
private final Context mContext;
@@ -89,6 +90,7 @@ public class BluetoothA2dp {
*/
public BluetoothA2dp(Context c) {
mContext = c;
+
IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
if (b == null) {
throw new RuntimeException("Bluetooth A2DP service not available!");
@@ -99,74 +101,75 @@ public class BluetoothA2dp {
/** Initiate a connection to an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when the
* connection is completed.
- * @param address Remote BT address.
- * @return Result code, negative indicates an immediate error.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
* @hide
*/
- public int connectSink(String address) {
- if (DBG) log("connectSink(" + address + ")");
+ public boolean connectSink(BluetoothDevice device) {
+ if (DBG) log("connectSink(" + device + ")");
try {
- return mService.connectSink(address);
+ return mService.connectSink(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/** Initiate disconnect from an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when
* disconnect is completed.
- * @param address Remote BT address.
- * @return Result code, negative indicates an immediate error.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
* @hide
*/
- public int disconnectSink(String address) {
- if (DBG) log("disconnectSink(" + address + ")");
+ public boolean disconnectSink(BluetoothDevice device) {
+ if (DBG) log("disconnectSink(" + device + ")");
try {
- return mService.disconnectSink(address);
+ return mService.disconnectSink(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/** Check if a specified A2DP sink is connected.
- * @param address Remote BT address.
+ * @param device Remote BT device.
* @return True if connected (or playing), false otherwise and on error.
* @hide
*/
- public boolean isSinkConnected(String address) {
- if (DBG) log("isSinkConnected(" + address + ")");
- int state = getSinkState(address);
+ public boolean isSinkConnected(BluetoothDevice device) {
+ if (DBG) log("isSinkConnected(" + device + ")");
+ int state = getSinkState(device);
return state == STATE_CONNECTED || state == STATE_PLAYING;
}
/** Check if any A2DP sink is connected.
- * @return a List of connected A2DP sinks, or null on error.
+ * @return a unmodifiable set of connected A2DP sinks, or null on error.
* @hide
*/
- public List<String> listConnectedSinks() {
- if (DBG) log("listConnectedSinks()");
+ public Set<BluetoothDevice> getConnectedSinks() {
+ if (DBG) log("getConnectedSinks()");
try {
- return mService.listConnectedSinks();
+ return Collections.unmodifiableSet(
+ new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
} catch (RemoteException e) {
- Log.w(TAG, "", e);
+ Log.e(TAG, "", e);
return null;
}
}
/** Get the state of an A2DP sink
- * @param address Remote BT address.
- * @return State code, or negative on error
+ * @param device Remote BT device.
+ * @return State code, one of STATE_
* @hide
*/
- public int getSinkState(String address) {
- if (DBG) log("getSinkState(" + address + ")");
+ public int getSinkState(BluetoothDevice device) {
+ if (DBG) log("getSinkState(" + device + ")");
try {
- return mService.getSinkState(address);
+ return mService.getSinkState(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return BluetoothA2dp.STATE_DISCONNECTED;
}
}
@@ -177,58 +180,33 @@ public class BluetoothA2dp {
* Sinks with priority greater than zero will accept incoming connections
* (if no sink is currently connected).
* Priority for unpaired sink must be PRIORITY_NONE.
- * @param address Paired sink
+ * @param device Paired sink
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
- * @return Result code, negative indicates an error
+ * @return true if priority is set, false on error
*/
- public int setSinkPriority(String address, int priority) {
- if (DBG) log("setSinkPriority(" + address + ", " + priority + ")");
+ public boolean setSinkPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
try {
- return mService.setSinkPriority(address, priority);
+ return mService.setSinkPriority(device, priority);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/**
* Get priority of a2dp sink.
- * @param address Sink
+ * @param device Sink
* @return non-negative priority, or negative error code on error.
*/
- public int getSinkPriority(String address) {
- if (DBG) log("getSinkPriority(" + address + ")");
+ public int getSinkPriority(BluetoothDevice device) {
+ if (DBG) log("getSinkPriority(" + device + ")");
try {
- return mService.getSinkPriority(address);
+ return mService.getSinkPriority(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
- }
- }
-
- /**
- * Check class bits for possible A2DP Sink support.
- * This is a simple heuristic that tries to guess if a device with the
- * given class bits might be a A2DP Sink. It is not accurate for all
- * devices. It tries to err on the side of false positives.
- * @return True if this device might be a A2DP sink
- */
- public static boolean doesClassMatchSink(int btClass) {
- if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
- return true;
- }
- // By the A2DP spec, sinks must indicate the RENDER service.
- // However we found some that do not (Chordette). So lets also
- // match on some other class bits.
- switch (BluetoothClass.Device.getDevice(btClass)) {
- case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
- case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
- case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- return true;
- default:
- return false;
+ Log.e(TAG, "", e);
+ return PRIORITY_OFF;
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
new file mode 100644
index 0000000..18f6995
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
+
+/**
+ * Represents the local Bluetooth adapter.
+ *
+ * <p>Use {@link android.content.Context#getSystemService} with {@link
+ * android.content.Context#BLUETOOTH_SERVICE} to get the default local
+ * Bluetooth adapter. On most Android devices there is only one local
+ * Bluetotoh adapter.
+ *
+ * <p>Use the {@link BluetoothDevice} class for operations on remote Bluetooth
+ * devices.
+ *
+ * <p>TODO: unhide more of this class
+ */
+public final class BluetoothAdapter {
+ private static final String TAG = "BluetoothAdapter";
+
+ /**
+ * Sentinel error value for this class. Guaranteed to not equal any other
+ * integer constant in this class. Provided as a convenience for functions
+ * that require a sentinel error value, for example:
+ * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ * BluetoothAdapter.ERROR)</code>
+ */
+ public static final int ERROR = -1;
+
+ /**
+ * Broadcast Action: The state of the local Bluetooth adapter has been
+ * changed.
+ * <p>For example, Bluetooth has been turned on or off.
+ * <p>Contains the extra fields {@link #EXTRA_STATE} and {@link
+ * #EXTRA_PREVIOUS_STATE} containing the new and old states
+ * respectively.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_STATE_CHANGED =
+ "android.bluetooth.intent.action.STATE_CHANGED";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
+ * intents to request the current power state. Possible values are:
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF},
+ */
+ public static final String EXTRA_STATE =
+ "android.bluetooth.intent.extra.STATE";
+ /**
+ * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
+ * intents to request the previous power state. Possible values are:
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF},
+ */
+ public static final String EXTRA_PREVIOUS_STATE =
+ "android.bluetooth.intent.extra.PREVIOUS_STATE";
+
+ /**
+ * Indicates the local Bluetooth adapter is off.
+ */
+ public static final int STATE_OFF = 40;
+ /**
+ * Indicates the local Bluetooth adapter is turning on. However local
+ * clients should wait for {@link #STATE_ON} before attempting to
+ * use the adapter.
+ */
+ public static final int STATE_TURNING_ON = 41;
+ /**
+ * Indicates the local Bluetooth adapter is on, and ready for use.
+ */
+ public static final int STATE_ON = 42;
+ /**
+ * Indicates the local Bluetooth adapter is turning off. Local clients
+ * should immediately attempt graceful disconnection of any remote links.
+ */
+ public static final int STATE_TURNING_OFF = 43;
+
+ /**
+ * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
+ * has changed.
+ * <p>Contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
+ * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
+ * respectively.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCAN_MODE_CHANGED =
+ "android.bluetooth.intent.action.SCAN_MODE_CHANGED";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
+ * intents to request the current scan mode. Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
+ */
+ public static final String EXTRA_SCAN_MODE = "android.bluetooth.intent.extra.SCAN_MODE";
+ /**
+ * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
+ * intents to request the previous scan mode. Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
+ */
+ public static final String EXTRA_PREVIOUS_SCAN_MODE =
+ "android.bluetooth.intent.extra.PREVIOUS_SCAN_MODE";
+
+ /**
+ * Indicates that both inquiry scan and page scan are disabled on the local
+ * Bluetooth adapter. Therefore this device is neither discoverable
+ * nor connectable from remote Bluetooth devices.
+ */
+ public static final int SCAN_MODE_NONE = 50;
+ /**
+ * Indicates that inquiry scan is disabled, but page scan is enabled on the
+ * local Bluetooth adapter. Therefore this device is not discoverable from
+ * remote Bluetooth devices, but is connectable from remote devices that
+ * have previously discovered this device.
+ */
+ public static final int SCAN_MODE_CONNECTABLE = 51;
+ /**
+ * Indicates that both inquiry scan and page scan are enabled on the local
+ * Bluetooth adapter. Therefore this device is both discoverable and
+ * connectable from remote Bluetooth devices.
+ */
+ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 53;
+
+ /** The user will be prompted to enter a pin
+ * @hide */
+ public static final int PAIRING_VARIANT_PIN = 0;
+ /** The user will be prompted to enter a passkey
+ * @hide */
+ public static final int PAIRING_VARIANT_PASSKEY = 1;
+ /** The user will be prompted to confirm the passkey displayed on the screen
+ * @hide */
+ public static final int PAIRING_VARIANT_CONFIRMATION = 2;
+
+ private final IBluetooth mService;
+
+ /**
+ * Do not use this constructor. Use Context.getSystemService() instead.
+ * @hide
+ */
+ public BluetoothAdapter(IBluetooth service) {
+ if (service == null) {
+ throw new IllegalArgumentException("service is null");
+ }
+ mService = service;
+ }
+
+ /**
+ * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
+ * address.
+ * <p>Valid Bluetooth hardware addresses must be upper case, in a format
+ * such as "00:11:22:33:AA:BB".
+ * <p>A {@link BluetoothDevice} will always be returned for a valid
+ * hardware address, even if this adapter has never seen that device.
+ *
+ * @param address valid Bluetooth MAC address
+ * @throws IllegalArgumentException if address is invalid
+ */
+ public BluetoothDevice getRemoteDevice(String address) {
+ return new BluetoothDevice(address);
+ }
+
+ /**
+ * Return true if Bluetooth is currently enabled and ready for use.
+ * <p>Equivalent to:
+ * <code>getBluetoothState() == STATE_ON</code>
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return true if the local adapter is turned on
+ */
+ public boolean isEnabled() {
+ try {
+ return mService.isEnabled();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Get the current state of the local Bluetooth adapter.
+ * <p>Possible return values are
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return current state of Bluetooth adapter
+ */
+ public int getState() {
+ try {
+ return mService.getBluetoothState();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return STATE_OFF;
+ }
+
+ /**
+ * Turn on the local Bluetooth adapter.
+ * <p>This powers on the underlying Bluetooth hardware, and starts all
+ * Bluetooth system services.
+ * <p>This is an asynchronous call: it will return immediatley, and
+ * clients should listen for {@link #ACTION_STATE_CHANGED}
+ * to be notified of subsequent adapter state changes. If this call returns
+ * true, then the adapter state will immediately transition from {@link
+ * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
+ * later transition to either {@link #STATE_OFF} or {@link
+ * #STATE_ON}. If this call returns false then there was an
+ * immediate problem that will prevent the adapter from being turned on -
+ * such as Airplane mode, or the adapter is already turned on.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @return true to indicate adapter startup has begun, or false on
+ * immediate error
+ */
+ public boolean enable() {
+ try {
+ return mService.enable();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Turn off the local Bluetooth adapter.
+ * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
+ * system services, and powers down the underlying Bluetooth hardware.
+ * <p>This is an asynchronous call: it will return immediatley, and
+ * clients should listen for {@link #ACTION_STATE_CHANGED}
+ * to be notified of subsequent adapter state changes. If this call returns
+ * true, then the adapter state will immediately transition from {@link
+ * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
+ * later transition to either {@link #STATE_OFF} or {@link
+ * #STATE_ON}. If this call returns false then there was an
+ * immediate problem that will prevent the adapter from being turned off -
+ * such as the adapter already being turned off.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @return true to indicate adapter shutdown has begun, or false on
+ * immediate error
+ */
+ public boolean disable() {
+ try {
+ return mService.disable(true);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Returns the hardware address of the local Bluetooth adapter.
+ * <p>For example, "00:11:22:AA:BB:CC".
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return Bluetooth hardware address as string
+ */
+ public String getAddress() {
+ try {
+ return mService.getAddress();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Get the friendly Bluetooth name of the local Bluetooth adapter.
+ * <p>This name is visible to remote Bluetooth devices.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return the Bluetooth name, or null on error
+ */
+ public String getName() {
+ try {
+ return mService.getName();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Set the friendly Bluetooth name of the local Bluetoth adapter.
+ * <p>This name is visible to remote Bluetooth devices.
+ * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
+ * many remote devices can only display the first 40 characters, and some
+ * may be limited to just 20.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param name a valid Bluetooth name
+ * @return true if the name was set, false otherwise
+ */
+ public boolean setName(String name) {
+ try {
+ return mService.setName(name);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Get the current Bluetooth scan mode of the local Bluetooth adaper.
+ * <p>The Bluetooth scan mode determines if the local adapter is
+ * connectable and/or discoverable from remote Bluetooth devices.
+ * <p>Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return scan mode
+ */
+ public int getScanMode() {
+ try {
+ return mService.getScanMode();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return SCAN_MODE_NONE;
+ }
+
+ /**
+ * Set the Bluetooth scan mode of the local Bluetooth adapter.
+ * <p>The Bluetooth scan mode determines if the local adapter is
+ * connectable and/or discoverable from remote Bluetooth devices.
+ * <p>For privacy reasons, it is recommended to limit the duration of time
+ * that the local adapter remains in a discoverable scan mode. For example,
+ * 2 minutes is a generous time to allow a remote Bluetooth device to
+ * initiate and complete its discovery process.
+ * <p>Valid scan mode values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param mode valid scan mode
+ * @return true if the scan mode was set, false otherwise
+ */
+ public boolean setScanMode(int mode) {
+ try {
+ return mService.setScanMode(mode);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /** @hide */
+ public int getDiscoverableTimeout() {
+ try {
+ return mService.getDiscoverableTimeout();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return -1;
+ }
+
+ /** @hide */
+ public void setDiscoverableTimeout(int timeout) {
+ try {
+ mService.setDiscoverableTimeout(timeout);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ }
+
+ /** @hide */
+ public boolean startDiscovery() {
+ try {
+ return mService.startDiscovery();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /** @hide */
+ public void cancelDiscovery() {
+ try {
+ mService.cancelDiscovery();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ }
+
+ /** @hide */
+ public boolean isDiscovering() {
+ try {
+ return mService.isDiscovering();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * List remote devices that are bonded (paired) to the local adapter.
+ *
+ * Bonding (pairing) is the process by which the user enters a pin code for
+ * the device, which generates a shared link key, allowing for
+ * authentication and encryption of future connections. In Android we
+ * require bonding before RFCOMM or SCO connections can be made to a remote
+ * device.
+ *
+ * This function lists which remote devices we have a link key for. It does
+ * not cause any RF transmission, and does not check if the remote device
+ * still has it's link key with us. If the other side no longer has its
+ * link key then the RFCOMM or SCO connection attempt will result in an
+ * error.
+ *
+ * This function does not check if the remote device is in range.
+ *
+ * Remote devices that have an in-progress bonding attempt are not
+ * returned.
+ *
+ * @return unmodifiable set of bonded devices, or null on error
+ * @hide
+ */
+ public Set<BluetoothDevice> getBondedDevices() {
+ try {
+ return toDeviceSet(mService.listBonds());
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Create a listening, secure RFCOMM Bluetooth socket.
+ * <p>A remote device connecting to this socket will be authenticated and
+ * communication on this socket will be encrypted.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+ * connections to listening {@link BluetoothServerSocket}.
+ * <p>Valid RFCOMM channels are in range 1 to 30.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * @param channel RFCOMM channel to listen on
+ * @return a listening RFCOMM BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions, or channel in use.
+ */
+ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, true, true, channel);
+ try {
+ socket.mSocket.bindListen();
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch (IOException e2) { }
+ throw e;
+ }
+ return socket;
+ }
+
+ /**
+ * Construct an unencrypted, unauthenticated, RFCOMM server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return An RFCOMM BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, false, false, port);
+ try {
+ socket.mSocket.bindListen();
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch (IOException e2) { }
+ throw e;
+ }
+ return socket;
+ }
+
+ /**
+ * Construct a SCO server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return A SCO BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_SCO, false, false, -1);
+ try {
+ socket.mSocket.bindListen();
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch (IOException e2) { }
+ throw e;
+ }
+ return socket;
+ }
+
+ private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
+ Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
+ for (int i = 0; i < addresses.length; i++) {
+ devices.add(getRemoteDevice(addresses[i]));
+ }
+ return Collections.unmodifiableSet(devices);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothAudioGateway.java b/core/java/android/bluetooth/BluetoothAudioGateway.java
index f3afd2a..abd7723 100644
--- a/core/java/android/bluetooth/BluetoothAudioGateway.java
+++ b/core/java/android/bluetooth/BluetoothAudioGateway.java
@@ -9,24 +9,22 @@ import android.util.Log;
/**
* Listen's for incoming RFCOMM connection for the headset / handsfree service.
*
- * This class is planned for deletion, in favor of a generic Rfcomm class.
+ * TODO: Use the new generic BluetoothSocket class instead of this legacy code
*
* @hide
*/
-public class BluetoothAudioGateway {
+public final class BluetoothAudioGateway {
private static final String TAG = "BT Audio Gateway";
private static final boolean DBG = false;
private int mNativeData;
static { classInitNative(); }
- private BluetoothDevice mBluetooth;
-
/* in */
private int mHandsfreeAgRfcommChannel = -1;
private int mHeadsetAgRfcommChannel = -1;
- /* out */
+ /* out - written by native code */
private String mConnectingHeadsetAddress;
private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */
private int mConnectingHeadsetSocketFd;
@@ -35,17 +33,18 @@ public class BluetoothAudioGateway {
private int mConnectingHandsfreeSocketFd;
private int mTimeoutRemainingMs; /* in/out */
+ private final BluetoothAdapter mAdapter;
+
public static final int DEFAULT_HF_AG_CHANNEL = 10;
public static final int DEFAULT_HS_AG_CHANNEL = 11;
- public BluetoothAudioGateway(BluetoothDevice bluetooth) {
- this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
+ public BluetoothAudioGateway(BluetoothAdapter adapter) {
+ this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
}
- public BluetoothAudioGateway(BluetoothDevice bluetooth,
- int handsfreeAgRfcommChannel,
- int headsetAgRfcommChannel) {
- mBluetooth = bluetooth;
+ public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel,
+ int headsetAgRfcommChannel) {
+ mAdapter = adapter;
mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel;
mHeadsetAgRfcommChannel = headsetAgRfcommChannel;
initializeNativeDataNative();
@@ -58,18 +57,17 @@ public class BluetoothAudioGateway {
private Handler mCallback;
public class IncomingConnectionInfo {
- IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd,
- int rfcommChan) {
- mBluetooth = bluetooth;
- mAddress = address;
+ public BluetoothAdapter mAdapter;
+ public BluetoothDevice mRemoteDevice;
+ public int mSocketFd;
+ public int mRfcommChan;
+ IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice,
+ int socketFd, int rfcommChan) {
+ mAdapter = adapter;
+ mRemoteDevice = remoteDevice;
mSocketFd = socketFd;
mRfcommChan = rfcommChan;
}
-
- public BluetoothDevice mBluetooth;
- public String mAddress;
- public int mSocketFd;
- public int mRfcommChan;
}
public static final int MSG_INCOMING_HEADSET_CONNECTION = 100;
@@ -111,12 +109,11 @@ public class BluetoothAudioGateway {
mConnectingHeadsetRfcommChannel);
Message msg = Message.obtain(mCallback);
msg.what = MSG_INCOMING_HEADSET_CONNECTION;
- msg.obj =
- new IncomingConnectionInfo(
- mBluetooth,
- mConnectingHeadsetAddress,
- mConnectingHeadsetSocketFd,
- mConnectingHeadsetRfcommChannel);
+ msg.obj = new IncomingConnectionInfo(
+ mAdapter,
+ mAdapter.getRemoteDevice(mConnectingHeadsetAddress),
+ mConnectingHeadsetSocketFd,
+ mConnectingHeadsetRfcommChannel);
msg.sendToTarget();
}
if (mConnectingHandsfreeRfcommChannel >= 0) {
@@ -126,12 +123,11 @@ public class BluetoothAudioGateway {
Message msg = Message.obtain();
msg.setTarget(mCallback);
msg.what = MSG_INCOMING_HANDSFREE_CONNECTION;
- msg.obj =
- new IncomingConnectionInfo(
- mBluetooth,
- mConnectingHandsfreeAddress,
- mConnectingHandsfreeSocketFd,
- mConnectingHandsfreeRfcommChannel);
+ msg.obj = new IncomingConnectionInfo(
+ mAdapter,
+ mAdapter.getRemoteDevice(mConnectingHandsfreeAddress),
+ mConnectingHandsfreeSocketFd,
+ mConnectingHandsfreeRfcommChannel);
msg.sendToTarget();
}
}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 88ce18b..0061f10 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -46,6 +46,10 @@ public class BluetoothClass {
/** Indicates the Bluetooth API could not retrieve the class */
public static final int ERROR = 0xFF000000;
+ public static final int PROFILE_HEADSET = 0;
+ public static final int PROFILE_A2DP = 1;
+ public static final int PROFILE_OPP = 2;
+
/** Every Bluetooth device has zero or more service classes */
public static class Service {
public static final int BITMASK = 0xFFE000;
@@ -187,5 +191,74 @@ public class BluetoothClass {
return (btClass & Device.BITMASK);
}
}
+
+ /**
+ * Check class bits for possible bluetooth profile support.
+ * This is a simple heuristic that tries to guess if a device with the
+ * given class bits might support specified profile. It is not accurate for all
+ * devices. It tries to err on the side of false positives.
+ * @param btClass The class
+ * @param profile The profile to be checked
+ * @return True if this device might support specified profile.
+ */
+ public static boolean doesClassMatch(int btClass, int profile) {
+ if (profile == PROFILE_A2DP) {
+ if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
+ return true;
+ }
+ // By the A2DP spec, sinks must indicate the RENDER service.
+ // However we found some that do not (Chordette). So lets also
+ // match on some other class bits.
+ switch (BluetoothClass.Device.getDevice(btClass)) {
+ case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
+ case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
+ case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ return true;
+ default:
+ return false;
+ }
+ } else if (profile == PROFILE_HEADSET) {
+ // The render service class is required by the spec for HFP, so is a
+ // pretty good signal
+ if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
+ return true;
+ }
+ // Just in case they forgot the render service class
+ switch (BluetoothClass.Device.getDevice(btClass)) {
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ return true;
+ default:
+ return false;
+ }
+ } else if (profile == PROFILE_OPP) {
+ if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.OBJECT_TRANSFER)) {
+ return true;
+ }
+
+ switch (BluetoothClass.Device.getDevice(btClass)) {
+ case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
+ case BluetoothClass.Device.COMPUTER_DESKTOP:
+ case BluetoothClass.Device.COMPUTER_SERVER:
+ case BluetoothClass.Device.COMPUTER_LAPTOP:
+ case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
+ case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
+ case BluetoothClass.Device.COMPUTER_WEARABLE:
+ case BluetoothClass.Device.PHONE_UNCATEGORIZED:
+ case BluetoothClass.Device.PHONE_CELLULAR:
+ case BluetoothClass.Device.PHONE_CORDLESS:
+ case BluetoothClass.Device.PHONE_SMART:
+ case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
+ case BluetoothClass.Device.PHONE_ISDN:
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothDevice.aidl b/core/java/android/bluetooth/BluetoothDevice.aidl
new file mode 100644
index 0000000..daae74d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothDevice.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothDevice;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 951b4b0..1ab4389 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,337 +16,203 @@
package android.bluetooth;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
+ * Represents a remote Bluetooth device.
*
- * Manages the local Bluetooth device. Scan for devices, create bondings,
- * power up and down the adapter.
+ * <p>Use {@link BluetoothAdapter#getRemoteDevice} to create a {@link
+ * BluetoothDevice}.
*
- * @hide
+ * <p>This class is really just a thin wrapper for a Bluetooth hardware
+ * address. Objects of this class are immutable. Operations on this class
+ * are performed on the remote Bluetooth hardware address, using the
+ * {@link BluetoothAdapter} that was used to create this {@link
+ * BluetoothDevice}.
+ *
+ * TODO: unhide more of this class
*/
-public class BluetoothDevice {
-
- public static final int BLUETOOTH_STATE_OFF = 0;
- public static final int BLUETOOTH_STATE_TURNING_ON = 1;
- public static final int BLUETOOTH_STATE_ON = 2;
- public static final int BLUETOOTH_STATE_TURNING_OFF = 3;
-
- /** Inquiry scan and page scan are both off.
- * Device is neither discoverable nor connectable */
- public static final int SCAN_MODE_NONE = 0;
- /** Page scan is on, inquiry scan is off.
- * Device is connectable, but not discoverable */
- public static final int SCAN_MODE_CONNECTABLE = 1;
- /** Page scan and inquiry scan are on.
- * Device is connectable and discoverable */
- public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3;
-
- public static final int RESULT_FAILURE = -1;
- public static final int RESULT_SUCCESS = 0;
+public final class BluetoothDevice implements Parcelable {
+ private static final String TAG = "BluetoothDevice";
+
+ /**
+ * Sentinel error value for this class. Guaranteed to not equal any other
+ * integer constant in this class. Provided as a convenience for functions
+ * that require a sentinel error value, for example:
+ * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ * BluetoothAdapter.ERROR)</code>
+ */
+ public static final int ERROR = -1;
/** We do not have a link key for the remote device, and are therefore not
- * bonded */
+ * bonded
+ * @hide*/
public static final int BOND_NOT_BONDED = 0;
- /** We have a link key for the remote device, and are probably bonded. */
+ /** We have a link key for the remote device, and are probably bonded.
+ * @hide */
public static final int BOND_BONDED = 1;
- /** We are currently attempting bonding */
+ /** We are currently attempting bonding
+ * @hide */
public static final int BOND_BONDING = 2;
- //TODO: Unify these result codes in BluetoothResult or BluetoothError
+ /** Ask device picker to show all kinds of BT devices.
+ * @hide */
+ public static final int DEVICE_PICKER_FILTER_TYPE_ALL = 0;
+ /** Ask device picker to show BT devices that support AUDIO profiles.
+ * @hide */
+ public static final int DEVICE_PICKER_FILTER_TYPE_AUDIO = 1;
+ /** Ask device picker to show BT devices that support Object Transfer.
+ * @hide */
+ public static final int DEVICE_PICKER_FILTER_TYPE_TRANSFER = 2;
+
+ /** A bond attempt succeeded
+ * @hide */
+ public static final int BOND_SUCCESS = 0;
/** A bond attempt failed because pins did not match, or remote device did
- * not respond to pin request in time */
+ * not respond to pin request in time
+ * @hide */
public static final int UNBOND_REASON_AUTH_FAILED = 1;
/** A bond attempt failed because the other side explicilty rejected
- * bonding */
+ * bonding
+ * @hide */
public static final int UNBOND_REASON_AUTH_REJECTED = 2;
- /** A bond attempt failed because we canceled the bonding process */
+ /** A bond attempt failed because we canceled the bonding process
+ * @hide */
public static final int UNBOND_REASON_AUTH_CANCELED = 3;
- /** A bond attempt failed because we could not contact the remote device */
+ /** A bond attempt failed because we could not contact the remote device
+ * @hide */
public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
- /** A bond attempt failed because a discovery is in progress */
+ /** A bond attempt failed because a discovery is in progress
+ * @hide */
public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
- /** An existing bond was explicitly revoked */
+ /** An existing bond was explicitly revoked
+ * @hide */
public static final int UNBOND_REASON_REMOVED = 6;
- private static final String TAG = "BluetoothDevice";
-
- private final IBluetoothDevice mService;
- /**
- * @hide - hide this because it takes a parameter of type
- * IBluetoothDevice, which is a System private class.
- * Also note that Context.getSystemService is a factory that
- * returns a BlueToothDevice. That is the right way to get
- * a BluetoothDevice.
- */
- public BluetoothDevice(IBluetoothDevice service) {
- mService = service;
- }
+ //TODO: Remove duplicates between here and BluetoothAdapter
+ /** The user will be prompted to enter a pin
+ * @hide */
+ public static final int PAIRING_VARIANT_PIN = 0;
+ /** The user will be prompted to enter a passkey
+ * @hide */
+ public static final int PAIRING_VARIANT_PASSKEY = 1;
+ /** The user will be prompted to confirm the passkey displayed on the screen
+ * @hide */
+ public static final int PAIRING_VARIANT_CONFIRMATION = 2;
- /**
- * Is Bluetooth currently turned on.
- *
- * @return true if Bluetooth enabled, false otherwise.
- */
- public boolean isEnabled() {
- try {
- return mService.isEnabled();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ private static final int ADDRESS_LENGTH = 17;
- /**
- * Get the current state of Bluetooth.
- *
- * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR.
- */
- public int getBluetoothState() {
- try {
- return mService.getBluetoothState();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR;
- }
+ private static IBluetooth sService; /* Guarenteed constant after first object constructed */
- /**
- * Enable the Bluetooth device.
- * Turn on the underlying hardware.
- * This is an asynchronous call,
- * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if
- * and when the device is sucessfully enabled.
- * @return false if we cannot enable the Bluetooth device. True does not
- * imply the device was enabled, it only implies that so far there were no
- * problems.
- */
- public boolean enable() {
- try {
- return mService.enable();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ private final String mAddress;
/**
- * Disable the Bluetooth device.
- * This turns off the underlying hardware.
- *
- * @return true if successful, false otherwise.
+ * Create a new BluetoothDevice
+ * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
+ * and is validated in this constructor.
+ * @param address valid Bluetooth MAC address
+ * @throws RuntimeException Bluetooth is not available on this platform
+ * @throws IllegalArgumentException address is invalid
+ * @hide
*/
- public boolean disable() {
- try {
- return mService.disable(true);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ /*package*/ BluetoothDevice(String address) {
+ synchronized (BluetoothDevice.class) {
+ if (sService == null) {
+ IBinder b = ServiceManager.getService(Context.BLUETOOTH_SERVICE);
+ if (b == null) {
+ throw new RuntimeException("Bluetooth service not available");
+ }
+ sService = IBluetooth.Stub.asInterface(b);
+ }
+ }
- public String getAddress() {
- try {
- return mService.getAddress();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
+ if (!checkBluetoothAddress(address)) {
+ throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
+ }
- /**
- * Get the friendly Bluetooth name of this device.
- *
- * This name is visible to remote Bluetooth devices. Currently it is only
- * possible to retrieve the Bluetooth name when Bluetooth is enabled.
- *
- * @return the Bluetooth name, or null if there was a problem.
- */
- public String getName() {
- try {
- return mService.getName();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ mAddress = address;
}
- /**
- * Set the friendly Bluetooth name of this device.
- *
- * This name is visible to remote Bluetooth devices. The Bluetooth Service
- * is responsible for persisting this name.
- *
- * @param name the name to set
- * @return true, if the name was successfully set. False otherwise.
- */
- public boolean setName(String name) {
- try {
- return mService.setName(name);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothDevice) {
+ return mAddress.equals(((BluetoothDevice)o).getAddress());
+ }
return false;
}
- public String getVersion() {
- try {
- return mService.getVersion();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getRevision() {
- try {
- return mService.getRevision();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getManufacturer() {
- try {
- return mService.getManufacturer();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getCompany() {
- try {
- return mService.getCompany();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
-
- /**
- * Get the current scan mode.
- * Used to determine if the local device is connectable and/or discoverable
- * @return Scan mode, one of SCAN_MODE_* or an error code
- */
- public int getScanMode() {
- try {
- return mService.getScanMode();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR_IPC;
+ @Override
+ public int hashCode() {
+ return mAddress.hashCode();
}
/**
- * Set the current scan mode.
- * Used to make the local device connectable and/or discoverable
- * @param scanMode One of SCAN_MODE_*
+ * Returns a string representation of this BluetoothDevice.
+ * <p>Currently this is the Bluetooth hardware address, for example
+ * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
+ * if you explicitly require the Bluetooth hardware address in case the
+ * {@link #toString} representation changes in the future.
+ * @return string representation of this BluetoothDevice
*/
- public void setScanMode(int scanMode) {
- try {
- mService.setScanMode(scanMode);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- }
-
- public int getDiscoverableTimeout() {
- try {
- return mService.getDiscoverableTimeout();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return -1;
- }
- public void setDiscoverableTimeout(int timeout) {
- try {
- mService.setDiscoverableTimeout(timeout);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- }
-
- public boolean startDiscovery() {
- return startDiscovery(true);
- }
- public boolean startDiscovery(boolean resolveNames) {
- try {
- return mService.startDiscovery(resolveNames);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ @Override
+ public String toString() {
+ return mAddress;
}
- public void cancelDiscovery() {
- try {
- mService.cancelDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ /** @hide */
+ public int describeContents() {
+ return 0;
}
- public boolean isDiscovering() {
- try {
- return mService.isDiscovering();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
-
- public boolean startPeriodicDiscovery() {
- try {
- return mService.startPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
- public boolean stopPeriodicDiscovery() {
- try {
- return mService.stopPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
- public boolean isPeriodicDiscovery() {
- try {
- return mService.isPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
-
- public String[] listRemoteDevices() {
- try {
- return mService.listRemoteDevices();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
+ /** @hide */
+ public static final Parcelable.Creator<BluetoothDevice> CREATOR =
+ new Parcelable.Creator<BluetoothDevice>() {
+ public BluetoothDevice createFromParcel(Parcel in) {
+ return new BluetoothDevice(in.readString());
+ }
+ public BluetoothDevice[] newArray(int size) {
+ return new BluetoothDevice[size];
+ }
+ };
- /**
- * List remote devices that have a low level (ACL) connection.
- *
- * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
- * an ACL connection even when not paired - this is common for SDP queries
- * or for in-progress pairing requests.
- *
- * In most cases you probably want to test if a higher level protocol is
- * connected, rather than testing ACL connections.
- *
- * @return bluetooth hardware addresses of remote devices with a current
- * ACL connection. Array size is 0 if no devices have a
- * connection. Null on error.
- */
- public String[] listAclConnections() {
- try {
- return mService.listAclConnections();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ /** @hide */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mAddress);
}
/**
- * Check if a specified remote device has a low level (ACL) connection.
- *
- * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
- * an ACL connection even when not paired - this is common for SDP queries
- * or for in-progress pairing requests.
- *
- * In most cases you probably want to test if a higher level protocol is
- * connected, rather than testing ACL connections.
- *
- * @param address the Bluetooth hardware address you want to check.
- * @return true if there is an ACL connection, false otherwise and on
- * error.
+ * Returns the hardware address of this BluetoothDevice.
+ * <p> For example, "00:11:22:AA:BB:CC".
+ * @return Bluetooth hardware address as string
*/
- public boolean isAclConnected(String address) {
- try {
- return mService.isAclConnected(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ public String getAddress() {
+ return mAddress;
}
/**
- * Perform a low level (ACL) disconnection of a remote device.
+ * Get the friendly Bluetooth name of the remote device.
*
- * This forcably disconnects the ACL layer connection to a remote device,
- * which will cause all RFCOMM, SDP and L2CAP connections to this remote
- * device to close.
+ * <p>The local adapter will automatically retrieve remote names when
+ * performing a device scan, and will cache them. This method just returns
+ * the name for this device from the cache.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
- * @param address the Bluetooth hardware address you want to disconnect.
- * @return true if the device was disconnected, false otherwise and on
- * error.
+ * @return the Bluetooth name, or null if there was a problem.
*/
- public boolean disconnectRemoteDeviceAcl(String address) {
+ public String getName() {
try {
- return mService.disconnectRemoteDeviceAcl(address);
+ return sService.getRemoteName(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ return null;
}
/**
@@ -358,173 +224,187 @@ public class BluetoothDevice {
* @param address the remote device Bluetooth address.
* @return false If there was an immediate problem creating the bonding,
* true otherwise.
+ * @hide
*/
- public boolean createBond(String address) {
+ public boolean createBond() {
try {
- return mService.createBond(address);
+ return sService.createBond(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
* Cancel an in-progress bonding request started with createBond.
+ * @hide
*/
- public boolean cancelBondProcess(String address) {
+ public boolean cancelBondProcess() {
try {
- return mService.cancelBondProcess(address);
+ return sService.cancelBondProcess(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
- * Remove an already exisiting bonding (delete the link key).
- */
- public boolean removeBond(String address) {
- try {
- return mService.removeBond(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
-
- /**
- * List remote devices that are bonded (paired) to the local device.
+ * Removes the remote device and the pairing information associated
+ * with it.
*
- * Bonding (pairing) is the process by which the user enters a pin code for
- * the device, which generates a shared link key, allowing for
- * authentication and encryption of future connections. In Android we
- * require bonding before RFCOMM or SCO connections can be made to a remote
- * device.
- *
- * This function lists which remote devices we have a link key for. It does
- * not cause any RF transmission, and does not check if the remote device
- * still has it's link key with us. If the other side no longer has its
- * link key then the RFCOMM or SCO connection attempt will result in an
- * error.
- *
- * This function does not check if the remote device is in range.
- *
- * Remote devices that have an in-progress bonding attempt are not
- * returned.
- *
- * @return bluetooth hardware addresses of remote devices that are
- * bonded. Array size is 0 if no devices are bonded. Null on error.
+ * @return true if the device was disconnected, false otherwise and on
+ * error.
+ * @hide
*/
- public String[] listBonds() {
+ public boolean removeBond() {
try {
- return mService.listBonds();
+ return sService.removeBond(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
/**
* Get the bonding state of a remote device.
*
* Result is one of:
- * BluetoothError.*
* BOND_*
+ * ERROR
*
* @param address Bluetooth hardware address of the remote device to check.
* @return Result code
+ * @hide
*/
- public int getBondState(String address) {
+ public int getBondState() {
try {
- return mService.getBondState(address);
+ return sService.getBondState(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR_IPC;
+ return BluetoothDevice.ERROR;
}
- public String getRemoteName(String address) {
+ /**
+ * Get trust state of a remote device.
+ * @hide
+ */
+ public boolean getTrustState() {
try {
- return mService.getRemoteName(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return sService.getTrustState(mAddress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
}
- public String getRemoteVersion(String address) {
+ /**
+ * Set trust state for a remote device.
+ * @param value the trust state value (true or false)
+ * @hide
+ */
+ public boolean setTrust(boolean value) {
try {
- return mService.getRemoteVersion(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return sService.setTrust(mAddress, value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
}
- public String getRemoteRevision(String address) {
+
+ /** @hide */
+ public int getBluetoothClass() {
try {
- return mService.getRemoteRevision(address);
+ return sService.getRemoteClass(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return BluetoothDevice.ERROR;
}
- public String getRemoteManufacturer(String address) {
+
+ /** @hide */
+ public String[] getUuids() {
try {
- return mService.getRemoteManufacturer(address);
+ return sService.getRemoteUuids(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
- public String getRemoteCompany(String address) {
- try {
- return mService.getRemoteCompany(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+
+ /** @hide */
+ public int getServiceChannel(String uuid) {
+ try {
+ return sService.getRemoteServiceChannel(mAddress, uuid);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return BluetoothDevice.ERROR;
}
- /**
- * Returns the RFCOMM channel associated with the 16-byte UUID on
- * the remote Bluetooth address.
- *
- * Performs a SDP ServiceSearchAttributeRequest transaction. The provided
- * uuid is verified in the returned record. If there was a problem, or the
- * specified uuid does not exist, -1 is returned.
- */
- public boolean getRemoteServiceChannel(String address, short uuid16,
- IBluetoothDeviceCallback callback) {
+ /** @hide */
+ public boolean setPin(byte[] pin) {
try {
- return mService.getRemoteServiceChannel(address, uuid16, callback);
+ return sService.setPin(mAddress, pin);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
- /**
- * Get the major, minor and servics classes of a remote device.
- * These classes are encoded as a 32-bit integer. See BluetoothClass.
- * @param address remote device
- * @return 32-bit class suitable for use with BluetoothClass, or
- * BluetoothClass.ERROR on error
- */
- public int getRemoteClass(String address) {
+ /** @hide */
+ public boolean setPasskey(int passkey) {
try {
- return mService.getRemoteClass(address);
+ return sService.setPasskey(mAddress, passkey);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothClass.ERROR;
+ return false;
}
- public byte[] getRemoteFeatures(String address) {
+ /** @hide */
+ public boolean setPairingConfirmation(boolean confirm) {
try {
- return mService.getRemoteFeatures(address);
+ return sService.setPairingConfirmation(mAddress, confirm);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- public String lastSeen(String address) {
+
+ /** @hide */
+ public boolean cancelPairingUserInput() {
try {
- return mService.lastSeen(address);
+ return sService.cancelPairingUserInput(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- public String lastUsed(String address) {
- try {
- return mService.lastUsed(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+
+ /**
+ * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
+ * outgoing connection to this remote device.
+ * <p>The remote device will be authenticated and communication on this
+ * socket will be encrypted.
+ * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+ * connection.
+ * <p>Valid RFCOMM channels are in range 1 to 30.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @param channel RFCOMM channel to connect to
+ * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions
+ */
+ public BluetoothSocket createRfcommSocket(int channel) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel);
}
- public boolean setPin(String address, byte[] pin) {
- try {
- return mService.setPin(address, pin);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ /**
+ * Construct an insecure RFCOMM socket ready to start an outgoing
+ * connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * The remote device will not be authenticated and communication on this
+ * socket will not be encrypted.
+ * @param port remote port
+ * @return An RFCOMM BluetoothSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port);
}
- public boolean cancelPin(String address) {
- try {
- return mService.cancelPin(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+
+ /**
+ * Construct a SCO socket ready to start an outgoing connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * @return a SCO BluetoothSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothSocket createScoSocket() throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1);
}
/**
@@ -534,6 +414,7 @@ public class BluetoothDevice {
* @param pin pin as java String
* @return the pin code as a UTF8 byte array, or null if it is an invalid
* Bluetooth pin.
+ * @hide
*/
public static byte[] convertPinToBytes(String pin) {
if (pin == null) {
@@ -552,8 +433,8 @@ public class BluetoothDevice {
return pinBytes;
}
- private static final int ADDRESS_LENGTH = 17;
- /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */
+ /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0"
+ * @hide */
public static boolean checkBluetoothAddress(String address) {
if (address == null || address.length() != ADDRESS_LENGTH) {
return false;
diff --git a/core/java/android/bluetooth/BluetoothError.java b/core/java/android/bluetooth/BluetoothError.java
deleted file mode 100644
index 2554bea..0000000
--- a/core/java/android/bluetooth/BluetoothError.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * Bluetooth API error codes.
- *
- * Errors are always negative.
- *
- * @hide
- */
-public class BluetoothError {
- /** No error */
- public static final int SUCCESS = 0;
-
- /** Generic error */
- public static final int ERROR = -1000;
-
- /** Bluetooth currently disabled */
- public static final int ERROR_DISABLED = -1001;
-
- /** IPC is not ready, for example service is not yet bound */
- public static final int ERROR_IPC_NOT_READY = -1011;
-
- /** Some other IPC error, for example a RemoteException */
- public static final int ERROR_IPC = -1012;
-
-}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index fe1e09a..d31b6ae 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -49,7 +49,7 @@ import android.util.Log;
*
* @hide
*/
-public class BluetoothHeadset {
+public final class BluetoothHeadset {
private static final String TAG = "BluetoothHeadset";
private static final boolean DBG = false;
@@ -163,16 +163,16 @@ public class BluetoothHeadset {
}
/**
- * Get the Bluetooth address of the current headset.
- * @return The Bluetooth address, or null if not in connected or connecting
+ * Get the BluetoothDevice for the current headset.
+ * @return current headset, or null if not in connected or connecting
* state, or if this proxy object is not connected to the Headset
* service.
*/
- public String getHeadsetAddress() {
- if (DBG) log("getHeadsetAddress()");
+ public BluetoothDevice getCurrentHeadset() {
+ if (DBG) log("getCurrentHeadset()");
if (mService != null) {
try {
- return mService.getHeadsetAddress();
+ return mService.getCurrentHeadset();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -185,19 +185,19 @@ public class BluetoothHeadset {
* Request to initiate a connection to a headset.
* This call does not block. Fails if a headset is already connecting
* or connected.
- * Initiates auto-connection if address is null. Tries to connect to all
+ * Initiates auto-connection if device is null. Tries to connect to all
* devices with priority greater than PRIORITY_AUTO in descending order.
- * @param address The Bluetooth Address to connect to, or null to
- * auto-connect to the last connected headset.
- * @return False if there was a problem initiating the connection
- * procedure, and no further HEADSET_STATE_CHANGED intents
- * will be expected.
+ * @param device device to connect to, or null to auto-connect last connected
+ * headset
+ * @return false if there was a problem initiating the connection
+ * procedure, and no further HEADSET_STATE_CHANGED intents
+ * will be expected.
*/
- public boolean connectHeadset(String address) {
- if (DBG) log("connectHeadset(" + address + ")");
+ public boolean connectHeadset(BluetoothDevice device) {
+ if (DBG) log("connectHeadset(" + device + ")");
if (mService != null) {
try {
- if (mService.connectHeadset(address)) {
+ if (mService.connectHeadset(device)) {
return true;
}
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -213,11 +213,11 @@ public class BluetoothHeadset {
* connecting). Returns false if not connected, or if this proxy object
* if not currently connected to the headset service.
*/
- public boolean isConnected(String address) {
- if (DBG) log("isConnected(" + address + ")");
+ public boolean isConnected(BluetoothDevice device) {
+ if (DBG) log("isConnected(" + device + ")");
if (mService != null) {
try {
- return mService.isConnected(address);
+ return mService.isConnected(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -295,16 +295,16 @@ public class BluetoothHeadset {
* auto-connected.
* Incoming connections are ignored regardless of priority if there is
* already a headset connected.
- * @param address Paired headset
+ * @param device paired headset
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
- * @return True if successful, false if there was some error.
+ * @return true if successful, false if there was some error
*/
- public boolean setPriority(String address, int priority) {
- if (DBG) log("setPriority(" + address + ", " + priority + ")");
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
if (mService != null) {
try {
- return mService.setPriority(address, priority);
+ return mService.setPriority(device, priority);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -315,14 +315,14 @@ public class BluetoothHeadset {
/**
* Get priority of headset.
- * @param address Headset
- * @return non-negative priority, or negative error code on error.
+ * @param device headset
+ * @return non-negative priority, or negative error code on error
*/
- public int getPriority(String address) {
- if (DBG) log("getPriority(" + address + ")");
+ public int getPriority(BluetoothDevice device) {
+ if (DBG) log("getPriority(" + device + ")");
if (mService != null) {
try {
- return mService.getPriority(address);
+ return mService.getPriority(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -356,30 +356,6 @@ public class BluetoothHeadset {
return -1;
}
- /**
- * Check class bits for possible HSP or HFP support.
- * This is a simple heuristic that tries to guess if a device with the
- * given class bits might support HSP or HFP. It is not accurate for all
- * devices. It tries to err on the side of false positives.
- * @return True if this device might support HSP or HFP.
- */
- public static boolean doesClassMatch(int btClass) {
- // The render service class is required by the spec for HFP, so is a
- // pretty good signal
- if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
- return true;
- }
- // Just in case they forgot the render service class
- switch (BluetoothClass.Device.getDevice(btClass)) {
- case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- return true;
- default:
- return false;
- }
- }
-
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
new file mode 100644
index 0000000..03af953
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * BluetoothInputStream.
+ *
+ * Used to write to a Bluetooth socket.
+ *
+ * @hide
+ */
+/*package*/ final class BluetoothInputStream extends InputStream {
+ private BluetoothSocket mSocket;
+
+ /*package*/ BluetoothInputStream(BluetoothSocket s) {
+ mSocket = s;
+ }
+
+ /**
+ * Return number of bytes available before this stream will block.
+ */
+ public int available() throws IOException {
+ return mSocket.available();
+ }
+
+ public void close() throws IOException {
+ mSocket.close();
+ }
+
+ /**
+ * Reads a single byte from this stream and returns it as an integer in the
+ * range from 0 to 255. Returns -1 if the end of the stream has been
+ * reached. Blocks until one byte has been read, the end of the source
+ * stream is detected or an exception is thrown.
+ *
+ * @return the byte read or -1 if the end of stream has been reached.
+ * @throws IOException
+ * if the stream is closed or another IOException occurs.
+ * @since Android 1.5
+ */
+ public int read() throws IOException {
+ byte b[] = new byte[1];
+ int ret = mSocket.read(b, 0, 1);
+ if (ret == 1) {
+ return (int)b[0] & 0xff;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Reads at most {@code length} bytes from this stream and stores them in
+ * the byte array {@code b} starting at {@code offset}.
+ *
+ * @param b
+ * the byte array in which to store the bytes read.
+ * @param offset
+ * the initial position in {@code buffer} to store the bytes
+ * read from this stream.
+ * @param length
+ * the maximum number of bytes to store in {@code b}.
+ * @return the number of bytes actually read or -1 if the end of the stream
+ * has been reached.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code length < 0}, or if
+ * {@code offset + length} is greater than the length of
+ * {@code b}.
+ * @throws IOException
+ * if the stream is closed or another IOException occurs.
+ * @since Android 1.5
+ */
+ public int read(byte[] b, int offset, int length) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("byte array is null");
+ }
+ if ((offset | length) < 0 || length > b.length - offset) {
+ throw new ArrayIndexOutOfBoundsException("invalid offset or length");
+ }
+ return mSocket.read(b, offset, length);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java
index 344601b..8de19f5 100644
--- a/core/java/android/bluetooth/BluetoothIntent.java
+++ b/core/java/android/bluetooth/BluetoothIntent.java
@@ -20,19 +20,14 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * Manages the local Bluetooth device. Scan for devices, create bondings,
- * power up and down the adapter.
+ * Bluetooth API constants.
*
+ * TODO: Deprecate this class
* @hide
*/
public interface BluetoothIntent {
- public static final String SCAN_MODE =
- "android.bluetooth.intent.SCAN_MODE";
- public static final String ADDRESS =
- "android.bluetooth.intent.ADDRESS";
+ public static final String DEVICE =
+ "android.bluetooth.intent.DEVICE";
public static final String NAME =
"android.bluetooth.intent.NAME";
public static final String ALIAS =
@@ -41,10 +36,6 @@ public interface BluetoothIntent {
"android.bluetooth.intent.RSSI";
public static final String CLASS =
"android.bluetooth.intent.CLASS";
- public static final String BLUETOOTH_STATE =
- "android.bluetooth.intent.BLUETOOTH_STATE";
- public static final String BLUETOOTH_PREVIOUS_STATE =
- "android.bluetooth.intent.BLUETOOTH_PREVIOUS_STATE";
public static final String HEADSET_STATE =
"android.bluetooth.intent.HEADSET_STATE";
public static final String HEADSET_PREVIOUS_STATE =
@@ -57,25 +48,43 @@ public interface BluetoothIntent {
"android.bluetooth.intent.BOND_PREVIOUS_STATE";
public static final String REASON =
"android.bluetooth.intent.REASON";
+ public static final String PAIRING_VARIANT =
+ "android.bluetooth.intent.PAIRING_VARIANT";
+ public static final String PASSKEY =
+ "android.bluetooth.intent.PASSKEY";
- /** Broadcast when the local Bluetooth device state changes, for example
- * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and
- * BLUETOOTH_PREVIOUS_STATE. */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String BLUETOOTH_STATE_CHANGED_ACTION =
- "android.bluetooth.intent.action.BLUETOOTH_STATE_CHANGED";
+ public static final String DEVICE_PICKER_NEED_AUTH =
+ "android.bluetooth.intent.DEVICE_PICKER_NEED_AUTH";
+ public static final String DEVICE_PICKER_FILTER_TYPE =
+ "android.bluetooth.intent.DEVICE_PICKER_FILTER_TYPE";
+ public static final String DEVICE_PICKER_LAUNCH_PACKAGE =
+ "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_PACKAGE";
+ public static final String DEVICE_PICKER_LAUNCH_CLASS =
+ "android.bluetooth.intent.DEVICE_PICKER_LAUNCH_CLASS";
+ /**
+ * Broadcast when one BT device is selected from BT device picker screen.
+ * Selected BT device address is contained in extra string "BluetoothIntent.ADDRESS".
+ */
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String NAME_CHANGED_ACTION =
- "android.bluetooth.intent.action.NAME_CHANGED";
+ public static final String DEVICE_PICKER_DEVICE_SELECTED =
+ "android.bluetooth.intent.action.DEVICE_SELECTED";
/**
- * Broadcast when the scan mode changes. Always contains an int extra
- * named SCAN_MODE that contains the new scan mode.
+ * Broadcast when someone want to select one BT device from devices list.
+ * This intent contains below extra data:
+ * - BluetoothIntent.DEVICE_PICKER_NEED_AUTH (boolean): if need authentication
+ * - BluetoothIntent.DEVICE_PICKER_FILTER_TYPE (int): what kinds of device should be listed
+ * - BluetoothIntent.DEVICE_PICKER_LAUNCH_PACKAGE (string): where(which package) this intent come from
+ * - BluetoothIntent.DEVICE_PICKER_LAUNCH_CLASS (string): where(which class) this intent come from
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String SCAN_MODE_CHANGED_ACTION =
- "android.bluetooth.intent.action.SCAN_MODE_CHANGED";
+ public static final String DEVICE_PICKER_DEVICE_PICKER =
+ "android.bluetooth.intent.action.DEVICE_PICKER";
+
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String NAME_CHANGED_ACTION =
+ "android.bluetooth.intent.action.NAME_CHANGED";
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String DISCOVERY_STARTED_ACTION =
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
new file mode 100644
index 0000000..62242a2
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * BluetoothOutputStream.
+ *
+ * Used to read from a Bluetooth socket.
+ *
+ * @hide
+ */
+/*package*/ final class BluetoothOutputStream extends OutputStream {
+ private BluetoothSocket mSocket;
+
+ /*package*/ BluetoothOutputStream(BluetoothSocket s) {
+ mSocket = s;
+ }
+
+ /**
+ * Close this output stream and the socket associated with it.
+ */
+ public void close() throws IOException {
+ mSocket.close();
+ }
+
+ /**
+ * Writes a single byte to this stream. Only the least significant byte of
+ * the integer {@code oneByte} is written to the stream.
+ *
+ * @param oneByte
+ * the byte to be written.
+ * @throws IOException
+ * if an error occurs while writing to this stream.
+ * @since Android 1.0
+ */
+ public void write(int oneByte) throws IOException {
+ byte b[] = new byte[1];
+ b[0] = (byte)oneByte;
+ mSocket.write(b, 0, 1);
+ }
+
+ /**
+ * Writes {@code count} bytes from the byte array {@code buffer} starting
+ * at position {@code offset} to this stream.
+ *
+ * @param b
+ * the buffer to be written.
+ * @param offset
+ * the start position in {@code buffer} from where to get bytes.
+ * @param count
+ * the number of bytes from {@code buffer} to write to this
+ * stream.
+ * @throws IOException
+ * if an error occurs while writing to this stream.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code count < 0}, or if
+ * {@code offset + count} is bigger than the length of
+ * {@code buffer}.
+ * @since Android 1.0
+ */
+ public void write(byte[] b, int offset, int count) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("buffer is null");
+ }
+ if ((offset | count) < 0 || count > b.length - offset) {
+ throw new IndexOutOfBoundsException("invalid offset or length");
+ }
+ mSocket.write(b, offset, count);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
new file mode 100644
index 0000000..645e241
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * The Android Bluetooth API is not finalized, and *will* change. Use at your
+ * own risk.
+ *
+ * Public API for controlling the Bluetooth Pbap Service. This includes
+ * Bluetooth Phone book Access profile.
+ * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
+ * Service via IPC.
+ *
+ * Creating a BluetoothPbap object will create a binding with the
+ * BluetoothPbap service. Users of this object should call close() when they
+ * are finished with the BluetoothPbap, so that this proxy object can unbind
+ * from the service.
+ *
+ * This BluetoothPbap object is not immediately bound to the
+ * BluetoothPbap service. Use the ServiceListener interface to obtain a
+ * notification when it is bound, this is especially important if you wish to
+ * immediately call methods on BluetoothPbap after construction.
+ *
+ * Android only supports one connected Bluetooth Pce at a time.
+ *
+ * @hide
+ */
+public class BluetoothPbap {
+
+ private static final String TAG = "BluetoothPbap";
+ private static final boolean DBG = false;
+
+ /** int extra for PBAP_STATE_CHANGED_ACTION */
+ public static final String PBAP_STATE =
+ "android.bluetooth.pbap.intent.PBAP_STATE";
+ /** int extra for PBAP_STATE_CHANGED_ACTION */
+ public static final String PBAP_PREVIOUS_STATE =
+ "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
+
+ /** Indicates the state of an pbap connection state has changed.
+ * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
+ * BluetoothIntent.ADDRESS extras.
+ */
+ public static final String PBAP_STATE_CHANGED_ACTION =
+ "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
+
+ private IBluetoothPbap mService;
+ private final Context mContext;
+ private final ServiceListener mServiceListener;
+
+ /** There was an error trying to obtain the state */
+ public static final int STATE_ERROR = -1;
+ /** No client currently connected */
+ public static final int STATE_DISCONNECTED = 0;
+ /** Connection attempt in progress */
+ public static final int STATE_CONNECTING = 1;
+ /** Client is currently connected */
+ public static final int STATE_CONNECTED = 2;
+
+ public static final int RESULT_FAILURE = 0;
+ public static final int RESULT_SUCCESS = 1;
+ /** Connection canceled before completion. */
+ public static final int RESULT_CANCELED = 2;
+
+ /**
+ * An interface for notifying Bluetooth PCE IPC clients when they have
+ * been connected to the BluetoothPbap service.
+ */
+ public interface ServiceListener {
+ /**
+ * Called to notify the client when this proxy object has been
+ * connected to the BluetoothPbap service. Clients must wait for
+ * this callback before making IPC calls on the BluetoothPbap
+ * service.
+ */
+ public void onServiceConnected();
+
+ /**
+ * Called to notify the client that this proxy object has been
+ * disconnected from the BluetoothPbap service. Clients must not
+ * make IPC calls on the BluetoothPbap service after this callback.
+ * This callback will currently only occur if the application hosting
+ * the BluetoothPbap service, but may be called more often in future.
+ */
+ public void onServiceDisconnected();
+ }
+
+ /**
+ * Create a BluetoothPbap proxy object.
+ */
+ public BluetoothPbap(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Close the connection to the backing service.
+ * Other public functions of BluetoothPbap will return default error
+ * results once close() has been called. Multiple invocations of close()
+ * are ok.
+ */
+ public synchronized void close() {
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+
+ /**
+ * Get the current state of the BluetoothPbap service.
+ * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
+ * object is currently not connected to the Pbap service.
+ */
+ public int getState() {
+ if (DBG) log("getState()");
+ if (mService != null) {
+ try {
+ return mService.getState();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return BluetoothPbap.STATE_ERROR;
+ }
+
+ /**
+ * Get the currently connected remote Bluetooth device (PCE).
+ * @return The remote Bluetooth device, or null if not in connected or
+ * connecting state, or if this proxy object is not connected to
+ * the Pbap service.
+ */
+ public BluetoothDevice getClient() {
+ if (DBG) log("getClient()");
+ if (mService != null) {
+ try {
+ return mService.getClient();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the specified Bluetooth device is connected (does not
+ * include connecting). Returns false if not connected, or if this proxy
+ * object is not currently connected to the Pbap service.
+ */
+ public boolean isConnected(BluetoothDevice device) {
+ if (DBG) log("isConnected(" + device + ")");
+ if (mService != null) {
+ try {
+ return mService.isConnected(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Disconnects the current Pbap client (PCE). Currently this call blocks,
+ * it may soon be made asynchornous. Returns false if this proxy object is
+ * not currently connected to the Pbap service.
+ */
+ public boolean disconnect() {
+ if (DBG) log("disconnect()");
+ if (mService != null) {
+ try {
+ mService.disconnect();
+ return true;
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Check class bits for possible PBAP support.
+ * This is a simple heuristic that tries to guess if a device with the
+ * given class bits might support PBAP. It is not accurate for all
+ * devices. It tries to err on the side of false positives.
+ * @return True if this device might support PBAP.
+ */
+ public static boolean doesClassMatchSink(int btClass) {
+ // TODO optimize the rule
+ switch (BluetoothClass.Device.getDevice(btClass)) {
+ case BluetoothClass.Device.COMPUTER_DESKTOP:
+ case BluetoothClass.Device.COMPUTER_LAPTOP:
+ case BluetoothClass.Device.COMPUTER_SERVER:
+ case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) log("Proxy object connected");
+ mService = IBluetoothPbap.Stub.asInterface(service);
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected();
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) log("Proxy object disconnected");
+ mService = null;
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected();
+ }
+ }
+ };
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
new file mode 100644
index 0000000..45dc432
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * A listening Bluetooth socket.
+ *
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. It will return a new, connected {@link BluetoothSocket} on an
+ * accepted connection. On the client side, use the same
+ * {@link BluetoothSocket} object to both intiate the outgoing connection,
+ * and to manage the connected socket.
+ *
+ * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
+ * connection orientated, streaming transport over Bluetooth. It is also known
+ * as the Serial Port Profile (SPP).
+ *
+ * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link
+ * BluetoothSocket} ready for an outgoing connection to a remote
+ * {@link BluetoothDevice}.
+ *
+ * <p>Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening
+ * {@link BluetoothServerSocket} ready for incoming connections to the local
+ * {@link BluetoothAdapter}.
+ *
+ * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the socket.
+ *
+ * <p>All methods on a {@link BluetoothServerSocket} require
+ * {@link android.Manifest.permission#BLUETOOTH}
+ */
+public final class BluetoothServerSocket implements Closeable {
+
+ /*package*/ final BluetoothSocket mSocket;
+
+ /**
+ * Construct a socket for incoming connections.
+ * @param type type of socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param port remote port
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient priveleges
+ */
+ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+ throws IOException {
+ mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
+ }
+
+ /**
+ * Block until a connection is established.
+ * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+ * <p>Once this call returns, it can be called again to accept subsequent
+ * incoming connections.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @return a connected {@link BluetoothSocket}
+ * @throws IOException on error, for example this call was aborted, or
+ * timeout
+ */
+ public BluetoothSocket accept() throws IOException {
+ return accept(-1);
+ }
+
+ /**
+ * Block until a connection is established, with timeout.
+ * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+ * <p>Once this call returns, it can be called again to accept subsequent
+ * incoming connections.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @return a connected {@link BluetoothSocket}
+ * @throws IOException on error, for example this call was aborted, or
+ * timeout
+ */
+ public BluetoothSocket accept(int timeout) throws IOException {
+ return mSocket.accept(timeout);
+ }
+
+ /**
+ * Immediately close this socket, and release all associated resources.
+ * <p>Causes blocked calls on this socket in other threads to immediately
+ * throw an IOException.
+ */
+ public void close() throws IOException {
+ mSocket.close();
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
new file mode 100644
index 0000000..e462ea6
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * A connected or connecting Bluetooth socket.
+ *
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. It will return a new, connected {@link BluetoothSocket} on an
+ * accepted connection. On the client side, use the same
+ * {@link BluetoothSocket} object to both intiate the outgoing connection,
+ * and to manage the connected socket.
+ *
+ * <p>The most common type of Bluetooth Socket is RFCOMM. RFCOMM is a
+ * connection orientated, streaming transport over Bluetooth. It is also known
+ * as the Serial Port Profile (SPP).
+ *
+ * <p>Use {@link BluetoothDevice#createRfcommSocket} to create a new {@link
+ * BluetoothSocket} ready for an outgoing connection to a remote
+ * {@link BluetoothDevice}.
+ *
+ * <p>Use {@link BluetoothAdapter#listenUsingRfcommOn} to create a listening
+ * {@link BluetoothServerSocket} ready for incoming connections to the local
+ * {@link BluetoothAdapter}.
+ *
+ * <p>{@link BluetoothSocket} and {@link BluetoothServerSocket} are thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the socket.
+ *
+ * <p>All methods on a {@link BluetoothSocket} require
+ * {@link android.Manifest.permission#BLUETOOTH}
+ */
+public final class BluetoothSocket implements Closeable {
+ /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
+ /*package*/ static final int TYPE_RFCOMM = 1;
+ /*package*/ static final int TYPE_SCO = 2;
+ /*package*/ static final int TYPE_L2CAP = 3;
+
+ private final int mType; /* one of TYPE_RFCOMM etc */
+ private final int mPort; /* RFCOMM channel or L2CAP psm */
+ private final BluetoothDevice mDevice; /* remote device */
+ private final String mAddress; /* remote address */
+ private final boolean mAuth;
+ private final boolean mEncrypt;
+ private final BluetoothInputStream mInputStream;
+ private final BluetoothOutputStream mOutputStream;
+
+ /** prevents all native calls after destroyNative() */
+ private boolean mClosed;
+
+ /** protects mClosed */
+ private final ReentrantReadWriteLock mLock;
+
+ /** used by native code only */
+ private int mSocketData;
+
+ /**
+ * Construct a BluetoothSocket.
+ * @param type type of socket
+ * @param fd fd to use for connected socket, or -1 for a new socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param device remote device that this socket can connect to
+ * @param port remote port
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient priveleges
+ */
+ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
+ BluetoothDevice device, int port) throws IOException {
+ mType = type;
+ mAuth = auth;
+ mEncrypt = encrypt;
+ mDevice = device;
+ if (device == null) {
+ mAddress = null;
+ } else {
+ mAddress = device.getAddress();
+ }
+ mPort = port;
+ if (fd == -1) {
+ initSocketNative();
+ } else {
+ initSocketFromFdNative(fd);
+ }
+ mInputStream = new BluetoothInputStream(this);
+ mOutputStream = new BluetoothOutputStream(this);
+ mClosed = false;
+ mLock = new ReentrantReadWriteLock();
+ }
+
+ /**
+ * Construct a BluetoothSocket from address.
+ * @param type type of socket
+ * @param fd fd to use for connected socket, or -1 for a new socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param address remote device that this socket can connect to
+ * @param port remote port
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient priveleges
+ */
+ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+ int port) throws IOException {
+ this(type, fd, auth, encrypt, new BluetoothDevice(address), port);
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Attempt to connect to a remote device.
+ * <p>This method will block until a connection is made or the connection
+ * fails. If this method returns without an exception then this socket
+ * is now connected.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @throws IOException on error, for example connection failure
+ */
+ public void connect() throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ connectNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Immediately close this socket, and release all associated resources.
+ * <p>Causes blocked calls on this socket in other threads to immediately
+ * throw an IOException.
+ */
+ public void close() throws IOException {
+ // abort blocking operations on the socket
+ mLock.readLock().lock();
+ try {
+ if (mClosed) return;
+ abortNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+
+ // all native calls are guarenteed to immediately return after
+ // abortNative(), so this lock should immediatley acquire
+ mLock.writeLock().lock();
+ try {
+ mClosed = true;
+ destroyNative();
+ } finally {
+ mLock.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Get the remote device this socket is connecting, or connected, to.
+ * @return remote device
+ */
+ public BluetoothDevice getRemoteDevice() {
+ return mDevice;
+ }
+
+ /**
+ * Get the input stream associated with this socket.
+ * <p>The input stream will be returned even if the socket is not yet
+ * connected, but operations on that stream will throw IOException until
+ * the associated socket is connected.
+ * @return InputStream
+ */
+ public InputStream getInputStream() throws IOException {
+ return mInputStream;
+ }
+
+ /**
+ * Get the output stream associated with this socket.
+ * <p>The output stream will be returned even if the socket is not yet
+ * connected, but operations on that stream will throw IOException until
+ * the associated socket is connected.
+ * @return OutputStream
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return mOutputStream;
+ }
+
+ /*package*/ void bindListen() throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ bindListenNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ BluetoothSocket accept(int timeout) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return acceptNative(timeout);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int available() throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return availableNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int read(byte[] b, int offset, int length) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return readNative(b, offset, length);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int write(byte[] b, int offset, int length) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return writeNative(b, offset, length);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ private native void initSocketNative() throws IOException;
+ private native void initSocketFromFdNative(int fd) throws IOException;
+ private native void connectNative() throws IOException;
+ private native void bindListenNative() throws IOException;
+ private native BluetoothSocket acceptNative(int timeout) throws IOException;
+ private native int availableNative() throws IOException;
+ private native int readNative(byte[] b, int offset, int length) throws IOException;
+ private native int writeNative(byte[] b, int offset, int length) throws IOException;
+ private native void abortNative() throws IOException;
+ private native void destroyNative() throws IOException;
+}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
new file mode 100644
index 0000000..c15bc20
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import java.util.UUID;
+
+/**
+* Static helper methods and constants to decode the UUID of remote devices.
+* @hide
+*/
+public final class BluetoothUuid {
+
+ /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
+ * for the various services.
+ *
+ * The following 128 bit values are calculated as:
+ * uuid * 2^96 + BASE_UUID
+ */
+ public static final UUID AudioSink = UUID.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+ public static final UUID AudioSource = UUID.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ public static final UUID AdvAudioDist = UUID.fromString("0000110D-0000-1000-8000-00805F9B34FB");
+ public static final UUID HSP = UUID.fromString("00001108-0000-1000-8000-00805F9B34FB");
+ public static final UUID Handsfree = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB");
+ public static final UUID AvrcpController =
+ UUID.fromString("0000110E-0000-1000-8000-00805F9B34FB");
+ public static final UUID AvrcpTarget = UUID.fromString("0000110C-0000-1000-8000-00805F9B34FB");
+
+ public static boolean isAudioSource(UUID uuid) {
+ return uuid.equals(AudioSource);
+ }
+
+ public static boolean isAudioSink(UUID uuid) {
+ return uuid.equals(AudioSink);
+ }
+
+ public static boolean isAdvAudioDist(UUID uuid) {
+ return uuid.equals(AdvAudioDist);
+ }
+
+ public static boolean isHandsfree(UUID uuid) {
+ return uuid.equals(Handsfree);
+ }
+
+ public static boolean isHeadset(UUID uuid) {
+ return uuid.equals(HSP);
+ }
+
+ public static boolean isAvrcpController(UUID uuid) {
+ return uuid.equals(AvrcpController);
+ }
+
+ public static boolean isAvrcpTarget(UUID uuid) {
+ return uuid.equals(AvrcpTarget);
+ }
+}
diff --git a/core/java/android/bluetooth/Database.java b/core/java/android/bluetooth/Database.java
deleted file mode 100644
index fef641a..0000000
--- a/core/java/android/bluetooth/Database.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.RfcommSocket;
-
-import android.util.Log;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * A low-level API to the Service Discovery Protocol (SDP) Database.
- *
- * Allows service records to be added to the local SDP database. Once added,
- * these services will be advertised to remote devices when they make SDP
- * queries on this device.
- *
- * Currently this API is a thin wrapper to the bluez SDP Database API. See:
- * http://wiki.bluez.org/wiki/Database
- * http://wiki.bluez.org/wiki/HOWTO/ManagingServiceRecords
- * @hide
- */
-public final class Database {
- private static Database mInstance;
-
- private static final String sLogName = "android.bluetooth.Database";
-
- /**
- * Class load time initialization
- */
- static {
- classInitNative();
- }
- private native static void classInitNative();
-
- /**
- * Private to enforce singleton property
- */
- private Database() {
- initializeNativeDataNative();
- }
- private native void initializeNativeDataNative();
-
- protected void finalize() throws Throwable {
- try {
- cleanupNativeDataNative();
- } finally {
- super.finalize();
- }
- }
- private native void cleanupNativeDataNative();
-
- /**
- * Singelton accessor
- * @return The singleton instance of Database
- */
- public static synchronized Database getInstance() {
- if (mInstance == null) {
- mInstance = new Database();
- }
- return mInstance;
- }
-
- /**
- * Advertise a service with an RfcommSocket.
- *
- * This adds the service the SDP Database with the following attributes
- * set: Service Name, Protocol Descriptor List, Service Class ID List
- * TODO: Construct a byte[] record directly, rather than via XML.
- * @param socket The rfcomm socket to advertise (by channel).
- * @param serviceName A short name for this service
- * @param uuid
- * Unique identifier for this service, by which clients
- * can search for your service
- * @return Handle to the new service record
- */
- public int advertiseRfcommService(RfcommSocket socket,
- String serviceName,
- UUID uuid) throws IOException {
- String xmlRecord =
- "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
- "<record>\n" +
- " <attribute id=\"0x0001\">\n" + // ServiceClassIDList
- " <sequence>\n" +
- " <uuid value=\""
- + uuid.toString() + // UUID for this service
- "\"/>\n" +
- " </sequence>\n" +
- " </attribute>\n" +
- " <attribute id=\"0x0004\">\n" + // ProtocolDescriptorList
- " <sequence>\n" +
- " <sequence>\n" +
- " <uuid value=\"0x0100\"/>\n" + // L2CAP
- " </sequence>\n" +
- " <sequence>\n" +
- " <uuid value=\"0x0003\"/>\n" + // RFCOMM
- " <uint8 value=\"" +
- socket.getPort() + // RFCOMM port
- "\" name=\"channel\"/>\n" +
- " </sequence>\n" +
- " </sequence>\n" +
- " </attribute>\n" +
- " <attribute id=\"0x0100\">\n" + // ServiceName
- " <text value=\"" + serviceName + "\"/>\n" +
- " </attribute>\n" +
- "</record>\n";
- Log.i(sLogName, xmlRecord);
- return addServiceRecordFromXml(xmlRecord);
- }
-
-
- /**
- * Add a new service record.
- * @param record The byte[] record
- * @return A handle to the new record
- */
- public synchronized int addServiceRecord(byte[] record) throws IOException {
- int handle = addServiceRecordNative(record);
- Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle));
- return handle;
- }
- private native int addServiceRecordNative(byte[] record)
- throws IOException;
-
- /**
- * Add a new service record, using XML.
- * @param record The record as an XML string
- * @return A handle to the new record
- */
- public synchronized int addServiceRecordFromXml(String record) throws IOException {
- int handle = addServiceRecordFromXmlNative(record);
- Log.i(sLogName, "Added SDP record: " + Integer.toHexString(handle));
- return handle;
- }
- private native int addServiceRecordFromXmlNative(String record)
- throws IOException;
-
- /**
- * Update an exisiting service record.
- * @param handle Handle to exisiting record
- * @param record The updated byte[] record
- */
- public synchronized void updateServiceRecord(int handle, byte[] record) {
- try {
- updateServiceRecordNative(handle, record);
- } catch (IOException e) {
- Log.e(getClass().toString(), e.getMessage());
- }
- }
- private native void updateServiceRecordNative(int handle, byte[] record)
- throws IOException;
-
- /**
- * Update an exisiting record, using XML.
- * @param handle Handle to exisiting record
- * @param record The record as an XML string.
- */
- public synchronized void updateServiceRecordFromXml(int handle, String record) {
- try {
- updateServiceRecordFromXmlNative(handle, record);
- } catch (IOException e) {
- Log.e(getClass().toString(), e.getMessage());
- }
- }
- private native void updateServiceRecordFromXmlNative(int handle, String record)
- throws IOException;
-
- /**
- * Remove a service record.
- * It is only possible to remove service records that were added by the
- * current connection.
- * @param handle Handle to exisiting record to be removed
- */
- public synchronized void removeServiceRecord(int handle) {
- try {
- removeServiceRecordNative(handle);
- } catch (IOException e) {
- Log.e(getClass().toString(), e.getMessage());
- }
- }
- private native void removeServiceRecordNative(int handle) throws IOException;
-}
diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java
index f987ffd..29cf41d 100644
--- a/core/java/android/bluetooth/HeadsetBase.java
+++ b/core/java/android/bluetooth/HeadsetBase.java
@@ -31,7 +31,7 @@ import android.util.Log;
*
* @hide
*/
-public class HeadsetBase {
+public final class HeadsetBase {
private static final String TAG = "Bluetooth HeadsetBase";
private static final boolean DBG = false;
@@ -42,8 +42,9 @@ public class HeadsetBase {
private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */
- private final BluetoothDevice mBluetooth;
- private final String mAddress;
+ private final BluetoothAdapter mAdapter;
+ private final BluetoothDevice mRemoteDevice;
+ private final String mAddress; // for native code
private final int mRfcommChannel;
private int mNativeData;
private Thread mEventThread;
@@ -73,12 +74,13 @@ public class HeadsetBase {
private native void cleanupNativeDataNative();
- public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address,
- int rfcommChannel) {
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
+ int rfcommChannel) {
mDirection = DIRECTION_OUTGOING;
mConnectTimestamp = System.currentTimeMillis();
- mBluetooth = bluetooth;
- mAddress = address;
+ mAdapter = adapter;
+ mRemoteDevice = device;
+ mAddress = device.getAddress();
mRfcommChannel = rfcommChannel;
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
mWakeLock.setReferenceCounted(false);
@@ -88,12 +90,13 @@ public class HeadsetBase {
}
/* Create from an already exisiting rfcomm connection */
- public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd,
- int rfcommChannel, Handler handler) {
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
+ int socketFd, int rfcommChannel, Handler handler) {
mDirection = DIRECTION_INCOMING;
mConnectTimestamp = System.currentTimeMillis();
- mBluetooth = bluetooth;
- mAddress = address;
+ mAdapter = adapter;
+ mRemoteDevice = device;
+ mAddress = device.getAddress();
mRfcommChannel = rfcommChannel;
mEventThreadHandler = handler;
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
@@ -252,12 +255,8 @@ public class HeadsetBase {
return mEventThread != null;
}
- public String getAddress() {
- return mAddress;
- }
-
- public String getName() {
- return mBluetooth.getRemoteName(mAddress);
+ public BluetoothDevice getRemoteDevice() {
+ return mRemoteDevice;
}
public int getDirection() {
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
new file mode 100644
index 0000000..a11ceac
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+/**
+ * System private API for talking with the Bluetooth service.
+ *
+ * {@hide}
+ */
+interface IBluetooth
+{
+ boolean isEnabled();
+ int getBluetoothState();
+ boolean enable();
+ boolean disable(boolean persistSetting);
+
+ String getAddress();
+ String getName();
+ boolean setName(in String name);
+
+ int getScanMode();
+ boolean setScanMode(int mode);
+
+ int getDiscoverableTimeout();
+ boolean setDiscoverableTimeout(int timeout);
+
+ boolean startDiscovery();
+ boolean cancelDiscovery();
+ boolean isDiscovering();
+
+ boolean createBond(in String address);
+ boolean cancelBondProcess(in String address);
+ boolean removeBond(in String address);
+ String[] listBonds();
+ int getBondState(in String address);
+
+ String getRemoteName(in String address);
+ int getRemoteClass(in String address);
+ String[] getRemoteUuids(in String address);
+ int getRemoteServiceChannel(in String address, String uuid);
+
+ boolean setPin(in String address, in byte[] pin);
+ boolean setPasskey(in String address, int passkey);
+ boolean setPairingConfirmation(in String address, boolean confirm);
+ boolean cancelPairingUserInput(in String address);
+
+ boolean setTrust(in String address, in boolean value);
+ boolean getTrustState(in String address);
+}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 55ff27f..2df7f23 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -16,16 +16,18 @@
package android.bluetooth;
+import android.bluetooth.BluetoothDevice;
+
/**
* System private API for Bluetooth A2DP service
*
* {@hide}
*/
interface IBluetoothA2dp {
- int connectSink(in String address);
- int disconnectSink(in String address);
- List<String> listConnectedSinks();
- int getSinkState(in String address);
- int setSinkPriority(in String address, int priority);
- int getSinkPriority(in String address);
+ boolean connectSink(in BluetoothDevice device);
+ boolean disconnectSink(in BluetoothDevice device);
+ BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports
+ int getSinkState(in BluetoothDevice device);
+ boolean setSinkPriority(in BluetoothDevice device, int priority);
+ int getSinkPriority(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetoothDevice.aidl
deleted file mode 100644
index 6cd792e..0000000
--- a/core/java/android/bluetooth/IBluetoothDevice.aidl
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.bluetooth.IBluetoothDeviceCallback;
-
-/**
- * System private API for talking with the Bluetooth service.
- *
- * {@hide}
- */
-interface IBluetoothDevice
-{
- boolean isEnabled();
- int getBluetoothState();
- boolean enable();
- boolean disable(boolean persistSetting);
-
- String getAddress();
- String getName();
- boolean setName(in String name);
- String getVersion();
- String getRevision();
- String getManufacturer();
- String getCompany();
-
- int getScanMode();
- boolean setScanMode(int mode);
-
- int getDiscoverableTimeout();
- boolean setDiscoverableTimeout(int timeout);
-
- boolean startDiscovery(boolean resolveNames);
- boolean cancelDiscovery();
- boolean isDiscovering();
- boolean startPeriodicDiscovery();
- boolean stopPeriodicDiscovery();
- boolean isPeriodicDiscovery();
- String[] listRemoteDevices();
-
- String[] listAclConnections();
- boolean isAclConnected(in String address);
- boolean disconnectRemoteDeviceAcl(in String address);
-
- boolean createBond(in String address);
- boolean cancelBondProcess(in String address);
- boolean removeBond(in String address);
- String[] listBonds();
- int getBondState(in String address);
-
- String getRemoteName(in String address);
- String getRemoteVersion(in String address);
- String getRemoteRevision(in String address);
- int getRemoteClass(in String address);
- String getRemoteManufacturer(in String address);
- String getRemoteCompany(in String address);
- boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback);
- byte[] getRemoteFeatures(in String adddress);
- String lastSeen(in String address);
- String lastUsed(in String address);
-
- boolean setPin(in String address, in byte[] pin);
- boolean cancelPin(in String address);
-}
diff --git a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl
deleted file mode 100644
index d057093..0000000
--- a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-/**
- * {@hide}
- */
-oneway interface IBluetoothDeviceCallback
-{
- void onGetRemoteServiceChannelResult(in String address, int channel);
-}
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 5f42fd6..6cccd50 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.bluetooth.BluetoothDevice;
+
/**
* System private API for Bluetooth Headset service
*
@@ -23,13 +25,13 @@ package android.bluetooth;
*/
interface IBluetoothHeadset {
int getState();
- String getHeadsetAddress();
- boolean connectHeadset(in String address);
+ BluetoothDevice getCurrentHeadset();
+ boolean connectHeadset(in BluetoothDevice device);
void disconnectHeadset();
- boolean isConnected(in String address);
+ boolean isConnected(in BluetoothDevice device);
boolean startVoiceRecognition();
boolean stopVoiceRecognition();
- boolean setPriority(in String address, int priority);
- int getPriority(in String address);
+ boolean setPriority(in BluetoothDevice device, int priority);
+ int getPriority(in BluetoothDevice device);
int getBatteryUsageHint();
}
diff --git a/core/java/android/bluetooth/IBluetoothPbap.aidl b/core/java/android/bluetooth/IBluetoothPbap.aidl
new file mode 100644
index 0000000..7cc77d1
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothPbap.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * System private API for Bluetooth pbap service
+ *
+ * {@hide}
+ */
+interface IBluetoothPbap {
+ int getState();
+ BluetoothDevice getClient();
+ boolean connect(in BluetoothDevice device);
+ void disconnect();
+ boolean isConnected(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/RfcommSocket.java b/core/java/android/bluetooth/RfcommSocket.java
deleted file mode 100644
index a33263f..0000000
--- a/core/java/android/bluetooth/RfcommSocket.java
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import java.io.IOException;
-import java.io.FileOutputStream;
-import java.io.FileInputStream;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * This class implements an API to the Bluetooth RFCOMM layer. An RFCOMM socket
- * is similar to a normal socket in that it takes an address and a port number.
- * The difference is of course that the address is a Bluetooth-device address,
- * and the port number is an RFCOMM channel. The API allows for the
- * establishment of listening sockets via methods
- * {@link #bind(String, int) bind}, {@link #listen(int) listen}, and
- * {@link #accept(RfcommSocket, int) accept}, as well as for the making of
- * outgoing connections with {@link #connect(String, int) connect},
- * {@link #connectAsync(String, int) connectAsync}, and
- * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
- *
- * After constructing a socket, you need to {@link #create() create} it and then
- * {@link #destroy() destroy} it when you are done using it. Both
- * {@link #create() create} and {@link #accept(RfcommSocket, int) accept} return
- * a {@link java.io.FileDescriptor FileDescriptor} for the actual data.
- * Alternatively, you may call {@link #getInputStream() getInputStream} and
- * {@link #getOutputStream() getOutputStream} to retrieve the respective streams
- * without going through the FileDescriptor.
- *
- * @hide
- */
-public class RfcommSocket {
-
- /**
- * Used by the native implementation of the class.
- */
- private int mNativeData;
-
- /**
- * Used by the native implementation of the class.
- */
- private int mPort;
-
- /**
- * Used by the native implementation of the class.
- */
- private String mAddress;
-
- /**
- * We save the return value of {@link #create() create} and
- * {@link #accept(RfcommSocket,int) accept} in this variable, and use it to
- * retrieve the I/O streams.
- */
- private FileDescriptor mFd;
-
- /**
- * After a call to {@link #waitForAsyncConnect(int) waitForAsyncConnect},
- * if the return value is zero, then, the the remaining time left to wait is
- * written into this variable (by the native implementation). It is possible
- * that {@link #waitForAsyncConnect(int) waitForAsyncConnect} returns before
- * the user-specified timeout expires, which is why we save the remaining
- * time in this member variable for the user to retrieve by calling method
- * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}.
- */
- private int mTimeoutRemainingMs;
-
- /**
- * Set to true when an asynchronous (nonblocking) connect is in progress.
- * {@see #connectAsync(String,int)}.
- */
- private boolean mIsConnecting;
-
- /**
- * Set to true after a successful call to {@link #bind(String,int) bind} and
- * used for error checking in {@link #listen(int) listen}. Reset to false
- * on {@link #destroy() destroy}.
- */
- private boolean mIsBound = false;
-
- /**
- * Set to true after a successful call to {@link #listen(int) listen} and
- * used for error checking in {@link #accept(RfcommSocket,int) accept}.
- * Reset to false on {@link #destroy() destroy}.
- */
- private boolean mIsListening = false;
-
- /**
- * Used to store the remaining time after an accept with a non-negative
- * timeout returns unsuccessfully. It is possible that a blocking
- * {@link #accept(int) accept} may wait for less than the time specified by
- * the user, which is why we store the remainder in this member variable for
- * it to be retrieved with method
- * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}.
- */
- private int mAcceptTimeoutRemainingMs;
-
- /**
- * Maintained by {@link #getInputStream() getInputStream}.
- */
- protected FileInputStream mInputStream;
-
- /**
- * Maintained by {@link #getOutputStream() getOutputStream}.
- */
- protected FileOutputStream mOutputStream;
-
- private native void initializeNativeDataNative();
-
- /**
- * Constructor.
- */
- public RfcommSocket() {
- initializeNativeDataNative();
- }
-
- private native void cleanupNativeDataNative();
-
- /**
- * Called by the GC to clean up the native data that we set up when we
- * construct the object.
- */
- protected void finalize() throws Throwable {
- try {
- cleanupNativeDataNative();
- } finally {
- super.finalize();
- }
- }
-
- private native static void classInitNative();
-
- static {
- classInitNative();
- }
-
- /**
- * Creates a socket. You need to call this method before performing any
- * other operation on a socket.
- *
- * @return FileDescriptor for the data stream.
- * @throws IOException
- * @see #destroy()
- */
- public FileDescriptor create() throws IOException {
- if (mFd == null) {
- mFd = createNative();
- }
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- return mFd;
- }
-
- private native FileDescriptor createNative();
-
- /**
- * Destroys a socket created by {@link #create() create}. Call this
- * function when you no longer use the socket in order to release the
- * underlying OS resources.
- *
- * @see #create()
- */
- public void destroy() {
- synchronized (this) {
- destroyNative();
- mFd = null;
- mIsBound = false;
- mIsListening = false;
- }
- }
-
- private native void destroyNative();
-
- /**
- * Returns the {@link java.io.FileDescriptor FileDescriptor} of the socket.
- *
- * @return the FileDescriptor
- * @throws IOException
- * when the socket has not been {@link #create() created}.
- */
- public FileDescriptor getFileDescriptor() throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- return mFd;
- }
-
- /**
- * Retrieves the input stream from the socket. Alternatively, you can do
- * that from the FileDescriptor returned by {@link #create() create} or
- * {@link #accept(RfcommSocket, int) accept}.
- *
- * @return InputStream
- * @throws IOException
- * if you have not called {@link #create() create} on the
- * socket.
- */
- public InputStream getInputStream() throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
-
- synchronized (this) {
- if (mInputStream == null) {
- mInputStream = new FileInputStream(mFd);
- }
-
- return mInputStream;
- }
- }
-
- /**
- * Retrieves the output stream from the socket. Alternatively, you can do
- * that from the FileDescriptor returned by {@link #create() create} or
- * {@link #accept(RfcommSocket, int) accept}.
- *
- * @return OutputStream
- * @throws IOException
- * if you have not called {@link #create() create} on the
- * socket.
- */
- public OutputStream getOutputStream() throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
-
- synchronized (this) {
- if (mOutputStream == null) {
- mOutputStream = new FileOutputStream(mFd);
- }
-
- return mOutputStream;
- }
- }
-
- /**
- * Starts a blocking connect to a remote RFCOMM socket. It takes the address
- * of a device and the RFCOMM channel (port) to which to connect.
- *
- * @param address
- * is the Bluetooth address of the remote device.
- * @param port
- * is the RFCOMM channel
- * @return true on success, false on failure
- * @throws IOException
- * if {@link #create() create} has not been called.
- * @see #connectAsync(String, int)
- */
- public boolean connect(String address, int port) throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- return connectNative(address, port);
- }
- }
-
- private native boolean connectNative(String address, int port);
-
- /**
- * Starts an asynchronous (nonblocking) connect to a remote RFCOMM socket.
- * It takes the address of the device to connect to, as well as the RFCOMM
- * channel (port). On successful return (return value is true), you need to
- * call method {@link #waitForAsyncConnect(int) waitForAsyncConnect} to
- * block for up to a specified number of milliseconds while waiting for the
- * asyncronous connect to complete.
- *
- * @param address
- * of remote device
- * @param port
- * the RFCOMM channel
- * @return true when the asynchronous connect has successfully started,
- * false if there was an error.
- * @throws IOException
- * is you have not called {@link #create() create}
- * @see #waitForAsyncConnect(int)
- * @see #getRemainingAsyncConnectWaitingTimeMs()
- * @see #connect(String, int)
- */
- public boolean connectAsync(String address, int port) throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- mIsConnecting = connectAsyncNative(address, port);
- return mIsConnecting;
- }
- }
-
- private native boolean connectAsyncNative(String address, int port);
-
- /**
- * Interrupts an asynchronous connect in progress. This method does nothing
- * when there is no asynchronous connect in progress.
- *
- * @throws IOException
- * if you have not called {@link #create() create}.
- * @see #connectAsync(String, int)
- */
- public void interruptAsyncConnect() throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- if (mIsConnecting) {
- mIsConnecting = !interruptAsyncConnectNative();
- }
- }
- }
-
- private native boolean interruptAsyncConnectNative();
-
- /**
- * Tells you whether there is an asynchronous connect in progress. This
- * method returns an undefined value when there is a synchronous connect in
- * progress.
- *
- * @return true if there is an asyc connect in progress, false otherwise
- * @see #connectAsync(String, int)
- */
- public boolean isConnecting() {
- return mIsConnecting;
- }
-
- /**
- * Blocks for a specified amount of milliseconds while waiting for an
- * asynchronous connect to complete. Returns an integer value to indicate
- * one of the following: the connect succeeded, the connect is still in
- * progress, or the connect failed. It is possible for this method to block
- * for less than the time specified by the user, and still return zero
- * (i.e., async connect is still in progress.) For this reason, if the
- * return value is zero, you need to call method
- * {@link #getRemainingAsyncConnectWaitingTimeMs() getRemainingAsyncConnectWaitingTimeMs}
- * to retrieve the remaining time.
- *
- * @param timeoutMs
- * the time to block while waiting for the async connect to
- * complete.
- * @return a positive value if the connect succeeds; zero, if the connect is
- * still in progress, and a negative value if the connect failed.
- *
- * @throws IOException
- * @see #getRemainingAsyncConnectWaitingTimeMs()
- * @see #connectAsync(String, int)
- */
- public int waitForAsyncConnect(int timeoutMs) throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- int ret = waitForAsyncConnectNative(timeoutMs);
- if (ret != 0) {
- mIsConnecting = false;
- }
- return ret;
- }
- }
-
- private native int waitForAsyncConnectNative(int timeoutMs);
-
- /**
- * Returns the number of milliseconds left to wait after the last call to
- * {@link #waitForAsyncConnect(int) waitForAsyncConnect}.
- *
- * It is possible that waitForAsyncConnect() waits for less than the time
- * specified by the user, and still returns zero (i.e., async connect is
- * still in progress.) For this reason, if the return value is zero, you
- * need to call this method to retrieve the remaining time before you call
- * waitForAsyncConnect again.
- *
- * @return the remaining timeout in milliseconds.
- * @see #waitForAsyncConnect(int)
- * @see #connectAsync(String, int)
- */
- public int getRemainingAsyncConnectWaitingTimeMs() {
- return mTimeoutRemainingMs;
- }
-
- /**
- * Shuts down both directions on a socket.
- *
- * @return true on success, false on failure; if the return value is false,
- * the socket might be left in a patially shut-down state (i.e. one
- * direction is shut down, but the other is still open.) In this
- * case, you should {@link #destroy() destroy} and then
- * {@link #create() create} the socket again.
- * @throws IOException
- * is you have not caled {@link #create() create}.
- * @see #shutdownInput()
- * @see #shutdownOutput()
- */
- public boolean shutdown() throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- if (shutdownNative(true)) {
- return shutdownNative(false);
- }
-
- return false;
- }
- }
-
- /**
- * Shuts down the input stream of the socket, but leaves the output stream
- * in its current state.
- *
- * @return true on success, false on failure
- * @throws IOException
- * is you have not called {@link #create() create}
- * @see #shutdown()
- * @see #shutdownOutput()
- */
- public boolean shutdownInput() throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- return shutdownNative(true);
- }
- }
-
- /**
- * Shut down the output stream of the socket, but leaves the input stream in
- * its current state.
- *
- * @return true on success, false on failure
- * @throws IOException
- * is you have not called {@link #create() create}
- * @see #shutdown()
- * @see #shutdownInput()
- */
- public boolean shutdownOutput() throws IOException {
- synchronized (this) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- return shutdownNative(false);
- }
- }
-
- private native boolean shutdownNative(boolean shutdownInput);
-
- /**
- * Tells you whether a socket is connected to another socket. This could be
- * for input or output or both.
- *
- * @return true if connected, false otherwise.
- * @see #isInputConnected()
- * @see #isOutputConnected()
- */
- public boolean isConnected() {
- return isConnectedNative() > 0;
- }
-
- /**
- * Determines whether input is connected (i.e., whether you can receive data
- * on this socket.)
- *
- * @return true if input is connected, false otherwise.
- * @see #isConnected()
- * @see #isOutputConnected()
- */
- public boolean isInputConnected() {
- return (isConnectedNative() & 1) != 0;
- }
-
- /**
- * Determines whether output is connected (i.e., whether you can send data
- * on this socket.)
- *
- * @return true if output is connected, false otherwise.
- * @see #isConnected()
- * @see #isInputConnected()
- */
- public boolean isOutputConnected() {
- return (isConnectedNative() & 2) != 0;
- }
-
- private native int isConnectedNative();
-
- /**
- * Binds a listening socket to the local device, or a non-listening socket
- * to a remote device. The port is automatically selected as the first
- * available port in the range 12 to 30.
- *
- * NOTE: Currently we ignore the device parameter and always bind the socket
- * to the local device, assuming that it is a listening socket.
- *
- * TODO: Use bind(0) in native code to have the kernel select an unused
- * port.
- *
- * @param device
- * Bluetooth address of device to bind to (currently ignored).
- * @return true on success, false on failure
- * @throws IOException
- * if you have not called {@link #create() create}
- * @see #listen(int)
- * @see #accept(RfcommSocket,int)
- */
- public boolean bind(String device) throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- for (int port = 12; port <= 30; port++) {
- if (bindNative(device, port)) {
- mIsBound = true;
- return true;
- }
- }
- mIsBound = false;
- return false;
- }
-
- /**
- * Binds a listening socket to the local device, or a non-listening socket
- * to a remote device.
- *
- * NOTE: Currently we ignore the device parameter and always bind the socket
- * to the local device, assuming that it is a listening socket.
- *
- * @param device
- * Bluetooth address of device to bind to (currently ignored).
- * @param port
- * RFCOMM channel to bind socket to.
- * @return true on success, false on failure
- * @throws IOException
- * if you have not called {@link #create() create}
- * @see #listen(int)
- * @see #accept(RfcommSocket,int)
- */
- public boolean bind(String device, int port) throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- mIsBound = bindNative(device, port);
- return mIsBound;
- }
-
- private native boolean bindNative(String device, int port);
-
- /**
- * Starts listening for incoming connections on this socket, after it has
- * been bound to an address and RFCOMM channel with
- * {@link #bind(String,int) bind}.
- *
- * @param backlog
- * the number of pending incoming connections to queue for
- * {@link #accept(RfcommSocket, int) accept}.
- * @return true on success, false on failure
- * @throws IOException
- * if you have not called {@link #create() create} or if the
- * socket has not been bound to a device and RFCOMM channel.
- */
- public boolean listen(int backlog) throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- if (!mIsBound) {
- throw new IOException("socket not bound");
- }
- mIsListening = listenNative(backlog);
- return mIsListening;
- }
-
- private native boolean listenNative(int backlog);
-
- /**
- * Accepts incoming-connection requests for a listening socket bound to an
- * RFCOMM channel. The user may provide a time to wait for an incoming
- * connection.
- *
- * Note that this method may return null (i.e., no incoming connection)
- * before the user-specified timeout expires. For this reason, on a null
- * return value, you need to call
- * {@link #getRemainingAcceptWaitingTimeMs() getRemainingAcceptWaitingTimeMs}
- * in order to see how much time is left to wait, before you call this
- * method again.
- *
- * @param newSock
- * is set to the new socket that is created as a result of a
- * successful accept.
- * @param timeoutMs
- * time (in milliseconds) to block while waiting to an
- * incoming-connection request. A negative value is an infinite
- * wait.
- * @return FileDescriptor of newSock on success, null on failure. Failure
- * occurs if the timeout expires without a successful connect.
- * @throws IOException
- * if the socket has not been {@link #create() create}ed, is
- * not bound, or is not a listening socket.
- * @see #bind(String, int)
- * @see #listen(int)
- * @see #getRemainingAcceptWaitingTimeMs()
- */
- public FileDescriptor accept(RfcommSocket newSock, int timeoutMs)
- throws IOException {
- synchronized (newSock) {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- if (mIsListening == false) {
- throw new IOException("not listening on socket");
- }
- newSock.mFd = acceptNative(newSock, timeoutMs);
- return newSock.mFd;
- }
- }
-
- /**
- * Returns the number of milliseconds left to wait after the last call to
- * {@link #accept(RfcommSocket, int) accept}.
- *
- * Since accept() may return null (i.e., no incoming connection) before the
- * user-specified timeout expires, you need to call this method in order to
- * see how much time is left to wait, and wait for that amount of time
- * before you call accept again.
- *
- * @return the remaining time, in milliseconds.
- */
- public int getRemainingAcceptWaitingTimeMs() {
- return mAcceptTimeoutRemainingMs;
- }
-
- private native FileDescriptor acceptNative(RfcommSocket newSock,
- int timeoutMs);
-
- /**
- * Get the port (rfcomm channel) associated with this socket.
- *
- * This is only valid if the port has been set via a successful call to
- * {@link #bind(String, int)}, {@link #connect(String, int)}
- * or {@link #connectAsync(String, int)}. This can be checked
- * with {@link #isListening()} and {@link #isConnected()}.
- * @return Port (rfcomm channel)
- */
- public int getPort() throws IOException {
- if (mFd == null) {
- throw new IOException("socket not created");
- }
- if (!mIsListening && !isConnected()) {
- throw new IOException("not listening or connected on socket");
- }
- return mPort;
- }
-
- /**
- * Return true if this socket is listening ({@link #listen(int)}
- * has been called successfully).
- */
- public boolean isListening() {
- return mIsListening;
- }
-}
diff --git a/core/java/android/content/AbstractCursorEntityIterator.java b/core/java/android/content/AbstractCursorEntityIterator.java
new file mode 100644
index 0000000..c2b13a4
--- /dev/null
+++ b/core/java/android/content/AbstractCursorEntityIterator.java
@@ -0,0 +1,120 @@
+package android.content;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.RemoteException;
+
+/**
+ * An abstract class that makes it easy to implement an EntityIterator over a cursor.
+ * The user must implement {@link #newEntityFromCursorLocked}, which runs inside of a
+ * database transaction.
+ */
+public abstract class AbstractCursorEntityIterator implements EntityIterator {
+ private final Cursor mEntityCursor;
+ private final SQLiteDatabase mDb;
+ private volatile Entity mNextEntity;
+ private volatile boolean mIsClosed;
+
+ public AbstractCursorEntityIterator(SQLiteDatabase db, Cursor entityCursor) {
+ mEntityCursor = entityCursor;
+ mDb = db;
+ mNextEntity = null;
+ mIsClosed = false;
+ }
+
+ /**
+ * If there are entries left in the cursor then advance the cursor and use the new row to
+ * populate mNextEntity. If the cursor is at the end or if advancing it causes the cursor
+ * to become at the end then set mEntityCursor to null. If newEntityFromCursor returns null
+ * then continue advancing until it either returns a non-null Entity or the cursor reaches
+ * the end.
+ */
+ private void fillEntityIfAvailable() {
+ while (mNextEntity == null) {
+ if (!mEntityCursor.moveToNext()) {
+ // the cursor is at then end, bail out
+ return;
+ }
+ // This may return null if newEntityFromCursor is not able to create an entity
+ // from the current cursor position. In that case this method will loop and try
+ // the next cursor position
+ mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+ }
+ mDb.beginTransaction();
+ try {
+ int position = mEntityCursor.getPosition();
+ mNextEntity = newEntityFromCursorLocked(mEntityCursor);
+ int newPosition = mEntityCursor.getPosition();
+ if (newPosition != position) {
+ throw new IllegalStateException("the cursor position changed during the call to"
+ + "newEntityFromCursorLocked, from " + position + " to " + newPosition);
+ }
+ } finally {
+ mDb.endTransaction();
+ }
+ }
+
+ /**
+ * Checks if there are more Entities accessible via this iterator. This may not be called
+ * if the iterator is already closed.
+ * @return true if the call to next() will return an Entity.
+ */
+ public boolean hasNext() {
+ if (mIsClosed) {
+ throw new IllegalStateException("calling hasNext() when the iterator is closed");
+ }
+ fillEntityIfAvailable();
+ return mNextEntity != null;
+ }
+
+ /**
+ * Returns the next Entity that is accessible via this iterator. This may not be called
+ * if the iterator is already closed.
+ * @return the next Entity that is accessible via this iterator
+ */
+ public Entity next() {
+ if (mIsClosed) {
+ throw new IllegalStateException("calling next() when the iterator is closed");
+ }
+ if (!hasNext()) {
+ throw new IllegalStateException("you may only call next() if hasNext() is true");
+ }
+
+ try {
+ return mNextEntity;
+ } finally {
+ mNextEntity = null;
+ }
+ }
+
+ public void reset() throws RemoteException {
+ if (mIsClosed) {
+ throw new IllegalStateException("calling reset() when the iterator is closed");
+ }
+ mEntityCursor.moveToPosition(-1);
+ mNextEntity = null;
+ }
+
+ /**
+ * Closes this iterator making it invalid. If is invalid for the user to call any public
+ * method on the iterator once it has been closed.
+ */
+ public void close() {
+ if (mIsClosed) {
+ throw new IllegalStateException("closing when already closed");
+ }
+ mIsClosed = true;
+ mEntityCursor.close();
+ }
+
+ /**
+ * Returns a new Entity from the current cursor position. This is called from within a
+ * database transaction. If a new entity cannot be created from this cursor position (e.g.
+ * if the row that is referred to no longer exists) then this may return null. The cursor
+ * is guaranteed to be pointing to a valid row when this call is made. The implementation
+ * of newEntityFromCursorLocked is not allowed to change the position of the cursor.
+ * @param cursor from where to read the data for the Entity
+ * @return an Entity that corresponds to the current cursor position or null
+ */
+ public abstract Entity newEntityFromCursorLocked(Cursor cursor);
+}
diff --git a/core/java/android/content/AbstractSyncableContentProvider.java b/core/java/android/content/AbstractSyncableContentProvider.java
index 249d9ba..808f30c 100644
--- a/core/java/android/content/AbstractSyncableContentProvider.java
+++ b/core/java/android/content/AbstractSyncableContentProvider.java
@@ -4,8 +4,9 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.Cursor;
import android.net.Uri;
-import android.accounts.AccountMonitor;
-import android.accounts.AccountMonitorListener;
+import android.accounts.OnAccountsUpdatedListener;
+import android.accounts.Account;
+import android.accounts.AccountManager;
import android.provider.SyncConstValue;
import android.util.Config;
import android.util.Log;
@@ -14,9 +15,12 @@ import android.text.TextUtils;
import java.util.Collections;
import java.util.Map;
-import java.util.HashMap;
import java.util.Vector;
import java.util.ArrayList;
+import java.util.Set;
+import java.util.HashSet;
+
+import com.google.android.collect.Maps;
/**
* A specialization of the ContentProvider that centralizes functionality
@@ -32,26 +36,30 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
private final String mDatabaseName;
private final int mDatabaseVersion;
private final Uri mContentUri;
- private AccountMonitor mAccountMonitor;
/** the account set in the last call to onSyncStart() */
- private String mSyncingAccount;
+ private Account mSyncingAccount;
private SyncStateContentProviderHelper mSyncState = null;
- private static final String[] sAccountProjection = new String[] {SyncConstValue._SYNC_ACCOUNT};
+ private static final String[] sAccountProjection =
+ new String[] {SyncConstValue._SYNC_ACCOUNT, SyncConstValue._SYNC_ACCOUNT_TYPE};
private boolean mIsTemporary;
private AbstractTableMerger mCurrentMerger = null;
private boolean mIsMergeCancelled = false;
- private static final String SYNC_ACCOUNT_WHERE_CLAUSE = SyncConstValue._SYNC_ACCOUNT + "=?";
+ private static final String SYNC_ACCOUNT_WHERE_CLAUSE =
+ SyncConstValue._SYNC_ACCOUNT + "=? AND " + SyncConstValue._SYNC_ACCOUNT_TYPE + "=?";
protected boolean isTemporary() {
return mIsTemporary;
}
+ private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>();
+ private final ThreadLocal<Set<Uri>> mPendingBatchNotifications = new ThreadLocal<Set<Uri>>();
+
/**
* Indicates whether or not this ContentProvider contains a full
* set of data or just diffs. This knowledge comes in handy when
@@ -133,7 +141,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (!upgradeDatabase(db, oldVersion, newVersion)) {
mSyncState.discardSyncData(db, null /* all accounts */);
- getContext().getContentResolver().startSync(mContentUri, new Bundle());
+ ContentResolver.requestSync(null /* all accounts */,
+ mContentUri.getAuthority(), new Bundle());
}
}
@@ -150,23 +159,36 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
mOpenHelper = new AbstractSyncableContentProvider.DatabaseHelper(getContext(),
mDatabaseName);
mSyncState = new SyncStateContentProviderHelper(mOpenHelper);
-
- AccountMonitorListener listener = new AccountMonitorListener() {
- public void onAccountsUpdated(String[] accounts) {
- // Some providers override onAccountsChanged(); give them a database to work with.
- mDb = mOpenHelper.getWritableDatabase();
- onAccountsChanged(accounts);
- TempProviderSyncAdapter syncAdapter = (TempProviderSyncAdapter)getSyncAdapter();
- if (syncAdapter != null) {
- syncAdapter.onAccountsChanged(accounts);
- }
- }
- };
- mAccountMonitor = new AccountMonitor(getContext(), listener);
+ AccountManager.get(getContext()).addOnAccountsUpdatedListener(
+ new OnAccountsUpdatedListener() {
+ public void onAccountsUpdated(Account[] accounts) {
+ // Some providers override onAccountsChanged(); give them a database to
+ // work with.
+ mDb = mOpenHelper.getWritableDatabase();
+ // Only call onAccountsChanged on GAIA accounts; otherwise, the contacts and
+ // calendar providers will choke as they try to sync unknown accounts with
+ // AbstractGDataSyncAdapter, which will put acore into a crash loop
+ ArrayList<Account> gaiaAccounts = new ArrayList<Account>();
+ for (Account acct: accounts) {
+ if (acct.type.equals("com.google.GAIA")) {
+ gaiaAccounts.add(acct);
+ }
+ }
+ accounts = new Account[gaiaAccounts.size()];
+ int i = 0;
+ for (Account acct: gaiaAccounts) {
+ accounts[i++] = acct;
+ }
+ onAccountsChanged(accounts);
+ TempProviderSyncAdapter syncAdapter = getTempProviderSyncAdapter();
+ if (syncAdapter != null) {
+ syncAdapter.onAccountsChanged(accounts);
+ }
+ }
+ }, null /* handler */, true /* updateImmediately */);
return true;
}
-
/**
* Get a non-persistent instance of this content provider.
* You must call {@link #close} on the returned
@@ -236,147 +258,117 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
return Collections.emptyList();
}
- /**
- * <p>
- * Call mOpenHelper.getWritableDatabase() and mDb.beginTransaction().
- * {@link #endTransaction} MUST be called after calling this method.
- * Those methods should be used like this:
- * </p>
- *
- * <pre class="prettyprint">
- * boolean successful = false;
- * beginTransaction();
- * try {
- * // Do something related to mDb
- * successful = true;
- * return ret;
- * } finally {
- * endTransaction(successful);
- * }
- * </pre>
- *
- * @hide This method is dangerous from the view of database manipulation, though using
- * this makes batch insertion/update/delete much faster.
- */
- public final void beginTransaction() {
+ @Override
+ public final int update(final Uri url, final ContentValues values,
+ final String selection, final String[] selectionArgs) {
mDb = mOpenHelper.getWritableDatabase();
- mDb.beginTransaction();
- }
-
- /**
- * <p>
- * Call mDb.endTransaction(). If successful is true, try to call
- * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
- * This method MUST be used with {@link #beginTransaction()}.
- * </p>
- *
- * @hide This method is dangerous from the view of database manipulation, though using
- * this makes batch insertion/update/delete much faster.
- */
- public final void endTransaction(boolean successful) {
+ final boolean notApplyingBatch = !applyingBatch();
+ if (notApplyingBatch) {
+ mDb.beginTransaction();
+ }
try {
- if (successful) {
- // setTransactionSuccessful() must be called just once during opening the
- // transaction.
- mDb.setTransactionSuccessful();
+ if (isTemporary() && mSyncState.matches(url)) {
+ int numRows = mSyncState.asContentProvider().update(
+ url, values, selection, selectionArgs);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ return numRows;
}
- } finally {
- mDb.endTransaction();
- }
- }
- @Override
- public final int update(final Uri uri, final ContentValues values,
- final String selection, final String[] selectionArgs) {
- boolean successful = false;
- beginTransaction();
- try {
- int ret = nonTransactionalUpdate(uri, values, selection, selectionArgs);
- successful = true;
- return ret;
+ int result = updateInternal(url, values, selection, selectionArgs);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ if (!isTemporary() && result > 0) {
+ if (notApplyingBatch) {
+ getContext().getContentResolver().notifyChange(url, null /* observer */,
+ changeRequiresLocalSync(url));
+ } else {
+ mPendingBatchNotifications.get().add(url);
+ }
+ }
+ return result;
} finally {
- endTransaction(successful);
- }
- }
-
- /**
- * @hide
- */
- public final int nonTransactionalUpdate(final Uri uri, final ContentValues values,
- final String selection, final String[] selectionArgs) {
- if (isTemporary() && mSyncState.matches(uri)) {
- int numRows = mSyncState.asContentProvider().update(
- uri, values, selection, selectionArgs);
- return numRows;
- }
-
- int result = updateInternal(uri, values, selection, selectionArgs);
- if (!isTemporary() && result > 0) {
- getContext().getContentResolver().notifyChange(uri, null /* observer */,
- changeRequiresLocalSync(uri));
+ if (notApplyingBatch) {
+ mDb.endTransaction();
+ }
}
-
- return result;
}
@Override
- public final int delete(final Uri uri, final String selection,
+ public final int delete(final Uri url, final String selection,
final String[] selectionArgs) {
- boolean successful = false;
- beginTransaction();
+ mDb = mOpenHelper.getWritableDatabase();
+ final boolean notApplyingBatch = !applyingBatch();
+ if (notApplyingBatch) {
+ mDb.beginTransaction();
+ }
try {
- int ret = nonTransactionalDelete(uri, selection, selectionArgs);
- successful = true;
- return ret;
+ if (isTemporary() && mSyncState.matches(url)) {
+ int numRows = mSyncState.asContentProvider().delete(url, selection, selectionArgs);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ return numRows;
+ }
+ int result = deleteInternal(url, selection, selectionArgs);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ if (!isTemporary() && result > 0) {
+ if (notApplyingBatch) {
+ getContext().getContentResolver().notifyChange(url, null /* observer */,
+ changeRequiresLocalSync(url));
+ } else {
+ mPendingBatchNotifications.get().add(url);
+ }
+ }
+ return result;
} finally {
- endTransaction(successful);
+ if (notApplyingBatch) {
+ mDb.endTransaction();
+ }
}
}
- /**
- * @hide
- */
- public final int nonTransactionalDelete(final Uri uri, final String selection,
- final String[] selectionArgs) {
- if (isTemporary() && mSyncState.matches(uri)) {
- int numRows = mSyncState.asContentProvider().delete(uri, selection, selectionArgs);
- return numRows;
- }
- int result = deleteInternal(uri, selection, selectionArgs);
- if (!isTemporary() && result > 0) {
- getContext().getContentResolver().notifyChange(uri, null /* observer */,
- changeRequiresLocalSync(uri));
- }
- return result;
+ private boolean applyingBatch() {
+ return mApplyingBatch.get() != null && mApplyingBatch.get();
}
@Override
- public final Uri insert(final Uri uri, final ContentValues values) {
- boolean successful = false;
- beginTransaction();
- try {
- Uri ret = nonTransactionalInsert(uri, values);
- successful = true;
- return ret;
- } finally {
- endTransaction(successful);
+ public final Uri insert(final Uri url, final ContentValues values) {
+ mDb = mOpenHelper.getWritableDatabase();
+ final boolean notApplyingBatch = !applyingBatch();
+ if (notApplyingBatch) {
+ mDb.beginTransaction();
}
- }
-
- /**
- * @hide
- */
- public final Uri nonTransactionalInsert(final Uri uri, final ContentValues values) {
- if (isTemporary() && mSyncState.matches(uri)) {
- Uri result = mSyncState.asContentProvider().insert(uri, values);
+ try {
+ if (isTemporary() && mSyncState.matches(url)) {
+ Uri result = mSyncState.asContentProvider().insert(url, values);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ return result;
+ }
+ Uri result = insertInternal(url, values);
+ if (notApplyingBatch) {
+ mDb.setTransactionSuccessful();
+ }
+ if (!isTemporary() && result != null) {
+ if (notApplyingBatch) {
+ getContext().getContentResolver().notifyChange(url, null /* observer */,
+ changeRequiresLocalSync(url));
+ } else {
+ mPendingBatchNotifications.get().add(url);
+ }
+ }
return result;
+ } finally {
+ if (notApplyingBatch) {
+ mDb.endTransaction();
+ }
}
- Uri result = insertInternal(uri, values);
- if (!isTemporary() && result != null) {
- getContext().getContentResolver().notifyChange(uri, null /* observer */,
- changeRequiresLocalSync(uri));
- }
- return result;
}
@Override
@@ -411,6 +403,92 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
}
/**
+ * <p>
+ * Start batch transaction. {@link #endTransaction} MUST be called after
+ * calling this method. Those methods should be used like this:
+ * </p>
+ *
+ * <pre class="prettyprint">
+ * boolean successful = false;
+ * beginBatch()
+ * try {
+ * // Do something related to mDb
+ * successful = true;
+ * return ret;
+ * } finally {
+ * endBatch(successful);
+ * }
+ * </pre>
+ *
+ * @hide This method should be used only when {@link ContentProvider#applyBatch} is not enough and must be
+ * used with {@link #endBatch}.
+ * e.g. If returned value has to be used during one transaction, this method might be useful.
+ */
+ public final void beginBatch() {
+ // initialize if this is the first time this thread has applied a batch
+ if (mApplyingBatch.get() == null) {
+ mApplyingBatch.set(false);
+ mPendingBatchNotifications.set(new HashSet<Uri>());
+ }
+
+ if (applyingBatch()) {
+ throw new IllegalStateException(
+ "applyBatch is not reentrant but mApplyingBatch is already set");
+ }
+ SQLiteDatabase db = getDatabase();
+ db.beginTransaction();
+ boolean successful = false;
+ try {
+ mApplyingBatch.set(true);
+ successful = true;
+ } finally {
+ if (!successful) {
+ // Something unexpected happened. We must call endTransaction() at least.
+ db.endTransaction();
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Finish batch transaction. If "successful" is true, try to call
+ * mDb.setTransactionSuccessful() before calling mDb.endTransaction().
+ * This method MUST be used with {@link #beginBatch()}.
+ * </p>
+ *
+ * @hide This method must be used with {@link #beginTransaction}
+ */
+ public final void endBatch(boolean successful) {
+ try {
+ if (successful) {
+ // setTransactionSuccessful() must be called just once during opening the
+ // transaction.
+ mDb.setTransactionSuccessful();
+ }
+ } finally {
+ mApplyingBatch.set(false);
+ getDatabase().endTransaction();
+ for (Uri url : mPendingBatchNotifications.get()) {
+ getContext().getContentResolver().notifyChange(url, null /* observer */,
+ changeRequiresLocalSync(url));
+ }
+ }
+ }
+
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ boolean successful = false;
+ beginBatch();
+ try {
+ ContentProviderResult[] results = super.applyBatch(operations);
+ successful = true;
+ return results;
+ } finally {
+ endBatch(successful);
+ }
+ }
+
+ /**
* Check if changes to this URI can be syncable changes.
* @param uri the URI of the resource that was changed
* @return true if changes to this URI can be syncable changes, false otherwise
@@ -437,8 +515,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
* @param context the sync context for the operation
* @param account
*/
- public void onSyncStart(SyncContext context, String account) {
- if (TextUtils.isEmpty(account)) {
+ public void onSyncStart(SyncContext context, Account account) {
+ if (account == null) {
throw new IllegalArgumentException("you passed in an empty account");
}
mSyncingAccount = account;
@@ -457,7 +535,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
* The account of the most recent call to onSyncStart()
* @return the account
*/
- public String getSyncingAccount() {
+ public Account getSyncingAccount() {
return mSyncingAccount;
}
@@ -568,12 +646,11 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
* Make sure that there are no entries for accounts that no longer exist
* @param accountsArray the array of currently-existing accounts
*/
- protected void onAccountsChanged(String[] accountsArray) {
- Map<String, Boolean> accounts = new HashMap<String, Boolean>();
- for (String account : accountsArray) {
+ protected void onAccountsChanged(Account[] accountsArray) {
+ Map<Account, Boolean> accounts = Maps.newHashMap();
+ for (Account account : accountsArray) {
accounts.put(account, false);
}
- accounts.put(SyncConstValue.NON_SYNCABLE_ACCOUNT, false);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Map<String, String> tableMap = db.getSyncedTables();
@@ -585,8 +662,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
try {
mSyncState.onAccountsChanged(accountsArray);
for (String table : tables) {
- deleteRowsForRemovedAccounts(accounts, table,
- SyncConstValue._SYNC_ACCOUNT);
+ deleteRowsForRemovedAccounts(accounts, table);
}
db.setTransactionSuccessful();
} finally {
@@ -601,23 +677,23 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
*
* @param accounts a map of existing accounts
* @param table the table to delete from
- * @param accountColumnName the name of the column that is expected
- * to hold the account.
*/
- protected void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts,
- String table, String accountColumnName) {
+ protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor c = db.query(table, sAccountProjection, null, null,
- accountColumnName, null, null);
+ "_sync_account, _sync_account_type", null, null);
try {
while (c.moveToNext()) {
- String account = c.getString(0);
- if (TextUtils.isEmpty(account)) {
+ String accountName = c.getString(0);
+ String accountType = c.getString(1);
+ if (TextUtils.isEmpty(accountName)) {
continue;
}
+ Account account = new Account(accountName, accountType);
if (!accounts.containsKey(account)) {
int numDeleted;
- numDeleted = db.delete(table, accountColumnName + "=?", new String[]{account});
+ numDeleted = db.delete(table, "_sync_account=? AND _sync_account_type=?",
+ new String[]{account.name, account.type});
if (Config.LOGV) {
Log.v(TAG, "deleted " + numDeleted
+ " records from table " + table
@@ -634,7 +710,7 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
* Called when the sync system determines that this provider should no longer
* contain records for the specified account.
*/
- public void wipeAccount(String account) {
+ public void wipeAccount(Account account) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Map<String, String> tableMap = db.getSyncedTables();
ArrayList<String> tables = new ArrayList<String>();
@@ -649,7 +725,8 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
// remove the data in the synced tables
for (String table : tables) {
- db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE, new String[]{account});
+ db.delete(table, SYNC_ACCOUNT_WHERE_CLAUSE,
+ new String[]{account.name, account.type});
}
db.setTransactionSuccessful();
} finally {
@@ -660,14 +737,14 @@ public abstract class AbstractSyncableContentProvider extends SyncableContentPro
/**
* Retrieves the SyncData bytes for the given account. The byte array returned may be null.
*/
- public byte[] readSyncDataBytes(String account) {
+ public byte[] readSyncDataBytes(Account account) {
return mSyncState.readSyncDataBytes(mOpenHelper.getReadableDatabase(), account);
}
/**
* Sets the SyncData bytes for the given account. The byte array may be null.
*/
- public void writeSyncDataBytes(String account, byte[] data) {
+ public void writeSyncDataBytes(Account account, byte[] data) {
mSyncState.writeSyncDataBytes(mOpenHelper.getWritableDatabase(), account, data);
}
}
diff --git a/core/java/android/content/AbstractTableMerger.java b/core/java/android/content/AbstractTableMerger.java
index 9f609a3..9545fd7 100644
--- a/core/java/android/content/AbstractTableMerger.java
+++ b/core/java/android/content/AbstractTableMerger.java
@@ -25,6 +25,7 @@ import android.provider.BaseColumns;
import static android.provider.SyncConstValue.*;
import android.text.TextUtils;
import android.util.Log;
+import android.accounts.Account;
/**
* @hide
@@ -55,15 +56,17 @@ public abstract class AbstractTableMerger
private volatile boolean mIsMergeCancelled;
- private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and " + _SYNC_ACCOUNT + "=?";
+ private static final String SELECT_MARKED = _SYNC_MARK + "> 0 and "
+ + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
private static final String SELECT_BY_SYNC_ID_AND_ACCOUNT =
- _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=?";
+ _SYNC_ID +"=? and " + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?";
private static final String SELECT_BY_ID = BaseColumns._ID +"=?";
private static final String SELECT_UNSYNCED =
- "(" + _SYNC_ACCOUNT + " IS NULL OR " + _SYNC_ACCOUNT + "=?) AND "
- + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 AND "
+ "(" + _SYNC_ACCOUNT + " IS NULL OR ("
+ + _SYNC_ACCOUNT + "=? and " + _SYNC_ACCOUNT_TYPE + "=?)) and "
+ + "(" + _SYNC_ID + " IS NULL OR (" + _SYNC_DIRTY + " > 0 and "
+ _SYNC_VERSION + " IS NOT NULL))";
public AbstractTableMerger(SQLiteDatabase database,
@@ -134,7 +137,7 @@ public abstract class AbstractTableMerger
* construct a temporary instance to hold them.
*/
public void merge(final SyncContext context,
- final String account,
+ final Account account,
final SyncableContentProvider serverDiffs,
TempProviderSyncResult result,
SyncResult syncResult, SyncableContentProvider temporaryInstanceFactory) {
@@ -157,7 +160,7 @@ public abstract class AbstractTableMerger
* @hide this is public for testing purposes only
*/
public void mergeServerDiffs(SyncContext context,
- String account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
+ Account account, SyncableContentProvider serverDiffs, SyncResult syncResult) {
boolean diffsArePartial = serverDiffs.getContainsDiffs();
// mark the current rows so that we can distinguish these from new
// inserts that occur during the merge
@@ -166,342 +169,340 @@ public abstract class AbstractTableMerger
mDb.update(mDeletedTable, mSyncMarkValues, null, null);
}
- // load the local database entries, so we can merge them with the server
- final String[] accountSelectionArgs = new String[]{account};
- Cursor localCursor = mDb.query(mTable, syncDirtyProjection,
- SELECT_MARKED, accountSelectionArgs, null, null,
- mTable + "." + _SYNC_ID);
- Cursor deletedCursor;
- if (mDeletedTable != null) {
- deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
+ Cursor localCursor = null;
+ Cursor deletedCursor = null;
+ Cursor diffsCursor = null;
+ try {
+ // load the local database entries, so we can merge them with the server
+ final String[] accountSelectionArgs = new String[]{account.name, account.type};
+ localCursor = mDb.query(mTable, syncDirtyProjection,
SELECT_MARKED, accountSelectionArgs, null, null,
- mDeletedTable + "." + _SYNC_ID);
- } else {
- deletedCursor =
- mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
- }
-
- // Apply updates and insertions from the server
- Cursor diffsCursor = serverDiffs.query(mTableURL,
- null, null, null, mTable + "." + _SYNC_ID);
- int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
- int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
- int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
- int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
- int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
-
- String lastSyncId = null;
- int diffsCount = 0;
- int localCount = 0;
- localCursor.moveToFirst();
- deletedCursor.moveToFirst();
- while (diffsCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- localCursor.close();
- deletedCursor.close();
- diffsCursor.close();
- return;
- }
- mDb.yieldIfContended();
- String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
- String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
- long localRowId = 0;
- String localSyncVersion = null;
-
- diffsCount++;
- context.setStatusText("Processing " + diffsCount + "/"
- + diffsCursor.getCount());
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
- diffsCount + ", " + serverSyncId);
-
- if (TRACE) {
- if (diffsCount == 10) {
- Debug.startMethodTracing("atmtrace");
- }
- if (diffsCount == 20) {
- Debug.stopMethodTracing();
- }
+ mTable + "." + _SYNC_ID);
+ if (mDeletedTable != null) {
+ deletedCursor = mDb.query(mDeletedTable, syncIdAndVersionProjection,
+ SELECT_MARKED, accountSelectionArgs, null, null,
+ mDeletedTable + "." + _SYNC_ID);
+ } else {
+ deletedCursor =
+ mDb.rawQuery("select 'a' as _sync_id, 'b' as _sync_version limit 0", null);
}
- boolean conflict = false;
- boolean update = false;
- boolean insert = false;
+ // Apply updates and insertions from the server
+ diffsCursor = serverDiffs.query(mTableURL,
+ null, null, null, mTable + "." + _SYNC_ID);
+ int deletedSyncIDColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_ID);
+ int deletedSyncVersionColumn = deletedCursor.getColumnIndexOrThrow(_SYNC_VERSION);
+ int serverSyncIDColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
+ int serverSyncVersionColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_VERSION);
+ int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "found event with serverSyncID " + serverSyncId);
- }
- if (TextUtils.isEmpty(serverSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.e(TAG, "server entry doesn't have a serverSyncID");
+ String lastSyncId = null;
+ int diffsCount = 0;
+ int localCount = 0;
+ localCursor.moveToFirst();
+ deletedCursor.moveToFirst();
+ while (diffsCursor.moveToNext()) {
+ if (mIsMergeCancelled) {
+ return;
}
- continue;
- }
-
- // It is possible that the sync adapter wrote the same record multiple times,
- // e.g. if the same record came via multiple feeds. If this happens just ignore
- // the duplicate records.
- if (serverSyncId.equals(lastSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
+ mDb.yieldIfContended();
+ String serverSyncId = diffsCursor.getString(serverSyncIDColumn);
+ String serverSyncVersion = diffsCursor.getString(serverSyncVersionColumn);
+ long localRowId = 0;
+ String localSyncVersion = null;
+
+ diffsCount++;
+ context.setStatusText("Processing " + diffsCount + "/"
+ + diffsCursor.getCount());
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "processing server entry " +
+ diffsCount + ", " + serverSyncId);
+
+ if (TRACE) {
+ if (diffsCount == 10) {
+ Debug.startMethodTracing("atmtrace");
+ }
+ if (diffsCount == 20) {
+ Debug.stopMethodTracing();
+ }
}
- continue;
- }
- lastSyncId = serverSyncId;
- String localSyncID = null;
- boolean localSyncDirty = false;
+ boolean conflict = false;
+ boolean update = false;
+ boolean insert = false;
- while (!localCursor.isAfterLast()) {
- if (mIsMergeCancelled) {
- localCursor.deactivate();
- deletedCursor.deactivate();
- diffsCursor.deactivate();
- return;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "found event with serverSyncID " + serverSyncId);
+ }
+ if (TextUtils.isEmpty(serverSyncId)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.e(TAG, "server entry doesn't have a serverSyncID");
+ }
+ continue;
}
- localCount++;
- localSyncID = localCursor.getString(2);
- // If the local record doesn't have a _sync_id then
- // it is new. Ignore it for now, we will send an insert
- // the the server later.
- if (TextUtils.isEmpty(localSyncID)) {
+ // It is possible that the sync adapter wrote the same record multiple times,
+ // e.g. if the same record came via multiple feeds. If this happens just ignore
+ // the duplicate records.
+ if (serverSyncId.equals(lastSyncId)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has no _sync_id, ignoring");
+ Log.v(TAG, "skipping record with duplicate remote server id " + lastSyncId);
}
- localCursor.moveToNext();
- localSyncID = null;
continue;
}
+ lastSyncId = serverSyncId;
- int comp = serverSyncId.compareTo(localSyncID);
+ String localSyncID = null;
+ boolean localSyncDirty = false;
- // the local DB has a record that the server doesn't have
- if (comp > 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that is < server _sync_id " + serverSyncId);
+ while (!localCursor.isAfterLast()) {
+ if (mIsMergeCancelled) {
+ return;
}
- if (diffsArePartial) {
- localCursor.moveToNext();
- } else {
- deleteRow(localCursor);
- if (mDeletedTable != null) {
- mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
+ localCount++;
+ localSyncID = localCursor.getString(2);
+
+ // If the local record doesn't have a _sync_id then
+ // it is new. Ignore it for now, we will send an insert
+ // the the server later.
+ if (TextUtils.isEmpty(localSyncID)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "local record " +
+ localCursor.getLong(1) +
+ " has no _sync_id, ignoring");
}
- syncResult.stats.numDeletes++;
- mDb.yieldIfContended();
+ localCursor.moveToNext();
+ localSyncID = null;
+ continue;
}
- localSyncID = null;
- continue;
- }
- // the server has a record that the local DB doesn't have
- if (comp < 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that is > server _sync_id " + serverSyncId);
+ int comp = serverSyncId.compareTo(localSyncID);
+
+ // the local DB has a record that the server doesn't have
+ if (comp > 0) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "local record " +
+ localCursor.getLong(1) +
+ " has _sync_id " + localSyncID +
+ " that is < server _sync_id " + serverSyncId);
+ }
+ if (diffsArePartial) {
+ localCursor.moveToNext();
+ } else {
+ deleteRow(localCursor);
+ if (mDeletedTable != null) {
+ mDb.delete(mDeletedTable, _SYNC_ID +"=?", new String[] {localSyncID});
+ }
+ syncResult.stats.numDeletes++;
+ mDb.yieldIfContended();
+ }
+ localSyncID = null;
+ continue;
}
- localSyncID = null;
- }
- // the server and the local DB both have this record
- if (comp == 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "local record " +
- localCursor.getLong(1) +
- " has _sync_id " + localSyncID +
- " that matches the server _sync_id");
+ // the server has a record that the local DB doesn't have
+ if (comp < 0) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "local record " +
+ localCursor.getLong(1) +
+ " has _sync_id " + localSyncID +
+ " that is > server _sync_id " + serverSyncId);
+ }
+ localSyncID = null;
}
- localSyncDirty = localCursor.getInt(0) != 0;
- localRowId = localCursor.getLong(1);
- localSyncVersion = localCursor.getString(3);
- localCursor.moveToNext();
- }
- break;
- }
+ // the server and the local DB both have this record
+ if (comp == 0) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "local record " +
+ localCursor.getLong(1) +
+ " has _sync_id " + localSyncID +
+ " that matches the server _sync_id");
+ }
+ localSyncDirty = localCursor.getInt(0) != 0;
+ localRowId = localCursor.getLong(1);
+ localSyncVersion = localCursor.getString(3);
+ localCursor.moveToNext();
+ }
- // If this record is in the deleted table then update the server version
- // in the deleted table, if necessary, and then ignore it here.
- // We will send a deletion indication to the server down a
- // little further.
- if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
+ break;
}
- final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
- if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
+
+ // If this record is in the deleted table then update the server version
+ // in the deleted table, if necessary, and then ignore it here.
+ // We will send a deletion indication to the server down a
+ // little further.
+ if (findInCursor(deletedCursor, deletedSyncIDColumn, serverSyncId)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
- + serverSyncVersion);
+ Log.v(TAG, "remote record " + serverSyncId + " is in the deleted table");
+ }
+ final String deletedSyncVersion = deletedCursor.getString(deletedSyncVersionColumn);
+ if (!TextUtils.equals(deletedSyncVersion, serverSyncVersion)) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "setting version of deleted record " + serverSyncId + " to "
+ + serverSyncVersion);
+ }
+ ContentValues values = new ContentValues();
+ values.put(_SYNC_VERSION, serverSyncVersion);
+ mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
}
- ContentValues values = new ContentValues();
- values.put(_SYNC_VERSION, serverSyncVersion);
- mDb.update(mDeletedTable, values, "_sync_id=?", new String[]{serverSyncId});
+ continue;
}
- continue;
- }
- // If the _sync_local_id is present in the diffsCursor
- // then this record corresponds to a local record that was just
- // inserted into the server and the _sync_local_id is the row id
- // of the local record. Set these fields so that the next check
- // treats this record as an update, which will allow the
- // merger to update the record with the server's sync id
- if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
- localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "the remote record with sync id " + serverSyncId
- + " has a local sync id, " + localRowId);
+ // If the _sync_local_id is present in the diffsCursor
+ // then this record corresponds to a local record that was just
+ // inserted into the server and the _sync_local_id is the row id
+ // of the local record. Set these fields so that the next check
+ // treats this record as an update, which will allow the
+ // merger to update the record with the server's sync id
+ if (!diffsCursor.isNull(serverSyncLocalIdColumn)) {
+ localRowId = diffsCursor.getLong(serverSyncLocalIdColumn);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "the remote record with sync id " + serverSyncId
+ + " has a local sync id, " + localRowId);
+ }
+ localSyncID = serverSyncId;
+ localSyncDirty = false;
+ localSyncVersion = null;
}
- localSyncID = serverSyncId;
- localSyncDirty = false;
- localSyncVersion = null;
- }
- if (!TextUtils.isEmpty(localSyncID)) {
- // An existing server item has changed
- // If serverSyncVersion is null, there is no edit URL;
- // server won't let this change be written.
- boolean recordChanged = (localSyncVersion == null) ||
- (serverSyncVersion == null) ||
- !serverSyncVersion.equals(localSyncVersion);
- if (recordChanged) {
- if (localSyncDirty) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId
- + " conflicts with local _sync_id " + localSyncID
- + ", local _id " + localRowId);
+ if (!TextUtils.isEmpty(localSyncID)) {
+ // An existing server item has changed
+ // If serverSyncVersion is null, there is no edit URL;
+ // server won't let this change be written.
+ boolean recordChanged = (localSyncVersion == null) ||
+ (serverSyncVersion == null) ||
+ !serverSyncVersion.equals(localSyncVersion);
+ if (recordChanged) {
+ if (localSyncDirty) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "remote record " + serverSyncId
+ + " conflicts with local _sync_id " + localSyncID
+ + ", local _id " + localRowId);
+ }
+ conflict = true;
+ } else {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "remote record " +
+ serverSyncId +
+ " updates local _sync_id " +
+ localSyncID + ", local _id " +
+ localRowId);
+ }
+ update = true;
}
- conflict = true;
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG,
- "remote record " +
- serverSyncId +
- " updates local _sync_id " +
- localSyncID + ", local _id " +
- localRowId);
+ "Skipping update: localSyncVersion: " + localSyncVersion +
+ ", serverSyncVersion: " + serverSyncVersion);
}
- update = true;
}
} else {
+ // the local db doesn't know about this record so add it
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "Skipping update: localSyncVersion: " + localSyncVersion +
- ", serverSyncVersion: " + serverSyncVersion);
+ Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
}
+ insert = true;
}
- } else {
- // the local db doesn't know about this record so add it
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "remote record " + serverSyncId + " is new, inserting");
+
+ if (update) {
+ updateRow(localRowId, serverDiffs, diffsCursor);
+ syncResult.stats.numUpdates++;
+ } else if (conflict) {
+ resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
+ syncResult.stats.numUpdates++;
+ } else if (insert) {
+ insertRow(serverDiffs, diffsCursor);
+ syncResult.stats.numInserts++;
}
- insert = true;
}
- if (update) {
- updateRow(localRowId, serverDiffs, diffsCursor);
- syncResult.stats.numUpdates++;
- } else if (conflict) {
- resolveRow(localRowId, serverSyncId, serverDiffs, diffsCursor);
- syncResult.stats.numUpdates++;
- } else if (insert) {
- insertRow(serverDiffs, diffsCursor);
- syncResult.stats.numInserts++;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "processed " + diffsCount + " server entries");
}
- }
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "processed " + diffsCount + " server entries");
- }
-
- // If tombstones aren't in use delete any remaining local rows that
- // don't have corresponding server rows. Keep the rows that don't
- // have a sync id since those were created locally and haven't been
- // synced to the server yet.
- if (!diffsArePartial) {
- while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
- if (mIsMergeCancelled) {
- localCursor.deactivate();
- deletedCursor.deactivate();
- diffsCursor.deactivate();
- return;
- }
- localCount++;
- final String localSyncId = localCursor.getString(2);
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG,
- "deleting local record " +
- localCursor.getLong(1) +
- " _sync_id " + localSyncId);
- }
- deleteRow(localCursor);
- if (mDeletedTable != null) {
- mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
+ // If tombstones aren't in use delete any remaining local rows that
+ // don't have corresponding server rows. Keep the rows that don't
+ // have a sync id since those were created locally and haven't been
+ // synced to the server yet.
+ if (!diffsArePartial) {
+ while (!localCursor.isAfterLast() && !TextUtils.isEmpty(localCursor.getString(2))) {
+ if (mIsMergeCancelled) {
+ return;
+ }
+ localCount++;
+ final String localSyncId = localCursor.getString(2);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG,
+ "deleting local record " +
+ localCursor.getLong(1) +
+ " _sync_id " + localSyncId);
+ }
+ deleteRow(localCursor);
+ if (mDeletedTable != null) {
+ mDb.delete(mDeletedTable, _SYNC_ID + "=?", new String[] {localSyncId});
+ }
+ syncResult.stats.numDeletes++;
+ mDb.yieldIfContended();
}
- syncResult.stats.numDeletes++;
- mDb.yieldIfContended();
}
+ if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
+ " local entries");
+ } finally {
+ if (diffsCursor != null) diffsCursor.close();
+ if (localCursor != null) localCursor.close();
+ if (deletedCursor != null) deletedCursor.close();
}
- if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "checked " + localCount +
- " local entries");
- diffsCursor.deactivate();
- localCursor.deactivate();
- deletedCursor.deactivate();
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "applying deletions from the server");
// Apply deletions from the server
if (mDeletedTableURL != null) {
diffsCursor = serverDiffs.query(mDeletedTableURL, null, null, null, null);
-
- while (diffsCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- diffsCursor.deactivate();
- return;
+ try {
+ while (diffsCursor.moveToNext()) {
+ if (mIsMergeCancelled) {
+ return;
+ }
+ // delete all rows that match each element in the diffsCursor
+ fullyDeleteMatchingRows(diffsCursor, account, syncResult);
+ mDb.yieldIfContended();
}
- // delete all rows that match each element in the diffsCursor
- fullyDeleteMatchingRows(diffsCursor, account, syncResult);
- mDb.yieldIfContended();
+ } finally {
+ diffsCursor.close();
}
- diffsCursor.deactivate();
}
}
- private void fullyDeleteMatchingRows(Cursor diffsCursor, String account,
+ private void fullyDeleteMatchingRows(Cursor diffsCursor, Account account,
SyncResult syncResult) {
int serverSyncIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_ID);
final boolean deleteBySyncId = !diffsCursor.isNull(serverSyncIdColumn);
// delete the rows explicitly so that the delete operation can be overridden
- final Cursor c;
final String[] selectionArgs;
- if (deleteBySyncId) {
- selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn), account};
- c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
- selectionArgs, null, null, null);
- } else {
- int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
- selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
- c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
- null, null, null);
- }
+ Cursor c = null;
try {
+ if (deleteBySyncId) {
+ selectionArgs = new String[]{diffsCursor.getString(serverSyncIdColumn),
+ account.name, account.type};
+ c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_SYNC_ID_AND_ACCOUNT,
+ selectionArgs, null, null, null);
+ } else {
+ int serverSyncLocalIdColumn = diffsCursor.getColumnIndexOrThrow(_SYNC_LOCAL_ID);
+ selectionArgs = new String[]{diffsCursor.getString(serverSyncLocalIdColumn)};
+ c = mDb.query(mTable, new String[]{BaseColumns._ID}, SELECT_BY_ID, selectionArgs,
+ null, null, null);
+ }
c.moveToFirst();
while (!c.isAfterLast()) {
deleteRow(c); // advances the cursor
syncResult.stats.numDeletes++;
}
} finally {
- c.deactivate();
+ if (c != null) c.close();
}
if (deleteBySyncId && mDeletedTable != null) {
mDb.delete(mDeletedTable, SELECT_BY_SYNC_ID_AND_ACCOUNT, selectionArgs);
@@ -519,43 +520,46 @@ public abstract class AbstractTableMerger
* Finds local changes, placing the results in the given result object.
* @param temporaryInstanceFactory As an optimization for the case
* where there are no client-side diffs, mergeResult may initially
- * have no {@link android.content.TempProviderSyncResult#tempContentProvider}. If this is
+ * have no {@link TempProviderSyncResult#tempContentProvider}. If this is
* the first in the sequence of AbstractTableMergers to find
* client-side diffs, it will use the given ContentProvider to
* create a temporary instance and store its {@link
- * ContentProvider} in the mergeResult.
+ * android.content.ContentProvider} in the mergeResult.
* @param account
* @param syncResult
*/
private void findLocalChanges(TempProviderSyncResult mergeResult,
- SyncableContentProvider temporaryInstanceFactory, String account,
+ SyncableContentProvider temporaryInstanceFactory, Account account,
SyncResult syncResult) {
SyncableContentProvider clientDiffs = mergeResult.tempContentProvider;
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client updates");
- final String[] accountSelectionArgs = new String[]{account};
+ final String[] accountSelectionArgs = new String[]{account.name, account.type};
// Generate the client updates and insertions
// Create a cursor for dirty records
+ long numInsertsOrUpdates = 0;
Cursor localChangesCursor = mDb.query(mTable, null, SELECT_UNSYNCED, accountSelectionArgs,
null, null, null);
- long numInsertsOrUpdates = localChangesCursor.getCount();
- while (localChangesCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- localChangesCursor.close();
- return;
- }
- if (clientDiffs == null) {
- clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+ try {
+ numInsertsOrUpdates = localChangesCursor.getCount();
+ while (localChangesCursor.moveToNext()) {
+ if (mIsMergeCancelled) {
+ return;
+ }
+ if (clientDiffs == null) {
+ clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+ }
+ mValues.clear();
+ cursorRowToContentValues(localChangesCursor, mValues);
+ mValues.remove("_id");
+ DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
+ _SYNC_LOCAL_ID);
+ clientDiffs.insert(mTableURL, mValues);
}
- mValues.clear();
- cursorRowToContentValues(localChangesCursor, mValues);
- mValues.remove("_id");
- DatabaseUtils.cursorLongToContentValues(localChangesCursor, "_id", mValues,
- _SYNC_LOCAL_ID);
- clientDiffs.insert(mTableURL, mValues);
+ } finally {
+ localChangesCursor.close();
}
- localChangesCursor.close();
// Generate the client deletions
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "generating client deletions");
@@ -564,23 +568,25 @@ public abstract class AbstractTableMerger
if (mDeletedTable != null) {
Cursor deletedCursor = mDb.query(mDeletedTable,
syncIdAndVersionProjection,
- _SYNC_ACCOUNT + "=? AND " + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
+ _SYNC_ACCOUNT + "=? AND " + _SYNC_ACCOUNT_TYPE + "=? AND "
+ + _SYNC_ID + " IS NOT NULL", accountSelectionArgs,
null, null, mDeletedTable + "." + _SYNC_ID);
-
- numDeletedEntries = deletedCursor.getCount();
- while (deletedCursor.moveToNext()) {
- if (mIsMergeCancelled) {
- deletedCursor.close();
- return;
- }
- if (clientDiffs == null) {
- clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+ try {
+ numDeletedEntries = deletedCursor.getCount();
+ while (deletedCursor.moveToNext()) {
+ if (mIsMergeCancelled) {
+ return;
+ }
+ if (clientDiffs == null) {
+ clientDiffs = temporaryInstanceFactory.getTemporaryInstance();
+ }
+ mValues.clear();
+ DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
+ clientDiffs.insert(mDeletedTableURL, mValues);
}
- mValues.clear();
- DatabaseUtils.cursorRowToContentValues(deletedCursor, mValues);
- clientDiffs.insert(mDeletedTableURL, mValues);
+ } finally {
+ deletedCursor.close();
}
- deletedCursor.close();
}
if (clientDiffs != null) {
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
new file mode 100644
index 0000000..6d870da
--- /dev/null
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.accounts.Account;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.NetStat;
+import android.util.EventLog;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation.
+ * If a sync operation is already in progress when a startSync() request is received then an error
+ * will be returned to the new request and the existing request will be allowed to continue.
+ * When a startSync() is received and there is no sync operation in progress then a thread
+ * will be started to run the operation and {@link #performSync} will be invoked on that thread.
+ * If a cancelSync() is received that matches an existing sync operation then the thread
+ * that is running that sync operation will be interrupted, which will indicate to the thread
+ * that the sync has been canceled.
+ *
+ * @hide
+ */
+public abstract class AbstractThreadedSyncAdapter {
+ private final Context mContext;
+ private final AtomicInteger mNumSyncStarts;
+ private final ISyncAdapterImpl mISyncAdapterImpl;
+
+ // all accesses to this member variable must be synchronized on mSyncThreadLock
+ private SyncThread mSyncThread;
+ private final Object mSyncThreadLock = new Object();
+
+ /** Kernel event log tag. Also listed in data/etc/event-log-tags. */
+ public static final int LOG_SYNC_DETAILS = 2743;
+ private static final String TAG = "Sync";
+ private final boolean mAutoInitialize;
+
+ /**
+ * Creates an {@link AbstractThreadedSyncAdapter}.
+ * @param context the {@link android.content.Context} that this is running within.
+ * @param autoInitialize if true then sync requests that have
+ * {@link ContentResolver#SYNC_EXTRAS_INITIALIZE} set will be internally handled by
+ * {@link AbstractThreadedSyncAdapter} by calling
+ * {@link ContentResolver#setIsSyncable(android.accounts.Account, String, int)} with 1 if it
+ * is currently set to <0.
+ */
+ public AbstractThreadedSyncAdapter(Context context, boolean autoInitialize) {
+ mContext = context;
+ mISyncAdapterImpl = new ISyncAdapterImpl();
+ mNumSyncStarts = new AtomicInteger(0);
+ mSyncThread = null;
+ mAutoInitialize = autoInitialize;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ class ISyncAdapterImpl extends ISyncAdapter.Stub {
+ public void startSync(ISyncContext syncContext, String authority, Account account,
+ Bundle extras) {
+ final SyncContext syncContextClient = new SyncContext(syncContext);
+
+ boolean alreadyInProgress;
+ // synchronize to make sure that mSyncThread doesn't change between when we
+ // check it and when we use it
+ synchronized (mSyncThreadLock) {
+ if (mSyncThread == null) {
+ if (mAutoInitialize
+ && extras != null
+ && extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
+ if (ContentResolver.getIsSyncable(account, authority) < 0) {
+ ContentResolver.setIsSyncable(account, authority, 1);
+ }
+ syncContextClient.onFinished(new SyncResult());
+ return;
+ }
+ mSyncThread = new SyncThread(
+ "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(),
+ syncContextClient, authority, account, extras);
+ mSyncThread.start();
+ alreadyInProgress = false;
+ } else {
+ alreadyInProgress = true;
+ }
+ }
+
+ // do this outside since we don't want to call back into the syncContext while
+ // holding the synchronization lock
+ if (alreadyInProgress) {
+ syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS);
+ }
+ }
+
+ public void cancelSync(ISyncContext syncContext) {
+ // synchronize to make sure that mSyncThread doesn't change between when we
+ // check it and when we use it
+ synchronized (mSyncThreadLock) {
+ if (mSyncThread != null
+ && mSyncThread.mSyncContext.getISyncContext().asBinder()
+ == syncContext.asBinder()) {
+ mSyncThread.interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * The thread that invokes performSync(). It also acquires the provider for this sync
+ * before calling performSync and releases it afterwards. Cancel this thread in order to
+ * cancel the sync.
+ */
+ private class SyncThread extends Thread {
+ private final SyncContext mSyncContext;
+ private final String mAuthority;
+ private final Account mAccount;
+ private final Bundle mExtras;
+ private long mInitialTxBytes;
+ private long mInitialRxBytes;
+
+ private SyncThread(String name, SyncContext syncContext, String authority,
+ Account account, Bundle extras) {
+ super(name);
+ mSyncContext = syncContext;
+ mAuthority = authority;
+ mAccount = account;
+ mExtras = extras;
+ }
+
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+ if (isCanceled()) {
+ return;
+ }
+
+ SyncResult syncResult = new SyncResult();
+ int uid = Process.myUid();
+ mInitialTxBytes = NetStat.getUidTxBytes(uid);
+ mInitialRxBytes = NetStat.getUidRxBytes(uid);
+ ContentProviderClient provider = null;
+ try {
+ provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
+ if (provider != null) {
+ AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras,
+ mAuthority, provider, syncResult);
+ } else {
+ // TODO(fredq) update the syncResults to indicate that we were unable to
+ // find the provider. maybe with a ProviderError?
+ }
+ } finally {
+ if (provider != null) {
+ provider.release();
+ }
+ if (!isCanceled()) {
+ mSyncContext.onFinished(syncResult);
+ }
+ logSyncDetails(NetStat.getUidTxBytes(uid) - mInitialTxBytes,
+ NetStat.getUidRxBytes(uid) - mInitialRxBytes, syncResult);
+ // synchronize so that the assignment will be seen by other threads
+ // that also synchronize accesses to mSyncThread
+ synchronized (mSyncThreadLock) {
+ mSyncThread = null;
+ }
+ }
+ }
+
+ private boolean isCanceled() {
+ return Thread.currentThread().isInterrupted();
+ }
+ }
+
+ /**
+ * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation.
+ */
+ public final ISyncAdapter getISyncAdapter() {
+ return mISyncAdapterImpl;
+ }
+
+ /**
+ * Perform a sync for this account. SyncAdapter-specific parameters may
+ * be specified in extras, which is guaranteed to not be null. Invocations
+ * of this method are guaranteed to be serialized.
+ *
+ * @param account the account that should be synced
+ * @param extras SyncAdapter-specific parameters
+ * @param authority the authority of this sync request
+ * @param provider a ContentProviderClient that points to the ContentProvider for this
+ * authority
+ * @param syncResult SyncAdapter-specific parameters
+ */
+ public abstract void performSync(Account account, Bundle extras,
+ String authority, ContentProviderClient provider, SyncResult syncResult);
+
+ /**
+ * Logs details on the sync.
+ * Normally this will be overridden by a subclass that will provide
+ * provider-specific details.
+ *
+ * @param bytesSent number of bytes the sync sent over the network
+ * @param bytesReceived number of bytes the sync received over the network
+ * @param result The SyncResult object holding info on the sync
+ */
+ protected void logSyncDetails(long bytesSent, long bytesReceived, SyncResult result) {
+ EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/content/ActiveSyncInfo.java b/core/java/android/content/ActiveSyncInfo.java
index 63be8d1..209dffa 100644
--- a/core/java/android/content/ActiveSyncInfo.java
+++ b/core/java/android/content/ActiveSyncInfo.java
@@ -16,17 +16,18 @@
package android.content;
+import android.accounts.Account;
import android.os.Parcel;
import android.os.Parcelable.Creator;
/** @hide */
public class ActiveSyncInfo {
public final int authorityId;
- public final String account;
+ public final Account account;
public final String authority;
public final long startTime;
- ActiveSyncInfo(int authorityId, String account, String authority,
+ ActiveSyncInfo(int authorityId, Account account, String authority,
long startTime) {
this.authorityId = authorityId;
this.account = account;
@@ -40,14 +41,14 @@ public class ActiveSyncInfo {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(authorityId);
- parcel.writeString(account);
+ account.writeToParcel(parcel, 0);
parcel.writeString(authority);
parcel.writeLong(startTime);
}
ActiveSyncInfo(Parcel parcel) {
authorityId = parcel.readInt();
- account = parcel.readString();
+ account = new Account(parcel);
authority = parcel.readString();
startTime = parcel.readLong();
}
diff --git a/core/java/android/content/AsyncQueryHandler.java b/core/java/android/content/AsyncQueryHandler.java
index ac851cc..5e88de0 100644
--- a/core/java/android/content/AsyncQueryHandler.java
+++ b/core/java/android/content/AsyncQueryHandler.java
@@ -38,7 +38,8 @@ public abstract class AsyncQueryHandler extends Handler {
private static final int EVENT_ARG_INSERT = 2;
private static final int EVENT_ARG_UPDATE = 3;
private static final int EVENT_ARG_DELETE = 4;
-
+ private static final int EVENT_ARG_QUERY_ENTITIES = 5;
+
/* package */ final WeakReference<ContentResolver> mResolver;
private static Looper sLooper = null;
@@ -85,12 +86,25 @@ public abstract class AsyncQueryHandler extends Handler {
cursor.getCount();
}
} catch (Exception e) {
+ Log.w(TAG, e.toString());
cursor = null;
}
args.result = cursor;
break;
+ case EVENT_ARG_QUERY_ENTITIES:
+ EntityIterator iterator = null;
+ try {
+ iterator = resolver.queryEntities(args.uri, args.selection,
+ args.selectionArgs, args.orderBy);
+ } catch (Exception e) {
+ Log.w(TAG, e.toString());
+ }
+
+ args.result = iterator;
+ break;
+
case EVENT_ARG_INSERT:
args.result = resolver.insert(args.uri, args.values);
break;
@@ -103,7 +117,6 @@ public abstract class AsyncQueryHandler extends Handler {
case EVENT_ARG_DELETE:
args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
break;
-
}
// passing the original token value back to the caller
@@ -128,7 +141,7 @@ public abstract class AsyncQueryHandler extends Handler {
if (sLooper == null) {
HandlerThread thread = new HandlerThread("AsyncQueryWorker");
thread.start();
-
+
sLooper = thread.getLooper();
}
}
@@ -182,6 +195,44 @@ public abstract class AsyncQueryHandler extends Handler {
}
/**
+ * This method begins an asynchronous query for an {@link EntityIterator}.
+ * When the query is done {@link #onQueryEntitiesComplete} is called.
+ *
+ * @param token A token passed into {@link #onQueryComplete} to identify the
+ * query.
+ * @param cookie An object that gets passed into {@link #onQueryComplete}
+ * @param uri The URI, using the content:// scheme, for the content to
+ * retrieve.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null
+ * will return all rows for the given URI.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in the order that
+ * they appear in the selection. The values will be bound as
+ * Strings.
+ * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
+ * (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ */
+ public void startQueryEntities(int token, Object cookie, Uri uri, String selection,
+ String[] selectionArgs, String orderBy) {
+ // Use the token as what so cancelOperations works properly
+ Message msg = mWorkerThreadHandler.obtainMessage(token);
+ msg.arg1 = EVENT_ARG_QUERY_ENTITIES;
+
+ WorkerArgs args = new WorkerArgs();
+ args.handler = this;
+ args.uri = uri;
+ args.selection = selection;
+ args.selectionArgs = selectionArgs;
+ args.orderBy = orderBy;
+ args.cookie = cookie;
+ msg.obj = args;
+
+ mWorkerThreadHandler.sendMessage(msg);
+ }
+
+ /**
* Attempts to cancel operation that has not already started. Note that
* there is no guarantee that the operation will be canceled. They still may
* result in a call to on[Query/Insert/Update/Delete]Complete after this
@@ -279,8 +330,8 @@ public abstract class AsyncQueryHandler extends Handler {
* Called when an asynchronous query is completed.
*
* @param token the token to identify the query, passed in from
- * {@link #startQuery}.
- * @param cookie the cookie object that's passed in from {@link #startQuery}.
+ * {@link #startQuery}.
+ * @param cookie the cookie object passed in from {@link #startQuery}.
* @param cursor The cursor holding the results from the query.
*/
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
@@ -288,6 +339,17 @@ public abstract class AsyncQueryHandler extends Handler {
}
/**
+ * Called when an asynchronous query is completed.
+ *
+ * @param token The token to identify the query.
+ * @param cookie The cookie object.
+ * @param iterator The iterator holding the query results.
+ */
+ protected void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
+ // Empty
+ }
+
+ /**
* Called when an asynchronous insert is completed.
*
* @param token the token to identify the query, passed in from
@@ -337,13 +399,17 @@ public abstract class AsyncQueryHandler extends Handler {
int token = msg.what;
int event = msg.arg1;
-
+
// pass token back to caller on each callback.
switch (event) {
case EVENT_ARG_QUERY:
onQueryComplete(token, args.cookie, (Cursor) args.result);
break;
+ case EVENT_ARG_QUERY_ENTITIES:
+ onQueryEntitiesComplete(token, args.cookie, (EntityIterator)args.result);
+ break;
+
case EVENT_ARG_INSERT:
onInsertComplete(token, args.cookie, (Uri) args.result);
break;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 6b50405..5b29b97 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -34,6 +34,7 @@ import android.os.Process;
import java.io.File;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* Content providers are one of the primary building blocks of Android applications, providing
@@ -130,6 +131,12 @@ public abstract class ContentProvider implements ComponentCallbacks {
selectionArgs, sortOrder);
}
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) {
+ enforceReadPermission(uri);
+ return ContentProvider.this.queryEntities(uri, selection, selectionArgs, sortOrder);
+ }
+
public String getType(Uri uri) {
return ContentProvider.this.getType(uri);
}
@@ -145,6 +152,25 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.bulkInsert(uri, initialValues);
}
+ public Uri insertEntity(Uri uri, Entity entities) {
+ enforceWritePermission(uri);
+ return ContentProvider.this.insertEntity(uri, entities);
+ }
+
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ for (ContentProviderOperation operation : operations) {
+ if (operation.isReadOperation()) {
+ enforceReadPermission(operation.getUri());
+ }
+
+ if (operation.isWriteOperation()) {
+ enforceWritePermission(operation.getUri());
+ }
+ }
+ return ContentProvider.this.applyBatch(operations);
+ }
+
public int delete(Uri uri, String selection, String[] selectionArgs) {
enforceWritePermission(uri);
return ContentProvider.this.delete(uri, selection, selectionArgs);
@@ -156,6 +182,11 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.update(uri, values, selection, selectionArgs);
}
+ public int updateEntity(Uri uri, Entity entity) {
+ enforceWritePermission(uri);
+ return ContentProvider.this.updateEntity(uri, entity);
+ }
+
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
if (mode != null && mode.startsWith("rw")) enforceWritePermission(uri);
@@ -170,12 +201,6 @@ public abstract class ContentProvider implements ComponentCallbacks {
return ContentProvider.this.openAssetFile(uri, mode);
}
- public ISyncAdapter getSyncAdapter() {
- enforceWritePermission(null);
- SyncAdapter sa = ContentProvider.this.getSyncAdapter();
- return sa != null ? sa.getISyncAdapter() : null;
- }
-
private void enforceReadPermission(Uri uri) {
final int uid = Binder.getCallingUid();
if (uid == mMyUid) {
@@ -377,9 +402,10 @@ public abstract class ContentProvider implements ComponentCallbacks {
* Example client call:<p>
* <pre>// Request a specific record.
* Cursor managedCursor = managedQuery(
- Contacts.People.CONTENT_URI.addId(2),
+ ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2),
projection, // Which columns to return.
null, // WHERE clause.
+ null, // WHERE clause value substitution
People.NAME + " ASC"); // Sort order.</pre>
* Example implementation:<p>
* <pre>// SQLiteQueryBuilder is a helper class that creates the
@@ -408,20 +434,28 @@ public abstract class ContentProvider implements ComponentCallbacks {
return c;</pre>
*
* @param uri The URI to query. This will be the full URI sent by the client;
- * if the client is requesting a specific record, the URI will end in a record number
- * that the implementation should parse and add to a WHERE or HAVING clause, specifying
- * that _id value.
+ * if the client is requesting a specific record, the URI will end in a record number
+ * that the implementation should parse and add to a WHERE or HAVING clause, specifying
+ * that _id value.
* @param projection The list of columns to put into the cursor. If
* null all columns are included.
* @param selection A selection criteria to apply when filtering rows.
* If null then all rows are included.
+ * @param selectionArgs You may include ?s in selection, which will be replaced by
+ * the values from selectionArgs, in order that they appear in the selection.
+ * The values will be bound as Strings.
* @param sortOrder How the rows in the cursor should be sorted.
- * If null then the provider is free to define the sort order.
+ * If null then the provider is free to define the sort order.
* @return a Cursor or null.
*/
public abstract Cursor query(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder);
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Return the MIME type of the data at the given URI. This should start with
* <code>vnd.android.cursor.item</code> for a single record,
@@ -472,6 +506,10 @@ public abstract class ContentProvider implements ComponentCallbacks {
return numValues;
}
+ public Uri insertEntity(Uri uri, Entity entity) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* A request to delete one or more rows. The selection clause is applied when performing
* the deletion, allowing the operation to affect multiple rows in a
@@ -516,6 +554,10 @@ public abstract class ContentProvider implements ComponentCallbacks {
public abstract int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs);
+ public int updateEntity(Uri uri, Entity entity) {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Open a file blob associated with a content URI.
* This method can be called from multiple
@@ -639,23 +681,6 @@ public abstract class ContentProvider implements ComponentCallbacks {
}
/**
- * Get the sync adapter that is to be used by this content provider.
- * This is intended for use by the sync system. If null then this
- * content provider is considered not syncable.
- * This method can be called from multiple
- * threads, as described in
- * <a href="{@docRoot}guide/topics/fundamentals.html#procthread">Application Fundamentals:
- * Processes and Threads</a>.
- *
- * @return the SyncAdapter that is to be used by this ContentProvider, or null
- * if this ContentProvider is not syncable
- * @hide
- */
- public SyncAdapter getSyncAdapter() {
- return null;
- }
-
- /**
* Returns true if this instance is a temporary content provider.
* @return true if this instance is a temporary content provider
*/
@@ -697,4 +722,27 @@ public abstract class ContentProvider implements ComponentCallbacks {
ContentProvider.this.onCreate();
}
}
-}
+
+ /**
+ * Applies each of the {@link ContentProviderOperation} objects and returns an array
+ * of their results. Passes through OperationApplicationException, which may be thrown
+ * by the call to {@link ContentProviderOperation#apply}.
+ * If all the applications succeed then a {@link ContentProviderResult} array with the
+ * same number of elements as the operations will be returned. It is implementation-specific
+ * how many, if any, operations will have been successfully applied if a call to
+ * apply results in a {@link OperationApplicationException}.
+ * @param operations the operations to apply
+ * @return the results of the applications
+ * @throws OperationApplicationException thrown if an application fails.
+ * See {@link ContentProviderOperation#apply} for more information.
+ */
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws OperationApplicationException {
+ final int numOperations = operations.size();
+ final ContentProviderResult[] results = new ContentProviderResult[numOperations];
+ for (int i = 0; i < numOperations; i++) {
+ results[i] = operations.get(i).apply(this, results, i);
+ }
+ return results;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
new file mode 100644
index 0000000..452653e
--- /dev/null
+++ b/core/java/android/content/ContentProviderClient.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.ParcelFileDescriptor;
+import android.content.res.AssetFileDescriptor;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+
+/**
+ * The public interface object used to interact with a {@link ContentProvider}. This is obtained by
+ * calling {@link ContentResolver#acquireContentProviderClient}. This object must be released
+ * using {@link #release} in order to indicate to the system that the {@link ContentProvider} is
+ * no longer needed and can be killed to free up resources.
+ */
+public class ContentProviderClient {
+ private final IContentProvider mContentProvider;
+ private final ContentResolver mContentResolver;
+
+ /**
+ * @hide
+ */
+ ContentProviderClient(ContentResolver contentResolver, IContentProvider contentProvider) {
+ mContentProvider = contentProvider;
+ mContentResolver = contentResolver;
+ }
+
+ /** see {@link ContentProvider#query} */
+ public Cursor query(Uri url, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) throws RemoteException {
+ return mContentProvider.query(url, projection, selection, selectionArgs, sortOrder);
+ }
+
+ /** see {@link ContentProvider#getType} */
+ public String getType(Uri url) throws RemoteException {
+ return mContentProvider.getType(url);
+ }
+
+ /** see {@link ContentProvider#insert} */
+ public Uri insert(Uri url, ContentValues initialValues)
+ throws RemoteException {
+ return mContentProvider.insert(url, initialValues);
+ }
+
+ /** see {@link ContentProvider#bulkInsert} */
+ public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException {
+ return mContentProvider.bulkInsert(url, initialValues);
+ }
+
+ /** see {@link ContentProvider#delete} */
+ public int delete(Uri url, String selection, String[] selectionArgs)
+ throws RemoteException {
+ return mContentProvider.delete(url, selection, selectionArgs);
+ }
+
+ /** see {@link ContentProvider#update} */
+ public int update(Uri url, ContentValues values, String selection,
+ String[] selectionArgs) throws RemoteException {
+ return mContentProvider.update(url, values, selection, selectionArgs);
+ }
+
+ /** see {@link ContentProvider#openFile} */
+ public ParcelFileDescriptor openFile(Uri url, String mode)
+ throws RemoteException, FileNotFoundException {
+ return mContentProvider.openFile(url, mode);
+ }
+
+ /** see {@link ContentProvider#openAssetFile} */
+ public AssetFileDescriptor openAssetFile(Uri url, String mode)
+ throws RemoteException, FileNotFoundException {
+ return mContentProvider.openAssetFile(url, mode);
+ }
+
+ /** see {@link ContentProvider#queryEntities} */
+ public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
+ String sortOrder) throws RemoteException {
+ return mContentProvider.queryEntities(uri, selection, selectionArgs, sortOrder);
+ }
+
+ /** see {@link ContentProvider#insertEntity} */
+ public Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+ return mContentProvider.insertEntity(uri, entity);
+ }
+
+ /** see {@link ContentProvider#updateEntity} */
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+ return mContentProvider.updateEntity(uri, entity);
+ }
+
+ /** see {@link ContentProvider#applyBatch} */
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
+ return mContentProvider.applyBatch(operations);
+ }
+
+ /**
+ * Call this to indicate to the system that the associated {@link ContentProvider} is no
+ * longer needed by this {@link ContentProviderClient}.
+ * @return true if this was release, false if it was already released
+ */
+ public boolean release() {
+ return mContentResolver.releaseProvider(mContentProvider);
+ }
+
+ /**
+ * Get a reference to the {@link ContentProvider} that is associated with this
+ * client. If the {@link ContentProvider} is running in a different process then
+ * null will be returned. This can be used if you know you are running in the same
+ * process as a provider, and want to get direct access to its implementation details.
+ *
+ * @return If the associated {@link ContentProvider} is local, returns it.
+ * Otherwise returns null.
+ */
+ public ContentProvider getLocalContentProvider() {
+ return ContentProvider.coerceToLocalContentProvider(mContentProvider);
+ }
+}
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index e5e3f74..e367ceb 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -33,6 +33,7 @@ import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* {@hide}
@@ -105,6 +106,20 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case QUERY_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri url = Uri.CREATOR.createFromParcel(data);
+ String selection = data.readString();
+ String[] selectionArgs = data.readStringArray();
+ String sortOrder = data.readString();
+ EntityIterator entityIterator = queryEntities(url, selection, selectionArgs,
+ sortOrder);
+ reply.writeNoException();
+ reply.writeStrongBinder(new IEntityIteratorImpl(entityIterator).asBinder());
+ return true;
+ }
+
case GET_TYPE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -140,6 +155,43 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return true;
}
+ case INSERT_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ Entity entity = (Entity) data.readParcelable(null);
+ Uri newUri = insertEntity(uri, entity);
+ reply.writeNoException();
+ Uri.writeToParcel(reply, newUri);
+ return true;
+ }
+
+ case UPDATE_ENTITIES_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ Uri uri = Uri.CREATOR.createFromParcel(data);
+ Entity entity = (Entity) data.readParcelable(null);
+ int count = updateEntity(uri, entity);
+ reply.writeNoException();
+ reply.writeInt(count);
+ return true;
+ }
+
+ case APPLY_BATCH_TRANSACTION:
+ {
+ data.enforceInterface(IContentProvider.descriptor);
+ final int numOperations = data.readInt();
+ final ArrayList<ContentProviderOperation> operations =
+ new ArrayList<ContentProviderOperation>(numOperations);
+ for (int i = 0; i < numOperations; i++) {
+ operations.add(i, ContentProviderOperation.CREATOR.createFromParcel(data));
+ }
+ final ContentProviderResult[] results = applyBatch(operations);
+ reply.writeNoException();
+ reply.writeTypedArray(results, 0);
+ return true;
+ }
+
case DELETE_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
@@ -206,15 +258,6 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
}
return true;
}
-
- case GET_SYNC_ADAPTER_TRANSACTION:
- {
- data.enforceInterface(IContentProvider.descriptor);
- ISyncAdapter sa = getSyncAdapter();
- reply.writeNoException();
- reply.writeStrongBinder(sa != null ? sa.asBinder() : null);
- return true;
- }
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
@@ -224,6 +267,29 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
return super.onTransact(code, data, reply, flags);
}
+ private class IEntityIteratorImpl extends IEntityIterator.Stub {
+ private final EntityIterator mEntityIterator;
+
+ IEntityIteratorImpl(EntityIterator iterator) {
+ mEntityIterator = iterator;
+ }
+ public boolean hasNext() throws RemoteException {
+ return mEntityIterator.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ return mEntityIterator.next();
+ }
+
+ public void reset() throws RemoteException {
+ mEntityIterator.reset();
+ }
+
+ public void close() throws RemoteException {
+ mEntityIterator.close();
+ }
+ }
+
public IBinder asBinder()
{
return this;
@@ -297,7 +363,7 @@ final class ContentProviderProxy implements IContentProvider
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder,
adaptor.getObserver(), window);
-
+
if (bulkCursor == null) {
return null;
}
@@ -305,6 +371,58 @@ final class ContentProviderProxy implements IContentProvider
return adaptor;
}
+ public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+ String sortOrder)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+
+ url.writeToParcel(data, 0);
+ data.writeString(selection);
+ data.writeStringArray(selectionArgs);
+ data.writeString(sortOrder);
+
+ mRemote.transact(IContentProvider.QUERY_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+
+ IBinder entityIteratorBinder = reply.readStrongBinder();
+
+ data.recycle();
+ reply.recycle();
+
+ return new RemoteEntityIterator(IEntityIterator.Stub.asInterface(entityIteratorBinder));
+ }
+
+ static class RemoteEntityIterator implements EntityIterator {
+ private final IEntityIterator mEntityIterator;
+ RemoteEntityIterator(IEntityIterator entityIterator) {
+ mEntityIterator = entityIterator;
+ }
+
+ public boolean hasNext() throws RemoteException {
+ return mEntityIterator.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ return mEntityIterator.next();
+ }
+
+ public void reset() throws RemoteException {
+ mEntityIterator.reset();
+ }
+
+ public void close() {
+ try {
+ mEntityIterator.close();
+ } catch (RemoteException e) {
+ // doesn't matter
+ }
+ }
+ }
+
public String getType(Uri url) throws RemoteException
{
Parcel data = Parcel.obtain();
@@ -366,6 +484,66 @@ final class ContentProviderProxy implements IContentProvider
return count;
}
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ data.writeInterfaceToken(IContentProvider.descriptor);
+ data.writeInt(operations.size());
+ for (ContentProviderOperation operation : operations) {
+ operation.writeToParcel(data, 0);
+ }
+ mRemote.transact(IContentProvider.APPLY_BATCH_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionWithOperationApplicationExceptionFromParcel(reply);
+ final ContentProviderResult[] results =
+ reply.createTypedArray(ContentProviderResult.CREATOR);
+
+ data.recycle();
+ reply.recycle();
+
+ return results;
+ }
+
+ public Uri insertEntity(Uri uri, Entity entity) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeParcelable(entity, 0);
+
+ mRemote.transact(IContentProvider.INSERT_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ return Uri.CREATOR.createFromParcel(reply);
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+
+ try {
+ data.writeInterfaceToken(IContentProvider.descriptor);
+ uri.writeToParcel(data, 0);
+ data.writeParcelable(entity, 0);
+
+ mRemote.transact(IContentProvider.UPDATE_ENTITIES_TRANSACTION, data, reply, 0);
+
+ DatabaseUtils.readExceptionFromParcel(reply);
+ return reply.readInt();
+ } finally {
+ data.recycle();
+ reply.recycle();
+ }
+ }
+
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException {
Parcel data = Parcel.obtain();
@@ -456,23 +634,6 @@ final class ContentProviderProxy implements IContentProvider
return fd;
}
- public ISyncAdapter getSyncAdapter() throws RemoteException {
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
-
- data.writeInterfaceToken(IContentProvider.descriptor);
-
- mRemote.transact(IContentProvider.GET_SYNC_ADAPTER_TRANSACTION, data, reply, 0);
-
- DatabaseUtils.readExceptionFromParcel(reply);
- ISyncAdapter syncAdapter = ISyncAdapter.Stub.asInterface(reply.readStrongBinder());
-
- data.recycle();
- reply.recycle();
-
- return syncAdapter;
- }
-
private IBinder mRemote;
}
diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java
new file mode 100644
index 0000000..238792b
--- /dev/null
+++ b/core/java/android/content/ContentProviderOperation.java
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ContentProviderOperation implements Parcelable {
+ /** @hide exposed for unit tests */
+ public final static int TYPE_INSERT = 1;
+ /** @hide exposed for unit tests */
+ public final static int TYPE_UPDATE = 2;
+ /** @hide exposed for unit tests */
+ public final static int TYPE_DELETE = 3;
+ /** @hide exposed for unit tests */
+ public final static int TYPE_ASSERT = 4;
+
+ private final int mType;
+ private final Uri mUri;
+ private final String mSelection;
+ private final String[] mSelectionArgs;
+ private final ContentValues mValues;
+ private final Integer mExpectedCount;
+ private final ContentValues mValuesBackReferences;
+ private final Map<Integer, Integer> mSelectionArgsBackReferences;
+ private final boolean mYieldAllowed;
+
+ /**
+ * Creates a {@link ContentProviderOperation} by copying the contents of a
+ * {@link Builder}.
+ */
+ private ContentProviderOperation(Builder builder) {
+ mType = builder.mType;
+ mUri = builder.mUri;
+ mValues = builder.mValues;
+ mSelection = builder.mSelection;
+ mSelectionArgs = builder.mSelectionArgs;
+ mExpectedCount = builder.mExpectedCount;
+ mSelectionArgsBackReferences = builder.mSelectionArgsBackReferences;
+ mValuesBackReferences = builder.mValuesBackReferences;
+ mYieldAllowed = builder.mYieldAllowed;
+ }
+
+ private ContentProviderOperation(Parcel source) {
+ mType = source.readInt();
+ mUri = Uri.CREATOR.createFromParcel(source);
+ mValues = source.readInt() != 0 ? ContentValues.CREATOR.createFromParcel(source) : null;
+ mSelection = source.readInt() != 0 ? source.readString() : null;
+ mSelectionArgs = source.readInt() != 0 ? source.readStringArray() : null;
+ mExpectedCount = source.readInt() != 0 ? source.readInt() : null;
+ mValuesBackReferences = source.readInt() != 0
+ ? ContentValues.CREATOR.createFromParcel(source)
+ : null;
+ mSelectionArgsBackReferences = source.readInt() != 0
+ ? new HashMap<Integer, Integer>()
+ : null;
+ if (mSelectionArgsBackReferences != null) {
+ final int count = source.readInt();
+ for (int i = 0; i < count; i++) {
+ mSelectionArgsBackReferences.put(source.readInt(), source.readInt());
+ }
+ }
+ mYieldAllowed = source.readInt() != 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ Uri.writeToParcel(dest, mUri);
+ if (mValues != null) {
+ dest.writeInt(1);
+ mValues.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mSelection != null) {
+ dest.writeInt(1);
+ dest.writeString(mSelection);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mSelectionArgs != null) {
+ dest.writeInt(1);
+ dest.writeStringArray(mSelectionArgs);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mExpectedCount != null) {
+ dest.writeInt(1);
+ dest.writeInt(mExpectedCount);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mValuesBackReferences != null) {
+ dest.writeInt(1);
+ mValuesBackReferences.writeToParcel(dest, 0);
+ } else {
+ dest.writeInt(0);
+ }
+ if (mSelectionArgsBackReferences != null) {
+ dest.writeInt(1);
+ dest.writeInt(mSelectionArgsBackReferences.size());
+ for (Map.Entry<Integer, Integer> entry : mSelectionArgsBackReferences.entrySet()) {
+ dest.writeInt(entry.getKey());
+ dest.writeInt(entry.getValue());
+ }
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(mYieldAllowed ? 1 : 0);
+ }
+
+ /**
+ * Create a {@link Builder} suitable for building an insert {@link ContentProviderOperation}.
+ * @param uri The {@link Uri} that is the target of the insert.
+ * @return a {@link Builder}
+ */
+ public static Builder newInsert(Uri uri) {
+ return new Builder(TYPE_INSERT, uri);
+ }
+
+ /**
+ * Create a {@link Builder} suitable for building an update {@link ContentProviderOperation}.
+ * @param uri The {@link Uri} that is the target of the update.
+ * @return a {@link Builder}
+ */
+ public static Builder newUpdate(Uri uri) {
+ return new Builder(TYPE_UPDATE, uri);
+ }
+
+ /**
+ * Create a {@link Builder} suitable for building a delete {@link ContentProviderOperation}.
+ * @param uri The {@link Uri} that is the target of the delete.
+ * @return a {@link Builder}
+ */
+ public static Builder newDelete(Uri uri) {
+ return new Builder(TYPE_DELETE, uri);
+ }
+
+ /**
+ * Create a {@link Builder} suitable for building a
+ * {@link ContentProviderOperation} to assert a set of values as provided
+ * through {@link Builder#withValues(ContentValues)}.
+ */
+ public static Builder newAssertQuery(Uri uri) {
+ return new Builder(TYPE_ASSERT, uri);
+ }
+
+ public Uri getUri() {
+ return mUri;
+ }
+
+ public boolean isYieldAllowed() {
+ return mYieldAllowed;
+ }
+
+ /** @hide exposed for unit tests */
+ public int getType() {
+ return mType;
+ }
+
+ public boolean isWriteOperation() {
+ return mType == TYPE_DELETE || mType == TYPE_INSERT || mType == TYPE_UPDATE;
+ }
+
+ public boolean isReadOperation() {
+ return mType == TYPE_ASSERT;
+ }
+
+ /**
+ * Applies this operation using the given provider. The backRefs array is used to resolve any
+ * back references that were requested using
+ * {@link Builder#withValueBackReferences(ContentValues)} and
+ * {@link Builder#withSelectionBackReference}.
+ * @param provider the {@link ContentProvider} on which this batch is applied
+ * @param backRefs a {@link ContentProviderResult} array that will be consulted
+ * to resolve any requested back references.
+ * @param numBackRefs the number of valid results on the backRefs array.
+ * @return a {@link ContentProviderResult} that contains either the {@link Uri} of the inserted
+ * row if this was an insert otherwise the number of rows affected.
+ * @throws OperationApplicationException thrown if either the insert fails or
+ * if the number of rows affected didn't match the expected count
+ */
+ public ContentProviderResult apply(ContentProvider provider, ContentProviderResult[] backRefs,
+ int numBackRefs) throws OperationApplicationException {
+ ContentValues values = resolveValueBackReferences(backRefs, numBackRefs);
+ String[] selectionArgs =
+ resolveSelectionArgsBackReferences(backRefs, numBackRefs);
+
+ if (mType == TYPE_INSERT) {
+ Uri newUri = provider.insert(mUri, values);
+ if (newUri == null) {
+ throw new OperationApplicationException("insert failed");
+ }
+ return new ContentProviderResult(newUri);
+ }
+
+ int numRows;
+ if (mType == TYPE_DELETE) {
+ numRows = provider.delete(mUri, mSelection, selectionArgs);
+ } else if (mType == TYPE_UPDATE) {
+ numRows = provider.update(mUri, values, mSelection, selectionArgs);
+ } else if (mType == TYPE_ASSERT) {
+ // Build projection map from expected values
+ final ArrayList<String> projectionList = new ArrayList<String>();
+ for (Map.Entry<String, Object> entry : values.valueSet()) {
+ projectionList.add(entry.getKey());
+ }
+
+ // Assert that all rows match expected values
+ final String[] projection = projectionList.toArray(new String[projectionList.size()]);
+ final Cursor cursor = provider.query(mUri, projection, mSelection, selectionArgs, null);
+ numRows = cursor.getCount();
+ try {
+ while (cursor.moveToNext()) {
+ for (int i = 0; i < projection.length; i++) {
+ final String cursorValue = cursor.getString(i);
+ final String expectedValue = values.getAsString(projection[i]);
+ if (!TextUtils.equals(cursorValue, expectedValue)) {
+ // Throw exception when expected values don't match
+ throw new OperationApplicationException("Found value " + cursorValue
+ + " when expected " + expectedValue + " for column "
+ + projection[i]);
+ }
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ } else {
+ throw new IllegalStateException("bad type, " + mType);
+ }
+
+ if (mExpectedCount != null && mExpectedCount != numRows) {
+ throw new OperationApplicationException("wrong number of rows: " + numRows);
+ }
+
+ return new ContentProviderResult(numRows);
+ }
+
+ /**
+ * The ContentValues back references are represented as a ContentValues object where the
+ * key refers to a column and the value is an index of the back reference whose
+ * valued should be associated with the column.
+ * @param backRefs an array of previous results
+ * @param numBackRefs the number of valid previous results in backRefs
+ * @return the ContentValues that should be used in this operation application after
+ * expansion of back references. This can be called if either mValues or mValuesBackReferences
+ * is null
+ * @VisibleForTesting this is intended to be a private method but it is exposed for
+ * unit testing purposes
+ */
+ public ContentValues resolveValueBackReferences(
+ ContentProviderResult[] backRefs, int numBackRefs) {
+ if (mValuesBackReferences == null) {
+ return mValues;
+ }
+ final ContentValues values;
+ if (mValues == null) {
+ values = new ContentValues();
+ } else {
+ values = new ContentValues(mValues);
+ }
+ for (Map.Entry<String, Object> entry : mValuesBackReferences.valueSet()) {
+ String key = entry.getKey();
+ Integer backRefIndex = mValuesBackReferences.getAsInteger(key);
+ if (backRefIndex == null) {
+ throw new IllegalArgumentException("values backref " + key + " is not an integer");
+ }
+ values.put(key, backRefToValue(backRefs, numBackRefs, backRefIndex));
+ }
+ return values;
+ }
+
+ /**
+ * The Selection Arguments back references are represented as a Map of Integer->Integer where
+ * the key is an index into the selection argument array (see {@link Builder#withSelection})
+ * and the value is the index of the previous result that should be used for that selection
+ * argument array slot.
+ * @param backRefs an array of previous results
+ * @param numBackRefs the number of valid previous results in backRefs
+ * @return the ContentValues that should be used in this operation application after
+ * expansion of back references. This can be called if either mValues or mValuesBackReferences
+ * is null
+ * @VisibleForTesting this is intended to be a private method but it is exposed for
+ * unit testing purposes
+ */
+ public String[] resolveSelectionArgsBackReferences(
+ ContentProviderResult[] backRefs, int numBackRefs) {
+ if (mSelectionArgsBackReferences == null) {
+ return mSelectionArgs;
+ }
+ String[] newArgs = new String[mSelectionArgs.length];
+ System.arraycopy(mSelectionArgs, 0, newArgs, 0, mSelectionArgs.length);
+ for (Map.Entry<Integer, Integer> selectionArgBackRef
+ : mSelectionArgsBackReferences.entrySet()) {
+ final Integer selectionArgIndex = selectionArgBackRef.getKey();
+ final int backRefIndex = selectionArgBackRef.getValue();
+ newArgs[selectionArgIndex] =
+ String.valueOf(backRefToValue(backRefs, numBackRefs, backRefIndex));
+ }
+ return newArgs;
+ }
+
+ /**
+ * Return the string representation of the requested back reference.
+ * @param backRefs an array of results
+ * @param numBackRefs the number of items in the backRefs array that are valid
+ * @param backRefIndex which backRef to be used
+ * @throws ArrayIndexOutOfBoundsException thrown if the backRefIndex is larger than
+ * the numBackRefs
+ * @return the string representation of the requested back reference.
+ */
+ private static long backRefToValue(ContentProviderResult[] backRefs, int numBackRefs,
+ Integer backRefIndex) {
+ if (backRefIndex >= numBackRefs) {
+ throw new ArrayIndexOutOfBoundsException("asked for back ref " + backRefIndex
+ + " but there are only " + numBackRefs + " back refs");
+ }
+ ContentProviderResult backRef = backRefs[backRefIndex];
+ long backRefValue;
+ if (backRef.uri != null) {
+ backRefValue = ContentUris.parseId(backRef.uri);
+ } else {
+ backRefValue = backRef.count;
+ }
+ return backRefValue;
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ContentProviderOperation> CREATOR =
+ new Creator<ContentProviderOperation>() {
+ public ContentProviderOperation createFromParcel(Parcel source) {
+ return new ContentProviderOperation(source);
+ }
+
+ public ContentProviderOperation[] newArray(int size) {
+ return new ContentProviderOperation[size];
+ }
+ };
+
+
+ /**
+ * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is
+ * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)},
+ * {@link ContentProviderOperation#newUpdate(android.net.Uri)},
+ * {@link ContentProviderOperation#newDelete(android.net.Uri)} or
+ * {@link ContentProviderOperation#newAssertQuery(Uri)}. The withXXX methods
+ * can then be used to add parameters to the builder. See the specific methods to find for
+ * which {@link Builder} type each is allowed. Call {@link #build} to create the
+ * {@link ContentProviderOperation} once all the parameters have been supplied.
+ */
+ public static class Builder {
+ private final int mType;
+ private final Uri mUri;
+ private String mSelection;
+ private String[] mSelectionArgs;
+ private ContentValues mValues;
+ private Integer mExpectedCount;
+ private ContentValues mValuesBackReferences;
+ private Map<Integer, Integer> mSelectionArgsBackReferences;
+ private boolean mYieldAllowed;
+
+ /** Create a {@link Builder} of a given type. The uri must not be null. */
+ private Builder(int type, Uri uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("uri must not be null");
+ }
+ mType = type;
+ mUri = uri;
+ }
+
+ /** Create a ContentProviderOperation from this {@link Builder}. */
+ public ContentProviderOperation build() {
+ if (mType == TYPE_UPDATE || mType == TYPE_ASSERT) {
+ if ((mValues == null || mValues.size() == 0)
+ && (mValuesBackReferences == null || mValuesBackReferences.size() == 0)) {
+ throw new IllegalArgumentException("Empty values");
+ }
+ }
+ return new ContentProviderOperation(this);
+ }
+
+ /**
+ * Add a {@link ContentValues} of back references. The key is the name of the column
+ * and the value is an integer that is the index of the previous result whose
+ * value should be used for the column. The value is added as a {@link String}.
+ * A column value from the back references takes precedence over a value specified in
+ * {@link #withValues}.
+ * This can only be used with builders of type insert, update, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withValueBackReferences(ContentValues backReferences) {
+ if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException(
+ "only inserts, updates, and asserts can have value back-references");
+ }
+ mValuesBackReferences = backReferences;
+ return this;
+ }
+
+ /**
+ * Add a ContentValues back reference.
+ * A column value from the back references takes precedence over a value specified in
+ * {@link #withValues}.
+ * This can only be used with builders of type insert, update, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withValueBackReference(String key, int previousResult) {
+ if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException(
+ "only inserts, updates, and asserts can have value back-references");
+ }
+ if (mValuesBackReferences == null) {
+ mValuesBackReferences = new ContentValues();
+ }
+ mValuesBackReferences.put(key, previousResult);
+ return this;
+ }
+
+ /**
+ * Add a back references as a selection arg. Any value at that index of the selection arg
+ * that was specified by {@link #withSelection} will be overwritten.
+ * This can only be used with builders of type update, delete, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withSelectionBackReference(int selectionArgIndex, int previousResult) {
+ if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException("only updates, deletes, and asserts "
+ + "can have selection back-references");
+ }
+ if (mSelectionArgsBackReferences == null) {
+ mSelectionArgsBackReferences = new HashMap<Integer, Integer>();
+ }
+ mSelectionArgsBackReferences.put(selectionArgIndex, previousResult);
+ return this;
+ }
+
+ /**
+ * The ContentValues to use. This may be null. These values may be overwritten by
+ * the corresponding value specified by {@link #withValueBackReference} or by
+ * future calls to {@link #withValues} or {@link #withValue}.
+ * This can only be used with builders of type insert, update, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withValues(ContentValues values) {
+ if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException(
+ "only inserts, updates, and asserts can have values");
+ }
+ if (mValues == null) {
+ mValues = new ContentValues();
+ }
+ mValues.putAll(values);
+ return this;
+ }
+
+ /**
+ * A value to insert or update. This value may be overwritten by
+ * the corresponding value specified by {@link #withValueBackReference}.
+ * This can only be used with builders of type insert, update, or assert.
+ * @param key the name of this value
+ * @param value the value itself. the type must be acceptable for insertion by
+ * {@link ContentValues#put}
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withValue(String key, Object value) {
+ if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException("only inserts and updates can have values");
+ }
+ if (mValues == null) {
+ mValues = new ContentValues();
+ }
+ if (value == null) {
+ mValues.putNull(key);
+ } else if (value instanceof String) {
+ mValues.put(key, (String) value);
+ } else if (value instanceof Byte) {
+ mValues.put(key, (Byte) value);
+ } else if (value instanceof Short) {
+ mValues.put(key, (Short) value);
+ } else if (value instanceof Integer) {
+ mValues.put(key, (Integer) value);
+ } else if (value instanceof Long) {
+ mValues.put(key, (Long) value);
+ } else if (value instanceof Float) {
+ mValues.put(key, (Float) value);
+ } else if (value instanceof Double) {
+ mValues.put(key, (Double) value);
+ } else if (value instanceof Boolean) {
+ mValues.put(key, (Boolean) value);
+ } else if (value instanceof byte[]) {
+ mValues.put(key, (byte[]) value);
+ } else {
+ throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
+ }
+ return this;
+ }
+
+ /**
+ * The selection and arguments to use. An occurrence of '?' in the selection will be
+ * replaced with the corresponding occurence of the selection argument. Any of the
+ * selection arguments may be overwritten by a selection argument back reference as
+ * specified by {@link #withSelectionBackReference}.
+ * This can only be used with builders of type update, delete, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withSelection(String selection, String[] selectionArgs) {
+ if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException(
+ "only updates, deletes, and asserts can have selections");
+ }
+ mSelection = selection;
+ mSelectionArgs = selectionArgs;
+ return this;
+ }
+
+ /**
+ * If set then if the number of rows affected by this operation do not match
+ * this count {@link OperationApplicationException} will be throw.
+ * This can only be used with builders of type update, delete, or assert.
+ * @return this builder, to allow for chaining.
+ */
+ public Builder withExpectedCount(int count) {
+ if (mType != TYPE_UPDATE && mType != TYPE_DELETE && mType != TYPE_ASSERT) {
+ throw new IllegalArgumentException(
+ "only updates, deletes, and asserts can have expected counts");
+ }
+ mExpectedCount = count;
+ return this;
+ }
+
+ public Builder withYieldAllowed(boolean yieldAllowed) {
+ mYieldAllowed = yieldAllowed;
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java
new file mode 100644
index 0000000..5d188ef
--- /dev/null
+++ b/core/java/android/content/ContentProviderResult.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.net.Uri;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Contains the result of the application of a {@link ContentProviderOperation}. It is guaranteed
+ * to have exactly one of {@link #uri} or {@link #count} set.
+ */
+public class ContentProviderResult implements Parcelable {
+ public final Uri uri;
+ public final Integer count;
+
+ public ContentProviderResult(Uri uri) {
+ if (uri == null) throw new IllegalArgumentException("uri must not be null");
+ this.uri = uri;
+ this.count = null;
+ }
+
+ public ContentProviderResult(int count) {
+ this.count = count;
+ this.uri = null;
+ }
+
+ public ContentProviderResult(Parcel source) {
+ int type = source.readInt();
+ if (type == 1) {
+ count = source.readInt();
+ uri = null;
+ } else {
+ count = null;
+ uri = Uri.CREATOR.createFromParcel(source);
+ }
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ if (uri == null) {
+ dest.writeInt(1);
+ dest.writeInt(count);
+ } else {
+ dest.writeInt(2);
+ uri.writeToParcel(dest, 0);
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ContentProviderResult> CREATOR =
+ new Creator<ContentProviderResult>() {
+ public ContentProviderResult createFromParcel(Parcel source) {
+ return new ContentProviderResult(source);
+ }
+
+ public ContentProviderResult[] newArray(int size) {
+ return new ContentProviderResult[size];
+ }
+ };
+
+ public String toString() {
+ if (uri != null) {
+ return "ContentProviderResult(uri=" + uri.toString() + ")";
+ }
+ return "ContentProviderResult(count=" + count + ")";
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 74144fc..88a4d02 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -30,6 +30,7 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
+import android.accounts.Account;
import android.util.Config;
import android.util.Log;
@@ -40,19 +41,40 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
+import java.util.ArrayList;
/**
* This class provides applications access to the content model.
*/
public abstract class ContentResolver {
- public final static String SYNC_EXTRAS_ACCOUNT = "account";
+ /**
+ * @deprecated instead use
+ * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
+ */
+ @Deprecated
+ public static final String SYNC_EXTRAS_ACCOUNT = "account";
public static final String SYNC_EXTRAS_EXPEDITED = "expedited";
+ /**
+ * @deprecated instead use
+ * {@link #SYNC_EXTRAS_MANUAL}
+ */
+ @Deprecated
public static final String SYNC_EXTRAS_FORCE = "force";
+ public static final String SYNC_EXTRAS_MANUAL = "force";
public static final String SYNC_EXTRAS_UPLOAD = "upload";
public static final String SYNC_EXTRAS_OVERRIDE_TOO_MANY_DELETIONS = "deletions_override";
public static final String SYNC_EXTRAS_DISCARD_LOCAL_DELETIONS = "discard_deletions";
+ /**
+ * Set by the SyncManager to request that the SyncAdapter initialize itself for
+ * the given account/authority pair. One required initialization step is to
+ * ensure that {@link #setIsSyncable(android.accounts.Account, String, int)} has been
+ * called with a >= 0 value. When this flag is set the SyncAdapter does not need to
+ * do a full sync, though it is allowed to do so.
+ */
+ public static final String SYNC_EXTRAS_INITIALIZE = "initialize";
+
public static final String SCHEME_CONTENT = "content";
public static final String SCHEME_ANDROID_RESOURCE = "android.resource";
public static final String SCHEME_FILE = "file";
@@ -88,7 +110,35 @@ public abstract class ContentResolver {
* in the cursor is the same.
*/
public static final String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
-
+
+ /** @hide */
+ public static final int SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
+ /** @hide */
+ public static final int SYNC_ERROR_AUTHENTICATION = 2;
+ /** @hide */
+ public static final int SYNC_ERROR_IO = 3;
+ /** @hide */
+ public static final int SYNC_ERROR_PARSE = 4;
+ /** @hide */
+ public static final int SYNC_ERROR_CONFLICT = 5;
+ /** @hide */
+ public static final int SYNC_ERROR_TOO_MANY_DELETIONS = 6;
+ /** @hide */
+ public static final int SYNC_ERROR_TOO_MANY_RETRIES = 7;
+ /** @hide */
+ public static final int SYNC_ERROR_INTERNAL = 8;
+
+ /** @hide */
+ public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1<<0;
+ /** @hide */
+ public static final int SYNC_OBSERVER_TYPE_PENDING = 1<<1;
+ /** @hide */
+ public static final int SYNC_OBSERVER_TYPE_ACTIVE = 1<<2;
+ /** @hide */
+ public static final int SYNC_OBSERVER_TYPE_STATUS = 1<<3;
+ /** @hide */
+ public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
+
public ContentResolver(Context context) {
mContext = context;
}
@@ -166,6 +216,94 @@ public abstract class ContentResolver {
}
/**
+ * EntityIterator wrapper that releases the associated ContentProviderClient when the
+ * iterator is closed.
+ */
+ private class EntityIteratorWrapper implements EntityIterator {
+ private final EntityIterator mInner;
+ private final ContentProviderClient mClient;
+ private volatile boolean mClientReleased;
+
+ EntityIteratorWrapper(EntityIterator inner, ContentProviderClient client) {
+ mInner = inner;
+ mClient = client;
+ mClientReleased = false;
+ }
+
+ public boolean hasNext() throws RemoteException {
+ if (mClientReleased) {
+ throw new IllegalStateException("this iterator is already closed");
+ }
+ return mInner.hasNext();
+ }
+
+ public Entity next() throws RemoteException {
+ if (mClientReleased) {
+ throw new IllegalStateException("this iterator is already closed");
+ }
+ return mInner.next();
+ }
+
+ public void reset() throws RemoteException {
+ if (mClientReleased) {
+ throw new IllegalStateException("this iterator is already closed");
+ }
+ mInner.reset();
+ }
+
+ public void close() {
+ mClient.release();
+ mInner.close();
+ mClientReleased = true;
+ }
+
+ protected void finalize() throws Throwable {
+ if (!mClientReleased) {
+ mClient.release();
+ }
+ super.finalize();
+ }
+ }
+
+ /**
+ * Query the given URI, returning an {@link EntityIterator} over the result set.
+ *
+ * @param uri The URI, using the content:// scheme, for the content to
+ * retrieve.
+ * @param selection A filter declaring which rows to return, formatted as an
+ * SQL WHERE clause (excluding the WHERE itself). Passing null will
+ * return all rows for the given URI.
+ * @param selectionArgs You may include ?s in selection, which will be
+ * replaced by the values from selectionArgs, in the order that they
+ * appear in the selection. The values will be bound as Strings.
+ * @param sortOrder How to order the rows, formatted as an SQL ORDER BY
+ * clause (excluding the ORDER BY itself). Passing null will use the
+ * default sort order, which may be unordered.
+ * @return An EntityIterator object
+ * @throws RemoteException thrown if a RemoteException is encountered while attempting
+ * to communicate with a remote provider.
+ * @throws IllegalArgumentException thrown if there is no provider that matches the uri
+ */
+ public final EntityIterator queryEntities(Uri uri,
+ String selection, String[] selectionArgs, String sortOrder) throws RemoteException {
+ ContentProviderClient provider = acquireContentProviderClient(uri);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown URL " + uri);
+ }
+ try {
+ EntityIterator entityIterator =
+ provider.queryEntities(uri, selection, selectionArgs, sortOrder);
+ return new EntityIteratorWrapper(entityIterator, provider);
+ } catch(RuntimeException e) {
+ provider.release();
+ throw e;
+ } catch(RemoteException e) {
+ provider.release();
+ throw e;
+ }
+ }
+
+ /**
* Open a stream on to the content associated with a content URI. If there
* is no data associated with the URI, FileNotFoundException is thrown.
*
@@ -485,6 +623,36 @@ public abstract class ContentResolver {
}
/**
+ * Applies each of the {@link ContentProviderOperation} objects and returns an array
+ * of their results. Passes through OperationApplicationException, which may be thrown
+ * by the call to {@link ContentProviderOperation#apply}.
+ * If all the applications succeed then a {@link ContentProviderResult} array with the
+ * same number of elements as the operations will be returned. It is implementation-specific
+ * how many, if any, operations will have been successfully applied if a call to
+ * apply results in a {@link OperationApplicationException}.
+ * @param authority the authority of the ContentProvider to which this batch should be applied
+ * @param operations the operations to apply
+ * @return the results of the applications
+ * @throws OperationApplicationException thrown if an application fails.
+ * See {@link ContentProviderOperation#apply} for more information.
+ * @throws RemoteException thrown if a RemoteException is encountered while attempting
+ * to communicate with a remote provider.
+ */
+ public ContentProviderResult[] applyBatch(String authority,
+ ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
+ ContentProviderClient provider = acquireContentProviderClient(authority);
+ if (provider == null) {
+ throw new IllegalArgumentException("Unknown authority " + authority);
+ }
+ try {
+ return provider.applyBatch(operations);
+ } finally {
+ provider.release();
+ }
+ }
+
+ /**
* Inserts multiple rows into a table at the given URL.
*
* This function make no guarantees about the atomicity of the insertions.
@@ -592,6 +760,46 @@ public abstract class ContentResolver {
}
/**
+ * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+ * that services the content at uri, starting the provider if necessary. Returns
+ * null if there is no provider associated wih the uri. The caller must indicate that they are
+ * done with the provider by calling {@link ContentProviderClient#release} which will allow
+ * the system to release the provider it it determines that there is no other reason for
+ * keeping it active.
+ * @param uri specifies which provider should be acquired
+ * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+ * that services the content at uri or null if there isn't one.
+ */
+ public final ContentProviderClient acquireContentProviderClient(Uri uri) {
+ IContentProvider provider = acquireProvider(uri);
+ if (provider != null) {
+ return new ContentProviderClient(this, provider);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+ * with the authority of name, starting the provider if necessary. Returns
+ * null if there is no provider associated wih the uri. The caller must indicate that they are
+ * done with the provider by calling {@link ContentProviderClient#release} which will allow
+ * the system to release the provider it it determines that there is no other reason for
+ * keeping it active.
+ * @param name specifies which provider should be acquired
+ * @return a {@link ContentProviderClient} that is associated with the {@link ContentProvider}
+ * with the authority of name or null if there isn't one.
+ */
+ public final ContentProviderClient acquireContentProviderClient(String name) {
+ IContentProvider provider = acquireProvider(name);
+ if (provider != null) {
+ return new ContentProviderClient(this, provider);
+ }
+
+ return null;
+ }
+
+ /**
* Register an observer class that gets callbacks when data identified by a
* given content URI changes.
*
@@ -676,11 +884,43 @@ public abstract class ContentResolver {
*
* @param uri the uri of the provider to sync or null to sync all providers.
* @param extras any extras to pass to the SyncAdapter.
+ * @deprecated instead use
+ * {@link #requestSync(android.accounts.Account, String, android.os.Bundle)}
*/
+ @Deprecated
public void startSync(Uri uri, Bundle extras) {
+ Account account = null;
+ if (extras != null) {
+ String accountName = extras.getString(SYNC_EXTRAS_ACCOUNT);
+ if (!TextUtils.isEmpty(accountName)) {
+ account = new Account(accountName, "com.google.GAIA");
+ }
+ extras.remove(SYNC_EXTRAS_ACCOUNT);
+ }
+ requestSync(account, uri != null ? uri.getAuthority() : null, extras);
+ }
+
+ /**
+ * Start an asynchronous sync operation. If you want to monitor the progress
+ * of the sync you may register a SyncObserver. Only values of the following
+ * types may be used in the extras bundle:
+ * <ul>
+ * <li>Integer</li>
+ * <li>Long</li>
+ * <li>Boolean</li>
+ * <li>Float</li>
+ * <li>Double</li>
+ * <li>String</li>
+ * </ul>
+ *
+ * @param account which account should be synced
+ * @param authority which authority should be synced
+ * @param extras any extras to pass to the SyncAdapter.
+ */
+ public static void requestSync(Account account, String authority, Bundle extras) {
validateSyncExtrasBundle(extras);
try {
- getContentService().startSync(uri, extras);
+ getContentService().requestSync(account, authority, extras);
} catch (RemoteException e) {
}
}
@@ -694,6 +934,7 @@ public abstract class ContentResolver {
* <li>Float</li>
* <li>Double</li>
* <li>String</li>
+ * <li>Account</li>
* <li>null</li>
* </ul>
* @param extras the Bundle to check
@@ -709,6 +950,7 @@ public abstract class ContentResolver {
if (value instanceof Float) continue;
if (value instanceof Double) continue;
if (value instanceof String) continue;
+ if (value instanceof Account) continue;
throw new IllegalArgumentException("unexpected value type: "
+ value.getClass().getName());
}
@@ -719,13 +961,211 @@ public abstract class ContentResolver {
}
}
+ /**
+ * Cancel any active or pending syncs that match the Uri. If the uri is null then
+ * all syncs will be canceled.
+ *
+ * @param uri the uri of the provider to sync or null to sync all providers.
+ * @deprecated instead use {@link #cancelSync(android.accounts.Account, String)}
+ */
+ @Deprecated
public void cancelSync(Uri uri) {
+ cancelSync(null /* all accounts */, uri != null ? uri.getAuthority() : null);
+ }
+
+ /**
+ * Cancel any active or pending syncs that match account and authority. The account and
+ * authority can each independently be set to null, which means that syncs with any account
+ * or authority, respectively, will match.
+ *
+ * @param account filters the syncs that match by this account
+ * @param authority filters the syncs that match by this authority
+ */
+ public static void cancelSync(Account account, String authority) {
+ try {
+ getContentService().cancelSync(account, authority);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Get information about the SyncAdapters that are known to the system.
+ * @return an array of SyncAdapters that have registered with the system
+ */
+ public static SyncAdapterType[] getSyncAdapterTypes() {
+ try {
+ return getContentService().getSyncAdapterTypes();
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Check if the provider should be synced when a network tickle is received
+ *
+ * @param account the account whose setting we are querying
+ * @param authority the provider whose setting we are querying
+ * @return true if the provider should be synced when a network tickle is received
+ */
+ public static boolean getSyncAutomatically(Account account, String authority) {
+ try {
+ return getContentService().getSyncAutomatically(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Set whether or not the provider is synced when it receives a network tickle.
+ *
+ * @param account the account whose setting we are querying
+ * @param authority the provider whose behavior is being controlled
+ * @param sync true if the provider should be synced when tickles are received for it
+ */
+ public static void setSyncAutomatically(Account account, String authority, boolean sync) {
+ try {
+ getContentService().setSyncAutomatically(account, authority, sync);
+ } catch (RemoteException e) {
+ // exception ignored; if this is thrown then it means the runtime is in the midst of
+ // being restarted
+ }
+ }
+
+ /**
+ * Check if this account/provider is syncable.
+ * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
+ */
+ public static int getIsSyncable(Account account, String authority) {
+ try {
+ return getContentService().getIsSyncable(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Set whether this account/provider is syncable.
+ * @param syncable >0 denotes syncable, 0 means not syncable, <0 means unknown
+ */
+ public static void setIsSyncable(Account account, String authority, int syncable) {
+ try {
+ getContentService().setIsSyncable(account, authority, syncable);
+ } catch (RemoteException e) {
+ // exception ignored; if this is thrown then it means the runtime is in the midst of
+ // being restarted
+ }
+ }
+
+ /**
+ * Gets the master auto-sync setting that applies to all the providers and accounts.
+ * If this is false then the per-provider auto-sync setting is ignored.
+ *
+ * @return the master auto-sync setting that applies to all the providers and accounts
+ */
+ public static boolean getMasterSyncAutomatically() {
+ try {
+ return getContentService().getMasterSyncAutomatically();
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Sets the master auto-sync setting that applies to all the providers and accounts.
+ * If this is false then the per-provider auto-sync setting is ignored.
+ *
+ * @param sync the master auto-sync setting that applies to all the providers and accounts
+ */
+ public static void setMasterSyncAutomatically(boolean sync) {
+ try {
+ getContentService().setMasterSyncAutomatically(sync);
+ } catch (RemoteException e) {
+ // exception ignored; if this is thrown then it means the runtime is in the midst of
+ // being restarted
+ }
+ }
+
+ /**
+ * Returns true if there is currently a sync operation for the given
+ * account or authority in the pending list, or actively being processed.
+ * @param account the account whose setting we are querying
+ * @param authority the provider whose behavior is being queried
+ * @return true if a sync is active for the given account or authority.
+ */
+ public static boolean isSyncActive(Account account, String authority) {
+ try {
+ return getContentService().isSyncActive(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * If a sync is active returns the information about it, otherwise returns false.
+ * @return the ActiveSyncInfo for the currently active sync or null if one is not active.
+ * @hide
+ */
+ public static ActiveSyncInfo getActiveSync() {
try {
- getContentService().cancelSync(uri);
+ return getContentService().getActiveSync();
} catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
}
}
+ /**
+ * Returns the status that matches the authority.
+ * @param account the account whose setting we are querying
+ * @param authority the provider whose behavior is being queried
+ * @return the SyncStatusInfo for the authority, or null if none exists
+ * @hide
+ */
+ public static SyncStatusInfo getSyncStatus(Account account, String authority) {
+ try {
+ return getContentService().getSyncStatus(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ /**
+ * Return true if the pending status is true of any matching authorities.
+ * @param account the account whose setting we are querying
+ * @param authority the provider whose behavior is being queried
+ * @return true if there is a pending sync with the matching account and authority
+ */
+ public static boolean isSyncPending(Account account, String authority) {
+ try {
+ return getContentService().isSyncPending(account, authority);
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ public static Object addStatusChangeListener(int mask, final SyncStatusObserver callback) {
+ try {
+ ISyncStatusObserver.Stub observer = new ISyncStatusObserver.Stub() {
+ public void onStatusChanged(int which) throws RemoteException {
+ callback.onStatusChanged(which);
+ }
+ };
+ getContentService().addStatusChangeListener(mask, observer);
+ return observer;
+ } catch (RemoteException e) {
+ throw new RuntimeException("the ContentService should always be reachable", e);
+ }
+ }
+
+ public static void removeStatusChangeListener(Object handle) {
+ try {
+ getContentService().removeStatusChangeListener((ISyncStatusObserver.Stub) handle);
+ } catch (RemoteException e) {
+ // exception ignored; if this is thrown then it means the runtime is in the midst of
+ // being restarted
+ }
+ }
+
+
private final class CursorWrapperInner extends CursorWrapper {
private IContentProvider mContentProvider;
public static final String TAG="CursorWrapperInner";
diff --git a/core/java/android/content/ContentService.java b/core/java/android/content/ContentService.java
index 6cd2c54..974a667 100644
--- a/core/java/android/content/ContentService.java
+++ b/core/java/android/content/ContentService.java
@@ -16,6 +16,7 @@
package android.content;
+import android.accounts.Account;
import android.database.IContentObserver;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
@@ -160,7 +161,9 @@ public final class ContentService extends IContentService.Stub {
}
if (syncToNetwork) {
SyncManager syncManager = getSyncManager();
- if (syncManager != null) syncManager.scheduleLocalSync(uri);
+ if (syncManager != null) {
+ syncManager.scheduleLocalSync(null /* all accounts */, uri.getAuthority());
+ }
}
} finally {
restoreCallingIdentity(identityToken);
@@ -186,14 +189,17 @@ public final class ContentService extends IContentService.Stub {
}
}
- public void startSync(Uri url, Bundle extras) {
+ public void requestSync(Account account, String authority, Bundle extras) {
ContentResolver.validateSyncExtrasBundle(extras);
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
- if (syncManager != null) syncManager.startSync(url, extras);
+ if (syncManager != null) {
+ syncManager.scheduleSync(account, authority, extras, 0 /* no delay */,
+ false /* onlyThoseWithUnkownSyncableState */);
+ }
} finally {
restoreCallingIdentity(identityToken);
}
@@ -201,34 +207,50 @@ public final class ContentService extends IContentService.Stub {
/**
* Clear all scheduled sync operations that match the uri and cancel the active sync
- * if it matches the uri. If the uri is null, clear all scheduled syncs and cancel
- * the active one, if there is one.
- * @param uri Filter on the sync operations to cancel, or all if null.
+ * if they match the authority and account, if they are present.
+ * @param account filter the pending and active syncs to cancel using this account
+ * @param authority filter the pending and active syncs to cancel using this authority
*/
- public void cancelSync(Uri uri) {
+ public void cancelSync(Account account, String authority) {
// This makes it so that future permission checks will be in the context of this
// process rather than the caller's process. We will restore this before returning.
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.clearScheduledSyncOperations(uri);
- syncManager.cancelActiveSync(uri);
+ syncManager.clearScheduledSyncOperations(account, authority);
+ syncManager.cancelActiveSync(account, authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
- public boolean getSyncProviderAutomatically(String providerName) {
+ /**
+ * Get information about the SyncAdapters that are known to the system.
+ * @return an array of SyncAdapters that have registered with the system
+ */
+ public SyncAdapterType[] getSyncAdapterTypes() {
+ // This makes it so that future permission checks will be in the context of this
+ // process rather than the caller's process. We will restore this before returning.
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncManager syncManager = getSyncManager();
+ return syncManager.getSyncAdapterTypes();
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public boolean getSyncAutomatically(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getSyncProviderAutomatically(
- null, providerName);
+ return syncManager.getSyncStorageEngine().getSyncAutomatically(
+ account, providerName);
}
} finally {
restoreCallingIdentity(identityToken);
@@ -236,51 +258,82 @@ public final class ContentService extends IContentService.Stub {
return false;
}
- public void setSyncProviderAutomatically(String providerName, boolean sync) {
+ public void setSyncAutomatically(Account account, String providerName, boolean sync) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().setSyncProviderAutomatically(
- null, providerName, sync);
+ syncManager.getSyncStorageEngine().setSyncAutomatically(
+ account, providerName, sync);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
- public boolean getListenForNetworkTickles() {
+ public int getIsSyncable(Account account, String providerName) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
"no permission to read the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getListenForNetworkTickles();
+ return syncManager.getSyncStorageEngine().getIsSyncable(
+ account, providerName);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ return -1;
+ }
+
+ public void setIsSyncable(Account account, String providerName, int syncable) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
+ "no permission to write the sync settings");
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncManager syncManager = getSyncManager();
+ if (syncManager != null) {
+ syncManager.getSyncStorageEngine().setIsSyncable(
+ account, providerName, syncable);
+ }
+ } finally {
+ restoreCallingIdentity(identityToken);
+ }
+ }
+
+ public boolean getMasterSyncAutomatically() {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_SETTINGS,
+ "no permission to read the sync settings");
+ long identityToken = clearCallingIdentity();
+ try {
+ SyncManager syncManager = getSyncManager();
+ if (syncManager != null) {
+ return syncManager.getSyncStorageEngine().getMasterSyncAutomatically();
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
-
- public void setListenForNetworkTickles(boolean flag) {
+
+ public void setMasterSyncAutomatically(boolean flag) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
"no permission to write the sync settings");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().setListenForNetworkTickles(flag);
+ syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
- public boolean isSyncActive(String account, String authority) {
+ public boolean isSyncActive(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
@@ -295,7 +348,7 @@ public final class ContentService extends IContentService.Stub {
}
return false;
}
-
+
public ActiveSyncInfo getActiveSync() {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
@@ -310,65 +363,62 @@ public final class ContentService extends IContentService.Stub {
}
return null;
}
-
- public SyncStatusInfo getStatusByAuthority(String authority) {
+
+ public SyncStatusInfo getSyncStatus(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().getStatusByAuthority(
- authority);
+ return syncManager.getSyncStorageEngine().getStatusByAccountAndAuthority(
+ account, authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
return null;
}
-
- public boolean isAuthorityPending(String account, String authority) {
+
+ public boolean isSyncPending(Account account, String authority) {
mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_SYNC_STATS,
"no permission to read the sync stats");
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- return syncManager.getSyncStorageEngine().isAuthorityPending(
- account, authority);
+ return syncManager.getSyncStorageEngine().isSyncPending(account, authority);
}
} finally {
restoreCallingIdentity(identityToken);
}
return false;
}
-
+
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().addStatusChangeListener(
- mask, callback);
+ syncManager.getSyncStorageEngine().addStatusChangeListener(mask, callback);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
-
+
public void removeStatusChangeListener(ISyncStatusObserver callback) {
long identityToken = clearCallingIdentity();
try {
SyncManager syncManager = getSyncManager();
if (syncManager != null) {
- syncManager.getSyncStorageEngine().removeStatusChangeListener(
- callback);
+ syncManager.getSyncStorageEngine().removeStatusChangeListener(callback);
}
} finally {
restoreCallingIdentity(identityToken);
}
}
-
+
public static IContentService main(Context context, boolean factoryTest) {
ContentService service = new ContentService(context, factoryTest);
ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 25b5de3..60551b8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -488,90 +488,52 @@ public abstract class Context {
public abstract String[] databaseList();
/**
- * Like {@link #peekWallpaper}, but always returns a valid Drawable. If
- * no wallpaper is set, the system default wallpaper is returned.
- *
- * @return Returns a Drawable object that will draw the wallpaper.
+ * @deprecated Use {@link android.app.WallpaperManager#getDrawable
+ * WallpaperManager.get()} instead.
*/
+ @Deprecated
public abstract Drawable getWallpaper();
/**
- * Retrieve the current system wallpaper. This is returned as an
- * abstract Drawable that you can install in a View to display whatever
- * wallpaper the user has currently set. If there is no wallpaper set,
- * a null pointer is returned.
- *
- * @return Returns a Drawable object that will draw the wallpaper or a
- * null pointer if these is none.
+ * @deprecated Use {@link android.app.WallpaperManager#peekDrawable
+ * WallpaperManager.peek()} instead.
*/
+ @Deprecated
public abstract Drawable peekWallpaper();
/**
- * Returns the desired minimum width for the wallpaper. Callers of
- * {@link #setWallpaper(android.graphics.Bitmap)} or
- * {@link #setWallpaper(java.io.InputStream)} should check this value
- * beforehand to make sure the supplied wallpaper respects the desired
- * minimum width.
- *
- * If the returned value is <= 0, the caller should use the width of
- * the default display instead.
- *
- * @return The desired minimum width for the wallpaper. This value should
- * be honored by applications that set the wallpaper but it is not
- * mandatory.
+ * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumWidth()
+ * WallpaperManager.getDesiredMinimumWidth()} instead.
*/
+ @Deprecated
public abstract int getWallpaperDesiredMinimumWidth();
/**
- * Returns the desired minimum height for the wallpaper. Callers of
- * {@link #setWallpaper(android.graphics.Bitmap)} or
- * {@link #setWallpaper(java.io.InputStream)} should check this value
- * beforehand to make sure the supplied wallpaper respects the desired
- * minimum height.
- *
- * If the returned value is <= 0, the caller should use the height of
- * the default display instead.
- *
- * @return The desired minimum height for the wallpaper. This value should
- * be honored by applications that set the wallpaper but it is not
- * mandatory.
+ * @deprecated Use {@link android.app.WallpaperManager#getDesiredMinimumHeight()
+ * WallpaperManager.getDesiredMinimumHeight()} instead.
*/
+ @Deprecated
public abstract int getWallpaperDesiredMinimumHeight();
/**
- * Change the current system wallpaper to a bitmap. The given bitmap is
- * converted to a PNG and stored as the wallpaper. On success, the intent
- * {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast.
- *
- * @param bitmap The bitmap to save.
- *
- * @throws IOException If an error occurs reverting to the default
- * wallpaper.
+ * @deprecated Use {@link android.app.WallpaperManager#setBitmap(Bitmap)
+ * WallpaperManager.set()} instead.
*/
+ @Deprecated
public abstract void setWallpaper(Bitmap bitmap) throws IOException;
/**
- * Change the current system wallpaper to a specific byte stream. The
- * give InputStream is copied into persistent storage and will now be
- * used as the wallpaper. Currently it must be either a JPEG or PNG
- * image. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
- * is broadcast.
- *
- * @param data A stream containing the raw data to install as a wallpaper.
- *
- * @throws IOException If an error occurs reverting to the default
- * wallpaper.
+ * @deprecated Use {@link android.app.WallpaperManager#setStream(InputStream)
+ * WallpaperManager.set()} instead.
*/
+ @Deprecated
public abstract void setWallpaper(InputStream data) throws IOException;
/**
- * Remove any currently set wallpaper, reverting to the system's default
- * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED}
- * is broadcast.
- *
- * @throws IOException If an error occurs reverting to the default
- * wallpaper.
+ * @deprecated Use {@link android.app.WallpaperManager#clear
+ * WallpaperManager.clear()} instead.
*/
+ @Deprecated
public abstract void clearWallpaper() throws IOException;
/**
@@ -1110,6 +1072,16 @@ public abstract class Context {
public static final String LAYOUT_INFLATER_SERVICE = "layout_inflater";
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.accounts.AccountManager} for receiving intents at a
+ * time of your choosing.
+ * TODO STOPSHIP perform a final review of the the account apis before shipping
+ *
+ * @see #getSystemService
+ * @see android.accounts.AccountManager
+ */
+ public static final String ACCOUNT_SERVICE = "account";
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.app.ActivityManager} for interacting with the global
* system state.
*
@@ -1179,11 +1151,10 @@ public abstract class Context {
public static final String SENSOR_SERVICE = "sensor";
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.bluetooth.BluetoothDevice} for interacting with Bluetooth.
+ * android.bluetooth.BluetoothAdapter} for using Bluetooth.
*
* @see #getSystemService
- * @see android.bluetooth.BluetoothDevice
- * @hide
+ * @see android.bluetooth.BluetoothAdapter
*/
public static final String BLUETOOTH_SERVICE = "bluetooth";
/**
diff --git a/core/java/android/content/Entity.aidl b/core/java/android/content/Entity.aidl
new file mode 100644
index 0000000..fb201f3
--- /dev/null
+++ b/core/java/android/content/Entity.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/content/Entity.aidl
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content;
+
+parcelable Entity;
diff --git a/core/java/android/content/Entity.java b/core/java/android/content/Entity.java
new file mode 100644
index 0000000..325dce5
--- /dev/null
+++ b/core/java/android/content/Entity.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Objects that pass through the ContentProvider and ContentResolver's methods that deal with
+ * Entities must implement this abstract base class and thus themselves be Parcelable.
+ */
+public final class Entity implements Parcelable {
+ final private ContentValues mValues;
+ final private ArrayList<NamedContentValues> mSubValues;
+
+ public Entity(ContentValues values) {
+ mValues = values;
+ mSubValues = new ArrayList<NamedContentValues>();
+ }
+
+ public ContentValues getEntityValues() {
+ return mValues;
+ }
+
+ public ArrayList<NamedContentValues> getSubValues() {
+ return mSubValues;
+ }
+
+ public void addSubValue(Uri uri, ContentValues values) {
+ mSubValues.add(new Entity.NamedContentValues(uri, values));
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ mValues.writeToParcel(dest, 0);
+ dest.writeInt(mSubValues.size());
+ for (NamedContentValues value : mSubValues) {
+ value.uri.writeToParcel(dest, 0);
+ value.values.writeToParcel(dest, 0);
+ }
+ }
+
+ private Entity(Parcel source) {
+ mValues = ContentValues.CREATOR.createFromParcel(source);
+ final int numValues = source.readInt();
+ mSubValues = new ArrayList<NamedContentValues>(numValues);
+ for (int i = 0; i < numValues; i++) {
+ final Uri uri = Uri.CREATOR.createFromParcel(source);
+ final ContentValues values = ContentValues.CREATOR.createFromParcel(source);
+ mSubValues.add(new NamedContentValues(uri, values));
+ }
+ }
+
+ public static final Creator<Entity> CREATOR = new Creator<Entity>() {
+ public Entity createFromParcel(Parcel source) {
+ return new Entity(source);
+ }
+
+ public Entity[] newArray(int size) {
+ return new Entity[size];
+ }
+ };
+
+ public static class NamedContentValues {
+ public final Uri uri;
+ public final ContentValues values;
+
+ public NamedContentValues(Uri uri, ContentValues values) {
+ this.uri = uri;
+ this.values = values;
+ }
+ }
+
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Entity: ").append(getEntityValues());
+ for (Entity.NamedContentValues namedValue : getSubValues()) {
+ sb.append("\n ").append(namedValue.uri);
+ sb.append("\n -> ").append(namedValue.values);
+ }
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/content/EntityIterator.java b/core/java/android/content/EntityIterator.java
new file mode 100644
index 0000000..3cc1040
--- /dev/null
+++ b/core/java/android/content/EntityIterator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.RemoteException;
+
+public interface EntityIterator {
+ /**
+ * Returns whether there are more elements to iterate, i.e. whether the
+ * iterator is positioned in front of an element.
+ *
+ * @return {@code true} if there are more elements, {@code false} otherwise.
+ * @see #next
+ * @since Android 1.0
+ */
+ public boolean hasNext() throws RemoteException;
+
+ /**
+ * Returns the next object in the iteration, i.e. returns the element in
+ * front of the iterator and advances the iterator by one position.
+ *
+ * @return the next object.
+ * @throws java.util.NoSuchElementException
+ * if there are no more elements.
+ * @see #hasNext
+ * @since Android 1.0
+ */
+ public Entity next() throws RemoteException;
+
+ public void reset() throws RemoteException;
+
+ /**
+ * Indicates that this iterator is no longer needed and that any associated resources
+ * may be released (such as a SQLite cursor).
+ */
+ public void close();
+}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 0606956..7e5aba5 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -28,6 +28,7 @@ import android.os.IInterface;
import android.os.ParcelFileDescriptor;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* The ipc interface to talk to a content provider.
@@ -43,19 +44,25 @@ public interface IContentProvider extends IInterface {
CursorWindow window) throws RemoteException;
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException;
+ public EntityIterator queryEntities(Uri url, String selection,
+ String[] selectionArgs, String sortOrder)
+ throws RemoteException;
public String getType(Uri url) throws RemoteException;
public Uri insert(Uri url, ContentValues initialValues)
throws RemoteException;
public int bulkInsert(Uri url, ContentValues[] initialValues) throws RemoteException;
+ public Uri insertEntity(Uri uri, Entity entities) throws RemoteException;
public int delete(Uri url, String selection, String[] selectionArgs)
throws RemoteException;
public int update(Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException;
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException;
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException;
- public ISyncAdapter getSyncAdapter() throws RemoteException;
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
@@ -65,8 +72,11 @@ public interface IContentProvider extends IInterface {
static final int INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
static final int DELETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
static final int UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
- static final int GET_SYNC_ADAPTER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10;
static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12;
static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
+ static final int INSERT_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 16;
+ static final int UPDATE_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 17;
+ static final int QUERY_ENTITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 18;
+ static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
}
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index 8617d949..b0f14c1 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -16,8 +16,10 @@
package android.content;
+import android.accounts.Account;
import android.content.ActiveSyncInfo;
import android.content.ISyncStatusObserver;
+import android.content.SyncAdapterType;
import android.content.SyncStatusInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -34,15 +36,15 @@ interface IContentService {
void notifyChange(in Uri uri, IContentObserver observer,
boolean observerWantsSelfNotifications, boolean syncToNetwork);
- void startSync(in Uri url, in Bundle extras);
- void cancelSync(in Uri uri);
+ void requestSync(in Account account, String authority, in Bundle extras);
+ void cancelSync(in Account account, String authority);
/**
* Check if the provider should be synced when a network tickle is received
* @param providerName the provider whose setting we are querying
* @return true of the provider should be synced when a network tickle is received
*/
- boolean getSyncProviderAutomatically(String providerName);
+ boolean getSyncAutomatically(in Account account, String providerName);
/**
* Set whether or not the provider is synced when it receives a network tickle.
@@ -50,32 +52,50 @@ interface IContentService {
* @param providerName the provider whose behavior is being controlled
* @param sync true if the provider should be synced when tickles are received for it
*/
- void setSyncProviderAutomatically(String providerName, boolean sync);
+ void setSyncAutomatically(in Account account, String providerName, boolean sync);
- void setListenForNetworkTickles(boolean flag);
+ /**
+ * Check if this account/provider is syncable.
+ * @return >0 if it is syncable, 0 if not, and <0 if the state isn't known yet.
+ */
+ int getIsSyncable(in Account account, String providerName);
+
+ /**
+ * Set whether this account/provider is syncable.
+ * @param syncable, >0 denotes syncable, 0 means not syncable, <0 means unknown
+ */
+ void setIsSyncable(in Account account, String providerName, int syncable);
- boolean getListenForNetworkTickles();
+ void setMasterSyncAutomatically(boolean flag);
+
+ boolean getMasterSyncAutomatically();
/**
* Returns true if there is currently a sync operation for the given
* account or authority in the pending list, or actively being processed.
*/
- boolean isSyncActive(String account, String authority);
+ boolean isSyncActive(in Account account, String authority);
ActiveSyncInfo getActiveSync();
/**
+ * Returns the types of the SyncAdapters that are registered with the system.
+ * @return Returns the types of the SyncAdapters that are registered with the system.
+ */
+ SyncAdapterType[] getSyncAdapterTypes();
+
+ /**
* Returns the status that matches the authority. If there are multiples accounts for
* the authority, the one with the latest "lastSuccessTime" status is returned.
* @param authority the authority whose row should be selected
* @return the SyncStatusInfo for the authority, or null if none exists
*/
- SyncStatusInfo getStatusByAuthority(String authority);
+ SyncStatusInfo getSyncStatus(in Account account, String authority);
/**
* Return true if the pending status is true of any matching authorities.
*/
- boolean isAuthorityPending(String account, String authority);
+ boolean isSyncPending(in Account account, String authority);
void addStatusChangeListener(int mask, ISyncStatusObserver callback);
diff --git a/core/java/android/content/IEntityIterator.java b/core/java/android/content/IEntityIterator.java
new file mode 100644
index 0000000..068581e
--- /dev/null
+++ b/core/java/android/content/IEntityIterator.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * ICPC interface methods for an iterator over Entity objects.
+ * @hide
+ */
+public interface IEntityIterator extends IInterface {
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends Binder implements IEntityIterator {
+ private static final String TAG = "IEntityIterator";
+ private static final java.lang.String DESCRIPTOR = "android.content.IEntityIterator";
+
+ /** Construct the stub at attach it to the interface. */
+ public Stub() {
+ this.attachInterface(this, DESCRIPTOR);
+ }
+ /**
+ * Cast an IBinder object into an IEntityIterator interface,
+ * generating a proxy if needed.
+ */
+ public static IEntityIterator asInterface(IBinder obj) {
+ if ((obj==null)) {
+ return null;
+ }
+ IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin!=null)&&(iin instanceof IEntityIterator))) {
+ return ((IEntityIterator)iin);
+ }
+ return new IEntityIterator.Stub.Proxy(obj);
+ }
+
+ public IBinder asBinder() {
+ return this;
+ }
+
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ switch (code) {
+ case INTERFACE_TRANSACTION:
+ {
+ reply.writeString(DESCRIPTOR);
+ return true;
+ }
+
+ case TRANSACTION_hasNext:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ boolean _result;
+ try {
+ _result = this.hasNext();
+ } catch (Exception e) {
+ Log.e(TAG, "caught exception in hasNext()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ reply.writeInt(((_result)?(1):(0)));
+ return true;
+ }
+
+ case TRANSACTION_next:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ Entity entity;
+ try {
+ entity = this.next();
+ } catch (RemoteException e) {
+ Log.e(TAG, "caught exception in next()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ entity.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ return true;
+ }
+
+ case TRANSACTION_reset:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ try {
+ this.reset();
+ } catch (RemoteException e) {
+ Log.e(TAG, "caught exception in next()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ return true;
+ }
+
+ case TRANSACTION_close:
+ {
+ data.enforceInterface(DESCRIPTOR);
+ try {
+ this.close();
+ } catch (RemoteException e) {
+ Log.e(TAG, "caught exception in close()", e);
+ reply.writeException(e);
+ return true;
+ }
+ reply.writeNoException();
+ return true;
+ }
+ }
+ return super.onTransact(code, data, reply, flags);
+ }
+
+ private static class Proxy implements IEntityIterator {
+ private IBinder mRemote;
+ Proxy(IBinder remote) {
+ mRemote = remote;
+ }
+ public IBinder asBinder() {
+ return mRemote;
+ }
+ public java.lang.String getInterfaceDescriptor() {
+ return DESCRIPTOR;
+ }
+ public boolean hasNext() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ boolean _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_hasNext, _data, _reply, 0);
+ _reply.readException();
+ _result = (0!=_reply.readInt());
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ public Entity next() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_next, _data, _reply, 0);
+ _reply.readException();
+ return Entity.CREATOR.createFromParcel(_reply);
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ public void reset() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_reset, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ public void close() throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ mRemote.transact(Stub.TRANSACTION_close, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ }
+ static final int TRANSACTION_hasNext = (IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_next = (IBinder.FIRST_CALL_TRANSACTION + 1);
+ static final int TRANSACTION_close = (IBinder.FIRST_CALL_TRANSACTION + 2);
+ static final int TRANSACTION_reset = (IBinder.FIRST_CALL_TRANSACTION + 3);
+ }
+ public boolean hasNext() throws RemoteException;
+ public Entity next() throws RemoteException;
+ public void reset() throws RemoteException;
+ public void close() throws RemoteException;
+}
diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl
index 671188c..4660527 100644
--- a/core/java/android/content/ISyncAdapter.aidl
+++ b/core/java/android/content/ISyncAdapter.aidl
@@ -16,6 +16,7 @@
package android.content;
+import android.accounts.Account;
import android.os.Bundle;
import android.content.ISyncContext;
@@ -30,14 +31,17 @@ oneway interface ISyncAdapter {
*
* @param syncContext the ISyncContext used to indicate the progress of the sync. When
* the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
+ * @param authority the authority that should be synced
* @param account the account that should be synced
* @param extras SyncAdapter-specific parameters
*/
- void startSync(ISyncContext syncContext, String account, in Bundle extras);
+ void startSync(ISyncContext syncContext, String authority,
+ in Account account, in Bundle extras);
/**
* Cancel the most recently initiated sync. Due to race conditions, this may arrive
* after the ISyncContext.onFinished() for that sync was called.
+ * @param syncContext the ISyncContext that was passed to {@link #startSync}
*/
- void cancelSync();
+ void cancelSync(ISyncContext syncContext);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c62d66b..c053ace 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -75,10 +75,10 @@ import java.util.Set;
* <p>Some examples of action/data pairs are:</p>
*
* <ul>
- * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/1</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/1</i></b> -- Display
* information about the person whose identifier is "1".</p>
* </li>
- * <li> <p><b>{@link #ACTION_DIAL} <i>content://contacts/1</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_DIAL} <i>content://contacts/people/1</i></b> -- Display
* the phone dialer with the person filled in.</p>
* </li>
* <li> <p><b>{@link #ACTION_VIEW} <i>tel:123</i></b> -- Display
@@ -89,10 +89,10 @@ import java.util.Set;
* <li> <p><b>{@link #ACTION_DIAL} <i>tel:123</i></b> -- Display
* the phone dialer with the given number filled in.</p>
* </li>
- * <li> <p><b>{@link #ACTION_EDIT} <i>content://contacts/1</i></b> -- Edit
+ * <li> <p><b>{@link #ACTION_EDIT} <i>content://contacts/people/1</i></b> -- Edit
* information about the person whose identifier is "1".</p>
* </li>
- * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/</i></b> -- Display
+ * <li> <p><b>{@link #ACTION_VIEW} <i>content://contacts/people/</i></b> -- Display
* a list of people, which the user can browse through. This example is a
* typical top-level entry into the Contacts application, showing you the
* list of people. Selecting a particular person to view would result in a
@@ -156,7 +156,7 @@ import java.util.Set;
* defined in the Intent class, but applications can also define their own.
* These strings use java style scoping, to ensure they are unique -- for
* example, the standard {@link #ACTION_VIEW} is called
- * "android.app.action.VIEW".</p>
+ * "android.intent.action.VIEW".</p>
*
* <p>Put together, the set of actions, data types, categories, and extra data
* defines a language for the system allowing for the expression of phrases
@@ -347,7 +347,7 @@ import java.util.Set;
* <li> <p><b>{ action=android.app.action.MAIN,
* category=android.app.category.LAUNCHER }</b> is the actual intent
* used by the Launcher to populate its top-level list.</p>
- * <li> <p><b>{ action=android.app.action.VIEW
+ * <li> <p><b>{ action=android.intent.action.VIEW
* data=content://com.google.provider.NotePad/notes }</b>
* displays a list of all the notes under
* "content://com.google.provider.NotePad/notes", which
@@ -399,7 +399,7 @@ import java.util.Set;
* NoteEditor activity:</p>
*
* <ul>
- * <li> <p><b>{ action=android.app.action.VIEW
+ * <li> <p><b>{ action=android.intent.action.VIEW
* data=content://com.google.provider.NotePad/notes/<var>{ID}</var> }</b>
* shows the user the content of note <var>{ID}</var>.</p>
* <li> <p><b>{ action=android.app.action.EDIT
@@ -923,6 +923,16 @@ public class Intent implements Parcelable {
* get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link
* #EXTRA_STREAM} field, containing the data to be sent.
* <p>
+ * Multiple types are supported, and receivers should handle mixed types
+ * whenever possible. The right way for the receiver to check them is to
+ * use the content resolver on each URI. The intent sender should try to
+ * put the most concrete mime type in the intent type, but it can fall
+ * back to {@literal <type>/*} or {@literal *}/* as needed.
+ * <p>
+ * e.g. if you are sending image/jpg and image/jpg, the intent's type can
+ * be image/jpg, but if you are sending image/jpg and image/png, then the
+ * intent's type should be image/*.
+ * <p>
* Optional standard extras, which may be interpreted by some recipients as
* appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC},
* {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}.
@@ -1267,6 +1277,8 @@ public class Intent implements Parcelable {
* enabled or disabled. The data contains the name of the package.
* <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
+ * <li> {@link #EXTRA_CHANGED_COMPONENT_NAME} containing the class name of the changed component.
+ * <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the default action of restarting the application.
* </ul>
*
* <p class="note">This is a protected intent that can only be sent
@@ -1394,13 +1406,14 @@ public class Intent implements Parcelable {
* by the system.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED";
+ public static final String ACTION_POWER_DISCONNECTED =
+ "android.intent.action.ACTION_POWER_DISCONNECTED";
/**
* Broadcast Action: Device is shutting down.
* This is broadcast when the device is being shut down (completely turned
* off, not sleeping). Once the broadcast is complete, the final shutdown
* will proceed and all unsaved data lost. Apps will not normally need
- * to handle this, since the forground activity will be paused as well.
+ * to handle this, since the foreground activity will be paused as well.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
@@ -1408,6 +1421,17 @@ public class Intent implements Parcelable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN";
/**
+ * Activity Action: Start this activity to request system shutdown.
+ * The optional boolean extra field {@link #EXTRA_KEY_CONFIRM} can be set to true
+ * to request confirmation from the user before shutting down.
+ *
+ * <p class="note">This is a protected intent that can only be sent
+ * by the system.
+ *
+ * {@hide}
+ */
+ public static final String ACTION_REQUEST_SHUTDOWN = "android.intent.action.ACTION_REQUEST_SHUTDOWN";
+ /**
* Broadcast Action: Indicates low memory condition on the device
*
* <p class="note">This is a protected intent that can only be sent
@@ -1684,6 +1708,31 @@ public class Intent implements Parcelable {
public static final String ACTION_REBOOT =
"android.intent.action.REBOOT";
+ /**
+ * Broadcast Action: a remote intent is to be broadcasted.
+ *
+ * A remote intent is used for remote RPC between devices. The remote intent
+ * is serialized and sent from one device to another device. The receiving
+ * device parses the remote intent and broadcasts it. Note that anyone can
+ * broadcast a remote intent. However, if the intent receiver of the remote intent
+ * does not trust intent broadcasts from arbitrary intent senders, it should require
+ * the sender to hold certain permissions so only trusted sender's broadcast will be
+ * let through.
+ */
+ public static final String ACTION_REMOTE_INTENT =
+ "android.intent.action.REMOTE_INTENT";
+
+ /**
+ * Broadcast Action: hook for permforming cleanup after a system update.
+ *
+ * The broadcast is sent when the system is booting, before the
+ * BOOT_COMPLETED broadcast. It is only sent to receivers in the system
+ * image. A receiver for this should do its work and then disable itself
+ * so that it does not get run again at the next boot.
+ * @hide
+ */
+ public static final String ACTION_PRE_BOOT_COMPLETED =
+ "android.intent.action.PRE_BOOT_COMPLETED";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -1812,6 +1861,14 @@ public class Intent implements Parcelable {
*/
public static final String CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST =
"android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST";
+
+ /**
+ * Broadcast Action: The phone was docked or undocked. Includes the extra
+ * field {@link #EXTRA_DOCK_STATE}, containing the current dock state.
+ * @hide
+ */
+ public static final String ACTION_DOCK_EVENT = "android.intent.action.DOCK_EVENT";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard extra data keys.
@@ -1872,12 +1929,29 @@ public class Intent implements Parcelable {
public static final String EXTRA_TITLE = "android.intent.extra.TITLE";
/**
+ * A Parcelable[] of {@link Intent} or
+ * {@link android.content.pm.LabeledIntent} objects as set with
+ * {@link #putExtra(String, Parcelable[])} of additional activities to place
+ * a the front of the list of choices, when shown to the user with a
+ * {@link #ACTION_CHOOSER}.
+ */
+ public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
+
+ /**
* A {@link android.view.KeyEvent} object containing the event that
* triggered the creation of the Intent it is in.
*/
public static final String EXTRA_KEY_EVENT = "android.intent.extra.KEY_EVENT";
/**
+ * Set to true in {@link #ACTION_REQUEST_SHUTDOWN} to request confirmation from the user
+ * before shutting down.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_KEY_CONFIRM = "android.intent.extra.KEY_CONFIRM";
+
+ /**
* Used as an boolean extra field in {@link android.content.Intent#ACTION_PACKAGE_REMOVED} or
* {@link android.content.Intent#ACTION_PACKAGE_CHANGED} intents to override the default action
* of restarting the application.
@@ -1926,6 +2000,37 @@ public class Intent implements Parcelable {
public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
/**
+ * Used as an int extra field in {@link android.content.Intent#ACTION_DOCK_EVENT}
+ * intents to request the dock state. Possible values are
+ * {@link android.content.Intent#EXTRA_DOCK_STATE_UNDOCKED},
+ * {@link android.content.Intent#EXTRA_DOCK_STATE_DESK}, or
+ * {@link android.content.Intent#EXTRA_DOCK_STATE_CAR}.
+ * @hide
+ */
+ public static final String EXTRA_DOCK_STATE = "android.intent.extra.DOCK_STATE";
+
+ /**
+ * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+ * to represent that the phone is not in any dock.
+ * @hide
+ */
+ public static final int EXTRA_DOCK_STATE_UNDOCKED = 0;
+
+ /**
+ * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+ * to represent that the phone is in a desk dock.
+ * @hide
+ */
+ public static final int EXTRA_DOCK_STATE_DESK = 1;
+
+ /**
+ * Used as an int value for {@link android.content.Intent#EXTRA_DOCK_STATE}
+ * to represent that the phone is in a car dock.
+ * @hide
+ */
+ public static final int EXTRA_DOCK_STATE_CAR = 2;
+
+ /**
* Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing
* the bug report.
*
@@ -1943,6 +2048,39 @@ public class Intent implements Parcelable {
public static final String EXTRA_INSTALLER_PACKAGE_NAME
= "android.intent.extra.INSTALLER_PACKAGE_NAME";
+ /**
+ * Used in the extra field in the remote intent. It's astring token passed with the
+ * remote intent.
+ */
+ public static final String EXTRA_REMOTE_INTENT_TOKEN =
+ "android.intent.extra.remote_intent_token";
+
+ /**
+ * Used as an int extra field in {@link android.content.Intent#ACTION_PACKAGE_CHANGED}
+ * intent to supply the name of the component that changed.
+ *
+ */
+ public static final String EXTRA_CHANGED_COMPONENT_NAME =
+ "android.intent.extra.changed_component_name";
+
+ /**
+ * @hide
+ * Magic extra system code can use when binding, to give a label for
+ * who it is that has bound to a service. This is an integer giving
+ * a framework string resource that can be displayed to the user.
+ */
+ public static final String EXTRA_CLIENT_LABEL =
+ "android.intent.extra.client_label";
+
+ /**
+ * @hide
+ * Magic extra system code can use when binding, to give a PendingIntent object
+ * that can be launched for the user to disable the system's use of this
+ * service.
+ */
+ public static final String EXTRA_CLIENT_INTENT =
+ "android.intent.extra.client_intent";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Intent flags (see mFlags variable).
@@ -2037,12 +2175,14 @@ public class Intent implements Parcelable {
* of activity B, then C and D will be finished and B receive the given
* Intent, resulting in the stack now being: A, B.
*
- * <p>The currently running instance of task B in the above example will
+ * <p>The currently running instance of activity B in the above example will
* either receive the new intent you are starting here in its
* onNewIntent() method, or be itself finished and restarted with the
* new intent. If it has declared its launch mode to be "multiple" (the
- * default) it will be finished and re-created; for all other launch modes
- * it will receive the Intent in the current instance.
+ * default) and you have not set {@link #FLAG_ACTIVITY_SINGLE_TOP} in
+ * the same intent, then it will be finished and re-created; for all other
+ * launch modes or if {@link #FLAG_ACTIVITY_SINGLE_TOP} is set then this
+ * Intent will be delivered to the current instance's onNewIntent().
*
* <p>This launch mode can also be used to good effect in conjunction with
* {@link #FLAG_ACTIVITY_NEW_TASK}: if used to start the root activity
@@ -2171,6 +2311,13 @@ public class Intent implements Parcelable {
* @hide
*/
public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000;
+ /**
+ * Set when this broadcast is for a boot upgrade, a special mode that
+ * allows the broadcast to be sent before the system is ready and launches
+ * the app process with no providers running in it.
+ * @hide
+ */
+ public static final int FLAG_RECEIVER_BOOT_UPGRADE = 0x10000000;
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -4929,7 +5076,8 @@ public class Intent implements Parcelable {
}
};
- private Intent(Parcel in) {
+ /** @hide */
+ protected Intent(Parcel in) {
readFromParcel(in);
}
diff --git a/core/java/android/content/OperationApplicationException.java b/core/java/android/content/OperationApplicationException.java
new file mode 100644
index 0000000..2fc19bb
--- /dev/null
+++ b/core/java/android/content/OperationApplicationException.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+/**
+ * Thrown when an application of a {@link ContentProviderOperation} fails due the specified
+ * constraints.
+ */
+public class OperationApplicationException extends Exception {
+ private final int mNumSuccessfulYieldPoints;
+
+ public OperationApplicationException() {
+ super();
+ mNumSuccessfulYieldPoints = 0;
+ }
+ public OperationApplicationException(String message) {
+ super(message);
+ mNumSuccessfulYieldPoints = 0;
+ }
+ public OperationApplicationException(String message, Throwable cause) {
+ super(message, cause);
+ mNumSuccessfulYieldPoints = 0;
+ }
+ public OperationApplicationException(Throwable cause) {
+ super(cause);
+ mNumSuccessfulYieldPoints = 0;
+ }
+ public OperationApplicationException(int numSuccessfulYieldPoints) {
+ super();
+ mNumSuccessfulYieldPoints = numSuccessfulYieldPoints;
+ }
+ public OperationApplicationException(String message, int numSuccessfulYieldPoints) {
+ super(message);
+ mNumSuccessfulYieldPoints = numSuccessfulYieldPoints;
+ }
+
+ public int getNumSuccessfulYieldPoints() {
+ return mNumSuccessfulYieldPoints;
+ }
+}
diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java
index 7826e50..88dc332 100644
--- a/core/java/android/content/SyncAdapter.java
+++ b/core/java/android/content/SyncAdapter.java
@@ -18,6 +18,7 @@ package android.content;
import android.os.Bundle;
import android.os.RemoteException;
+import android.accounts.Account;
/**
* @hide
@@ -29,12 +30,12 @@ public abstract class SyncAdapter {
public static final int LOG_SYNC_DETAILS = 2743;
class Transport extends ISyncAdapter.Stub {
- public void startSync(ISyncContext syncContext, String account,
+ public void startSync(ISyncContext syncContext, String authority, Account account,
Bundle extras) throws RemoteException {
- SyncAdapter.this.startSync(new SyncContext(syncContext), account, extras);
+ SyncAdapter.this.startSync(new SyncContext(syncContext), account, authority, extras);
}
- public void cancelSync() throws RemoteException {
+ public void cancelSync(ISyncContext syncContext) throws RemoteException {
SyncAdapter.this.cancelSync();
}
}
@@ -42,9 +43,9 @@ public abstract class SyncAdapter {
Transport mTransport = new Transport();
/**
- * Get the Transport object. (note this is package private).
+ * Get the Transport object.
*/
- final ISyncAdapter getISyncAdapter()
+ public final ISyncAdapter getISyncAdapter()
{
return mTransport;
}
@@ -57,9 +58,11 @@ public abstract class SyncAdapter {
* @param syncContext the ISyncContext used to indicate the progress of the sync. When
* the sync is finished (successfully or not) ISyncContext.onFinished() must be called.
* @param account the account that should be synced
+ * @param authority the authority if the sync request
* @param extras SyncAdapter-specific parameters
*/
- public abstract void startSync(SyncContext syncContext, String account, Bundle extras);
+ public abstract void startSync(SyncContext syncContext, Account account, String authority,
+ Bundle extras);
/**
* Cancel the most recently initiated sync. Due to race conditions, this may arrive
diff --git a/core/java/android/content/SyncAdapterType.aidl b/core/java/android/content/SyncAdapterType.aidl
new file mode 100644
index 0000000..e67841f
--- /dev/null
+++ b/core/java/android/content/SyncAdapterType.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+parcelable SyncAdapterType;
+
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
new file mode 100644
index 0000000..25cbdb1
--- /dev/null
+++ b/core/java/android/content/SyncAdapterType.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.text.TextUtils;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Value type that represents a SyncAdapterType. This object overrides {@link #equals} and
+ * {@link #hashCode}, making it suitable for use as the key of a {@link java.util.Map}
+ */
+public class SyncAdapterType implements Parcelable {
+ public final String authority;
+ public final String accountType;
+ public final boolean isKey;
+ private final boolean userVisible;
+ private final boolean supportsUploading;
+
+ public SyncAdapterType(String authority, String accountType, boolean userVisible,
+ boolean supportsUploading) {
+ if (TextUtils.isEmpty(authority)) {
+ throw new IllegalArgumentException("the authority must not be empty: " + authority);
+ }
+ if (TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+ }
+ this.authority = authority;
+ this.accountType = accountType;
+ this.userVisible = userVisible;
+ this.supportsUploading = supportsUploading;
+ this.isKey = false;
+ }
+
+ private SyncAdapterType(String authority, String accountType) {
+ if (TextUtils.isEmpty(authority)) {
+ throw new IllegalArgumentException("the authority must not be empty: " + authority);
+ }
+ if (TextUtils.isEmpty(accountType)) {
+ throw new IllegalArgumentException("the accountType must not be empty: " + accountType);
+ }
+ this.authority = authority;
+ this.accountType = accountType;
+ this.userVisible = true;
+ this.supportsUploading = true;
+ this.isKey = true;
+ }
+
+ public boolean supportsUploading() {
+ if (isKey) {
+ throw new IllegalStateException(
+ "this method is not allowed to be called when this is a key");
+ }
+ return supportsUploading;
+ }
+
+ public boolean isUserVisible() {
+ if (isKey) {
+ throw new IllegalStateException(
+ "this method is not allowed to be called when this is a key");
+ }
+ return userVisible;
+ }
+
+ public static SyncAdapterType newKey(String authority, String accountType) {
+ return new SyncAdapterType(authority, accountType);
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof SyncAdapterType)) return false;
+ final SyncAdapterType other = (SyncAdapterType)o;
+ // don't include userVisible or supportsUploading in the equality check
+ return authority.equals(other.authority) && accountType.equals(other.accountType);
+ }
+
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + authority.hashCode();
+ result = 31 * result + accountType.hashCode();
+ // don't include userVisible or supportsUploading the hash
+ return result;
+ }
+
+ public String toString() {
+ if (isKey) {
+ return "SyncAdapterType Key {name=" + authority
+ + ", type=" + accountType
+ + "}";
+ } else {
+ return "SyncAdapterType {name=" + authority
+ + ", type=" + accountType
+ + ", userVisible=" + userVisible
+ + ", supportsUploading=" + supportsUploading
+ + "}";
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ if (isKey) {
+ throw new IllegalStateException("keys aren't parcelable");
+ }
+
+ dest.writeString(authority);
+ dest.writeString(accountType);
+ dest.writeInt(userVisible ? 1 : 0);
+ dest.writeInt(supportsUploading ? 1 : 0);
+ }
+
+ public SyncAdapterType(Parcel source) {
+ this(
+ source.readString(),
+ source.readString(),
+ source.readInt() != 0,
+ source.readInt() != 0);
+ }
+
+ public static final Creator<SyncAdapterType> CREATOR = new Creator<SyncAdapterType>() {
+ public SyncAdapterType createFromParcel(Parcel source) {
+ return new SyncAdapterType(source);
+ }
+
+ public SyncAdapterType[] newArray(int size) {
+ return new SyncAdapterType[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
new file mode 100644
index 0000000..7d9f1de
--- /dev/null
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.pm.RegisteredServicesCache;
+import android.content.res.TypedArray;
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * A cache of services that export the {@link android.content.ISyncAdapter} interface.
+ * @hide
+ */
+/* package private */ class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
+ private static final String TAG = "Account";
+
+ private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
+ private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
+ private static final String ATTRIBUTES_NAME = "sync-adapter";
+
+ SyncAdaptersCache(Context context) {
+ super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME);
+ }
+
+ public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
+ TypedArray sa = mContext.getResources().obtainAttributes(attrs,
+ com.android.internal.R.styleable.SyncAdapter);
+ try {
+ final String authority =
+ sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
+ final String accountType =
+ sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
+ if (authority == null || accountType == null) {
+ return null;
+ }
+ final boolean userVisible =
+ sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_userVisible, true);
+ final boolean supportsUploading =
+ sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_supportsUploading,
+ true);
+ return new SyncAdapterType(authority, accountType, userVisible, supportsUploading);
+ } finally {
+ sa.recycle();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 4d2cce8..6d27bc7 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -21,8 +21,9 @@ import com.google.android.collect.Maps;
import com.android.internal.R;
import com.android.internal.util.ArrayUtils;
-import android.accounts.AccountMonitor;
-import android.accounts.AccountMonitorListener;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdatedListener;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -30,11 +31,11 @@ import android.app.PendingIntent;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.RegisteredServicesCache;
+import android.content.pm.ProviderInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -48,7 +49,6 @@ import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Config;
@@ -72,11 +72,12 @@ import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Random;
+import java.util.Collection;
/**
* @hide
*/
-class SyncManager {
+class SyncManager implements OnAccountsUpdatedListener {
private static final String TAG = "SyncManager";
// used during dumping of the Sync history
@@ -117,14 +118,11 @@ class SyncManager {
private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarmWakeLock";
private Context mContext;
- private ContentResolver mContentResolver;
private String mStatusText = "";
private long mHeartbeatTime = 0;
- private AccountMonitor mAccountMonitor;
-
- private volatile String[] mAccounts = null;
+ private volatile Account[] mAccounts = null;
volatile private PowerManager.WakeLock mSyncWakeLock;
volatile private PowerManager.WakeLock mHandleAlarmWakeLock;
@@ -150,18 +148,22 @@ class SyncManager {
private volatile boolean mSyncPollInitialized;
private final PendingIntent mSyncAlarmIntent;
private final PendingIntent mSyncPollAlarmIntent;
+ // Synchronized on "this". Instead of using this directly one should instead call
+ // its accessor, getConnManager().
+ private ConnectivityManager mConnManagerDoNotUseDirectly;
+
+ private final SyncAdaptersCache mSyncAdapters;
private BroadcastReceiver mStorageIntentReceiver =
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- ensureContentResolver();
String action = intent.getAction();
if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Internal storage is low.");
}
mStorageIsLow = true;
- cancelActiveSync(null /* no url */);
+ cancelActiveSync(null /* any account */, null /* any authority */);
} else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Internal storage is ok.");
@@ -172,6 +174,57 @@ class SyncManager {
}
};
+ private BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ if (!mFactoryTest) {
+ mBootCompleted = true;
+ AccountManager.get(mContext).addOnAccountsUpdatedListener(SyncManager.this,
+ mSyncHandler, true /* updateImmediately */);
+ }
+ }
+ };
+
+ public void onAccountsUpdated(Account[] accounts) {
+ // remember if this was the first time this was called after an update
+ final boolean justBootedUp = mAccounts == null;
+ mAccounts = accounts;
+
+ // if a sync is in progress yet it is no longer in the accounts list,
+ // cancel it
+ ActiveSyncContext activeSyncContext = mActiveSyncContext;
+ if (activeSyncContext != null) {
+ if (!ArrayUtils.contains(accounts, activeSyncContext.mSyncOperation.account)) {
+ Log.d(TAG, "canceling sync since the account has been removed");
+ sendSyncFinishedOrCanceledMessage(activeSyncContext,
+ null /* no result since this is a cancel */);
+ }
+ }
+
+ // we must do this since we don't bother scheduling alarms when
+ // the accounts are not set yet
+ sendCheckAlarmsMessage();
+
+ mSyncStorageEngine.doDatabaseCleanup(accounts);
+
+ if (accounts.length > 0) {
+ // If this is the first time this was called after a bootup then
+ // the accounts haven't really changed, instead they were just loaded
+ // from the AccountManager. Otherwise at least one of the accounts
+ // has a change.
+ //
+ // If there was a real account change then force a sync of all accounts.
+ // This is a bit of overkill, but at least it will end up retrying syncs
+ // that failed due to an authentication failure and thus will recover if the
+ // account change was a password update.
+ //
+ // If this was the bootup case then don't sync everything, instead only
+ // sync those that have an unknown syncable state, which will give them
+ // a chance to set their syncable state.
+ boolean onlyThoseWithUnkownSyncableState = !justBootedUp;
+ scheduleSync(null, null, null, 0 /* no delay */, onlyThoseWithUnkownSyncableState);
+ }
+ }
+
private BroadcastReceiver mConnectivityIntentReceiver =
new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
@@ -229,7 +282,23 @@ class SyncManager {
private static final String SYNCMANAGER_PREFS_FILENAME = "/data/system/syncmanager.prefs";
+ private final boolean mFactoryTest;
+
+ private volatile boolean mBootCompleted = false;
+
+ private ConnectivityManager getConnectivityManager() {
+ synchronized (this) {
+ if (mConnManagerDoNotUseDirectly == null) {
+ mConnManagerDoNotUseDirectly = (ConnectivityManager)mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ }
+ return mConnManagerDoNotUseDirectly;
+ }
+ }
+
public SyncManager(Context context, boolean factoryTest) {
+ mFactoryTest = factoryTest;
+
// Initialize the SyncStorageEngine first, before registering observers
// and creating threads and so on; it may fail if the disk is full.
SyncStorageEngine.init(context);
@@ -244,6 +313,8 @@ class SyncManager {
mPackageManager = null;
+ mSyncAdapters = new SyncAdaptersCache(mContext);
+
mSyncAlarmIntent = PendingIntent.getBroadcast(
mContext, 0 /* ignored */, new Intent(ACTION_SYNC_ALARM), 0);
@@ -253,6 +324,9 @@ class SyncManager {
IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
context.registerReceiver(mConnectivityIntentReceiver, intentFilter);
+ intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ context.registerReceiver(mBootCompletedReceiver, intentFilter);
+
intentFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
context.registerReceiver(mStorageIntentReceiver, intentFilter);
@@ -282,48 +356,12 @@ class SyncManager {
mHandleAlarmWakeLock.setReferenceCounted(false);
mSyncStorageEngine.addStatusChangeListener(
- SyncStorageEngine.CHANGE_SETTINGS, new ISyncStatusObserver.Stub() {
+ ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, new ISyncStatusObserver.Stub() {
public void onStatusChanged(int which) {
// force the sync loop to run if the settings change
sendCheckAlarmsMessage();
}
});
-
- if (!factoryTest) {
- AccountMonitorListener listener = new AccountMonitorListener() {
- public void onAccountsUpdated(String[] accounts) {
- final boolean hadAccountsAlready = mAccounts != null;
- // copy the accounts into a new array and change mAccounts to point to it
- String[] newAccounts = new String[accounts.length];
- System.arraycopy(accounts, 0, newAccounts, 0, accounts.length);
- mAccounts = newAccounts;
-
- // if a sync is in progress yet it is no longer in the accounts list, cancel it
- ActiveSyncContext activeSyncContext = mActiveSyncContext;
- if (activeSyncContext != null) {
- if (!ArrayUtils.contains(newAccounts,
- activeSyncContext.mSyncOperation.account)) {
- Log.d(TAG, "canceling sync since the account has been removed");
- sendSyncFinishedOrCanceledMessage(activeSyncContext,
- null /* no result since this is a cancel */);
- }
- }
-
- // we must do this since we don't bother scheduling alarms when
- // the accounts are not set yet
- sendCheckAlarmsMessage();
-
- mSyncStorageEngine.doDatabaseCleanup(accounts);
-
- if (hadAccountsAlready && mAccounts.length > 0) {
- // request a sync so that if the password was changed we will retry any sync
- // that failed when it was wrong
- startSync(null /* all providers */, null /* no extras */);
- }
- }
- };
- mAccountMonitor = new AccountMonitor(context, listener);
- }
}
private synchronized void initializeSyncPoll() {
@@ -397,7 +435,8 @@ class SyncManager {
scheduleSyncPollAlarm(nextRelativePollTimeMs);
// perform a poll
- scheduleSync(null /* sync all syncable providers */, new Bundle(), 0 /* no delay */);
+ scheduleSync(null /* sync all syncable accounts */, null /* sync all syncable providers */,
+ new Bundle(), 0 /* no delay */, false /* onlyThoseWithUnkownSyncableState */);
}
private void writeSyncPollTime(long when) {
@@ -452,19 +491,13 @@ class SyncManager {
return mSyncStorageEngine;
}
- private void ensureContentResolver() {
- if (mContentResolver == null) {
- mContentResolver = mContext.getContentResolver();
- }
- }
-
private void ensureAlarmService() {
if (mAlarmService == null) {
mAlarmService = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
}
}
- public String getSyncingAccount() {
+ public Account getSyncingAccount() {
ActiveSyncContext activeSyncContext = mActiveSyncContext;
return (activeSyncContext != null) ? activeSyncContext.mSyncOperation.account : null;
}
@@ -499,21 +532,23 @@ class SyncManager {
*
* <p>You'll start getting callbacks after this.
*
- * @param url The Uri of a specific provider to be synced, or
- * null to sync all providers.
+ * @param requestedAccount the account to sync, may be null to signify all accounts
+ * @param requestedAuthority the authority to sync, may be null to indicate all authorities
* @param extras a Map of SyncAdapter-specific information to control
* syncs of a specific provider. Can be null. Is ignored
* if the url is null.
* @param delay how many milliseconds in the future to wait before performing this
- * sync. -1 means to make this the next sync to perform.
+ * @param onlyThoseWithUnkownSyncableState
*/
- public void scheduleSync(Uri url, Bundle extras, long delay) {
+ public void scheduleSync(Account requestedAccount, String requestedAuthority,
+ Bundle extras, long delay, boolean onlyThoseWithUnkownSyncableState) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
- if (isLoggable) {
- Log.v(TAG, "scheduleSync:"
- + " delay " + delay
- + ", url " + ((url == null) ? "(null)" : url)
- + ", extras " + ((extras == null) ? "(null)" : extras));
+
+ if (!mBootCompleted) {
+ if (isLoggable) {
+ Log.v(TAG, "suppressing scheduleSync() since boot hasn't completed");
+ }
+ return;
}
if (!isSyncEnabled()) {
@@ -524,6 +559,14 @@ class SyncManager {
return;
}
+ if (!getConnectivityManager().getBackgroundDataSetting()) {
+ if (isLoggable) {
+ Log.v(TAG, "not syncing because background data usage isn't allowed");
+ }
+ setStatusText("Sync is disabled.");
+ return;
+ }
+
if (mAccounts == null) setStatusText("The accounts aren't known yet.");
if (!mDataConnectionIsConnected) setStatusText("No data connection");
if (mStorageIsLow) setStatusText("Memory low");
@@ -535,10 +578,9 @@ class SyncManager {
delay = -1; // this means schedule at the front of the queue
}
- String[] accounts;
- String accountFromExtras = extras.getString(ContentResolver.SYNC_EXTRAS_ACCOUNT);
- if (!TextUtils.isEmpty(accountFromExtras)) {
- accounts = new String[]{accountFromExtras};
+ Account[] accounts;
+ if (requestedAccount != null) {
+ accounts = new Account[]{requestedAccount};
} else {
// if the accounts aren't configured yet then we can't support an account-less
// sync request
@@ -560,14 +602,14 @@ class SyncManager {
}
final boolean uploadOnly = extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, false);
- final boolean force = extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
+ final boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
int source;
if (uploadOnly) {
source = SyncStorageEngine.SOURCE_LOCAL;
- } else if (force) {
+ } else if (manualSync) {
source = SyncStorageEngine.SOURCE_USER;
- } else if (url == null) {
+ } else if (requestedAuthority == null) {
source = SyncStorageEngine.SOURCE_POLL;
} else {
// this isn't strictly server, since arbitrary callers can (and do) request
@@ -575,59 +617,84 @@ class SyncManager {
source = SyncStorageEngine.SOURCE_SERVER;
}
- List<String> names = new ArrayList<String>();
- List<ProviderInfo> providers = new ArrayList<ProviderInfo>();
- populateProvidersList(url, names, providers);
+ // Compile a list of authorities that have sync adapters.
+ // For each authority sync each account that matches a sync adapter.
+ final HashSet<String> syncableAuthorities = new HashSet<String>();
+ for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter :
+ mSyncAdapters.getAllServices()) {
+ syncableAuthorities.add(syncAdapter.type.authority);
+ }
- final int numProviders = providers.size();
- for (int i = 0; i < numProviders; i++) {
- if (!providers.get(i).isSyncable) continue;
- final String name = names.get(i);
- for (String account : accounts) {
- scheduleSyncOperation(new SyncOperation(account, source, name, extras, delay));
- // TODO: remove this when Calendar supports multiple accounts. Until then
- // pretend that only the first account exists when syncing calendar.
- if ("calendar".equals(name)) {
- break;
- }
- }
+ // if the url was specified then replace the list of authorities with just this authority
+ // or clear it if this authority isn't syncable
+ if (requestedAuthority != null) {
+ final boolean hasSyncAdapter = syncableAuthorities.contains(requestedAuthority);
+ syncableAuthorities.clear();
+ if (hasSyncAdapter) syncableAuthorities.add(requestedAuthority);
}
- }
- private void setStatusText(String message) {
- mStatusText = message;
- }
+ final boolean masterSyncAutomatically = mSyncStorageEngine.getMasterSyncAutomatically();
- private void populateProvidersList(Uri url, List<String> names, List<ProviderInfo> providers) {
- try {
- final IPackageManager packageManager = getPackageManager();
- if (url == null) {
- packageManager.querySyncProviders(names, providers);
- } else {
- final String authority = url.getAuthority();
- ProviderInfo info = packageManager.resolveContentProvider(url.getAuthority(), 0);
- if (info != null) {
- // only set this provider if the requested authority is the primary authority
- String[] providerNames = info.authority.split(";");
- if (url.getAuthority().equals(providerNames[0])) {
- names.add(authority);
- providers.add(info);
+ for (String authority : syncableAuthorities) {
+ for (Account account : accounts) {
+ int isSyncable = mSyncStorageEngine.getIsSyncable(account, authority);
+ if (isSyncable == 0) {
+ continue;
+ }
+ if (onlyThoseWithUnkownSyncableState && isSyncable >= 0) {
+ continue;
+ }
+ final RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(
+ SyncAdapterType.newKey(authority, account.type));
+ if (syncAdapterInfo != null) {
+ if (!syncAdapterInfo.type.supportsUploading() && uploadOnly) {
+ continue;
}
+
+ // make this an initialization sync if the isSyncable state is unknown
+ Bundle extrasCopy = extras;
+ long delayCopy = delay;
+ if (isSyncable < 0) {
+ extrasCopy = new Bundle(extras);
+ extrasCopy.putBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, true);
+ delayCopy = -1; // expedite this
+ } else {
+ final boolean syncAutomatically = masterSyncAutomatically
+ && mSyncStorageEngine.getSyncAutomatically(account, authority);
+ boolean syncAllowed = manualSync || syncAutomatically;
+ if (!syncAllowed) {
+ if (isLoggable) {
+ Log.d(TAG, "scheduleSync: sync of " + account + ", " + authority
+ + " is not allowed, dropping request");
+ continue;
+ }
+ }
+ }
+ if (isLoggable) {
+ Log.v(TAG, "scheduleSync:"
+ + " delay " + delayCopy
+ + ", source " + source
+ + ", account " + account
+ + ", authority " + authority
+ + ", extras " + extrasCopy);
+ }
+ scheduleSyncOperation(
+ new SyncOperation(account, source, authority, extrasCopy, delayCopy));
}
}
- } catch (RemoteException ex) {
- // we should really never get this, but if we do then clear the lists, which
- // will result in the dropping of the sync request
- Log.e(TAG, "error trying to get the ProviderInfo for " + url, ex);
- names.clear();
- providers.clear();
}
}
- public void scheduleLocalSync(Uri url) {
+ private void setStatusText(String message) {
+ mStatusText = message;
+ }
+
+ public void scheduleLocalSync(Account account, String authority) {
final Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
- scheduleSync(url, extras, LOCAL_SYNC_DELAY);
+ scheduleSync(account, authority, extras, LOCAL_SYNC_DELAY,
+ false /* onlyThoseWithUnkownSyncableState */);
}
private IPackageManager getPackageManager() {
@@ -641,18 +708,16 @@ class SyncManager {
return mPackageManager;
}
- /**
- * Initiate a sync for this given URL, or pass null for a full sync.
- *
- * <p>You'll start getting callbacks after this.
- *
- * @param url The Uri of a specific provider to be synced, or
- * null to sync all providers.
- * @param extras a Map of SyncAdapter specific information to control
- * syncs of a specific provider. Can be null. Is ignored
- */
- public void startSync(Uri url, Bundle extras) {
- scheduleSync(url, extras, 0 /* no delay */);
+ public SyncAdapterType[] getSyncAdapterTypes() {
+ final Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> serviceInfos =
+ mSyncAdapters.getAllServices();
+ SyncAdapterType[] types = new SyncAdapterType[serviceInfos.size()];
+ int i = 0;
+ for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> serviceInfo : serviceInfos) {
+ types[i] = serviceInfo.type;
+ ++i;
+ }
+ return types;
}
public void updateHeartbeatTime() {
@@ -721,8 +786,7 @@ class SyncManager {
}
// Cap the delay
- ensureContentResolver();
- long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContentResolver,
+ long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContext.getContentResolver(),
Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS);
if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) {
@@ -736,17 +800,22 @@ class SyncManager {
}
/**
- * Cancel the active sync if it matches the uri. The uri corresponds to the one passed
- * in to startSync().
- * @param uri If non-null, the active sync is only canceled if it matches the uri.
- * If null, any active sync is canceled.
+ * Cancel the active sync if it matches the authority and account.
+ * @param account limit the cancelations to syncs with this account, if non-null
+ * @param authority limit the cancelations to syncs with this authority, if non-null
*/
- public void cancelActiveSync(Uri uri) {
+ public void cancelActiveSync(Account account, String authority) {
ActiveSyncContext activeSyncContext = mActiveSyncContext;
if (activeSyncContext != null) {
- // if a Uri was specified then only cancel the sync if it matches the the uri
- if (uri != null) {
- if (!uri.getAuthority().equals(activeSyncContext.mSyncOperation.authority)) {
+ // if an authority was specified then only cancel the sync if it matches
+ if (account != null) {
+ if (!account.equals(activeSyncContext.mSyncOperation.account)) {
+ return;
+ }
+ }
+ // if an account was specified then only cancel the sync if it matches
+ if (authority != null) {
+ if (!authority.equals(activeSyncContext.mSyncOperation.authority)) {
return;
}
}
@@ -798,14 +867,13 @@ class SyncManager {
}
/**
- * Remove any scheduled sync operations that match uri. The uri corresponds to the one passed
- * in to startSync().
- * @param uri If non-null, only operations that match the uri are cleared.
- * If null, all operations are cleared.
+ * Remove scheduled sync operations.
+ * @param account limit the removals to operations with this account, if non-null
+ * @param authority limit the removals to operations with this authority, if non-null
*/
- public void clearScheduledSyncOperations(Uri uri) {
+ public void clearScheduledSyncOperations(Account account, String authority) {
synchronized (mSyncQueue) {
- mSyncQueue.clear(null, uri != null ? uri.getAuthority() : null);
+ mSyncQueue.clear(account, authority);
}
}
@@ -857,7 +925,7 @@ class SyncManager {
* Value type that represents a sync operation.
*/
static class SyncOperation implements Comparable {
- final String account;
+ final Account account;
int syncSource;
String authority;
Bundle extras;
@@ -866,7 +934,7 @@ class SyncManager {
long delay;
SyncStorageEngine.PendingOperation pendingOperation;
- SyncOperation(String account, int source, String authority, Bundle extras, long delay) {
+ SyncOperation(Account account, int source, String authority, Bundle extras, long delay) {
this.account = account;
this.syncSource = source;
this.authority = authority;
@@ -937,21 +1005,19 @@ class SyncManager {
/**
* @hide
*/
- class ActiveSyncContext extends ISyncContext.Stub {
+ class ActiveSyncContext extends ISyncContext.Stub implements ServiceConnection {
final SyncOperation mSyncOperation;
final long mHistoryRowId;
- final IContentProvider mContentProvider;
- final ISyncAdapter mSyncAdapter;
+ ISyncAdapter mSyncAdapter;
final long mStartTime;
long mTimeoutStartTime;
- public ActiveSyncContext(SyncOperation syncOperation, IContentProvider contentProvider,
- ISyncAdapter syncAdapter, long historyRowId) {
+ public ActiveSyncContext(SyncOperation syncOperation,
+ long historyRowId) {
super();
mSyncOperation = syncOperation;
mHistoryRowId = historyRowId;
- mContentProvider = contentProvider;
- mSyncAdapter = syncAdapter;
+ mSyncAdapter = null;
mStartTime = SystemClock.elapsedRealtime();
mTimeoutStartTime = mStartTime;
}
@@ -977,6 +1043,41 @@ class SyncManager {
.append(", syncOperation ").append(mSyncOperation);
}
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Message msg = mSyncHandler.obtainMessage();
+ msg.what = SyncHandler.MESSAGE_SERVICE_CONNECTED;
+ msg.obj = new ServiceConnectionData(this, ISyncAdapter.Stub.asInterface(service));
+ mSyncHandler.sendMessage(msg);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ Message msg = mSyncHandler.obtainMessage();
+ msg.what = SyncHandler.MESSAGE_SERVICE_DISCONNECTED;
+ msg.obj = new ServiceConnectionData(this, null);
+ mSyncHandler.sendMessage(msg);
+ }
+
+ boolean bindToSyncAdapter(RegisteredServicesCache.ServiceInfo info) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "bindToSyncAdapter: " + info.componentName + ", connection " + this);
+ }
+ Intent intent = new Intent();
+ intent.setAction("android.content.SyncAdapter");
+ intent.setComponent(info.componentName);
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.sync_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0));
+ return mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
+ }
+
+ void unBindFromSyncAdapter() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "unBindFromSyncAdapter: connection " + this);
+ }
+ mContext.unbindService(this);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -991,6 +1092,12 @@ class SyncManager {
if (isSyncEnabled()) {
dumpSyncHistory(pw, sb);
}
+
+ pw.println();
+ pw.println("SyncAdapters:");
+ for (RegisteredServicesCache.ServiceInfo info : mSyncAdapters.getAllServices()) {
+ pw.println(" " + info);
+ }
}
static String formatTime(long time) {
@@ -1004,7 +1111,7 @@ class SyncManager {
pw.print("data connected: "); pw.println(mDataConnectionIsConnected);
pw.print("memory low: "); pw.println(mStorageIsLow);
- final String[] accounts = mAccounts;
+ final Account[] accounts = mAccounts;
pw.print("accounts: ");
if (accounts != null) {
pw.println(accounts.length);
@@ -1068,7 +1175,8 @@ class SyncManager {
for (int i=0; i<N; i++) {
SyncStorageEngine.PendingOperation op = ops.get(i);
pw.print(" #"); pw.print(i); pw.print(": account=");
- pw.print(op.account); pw.print(" authority=");
+ pw.print(op.account.name); pw.print(":");
+ pw.print(op.account.type); pw.print(" authority=");
pw.println(op.authority);
if (op.extras != null && op.extras.size() > 0) {
sb.setLength(0);
@@ -1078,7 +1186,7 @@ class SyncManager {
}
}
- HashSet<String> processedAccounts = new HashSet<String>();
+ HashSet<Account> processedAccounts = new HashSet<Account>();
ArrayList<SyncStatusInfo> statuses
= mSyncStorageEngine.getSyncStatus();
if (statuses != null && statuses.size() > 0) {
@@ -1090,7 +1198,7 @@ class SyncManager {
SyncStorageEngine.AuthorityInfo authority
= mSyncStorageEngine.getAuthority(status.authorityId);
if (authority != null) {
- String curAccount = authority.account;
+ Account curAccount = authority.account;
if (processedAccounts.contains(curAccount)) {
continue;
@@ -1098,8 +1206,9 @@ class SyncManager {
processedAccounts.add(curAccount);
- pw.print(" Account "); pw.print(authority.account);
- pw.println(":");
+ pw.print(" Account "); pw.print(authority.account.name);
+ pw.print(" "); pw.print(authority.account.type);
+ pw.println(":");
for (int j=i; j<N; j++) {
status = statuses.get(j);
authority = mSyncStorageEngine.getAuthority(status.authorityId);
@@ -1219,9 +1328,15 @@ class SyncManager {
SyncStorageEngine.AuthorityInfo authority
= mSyncStorageEngine.getAuthority(item.authorityId);
pw.print(" #"); pw.print(i+1); pw.print(": ");
- pw.print(authority != null ? authority.account : "<no account>");
- pw.print(" ");
- pw.print(authority != null ? authority.authority : "<no account>");
+ if (authority != null) {
+ pw.print(authority.account.name);
+ pw.print(":");
+ pw.print(authority.account.type);
+ pw.print(" ");
+ pw.print(authority.authority);
+ } else {
+ pw.print("<no account>");
+ }
Time time = new Time();
time.set(item.eventTime);
pw.print(" "); pw.print(SyncStorageEngine.SOURCES[item.source]);
@@ -1278,6 +1393,15 @@ class SyncManager {
}
}
+ class ServiceConnectionData {
+ public final ActiveSyncContext activeSyncContext;
+ public final ISyncAdapter syncAdapter;
+ ServiceConnectionData(ActiveSyncContext activeSyncContext, ISyncAdapter syncAdapter) {
+ this.activeSyncContext = activeSyncContext;
+ this.syncAdapter = syncAdapter;
+ }
+ }
+
/**
* Handles SyncOperation Messages that are posted to the associated
* HandlerThread.
@@ -1287,6 +1411,8 @@ class SyncManager {
private static final int MESSAGE_SYNC_FINISHED = 1;
private static final int MESSAGE_SYNC_ALARM = 2;
private static final int MESSAGE_CHECK_ALARMS = 3;
+ private static final int MESSAGE_SERVICE_CONNECTED = 4;
+ private static final int MESSAGE_SERVICE_DISCONNECTED = 5;
public final SyncNotificationInfo mSyncNotificationInfo = new SyncNotificationInfo();
private Long mAlarmScheduleTime = null;
@@ -1301,7 +1427,7 @@ class SyncManager {
*/
class SyncNotificationInfo {
// only valid if isActive is true
- public String account;
+ public Account account;
// only valid if isActive is true
public String authority;
@@ -1358,6 +1484,53 @@ class SyncManager {
runStateIdle();
break;
+ case SyncHandler.MESSAGE_SERVICE_CONNECTED: {
+ ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_CONNECTED: "
+ + msgData.activeSyncContext
+ + " active is " + mActiveSyncContext);
+ }
+ // check that this isn't an old message
+ if (mActiveSyncContext == msgData.activeSyncContext) {
+ runBoundToSyncAdapter(msgData.syncAdapter);
+ }
+ break;
+ }
+
+ case SyncHandler.MESSAGE_SERVICE_DISCONNECTED: {
+ ServiceConnectionData msgData = (ServiceConnectionData)msg.obj;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "handleSyncHandlerMessage: MESSAGE_SERVICE_DISCONNECTED: "
+ + msgData.activeSyncContext
+ + " active is " + mActiveSyncContext);
+ }
+ // check that this isn't an old message
+ if (mActiveSyncContext == msgData.activeSyncContext) {
+ // cancel the sync if we have a syncadapter, which means one is
+ // outstanding
+ if (mActiveSyncContext.mSyncAdapter != null) {
+ try {
+ mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext);
+ } catch (RemoteException e) {
+ // we don't need to retry this in this case
+ }
+ }
+
+ // pretend that the sync failed with an IOException,
+ // which is a soft error
+ SyncResult syncResult = new SyncResult();
+ syncResult.stats.numIoExceptions++;
+ runSyncFinishedOrCanceled(syncResult);
+
+ // since we are no longer syncing, check if it is time to start a new
+ // sync
+ runStateIdle();
+ }
+
+ break;
+ }
+
case SyncHandler.MESSAGE_SYNC_ALARM: {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) {
@@ -1456,7 +1629,7 @@ class SyncManager {
// If the accounts aren't known yet then we aren't ready to run. We will be kicked
// when the account lookup request does complete.
- String[] accounts = mAccounts;
+ Account[] accounts = mAccounts;
if (accounts == null) {
if (isLoggable) {
Log.v(TAG, "runStateIdle: accounts not known, skipping");
@@ -1468,14 +1641,13 @@ class SyncManager {
// Otherwise consume SyncOperations from the head of the SyncQueue until one is
// found that is runnable (not disabled, etc). If that one is ready to run then
// start it, otherwise just get out.
- SyncOperation syncOperation;
- final ConnectivityManager connManager = (ConnectivityManager)
- mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- final boolean backgroundDataSetting = connManager.getBackgroundDataSetting();
+ SyncOperation op;
+ final boolean backgroundDataUsageAllowed =
+ getConnectivityManager().getBackgroundDataSetting();
synchronized (mSyncQueue) {
while (true) {
- syncOperation = mSyncQueue.head();
- if (syncOperation == null) {
+ op = mSyncQueue.head();
+ if (op == null) {
if (isLoggable) {
Log.v(TAG, "runStateIdle: no more sync operations, returning");
}
@@ -1485,39 +1657,49 @@ class SyncManager {
// Sync is disabled, drop this operation.
if (!isSyncEnabled()) {
if (isLoggable) {
- Log.v(TAG, "runStateIdle: sync disabled, dropping " + syncOperation);
+ Log.v(TAG, "runStateIdle: sync disabled, dropping " + op);
}
mSyncQueue.popHead();
continue;
}
- // skip the sync if it isn't a force and the settings are off for this provider
- final boolean force = syncOperation.extras.getBoolean(
- ContentResolver.SYNC_EXTRAS_FORCE, false);
- if (!force && (!backgroundDataSetting
- || !mSyncStorageEngine.getListenForNetworkTickles()
- || !mSyncStorageEngine.getSyncProviderAutomatically(
- null, syncOperation.authority))) {
+ // skip the sync if it isn't manual and auto sync is disabled
+ final boolean manualSync = op.extras.getBoolean(
+ ContentResolver.SYNC_EXTRAS_MANUAL, false);
+ final boolean syncAutomatically =
+ mSyncStorageEngine.getSyncAutomatically(op.account, op.authority)
+ && mSyncStorageEngine.getMasterSyncAutomatically();
+ boolean syncAllowed =
+ manualSync || (backgroundDataUsageAllowed && syncAutomatically);
+ int isSyncable = mSyncStorageEngine.getIsSyncable(op.account, op.authority);
+ if (isSyncable == 0) {
+ // if not syncable, don't allow
+ syncAllowed = false;
+ } else if (isSyncable < 0) {
+ // if the syncable state is unknown, only allow initialization syncs
+ syncAllowed =
+ op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false);
+ }
+ if (!syncAllowed) {
if (isLoggable) {
- Log.v(TAG, "runStateIdle: sync off, dropping " + syncOperation);
+ Log.v(TAG, "runStateIdle: sync off, dropping " + op);
}
mSyncQueue.popHead();
continue;
}
// skip the sync if the account of this operation no longer exists
- if (!ArrayUtils.contains(accounts, syncOperation.account)) {
+ if (!ArrayUtils.contains(accounts, op.account)) {
mSyncQueue.popHead();
if (isLoggable) {
- Log.v(TAG, "runStateIdle: account not present, dropping "
- + syncOperation);
+ Log.v(TAG, "runStateIdle: account not present, dropping " + op);
}
continue;
}
// go ahead and try to sync this syncOperation
if (isLoggable) {
- Log.v(TAG, "runStateIdle: found sync candidate: " + syncOperation);
+ Log.v(TAG, "runStateIdle: found sync candidate: " + op);
}
break;
}
@@ -1525,11 +1707,10 @@ class SyncManager {
// If the first SyncOperation isn't ready to run schedule a wakeup and
// get out.
final long now = SystemClock.elapsedRealtime();
- if (syncOperation.earliestRunTime > now) {
+ if (op.earliestRunTime > now) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "runStateIdle: the time is " + now + " yet the next "
- + "sync operation is for " + syncOperation.earliestRunTime
- + ": " + syncOperation);
+ + "sync operation is for " + op.earliestRunTime + ": " + op);
}
return;
}
@@ -1537,72 +1718,72 @@ class SyncManager {
// We will do this sync. Remove it from the queue and run it outside of the
// synchronized block.
if (isLoggable) {
- Log.v(TAG, "runStateIdle: we are going to sync " + syncOperation);
+ Log.v(TAG, "runStateIdle: we are going to sync " + op);
}
mSyncQueue.popHead();
}
- String providerName = syncOperation.authority;
- ensureContentResolver();
- IContentProvider contentProvider;
-
- // acquire the provider and update the sync history
- try {
- contentProvider = mContentResolver.acquireProvider(providerName);
- if (contentProvider == null) {
- Log.e(TAG, "Provider " + providerName + " doesn't exist");
- return;
- }
- if (contentProvider.getSyncAdapter() == null) {
- Log.e(TAG, "Provider " + providerName + " isn't syncable, " + contentProvider);
- return;
+ // connect to the sync adapter
+ SyncAdapterType syncAdapterType = SyncAdapterType.newKey(op.authority, op.account.type);
+ RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapterInfo =
+ mSyncAdapters.getServiceInfo(syncAdapterType);
+ if (syncAdapterInfo == null) {
+ if (Config.LOGD) {
+ Log.d(TAG, "can't find a sync adapter for " + syncAdapterType);
}
- } catch (RemoteException remoteExc) {
- Log.e(TAG, "Caught a RemoteException while preparing for sync, rescheduling "
- + syncOperation, remoteExc);
- rescheduleWithDelay(syncOperation);
+ runStateIdle();
return;
- } catch (RuntimeException exc) {
- Log.e(TAG, "Caught a RuntimeException while validating sync of " + providerName,
- exc);
+ }
+
+ ActiveSyncContext activeSyncContext =
+ new ActiveSyncContext(op, insertStartSyncEvent(op));
+ mActiveSyncContext = activeSyncContext;
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "runStateIdle: setting mActiveSyncContext to " + mActiveSyncContext);
+ }
+ mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+ if (!activeSyncContext.bindToSyncAdapter(syncAdapterInfo)) {
+ Log.e(TAG, "Bind attempt failed to " + syncAdapterInfo);
+ mActiveSyncContext = null;
+ mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+ runStateIdle();
return;
}
- final long historyRowId = insertStartSyncEvent(syncOperation);
+ mSyncWakeLock.acquire();
+ // no need to schedule an alarm, as that will be done by our caller.
+ // the next step will occur when we get either a timeout or a
+ // MESSAGE_SERVICE_CONNECTED or MESSAGE_SERVICE_DISCONNECTED message
+ }
+
+ private void runBoundToSyncAdapter(ISyncAdapter syncAdapter) {
+ mActiveSyncContext.mSyncAdapter = syncAdapter;
+ final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation;
try {
- ISyncAdapter syncAdapter = contentProvider.getSyncAdapter();
- ActiveSyncContext activeSyncContext = new ActiveSyncContext(syncOperation,
- contentProvider, syncAdapter, historyRowId);
- mSyncWakeLock.acquire();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "starting sync of " + syncOperation);
- }
- syncAdapter.startSync(activeSyncContext, syncOperation.account,
- syncOperation.extras);
- mActiveSyncContext = activeSyncContext;
- mSyncStorageEngine.setActiveSync(mActiveSyncContext);
+ syncAdapter.startSync(mActiveSyncContext, syncOperation.authority,
+ syncOperation.account, syncOperation.extras);
} catch (RemoteException remoteExc) {
if (Config.LOGD) {
Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc);
}
+ mActiveSyncContext.unBindFromSyncAdapter();
mActiveSyncContext = null;
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
rescheduleWithDelay(syncOperation);
} catch (RuntimeException exc) {
+ mActiveSyncContext.unBindFromSyncAdapter();
mActiveSyncContext = null;
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
Log.e(TAG, "Caught a RuntimeException while starting the sync " + syncOperation,
exc);
}
-
- // no need to schedule an alarm, as that will be done by our caller.
}
private void runSyncFinishedOrCanceled(SyncResult syncResult) {
boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
if (isLoggable) Log.v(TAG, "runSyncFinishedOrCanceled");
- ActiveSyncContext activeSyncContext = mActiveSyncContext;
+ final ActiveSyncContext activeSyncContext = mActiveSyncContext;
mActiveSyncContext = null;
mSyncStorageEngine.setActiveSync(mActiveSyncContext);
@@ -1642,10 +1823,12 @@ class SyncManager {
Log.v(TAG, "runSyncFinishedOrCanceled: is a cancel: operation "
+ syncOperation);
}
- try {
- activeSyncContext.mSyncAdapter.cancelSync();
- } catch (RemoteException e) {
- // we don't need to retry this in this case
+ if (activeSyncContext.mSyncAdapter != null) {
+ try {
+ activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext);
+ } catch (RemoteException e) {
+ // we don't need to retry this in this case
+ }
}
historyMessage = SyncStorageEngine.MESG_CANCELED;
downstreamActivity = 0;
@@ -1655,7 +1838,7 @@ class SyncManager {
stopSyncEvent(activeSyncContext.mHistoryRowId, syncOperation, historyMessage,
upstreamActivity, downstreamActivity, elapsedTime);
- mContentResolver.releaseProvider(activeSyncContext.mContentProvider);
+ activeSyncContext.unBindFromSyncAdapter();
if (syncResult != null && syncResult.tooManyDeletions) {
installHandleTooManyDeletesNotification(syncOperation.account,
@@ -1683,21 +1866,21 @@ class SyncManager {
*/
private int syncResultToErrorNumber(SyncResult syncResult) {
if (syncResult.syncAlreadyInProgress)
- return SyncStorageEngine.ERROR_SYNC_ALREADY_IN_PROGRESS;
+ return ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
if (syncResult.stats.numAuthExceptions > 0)
- return SyncStorageEngine.ERROR_AUTHENTICATION;
+ return ContentResolver.SYNC_ERROR_AUTHENTICATION;
if (syncResult.stats.numIoExceptions > 0)
- return SyncStorageEngine.ERROR_IO;
+ return ContentResolver.SYNC_ERROR_IO;
if (syncResult.stats.numParseExceptions > 0)
- return SyncStorageEngine.ERROR_PARSE;
+ return ContentResolver.SYNC_ERROR_PARSE;
if (syncResult.stats.numConflictDetectedExceptions > 0)
- return SyncStorageEngine.ERROR_CONFLICT;
+ return ContentResolver.SYNC_ERROR_CONFLICT;
if (syncResult.tooManyDeletions)
- return SyncStorageEngine.ERROR_TOO_MANY_DELETIONS;
+ return ContentResolver.SYNC_ERROR_TOO_MANY_DELETIONS;
if (syncResult.tooManyRetries)
- return SyncStorageEngine.ERROR_TOO_MANY_RETRIES;
+ return ContentResolver.SYNC_ERROR_TOO_MANY_RETRIES;
if (syncResult.databaseError)
- return SyncStorageEngine.ERROR_INTERNAL;
+ return ContentResolver.SYNC_ERROR_INTERNAL;
throw new IllegalStateException("we are not in an error state, " + syncResult);
}
@@ -1738,9 +1921,10 @@ class SyncManager {
} else {
final boolean timeToShowNotification =
now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY;
- final boolean syncIsForced = syncOperation.extras
- .getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
- shouldInstall = timeToShowNotification || syncIsForced;
+ // show the notification immediately if this is a manual sync
+ final boolean manualSync = syncOperation.extras
+ .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
+ shouldInstall = timeToShowNotification || manualSync;
}
}
@@ -1855,19 +2039,28 @@ class SyncManager {
private void sendSyncStateIntent() {
Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED);
+ syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
syncStateIntent.putExtra("active", mNeedSyncActiveNotification);
syncStateIntent.putExtra("failing", mNeedSyncErrorNotification);
mContext.sendBroadcast(syncStateIntent);
}
- private void installHandleTooManyDeletesNotification(String account, String authority,
+ private void installHandleTooManyDeletesNotification(Account account, String authority,
long numDeletes) {
if (mNotificationMgr == null) return;
+
+ final ProviderInfo providerInfo = mContext.getPackageManager().resolveContentProvider(
+ authority, 0 /* flags */);
+ if (providerInfo == null) {
+ return;
+ }
+ CharSequence authorityName = providerInfo.loadLabel(mContext.getPackageManager());
+
Intent clickIntent = new Intent();
clickIntent.setClassName("com.android.providers.subscribedfeeds",
"com.android.settings.SyncActivityTooManyDeletes");
clickIntent.putExtra("account", account);
- clickIntent.putExtra("provider", authority);
+ clickIntent.putExtra("provider", authorityName.toString());
clickIntent.putExtra("numDeletes", numDeletes);
if (!isActivityAvailable(clickIntent)) {
@@ -1881,14 +2074,13 @@ class SyncManager {
CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
R.string.contentServiceTooManyDeletesNotificationDesc);
- String[] authorities = authority.split(";");
Notification notification =
new Notification(R.drawable.stat_notify_sync_error,
mContext.getString(R.string.contentServiceSync),
System.currentTimeMillis());
notification.setLatestEventInfo(mContext,
mContext.getString(R.string.contentServiceSyncNotificationTitle),
- String.format(tooManyDeletesDescFormat.toString(), authorities[0]),
+ String.format(tooManyDeletesDescFormat.toString(), authorityName),
pendingIntent);
notification.flags |= Notification.FLAG_ONGOING_EVENT;
mNotificationMgr.notify(account.hashCode() ^ authority.hashCode(), notification);
@@ -1995,9 +2187,9 @@ class SyncManager {
SyncOperation existingOperation = mOpsByKey.get(operationKey);
// if this operation matches an existing operation that is being retried (delay > 0)
- // and this operation isn't forced, ignore this operation
+ // and this isn't a manual sync operation, ignore this operation
if (existingOperation != null && existingOperation.delay > 0) {
- if (!operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false)) {
+ if (!operation.extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false)) {
return false;
}
}
@@ -2071,7 +2263,7 @@ class SyncManager {
if (DEBUG_CHECK_DATA_CONSISTENCY) debugCheckDataStructures(true /* check the DB */);
}
- public void clear(String account, String authority) {
+ public void clear(Account account, String authority) {
Iterator<Map.Entry<String, SyncOperation>> entries = mOpsByKey.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, SyncOperation> entry = entries.next();
diff --git a/core/java/android/content/SyncResult.java b/core/java/android/content/SyncResult.java
index f3260f3..4c201e6 100644
--- a/core/java/android/content/SyncResult.java
+++ b/core/java/android/content/SyncResult.java
@@ -113,14 +113,19 @@ public final class SyncResult implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append(" syncAlreadyInProgress: ").append(syncAlreadyInProgress);
- sb.append(" tooManyDeletions: ").append(tooManyDeletions);
- sb.append(" tooManyRetries: ").append(tooManyRetries);
- sb.append(" databaseError: ").append(databaseError);
- sb.append(" fullSyncRequested: ").append(fullSyncRequested);
- sb.append(" partialSyncUnavailable: ").append(partialSyncUnavailable);
- sb.append(" moreRecordsToGet: ").append(moreRecordsToGet);
- sb.append(" stats: ").append(stats);
+ sb.append("SyncResult:");
+ if (syncAlreadyInProgress) {
+ sb.append(" syncAlreadyInProgress: ").append(syncAlreadyInProgress);
+ }
+ if (tooManyDeletions) sb.append(" tooManyDeletions: ").append(tooManyDeletions);
+ if (tooManyRetries) sb.append(" tooManyRetries: ").append(tooManyRetries);
+ if (databaseError) sb.append(" databaseError: ").append(databaseError);
+ if (fullSyncRequested) sb.append(" fullSyncRequested: ").append(fullSyncRequested);
+ if (partialSyncUnavailable) {
+ sb.append(" partialSyncUnavailable: ").append(partialSyncUnavailable);
+ }
+ if (moreRecordsToGet) sb.append(" moreRecordsToGet: ").append(moreRecordsToGet);
+ sb.append(stats);
return sb.toString();
}
diff --git a/core/java/android/content/SyncStateContentProviderHelper.java b/core/java/android/content/SyncStateContentProviderHelper.java
index f503e6f..64bbe25 100644
--- a/core/java/android/content/SyncStateContentProviderHelper.java
+++ b/core/java/android/content/SyncStateContentProviderHelper.java
@@ -23,6 +23,7 @@ import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
+import android.accounts.Account;
/**
* Extends the schema of a ContentProvider to include the _sync_state table
@@ -43,14 +44,15 @@ public class SyncStateContentProviderHelper {
private static final Uri CONTENT_URI =
Uri.parse("content://" + SYNC_STATE_AUTHORITY + "/state");
- private static final String ACCOUNT_WHERE = "_sync_account = ?";
+ private static final String ACCOUNT_WHERE = "_sync_account = ? AND _sync_account_type = ?";
private final Provider mInternalProviderInterface;
private static final String SYNC_STATE_TABLE = "_sync_state";
- private static long DB_VERSION = 2;
+ private static long DB_VERSION = 3;
- private static final String[] ACCOUNT_PROJECTION = new String[]{"_sync_account"};
+ private static final String[] ACCOUNT_PROJECTION =
+ new String[]{"_sync_account", "_sync_account_type"};
static {
sURIMatcher.addURI(SYNC_STATE_AUTHORITY, "state", STATE);
@@ -70,8 +72,9 @@ public class SyncStateContentProviderHelper {
db.execSQL("CREATE TABLE _sync_state (" +
"_id INTEGER PRIMARY KEY," +
"_sync_account TEXT," +
+ "_sync_account_type TEXT," +
"data TEXT," +
- "UNIQUE(_sync_account)" +
+ "UNIQUE(_sync_account, _sync_account_type)" +
");");
db.execSQL("DROP TABLE IF EXISTS _sync_state_metadata");
@@ -168,15 +171,17 @@ public class SyncStateContentProviderHelper {
* @param account the account of the row that should be copied over.
*/
public void copySyncState(SQLiteDatabase dbSrc, SQLiteDatabase dbDest,
- String account) {
- final String[] whereArgs = new String[]{account};
- Cursor c = dbSrc.query(SYNC_STATE_TABLE, new String[]{"_sync_account", "data"},
+ Account account) {
+ final String[] whereArgs = new String[]{account.name, account.type};
+ Cursor c = dbSrc.query(SYNC_STATE_TABLE,
+ new String[]{"_sync_account", "_sync_account_type", "data"},
ACCOUNT_WHERE, whereArgs, null, null, null);
try {
if (c.moveToNext()) {
ContentValues values = new ContentValues();
values.put("_sync_account", c.getString(0));
- values.put("data", c.getBlob(1));
+ values.put("_sync_account_type", c.getString(1));
+ values.put("data", c.getBlob(2));
dbDest.replace(SYNC_STATE_TABLE, "_sync_account", values);
}
} finally {
@@ -184,14 +189,17 @@ public class SyncStateContentProviderHelper {
}
}
- public void onAccountsChanged(String[] accounts) {
+ public void onAccountsChanged(Account[] accounts) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
try {
while (c.moveToNext()) {
- final String account = c.getString(0);
+ final String accountName = c.getString(0);
+ final String accountType = c.getString(1);
+ Account account = new Account(accountName, accountType);
if (!ArrayUtils.contains(accounts, account)) {
- db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
+ db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE,
+ new String[]{accountName, accountType});
}
}
} finally {
@@ -199,9 +207,9 @@ public class SyncStateContentProviderHelper {
}
}
- public void discardSyncData(SQLiteDatabase db, String account) {
+ public void discardSyncData(SQLiteDatabase db, Account account) {
if (account != null) {
- db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account});
+ db.delete(SYNC_STATE_TABLE, ACCOUNT_WHERE, new String[]{account.name, account.type});
} else {
db.delete(SYNC_STATE_TABLE, null, null);
}
@@ -210,9 +218,9 @@ public class SyncStateContentProviderHelper {
/**
* Retrieves the SyncData bytes for the given account. The byte array returned may be null.
*/
- public byte[] readSyncDataBytes(SQLiteDatabase db, String account) {
+ public byte[] readSyncDataBytes(SQLiteDatabase db, Account account) {
Cursor c = db.query(SYNC_STATE_TABLE, null, ACCOUNT_WHERE,
- new String[]{account}, null, null, null);
+ new String[]{account.name, account.type}, null, null, null);
try {
if (c.moveToFirst()) {
return c.getBlob(c.getColumnIndexOrThrow("data"));
@@ -226,9 +234,10 @@ public class SyncStateContentProviderHelper {
/**
* Sets the SyncData bytes for the given account. The bytes array may be null.
*/
- public void writeSyncDataBytes(SQLiteDatabase db, String account, byte[] data) {
+ public void writeSyncDataBytes(SQLiteDatabase db, Account account, byte[] data) {
ContentValues values = new ContentValues();
values.put("data", data);
- db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE, new String[]{account});
+ db.update(SYNC_STATE_TABLE, values, ACCOUNT_WHERE,
+ new String[]{account.name, account.type});
}
}
diff --git a/core/java/android/content/SyncStats.java b/core/java/android/content/SyncStats.java
index b561b05..cc544c0 100644
--- a/core/java/android/content/SyncStats.java
+++ b/core/java/android/content/SyncStats.java
@@ -60,15 +60,18 @@ public class SyncStats implements Parcelable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("numAuthExceptions: ").append(numAuthExceptions);
- sb.append(" numIoExceptions: ").append(numIoExceptions);
- sb.append(" numParseExceptions: ").append(numParseExceptions);
- sb.append(" numConflictDetectedExceptions: ").append(numConflictDetectedExceptions);
- sb.append(" numInserts: ").append(numInserts);
- sb.append(" numUpdates: ").append(numUpdates);
- sb.append(" numDeletes: ").append(numDeletes);
- sb.append(" numEntries: ").append(numEntries);
- sb.append(" numSkippedEntries: ").append(numSkippedEntries);
+ sb.append(" stats [");
+ if (numAuthExceptions > 0) sb.append(" numAuthExceptions: ").append(numAuthExceptions);
+ if (numIoExceptions > 0) sb.append(" numIoExceptions: ").append(numIoExceptions);
+ if (numParseExceptions > 0) sb.append(" numParseExceptions: ").append(numParseExceptions);
+ if (numConflictDetectedExceptions > 0)
+ sb.append(" numConflictDetectedExceptions: ").append(numConflictDetectedExceptions);
+ if (numInserts > 0) sb.append(" numInserts: ").append(numInserts);
+ if (numUpdates > 0) sb.append(" numUpdates: ").append(numUpdates);
+ if (numDeletes > 0) sb.append(" numDeletes: ").append(numDeletes);
+ if (numEntries > 0) sb.append(" numEntries: ").append(numEntries);
+ if (numSkippedEntries > 0) sb.append(" numSkippedEntries: ").append(numSkippedEntries);
+ sb.append("]");
return sb.toString();
}
diff --git a/core/java/android/content/SyncStatusObserver.java b/core/java/android/content/SyncStatusObserver.java
new file mode 100644
index 0000000..663378a
--- /dev/null
+++ b/core/java/android/content/SyncStatusObserver.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+public interface SyncStatusObserver {
+ void onStatusChanged(int which);
+}
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 756f35c..f251984 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -24,6 +24,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.accounts.Account;
import android.backup.IBackupManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
@@ -53,14 +54,14 @@ import java.util.TimeZone;
/**
* Singleton that tracks the sync data and overall sync
* history on the device.
- *
+ *
* @hide
*/
public class SyncStorageEngine extends Handler {
private static final String TAG = "SyncManager";
private static final boolean DEBUG = false;
private static final boolean DEBUG_FILE = false;
-
+
// @VisibleForTesting
static final long MILLIS_IN_4WEEKS = 1000L * 60 * 60 * 24 * 7 * 4;
@@ -88,6 +89,9 @@ public class SyncStorageEngine extends Handler {
/** Enum value for a user-initiated sync. */
public static final int SOURCE_USER = 3;
+ private static final Intent SYNC_CONNECTION_SETTING_CHANGED_INTENT =
+ new Intent("com.android.sync.SYNC_CONN_STATUS_CHANGED");
+
// TODO: i18n -- grab these out of resources.
/** String names for the sync source types. */
public static final String[] SOURCES = { "SERVER",
@@ -95,44 +99,30 @@ public class SyncStorageEngine extends Handler {
"POLL",
"USER" };
- // Error types
- public static final int ERROR_SYNC_ALREADY_IN_PROGRESS = 1;
- public static final int ERROR_AUTHENTICATION = 2;
- public static final int ERROR_IO = 3;
- public static final int ERROR_PARSE = 4;
- public static final int ERROR_CONFLICT = 5;
- public static final int ERROR_TOO_MANY_DELETIONS = 6;
- public static final int ERROR_TOO_MANY_RETRIES = 7;
- public static final int ERROR_INTERNAL = 8;
-
// The MESG column will contain one of these or one of the Error types.
public static final String MESG_SUCCESS = "success";
public static final String MESG_CANCELED = "canceled";
- public static final int CHANGE_SETTINGS = 1<<0;
- public static final int CHANGE_PENDING = 1<<1;
- public static final int CHANGE_ACTIVE = 1<<2;
- public static final int CHANGE_STATUS = 1<<3;
- public static final int CHANGE_ALL = 0x7fffffff;
-
public static final int MAX_HISTORY = 15;
-
+
private static final int MSG_WRITE_STATUS = 1;
private static final long WRITE_STATUS_DELAY = 1000*60*10; // 10 minutes
-
+
private static final int MSG_WRITE_STATISTICS = 2;
private static final long WRITE_STATISTICS_DELAY = 1000*60*30; // 1/2 hour
-
+
+ private static final boolean SYNC_ENABLED_DEFAULT = false;
+
public static class PendingOperation {
- final String account;
+ final Account account;
final int syncSource;
final String authority;
final Bundle extras; // note: read-only.
-
+
int authorityId;
byte[] flatExtras;
-
- PendingOperation(String account, int source,
+
+ PendingOperation(Account account, int source,
String authority, Bundle extras) {
this.account = account;
this.syncSource = source;
@@ -149,31 +139,33 @@ public class SyncStorageEngine extends Handler {
this.authorityId = other.authorityId;
}
}
-
+
static class AccountInfo {
- final String account;
+ final Account account;
final HashMap<String, AuthorityInfo> authorities =
new HashMap<String, AuthorityInfo>();
-
- AccountInfo(String account) {
+
+ AccountInfo(Account account) {
this.account = account;
}
}
-
+
public static class AuthorityInfo {
- final String account;
+ final Account account;
final String authority;
final int ident;
boolean enabled;
-
- AuthorityInfo(String account, String authority, int ident) {
+ int syncable;
+
+ AuthorityInfo(Account account, String authority, int ident) {
this.account = account;
this.authority = authority;
this.ident = ident;
- enabled = true;
+ enabled = SYNC_ENABLED_DEFAULT;
+ syncable = -1; // default to "unknown"
}
}
-
+
public static class SyncHistoryItem {
int authorityId;
int historyId;
@@ -185,69 +177,69 @@ public class SyncStorageEngine extends Handler {
long downstreamActivity;
String mesg;
}
-
+
public static class DayStats {
public final int day;
public int successCount;
public long successTime;
public int failureCount;
public long failureTime;
-
+
public DayStats(int day) {
this.day = day;
}
}
-
+
// Primary list of all syncable authorities. Also our global lock.
private final SparseArray<AuthorityInfo> mAuthorities =
new SparseArray<AuthorityInfo>();
-
- private final HashMap<String, AccountInfo> mAccounts =
- new HashMap<String, AccountInfo>();
+
+ private final HashMap<Account, AccountInfo> mAccounts =
+ new HashMap<Account, AccountInfo>();
private final ArrayList<PendingOperation> mPendingOperations =
new ArrayList<PendingOperation>();
-
+
private ActiveSyncInfo mActiveSync;
-
+
private final SparseArray<SyncStatusInfo> mSyncStatus =
new SparseArray<SyncStatusInfo>();
-
+
private final ArrayList<SyncHistoryItem> mSyncHistory =
new ArrayList<SyncHistoryItem>();
-
+
private final RemoteCallbackList<ISyncStatusObserver> mChangeListeners
= new RemoteCallbackList<ISyncStatusObserver>();
-
+
// We keep 4 weeks of stats.
private final DayStats[] mDayStats = new DayStats[7*4];
private final Calendar mCal;
private int mYear;
private int mYearInDays;
-
+
private final Context mContext;
private static volatile SyncStorageEngine sSyncStorageEngine = null;
-
+
/**
* This file contains the core engine state: all accounts and the
* settings for them. It must never be lost, and should be changed
* infrequently, so it is stored as an XML file.
*/
private final AtomicFile mAccountInfoFile;
-
+
/**
* This file contains the current sync status. We would like to retain
* it across boots, but its loss is not the end of the world, so we store
* this information as binary data.
*/
private final AtomicFile mStatusFile;
-
+
/**
* This file contains sync statistics. This is purely debugging information
* so is written infrequently and can be thrown away at any time.
*/
private final AtomicFile mStatisticsFile;
-
+
/**
* This file contains the pending sync operations. It is a binary file,
* which must be updated every time an operation is added or removed,
@@ -256,16 +248,16 @@ public class SyncStorageEngine extends Handler {
private final AtomicFile mPendingFile;
private static final int PENDING_FINISH_TO_WRITE = 4;
private int mNumPendingFinished = 0;
-
+
private int mNextHistoryId = 0;
- private boolean mListenForTickles = true;
-
+ private boolean mMasterSyncAutomatically = true;
+
private SyncStorageEngine(Context context) {
mContext = context;
sSyncStorageEngine = this;
-
+
mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
-
+
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
File syncDir = new File(systemDir, "sync");
@@ -273,7 +265,7 @@ public class SyncStorageEngine extends Handler {
mStatusFile = new AtomicFile(new File(syncDir, "status.bin"));
mPendingFile = new AtomicFile(new File(syncDir, "pending.bin"));
mStatisticsFile = new AtomicFile(new File(syncDir, "stats.bin"));
-
+
readAccountInfoLocked();
readStatusLocked();
readPendingOperationsLocked();
@@ -310,19 +302,19 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
public void addStatusChangeListener(int mask, ISyncStatusObserver callback) {
synchronized (mAuthorities) {
mChangeListeners.register(callback, mask);
}
}
-
+
public void removeStatusChangeListener(ISyncStatusObserver callback) {
synchronized (mAuthorities) {
mChangeListeners.unregister(callback);
}
}
-
+
private void reportChange(int which) {
ArrayList<ISyncStatusObserver> reports = null;
synchronized (mAuthorities) {
@@ -340,9 +332,9 @@ public class SyncStorageEngine extends Handler {
}
mChangeListeners.finishBroadcast();
}
-
+
if (DEBUG) Log.v(TAG, "reportChange " + which + " to: " + reports);
-
+
if (reports != null) {
int i = reports.size();
while (i > 0) {
@@ -366,18 +358,18 @@ public class SyncStorageEngine extends Handler {
}
}
- public boolean getSyncProviderAutomatically(String account, String providerName) {
+ public boolean getSyncAutomatically(Account account, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
AuthorityInfo authority = getAuthorityLocked(account, providerName,
- "getSyncProviderAutomatically");
- return authority != null ? authority.enabled : false;
+ "getSyncAutomatically");
+ return authority != null && authority.enabled;
}
-
+
int i = mAuthorities.size();
while (i > 0) {
i--;
- AuthorityInfo authority = mAuthorities.get(i);
+ AuthorityInfo authority = mAuthorities.valueAt(i);
if (authority.authority.equals(providerName)
&& authority.enabled) {
return true;
@@ -387,61 +379,102 @@ public class SyncStorageEngine extends Handler {
}
}
- public void setSyncProviderAutomatically(String account, String providerName, boolean sync) {
+ public void setSyncAutomatically(Account account, String providerName, boolean sync) {
+ boolean wasEnabled;
+ synchronized (mAuthorities) {
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ wasEnabled = authority.enabled;
+ authority.enabled = sync;
+ writeAccountInfoLocked();
+ }
+
+ if (!wasEnabled && sync) {
+ mContext.getContentResolver().requestSync(account, providerName, new Bundle());
+ }
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ }
+
+ public int getIsSyncable(Account account, String providerName) {
synchronized (mAuthorities) {
if (account != null) {
AuthorityInfo authority = getAuthorityLocked(account, providerName,
- "setSyncProviderAutomatically");
- if (authority != null) {
- authority.enabled = sync;
+ "getIsSyncable");
+ if (authority == null) {
+ return -1;
}
- } else {
- int i = mAuthorities.size();
- while (i > 0) {
- i--;
- AuthorityInfo authority = mAuthorities.get(i);
- if (authority.authority.equals(providerName)) {
- authority.enabled = sync;
- }
+ return authority.syncable;
+ }
+
+ int i = mAuthorities.size();
+ while (i > 0) {
+ i--;
+ AuthorityInfo authority = mAuthorities.valueAt(i);
+ if (authority.authority.equals(providerName)) {
+ return authority.syncable;
}
}
+ return -1;
+ }
+ }
+
+ public void setIsSyncable(Account account, String providerName, int syncable) {
+ int oldState;
+ if (syncable > 1) {
+ syncable = 1;
+ } else if (syncable < -1) {
+ syncable = -1;
+ }
+ Log.d(TAG, "setIsSyncable: " + account + ", provider " + providerName + " -> " + syncable);
+ synchronized (mAuthorities) {
+ AuthorityInfo authority = getOrCreateAuthorityLocked(account, providerName, -1, false);
+ oldState = authority.syncable;
+ authority.syncable = syncable;
writeAccountInfoLocked();
}
-
- reportChange(CHANGE_SETTINGS);
+
+ if (oldState <= 0 && syncable > 0) {
+ mContext.getContentResolver().requestSync(account, providerName, new Bundle());
+ }
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
}
- public void setListenForNetworkTickles(boolean flag) {
+ public void setMasterSyncAutomatically(boolean flag) {
+ boolean old;
synchronized (mAuthorities) {
- mListenForTickles = flag;
+ old = mMasterSyncAutomatically;
+ mMasterSyncAutomatically = flag;
writeAccountInfoLocked();
}
- reportChange(CHANGE_SETTINGS);
+ if (!old && flag) {
+ mContext.getContentResolver().requestSync(null, null, new Bundle());
+ }
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
+ mContext.sendBroadcast(SYNC_CONNECTION_SETTING_CHANGED_INTENT);
}
- public boolean getListenForNetworkTickles() {
+ public boolean getMasterSyncAutomatically() {
synchronized (mAuthorities) {
- return mListenForTickles;
+ return mMasterSyncAutomatically;
}
}
-
- public AuthorityInfo getAuthority(String account, String authority) {
+
+ public AuthorityInfo getAuthority(Account account, String authority) {
synchronized (mAuthorities) {
return getAuthorityLocked(account, authority, null);
}
}
-
+
public AuthorityInfo getAuthority(int authorityId) {
synchronized (mAuthorities) {
return mAuthorities.get(authorityId);
}
}
-
+
/**
* Returns true if there is currently a sync operation for the given
* account or authority in the pending list, or actively being processed.
*/
- public boolean isSyncActive(String account, String authority) {
+ public boolean isSyncActive(Account account, String authority) {
synchronized (mAuthorities) {
int i = mPendingOperations.size();
while (i > 0) {
@@ -453,7 +486,7 @@ public class SyncStorageEngine extends Handler {
return true;
}
}
-
+
if (mActiveSync != null) {
AuthorityInfo ainfo = getAuthority(mActiveSync.authorityId);
if (ainfo != null && ainfo.account.equals(account)
@@ -462,17 +495,17 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
return false;
}
-
+
public PendingOperation insertIntoPending(PendingOperation op) {
synchronized (mAuthorities) {
if (DEBUG) Log.v(TAG, "insertIntoPending: account=" + op.account
+ " auth=" + op.authority
+ " src=" + op.syncSource
+ " extras=" + op.extras);
-
+
AuthorityInfo authority = getOrCreateAuthorityLocked(op.account,
op.authority,
-1 /* desired identifier */,
@@ -480,17 +513,17 @@ public class SyncStorageEngine extends Handler {
if (authority == null) {
return null;
}
-
+
op = new PendingOperation(op);
op.authorityId = authority.ident;
mPendingOperations.add(op);
appendPendingOperationLocked(op);
-
+
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = true;
}
-
- reportChange(CHANGE_PENDING);
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
return op;
}
@@ -509,7 +542,7 @@ public class SyncStorageEngine extends Handler {
} else {
mNumPendingFinished++;
}
-
+
AuthorityInfo authority = getAuthorityLocked(op.account, op.authority,
"deleteFromPending");
if (authority != null) {
@@ -524,19 +557,19 @@ public class SyncStorageEngine extends Handler {
break;
}
}
-
+
if (!morePending) {
if (DEBUG) Log.v(TAG, "no more pending!");
SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident);
status.pending = false;
}
}
-
+
res = true;
}
}
-
- reportChange(CHANGE_PENDING);
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
return res;
}
@@ -548,11 +581,11 @@ public class SyncStorageEngine extends Handler {
mPendingOperations.clear();
final int N = mSyncStatus.size();
for (int i=0; i<N; i++) {
- mSyncStatus.get(i).pending = false;
+ mSyncStatus.valueAt(i).pending = false;
}
writePendingOperationsLocked();
}
- reportChange(CHANGE_PENDING);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_PENDING);
return num;
}
@@ -566,7 +599,7 @@ public class SyncStorageEngine extends Handler {
return new ArrayList<PendingOperation>(mPendingOperations);
}
}
-
+
/**
* Return the number of currently pending operations.
*/
@@ -575,12 +608,12 @@ public class SyncStorageEngine extends Handler {
return mPendingOperations.size();
}
}
-
+
/**
* Called when the set of account has changed, given the new array of
* active accounts.
*/
- public void doDatabaseCleanup(String[] accounts) {
+ public void doDatabaseCleanup(Account[] accounts) {
synchronized (mAuthorities) {
if (DEBUG) Log.w(TAG, "Updating for new accounts...");
SparseArray<AuthorityInfo> removing = new SparseArray<AuthorityInfo>();
@@ -596,7 +629,7 @@ public class SyncStorageEngine extends Handler {
accIt.remove();
}
}
-
+
// Clean out all data structures.
int i = removing.size();
if (i > 0) {
@@ -658,21 +691,21 @@ public class SyncStorageEngine extends Handler {
mActiveSync = null;
}
}
-
- reportChange(CHANGE_ACTIVE);
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
}
/**
* To allow others to send active change reports, to poke clients.
*/
public void reportActiveChange() {
- reportChange(CHANGE_ACTIVE);
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_ACTIVE);
}
-
+
/**
* Note that sync has started for the given account and authority.
*/
- public long insertStartSyncEvent(String accountName, String authorityName,
+ public long insertStartSyncEvent(Account accountName, String authorityName,
long now, int source) {
long id;
synchronized (mAuthorities) {
@@ -697,8 +730,8 @@ public class SyncStorageEngine extends Handler {
id = item.historyId;
if (DEBUG) Log.v(TAG, "returning historyId " + id);
}
-
- reportChange(CHANGE_STATUS);
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
return id;
}
@@ -716,20 +749,20 @@ public class SyncStorageEngine extends Handler {
}
item = null;
}
-
+
if (item == null) {
Log.w(TAG, "stopSyncEvent: no history for id " + historyId);
return;
}
-
+
item.elapsedTime = elapsedTime;
item.event = EVENT_STOP;
item.mesg = resultMessage;
item.downstreamActivity = downstreamActivity;
item.upstreamActivity = upstreamActivity;
-
+
SyncStatusInfo status = getOrCreateSyncStatusLocked(item.authorityId);
-
+
status.numSyncs++;
status.totalElapsedTime += elapsedTime;
switch (item.source) {
@@ -746,7 +779,7 @@ public class SyncStorageEngine extends Handler {
status.numSourceServer++;
break;
}
-
+
boolean writeStatisticsNow = false;
int day = getCurrentDayLocked();
if (mDayStats[0] == null) {
@@ -758,7 +791,7 @@ public class SyncStorageEngine extends Handler {
} else if (mDayStats[0] == null) {
}
final DayStats ds = mDayStats[0];
-
+
final long lastSyncTime = (item.eventTime + elapsedTime);
boolean writeStatusNow = false;
if (MESG_SUCCESS.equals(resultMessage)) {
@@ -787,7 +820,7 @@ public class SyncStorageEngine extends Handler {
ds.failureCount++;
ds.failureTime += elapsedTime;
}
-
+
if (writeStatusNow) {
writeStatusLocked();
} else if (!hasMessages(MSG_WRITE_STATUS)) {
@@ -799,10 +832,10 @@ public class SyncStorageEngine extends Handler {
} else if (!hasMessages(MSG_WRITE_STATISTICS)) {
sendMessageDelayed(obtainMessage(MSG_WRITE_STATISTICS),
WRITE_STATISTICS_DELAY);
- }
+ }
}
-
- reportChange(CHANGE_STATUS);
+
+ reportChange(ContentResolver.SYNC_OBSERVER_TYPE_STATUS);
}
/**
@@ -815,7 +848,7 @@ public class SyncStorageEngine extends Handler {
return mActiveSync;
}
}
-
+
/**
* Return an array of the current sync status for all authorities. Note
* that the objects inside the array are the real, live status objects,
@@ -831,40 +864,41 @@ public class SyncStorageEngine extends Handler {
return ops;
}
}
-
+
/**
- * Returns the status that matches the authority. If there are multiples accounts for
- * the authority, the one with the latest "lastSuccessTime" status is returned.
+ * Returns the status that matches the authority and account.
+ *
+ * @param account the account we want to check
* @param authority the authority whose row should be selected
* @return the SyncStatusInfo for the authority, or null if none exists
*/
- public SyncStatusInfo getStatusByAuthority(String authority) {
+ public SyncStatusInfo getStatusByAccountAndAuthority(Account account, String authority) {
+ if (account == null || authority == null) {
+ throw new IllegalArgumentException();
+ }
synchronized (mAuthorities) {
- SyncStatusInfo best = null;
final int N = mSyncStatus.size();
for (int i=0; i<N; i++) {
- SyncStatusInfo cur = mSyncStatus.get(i);
+ SyncStatusInfo cur = mSyncStatus.valueAt(i);
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
- if (ainfo != null && ainfo.authority.equals(authority)) {
- if (best == null) {
- best = cur;
- } else if (best.lastSuccessTime > cur.lastSuccessTime) {
- best = cur;
- }
+
+ if (ainfo != null && ainfo.authority.equals(authority) &&
+ account.equals(ainfo.account)) {
+ return cur;
}
}
- return best;
+ return null;
}
}
-
+
/**
* Return true if the pending status is true of any matching authorities.
*/
- public boolean isAuthorityPending(String account, String authority) {
+ public boolean isSyncPending(Account account, String authority) {
synchronized (mAuthorities) {
final int N = mSyncStatus.size();
for (int i=0; i<N; i++) {
- SyncStatusInfo cur = mSyncStatus.get(i);
+ SyncStatusInfo cur = mSyncStatus.valueAt(i);
AuthorityInfo ainfo = mAuthorities.get(cur.authorityId);
if (ainfo == null) {
continue;
@@ -895,7 +929,7 @@ public class SyncStorageEngine extends Handler {
return items;
}
}
-
+
/**
* Return an array of the current per-day statistics. Note
* that the objects inside the array are the real, live status objects,
@@ -908,7 +942,7 @@ public class SyncStorageEngine extends Handler {
return ds;
}
}
-
+
/**
* If sync is failing for any of the provider/accounts then determine the time at which it
* started failing and return the earliest time over all the provider/accounts. If none are
@@ -916,10 +950,10 @@ public class SyncStorageEngine extends Handler {
*/
public long getInitialSyncFailureTime() {
synchronized (mAuthorities) {
- if (!mListenForTickles) {
+ if (!mMasterSyncAutomatically) {
return 0;
}
-
+
long oldest = 0;
int i = mSyncStatus.size();
while (i > 0) {
@@ -932,11 +966,11 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
return oldest;
}
}
-
+
private int getCurrentDayLocked() {
mCal.setTimeInMillis(System.currentTimeMillis());
final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR);
@@ -948,36 +982,40 @@ public class SyncStorageEngine extends Handler {
}
return dayOfYear + mYearInDays;
}
-
+
/**
* Retrieve an authority, returning null if one does not exist.
- *
+ *
* @param accountName The name of the account for the authority.
* @param authorityName The name of the authority itself.
* @param tag If non-null, this will be used in a log message if the
* requested authority does not exist.
*/
- private AuthorityInfo getAuthorityLocked(String accountName, String authorityName,
+ private AuthorityInfo getAuthorityLocked(Account accountName, String authorityName,
String tag) {
AccountInfo account = mAccounts.get(accountName);
if (account == null) {
if (tag != null) {
- Log.w(TAG, tag + ": unknown account " + accountName);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, tag + ": unknown account " + accountName);
+ }
}
return null;
}
AuthorityInfo authority = account.authorities.get(authorityName);
if (authority == null) {
if (tag != null) {
- Log.w(TAG, tag + ": unknown authority " + authorityName);
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, tag + ": unknown authority " + authorityName);
+ }
}
return null;
}
-
+
return authority;
}
-
- private AuthorityInfo getOrCreateAuthorityLocked(String accountName,
+
+ private AuthorityInfo getOrCreateAuthorityLocked(Account accountName,
String authorityName, int ident, boolean doWrite) {
AccountInfo account = mAccounts.get(accountName);
if (account == null) {
@@ -997,6 +1035,8 @@ public class SyncStorageEngine extends Handler {
ident++;
}
}
+ Log.d(TAG, "created a new AuthorityInfo for " + accountName
+ + ", provider " + authorityName);
authority = new AuthorityInfo(accountName, authorityName, ident);
account.authorities.put(authorityName, authority);
mAuthorities.put(ident, authority);
@@ -1004,10 +1044,10 @@ public class SyncStorageEngine extends Handler {
writeAccountInfoLocked();
}
}
-
+
return authority;
}
-
+
private SyncStatusInfo getOrCreateSyncStatusLocked(int authorityId) {
SyncStatusInfo status = mSyncStatus.get(authorityId);
if (status == null) {
@@ -1016,22 +1056,22 @@ public class SyncStorageEngine extends Handler {
}
return status;
}
-
+
public void writeAllState() {
synchronized (mAuthorities) {
// Account info is always written so no need to do it here.
-
+
if (mNumPendingFinished > 0) {
// Only write these if they are out of date.
writePendingOperationsLocked();
}
-
+
// Just always write these... they are likely out of date.
writeStatusLocked();
writeStatisticsLocked();
}
}
-
+
/**
* Read all account information back in to the initial engine state.
*/
@@ -1050,7 +1090,7 @@ public class SyncStorageEngine extends Handler {
if ("accounts".equals(tagName)) {
String listen = parser.getAttributeValue(
null, "listen-for-tickles");
- mListenForTickles = listen == null
+ mMasterSyncAutomatically = listen == null
|| Boolean.parseBoolean(listen);
eventType = parser.next();
do {
@@ -1068,26 +1108,43 @@ public class SyncStorageEngine extends Handler {
if (id >= 0) {
String accountName = parser.getAttributeValue(
null, "account");
+ String accountType = parser.getAttributeValue(
+ null, "type");
+ if (accountType == null) {
+ accountType = "com.google.GAIA";
+ }
String authorityName = parser.getAttributeValue(
null, "authority");
String enabled = parser.getAttributeValue(
null, "enabled");
- AuthorityInfo authority = mAuthorities.get(id);
+ String syncable = parser.getAttributeValue(null, "syncable");
+ AuthorityInfo authority = mAuthorities.get(id);
if (DEBUG_FILE) Log.v(TAG, "Adding authority: account="
+ accountName + " auth=" + authorityName
- + " enabled=" + enabled);
+ + " enabled=" + enabled
+ + " syncable=" + syncable);
if (authority == null) {
if (DEBUG_FILE) Log.v(TAG, "Creating entry");
authority = getOrCreateAuthorityLocked(
- accountName, authorityName, id, false);
+ new Account(accountName, accountType),
+ authorityName, id, false);
}
if (authority != null) {
authority.enabled = enabled == null
|| Boolean.parseBoolean(enabled);
+ if ("unknown".equals(syncable)) {
+ authority.syncable = -1;
+ } else {
+ authority.syncable =
+ (syncable == null || Boolean.parseBoolean(enabled))
+ ? 1
+ : 0;
+ }
} else {
Log.w(TAG, "Failure adding authority: account="
+ accountName + " auth=" + authorityName
- + " enabled=" + enabled);
+ + " enabled=" + enabled
+ + " syncable=" + syncable);
}
}
}
@@ -1109,43 +1166,49 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
/**
* Write all account information to the account file.
*/
private void writeAccountInfoLocked() {
if (DEBUG_FILE) Log.v(TAG, "Writing new " + mAccountInfoFile.getBaseFile());
FileOutputStream fos = null;
-
+
try {
fos = mAccountInfoFile.startWrite();
XmlSerializer out = new FastXmlSerializer();
out.setOutput(fos, "utf-8");
out.startDocument(null, true);
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
+
out.startTag(null, "accounts");
- if (!mListenForTickles) {
+ if (!mMasterSyncAutomatically) {
out.attribute(null, "listen-for-tickles", "false");
}
-
+
final int N = mAuthorities.size();
for (int i=0; i<N; i++) {
- AuthorityInfo authority = mAuthorities.get(i);
+ AuthorityInfo authority = mAuthorities.valueAt(i);
out.startTag(null, "authority");
out.attribute(null, "id", Integer.toString(authority.ident));
- out.attribute(null, "account", authority.account);
+ out.attribute(null, "account", authority.account.name);
+ out.attribute(null, "type", authority.account.type);
out.attribute(null, "authority", authority.authority);
if (!authority.enabled) {
out.attribute(null, "enabled", "false");
}
+ if (authority.syncable < 0) {
+ out.attribute(null, "syncable", "unknown");
+ } else if (authority.syncable == 0) {
+ out.attribute(null, "syncable", "false");
+ }
out.endTag(null, "authority");
}
-
+
out.endTag(null, "accounts");
-
+
out.endDocument();
-
+
mAccountInfoFile.finishWrite(fos);
} catch (java.io.IOException e1) {
Log.w(TAG, "Error writing accounts", e1);
@@ -1154,15 +1217,15 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
static int getIntColumn(Cursor c, String name) {
return c.getInt(c.getColumnIndex(name));
}
-
+
static long getLongColumn(Cursor c, String name) {
return c.getLong(c.getColumnIndex(name));
}
-
+
/**
* Load sync engine state from the old syncmanager database, and then
* erase it. Note that we don't deal with pending operations, active
@@ -1181,8 +1244,10 @@ public class SyncStorageEngine extends Handler {
SQLiteDatabase.OPEN_READONLY);
} catch (SQLiteException e) {
}
-
+
if (db != null) {
+ final boolean hasType = db.getVersion() >= 11;
+
// Copy in all of the status information, as well as accounts.
if (DEBUG_FILE) Log.v(TAG, "Reading legacy sync accounts db");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -1190,6 +1255,9 @@ public class SyncStorageEngine extends Handler {
HashMap<String,String> map = new HashMap<String,String>();
map.put("_id", "status._id as _id");
map.put("account", "stats.account as account");
+ if (hasType) {
+ map.put("account_type", "stats.account_type as account_type");
+ }
map.put("authority", "stats.authority as authority");
map.put("totalElapsedTime", "totalElapsedTime");
map.put("numSyncs", "numSyncs");
@@ -1208,16 +1276,22 @@ public class SyncStorageEngine extends Handler {
Cursor c = qb.query(db, null, null, null, null, null, null);
while (c.moveToNext()) {
String accountName = c.getString(c.getColumnIndex("account"));
+ String accountType = hasType
+ ? c.getString(c.getColumnIndex("account_type")) : null;
+ if (accountType == null) {
+ accountType = "com.google.GAIA";
+ }
String authorityName = c.getString(c.getColumnIndex("authority"));
AuthorityInfo authority = this.getOrCreateAuthorityLocked(
- accountName, authorityName, -1, false);
+ new Account(accountName, accountType),
+ authorityName, -1, false);
if (authority != null) {
int i = mSyncStatus.size();
boolean found = false;
SyncStatusInfo st = null;
while (i > 0) {
i--;
- st = mSyncStatus.get(i);
+ st = mSyncStatus.valueAt(i);
if (st.authorityId == authority.ident) {
found = true;
break;
@@ -1241,9 +1315,9 @@ public class SyncStorageEngine extends Handler {
st.pending = getIntColumn(c, "pending") != 0;
}
}
-
+
c.close();
-
+
// Retrieve the settings.
qb = new SQLiteQueryBuilder();
qb.setTables("settings");
@@ -1253,29 +1327,35 @@ public class SyncStorageEngine extends Handler {
String value = c.getString(c.getColumnIndex("value"));
if (name == null) continue;
if (name.equals("listen_for_tickles")) {
- setListenForNetworkTickles(value == null
- || Boolean.parseBoolean(value));
+ setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value));
} else if (name.startsWith("sync_provider_")) {
String provider = name.substring("sync_provider_".length(),
name.length());
- setSyncProviderAutomatically(null, provider,
- value == null || Boolean.parseBoolean(value));
+ int i = mAuthorities.size();
+ while (i > 0) {
+ i--;
+ AuthorityInfo authority = mAuthorities.valueAt(i);
+ if (authority.authority.equals(provider)) {
+ authority.enabled = value == null || Boolean.parseBoolean(value);
+ authority.syncable = 1;
+ }
+ }
}
}
-
+
c.close();
-
+
db.close();
-
+
writeAccountInfoLocked();
writeStatusLocked();
(new File(path)).delete();
}
}
-
+
public static final int STATUS_FILE_END = 0;
public static final int STATUS_FILE_ITEM = 100;
-
+
/**
* Read all sync status back in to the initial engine state.
*/
@@ -1306,17 +1386,17 @@ public class SyncStorageEngine extends Handler {
Log.i(TAG, "No initial status");
}
}
-
+
/**
* Write all sync status to the sync status file.
*/
private void writeStatusLocked() {
if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatusFile.getBaseFile());
-
+
// The file is being written, so we don't need to have a scheduled
// write until the next change.
removeMessages(MSG_WRITE_STATUS);
-
+
FileOutputStream fos = null;
try {
fos = mStatusFile.startWrite();
@@ -1330,7 +1410,7 @@ public class SyncStorageEngine extends Handler {
out.writeInt(STATUS_FILE_END);
fos.write(out.marshall());
out.recycle();
-
+
mStatusFile.finishWrite(fos);
} catch (java.io.IOException e1) {
Log.w(TAG, "Error writing status", e1);
@@ -1339,9 +1419,9 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
public static final int PENDING_OPERATION_VERSION = 1;
-
+
/**
* Read all pending operations back in to the initial engine state.
*/
@@ -1385,7 +1465,7 @@ public class SyncStorageEngine extends Handler {
Log.i(TAG, "No initial pending operations");
}
}
-
+
private void writePendingOperationLocked(PendingOperation op, Parcel out) {
out.writeInt(PENDING_OPERATION_VERSION);
out.writeInt(op.authorityId);
@@ -1395,7 +1475,7 @@ public class SyncStorageEngine extends Handler {
}
out.writeByteArray(op.flatExtras);
}
-
+
/**
* Write all currently pending ops to the pending ops file.
*/
@@ -1408,10 +1488,10 @@ public class SyncStorageEngine extends Handler {
mPendingFile.truncate();
return;
}
-
+
if (DEBUG_FILE) Log.v(TAG, "Writing new " + mPendingFile.getBaseFile());
fos = mPendingFile.startWrite();
-
+
Parcel out = Parcel.obtain();
for (int i=0; i<N; i++) {
PendingOperation op = mPendingOperations.get(i);
@@ -1419,7 +1499,7 @@ public class SyncStorageEngine extends Handler {
}
fos.write(out.marshall());
out.recycle();
-
+
mPendingFile.finishWrite(fos);
} catch (java.io.IOException e1) {
Log.w(TAG, "Error writing pending operations", e1);
@@ -1428,7 +1508,7 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
/**
* Append the given operation to the pending ops file; if unable to,
* write all pending ops.
@@ -1443,7 +1523,7 @@ public class SyncStorageEngine extends Handler {
writePendingOperationsLocked();
return;
}
-
+
try {
Parcel out = Parcel.obtain();
writePendingOperationLocked(op, out);
@@ -1458,7 +1538,7 @@ public class SyncStorageEngine extends Handler {
}
}
}
-
+
static private byte[] flattenBundle(Bundle bundle) {
byte[] flatData = null;
Parcel parcel = Parcel.obtain();
@@ -1470,7 +1550,7 @@ public class SyncStorageEngine extends Handler {
}
return flatData;
}
-
+
static private Bundle unflattenBundle(byte[] flatData) {
Bundle bundle;
Parcel parcel = Parcel.obtain();
@@ -1487,11 +1567,11 @@ public class SyncStorageEngine extends Handler {
}
return bundle;
}
-
+
public static final int STATISTICS_FILE_END = 0;
public static final int STATISTICS_FILE_ITEM_OLD = 100;
public static final int STATISTICS_FILE_ITEM = 101;
-
+
/**
* Read all sync statistics back in to the initial engine state.
*/
@@ -1529,17 +1609,17 @@ public class SyncStorageEngine extends Handler {
Log.i(TAG, "No initial statistics");
}
}
-
+
/**
* Write all sync statistics to the sync status file.
*/
private void writeStatisticsLocked() {
if (DEBUG_FILE) Log.v(TAG, "Writing new " + mStatisticsFile.getBaseFile());
-
+
// The file is being written, so we don't need to have a scheduled
// write until the next change.
removeMessages(MSG_WRITE_STATISTICS);
-
+
FileOutputStream fos = null;
try {
fos = mStatisticsFile.startWrite();
@@ -1560,7 +1640,7 @@ public class SyncStorageEngine extends Handler {
out.writeInt(STATISTICS_FILE_END);
fos.write(out.marshall());
out.recycle();
-
+
mStatisticsFile.finishWrite(fos);
} catch (java.io.IOException e1) {
Log.w(TAG, "Error writing stats", e1);
diff --git a/core/java/android/content/SyncableContentProvider.java b/core/java/android/content/SyncableContentProvider.java
index e0cd786..ab4e91c 100644
--- a/core/java/android/content/SyncableContentProvider.java
+++ b/core/java/android/content/SyncableContentProvider.java
@@ -19,6 +19,7 @@ package android.content;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
+import android.accounts.Account;
import java.util.Map;
@@ -32,6 +33,16 @@ import java.util.Map;
public abstract class SyncableContentProvider extends ContentProvider {
protected abstract boolean isTemporary();
+ private volatile TempProviderSyncAdapter mTempProviderSyncAdapter;
+
+ public void setTempProviderSyncAdapter(TempProviderSyncAdapter syncAdapter) {
+ mTempProviderSyncAdapter = syncAdapter;
+ }
+
+ public TempProviderSyncAdapter getTempProviderSyncAdapter() {
+ return mTempProviderSyncAdapter;
+ }
+
/**
* Close resources that must be closed. You must call this to properly release
* the resources used by the SyncableContentProvider.
@@ -110,7 +121,7 @@ public abstract class SyncableContentProvider extends ContentProvider {
* @param context the sync context for the operation
* @param account
*/
- public abstract void onSyncStart(SyncContext context, String account);
+ public abstract void onSyncStart(SyncContext context, Account account);
/**
* Called right after a sync is completed
@@ -124,7 +135,7 @@ public abstract class SyncableContentProvider extends ContentProvider {
* The account of the most recent call to onSyncStart()
* @return the account
*/
- public abstract String getSyncingAccount();
+ public abstract Account getSyncingAccount();
/**
* Merge diffs from a sync source with this content provider.
@@ -194,7 +205,7 @@ public abstract class SyncableContentProvider extends ContentProvider {
* Make sure that there are no entries for accounts that no longer exist
* @param accountsArray the array of currently-existing accounts
*/
- protected abstract void onAccountsChanged(String[] accountsArray);
+ protected abstract void onAccountsChanged(Account[] accountsArray);
/**
* A helper method to delete all rows whose account is not in the accounts
@@ -203,26 +214,24 @@ public abstract class SyncableContentProvider extends ContentProvider {
*
* @param accounts a map of existing accounts
* @param table the table to delete from
- * @param accountColumnName the name of the column that is expected
- * to hold the account.
*/
- protected abstract void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts,
- String table, String accountColumnName);
+ protected abstract void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts,
+ String table);
/**
* Called when the sync system determines that this provider should no longer
* contain records for the specified account.
*/
- public abstract void wipeAccount(String account);
+ public abstract void wipeAccount(Account account);
/**
* Retrieves the SyncData bytes for the given account. The byte array returned may be null.
*/
- public abstract byte[] readSyncDataBytes(String account);
+ public abstract byte[] readSyncDataBytes(Account account);
/**
* Sets the SyncData bytes for the given account. The bytes array may be null.
*/
- public abstract void writeSyncDataBytes(String account, byte[] data);
+ public abstract void writeSyncDataBytes(Account account, byte[] data);
}
diff --git a/core/java/android/content/TempProviderSyncAdapter.java b/core/java/android/content/TempProviderSyncAdapter.java
index eb3a5da..b46c545 100644
--- a/core/java/android/content/TempProviderSyncAdapter.java
+++ b/core/java/android/content/TempProviderSyncAdapter.java
@@ -12,6 +12,11 @@ import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.TimingLogger;
+import android.accounts.Account;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import java.io.IOException;
/**
* @hide
@@ -62,12 +67,10 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
*
* @param context allows you to publish status and interact with the
* @param account the account to sync
- * @param forced if true then the sync was forced
+ * @param manualSync true if this sync was requested manually by the user
* @param result information to track what happened during this sync attempt
- * @return true, if the sync was successfully started. One reason it can
- * fail to start is if there is no user configured on the device.
*/
- public abstract void onSyncStarting(SyncContext context, String account, boolean forced,
+ public abstract void onSyncStarting(SyncContext context, Account account, boolean manualSync,
SyncResult result);
/**
@@ -85,6 +88,9 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
*/
public abstract boolean isReadOnly();
+ public abstract boolean getIsSyncable(Account account)
+ throws IOException, AuthenticatorException, OperationCanceledException;
+
/**
* Get diffs from the server since the last completed sync and put them
* into a temporary provider.
@@ -168,12 +174,13 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
* exist.
* @param accounts the list of accounts
*/
- public abstract void onAccountsChanged(String[] accounts);
+ public abstract void onAccountsChanged(Account[] accounts);
private Context mContext;
private class SyncThread extends Thread {
- private final String mAccount;
+ private final Account mAccount;
+ private final String mAuthority;
private final Bundle mExtras;
private final SyncContext mSyncContext;
private volatile boolean mIsCanceled = false;
@@ -181,9 +188,10 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
private long mInitialRxBytes;
private final SyncResult mResult;
- SyncThread(SyncContext syncContext, String account, Bundle extras) {
+ SyncThread(SyncContext syncContext, Account account, String authority, Bundle extras) {
super("SyncThread");
mAccount = account;
+ mAuthority = authority;
mExtras = extras;
mSyncContext = syncContext;
mResult = new SyncResult();
@@ -207,7 +215,7 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
mInitialTxBytes = NetStat.getUidTxBytes(uid);
mInitialRxBytes = NetStat.getUidRxBytes(uid);
try {
- sync(mSyncContext, mAccount, mExtras);
+ sync(mSyncContext, mAccount, mAuthority, mExtras);
} catch (SQLException e) {
Log.e(TAG, "Sync failed", e);
mResult.databaseError = true;
@@ -221,19 +229,45 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
}
}
- private void sync(SyncContext syncContext, String account, Bundle extras) {
+ private void sync(SyncContext syncContext, Account account, String authority,
+ Bundle extras) {
mIsCanceled = false;
mProviderSyncStarted = false;
mAdapterSyncStarted = false;
String message = null;
- boolean syncForced = extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false);
+ // always attempt to initialize if the isSyncable state isn't set yet
+ int isSyncable = ContentResolver.getIsSyncable(account, authority);
+ if (isSyncable < 0) {
+ try {
+ isSyncable = (getIsSyncable(account)) ? 1 : 0;
+ ContentResolver.setIsSyncable(account, authority, isSyncable);
+ } catch (IOException e) {
+ ++mResult.stats.numIoExceptions;
+ } catch (AuthenticatorException e) {
+ ++mResult.stats.numParseExceptions;
+ } catch (OperationCanceledException e) {
+ // do nothing
+ }
+ }
+
+ // if this is an initialization request then our work is done here
+ if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
+ return;
+ }
+
+ // if we aren't syncable then get out
+ if (isSyncable <= 0) {
+ return;
+ }
+
+ boolean manualSync = extras.getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
try {
mProvider.onSyncStart(syncContext, account);
mProviderSyncStarted = true;
- onSyncStarting(syncContext, account, syncForced, mResult);
+ onSyncStarting(syncContext, account, manualSync, mResult);
if (mResult.hasError()) {
message = "SyncAdapter failed while trying to start sync";
return;
@@ -273,7 +307,7 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
}
}
- private void runSyncLoop(SyncContext syncContext, String account, Bundle extras) {
+ private void runSyncLoop(SyncContext syncContext, Account account, Bundle extras) {
TimingLogger syncTimer = new TimingLogger(TAG + "Profiling", "sync");
syncTimer.addSplit("start");
int loopCount = 0;
@@ -518,13 +552,14 @@ public abstract class TempProviderSyncAdapter extends SyncAdapter {
EventLog.writeEvent(SyncAdapter.LOG_SYNC_DETAILS, TAG, bytesSent, bytesReceived, "");
}
- public void startSync(SyncContext syncContext, String account, Bundle extras) {
+ public void startSync(SyncContext syncContext, Account account, String authority,
+ Bundle extras) {
if (mSyncThread != null) {
syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS);
return;
}
- mSyncThread = new SyncThread(syncContext, account, extras);
+ mSyncThread = new SyncThread(syncContext, account, authority, extras);
mSyncThread.start();
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0a42a6f..8839f95 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -184,7 +184,29 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@hide}
*/
public static final int FLAG_ALLOW_BACKUP = 1<<14;
-
+
+ /**
+ * Value for {@link #flags}: this is false if the application has set
+ * its android:killAfterRestore to false, true otherwise.
+ *
+ * <p>If android:allowBackup is set to false or no android:backupAgent
+ * is specified, this flag will be ignored.
+ *
+ * {@hide}
+ */
+ public static final int FLAG_KILL_AFTER_RESTORE = 1<<15;
+
+ /**
+ * Value for {@link #flags}: this is true if the application has set
+ * its android:restoreNeedsApplication to true, false otherwise.
+ *
+ * <p>If android:allowBackup is set to false or no android:backupAgent
+ * is specified, this flag will be ignored.
+ *
+ * {@hide}
+ */
+ public static final int FLAG_RESTORE_NEEDS_APPLICATION = 1<<16;
+
/**
* Flags associated with the application. Any combination of
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
@@ -193,7 +215,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
* {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
* {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
- * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS}.
+ * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
+ * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}.
*/
public int flags = 0;
diff --git a/core/java/android/content/pm/ConfigurationInfo.java b/core/java/android/content/pm/ConfigurationInfo.java
index fb7a47f..8edd436 100755
--- a/core/java/android/content/pm/ConfigurationInfo.java
+++ b/core/java/android/content/pm/ConfigurationInfo.java
@@ -22,9 +22,9 @@ import android.os.Parcelable;
/**
* Information you can retrieve about hardware configuration preferences
* declared by an application. This corresponds to information collected from the
- * AndroidManifest.xml's &lt;uses-configuration&gt; and the &lt;uses-feature&gt;tags.
+ * AndroidManifest.xml's &lt;uses-configuration&gt; and &lt;uses-feature&gt; tags.
*/
-public class ConfigurationInfo implements Parcelable {
+public class ConfigurationInfo implements Parcelable {
/**
* The kind of touch screen attached to the device.
* One of: {@link android.content.res.Configuration#TOUCHSCREEN_NOTOUCH},
@@ -92,13 +92,13 @@ public class ConfigurationInfo implements Parcelable {
}
public String toString() {
- return "ApplicationHardwarePreferences{"
+ return "ConfigurationInfo{"
+ Integer.toHexString(System.identityHashCode(this))
- + ", touchscreen = " + reqTouchScreen + "}"
- + ", inputMethod = " + reqKeyboardType + "}"
- + ", navigation = " + reqNavigation + "}"
- + ", reqInputFeatures = " + reqInputFeatures + "}"
- + ", reqGlEsVersion = " + reqGlEsVersion + "}";
+ + " touchscreen = " + reqTouchScreen
+ + " inputMethod = " + reqKeyboardType
+ + " navigation = " + reqNavigation
+ + " reqInputFeatures = " + reqInputFeatures
+ + " reqGlEsVersion = " + reqGlEsVersion + "}";
}
public int describeContents() {
diff --git a/core/java/android/content/pm/FeatureInfo.aidl b/core/java/android/content/pm/FeatureInfo.aidl
new file mode 100755
index 0000000..d84a84c
--- /dev/null
+++ b/core/java/android/content/pm/FeatureInfo.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm;
+
+parcelable FeatureInfo;
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
new file mode 100644
index 0000000..57d61fd
--- /dev/null
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -0,0 +1,101 @@
+package android.content.pm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.Parcelable.Creator;
+
+/**
+ * A single feature that can be requested by an application. This corresponds
+ * to information collected from the
+ * AndroidManifest.xml's &lt;uses-feature&gt; tag.
+ */
+public class FeatureInfo implements Parcelable {
+ /**
+ * The name of this feature, for example "android.hardware.camera". If
+ * this is null, then this is an OpenGL ES version feature as described
+ * in {@link #reqGlEsVersion}.
+ */
+ public String name;
+
+ /**
+ * Default value for {@link #reqGlEsVersion};
+ */
+ public static final int GL_ES_VERSION_UNDEFINED = 0;
+
+ /**
+ * The GLES version used by an application. The upper order 16 bits represent the
+ * major version and the lower order 16 bits the minor version. Only valid
+ * if {@link #name} is null.
+ */
+ public int reqGlEsVersion;
+
+ /**
+ * Set on {@link #flags} if this feature has been required by the application.
+ */
+ public static final int FLAG_REQUIRED = 0x0001;
+
+ /**
+ * Additional flags. May be zero or more of {@link #FLAG_REQUIRED}.
+ */
+ public int flags;
+
+ public FeatureInfo() {
+ }
+
+ public FeatureInfo(FeatureInfo orig) {
+ name = orig.name;
+ reqGlEsVersion = orig.reqGlEsVersion;
+ flags = orig.flags;
+ }
+
+ public String toString() {
+ if (name != null) {
+ return "FeatureInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + name + " fl=0x" + Integer.toHexString(flags) + "}";
+ } else {
+ return "FeatureInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " glEsVers=" + getGlEsVersion()
+ + " fl=0x" + Integer.toHexString(flags) + "}";
+ }
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(name);
+ dest.writeInt(reqGlEsVersion);
+ dest.writeInt(flags);
+ }
+
+ public static final Creator<FeatureInfo> CREATOR =
+ new Creator<FeatureInfo>() {
+ public FeatureInfo createFromParcel(Parcel source) {
+ return new FeatureInfo(source);
+ }
+ public FeatureInfo[] newArray(int size) {
+ return new FeatureInfo[size];
+ }
+ };
+
+ private FeatureInfo(Parcel source) {
+ name = source.readString();
+ reqGlEsVersion = source.readInt();
+ flags = source.readInt();
+ }
+
+ /**
+ * This method extracts the major and minor version of reqGLEsVersion attribute
+ * and returns it as a string. Say reqGlEsVersion value of 0x00010002 is returned
+ * as 1.2
+ * @return String representation of the reqGlEsVersion attribute
+ */
+ public String getGlEsVersion() {
+ int major = ((reqGlEsVersion & 0xffff0000) >> 16);
+ int minor = reqGlEsVersion & 0x0000ffff;
+ return String.valueOf(major)+"."+String.valueOf(minor);
+ }
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e587ca7..c322951 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDataObserver;
@@ -75,6 +76,8 @@ interface IPackageManager {
int checkSignatures(String pkg1, String pkg2);
+ int checkUidSignatures(int uid1, int uid2);
+
String[] getPackagesForUid(int uid);
String getNameForUid(int uid);
@@ -274,6 +277,12 @@ interface IPackageManager {
*/
String[] getSystemSharedLibraryNames();
+ /**
+ * Get a list of features that are available on the
+ * system.
+ */
+ FeatureInfo[] getSystemAvailableFeatures();
+
void enterSafeMode();
boolean isSafeMode();
void systemReady();
diff --git a/core/java/android/content/pm/LabeledIntent.java b/core/java/android/content/pm/LabeledIntent.java
new file mode 100644
index 0000000..d70a698
--- /dev/null
+++ b/core/java/android/content/pm/LabeledIntent.java
@@ -0,0 +1,177 @@
+package android.content.pm;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.text.TextUtils;
+
+/**
+ * A special subclass of Intent that can have a custom label/icon
+ * associated with it. Primarily for use with {@link Intent#ACTION_CHOOSER}.
+ */
+public class LabeledIntent extends Intent {
+ private String mSourcePackage;
+ private int mLabelRes;
+ private CharSequence mNonLocalizedLabel;
+ private int mIcon;
+
+ /**
+ * Create a labeled intent from the given intent, supplying the label
+ * and icon resources for it.
+ *
+ * @param origIntent The original Intent to copy.
+ * @param sourcePackage The package in which the label and icon live.
+ * @param labelRes Resource containing the label, or 0 if none.
+ * @param icon Resource containing the icon, or 0 if none.
+ */
+ public LabeledIntent(Intent origIntent, String sourcePackage,
+ int labelRes, int icon) {
+ super(origIntent);
+ mSourcePackage = sourcePackage;
+ mLabelRes = labelRes;
+ mNonLocalizedLabel = null;
+ mIcon = icon;
+ }
+
+ /**
+ * Create a labeled intent from the given intent, supplying a textual
+ * label and icon resource for it.
+ *
+ * @param origIntent The original Intent to copy.
+ * @param sourcePackage The package in which the label and icon live.
+ * @param nonLocalizedLabel Concrete text to use for the label.
+ * @param icon Resource containing the icon, or 0 if none.
+ */
+ public LabeledIntent(Intent origIntent, String sourcePackage,
+ CharSequence nonLocalizedLabel, int icon) {
+ super(origIntent);
+ mSourcePackage = sourcePackage;
+ mLabelRes = 0;
+ mNonLocalizedLabel = nonLocalizedLabel;
+ mIcon = icon;
+ }
+
+ /**
+ * Create a labeled intent with no intent data but supplying the label
+ * and icon resources for it.
+ *
+ * @param sourcePackage The package in which the label and icon live.
+ * @param labelRes Resource containing the label, or 0 if none.
+ * @param icon Resource containing the icon, or 0 if none.
+ */
+ public LabeledIntent(String sourcePackage, int labelRes, int icon) {
+ mSourcePackage = sourcePackage;
+ mLabelRes = labelRes;
+ mNonLocalizedLabel = null;
+ mIcon = icon;
+ }
+
+ /**
+ * Create a labeled intent with no intent data but supplying a textual
+ * label and icon resource for it.
+ *
+ * @param sourcePackage The package in which the label and icon live.
+ * @param nonLocalizedLabel Concrete text to use for the label.
+ * @param icon Resource containing the icon, or 0 if none.
+ */
+ public LabeledIntent(String sourcePackage,
+ CharSequence nonLocalizedLabel, int icon) {
+ mSourcePackage = sourcePackage;
+ mLabelRes = 0;
+ mNonLocalizedLabel = nonLocalizedLabel;
+ mIcon = icon;
+ }
+
+ /**
+ * Return the name of the package holding label and icon resources.
+ */
+ public String getSourcePackage() {
+ return mSourcePackage;
+ }
+
+ /**
+ * Return any resource identifier that has been given for the label text.
+ */
+ public int getLabelResource() {
+ return mLabelRes;
+ }
+
+ /**
+ * Return any concrete text that has been given for the label text.
+ */
+ public CharSequence getNonLocalizedLabel() {
+ return mNonLocalizedLabel;
+ }
+
+ /**
+ * Return any resource identifier that has been given for the label icon.
+ */
+ public int getIconResource() {
+ return mIcon;
+ }
+
+ /**
+ * Retrieve the label associated with this object. If the object does
+ * not have a label, null will be returned, in which case you will probably
+ * want to load the label from the underlying resolved info for the Intent.
+ */
+ public CharSequence loadLabel(PackageManager pm) {
+ if (mNonLocalizedLabel != null) {
+ return mNonLocalizedLabel;
+ }
+ if (mLabelRes != 0 && mSourcePackage != null) {
+ CharSequence label = pm.getText(mSourcePackage, mLabelRes, null);
+ if (label != null) {
+ return label;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve the icon associated with this object. If the object does
+ * not have a icon, null will be returned, in which case you will probably
+ * want to load the icon from the underlying resolved info for the Intent.
+ */
+ public Drawable loadIcon(PackageManager pm) {
+ if (mIcon != 0 && mSourcePackage != null) {
+ Drawable icon = pm.getDrawable(mSourcePackage, mIcon, null);
+ if (icon != null) {
+ return icon;
+ }
+ }
+ return null;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ super.writeToParcel(dest, parcelableFlags);
+ dest.writeString(mSourcePackage);
+ dest.writeInt(mLabelRes);
+ TextUtils.writeToParcel(mNonLocalizedLabel, dest, parcelableFlags);
+ dest.writeInt(mIcon);
+ }
+
+ /** @hide */
+ protected LabeledIntent(Parcel in) {
+ readFromParcel(in);
+ }
+
+ public void readFromParcel(Parcel in) {
+ super.readFromParcel(in);
+ mSourcePackage = in.readString();
+ mLabelRes = in.readInt();
+ mNonLocalizedLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mIcon = in.readInt();
+ }
+
+ public static final Creator<LabeledIntent> CREATOR
+ = new Creator<LabeledIntent>() {
+ public LabeledIntent createFromParcel(Parcel source) {
+ return new LabeledIntent(source);
+ }
+ public LabeledIntent[] newArray(int size) {
+ return new LabeledIntent[size];
+ }
+ };
+
+}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d9326f2..a8ce889 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -127,6 +127,11 @@ public class PackageInfo implements Parcelable {
*/
public ConfigurationInfo[] configPreferences;
+ /**
+ * The features that this application has said it requires.
+ */
+ public FeatureInfo[] reqFeatures;
+
public PackageInfo() {
}
@@ -162,6 +167,7 @@ public class PackageInfo implements Parcelable {
dest.writeStringArray(requestedPermissions);
dest.writeTypedArray(signatures, parcelableFlags);
dest.writeTypedArray(configPreferences, parcelableFlags);
+ dest.writeTypedArray(reqFeatures, parcelableFlags);
}
public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -195,5 +201,6 @@ public class PackageInfo implements Parcelable {
requestedPermissions = source.createStringArray();
signatures = source.createTypedArray(Signature.CREATOR);
configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
+ reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 67bd1ac..825eb85 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -159,8 +159,10 @@ public abstract class PackageManager {
/**
* {@link PackageInfo} flag: return information about
- * hardware preferences
- * {@link PackageInfo#configPreferences}
+ * hardware preferences in
+ * {@link PackageInfo#configPreferences PackageInfo.configPreferences} and
+ * requested features in {@link PackageInfo#reqFeatures
+ * PackageInfo.reqFeatures}.
*/
public static final int GET_CONFIGURATIONS = 0x00004000;
@@ -400,6 +402,14 @@ public abstract class PackageManager {
public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;
/**
+ * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+ * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+ * the new package uses a feature that is not available.
+ * @hide
+ */
+ public static final int INSTALL_FAILED_MISSING_FEATURE = -17;
+
+ /**
* Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
* {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
* if the parser was given a path that is not a file, or does not end with the expected
@@ -865,6 +875,7 @@ public abstract class PackageManager {
* {@link #SIGNATURE_SECOND_NOT_SIGNED}, {@link #SIGNATURE_NO_MATCH},
* or {@link #SIGNATURE_UNKNOWN_PACKAGE}.
*
+ * @see #checkSignatures(int, int)
* @see #SIGNATURE_MATCH
* @see #SIGNATURE_NEITHER_SIGNED
* @see #SIGNATURE_FIRST_NOT_SIGNED
@@ -875,6 +886,34 @@ public abstract class PackageManager {
public abstract int checkSignatures(String pkg1, String pkg2);
/**
+ * Like {@link #checkSignatures(String, String)}, but takes UIDs of
+ * the two packages to be checked. This can be useful, for example,
+ * when doing the check in an IPC, where the UID is the only identity
+ * available. It is functionally identical to determining the package
+ * associated with the UIDs and checking their signatures.
+ *
+ * @param uid1 First UID whose signature will be compared.
+ * @param uid2 Second UID whose signature will be compared.
+ * @return Returns an integer indicating whether there is a matching
+ * signature: the value is >= 0 if there is a match (or neither package
+ * is signed), or < 0 if there is not a match. The match result can be
+ * further distinguished with the success (>= 0) constants
+ * {@link #SIGNATURE_MATCH}, {@link #SIGNATURE_NEITHER_SIGNED}; or
+ * failure (< 0) constants {@link #SIGNATURE_FIRST_NOT_SIGNED},
+ * {@link #SIGNATURE_SECOND_NOT_SIGNED}, {@link #SIGNATURE_NO_MATCH},
+ * or {@link #SIGNATURE_UNKNOWN_PACKAGE}.
+ *
+ * @see #checkSignatures(int, int)
+ * @see #SIGNATURE_MATCH
+ * @see #SIGNATURE_NEITHER_SIGNED
+ * @see #SIGNATURE_FIRST_NOT_SIGNED
+ * @see #SIGNATURE_SECOND_NOT_SIGNED
+ * @see #SIGNATURE_NO_MATCH
+ * @see #SIGNATURE_UNKNOWN_PACKAGE
+ */
+ public abstract int checkSignatures(int uid1, int uid2);
+
+ /**
* Retrieve the names of all packages that are associated with a particular
* user id. In most cases, this will be a single package name, the package
* that has been assigned that user id. Where there are multiple packages
@@ -951,6 +990,16 @@ public abstract class PackageManager {
public abstract String[] getSystemSharedLibraryNames();
/**
+ * Get a list of features that are available on the
+ * system.
+ *
+ * @return An array of FeatureInfo classes describing the features
+ * that are available on the system, or null if there are none(!!).
+ *
+ */
+ public abstract FeatureInfo[] getSystemAvailableFeatures();
+
+ /**
* Determine the best action to perform for a given Intent. This is how
* {@link Intent#resolveActivity} finds an activity if a class has not
* been explicitly specified.
@@ -1429,8 +1478,6 @@ public abstract class PackageManager {
* which market the package came from.
*
* @param packageName The name of the package to query
- *
- * @hide
*/
public abstract String getInstallerPackageName(String packageName);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 33f4b52..b4a6fee 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -184,9 +184,12 @@ public class PackageParser {
int N = p.configPreferences.size();
if (N > 0) {
pi.configPreferences = new ConfigurationInfo[N];
- for (int i=0; i<N; i++) {
- pi.configPreferences[i] = p.configPreferences.get(i);
- }
+ p.configPreferences.toArray(pi.configPreferences);
+ }
+ N = p.reqFeatures != null ? p.reqFeatures.size() : 0;
+ if (N > 0) {
+ pi.reqFeatures = new FeatureInfo[N];
+ p.reqFeatures.toArray(pi.reqFeatures);
}
}
if ((flags&PackageManager.GET_ACTIVITIES) != 0) {
@@ -268,10 +271,12 @@ public class PackageParser {
}
}
if ((flags&PackageManager.GET_SIGNATURES) != 0) {
- int N = p.mSignatures.length;
- if (N > 0) {
- pi.signatures = new Signature[N];
- System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
+ if (p.mSignatures != null) {
+ int N = p.mSignatures.length;
+ if (N > 0) {
+ pi.signatures = new Signature[N];
+ System.arraycopy(p.mSignatures, 0, pi.signatures, 0, N);
+ }
}
}
return pi;
@@ -758,14 +763,32 @@ public class PackageParser {
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-feature")) {
- ConfigurationInfo cPref = new ConfigurationInfo();
+ FeatureInfo fi = new FeatureInfo();
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesFeature);
- cPref.reqGlEsVersion = sa.getInt(
- com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
- ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
+ fi.name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_name);
+ if (fi.name == null) {
+ fi.reqGlEsVersion = sa.getInt(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_glEsVersion,
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ }
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesFeature_required,
+ true)) {
+ fi.flags |= FeatureInfo.FLAG_REQUIRED;
+ }
sa.recycle();
- pkg.configPreferences.add(cPref);
+ if (pkg.reqFeatures == null) {
+ pkg.reqFeatures = new ArrayList<FeatureInfo>();
+ }
+ pkg.reqFeatures.add(fi);
+
+ if (fi.name == null) {
+ ConfigurationInfo cPref = new ConfigurationInfo();
+ cPref.reqGlEsVersion = fi.reqGlEsVersion;
+ pkg.configPreferences.add(cPref);
+ }
XmlUtils.skipCurrentTag(parser);
@@ -944,11 +967,6 @@ public class PackageParser {
}
}
- if (pkg.usesLibraries.size() > 0) {
- pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
- pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
- }
-
if (supportsSmallScreens < 0 || (supportsSmallScreens > 0
&& pkg.applicationInfo.targetSdkVersion
>= android.os.Build.VERSION_CODES.DONUT)) {
@@ -1278,12 +1296,26 @@ public class PackageParser {
com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+
+ // backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant
+ // if backup is possible for the given application.
String backupAgent = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
if (backupAgent != null) {
ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
Log.v(TAG, "android:backupAgent = " + ai.backupAgentName
+ " from " + pkgName + "+" + backupAgent);
+
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
+ true)) {
+ ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+ }
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_restoreNeedsApplication,
+ false)) {
+ ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION;
+ }
}
}
@@ -1434,11 +1466,28 @@ public class PackageParser {
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
+ boolean req = sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
+ true);
sa.recycle();
- if (lname != null && !owner.usesLibraries.contains(lname)) {
- owner.usesLibraries.add(lname.intern());
+ if (lname != null) {
+ if (req) {
+ if (owner.usesLibraries == null) {
+ owner.usesLibraries = new ArrayList<String>();
+ }
+ if (!owner.usesLibraries.contains(lname)) {
+ owner.usesLibraries.add(lname.intern());
+ }
+ } else {
+ if (owner.usesOptionalLibraries == null) {
+ owner.usesOptionalLibraries = new ArrayList<String>();
+ }
+ if (!owner.usesOptionalLibraries.contains(lname)) {
+ owner.usesOptionalLibraries.add(lname.intern());
+ }
+ }
}
XmlUtils.skipCurrentTag(parser);
@@ -2416,7 +2465,8 @@ public class PackageParser {
public ArrayList<String> protectedBroadcasts;
- public final ArrayList<String> usesLibraries = new ArrayList<String>();
+ public ArrayList<String> usesLibraries = null;
+ public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
// We store the application meta-data independently to avoid multiple unwanted references
@@ -2464,6 +2514,11 @@ public class PackageParser {
public final ArrayList<ConfigurationInfo> configPreferences =
new ArrayList<ConfigurationInfo>();
+ /*
+ * Applications requested features
+ */
+ public ArrayList<FeatureInfo> reqFeatures = null;
+
public Package(String _name) {
packageName = _name;
applicationInfo.packageName = _name;
diff --git a/core/java/android/content/pm/ProviderInfo.java b/core/java/android/content/pm/ProviderInfo.java
index d01460e..ec01775 100644
--- a/core/java/android/content/pm/ProviderInfo.java
+++ b/core/java/android/content/pm/ProviderInfo.java
@@ -74,7 +74,12 @@ public final class ProviderInfo extends ComponentInfo
* running in the same process. Higher goes first. */
public int initOrder = 0;
- /** Whether or not this provider is syncable. */
+ /**
+ * Whether or not this provider is syncable.
+ * @deprecated This flag is now being ignored. The current way to make a provider
+ * syncable is to provide a SyncAdapter service for a given provider/account type.
+ */
+ @Deprecated
public boolean isSyncable = false;
public ProviderInfo() {
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
new file mode 100644
index 0000000..f5f8f30
--- /dev/null
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.Context;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ComponentName;
+import android.content.res.XmlResourceParser;
+import android.util.Log;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import java.util.Map;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.IOException;
+
+import com.google.android.collect.Maps;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * A cache of registered services. This cache
+ * is built by interrogating the {@link PackageManager} and is updated as packages are added,
+ * removed and changed. The services are referred to by type V and
+ * are made available via the {@link #getServiceInfo} method.
+ * @hide
+ */
+public abstract class RegisteredServicesCache<V> {
+ private static final String TAG = "PackageManager";
+
+ public final Context mContext;
+ private final String mInterfaceName;
+ private final String mMetaDataName;
+ private final String mAttributesName;
+
+ // no need to be synchronized since the map is never changed once mService is written
+ volatile Map<V, ServiceInfo<V>> mServices;
+
+ // synchronized on "this"
+ private BroadcastReceiver mReceiver = null;
+
+ public RegisteredServicesCache(Context context, String interfaceName, String metaDataName,
+ String attributeName) {
+ mContext = context;
+ mInterfaceName = interfaceName;
+ mMetaDataName = metaDataName;
+ mAttributesName = attributeName;
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ getAllServices();
+ Map<V, ServiceInfo<V>> services = mServices;
+ fout.println("RegisteredServicesCache: " + services.size() + " services");
+ for (ServiceInfo info : services.values()) {
+ fout.println(" " + info);
+ }
+ }
+
+ private boolean maybeRegisterForPackageChanges() {
+ synchronized (this) {
+ if (mReceiver == null) {
+ synchronized (this) {
+ mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mServices = generateServicesMap();
+ }
+ };
+ }
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiver(mReceiver, intentFilter);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private void maybeUnregisterForPackageChanges() {
+ synchronized (this) {
+ if (mReceiver != null) {
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+ }
+ }
+
+ /**
+ * Value type that describes a Service. The information within can be used
+ * to bind to the service.
+ */
+ public static class ServiceInfo<V> {
+ public final V type;
+ public final ComponentName componentName;
+ public final int uid;
+
+ private ServiceInfo(V type, ComponentName componentName, int uid) {
+ this.type = type;
+ this.componentName = componentName;
+ this.uid = uid;
+ }
+
+ @Override
+ public String toString() {
+ return "ServiceInfo: " + type + ", " + componentName;
+ }
+ }
+
+ /**
+ * Accessor for the registered authenticators.
+ * @param type the account type of the authenticator
+ * @return the AuthenticatorInfo that matches the account type or null if none is present
+ */
+ public ServiceInfo<V> getServiceInfo(V type) {
+ if (mServices == null) {
+ maybeRegisterForPackageChanges();
+ mServices = generateServicesMap();
+ }
+ return mServices.get(type);
+ }
+
+ /**
+ * @return a collection of {@link RegisteredServicesCache.ServiceInfo} objects for all
+ * registered authenticators.
+ */
+ public Collection<ServiceInfo<V>> getAllServices() {
+ if (mServices == null) {
+ maybeRegisterForPackageChanges();
+ mServices = generateServicesMap();
+ }
+ return Collections.unmodifiableCollection(mServices.values());
+ }
+
+ /**
+ * Stops the monitoring of package additions, removals and changes.
+ */
+ public void close() {
+ maybeUnregisterForPackageChanges();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ synchronized (this) {
+ if (mReceiver != null) {
+ Log.e(TAG, "RegisteredServicesCache finalized without being closed");
+ }
+ }
+ close();
+ super.finalize();
+ }
+
+ Map<V, ServiceInfo<V>> generateServicesMap() {
+ Map<V, ServiceInfo<V>> services = Maps.newHashMap();
+ PackageManager pm = mContext.getPackageManager();
+
+ List<ResolveInfo> resolveInfos =
+ pm.queryIntentServices(new Intent(mInterfaceName), PackageManager.GET_META_DATA);
+
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ try {
+ ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+ if (info != null) {
+ services.put(info.type, info);
+ } else {
+ Log.w(TAG, "Unable to load input method " + resolveInfo.toString());
+ }
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to load input method " + resolveInfo.toString(), e);
+ }
+ }
+
+ return services;
+ }
+
+ private ServiceInfo<V> parseServiceInfo(ResolveInfo service)
+ throws XmlPullParserException, IOException {
+ android.content.pm.ServiceInfo si = service.serviceInfo;
+ ComponentName componentName = new ComponentName(si.packageName, si.name);
+
+ PackageManager pm = mContext.getPackageManager();
+
+ XmlResourceParser parser = null;
+ try {
+ parser = si.loadXmlMetaData(pm, mMetaDataName);
+ if (parser == null) {
+ throw new XmlPullParserException("No " + mMetaDataName + " meta-data");
+ }
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ }
+
+ String nodeName = parser.getName();
+ if (!mAttributesName.equals(nodeName)) {
+ throw new XmlPullParserException(
+ "Meta-data does not start with " + mAttributesName + " tag");
+ }
+
+ V v = parseServiceAttributes(si.packageName, attrs);
+ if (v == null) {
+ return null;
+ }
+ final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
+ final ApplicationInfo applicationInfo = serviceInfo.applicationInfo;
+ final int uid = applicationInfo.uid;
+ return new ServiceInfo<V>(v, componentName, uid);
+ } finally {
+ if (parser != null) parser.close();
+ }
+ }
+
+ public abstract V parseServiceAttributes(String packageName, AttributeSet attrs);
+}
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index ee49c02..380db65 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -92,6 +92,13 @@ public class ResolveInfo implements Parcelable {
public int icon;
/**
+ * Optional -- if non-null, the {@link #labelRes} and {@link #icon}
+ * resources will be loaded from this package, rather than the one
+ * containing the resolved component.
+ */
+ public String resolvePackageName;
+
+ /**
* Retrieve the current textual label associated with this resolution. This
* will call back on the given PackageManager to load the label from
* the application.
@@ -106,9 +113,15 @@ public class ResolveInfo implements Parcelable {
if (nonLocalizedLabel != null) {
return nonLocalizedLabel;
}
+ CharSequence label;
+ if (resolvePackageName != null && labelRes != 0) {
+ label = pm.getText(resolvePackageName, labelRes, null);
+ if (label != null) {
+ return label;
+ }
+ }
ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
ApplicationInfo ai = ci.applicationInfo;
- CharSequence label;
if (labelRes != 0) {
label = pm.getText(ci.packageName, labelRes, ai);
if (label != null) {
@@ -133,6 +146,12 @@ public class ResolveInfo implements Parcelable {
ComponentInfo ci = activityInfo != null ? activityInfo : serviceInfo;
ApplicationInfo ai = ci.applicationInfo;
Drawable dr;
+ if (resolvePackageName != null && icon != 0) {
+ dr = pm.getDrawable(resolvePackageName, icon, null);
+ if (dr != null) {
+ return dr;
+ }
+ }
if (icon != 0) {
dr = pm.getDrawable(ci.packageName, icon, ai);
if (dr != null) {
@@ -160,24 +179,26 @@ public class ResolveInfo implements Parcelable {
if (filter != null) {
pw.println(prefix + "Filter:");
filter.dump(pw, prefix + " ");
- } else {
- pw.println(prefix + "Filter: null");
}
pw.println(prefix + "priority=" + priority
+ " preferredOrder=" + preferredOrder
+ " match=0x" + Integer.toHexString(match)
+ " specificIndex=" + specificIndex
+ " isDefault=" + isDefault);
- pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
- + " nonLocalizedLabel=" + nonLocalizedLabel
- + " icon=0x" + Integer.toHexString(icon));
+ if (resolvePackageName != null) {
+ pw.println(prefix + "resolvePackageName=" + resolvePackageName);
+ }
+ if (labelRes != 0 || nonLocalizedLabel != null || icon != 0) {
+ pw.println(prefix + "labelRes=0x" + Integer.toHexString(labelRes)
+ + " nonLocalizedLabel=" + nonLocalizedLabel
+ + " icon=0x" + Integer.toHexString(icon));
+ }
if (activityInfo != null) {
pw.println(prefix + "ActivityInfo:");
activityInfo.dump(pw, prefix + " ");
} else if (serviceInfo != null) {
pw.println(prefix + "ServiceInfo:");
- // TODO
- //serviceInfo.dump(pw, prefix + " ");
+ serviceInfo.dump(pw, prefix + " ");
}
}
@@ -219,6 +240,7 @@ public class ResolveInfo implements Parcelable {
dest.writeInt(labelRes);
TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
dest.writeInt(icon);
+ dest.writeString(resolvePackageName);
}
public static final Creator<ResolveInfo> CREATOR
@@ -257,6 +279,7 @@ public class ResolveInfo implements Parcelable {
nonLocalizedLabel
= TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
icon = source.readInt();
+ resolvePackageName = source.readString();
}
public static class DisplayNameComparator
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index b60650c..51d2a4d 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -2,6 +2,7 @@ package android.content.pm;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.Printer;
/**
* Information you can retrieve about a particular application
@@ -24,6 +25,11 @@ public class ServiceInfo extends ComponentInfo
permission = orig.permission;
}
+ public void dump(Printer pw, String prefix) {
+ super.dumpFront(pw, prefix);
+ pw.println(prefix + "permission=" + permission);
+ }
+
public String toString() {
return "ServiceInfo{"
+ Integer.toHexString(System.identityHashCode(this))
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 5f44cc9..cbf8410 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -93,7 +93,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
/**
* The kind of keyboard attached to the device.
- * One of: {@link #KEYBOARD_QWERTY}, {@link #KEYBOARD_12KEY}.
+ * One of: {@link #KEYBOARD_NOKEYS}, {@link #KEYBOARD_QWERTY},
+ * {@link #KEYBOARD_12KEY}.
*/
public int keyboard;
@@ -132,8 +133,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
/**
* The kind of navigation method available on the device.
- * One of: {@link #NAVIGATION_DPAD}, {@link #NAVIGATION_TRACKBALL},
- * {@link #NAVIGATION_WHEEL}.
+ * One of: {@link #NAVIGATION_NONAV}, {@link #NAVIGATION_DPAD},
+ * {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
*/
public int navigation;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2354519..7d412a7 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.content.pm.ApplicationInfo;
+import android.graphics.BitmapFactory;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
@@ -1707,7 +1708,8 @@ public class Resources {
InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_BUFFER);
// System.out.println("Opened file " + file + ": " + is);
- dr = Drawable.createFromResourceStream(this, value, is, file);
+ dr = Drawable.createFromResourceStream(this, value, is,
+ file, null);
is.close();
// System.out.println("Created stream: " + dr);
} catch (Exception e) {
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
index e684cb8..8fb82be 100644
--- a/core/java/android/content/res/StringBlock.java
+++ b/core/java/android/content/res/StringBlock.java
@@ -202,7 +202,7 @@ final class StringBlock {
sub = subtag(tag, ";size=");
if (sub != null) {
int size = Integer.parseInt(sub);
- buffer.setSpan(new AbsoluteSizeSpan(size),
+ buffer.setSpan(new AbsoluteSizeSpan(size, true),
style[i+1], style[i+2]+1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
@@ -310,7 +310,7 @@ final class StringBlock {
* the ascent if possible, or the descent if shrinking the ascent further
* will make the text unreadable.
*/
- private static class Height implements LineHeightSpan {
+ private static class Height implements LineHeightSpan.WithDensity {
private int mSize;
private static float sProportion = 0;
@@ -321,9 +321,21 @@ final class StringBlock {
public void chooseHeight(CharSequence text, int start, int end,
int spanstartv, int v,
Paint.FontMetricsInt fm) {
- if (fm.bottom - fm.top < mSize) {
- fm.top = fm.bottom - mSize;
- fm.ascent = fm.ascent - mSize;
+ // Should not get called, at least not by StaticLayout.
+ chooseHeight(text, start, end, spanstartv, v, fm, null);
+ }
+
+ public void chooseHeight(CharSequence text, int start, int end,
+ int spanstartv, int v,
+ Paint.FontMetricsInt fm, TextPaint paint) {
+ int size = mSize;
+ if (paint != null) {
+ size *= paint.density;
+ }
+
+ if (fm.bottom - fm.top < size) {
+ fm.top = fm.bottom - size;
+ fm.ascent = fm.ascent - size;
} else {
if (sProportion == 0) {
/*
@@ -343,27 +355,27 @@ final class StringBlock {
int need = (int) Math.ceil(-fm.top * sProportion);
- if (mSize - fm.descent >= need) {
+ if (size - fm.descent >= need) {
/*
* It is safe to shrink the ascent this much.
*/
- fm.top = fm.bottom - mSize;
- fm.ascent = fm.descent - mSize;
- } else if (mSize >= need) {
+ fm.top = fm.bottom - size;
+ fm.ascent = fm.descent - size;
+ } else if (size >= need) {
/*
* We can't show all the descent, but we can at least
* show all the ascent.
*/
fm.top = fm.ascent = -need;
- fm.bottom = fm.descent = fm.top + mSize;
+ fm.bottom = fm.descent = fm.top + size;
} else {
/*
* Show as much of the ascent as we can, and no descent.
*/
- fm.top = fm.ascent = -mSize;
+ fm.top = fm.ascent = -size;
fm.bottom = fm.descent = 0;
}
}
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index 4ac0aef..27a02e2 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -166,6 +166,48 @@ public abstract class AbstractWindowedCursor extends AbstractCursor
return mWindow.isBlob(mPos, columnIndex);
}
+ public boolean isString(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object == null || object instanceof String;
+ }
+ }
+
+ return mWindow.isString(mPos, columnIndex);
+ }
+
+ public boolean isLong(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object != null && (object instanceof Integer || object instanceof Long);
+ }
+ }
+
+ return mWindow.isLong(mPos, columnIndex);
+ }
+
+ public boolean isFloat(int columnIndex)
+ {
+ checkPosition();
+
+ synchronized(mUpdatedRows) {
+ if (isFieldUpdated(columnIndex)) {
+ Object object = getUpdatedField(columnIndex);
+ return object != null && (object instanceof Float || object instanceof Double);
+ }
+ }
+
+ return mWindow.isFloat(mPos, columnIndex);
+ }
+
@Override
protected void checkPosition()
{
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 8e26730..99db81b 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -263,7 +263,58 @@ public class CursorWindow extends SQLiteClosable implements Parcelable {
}
}
+ /**
+ * Checks if a field contains a long
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is a long
+ */
+ public boolean isLong(int row, int col) {
+ acquireReference();
+ try {
+ return isInteger_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
+ /**
+ * Checks if a field contains a float.
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is a float
+ */
+ public boolean isFloat(int row, int col) {
+ acquireReference();
+ try {
+ return isFloat_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
+ /**
+ * Checks if a field contains either a String or is null.
+ *
+ * @param row the row to read from, row - getStartPosition() being the actual row in the window
+ * @param col the column to read from
+ * @return {@code true} if given field is {@code NULL} or a String
+ */
+ public boolean isString(int row, int col) {
+ acquireReference();
+ try {
+ return isString_native(row - mStartPos, col);
+ } finally {
+ releaseReference();
+ }
+ }
+
private native boolean isBlob_native(int row, int col);
+ private native boolean isString_native(int row, int col);
+ private native boolean isInteger_native(int row, int col);
+ private native boolean isFloat_native(int row, int col);
/**
* Returns a String for the given field.
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 10f3806..4ca6601 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -20,6 +20,7 @@ import org.apache.commons.codec.binary.Hex;
import android.content.ContentValues;
import android.content.Context;
+import android.content.OperationApplicationException;
import android.database.sqlite.SQLiteAbortException;
import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
@@ -82,6 +83,8 @@ public class DatabaseUtils {
code = 8;
} else if (e instanceof SQLiteException) {
code = 9;
+ } else if (e instanceof OperationApplicationException) {
+ code = 10;
} else {
reply.writeException(e);
Log.e(TAG, "Writing exception to parcel", e);
@@ -123,6 +126,18 @@ public class DatabaseUtils {
}
}
+ public static void readExceptionWithOperationApplicationExceptionFromParcel(
+ Parcel reply) throws OperationApplicationException {
+ int code = reply.readInt();
+ if (code == 0) return;
+ String msg = reply.readString();
+ if (code == 10) {
+ throw new OperationApplicationException(msg);
+ } else {
+ DatabaseUtils.readExceptionFromParcel(reply, msg, code);
+ }
+ }
+
private static final void readExceptionFromParcel(Parcel reply, String msg, int code) {
switch (code) {
case 2:
@@ -211,7 +226,7 @@ public class DatabaseUtils {
sb.append(sqlString);
sb.append('\'');
}
-
+
/**
* SQL-escape a string.
*/
@@ -240,7 +255,7 @@ public class DatabaseUtils {
appendEscapedSQLString(sql, value.toString());
}
}
-
+
/**
* Concatenates two SQL WHERE clauses, handling empty or null values.
* @hide
@@ -252,12 +267,12 @@ public class DatabaseUtils {
if (TextUtils.isEmpty(b)) {
return a;
}
-
+
return "(" + a + ") AND (" + b + ")";
}
-
+
/**
- * return the collation key
+ * return the collation key
* @param name
* @return the collation key
*/
@@ -269,7 +284,7 @@ public class DatabaseUtils {
return "";
}
}
-
+
/**
* return the collation key in hex format
* @param name
@@ -280,7 +295,7 @@ public class DatabaseUtils {
char[] keys = Hex.encodeHex(arr);
return new String(keys, 0, getKeyLen(arr) * 2);
}
-
+
private static int getKeyLen(byte[] arr) {
if (arr[arr.length - 1] != 0) {
return arr.length;
@@ -289,16 +304,16 @@ public class DatabaseUtils {
return arr.length-1;
}
}
-
+
private static byte[] getCollationKeyInBytes(String name) {
if (mColl == null) {
mColl = Collator.getInstance();
mColl.setStrength(Collator.PRIMARY);
}
- return mColl.getCollationKey(name).toByteArray();
+ return mColl.getCollationKey(name).toByteArray();
}
-
- private static Collator mColl = null;
+
+ private static Collator mColl = null;
/**
* Prints the contents of a Cursor to System.out. The position is restored
* after printing.
@@ -591,10 +606,12 @@ public class DatabaseUtils {
public static long queryNumEntries(SQLiteDatabase db, String table) {
Cursor cursor = db.query(table, countProjection,
null, null, null, null, null);
- cursor.moveToFirst();
- long count = cursor.getLong(0);
- cursor.deactivate();
- return count;
+ try {
+ cursor.moveToFirst();
+ return cursor.getLong(0);
+ } finally {
+ cursor.close();
+ }
}
/**
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 184d6dc..0cd4036 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -62,53 +62,53 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public enum ConflictAlgorithm {
/**
- * When a constraint violation occurs, an immediate ROLLBACK occurs,
- * thus ending the current transaction, and the command aborts with a
- * return code of SQLITE_CONSTRAINT. If no transaction is active
+ * When a constraint violation occurs, an immediate ROLLBACK occurs,
+ * thus ending the current transaction, and the command aborts with a
+ * return code of SQLITE_CONSTRAINT. If no transaction is active
* (other than the implied transaction that is created on every command)
* then this algorithm works the same as ABORT.
*/
ROLLBACK("ROLLBACK"),
-
+
/**
- * When a constraint violation occurs,no ROLLBACK is executed
- * so changes from prior commands within the same transaction
+ * When a constraint violation occurs,no ROLLBACK is executed
+ * so changes from prior commands within the same transaction
* are preserved. This is the default behavior.
*/
ABORT("ABORT"),
-
+
/**
- * When a constraint violation occurs, the command aborts with a return
- * code SQLITE_CONSTRAINT. But any changes to the database that
- * the command made prior to encountering the constraint violation
+ * When a constraint violation occurs, the command aborts with a return
+ * code SQLITE_CONSTRAINT. But any changes to the database that
+ * the command made prior to encountering the constraint violation
* are preserved and are not backed out.
*/
FAIL("FAIL"),
-
+
/**
- * When a constraint violation occurs, the one row that contains
- * the constraint violation is not inserted or changed.
- * But the command continues executing normally. Other rows before and
- * after the row that contained the constraint violation continue to be
+ * When a constraint violation occurs, the one row that contains
+ * the constraint violation is not inserted or changed.
+ * But the command continues executing normally. Other rows before and
+ * after the row that contained the constraint violation continue to be
* inserted or updated normally. No error is returned.
*/
IGNORE("IGNORE"),
-
+
/**
* When a UNIQUE constraint violation occurs, the pre-existing rows that
- * are causing the constraint violation are removed prior to inserting
+ * are causing the constraint violation are removed prior to inserting
* or updating the current row. Thus the insert or update always occurs.
- * The command continues executing normally. No error is returned.
+ * The command continues executing normally. No error is returned.
* If a NOT NULL constraint violation occurs, the NULL value is replaced
- * by the default value for that column. If the column has no default
- * value, then the ABORT algorithm is used. If a CHECK constraint
- * violation occurs then the IGNORE algorithm is used. When this conflict
- * resolution strategy deletes rows in order to satisfy a constraint,
+ * by the default value for that column. If the column has no default
+ * value, then the ABORT algorithm is used. If a CHECK constraint
+ * violation occurs then the IGNORE algorithm is used. When this conflict
+ * resolution strategy deletes rows in order to satisfy a constraint,
* it does not invoke delete triggers on those rows.
* This behavior might change in a future release.
*/
REPLACE("REPLACE");
-
+
private final String mValue;
ConflictAlgorithm(String value) {
mValue = value;
@@ -117,7 +117,7 @@ public class SQLiteDatabase extends SQLiteClosable {
return mValue;
}
}
-
+
/**
* Maximum Length Of A LIKE Or GLOB Pattern
* The pattern matching algorithm used in the default LIKE and GLOB implementation
@@ -175,22 +175,29 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
private boolean mTransactionIsSuccessful;
+ /**
+ * Valid during the life of a transaction.
+ */
+ private SQLiteTransactionListener mTransactionListener;
+
/** Synchronize on this when accessing the database */
private final ReentrantLock mLock = new ReentrantLock(true);
private long mLockAcquiredWallTime = 0L;
private long mLockAcquiredThreadTime = 0L;
-
+
// limit the frequency of complaints about each database to one within 20 sec
- // unless run command adb shell setprop log.tag.Database VERBOSE
+ // unless run command adb shell setprop log.tag.Database VERBOSE
private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
/** If the lock is held this long then a warning will be printed when it is released. */
private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
+ private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
+
private long mLastLockMessageTime = 0L;
-
+
/** Used by native code, do not rename */
/* package */ int mNativeHandle = 0;
@@ -205,15 +212,15 @@ public class SQLiteDatabase extends SQLiteClosable {
/** The optional factory to use when creating new Cursors */
private CursorFactory mFactory;
-
+
private WeakHashMap<SQLiteClosable, Object> mPrograms;
-
+
private final RuntimeException mLeakedException;
// package visible, since callers will access directly to minimize overhead in the case
// that logging is not enabled.
/* package */ final boolean mLogStats;
-
+
/**
* @param closable
*/
@@ -225,7 +232,7 @@ public class SQLiteDatabase extends SQLiteClosable {
unlock();
}
}
-
+
void removeSQLiteClosable(SQLiteClosable closable) {
lock();
try {
@@ -233,8 +240,8 @@ public class SQLiteDatabase extends SQLiteClosable {
} finally {
unlock();
}
- }
-
+ }
+
@Override
protected void onAllReferencesReleased() {
if (isOpen()) {
@@ -245,10 +252,10 @@ public class SQLiteDatabase extends SQLiteClosable {
/**
* Attempts to release memory that SQLite holds but does not require to
* operate properly. Typically this memory will come from the page cache.
- *
+ *
* @return the number of bytes actually released
*/
- static public native int releaseMemory();
+ static public native int releaseMemory();
/**
* Control whether or not the SQLiteDatabase is made thread-safe by using locks
@@ -284,7 +291,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* touch the native sqlite3* object since it is single threaded and uses
* a polling lock contention algorithm. The lock is recursive, and may be acquired
* multiple times by the same thread. This is a no-op if mLockingEnabled is false.
- *
+ *
* @see #unlock()
*/
/* package */ void lock() {
@@ -320,7 +327,7 @@ public class SQLiteDatabase extends SQLiteClosable {
/**
* Releases the database lock. This is a no-op if mLockingEnabled is false.
- *
+ *
* @see #unlock()
*/
/* package */ void unlock() {
@@ -350,7 +357,7 @@ public class SQLiteDatabase extends SQLiteClosable {
private void checkLockHoldTime() {
// Use elapsed real-time since the CPU may sleep when waiting for IO
long elapsedTime = SystemClock.elapsedRealtime();
- long lockedTime = elapsedTime - mLockAcquiredWallTime;
+ long lockedTime = elapsedTime - mLockAcquiredWallTime;
if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
!Log.isLoggable(TAG, Log.VERBOSE) &&
(elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
@@ -392,6 +399,31 @@ public class SQLiteDatabase extends SQLiteClosable {
* </pre>
*/
public void beginTransaction() {
+ beginTransactionWithListener(null /* transactionStatusCallback */);
+ }
+
+ /**
+ * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
+ * the work done in that transaction and all of the nested transactions will be committed or
+ * rolled back. The changes will be rolled back if any transaction is ended without being
+ * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
+ *
+ * <p>Here is the standard idiom for transactions:
+ *
+ * <pre>
+ * db.beginTransactionWithListener(listener);
+ * try {
+ * ...
+ * db.setTransactionSuccessful();
+ * } finally {
+ * db.endTransaction();
+ * }
+ * </pre>
+ * @param transactionListener listener that should be notified when the transaction begins,
+ * commits, or is rolled back, either explicitly or by a call to
+ * {@link #yieldIfContendedSafely}.
+ */
+ public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
lockForced();
boolean ok = false;
try {
@@ -411,8 +443,17 @@ public class SQLiteDatabase extends SQLiteClosable {
// This thread didn't already have the lock, so begin a database
// transaction now.
execSQL("BEGIN EXCLUSIVE;");
+ mTransactionListener = transactionListener;
mTransactionIsSuccessful = true;
mInnerTransactionIsSuccessful = false;
+ if (transactionListener != null) {
+ try {
+ transactionListener.onBegin();
+ } catch (RuntimeException e) {
+ execSQL("ROLLBACK;");
+ throw e;
+ }
+ }
ok = true;
} finally {
if (!ok) {
@@ -440,11 +481,27 @@ public class SQLiteDatabase extends SQLiteClosable {
if (mLock.getHoldCount() != 1) {
return;
}
+ RuntimeException savedException = null;
+ if (mTransactionListener != null) {
+ try {
+ if (mTransactionIsSuccessful) {
+ mTransactionListener.onCommit();
+ } else {
+ mTransactionListener.onRollback();
+ }
+ } catch (RuntimeException e) {
+ savedException = e;
+ mTransactionIsSuccessful = false;
+ }
+ }
if (mTransactionIsSuccessful) {
execSQL("COMMIT;");
} else {
try {
execSQL("ROLLBACK;");
+ if (savedException != null) {
+ throw savedException;
+ }
} catch (SQLException e) {
if (Config.LOGD) {
Log.d(TAG, "exception during rollback, maybe the DB previously "
@@ -453,6 +510,7 @@ public class SQLiteDatabase extends SQLiteClosable {
}
}
} finally {
+ mTransactionListener = null;
unlockForced();
if (Config.LOGV) {
Log.v(TAG, "unlocked " + Thread.currentThread()
@@ -517,8 +575,10 @@ public class SQLiteDatabase extends SQLiteClosable {
* @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
* will not be yielded. Use yieldIfContendedSafely instead.
*/
+ @Deprecated
public boolean yieldIfContended() {
- return yieldIfContendedHelper(false /* do not check yielding */);
+ return yieldIfContendedHelper(false /* do not check yielding */,
+ -1 /* sleepAfterYieldDelay */);
}
/**
@@ -526,14 +586,29 @@ public class SQLiteDatabase extends SQLiteClosable {
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful. This assumes
* that there are no nested transactions (beginTransaction has only been called once) and will
- * through an exception if that is not the case.
+ * throw an exception if that is not the case.
* @return true if the transaction was yielded
*/
public boolean yieldIfContendedSafely() {
- return yieldIfContendedHelper(true /* check yielding */);
+ return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
}
- private boolean yieldIfContendedHelper(boolean checkFullyYielded) {
+ /**
+ * Temporarily end the transaction to let other threads run. The transaction is assumed to be
+ * successful so far. Do not call setTransactionSuccessful before calling this. When this
+ * returns a new transaction will have been created but not marked as successful. This assumes
+ * that there are no nested transactions (beginTransaction has only been called once) and will
+ * throw an exception if that is not the case.
+ * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
+ * the lock was actually yielded. This will allow other background threads to make some
+ * more progress than they would if we started the transaction immediately.
+ * @return true if the transaction was yielded
+ */
+ public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
+ return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
+ }
+
+ private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
if (mLock.getQueueLength() == 0) {
// Reset the lock acquire time since we know that the thread was willing to yield
// the lock at this time.
@@ -542,6 +617,7 @@ public class SQLiteDatabase extends SQLiteClosable {
return false;
}
setTransactionSuccessful();
+ SQLiteTransactionListener transactionListener = mTransactionListener;
endTransaction();
if (checkFullyYielded) {
if (this.isDbLockedByCurrentThread()) {
@@ -549,7 +625,25 @@ public class SQLiteDatabase extends SQLiteClosable {
"Db locked more than once. yielfIfContended cannot yield");
}
}
- beginTransaction();
+ if (sleepAfterYieldDelay > 0) {
+ // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
+ // check if anyone is using the database. If the database is not contended,
+ // retake the lock and return.
+ long remainingDelay = sleepAfterYieldDelay;
+ while (remainingDelay > 0) {
+ try {
+ Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
+ remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
+ } catch (InterruptedException e) {
+ Thread.interrupted();
+ }
+ remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
+ if (mLock.getQueueLength() == 0) {
+ break;
+ }
+ }
+ }
+ beginTransactionWithListener(transactionListener);
return true;
}
@@ -696,9 +790,9 @@ public class SQLiteDatabase extends SQLiteClosable {
if (program != null) {
program.onAllReferencesReleasedFromContainer();
}
- }
+ }
}
-
+
/**
* Native call to close the database.
*/
@@ -1133,8 +1227,8 @@ public class SQLiteDatabase extends SQLiteClosable {
/**
* Runs the provided SQL and returns a cursor over the result set.
- * The cursor will read an initial set of rows and the return to the caller.
- * It will continue to read in batches and send data changed notifications
+ * The cursor will read an initial set of rows and the return to the caller.
+ * It will continue to read in batches and send data changed notifications
* when the later batches are ready.
* @param sql the SQL query. The SQL string must not be ; terminated
* @param selectionArgs You may include ?s in where clause in the query,
@@ -1143,19 +1237,19 @@ public class SQLiteDatabase extends SQLiteClosable {
* @param initialRead set the initial count of items to read from the cursor
* @param maxRead set the count of items to read on each iteration after the first
* @return A {@link Cursor} object, which is positioned before the first entry
- *
+ *
* This work is incomplete and not fully tested or reviewed, so currently
* hidden.
* @hide
*/
- public Cursor rawQuery(String sql, String[] selectionArgs,
+ public Cursor rawQuery(String sql, String[] selectionArgs,
int initialRead, int maxRead) {
SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
null, sql, selectionArgs, null);
c.setLoadStyle(initialRead, maxRead);
return c;
}
-
+
/**
* Convenience method for inserting a row into the database.
*
@@ -1208,7 +1302,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long replace(String table, String nullColumnHack, ContentValues initialValues) {
try {
- return insertWithOnConflict(table, nullColumnHack, initialValues,
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
ConflictAlgorithm.REPLACE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + initialValues, e);
@@ -1230,7 +1324,7 @@ public class SQLiteDatabase extends SQLiteClosable {
*/
public long replaceOrThrow(String table, String nullColumnHack,
ContentValues initialValues) throws SQLException {
- return insertWithOnConflict(table, nullColumnHack, initialValues,
+ return insertWithOnConflict(table, nullColumnHack, initialValues,
ConflictAlgorithm.REPLACE);
}
@@ -1386,7 +1480,7 @@ public class SQLiteDatabase extends SQLiteClosable {
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
return updateWithOnConflict(table, values, whereClause, whereArgs, null);
}
-
+
/**
* Convenience method for updating rows in the database.
*
@@ -1399,7 +1493,7 @@ public class SQLiteDatabase extends SQLiteClosable {
* @return the number of rows affected
* @hide
*/
- public int updateWithOnConflict(String table, ContentValues values,
+ public int updateWithOnConflict(String table, ContentValues values,
String whereClause, String[] whereArgs, ConflictAlgorithm algorithm) {
if (!isOpen()) {
throw new IllegalStateException("database not open");
@@ -1416,7 +1510,7 @@ public class SQLiteDatabase extends SQLiteClosable {
sql.append(algorithm.value());
sql.append(" ");
}
-
+
sql.append(table);
sql.append(" SET ");
@@ -1577,7 +1671,7 @@ public class SQLiteDatabase extends SQLiteClosable {
mFlags = flags;
mPath = path;
mLogStats = "1".equals(android.os.SystemProperties.get("db.logstats"));
-
+
mLeakedException = new IllegalStateException(path +
" SQLiteDatabase created and never closed");
mFactory = factory;
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d04afb0..84d8879 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -17,6 +17,7 @@
package android.database.sqlite;
import android.util.Config;
+import android.util.Log;
/**
* Provides debugging info about all SQLite databases running in the current process.
@@ -27,23 +28,27 @@ public final class SQLiteDebug {
/**
* Controls the printing of SQL statements as they are executed.
*/
- public static final boolean DEBUG_SQL_STATEMENTS = Config.LOGV;
+ public static final boolean DEBUG_SQL_STATEMENTS =
+ Log.isLoggable("SQLiteStatements", Log.VERBOSE);
/**
* Controls the stack trace reporting of active cursors being
* finalized.
*/
- public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION = Config.LOGV;
+ public static final boolean DEBUG_ACTIVE_CURSOR_FINALIZATION =
+ Log.isLoggable("SQLiteCursorClosing", Log.VERBOSE);
/**
* Controls the tracking of time spent holding the database lock.
*/
- public static final boolean DEBUG_LOCK_TIME_TRACKING = false;
+ public static final boolean DEBUG_LOCK_TIME_TRACKING =
+ Log.isLoggable("SQLiteLockTime", Log.VERBOSE);
/**
* Controls the printing of stack traces when tracking the time spent holding the database lock.
*/
- public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE = false;
+ public static final boolean DEBUG_LOCK_TIME_TRACKING_STACK_TRACE =
+ Log.isLoggable("SQLiteLockStackTrace", Log.VERBOSE);
/**
* Contains statistics about the active pagers in the current process.
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 8a63919..af54a71 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -355,23 +355,26 @@ public class SQLiteQueryBuilder
String groupBy, String having, String sortOrder, String limit) {
String[] projection = computeProjection(projectionIn);
+ StringBuilder where = new StringBuilder();
+
if (mWhereClause.length() > 0) {
- mWhereClause.append(')');
+ where.append(mWhereClause.toString());
+ where.append(')');
}
// Tack on the user's selection, if present.
if (selection != null && selection.length() > 0) {
if (mWhereClause.length() > 0) {
- mWhereClause.append(" AND ");
+ where.append(" AND ");
}
- mWhereClause.append('(');
- mWhereClause.append(selection);
- mWhereClause.append(')');
+ where.append('(');
+ where.append(selection);
+ where.append(')');
}
return buildQueryString(
- mDistinct, mTables, projection, mWhereClause.toString(),
+ mDistinct, mTables, projection, where.toString(),
groupBy, having, sortOrder, limit);
}
diff --git a/core/java/android/database/sqlite/SQLiteTransactionListener.java b/core/java/android/database/sqlite/SQLiteTransactionListener.java
new file mode 100644
index 0000000..e97ece8
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteTransactionListener.java
@@ -0,0 +1,21 @@
+package android.database.sqlite;
+
+/**
+ * A listener for transaction events.
+ */
+public interface SQLiteTransactionListener {
+ /**
+ * Called immediately after the transaction begins.
+ */
+ void onBegin();
+
+ /**
+ * Called immediately before commiting the transaction.
+ */
+ void onCommit();
+
+ /**
+ * Called if the transaction is about to be rolled back.
+ */
+ void onRollback();
+}
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 142bbd9..04daa1c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -17,7 +17,9 @@
package android.hardware;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.StringTokenizer;
import java.io.IOException;
@@ -55,17 +57,18 @@ import android.os.Message;
*/
public class Camera {
private static final String TAG = "Camera";
-
+
// These match the enums in frameworks/base/include/ui/Camera.h
- private static final int CAMERA_MSG_ERROR = 0;
- private static final int CAMERA_MSG_SHUTTER = 1;
- private static final int CAMERA_MSG_FOCUS = 2;
- private static final int CAMERA_MSG_ZOOM = 3;
- private static final int CAMERA_MSG_PREVIEW_FRAME = 4;
- private static final int CAMERA_MSG_VIDEO_FRAME = 5;
- private static final int CAMERA_MSG_POSTVIEW_FRAME = 6;
- private static final int CAMERA_MSG_RAW_IMAGE = 7;
- private static final int CAMERA_MSG_COMPRESSED_IMAGE = 8;
+ private static final int CAMERA_MSG_ERROR = 0x001;
+ private static final int CAMERA_MSG_SHUTTER = 0x002;
+ private static final int CAMERA_MSG_FOCUS = 0x004;
+ private static final int CAMERA_MSG_ZOOM = 0x008;
+ private static final int CAMERA_MSG_PREVIEW_FRAME = 0x010;
+ private static final int CAMERA_MSG_VIDEO_FRAME = 0x020;
+ private static final int CAMERA_MSG_POSTVIEW_FRAME = 0x040;
+ private static final int CAMERA_MSG_RAW_IMAGE = 0x080;
+ private static final int CAMERA_MSG_COMPRESSED_IMAGE = 0x100;
+ private static final int CAMERA_MSG_ALL_MSGS = 0x1FF;
private int mNativeContext; // accessed by native methods
private EventHandler mEventHandler;
@@ -73,15 +76,17 @@ public class Camera {
private PictureCallback mRawImageCallback;
private PictureCallback mJpegCallback;
private PreviewCallback mPreviewCallback;
+ private PictureCallback mPostviewCallback;
private AutoFocusCallback mAutoFocusCallback;
+ private ZoomCallback mZoomCallback;
private ErrorCallback mErrorCallback;
private boolean mOneShot;
-
+
/**
* Returns a new Camera object.
*/
- public static Camera open() {
- return new Camera();
+ public static Camera open() {
+ return new Camera();
}
Camera() {
@@ -89,6 +94,8 @@ public class Camera {
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
+ mPostviewCallback = null;
+ mZoomCallback = null;
Looper looper;
if ((looper = Looper.myLooper()) != null) {
@@ -101,32 +108,32 @@ public class Camera {
native_setup(new WeakReference<Camera>(this));
}
-
- protected void finalize() {
- native_release();
+
+ protected void finalize() {
+ native_release();
}
-
+
private native final void native_setup(Object camera_this);
private native final void native_release();
-
+
/**
* Disconnects and releases the Camera object resources.
- * <p>It is recommended that you call this as soon as you're done with the
+ * <p>It is recommended that you call this as soon as you're done with the
* Camera object.</p>
*/
- public final void release() {
+ public final void release() {
native_release();
}
/**
* Reconnect to the camera after passing it to MediaRecorder. To save
* setup/teardown time, a client of Camera can pass an initialized Camera
- * object to a MediaRecorder to use for video recording. Once the
+ * object to a MediaRecorder to use for video recording. Once the
* MediaRecorder is done with the Camera, this method can be used to
* re-establish a connection with the camera hardware. NOTE: The Camera
* object must first be unlocked by the process that owns it before it
- * can be connected to another proces.
+ * can be connected to another process.
*
* @throws IOException if the method fails.
*
@@ -134,7 +141,7 @@ public class Camera {
* @hide
*/
public native final void reconnect() throws IOException;
-
+
/**
* Lock the camera to prevent other processes from accessing it. To save
* setup/teardown time, a client of Camera can pass an initialized Camera
@@ -149,9 +156,9 @@ public class Camera {
* @hide
*/
public native final int lock();
-
+
/**
- * Unlock the camera to allow aother process to access it. To save
+ * Unlock the camera to allow another process to access it. To save
* setup/teardown time, a client of Camera can pass an initialized Camera
* object to another process. This method is used to unlock the Camera
* object before handing off the Camera object to the other process.
@@ -162,12 +169,12 @@ public class Camera {
* @hide
*/
public native final int unlock();
-
+
/**
* Sets the SurfaceHolder to be used for a picture preview. If the surface
* changed since the last call, the screen will blank. Nothing happens
* if the same surface is re-set.
- *
+ *
* @param holder the SurfaceHolder upon which to place the picture preview
* @throws IOException if the method fails.
*/
@@ -192,21 +199,24 @@ public class Camera {
* @param data The contents of the preview frame in the format defined
* by {@link android.graphics.PixelFormat}, which can be queried
* with {@link android.hardware.Camera.Parameters#getPreviewFormat()}.
+ * If {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}
+ * is never called, the default will be the YCbCr_420_SP
+ * (NV21) format.
* @param camera The Camera service object.
*/
void onPreviewFrame(byte[] data, Camera camera);
};
-
+
/**
* Start drawing preview frames to the surface.
*/
public native final void startPreview();
-
+
/**
* Stop drawing preview frames to the surface.
*/
public native final void stopPreview();
-
+
/**
* Return current preview state.
*
@@ -214,7 +224,7 @@ public class Camera {
* @hide
*/
public native final boolean previewEnabled();
-
+
/**
* Can be called at any time to instruct the camera to use a callback for
* each preview frame in addition to displaying it.
@@ -263,15 +273,17 @@ public class Camera {
return;
case CAMERA_MSG_RAW_IMAGE:
- if (mRawImageCallback != null)
+ if (mRawImageCallback != null) {
mRawImageCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
return;
case CAMERA_MSG_COMPRESSED_IMAGE:
- if (mJpegCallback != null)
+ if (mJpegCallback != null) {
mJpegCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
return;
-
+
case CAMERA_MSG_PREVIEW_FRAME:
if (mPreviewCallback != null) {
mPreviewCallback.onPreviewFrame((byte[])msg.obj, mCamera);
@@ -281,15 +293,29 @@ public class Camera {
}
return;
+ case CAMERA_MSG_POSTVIEW_FRAME:
+ if (mPostviewCallback != null) {
+ mPostviewCallback.onPictureTaken((byte[])msg.obj, mCamera);
+ }
+ return;
+
case CAMERA_MSG_FOCUS:
- if (mAutoFocusCallback != null)
+ if (mAutoFocusCallback != null) {
mAutoFocusCallback.onAutoFocus(msg.arg1 == 0 ? false : true, mCamera);
+ }
+ return;
+
+ case CAMERA_MSG_ZOOM:
+ if (mZoomCallback != null) {
+ mZoomCallback.onZoomUpdate(msg.arg1, mCamera);
+ }
return;
case CAMERA_MSG_ERROR :
Log.e(TAG, "Error " + msg.arg1);
- if (mErrorCallback != null)
+ if (mErrorCallback != null) {
mErrorCallback.onError(msg.arg1, mCamera);
+ }
return;
default:
@@ -325,8 +351,10 @@ public class Camera {
public interface AutoFocusCallback
{
/**
- * Callback for the camera auto focus.
- *
+ * Callback for the camera auto focus. If the camera does not support
+ * auto-focus and autoFocus is called, onAutoFocus will be called
+ * immediately with success.
+ *
* @param success true if focus was successful, false if otherwise
* @param camera the Camera service object
*/
@@ -334,13 +362,12 @@ public class Camera {
};
/**
- * Starts auto-focus function and registers a callback function to
- * run when camera is focused. Only valid after startPreview() has
- * been called.
- * <p>Devices that do not support auto-focus will trigger a "fake"
- * callback to the
- * {@link android.hardware.Camera.AutoFocusCallback}.
- * If your application should not be installed
+ * Starts auto-focus function and registers a callback function to run when
+ * camera is focused. Only valid after startPreview() has been called. If
+ * the camera does not support auto-focus, it is a no-op and {@link
+ * AutoFocusCallback#onAutoFocus(boolean, Camera)} callback will be called
+ * immediately.
+ * <p>If your application should not be installed
* on devices without auto-focus, you must declare that your application
* uses auto-focus with the
* <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">&lt;uses-feature></a>
@@ -373,7 +400,7 @@ public class Camera {
public interface PictureCallback {
/**
* Callback for when a picture is taken.
- *
+ *
* @param data a byte array of the picture data
* @param camera the Camera service object
*/
@@ -381,28 +408,78 @@ public class Camera {
};
/**
- * Triggers an asynchronous image capture. The camera service
- * will initiate a series of callbacks to the application as the
- * image capture progresses. The shutter callback occurs after
- * the image is captured. This can be used to trigger a sound
- * to let the user know that image has been captured. The raw
- * callback occurs when the raw image data is available. The jpeg
- * callback occurs when the compressed image is available. If the
- * application does not need a particular callback, a null can be
- * passed instead of a callback method.
- *
+ * Triggers an asynchronous image capture. The camera service will initiate
+ * a series of callbacks to the application as the image capture progresses.
+ * The shutter callback occurs after the image is captured. This can be used
+ * to trigger a sound to let the user know that image has been captured. The
+ * raw callback occurs when the raw image data is available (NOTE: the data
+ * may be null if the hardware does not have enough memory to make a copy).
+ * The jpeg callback occurs when the compressed image is available. If the
+ * application does not need a particular callback, a null can be passed
+ * instead of a callback method.
+ *
* @param shutter callback after the image is captured, may be null
* @param raw callback with raw image data, may be null
* @param jpeg callback with jpeg image data, may be null
*/
public final void takePicture(ShutterCallback shutter, PictureCallback raw,
PictureCallback jpeg) {
+ takePicture(shutter, raw, null, jpeg);
+ }
+ private native final void native_takePicture();
+
+ /**
+ * Triggers an asynchronous image capture. The camera service will initiate
+ * a series of callbacks to the application as the image capture progresses.
+ * The shutter callback occurs after the image is captured. This can be used
+ * to trigger a sound to let the user know that image has been captured. The
+ * raw callback occurs when the raw image data is available (NOTE: the data
+ * may be null if the hardware does not have enough memory to make a copy).
+ * The postview callback occurs when a scaled, fully processed postview
+ * image is available (NOTE: not all hardware supports this). The jpeg
+ * callback occurs when the compressed image is available. If the
+ * application does not need a particular callback, a null can be passed
+ * instead of a callback method.
+ *
+ * @param shutter callback after the image is captured, may be null
+ * @param raw callback with raw image data, may be null
+ * @param postview callback with postview image data, may be null
+ * @param jpeg callback with jpeg image data, may be null
+ */
+ public final void takePicture(ShutterCallback shutter, PictureCallback raw,
+ PictureCallback postview, PictureCallback jpeg) {
mShutterCallback = shutter;
mRawImageCallback = raw;
+ mPostviewCallback = postview;
mJpegCallback = jpeg;
native_takePicture();
}
- private native final void native_takePicture();
+
+ /**
+ * Handles the zoom callback.
+ */
+ public interface ZoomCallback
+ {
+ /**
+ * Callback for zoom updates
+ * @param zoomLevel new zoom level in 1/1000 increments,
+ * e.g. a zoom of 3.2x is stored as 3200. Accuracy of the
+ * value is dependent on the hardware implementation. Not
+ * all devices will generate this callback.
+ * @param camera the Camera service object
+ */
+ void onZoomUpdate(int zoomLevel, Camera camera);
+ };
+
+ /**
+ * Registers a callback to be invoked when the zoom
+ * level is updated by the camera driver.
+ * @param cb the callback to run
+ */
+ public final void setZoomCallback(ZoomCallback cb)
+ {
+ mZoomCallback = cb;
+ }
// These match the enum in include/ui/Camera.h
/** Unspecified camerar error. @see #ErrorCallback */
@@ -410,7 +487,7 @@ public class Camera {
/** Media server died. In this case, the application must release the
* Camera object and instantiate a new one. @see #ErrorCallback */
public static final int CAMERA_ERROR_SERVER_DIED = 100;
-
+
/**
* Handles the camera error callback.
*/
@@ -436,13 +513,13 @@ public class Camera {
{
mErrorCallback = cb;
}
-
+
private native final void native_setParameters(String params);
private native final String native_getParameters();
/**
* Sets the Parameters for pictures from this Camera service.
- *
+ *
* @param params the Parameters to use for this Camera service
*/
public void setParameters(Parameters params) {
@@ -465,7 +542,7 @@ public class Camera {
public class Size {
/**
* Sets the dimensions for pictures.
- *
+ *
* @param w the photo width (pixels)
* @param h the photo height (pixels)
*/
@@ -481,8 +558,111 @@ public class Camera {
/**
* Handles the parameters for pictures created by a Camera service.
+ *
+ * <p>To make camera parameters take effect, applications have to call
+ * Camera.setParameters. For example, after setWhiteBalance is called, white
+ * balance is not changed until Camera.setParameters() is called.
+ *
+ * <p>Different devices may have different camera capabilities, such as
+ * picture size or flash modes. The application should query the camera
+ * capabilities before setting parameters. For example, the application
+ * should call getSupportedColorEffects before calling setEffect. If the
+ * camera does not support color effects, getSupportedColorEffects will
+ * return null.
*/
public class Parameters {
+ // Parameter keys to communicate with the camera driver.
+ private static final String KEY_PREVIEW_SIZE = "preview-size";
+ private static final String KEY_PREVIEW_FORMAT = "preview-format";
+ private static final String KEY_PREVIEW_FRAME_RATE = "preview-frame-rate";
+ private static final String KEY_PICTURE_SIZE = "picture-size";
+ private static final String KEY_PICTURE_FORMAT = "picture-format";
+ private static final String KEY_JPEG_THUMBNAIL_WIDTH = "jpeg-thumbnail-width";
+ private static final String KEY_JPEG_THUMBNAIL_HEIGHT = "jpeg-thumbnail-height";
+ private static final String KEY_JPEG_THUMBNAIL_QUALITY = "jpeg-thumbnail-quality";
+ private static final String KEY_JPEG_QUALITY = "jpeg-quality";
+ private static final String KEY_ROTATION = "rotation";
+ private static final String KEY_GPS_LATITUDE = "gps-latitude";
+ private static final String KEY_GPS_LONGITUDE = "gps-longitude";
+ private static final String KEY_GPS_ALTITUDE = "gps-altitude";
+ private static final String KEY_GPS_TIMESTAMP = "gps-timestamp";
+ private static final String KEY_WHITE_BALANCE = "whitebalance";
+ private static final String KEY_EFFECT = "effect";
+ private static final String KEY_ANTIBANDING = "antibanding";
+ private static final String KEY_SCENE_MODE = "scene-mode";
+ private static final String KEY_FLASH_MODE = "flash-mode";
+ // Parameter key suffix for supported values.
+ private static final String SUPPORTED_VALUES_SUFFIX = "-values";
+
+ // Values for white balance settings.
+ public static final String WHITE_BALANCE_AUTO = "auto";
+ public static final String WHITE_BALANCE_INCANDESCENT = "incandescent";
+ public static final String WHITE_BALANCE_FLUORESCENT = "fluorescent";
+ public static final String WHITE_BALANCE_WARM_FLUORESCENT = "warm-fluorescent";
+ public static final String WHITE_BALANCE_DAYLIGHT = "daylight";
+ public static final String WHITE_BALANCE_CLOUDY_DAYLIGHT = "cloudy-daylight";
+ public static final String WHITE_BALANCE_TWILIGHT = "twilight";
+ public static final String WHITE_BALANCE_SHADE = "shade";
+
+ // Values for color effect settings.
+ public static final String EFFECT_NONE = "none";
+ public static final String EFFECT_MONO = "mono";
+ public static final String EFFECT_NEGATIVE = "negative";
+ public static final String EFFECT_SOLARIZE = "solarize";
+ public static final String EFFECT_SEPIA = "sepia";
+ public static final String EFFECT_POSTERIZE = "posterize";
+ public static final String EFFECT_WHITEBOARD = "whiteboard";
+ public static final String EFFECT_BLACKBOARD = "blackboard";
+ public static final String EFFECT_AQUA = "aqua";
+
+ // Values for antibanding settings.
+ public static final String ANTIBANDING_AUTO = "auto";
+ public static final String ANTIBANDING_50HZ = "50hz";
+ public static final String ANTIBANDING_60HZ = "60hz";
+ public static final String ANTIBANDING_OFF = "off";
+
+ // Values for flash mode settings.
+ /**
+ * Flash will not be fired.
+ */
+ public static final String FLASH_MODE_OFF = "off";
+ /**
+ * Flash will be fired automatically when required. The timing is
+ * decided by camera driver.
+ */
+ public static final String FLASH_MODE_AUTO = "auto";
+ /**
+ * Flash will always be fired. The timing is decided by camera driver.
+ */
+ public static final String FLASH_MODE_ON = "on";
+ /**
+ * Flash will be fired in red-eye reduction mode.
+ */
+ public static final String FLASH_MODE_RED_EYE = "red-eye";
+
+ // Values for scene mode settings.
+ public static final String SCENE_MODE_AUTO = "auto";
+ public static final String SCENE_MODE_ACTION = "action";
+ public static final String SCENE_MODE_PORTRAIT = "portrait";
+ public static final String SCENE_MODE_LANDSCAPE = "landscape";
+ public static final String SCENE_MODE_NIGHT = "night";
+ public static final String SCENE_MODE_NIGHT_PORTRAIT = "night-portrait";
+ public static final String SCENE_MODE_THEATRE = "theatre";
+ public static final String SCENE_MODE_BEACH = "beach";
+ public static final String SCENE_MODE_SNOW = "snow";
+ public static final String SCENE_MODE_SUNSET = "sunset";
+ public static final String SCENE_MODE_STEADYPHOTO = "steadyphoto";
+ public static final String SCENE_MODE_FIREWORKS = "fireworks";
+ public static final String SCENE_MODE_SPORTS = "sports";
+ public static final String SCENE_MODE_PARTY = "party";
+ public static final String SCENE_MODE_CANDLELIGHT = "candlelight";
+
+ // Formats for setPreviewFormat and setPictureFormat.
+ private static final String PIXEL_FORMAT_YUV422SP = "yuv422sp";
+ private static final String PIXEL_FORMAT_YUV420SP = "yuv420sp";
+ private static final String PIXEL_FORMAT_RGB565 = "rgb565";
+ private static final String PIXEL_FORMAT_JPEG = "jpeg";
+
private HashMap<String, String> mMap;
private Parameters() {
@@ -505,7 +685,7 @@ public class Camera {
* Creates a single string with all the parameters set in
* this Parameters object.
* <p>The {@link #unflatten(String)} method does the reverse.</p>
- *
+ *
* @return a String with all values from this Parameters object, in
* semi-colon delimited key-value pairs
*/
@@ -523,16 +703,16 @@ public class Camera {
}
/**
- * Takes a flattened string of parameters and adds each one to
+ * Takes a flattened string of parameters and adds each one to
* this Parameters object.
* <p>The {@link #flatten()} method does the reverse.</p>
- *
- * @param flattened a String of parameters (key-value paired) that
+ *
+ * @param flattened a String of parameters (key-value paired) that
* are semi-colon delimited
*/
public void unflatten(String flattened) {
mMap.clear();
-
+
StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
while (tokenizer.hasMoreElements()) {
String kv = tokenizer.nextToken();
@@ -545,14 +725,14 @@ public class Camera {
mMap.put(k, v);
}
}
-
+
public void remove(String key) {
mMap.remove(key);
}
/**
* Sets a String parameter.
- *
+ *
* @param key the key name for the parameter
* @param value the String value of the parameter
*/
@@ -571,7 +751,7 @@ public class Camera {
/**
* Sets an integer parameter.
- *
+ *
* @param key the key name for the parameter
* @param value the int value of the parameter
*/
@@ -581,7 +761,7 @@ public class Camera {
/**
* Returns the value of a String parameter.
- *
+ *
* @param key the key name for the parameter
* @return the String value of the parameter
*/
@@ -591,7 +771,7 @@ public class Camera {
/**
* Returns the value of an integer parameter.
- *
+ *
* @param key the key name for the parameter
* @return the int value of the parameter
*/
@@ -601,109 +781,130 @@ public class Camera {
/**
* Sets the dimensions for preview pictures.
- *
+ *
* @param width the width of the pictures, in pixels
* @param height the height of the pictures, in pixels
*/
public void setPreviewSize(int width, int height) {
String v = Integer.toString(width) + "x" + Integer.toString(height);
- set("preview-size", v);
+ set(KEY_PREVIEW_SIZE, v);
}
/**
* Returns the dimensions setting for preview pictures.
- *
- * @return a Size object with the height and width setting
+ *
+ * @return a Size object with the height and width setting
* for the preview picture
*/
public Size getPreviewSize() {
- String pair = get("preview-size");
- if (pair == null)
- return null;
- String[] dims = pair.split("x");
- if (dims.length != 2)
- return null;
-
- return new Size(Integer.parseInt(dims[0]),
- Integer.parseInt(dims[1]));
+ String pair = get(KEY_PREVIEW_SIZE);
+ return strToSize(pair);
+ }
+ /**
+ * Gets the supported preview sizes.
+ *
+ * @return a List of Size object. null if preview size setting is not
+ * supported.
+ */
+ public List<Size> getSupportedPreviewSizes() {
+ String str = get(KEY_PREVIEW_SIZE + SUPPORTED_VALUES_SUFFIX);
+ return splitSize(str);
}
/**
- * Sets the dimensions for EXIF thumbnails.
- *
+ * Sets the dimensions for EXIF thumbnail in Jpeg picture.
+ *
* @param width the width of the thumbnail, in pixels
* @param height the height of the thumbnail, in pixels
+ */
+ public void setJpegThumbnailSize(int width, int height) {
+ set(KEY_JPEG_THUMBNAIL_WIDTH, width);
+ set(KEY_JPEG_THUMBNAIL_HEIGHT, height);
+ }
+
+ /**
+ * Returns the dimensions for EXIF thumbnail in Jpeg picture.
*
- * FIXME: unhide before release
- * @hide
+ * @return a Size object with the height and width setting for the EXIF
+ * thumbnails
*/
- public void setThumbnailSize(int width, int height) {
- set("jpeg-thumbnail-width", width);
- set("jpeg-thumbnail-height", height);
+ public Size getJpegThumbnailSize() {
+ return new Size(getInt(KEY_JPEG_THUMBNAIL_WIDTH),
+ getInt(KEY_JPEG_THUMBNAIL_HEIGHT));
}
/**
- * Returns the dimensions for EXIF thumbnail
- *
- * @return a Size object with the height and width setting
- * for the EXIF thumbnails
+ * Sets the quality of the EXIF thumbnail in Jpeg picture.
*
- * FIXME: unhide before release
- * @hide
+ * @param quality the JPEG quality of the EXIF thumbnail. The range is 1
+ * to 100, with 100 being the best.
*/
- public Size getThumbnailSize() {
- return new Size(getInt("jpeg-thumbnail-width"),
- getInt("jpeg-thumbnail-height"));
+ public void setJpegThumbnailQuality(int quality) {
+ set(KEY_JPEG_THUMBNAIL_QUALITY, quality);
}
/**
- * Sets the quality of the EXIF thumbnail
- *
- * @param quality the JPEG quality of the EXIT thumbnail
+ * Returns the quality setting for the EXIF thumbnail in Jpeg picture.
*
- * FIXME: unhide before release
- * @hide
+ * @return the JPEG quality setting of the EXIF thumbnail.
*/
- public void setThumbnailQuality(int quality) {
- set("jpeg-thumbnail-quality", quality);
+ public int getJpegThumbnailQuality() {
+ return getInt(KEY_JPEG_THUMBNAIL_QUALITY);
}
/**
- * Returns the quality setting for the EXIF thumbnail
- *
- * @return the JPEG quality setting of the EXIF thumbnail
+ * Sets Jpeg quality of captured picture.
*
- * FIXME: unhide before release
- * @hide
+ * @param quality the JPEG quality of captured picture. The range is 1
+ * to 100, with 100 being the best.
*/
- public int getThumbnailQuality() {
- return getInt("jpeg-thumbnail-quality");
+ public void setJpegQuality(int quality) {
+ set(KEY_JPEG_QUALITY, quality);
+ }
+
+ /**
+ * Returns the quality setting for the JPEG picture.
+ *
+ * @return the JPEG picture quality setting.
+ */
+ public int getJpegQuality() {
+ return getInt(KEY_JPEG_QUALITY);
}
/**
* Sets the rate at which preview frames are received.
- *
+ *
* @param fps the frame rate (frames per second)
*/
public void setPreviewFrameRate(int fps) {
- set("preview-frame-rate", fps);
+ set(KEY_PREVIEW_FRAME_RATE, fps);
}
/**
* Returns the setting for the rate at which preview frames
* are received.
- *
+ *
* @return the frame rate setting (frames per second)
*/
public int getPreviewFrameRate() {
- return getInt("preview-frame-rate");
+ return getInt(KEY_PREVIEW_FRAME_RATE);
+ }
+
+ /**
+ * Gets the supported preview frame rates.
+ *
+ * @return a List of Integer objects (preview frame rates). null if
+ * preview frame rate setting is not supported.
+ */
+ public List<Integer> getSupportedPreviewFrameRates() {
+ String str = get(KEY_PREVIEW_FRAME_RATE + SUPPORTED_VALUES_SUFFIX);
+ return splitInt(str);
}
/**
* Sets the image format for preview pictures.
- * <p>If the image format is not set with this method, then the
- * preview format will default to
+ * <p>If this is never called, the default format will be
* {@link android.graphics.PixelFormat#YCbCr_420_SP}, which
* uses the NV21 encoding format.</p>
*
@@ -717,57 +918,73 @@ public class Camera {
public void setPreviewFormat(int pixel_format) {
String s = cameraFormatForPixelFormat(pixel_format);
if (s == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(
+ "Invalid pixel_format=" + pixel_format);
}
- set("preview-format", s);
+ set(KEY_PREVIEW_FORMAT, s);
}
/**
- * Returns the image format for preview pictures.
+ * Returns the image format for preview pictures got from
+ * {@link PreviewCallback}.
*
* @return the {@link android.graphics.PixelFormat} int representing
* the preview picture format.
*/
public int getPreviewFormat() {
- return pixelFormatForCameraFormat(get("preview-format"));
+ return pixelFormatForCameraFormat(get(KEY_PREVIEW_FORMAT));
+ }
+
+ /**
+ * Gets the supported preview formats.
+ *
+ * @return a List of Integer objects. null if preview format setting is
+ * not supported.
+ */
+ public List<Integer> getSupportedPreviewFormats() {
+ String str = get(KEY_PREVIEW_FORMAT + SUPPORTED_VALUES_SUFFIX);
+ return splitInt(str);
}
/**
* Sets the dimensions for pictures.
- *
+ *
* @param width the width for pictures, in pixels
* @param height the height for pictures, in pixels
*/
public void setPictureSize(int width, int height) {
String v = Integer.toString(width) + "x" + Integer.toString(height);
- set("picture-size", v);
+ set(KEY_PICTURE_SIZE, v);
}
/**
* Returns the dimension setting for pictures.
- *
- * @return a Size object with the height and width setting
+ *
+ * @return a Size object with the height and width setting
* for pictures
*/
public Size getPictureSize() {
- String pair = get("picture-size");
- if (pair == null)
- return null;
- String[] dims = pair.split("x");
- if (dims.length != 2)
- return null;
-
- return new Size(Integer.parseInt(dims[0]),
- Integer.parseInt(dims[1]));
+ String pair = get(KEY_PICTURE_SIZE);
+ return strToSize(pair);
+ }
+ /**
+ * Gets the supported picture sizes.
+ *
+ * @return a List of Size objects. null if picture size setting is not
+ * supported.
+ */
+ public List<Size> getSupportedPictureSizes() {
+ String str = get(KEY_PICTURE_SIZE + SUPPORTED_VALUES_SUFFIX);
+ return splitSize(str);
}
/**
* Sets the image format for pictures.
- *
- * @param pixel_format the desired picture format
- * (<var>PixelFormat.YCbCr_420_SP</var>,
+ *
+ * @param pixel_format the desired picture format
+ * (<var>PixelFormat.YCbCr_420_SP (NV21)</var>,
* <var>PixelFormat.RGB_565</var>, or
* <var>PixelFormat.JPEG</var>)
* @see android.graphics.PixelFormat
@@ -775,27 +992,39 @@ public class Camera {
public void setPictureFormat(int pixel_format) {
String s = cameraFormatForPixelFormat(pixel_format);
if (s == null) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException(
+ "Invalid pixel_format=" + pixel_format);
}
- set("picture-format", s);
+ set(KEY_PICTURE_FORMAT, s);
}
/**
* Returns the image format for pictures.
- *
+ *
* @return the PixelFormat int representing the picture format
*/
public int getPictureFormat() {
- return pixelFormatForCameraFormat(get("picture-format"));
+ return pixelFormatForCameraFormat(get(KEY_PICTURE_FORMAT));
+ }
+
+ /**
+ * Gets the supported picture formats.
+ *
+ * @return a List of Integer objects (values are PixelFormat.XXX). null
+ * if picture setting is not supported.
+ */
+ public List<Integer> getSupportedPictureFormats() {
+ String str = get(KEY_PICTURE_SIZE + SUPPORTED_VALUES_SUFFIX);
+ return splitInt(str);
}
private String cameraFormatForPixelFormat(int pixel_format) {
switch(pixel_format) {
- case PixelFormat.YCbCr_422_SP: return "yuv422sp";
- case PixelFormat.YCbCr_420_SP: return "yuv420sp";
- case PixelFormat.RGB_565: return "rgb565";
- case PixelFormat.JPEG: return "jpeg";
+ case PixelFormat.YCbCr_422_SP: return PIXEL_FORMAT_YUV422SP;
+ case PixelFormat.YCbCr_420_SP: return PIXEL_FORMAT_YUV420SP;
+ case PixelFormat.RGB_565: return PIXEL_FORMAT_RGB565;
+ case PixelFormat.JPEG: return PIXEL_FORMAT_JPEG;
default: return null;
}
}
@@ -804,21 +1033,309 @@ public class Camera {
if (format == null)
return PixelFormat.UNKNOWN;
- if (format.equals("yuv422sp"))
+ if (format.equals(PIXEL_FORMAT_YUV422SP))
return PixelFormat.YCbCr_422_SP;
- if (format.equals("yuv420sp"))
+ if (format.equals(PIXEL_FORMAT_YUV420SP))
return PixelFormat.YCbCr_420_SP;
- if (format.equals("rgb565"))
+ if (format.equals(PIXEL_FORMAT_RGB565))
return PixelFormat.RGB_565;
- if (format.equals("jpeg"))
+ if (format.equals(PIXEL_FORMAT_JPEG))
return PixelFormat.JPEG;
return PixelFormat.UNKNOWN;
}
+ /**
+ * Sets the orientation of the device in degrees, which instructs the
+ * camera driver to rotate the picture and thumbnail, in order to match
+ * what the user sees from the viewfinder. For example, suppose the
+ * natural position of the device is landscape. If the user takes a
+ * picture in landscape mode in 2048x1536 resolution, the rotation
+ * should be set to 0. If the user rotates the phone 90 degrees
+ * clockwise, the rotation should be set to 90. Applications can use
+ * {@link android.view.OrientationEventListener} to set this parameter.
+ *
+ * Since the picture is rotated, the orientation in the EXIF header is
+ * missing or always 1 (row #0 is top and column #0 is left side).
+ *
+ * @param rotation The orientation of the device in degrees. Rotation
+ * can only be 0, 90, 180 or 270.
+ * @throws IllegalArgumentException if rotation value is invalid.
+ * @see android.view.OrientationEventListener
+ */
+ public void setRotation(int rotation) {
+ if (rotation == 0 || rotation == 90 || rotation == 180
+ || rotation == 270) {
+ set(KEY_ROTATION, Integer.toString(rotation));
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid rotation=" + rotation);
+ }
+ }
+
+ /**
+ * Sets GPS latitude coordinate. This will be stored in JPEG EXIF
+ * header.
+ *
+ * @param latitude GPS latitude coordinate.
+ */
+ public void setGpsLatitude(double latitude) {
+ set(KEY_GPS_LATITUDE, Double.toString(latitude));
+ }
+
+ /**
+ * Sets GPS longitude coordinate. This will be stored in JPEG EXIF
+ * header.
+ *
+ * @param longitude GPS longitude coordinate.
+ */
+ public void setGpsLongitude(double longitude) {
+ set(KEY_GPS_LONGITUDE, Double.toString(longitude));
+ }
+
+ /**
+ * Sets GPS altitude. This will be stored in JPEG EXIF header.
+ *
+ * @param altitude GPS altitude in meters.
+ */
+ public void setGpsAltitude(double altitude) {
+ set(KEY_GPS_ALTITUDE, Double.toString(altitude));
+ }
+
+ /**
+ * Sets GPS timestamp. This will be stored in JPEG EXIF header.
+ *
+ * @param timestamp GPS timestamp (UTC in seconds since January 1,
+ * 1970).
+ */
+ public void setGpsTimestamp(long timestamp) {
+ set(KEY_GPS_TIMESTAMP, Long.toString(timestamp));
+ }
+
+ /**
+ * Removes GPS latitude, longitude, altitude, and timestamp from the
+ * parameters.
+ */
+ public void removeGpsData() {
+ remove(KEY_GPS_LATITUDE);
+ remove(KEY_GPS_LONGITUDE);
+ remove(KEY_GPS_ALTITUDE);
+ remove(KEY_GPS_TIMESTAMP);
+ }
+
+ /**
+ * Gets the current white balance setting.
+ *
+ * @return one of WHITE_BALANCE_XXX string constant. null if white
+ * balance setting is not supported.
+ */
+ public String getWhiteBalance() {
+ return get(KEY_WHITE_BALANCE);
+ }
+
+ /**
+ * Sets the white balance.
+ *
+ * @param value WHITE_BALANCE_XXX string constant.
+ */
+ public void setWhiteBalance(String value) {
+ set(KEY_WHITE_BALANCE, value);
+ }
+
+ /**
+ * Gets the supported white balance.
+ *
+ * @return a List of WHITE_BALANCE_XXX string constants. null if white
+ * balance setting is not supported.
+ */
+ public List<String> getSupportedWhiteBalance() {
+ String str = get(KEY_WHITE_BALANCE + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+ /**
+ * Gets the current color effect setting.
+ *
+ * @return one of EFFECT_XXX string constant. null if color effect
+ * setting is not supported.
+ */
+ public String getColorEffect() {
+ return get(KEY_EFFECT);
+ }
+
+ /**
+ * Sets the current color effect setting.
+ *
+ * @param value EFFECT_XXX string constants.
+ */
+ public void setColorEffect(String value) {
+ set(KEY_EFFECT, value);
+ }
+
+ /**
+ * Gets the supported color effects.
+ *
+ * @return a List of EFFECT_XXX string constants. null if color effect
+ * setting is not supported.
+ */
+ public List<String> getSupportedColorEffects() {
+ String str = get(KEY_EFFECT + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+
+ /**
+ * Gets the current antibanding setting.
+ *
+ * @return one of ANTIBANDING_XXX string constant. null if antibanding
+ * setting is not supported.
+ */
+ public String getAntibanding() {
+ return get(KEY_ANTIBANDING);
+ }
+
+ /**
+ * Sets the antibanding.
+ *
+ * @param antibanding ANTIBANDING_XXX string constant.
+ */
+ public void setAntibanding(String antibanding) {
+ set(KEY_ANTIBANDING, antibanding);
+ }
+
+ /**
+ * Gets the supported antibanding values.
+ *
+ * @return a List of ANTIBANDING_XXX string constants. null if
+ * antibanding setting is not supported.
+ */
+ public List<String> getSupportedAntibanding() {
+ String str = get(KEY_ANTIBANDING + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+ /**
+ * Gets the current scene mode setting.
+ *
+ * @return one of SCENE_MODE_XXX string constant. null if scene mode
+ * setting is not supported.
+ */
+ public String getSceneMode() {
+ return get(KEY_SCENE_MODE);
+ }
+
+ /**
+ * Sets the scene mode.
+ *
+ * @param value SCENE_MODE_XXX string constants.
+ */
+ public void setSceneMode(String value) {
+ set(KEY_SCENE_MODE, value);
+ }
+
+ /**
+ * Gets the supported scene modes.
+ *
+ * @return a List of SCENE_MODE_XXX string constant. null if scene mode
+ * setting is not supported.
+ */
+ public List<String> getSupportedSceneModes() {
+ String str = get(KEY_SCENE_MODE + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+ /**
+ * Gets the current flash mode setting.
+ *
+ * @return one of FLASH_MODE_XXX string constant. null if flash mode
+ * setting is not supported.
+ */
+ public String getFlashMode() {
+ return get(KEY_FLASH_MODE);
+ }
+
+ /**
+ * Sets the flash mode.
+ *
+ * @param value FLASH_MODE_XXX string constants.
+ */
+ public void setFlashMode(String value) {
+ set(KEY_FLASH_MODE, value);
+ }
+
+ /**
+ * Gets the supported flash modes.
+ *
+ * @return a List of FLASH_MODE_XXX string constants. null if flash mode
+ * setting is not supported.
+ */
+ public List<String> getSupportedFlashModes() {
+ String str = get(KEY_FLASH_MODE + SUPPORTED_VALUES_SUFFIX);
+ return split(str);
+ }
+
+ // Splits a comma delimited string to an ArrayList of String.
+ // Return null if the passing string is null or the size is 0.
+ private ArrayList<String> split(String str) {
+ if (str == null) return null;
+
+ // Use StringTokenizer because it is faster than split.
+ StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ ArrayList<String> substrings = new ArrayList<String>();
+ while (tokenizer.hasMoreElements()) {
+ substrings.add(tokenizer.nextToken());
+ }
+ return substrings;
+ }
+
+ // Splits a comma delimited string to an ArrayList of Integer.
+ // Return null if the passing string is null or the size is 0.
+ private ArrayList<Integer> splitInt(String str) {
+ if (str == null) return null;
+
+ StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ ArrayList<Integer> substrings = new ArrayList<Integer>();
+ while (tokenizer.hasMoreElements()) {
+ String token = tokenizer.nextToken();
+ substrings.add(Integer.parseInt(token));
+ }
+ if (substrings.size() == 0) return null;
+ return substrings;
+ }
+
+ // Splits a comma delimited string to an ArrayList of Size.
+ // Return null if the passing string is null or the size is 0.
+ private ArrayList<Size> splitSize(String str) {
+ if (str == null) return null;
+
+ StringTokenizer tokenizer = new StringTokenizer(str, ",");
+ ArrayList<Size> sizeList = new ArrayList<Size>();
+ while (tokenizer.hasMoreElements()) {
+ Size size = strToSize(tokenizer.nextToken());
+ if (size != null) sizeList.add(size);
+ }
+ if (sizeList.size() == 0) return null;
+ return sizeList;
+ }
+
+ // Parses a string (ex: "480x320") to Size object.
+ // Return null if the passing string is null.
+ private Size strToSize(String str) {
+ if (str == null) return null;
+
+ int pos = str.indexOf('x');
+ if (pos != -1) {
+ String width = str.substring(0, pos);
+ String height = str.substring(pos + 1);
+ return new Size(Integer.parseInt(width),
+ Integer.parseInt(height));
+ }
+ Log.e(TAG, "Invalid size parameter string=" + str);
+ return null;
+ }
};
}
diff --git a/core/java/android/hardware/SensorListener.java b/core/java/android/hardware/SensorListener.java
index cfa184b..c71e968 100644
--- a/core/java/android/hardware/SensorListener.java
+++ b/core/java/android/hardware/SensorListener.java
@@ -20,9 +20,8 @@ package android.hardware;
* Used for receiving notifications from the SensorManager when
* sensor values have changed.
*
- * This interface is deprecated, use
+ * @deprecated Use
* {@link android.hardware.SensorEventListener SensorEventListener} instead.
- *
*/
@Deprecated
public interface SensorListener {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index bf945ec..271f973 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -116,47 +116,67 @@ public class SensorManager
@Deprecated
public static final int SENSOR_ORIENTATION_RAW = 1 << 7;
- /** A constant that includes all sensors */
+ /** A constant that includes all sensors
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int SENSOR_ALL = 0x7F;
- /** Smallest sensor ID */
+ /** Smallest sensor ID
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int SENSOR_MIN = SENSOR_ORIENTATION;
- /** Largest sensor ID */
+ /** Largest sensor ID
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int SENSOR_MAX = ((SENSOR_ALL + 1)>>1);
/** Index of the X value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int DATA_X = 0;
/** Index of the Y value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int DATA_Y = 1;
/** Index of the Z value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int DATA_Z = 2;
/** Offset to the untransformed values in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int RAW_DATA_INDEX = 3;
/** Index of the untransformed X value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int RAW_DATA_X = 3;
/** Index of the untransformed Y value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int RAW_DATA_Y = 4;
/** Index of the untransformed Z value in the array returned by
- * {@link android.hardware.SensorListener#onSensorChanged} */
+ * {@link android.hardware.SensorListener#onSensorChanged}
+ * @deprecated use {@link android.hardware.Sensor Sensor} instead.
+ */
@Deprecated
public static final int RAW_DATA_Z = 5;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 6ee92ce..1f640ea 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -1992,8 +1992,9 @@ public class InputMethodService extends AbstractInputMethodService {
req.flags = InputConnection.GET_TEXT_WITH_STYLES;
req.hintMaxLines = 10;
req.hintMaxChars = 10000;
- mExtractedText = getCurrentInputConnection().getExtractedText(req,
- InputConnection.GET_EXTRACTED_TEXT_MONITOR);
+ InputConnection ic = getCurrentInputConnection();
+ mExtractedText = ic == null? null
+ : ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
final EditorInfo ei = getCurrentInputEditorInfo();
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index fea63be..4814b0a 100755
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -142,7 +142,7 @@ public class Keyboard {
private int[][] mGridNeighbors;
private int mProximityThreshold;
/** Number of key widths from current touch point to search for nearest keys. */
- private static float SEARCH_DISTANCE = 1.4f;
+ private static float SEARCH_DISTANCE = 1.8f;
/**
* Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 9c9c143..a141a2a 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -30,7 +30,6 @@ import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard.Key;
import android.os.Handler;
import android.os.Message;
-import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.GestureDetector;
@@ -38,6 +37,7 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup.LayoutParams;
import android.widget.PopupWindow;
import android.widget.TextView;
@@ -163,8 +163,8 @@ public class KeyboardView extends View implements View.OnClickListener {
private static final int MSG_REPEAT = 3;
private static final int MSG_LONGPRESS = 4;
- private static final int DELAY_BEFORE_PREVIEW = 40;
- private static final int DELAY_AFTER_PREVIEW = 60;
+ private static final int DELAY_BEFORE_PREVIEW = 0;
+ private static final int DELAY_AFTER_PREVIEW = 70;
private int mVerticalCorrection;
private int mProximityThreshold;
@@ -202,13 +202,17 @@ public class KeyboardView extends View implements View.OnClickListener {
private boolean mAbortKey;
private Key mInvalidatedKey;
private Rect mClipRegion = new Rect(0, 0, 0, 0);
-
+
+ // Variables for dealing with multiple pointers
+ private int mOldPointerCount = 1;
+ private float mOldPointerX;
+ private float mOldPointerY;
+
private Drawable mKeyBackground;
private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
private static final int REPEAT_START_DELAY = 400;
- private static final int LONGPRESS_TIMEOUT = 800;
- // Deemed to be too short : ViewConfiguration.getLongPressTimeout();
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
private static int MAX_NEARBY_KEYS = 12;
private int[] mDistances = new int[MAX_NEARBY_KEYS];
@@ -227,6 +231,8 @@ public class KeyboardView extends View implements View.OnClickListener {
private Rect mDirtyRect = new Rect();
/** The keyboard bitmap for faster updates */
private Bitmap mBuffer;
+ /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
+ private boolean mKeyboardChanged;
/** The canvas for the above mutable keyboard bitmap */
private Canvas mCanvas;
@@ -340,6 +346,7 @@ public class KeyboardView extends View implements View.OnClickListener {
mPaint.setAntiAlias(true);
mPaint.setTextSize(keyTextSize);
mPaint.setTextAlign(Align.CENTER);
+ mPaint.setAlpha(255);
mPadding = new Rect(0, 0, 0, 0);
mMiniKeyboardCache = new HashMap<Key,View>();
@@ -405,12 +412,14 @@ public class KeyboardView extends View implements View.OnClickListener {
List<Key> keys = mKeyboard.getKeys();
mKeys = keys.toArray(new Key[keys.size()]);
requestLayout();
- // Release buffer, just in case the new keyboard has a different size.
- // It will be reallocated on the next draw.
- mBuffer = null;
+ // Hint to reallocate the buffer if the size changed
+ mKeyboardChanged = true;
invalidateAllKeys();
computeProximityThreshold(keyboard);
mMiniKeyboardCache.clear(); // Not really necessary to do every time, but will free up views
+ // Switching to a different keyboard should abort any pending keys so that the key up
+ // doesn't get delivered to the old or new keyboard
+ mAbortKey = true; // Until the next ACTION_DOWN
}
/**
@@ -564,17 +573,21 @@ public class KeyboardView extends View implements View.OnClickListener {
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (mDrawPending || mBuffer == null) {
+ if (mDrawPending || mBuffer == null || mKeyboardChanged) {
onBufferDraw();
}
canvas.drawBitmap(mBuffer, 0, 0, null);
}
-
+
private void onBufferDraw() {
- if (mBuffer == null) {
- mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
- mCanvas = new Canvas(mBuffer);
+ if (mBuffer == null || mKeyboardChanged) {
+ if (mBuffer == null || mKeyboardChanged &&
+ (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
+ mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBuffer);
+ }
invalidateAllKeys();
+ mKeyboardChanged = false;
}
final Canvas canvas = mCanvas;
canvas.clipRect(mDirtyRect, Op.REPLACE);
@@ -590,7 +603,6 @@ public class KeyboardView extends View implements View.OnClickListener {
final Key[] keys = mKeys;
final Key invalidKey = mInvalidatedKey;
- paint.setAlpha(255);
paint.setColor(mKeyTextColor);
boolean drawSingleKey = false;
if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
@@ -611,7 +623,7 @@ public class KeyboardView extends View implements View.OnClickListener {
}
int[] drawableState = key.getCurrentDrawableState();
keyBackground.setState(drawableState);
-
+
// Switch the character to uppercase if shift is pressed
String label = key.label == null? null : adjustCase(key.label).toString();
@@ -680,7 +692,6 @@ public class KeyboardView extends View implements View.OnClickListener {
private int getKeyIndices(int x, int y, int[] allKeys) {
final Key[] keys = mKeys;
- final boolean shifted = mKeyboard.isShifted();
int primaryIndex = NOT_A_KEY;
int closestKey = NOT_A_KEY;
int closestKeyDist = mProximityThreshold + 1;
@@ -1011,19 +1022,56 @@ public class KeyboardView extends View implements View.OnClickListener {
}
return false;
}
-
+
@Override
public boolean onTouchEvent(MotionEvent me) {
+ // Convert multi-pointer up/down events to single up/down events to
+ // deal with the typical multi-pointer behavior of two-thumb typing
+ int pointerCount = me.getPointerCount();
+ boolean result = false;
+ if (pointerCount != mOldPointerCount) {
+ long now = me.getEventTime();
+ if (pointerCount == 1) {
+ // Send a down event for the latest pointer
+ MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+ me.getX(), me.getY(), me.getMetaState());
+ result = onModifiedTouchEvent(down);
+ down.recycle();
+ // If it's an up action, then deliver the up as well.
+ if (me.getAction() == MotionEvent.ACTION_UP) {
+ result = onModifiedTouchEvent(me);
+ }
+ } else {
+ // Send an up event for the last pointer
+ MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
+ mOldPointerX, mOldPointerY, me.getMetaState());
+ result = onModifiedTouchEvent(up);
+ up.recycle();
+ }
+ } else {
+ if (pointerCount == 1) {
+ mOldPointerX = me.getX();
+ mOldPointerY = me.getY();
+ result = onModifiedTouchEvent(me);
+ } else {
+ // Don't do anything when 2 pointers are down and moving.
+ result = true;
+ }
+ }
+ mOldPointerCount = pointerCount;
+ return result;
+ }
+
+ private boolean onModifiedTouchEvent(MotionEvent me) {
int touchX = (int) me.getX() - mPaddingLeft;
int touchY = (int) me.getY() + mVerticalCorrection - mPaddingTop;
int action = me.getAction();
long eventTime = me.getEventTime();
int keyIndex = getKeyIndices(touchX, touchY, null);
-
if (mGestureDetector.onTouchEvent(me)) {
showPreview(NOT_A_KEY);
mHandler.removeMessages(MSG_REPEAT);
- mHandler.removeMessages(MSG_LONGPRESS);
+ mHandler.removeMessages(MSG_LONGPRESS);
return true;
}
@@ -1072,7 +1120,7 @@ public class KeyboardView extends View implements View.OnClickListener {
if (keyIndex == mCurrentKey) {
mCurrentKeyTime += eventTime - mLastMoveTime;
continueLongPress = true;
- } else {
+ } else if (mRepeatKeyIndex == NOT_A_KEY) {
resetMultiTap();
mLastKey = mCurrentKey;
mLastCodeX = mLastX;
@@ -1083,10 +1131,6 @@ public class KeyboardView extends View implements View.OnClickListener {
mCurrentKeyTime = 0;
}
}
- if (keyIndex != mRepeatKeyIndex) {
- mHandler.removeMessages(MSG_REPEAT);
- mRepeatKeyIndex = NOT_A_KEY;
- }
}
if (!continueLongPress) {
// Cancel old longpress
@@ -1097,7 +1141,7 @@ public class KeyboardView extends View implements View.OnClickListener {
mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
}
}
- showPreview(keyIndex);
+ showPreview(mCurrentKey);
break;
case MotionEvent.ACTION_UP:
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1429bc1..a127df0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -18,6 +18,7 @@ package android.net;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.os.Binder;
import android.os.RemoteException;
/**
@@ -114,15 +115,64 @@ public class ConnectivityManager
public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
- public static final int TYPE_MOBILE = 0;
- public static final int TYPE_WIFI = 1;
+ /**
+ * The Default Mobile data connection. When active, all data traffic
+ * will use this connection by default. Should not coexist with other
+ * default connections.
+ */
+ public static final int TYPE_MOBILE = 0;
+ /**
+ * The Default WIFI data connection. When active, all data traffic
+ * will use this connection by default. Should not coexist with other
+ * default connections.
+ */
+ public static final int TYPE_WIFI = 1;
+ /**
+ * An MMS-specific Mobile data connection. This connection may be the
+ * same as {@link #TYPEMOBILE} but it may be different. This is used
+ * by applications needing to talk to the carrier's Multimedia Messaging
+ * Service servers. It may coexist with default data connections.
+ * {@hide}
+ */
+ public static final int TYPE_MOBILE_MMS = 2;
+ /**
+ * A SUPL-specific Mobile data connection. This connection may be the
+ * same as {@link #TYPEMOBILE} but it may be different. This is used
+ * by applications needing to talk to the carrier's Secure User Plane
+ * Location servers for help locating the device. It may coexist with
+ * default data connections.
+ * {@hide}
+ */
+ public static final int TYPE_MOBILE_SUPL = 3;
+ /**
+ * A DUN-specific Mobile data connection. This connection may be the
+ * same as {@link #TYPEMOBILE} but it may be different. This is used
+ * by applicaitons performing a Dial Up Networking bridge so that
+ * the carrier is aware of DUN traffic. It may coexist with default data
+ * connections.
+ * {@hide}
+ */
+ public static final int TYPE_MOBILE_DUN = 4;
+ /**
+ * A High Priority Mobile data connection. This connection is typically
+ * the same as {@link #TYPEMOBILE} but the routing setup is different.
+ * Only requesting processes will have access to the Mobile DNS servers
+ * and only IP's explicitly requested via {@link #requestRouteToHost}
+ * will route over this interface.
+ *{@hide}
+ */
+ public static final int TYPE_MOBILE_HIPRI = 5;
+ /** {@hide} */
+ public static final int MAX_RADIO_TYPE = TYPE_WIFI;
+ /** {@hide} */
+ public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_HIPRI;
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
private IConnectivityManager mService;
static public boolean isNetworkTypeValid(int networkType) {
- return networkType == TYPE_WIFI || networkType == TYPE_MOBILE;
+ return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
}
public void setNetworkPreference(int preference) {
@@ -195,7 +245,8 @@ public class ConnectivityManager
*/
public int startUsingNetworkFeature(int networkType, String feature) {
try {
- return mService.startUsingNetworkFeature(networkType, feature);
+ return mService.startUsingNetworkFeature(networkType, feature,
+ new Binder());
} catch (RemoteException e) {
return -1;
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index de68598..9f59cce 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.NetworkInfo;
+import android.os.IBinder;
/**
* Interface that answers queries about, and allows changing, the
@@ -39,7 +40,8 @@ interface IConnectivityManager
boolean setRadio(int networkType, boolean turnOn);
- int startUsingNetworkFeature(int networkType, in String feature);
+ int startUsingNetworkFeature(int networkType, in String feature,
+ in IBinder binder);
int stopUsingNetworkFeature(int networkType, in String feature);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 1064fb6..f88fcdc 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -32,9 +32,6 @@ import android.telephony.TelephonyManager;
import android.util.Log;
import android.text.TextUtils;
-import java.util.List;
-import java.util.ArrayList;
-
/**
* Track the state of mobile data connectivity. This is done by
* receiving broadcast intents from the Phone process whenever
@@ -45,36 +42,48 @@ import java.util.ArrayList;
public class MobileDataStateTracker extends NetworkStateTracker {
private static final String TAG = "MobileDataStateTracker";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private Phone.DataState mMobileDataState;
private ITelephony mPhoneService;
- private static final String[] sDnsPropNames = {
- "net.rmnet0.dns1",
- "net.rmnet0.dns2",
- "net.eth0.dns1",
- "net.eth0.dns2",
- "net.eth0.dns3",
- "net.eth0.dns4",
- "net.gprs.dns1",
- "net.gprs.dns2"
- };
- private List<String> mDnsServers;
- private String mInterfaceName;
- private int mDefaultGatewayAddr;
- private int mLastCallingPid = -1;
+
+ private String mApnType;
+ private boolean mEnabled;
+ private BroadcastReceiver mStateReceiver;
/**
* Create a new MobileDataStateTracker
* @param context the application context of the caller
* @param target a message handler for getting callbacks about state changes
+ * @param netType the ConnectivityManager network type
+ * @param apnType the Phone apnType
+ * @param tag the name of this network
*/
- public MobileDataStateTracker(Context context, Handler target) {
- super(context, target, ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.getDefault().getNetworkType(), "MOBILE",
- TelephonyManager.getDefault().getNetworkTypeName());
+ public MobileDataStateTracker(Context context, Handler target,
+ int netType, String apnType, String tag) {
+ super(context, target, netType,
+ TelephonyManager.getDefault().getNetworkType(), tag,
+ TelephonyManager.getDefault().getNetworkTypeName());
+ mApnType = apnType;
mPhoneService = null;
- mDnsServers = new ArrayList<String>();
+ if(netType == ConnectivityManager.TYPE_MOBILE) {
+ mEnabled = true;
+ } else {
+ mEnabled = false;
+ }
+
+ mDnsPropNames = new String[] {
+ "net.rmnet0.dns1",
+ "net.rmnet0.dns2",
+ "net.eth0.dns1",
+ "net.eth0.dns2",
+ "net.eth0.dns3",
+ "net.eth0.dns4",
+ "net.gprs.dns1",
+ "net.gprs.dns2",
+ "net.ppp0.dns1",
+ "net.ppp0.dns2"};
+
}
/**
@@ -86,105 +95,128 @@ public class MobileDataStateTracker extends NetworkStateTracker {
filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
- Intent intent = mContext.registerReceiver(new MobileDataStateReceiver(), filter);
+ mStateReceiver = new MobileDataStateReceiver();
+ Intent intent = mContext.registerReceiver(mStateReceiver, filter);
if (intent != null)
mMobileDataState = getMobileDataState(intent);
else
mMobileDataState = Phone.DataState.DISCONNECTED;
}
- private static Phone.DataState getMobileDataState(Intent intent) {
+ private Phone.DataState getMobileDataState(Intent intent) {
String str = intent.getStringExtra(Phone.STATE_KEY);
- if (str != null)
- return Enum.valueOf(Phone.DataState.class, str);
- else
- return Phone.DataState.DISCONNECTED;
- }
-
- private class MobileDataStateReceiver extends BroadcastReceiver {
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
- Phone.DataState state = getMobileDataState(intent);
- String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
- String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
- boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false);
- if (DBG) Log.d(TAG, "Received " + intent.getAction() +
- " broadcast - state = " + state
- + ", unavailable = " + unavailable
- + ", reason = " + (reason == null ? "(unspecified)" : reason));
- mNetworkInfo.setIsAvailable(!unavailable);
- if (mMobileDataState != state) {
- mMobileDataState = state;
-
- switch (state) {
- case DISCONNECTED:
- setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
- if (mInterfaceName != null) {
- NetworkUtils.resetConnections(mInterfaceName);
- }
- mInterfaceName = null;
- mDefaultGatewayAddr = 0;
- break;
- case CONNECTING:
- setDetailedState(DetailedState.CONNECTING, reason, apnName);
- break;
- case SUSPENDED:
- setDetailedState(DetailedState.SUSPENDED, reason, apnName);
- break;
- case CONNECTED:
- mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
- if (mInterfaceName == null) {
- Log.d(TAG, "CONNECTED event did not supply interface name.");
- }
- setupDnsProperties();
- setDetailedState(DetailedState.CONNECTED, reason, apnName);
- break;
- }
- }
- } else if (intent.getAction().equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
- String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
- String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
- if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" +
- reason == null ? "" : "(" + reason + ")");
- setDetailedState(DetailedState.FAILED, reason, apnName);
+ if (str != null) {
+ String apnTypeList =
+ intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
+ if (isApnTypeIncluded(apnTypeList)) {
+ return Enum.valueOf(Phone.DataState.class, str);
}
- TelephonyManager tm = TelephonyManager.getDefault();
- setRoamingStatus(tm.isNetworkRoaming());
- setSubtype(tm.getNetworkType(), tm.getNetworkTypeName());
}
+ return Phone.DataState.DISCONNECTED;
}
- /**
- * Make sure that route(s) exist to the carrier DNS server(s).
- */
- public void addPrivateRoutes() {
- if (mInterfaceName != null) {
- for (String addrString : mDnsServers) {
- int addr = NetworkUtils.lookupHost(addrString);
- if (addr != -1) {
- NetworkUtils.addHostRoute(mInterfaceName, addr);
- }
- }
- }
- }
+ private boolean isApnTypeIncluded(String typeList) {
+ /* comma seperated list - split and check */
+ if (typeList == null)
+ return false;
- public void removePrivateRoutes() {
- if(mInterfaceName != null) {
- NetworkUtils.removeHostRoutes(mInterfaceName);
+ String[] list = typeList.split(",");
+ for(int i=0; i< list.length; i++) {
+ if (TextUtils.equals(list[i], mApnType) ||
+ TextUtils.equals(list[i], Phone.APN_TYPE_ALL)) {
+ return true;
+ }
}
+ return false;
}
- public void removeDefaultRoute() {
- if(mInterfaceName != null) {
- mDefaultGatewayAddr = NetworkUtils.getDefaultRoute(mInterfaceName);
- NetworkUtils.removeDefaultRoute(mInterfaceName);
- }
- }
+ private class MobileDataStateReceiver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ synchronized(this) {
+ if (intent.getAction().equals(TelephonyIntents.
+ ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
+ Phone.DataState state = getMobileDataState(intent);
+ String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
+ String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
+ String apnTypeList = intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
+
+ boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
+ false);
+ if (DBG) Log.d(TAG, mApnType + " Received " + intent.getAction() +
+ " broadcast - state = " + state + ", oldstate = " + mMobileDataState +
+ ", unavailable = " + unavailable + ", reason = " +
+ (reason == null ? "(unspecified)" : reason));
+
+
+ if (isApnTypeIncluded(apnTypeList)) {
+ // set this even if the apn isn't Enabled
+ mNetworkInfo.setIsAvailable(!unavailable);
+ if (mEnabled == false) {
+ // if we're not enabled but the APN Type is supported by this connection
+ // we should record the interface name if one's provided. If the user
+ // turns on this network we will need the interfacename but won't get
+ // a fresh connected message - TODO fix this..
+ if (state == Phone.DataState.CONNECTED) {
+ if (DBG) Log.d(TAG, "replacing old mInterfaceName (" +
+ mInterfaceName + ") with " +
+ intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY) +
+ " for " + mApnType);
+ mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
+ }
+ if (DBG) Log.d(TAG, " dropped - mEnabled = false");
+ return;
+ }
+ } else {
+ if (DBG) Log.d(TAG, " dropped - wrong Apn");
+ return;
+ }
- public void restoreDefaultRoute() {
- // 0 is not a valid address for a gateway
- if (mInterfaceName != null && mDefaultGatewayAddr != 0) {
- NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
+ if (mMobileDataState != state) {
+ mMobileDataState = state;
+ switch (state) {
+ case DISCONNECTED:
+ if(isTeardownRequested()) {
+ mEnabled = false;
+ setTeardownRequested(false);
+ }
+
+ setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
+ if (mInterfaceName != null) {
+ NetworkUtils.resetConnections(mInterfaceName);
+ }
+ if (DBG) Log.d(TAG, "clearing mInterfaceName for "+ mApnType +
+ " as it DISCONNECTED");
+ mInterfaceName = null;
+ mDefaultGatewayAddr = 0;
+ break;
+ case CONNECTING:
+ setDetailedState(DetailedState.CONNECTING, reason, apnName);
+ break;
+ case SUSPENDED:
+ setDetailedState(DetailedState.SUSPENDED, reason, apnName);
+ break;
+ case CONNECTED:
+ mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
+ if (mInterfaceName == null) {
+ Log.d(TAG, "CONNECTED event did not supply interface name.");
+ }
+ setDetailedState(DetailedState.CONNECTED, reason, apnName);
+ break;
+ }
+ }
+ } else if (intent.getAction().
+ equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
+ mEnabled = false;
+ String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
+ String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
+ if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" +
+ reason == null ? "" : "(" + reason + ")");
+ setDetailedState(DetailedState.FAILED, reason, apnName);
+ }
+ TelephonyManager tm = TelephonyManager.getDefault();
+ setRoamingStatus(tm.isNetworkRoaming());
+ setSubtype(tm.getNetworkType(), tm.getNetworkTypeName());
+ }
}
}
@@ -219,15 +251,6 @@ public class MobileDataStateTracker extends NetworkStateTracker {
}
/**
- * Return the IP addresses of the DNS servers available for the mobile data
- * network interface.
- * @return a list of DNS addresses, with no holes.
- */
- public String[] getNameServers() {
- return getNameServerList(sDnsPropNames);
- }
-
- /**
* {@inheritDoc}
* The mobile data network subtype indicates what generation network technology is in effect,
* e.g., GPRS, EDGE, UMTS, etc.
@@ -254,9 +277,21 @@ public class MobileDataStateTracker extends NetworkStateTracker {
case TelephonyManager.NETWORK_TYPE_UMTS:
networkTypeStr = "umts";
break;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ networkTypeStr = "hsdpa";
+ break;
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ networkTypeStr = "hsupa";
+ break;
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ networkTypeStr = "hspa";
+ break;
case TelephonyManager.NETWORK_TYPE_CDMA:
networkTypeStr = "cdma";
break;
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ networkTypeStr = "1xrtt";
+ break;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
networkTypeStr = "evdo";
break;
@@ -273,54 +308,51 @@ public class MobileDataStateTracker extends NetworkStateTracker {
*/
@Override
public boolean teardown() {
- getPhoneService(false);
- /*
- * If the phone process has crashed in the past, we'll get a
- * RemoteException and need to re-reference the service.
- */
- for (int retry = 0; retry < 2; retry++) {
- if (mPhoneService == null) {
- Log.w(TAG,
- "Ignoring mobile data teardown request because could not acquire PhoneService");
- break;
- }
-
- try {
- return mPhoneService.disableDataConnectivity();
- } catch (RemoteException e) {
- if (retry == 0) getPhoneService(true);
- }
- }
-
- Log.w(TAG, "Failed to tear down mobile data connectivity");
- return false;
+ setTeardownRequested(true);
+ return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED);
}
/**
* Re-enable mobile data connectivity after a {@link #teardown()}.
*/
public boolean reconnect() {
- getPhoneService(false);
- /*
- * If the phone process has crashed in the past, we'll get a
- * RemoteException and need to re-reference the service.
- */
- for (int retry = 0; retry < 2; retry++) {
- if (mPhoneService == null) {
- Log.w(TAG,
- "Ignoring mobile data connect request because could not acquire PhoneService");
+ setTeardownRequested(false);
+ switch (setEnableApn(mApnType, true)) {
+ case Phone.APN_ALREADY_ACTIVE:
+ mEnabled = true;
+ // need to set self to CONNECTING so the below message is handled.
+ mMobileDataState = Phone.DataState.CONNECTING;
+ setDetailedState(DetailedState.CONNECTING, Phone.REASON_APN_CHANGED, null);
+ //send out a connected message
+ Intent intent = new Intent(TelephonyIntents.
+ ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+ intent.putExtra(Phone.STATE_KEY, Phone.DataState.CONNECTED.toString());
+ intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, Phone.REASON_APN_CHANGED);
+ intent.putExtra(Phone.DATA_APN_TYPES_KEY, mApnType);
+ intent.putExtra(Phone.DATA_IFACE_NAME_KEY, mInterfaceName);
+ intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, false);
+ if (mStateReceiver != null) mStateReceiver.onReceive(mContext, intent);
+ break;
+ case Phone.APN_REQUEST_STARTED:
+ mEnabled = true;
+ // no need to do anything - we're already due some status update intents
+ break;
+ case Phone.APN_REQUEST_FAILED:
+ if (mPhoneService == null && mApnType == Phone.APN_TYPE_DEFAULT) {
+ // on startup we may try to talk to the phone before it's ready
+ // just leave mEnabled as it is for the default apn.
+ return false;
+ }
+ // else fall through
+ case Phone.APN_TYPE_NOT_AVAILABLE:
+ mEnabled = false;
+ break;
+ default:
+ Log.e(TAG, "Error in reconnect - unexpected response.");
+ mEnabled = false;
break;
- }
-
- try {
- return mPhoneService.enableDataConnectivity();
- } catch (RemoteException e) {
- if (retry == 0) getPhoneService(true);
- }
}
-
- Log.w(TAG, "Failed to set up mobile data connectivity");
- return false;
+ return mEnabled;
}
/**
@@ -374,14 +406,7 @@ public class MobileDataStateTracker extends NetworkStateTracker {
* </ul>
*/
public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
- mLastCallingPid = callingPid;
- return setEnableApn(Phone.APN_TYPE_MMS, true);
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
- return setEnableApn(Phone.APN_TYPE_SUPL, true);
- } else {
- return -1;
- }
+ return -1;
}
/**
@@ -397,13 +422,7 @@ public class MobileDataStateTracker extends NetworkStateTracker {
* the value {@code -1} always indicates failure.
*/
public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
- if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
- return setEnableApn(Phone.APN_TYPE_MMS, false);
- } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
- return setEnableApn(Phone.APN_TYPE_SUPL, false);
- } else {
- return -1;
- }
+ return -1;
}
/**
@@ -415,10 +434,11 @@ public class MobileDataStateTracker extends NetworkStateTracker {
*/
@Override
public boolean requestRouteToHost(int hostAddress) {
+ if (DBG) {
+ Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress) +
+ " for " + mApnType + "(" + mInterfaceName + ")");
+ }
if (mInterfaceName != null && hostAddress != -1) {
- if (DBG) {
- Log.d(TAG, "Requested host route to " + Integer.toHexString(hostAddress));
- }
return NetworkUtils.addHostRoute(mInterfaceName, hostAddress) == 0;
} else {
return false;
@@ -433,43 +453,6 @@ public class MobileDataStateTracker extends NetworkStateTracker {
return sb.toString();
}
- private void setupDnsProperties() {
- mDnsServers.clear();
- // Set up per-process DNS server list on behalf of the MMS process
- int i = 1;
- if (mInterfaceName != null) {
- for (String propName : sDnsPropNames) {
- if (propName.indexOf(mInterfaceName) != -1) {
- String propVal = SystemProperties.get(propName);
- if (propVal != null && propVal.length() != 0 && !propVal.equals("0.0.0.0")) {
- mDnsServers.add(propVal);
- if (mLastCallingPid != -1) {
- SystemProperties.set("net.dns" + i + "." + mLastCallingPid, propVal);
- }
- ++i;
- }
- }
- }
- }
- if (i == 1) {
- Log.d(TAG, "DNS server addresses are not known.");
- } else if (mLastCallingPid != -1) {
- /*
- * Bump the property that tells the name resolver library
- * to reread the DNS server list from the properties.
- */
- String propVal = SystemProperties.get("net.dnschange");
- if (propVal.length() != 0) {
- try {
- int n = Integer.parseInt(propVal);
- SystemProperties.set("net.dnschange", "" + (n+1));
- } catch (NumberFormatException e) {
- }
- }
- }
- mLastCallingPid = -1;
- }
-
/**
* Internal method supporting the ENABLE_MMS feature.
* @param apnType the type of APN to be enabled or disabled (e.g., mms)
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 37087ac..54529ae 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -27,6 +27,7 @@ import android.text.TextUtils;
import android.util.Config;
import android.util.Log;
+
/**
* Each subclass of this class keeps track of the state of connectivity
* of a network interface. All state information for a network should
@@ -40,11 +41,16 @@ public abstract class NetworkStateTracker extends Handler {
protected NetworkInfo mNetworkInfo;
protected Context mContext;
protected Handler mTarget;
+ protected String mInterfaceName;
+ protected String[] mDnsPropNames;
+ private boolean mPrivateDnsRouteSet;
+ protected int mDefaultGatewayAddr;
+ private boolean mDefaultRouteSet;
private boolean mTeardownRequested;
- private static boolean DBG = Config.LOGV;
+ private static boolean DBG = true;
private static final String TAG = "NetworkStateTracker";
-
+
public static final int EVENT_STATE_CHANGED = 1;
public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2;
/**
@@ -56,6 +62,7 @@ public abstract class NetworkStateTracker extends Handler {
public static final int EVENT_CONFIGURATION_CHANGED = 4;
public static final int EVENT_ROAMING_CHANGED = 5;
public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6;
+ public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7;
public NetworkStateTracker(Context context,
Handler target,
@@ -67,6 +74,7 @@ public abstract class NetworkStateTracker extends Handler {
mContext = context;
mTarget = target;
mTeardownRequested = false;
+
this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName);
}
@@ -75,19 +83,21 @@ public abstract class NetworkStateTracker extends Handler {
}
/**
- * Return the list of DNS servers associated with this network.
- * @return a list of the IP addresses of the DNS servers available
- * for the network.
- */
- public abstract String[] getNameServers();
-
- /**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
*/
public abstract String getTcpBufferSizesPropName();
/**
+ * Return the IP addresses of the DNS servers available for the mobile data
+ * network interface.
+ * @return a list of DNS addresses, with no holes.
+ */
+ public String[] getNameServers() {
+ return getNameServerList(mDnsPropNames);
+ }
+
+ /**
* Return the IP addresses of the DNS servers available for this
* network interface.
* @param propertyNames the names of the system properties whose values
@@ -112,6 +122,50 @@ public abstract class NetworkStateTracker extends Handler {
return dnsAddresses;
}
+ public void addPrivateDnsRoutes() {
+ if (DBG) Log.d(TAG, "addPrivateDnsRoutes for " + this +
+ "(" + mInterfaceName + ")");
+ if (mInterfaceName != null && !mPrivateDnsRouteSet) {
+ for (String addrString : getNameServers()) {
+ int addr = NetworkUtils.lookupHost(addrString);
+ if (addr != -1) {
+ NetworkUtils.addHostRoute(mInterfaceName, addr);
+ }
+ }
+ mPrivateDnsRouteSet = true;
+ }
+ }
+
+ public void removePrivateDnsRoutes() {
+ if (DBG) Log.d(TAG, "removePrivateDnsRoutes for " + this +
+ "(" + mInterfaceName + ")");
+ // TODO - we should do this explicitly but the NetUtils api doesnt
+ // support this yet - must remove all. No worse than before
+ if (mInterfaceName != null && mPrivateDnsRouteSet) {
+ NetworkUtils.removeHostRoutes(mInterfaceName);
+ mPrivateDnsRouteSet = false;
+ }
+ }
+
+ public void addDefaultRoute() {
+ if (DBG) Log.d(TAG, "addDefaultRoute for " + this + "(" +
+ mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr);
+ if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) &&
+ mDefaultRouteSet == false) {
+ NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr);
+ mDefaultRouteSet = true;
+ }
+ }
+
+ public void removeDefaultRoute() {
+ if (DBG) Log.d(TAG, "removeDefaultRoute for " + this + "(" +
+ mInterfaceName + ")");
+ if (mInterfaceName != null && mDefaultRouteSet == true) {
+ NetworkUtils.removeDefaultRoute(mInterfaceName);
+ mDefaultRouteSet = false;
+ }
+ }
+
/**
* Reads the network specific TCP buffer sizes from SystemProperties
* net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system
@@ -209,6 +263,7 @@ public abstract class NetworkStateTracker extends Handler {
* @param extraInfo optional {@code String} providing extra information about the state change
*/
public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) {
+ if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state);
if (state != mNetworkInfo.getDetailedState()) {
boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
String lastReason = mNetworkInfo.getReason();
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 1153648..a3ae01b 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -25,6 +25,9 @@ import java.net.UnknownHostException;
* {@hide}
*/
public class NetworkUtils {
+ /** Bring the named network interface up. */
+ public native static int enableInterface(String interfaceName);
+
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 421d013..298be3b 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -374,7 +374,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
/**
* Creates a Uri which parses the given encoded URI string.
*
- * @param uriString an RFC 3296-compliant, encoded URI
+ * @param uriString an RFC 2396-compliant, encoded URI
* @throws NullPointerException if uriString is null
* @return Uri for this given uri string
*/
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index f4a2a6a..f6159de 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -54,7 +54,7 @@ public class WebAddress {
static Pattern sAddressPattern = Pattern.compile(
/* scheme */ "(?:(http|HTTP|https|HTTPS|file|FILE)\\:\\/\\/)?" +
/* authority */ "(?:([-A-Za-z0-9$_.+!*'(),;?&=]+(?:\\:[-A-Za-z0-9$_.+!*'(),;?&=]+)?)@)?" +
- /* host */ "([-A-Za-z0-9%]+(?:\\.[-A-Za-z0-9%]+)*)?" +
+ /* host */ "([-A-Za-z0-9%_]+(?:\\.[-A-Za-z0-9%_]+)*)?" +
/* port */ "(?:\\:([0-9]+))?" +
/* path */ "(\\/?.*)?");
diff --git a/core/java/android/net/http/ConnectionThread.java b/core/java/android/net/http/ConnectionThread.java
index 8e759e2..0b30e58 100644
--- a/core/java/android/net/http/ConnectionThread.java
+++ b/core/java/android/net/http/ConnectionThread.java
@@ -32,8 +32,8 @@ class ConnectionThread extends Thread {
static final int WAIT_TICK = 1000;
// Performance probe
- long mStartThreadTime;
long mCurrentThreadTime;
+ long mTotalThreadTime;
private boolean mWaiting;
private volatile boolean mRunning = true;
@@ -69,12 +69,21 @@ class ConnectionThread extends Thread {
*/
public void run() {
android.os.Process.setThreadPriority(
+ android.os.Process.THREAD_PRIORITY_DEFAULT +
android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
- mStartThreadTime = -1;
- mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
+ // these are used to get performance data. When it is not in the timing,
+ // mCurrentThreadTime is 0. When it starts timing, mCurrentThreadTime is
+ // first set to -1, it will be set to the current thread time when the
+ // next request starts.
+ mCurrentThreadTime = 0;
+ mTotalThreadTime = 0;
while (mRunning) {
+ if (mCurrentThreadTime == -1) {
+ mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
+ }
+
Request request;
/* Get a request to process */
@@ -86,14 +95,14 @@ class ConnectionThread extends Thread {
if (HttpLog.LOGV) HttpLog.v("ConnectionThread: Waiting for work");
mWaiting = true;
try {
- if (mStartThreadTime != -1) {
- mCurrentThreadTime = SystemClock
- .currentThreadTimeMillis();
- }
mRequestFeeder.wait();
} catch (InterruptedException e) {
}
mWaiting = false;
+ if (mCurrentThreadTime != 0) {
+ mCurrentThreadTime = SystemClock
+ .currentThreadTimeMillis();
+ }
}
} else {
if (HttpLog.LOGV) HttpLog.v("ConnectionThread: new request " +
@@ -123,6 +132,12 @@ class ConnectionThread extends Thread {
mConnection.closeConnection();
}
mConnection = null;
+
+ if (mCurrentThreadTime > 0) {
+ long start = mCurrentThreadTime;
+ mCurrentThreadTime = SystemClock.currentThreadTimeMillis();
+ mTotalThreadTime += mCurrentThreadTime - start;
+ }
}
}
diff --git a/core/java/android/net/http/Request.java b/core/java/android/net/http/Request.java
index df4fff0..1b6568e 100644
--- a/core/java/android/net/http/Request.java
+++ b/core/java/android/net/http/Request.java
@@ -67,9 +67,6 @@ class Request {
/** Set if I'm using a proxy server */
HttpHost mProxyHost;
- /** True if request is .html, .js, .css */
- boolean mHighPriority;
-
/** True if request has been cancelled */
volatile boolean mCancelled = false;
@@ -102,26 +99,29 @@ class Request {
* @param eventHandler request will make progress callbacks on
* this interface
* @param headers reqeust headers
- * @param highPriority true for .html, css, .cs
*/
Request(String method, HttpHost host, HttpHost proxyHost, String path,
InputStream bodyProvider, int bodyLength,
EventHandler eventHandler,
- Map<String, String> headers, boolean highPriority) {
+ Map<String, String> headers) {
mEventHandler = eventHandler;
mHost = host;
mProxyHost = proxyHost;
mPath = path;
- mHighPriority = highPriority;
mBodyProvider = bodyProvider;
mBodyLength = bodyLength;
- if (bodyProvider == null) {
+ if (bodyProvider == null && !"POST".equalsIgnoreCase(method)) {
mHttpRequest = new BasicHttpRequest(method, getUri());
} else {
mHttpRequest = new BasicHttpEntityEnclosingRequest(
method, getUri());
- setBodyProvider(bodyProvider, bodyLength);
+ // it is ok to have null entity for BasicHttpEntityEnclosingRequest.
+ // By using BasicHttpEntityEnclosingRequest, it will set up the
+ // correct content-length, content-type and content-encoding.
+ if (bodyProvider != null) {
+ setBodyProvider(bodyProvider, bodyLength);
+ }
}
addHeader(HOST_HEADER, getHostPort());
@@ -255,6 +255,8 @@ class Request {
// process gzip content encoding
Header contentEncoding = entity.getContentEncoding();
InputStream nis = null;
+ byte[] buf = null;
+ int count = 0;
try {
if (contentEncoding != null &&
contentEncoding.getValue().equals("gzip")) {
@@ -265,9 +267,8 @@ class Request {
/* accumulate enough data to make it worth pushing it
* up the stack */
- byte[] buf = mConnection.getBuf();
+ buf = mConnection.getBuf();
int len = 0;
- int count = 0;
int lowWater = buf.length / 2;
while (len != -1) {
len = nis.read(buf, count, buf.length - count);
@@ -284,6 +285,10 @@ class Request {
/* InflaterInputStream throws an EOFException when the
server truncates gzipped content. Handle this case
as we do truncated non-gzipped content: no error */
+ if (count > 0) {
+ // if there is uncommited content, we should commit them
+ mEventHandler.data(buf, count);
+ }
if (HttpLog.LOGV) HttpLog.v( "readResponse() handling " + e);
} catch(IOException e) {
// don't throw if we have a non-OK status code
@@ -346,7 +351,7 @@ class Request {
* for debugging
*/
public String toString() {
- return (mHighPriority ? "P*" : "") + mPath;
+ return mPath;
}
@@ -412,8 +417,7 @@ class Request {
}
return status >= HttpStatus.SC_OK
&& status != HttpStatus.SC_NO_CONTENT
- && status != HttpStatus.SC_NOT_MODIFIED
- && status != HttpStatus.SC_RESET_CONTENT;
+ && status != HttpStatus.SC_NOT_MODIFIED;
}
/**
diff --git a/core/java/android/net/http/RequestHandle.java b/core/java/android/net/http/RequestHandle.java
index 6a97951..190ae7a 100644
--- a/core/java/android/net/http/RequestHandle.java
+++ b/core/java/android/net/http/RequestHandle.java
@@ -419,6 +419,6 @@ public class RequestHandle {
mRequest = mRequestQueue.queueRequest(
mUrl, mUri, mMethod, mHeaders, mRequest.mEventHandler,
mBodyProvider,
- mBodyLength, mRequest.mHighPriority).mRequest;
+ mBodyLength).mRequest;
}
}
diff --git a/core/java/android/net/http/RequestQueue.java b/core/java/android/net/http/RequestQueue.java
index 66d5722..e14af66 100644
--- a/core/java/android/net/http/RequestQueue.java
+++ b/core/java/android/net/http/RequestQueue.java
@@ -57,9 +57,6 @@ public class RequestQueue implements RequestFeeder {
*/
private LinkedHashMap<HttpHost, LinkedList<Request>> mPending;
- /* Support for notifying a client when queue is empty */
- private boolean mClientWaiting = false;
-
/** true if connected */
boolean mNetworkConnected = true;
@@ -245,7 +242,9 @@ public class RequestQueue implements RequestFeeder {
public void startTiming() {
for (int i = 0; i < mConnectionCount; i++) {
- mThreads[i].mStartThreadTime = mThreads[i].mCurrentThreadTime;
+ ConnectionThread rt = mThreads[i];
+ rt.mCurrentThreadTime = -1;
+ rt.mTotalThreadTime = 0;
}
mTotalRequest = 0;
mTotalConnection = 0;
@@ -255,12 +254,14 @@ public class RequestQueue implements RequestFeeder {
int totalTime = 0;
for (int i = 0; i < mConnectionCount; i++) {
ConnectionThread rt = mThreads[i];
- totalTime += (rt.mCurrentThreadTime - rt.mStartThreadTime);
- rt.mStartThreadTime = -1;
+ if (rt.mCurrentThreadTime != -1) {
+ totalTime += rt.mTotalThreadTime;
+ }
+ rt.mCurrentThreadTime = 0;
}
Log.d("Http", "Http thread used " + totalTime + " ms " + " for "
+ mTotalRequest + " requests and " + mTotalConnection
- + " connections");
+ + " new connections");
}
void logState() {
@@ -434,16 +435,14 @@ public class RequestQueue implements RequestFeeder {
* data. Callbacks will be made on the supplied instance.
* @param bodyProvider InputStream providing HTTP body, null if none
* @param bodyLength length of body, must be 0 if bodyProvider is null
- * @param highPriority If true, queues before low priority
- * requests if possible
*/
public RequestHandle queueRequest(
String url, String method,
Map<String, String> headers, EventHandler eventHandler,
- InputStream bodyProvider, int bodyLength, boolean highPriority) {
+ InputStream bodyProvider, int bodyLength) {
WebAddress uri = new WebAddress(url);
return queueRequest(url, uri, method, headers, eventHandler,
- bodyProvider, bodyLength, highPriority);
+ bodyProvider, bodyLength);
}
/**
@@ -456,14 +455,11 @@ public class RequestQueue implements RequestFeeder {
* data. Callbacks will be made on the supplied instance.
* @param bodyProvider InputStream providing HTTP body, null if none
* @param bodyLength length of body, must be 0 if bodyProvider is null
- * @param highPriority If true, queues before low priority
- * requests if possible
*/
public RequestHandle queueRequest(
String url, WebAddress uri, String method, Map<String, String> headers,
EventHandler eventHandler,
- InputStream bodyProvider, int bodyLength,
- boolean highPriority) {
+ InputStream bodyProvider, int bodyLength) {
if (HttpLog.LOGV) HttpLog.v("RequestQueue.queueRequest " + uri);
@@ -478,9 +474,9 @@ public class RequestQueue implements RequestFeeder {
// set up request
req = new Request(method, httpHost, mProxyHost, uri.mPath, bodyProvider,
- bodyLength, eventHandler, headers, highPriority);
+ bodyLength, eventHandler, headers);
- queueRequest(req, highPriority);
+ queueRequest(req, false);
mActivePool.mTotalRequest++;
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1775a4b..6c2a27a 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -70,6 +70,7 @@ public class Build {
*
* @deprecated Use {@link #SDK_INT} to easily get this as an integer.
*/
+ @Deprecated
public static final String SDK = getString("ro.build.version.sdk");
/**
@@ -132,6 +133,19 @@ public class Build {
* </ul>
*/
public static final int DONUT = 4;
+ /**
+ * Current work on "Eclair" development branch.
+ *
+ * <p>Applications targeting this or a later release will get these
+ * new changes in behavior:</p>
+ * <ul>
+ * <li> The {@link android.app.Service#onStartCommand
+ * Service.onStartCommand} function will return the new
+ * {@link android.app.Service#START_STICKY} behavior instead of the
+ * old compatibility {@link android.app.Service#START_STICKY_COMPATIBILITY}.
+ * </ul>
+ */
+ public static final int ECLAIR = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index d40ea6b..5352cf6 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -104,7 +104,7 @@ public final class Debug
* This class is used to retrieved various statistics about the memory mappings for this
* process. The returns info broken down by dalvik, native, and other. All results are in kB.
*/
- public static class MemoryInfo {
+ public static class MemoryInfo implements Parcelable {
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
@@ -125,6 +125,50 @@ public final class Debug
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
+
+ public MemoryInfo() {
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(dalvikPss);
+ dest.writeInt(dalvikPrivateDirty);
+ dest.writeInt(dalvikSharedDirty);
+ dest.writeInt(nativePss);
+ dest.writeInt(nativePrivateDirty);
+ dest.writeInt(nativeSharedDirty);
+ dest.writeInt(otherPss);
+ dest.writeInt(otherPrivateDirty);
+ dest.writeInt(otherSharedDirty);
+ }
+
+ public void readFromParcel(Parcel source) {
+ dalvikPss = source.readInt();
+ dalvikPrivateDirty = source.readInt();
+ dalvikSharedDirty = source.readInt();
+ nativePss = source.readInt();
+ nativePrivateDirty = source.readInt();
+ nativeSharedDirty = source.readInt();
+ otherPss = source.readInt();
+ otherPrivateDirty = source.readInt();
+ otherSharedDirty = source.readInt();
+ }
+
+ public static final Creator<MemoryInfo> CREATOR = new Creator<MemoryInfo>() {
+ public MemoryInfo createFromParcel(Parcel source) {
+ return new MemoryInfo(source);
+ }
+ public MemoryInfo[] newArray(int size) {
+ return new MemoryInfo[size];
+ }
+ };
+
+ private MemoryInfo(Parcel source) {
+ readFromParcel(source);
+ }
}
@@ -556,6 +600,13 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
public static native void getMemoryInfo(MemoryInfo memoryInfo);
/**
+ * Note: currently only works when the requested pid has the same UID
+ * as the caller.
+ * @hide
+ */
+ public static native void getMemoryInfo(int pid, MemoryInfo memoryInfo);
+
+ /**
* Establish an object allocation limit in the current thread. Useful
* for catching regressions in code that is expected to operate
* without causing any allocations.
@@ -662,6 +713,25 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
public static final native int getBinderDeathObjectCount();
/**
+ * Primes the register map cache.
+ *
+ * Only works for classes in the bootstrap class loader. Does not
+ * cause classes to be loaded if they're not already present.
+ *
+ * The classAndMethodDesc argument is a concatentation of the VM-internal
+ * class descriptor, method name, and method descriptor. Examples:
+ * Landroid/os/Looper;.loop:()V
+ * Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V
+ *
+ * @param classAndMethodDesc the method to prepare
+ *
+ * @hide
+ */
+ public static final boolean cacheRegisterMap(String classAndMethodDesc) {
+ return VMDebug.cacheRegisterMap(classAndMethodDesc);
+ }
+
+ /**
* API for gathering and querying instruction counts.
*
* Example usage:
diff --git a/core/java/android/os/Exec.java b/core/java/android/os/Exec.java
deleted file mode 100644
index a50d5fe..0000000
--- a/core/java/android/os/Exec.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import java.io.FileDescriptor;
-
-/**
- * @hide
- * Tools for executing commands. Not for public consumption.
- */
-
-public class Exec
-{
- /**
- * @param cmd The command to execute
- * @param arg0 The first argument to the command, may be null
- * @param arg1 the second argument to the command, may be null
- * @return the file descriptor of the started process.
- *
- */
- public static FileDescriptor createSubprocess(
- String cmd, String arg0, String arg1) {
- return createSubprocess(cmd, arg0, arg1, null);
- }
-
- /**
- * @param cmd The command to execute
- * @param arg0 The first argument to the command, may be null
- * @param arg1 the second argument to the command, may be null
- * @param processId A one-element array to which the process ID of the
- * started process will be written.
- * @return the file descriptor of the started process.
- *
- */
- public static native FileDescriptor createSubprocess(
- String cmd, String arg0, String arg1, int[] processId);
-
- public static native void setPtyWindowSize(FileDescriptor fd,
- int row, int col, int xpixel, int ypixel);
- /**
- * Causes the calling thread to wait for the process associated with the
- * receiver to finish executing.
- *
- * @return The exit value of the Process being waited on
- *
- */
- public static native int waitFor(int processId);
-}
-
diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java
index d9804ea..38d252e 100644
--- a/core/java/android/os/FileObserver.java
+++ b/core/java/android/os/FileObserver.java
@@ -25,22 +25,35 @@ import java.util.ArrayList;
import java.util.HashMap;
public abstract class FileObserver {
- public static final int ACCESS = 0x00000001; /* File was accessed */
- public static final int MODIFY = 0x00000002; /* File was modified */
- public static final int ATTRIB = 0x00000004; /* Metadata changed */
- public static final int CLOSE_WRITE = 0x00000008; /* Writtable file was closed */
- public static final int CLOSE_NOWRITE = 0x00000010; /* Unwrittable file closed */
- public static final int OPEN = 0x00000020; /* File was opened */
- public static final int MOVED_FROM = 0x00000040; /* File was moved from X */
- public static final int MOVED_TO = 0x00000080; /* File was moved to Y */
- public static final int CREATE = 0x00000100; /* Subfile was created */
- public static final int DELETE = 0x00000200; /* Subfile was deleted */
- public static final int DELETE_SELF = 0x00000400; /* Self was deleted */
- public static final int MOVE_SELF = 0x00000800; /* Self was moved */
+ /** File was accessed */
+ public static final int ACCESS = 0x00000001;
+ /** File was modified */
+ public static final int MODIFY = 0x00000002;
+ /** Metadata changed */
+ public static final int ATTRIB = 0x00000004;
+ /** Writable file was closed */
+ public static final int CLOSE_WRITE = 0x00000008;
+ /** Unwrittable file closed */
+ public static final int CLOSE_NOWRITE = 0x00000010;
+ /** File was opened */
+ public static final int OPEN = 0x00000020;
+ /** File was moved from X */
+ public static final int MOVED_FROM = 0x00000040;
+ /** File was moved to Y */
+ public static final int MOVED_TO = 0x00000080;
+ /** Subfile was created */
+ public static final int CREATE = 0x00000100;
+ /** Subfile was deleted */
+ public static final int DELETE = 0x00000200;
+ /** Self was deleted */
+ public static final int DELETE_SELF = 0x00000400;
+ /** Self was moved */
+ public static final int MOVE_SELF = 0x00000800;
+
public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE
| CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE
| DELETE_SELF | MOVE_SELF;
-
+
private static final String LOG_TAG = "FileObserver";
private static class ObserverThread extends Thread {
diff --git a/core/java/android/os/HandlerStateMachine.java b/core/java/android/os/HandlerStateMachine.java
index d004a25..9e7902b 100644
--- a/core/java/android/os/HandlerStateMachine.java
+++ b/core/java/android/os/HandlerStateMachine.java
@@ -56,22 +56,22 @@ import android.util.LogPrinter;
}
class S1 extends HandlerState {
- @Override public void enter(Message message) {
+ &amp;#064;Override public void enter(Message message) {
}
- @Override public void processMessage(Message message) {
+ &amp;#064;Override public void processMessage(Message message) {
deferMessage(message);
if (message.what == TEST_WHAT_2) {
transitionTo(mS2);
}
}
- @Override public void exit(Message message) {
+ &amp;#064;Override public void exit(Message message) {
}
}
class S2 extends HandlerState {
- @Override public void processMessage(Message message) {
+ &amp;#064;Override public void processMessage(Message message) {
// Do some processing
if (message.what == TEST_WHAT_2) {
transtionTo(mS1);
diff --git a/core/java/android/os/IHardwareService.aidl b/core/java/android/os/IHardwareService.aidl
index fb121bb..aebcb3c 100755
--- a/core/java/android/os/IHardwareService.aidl
+++ b/core/java/android/os/IHardwareService.aidl
@@ -20,9 +20,9 @@ package android.os;
interface IHardwareService
{
// Vibrator support
- void vibrate(long milliseconds);
+ void vibrate(long milliseconds, IBinder token);
void vibratePattern(in long[] pattern, int repeat, IBinder token);
- void cancelVibrate();
+ void cancelVibrate(IBinder token);
// flashlight support
boolean getFlashlightEnabled();
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 5486920..188e7ff 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -26,6 +26,7 @@ interface IPowerManager
void userActivity(long when, boolean noChangeLights);
void userActivityWithForce(long when, boolean noChangeLights, boolean force);
void setPokeLock(int pokey, IBinder lock, String tag);
+ int getSupportedWakeLockFlags();
void setStayOnSetting(int val);
long getScreenOnTime();
void preventScreenOn(boolean prevent);
diff --git a/core/java/android/os/LatencyTimer.java b/core/java/android/os/LatencyTimer.java
new file mode 100644
index 0000000..ed2f0f9e
--- /dev/null
+++ b/core/java/android/os/LatencyTimer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * A class to help with measuring latency in your code.
+ *
+ * Suggested usage:
+ * 1) Instanciate a LatencyTimer as a class field.
+ * private [static] LatencyTimer mLt = new LatencyTimer(100, 1000);
+ * 2) At various points in the code call sample with a string and the time delta to some fixed time.
+ * The string should be unique at each point of the code you are measuring.
+ * mLt.sample("before processing event", System.nanoTime() - event.getEventTimeNano());
+ * processEvent(event);
+ * mLt.sample("after processing event ", System.nanoTime() - event.getEventTimeNano());
+ *
+ * @hide
+ */
+public final class LatencyTimer
+{
+ final String TAG = "LatencyTimer";
+ final int mSampleSize;
+ final int mScaleFactor;
+ volatile HashMap<String, long[]> store = new HashMap<String, long[]>();
+
+ /**
+ * Creates a LatencyTimer object
+ * @param sampleSize number of samples to collect before printing out the average
+ * @param scaleFactor divisor used to make each sample smaller to prevent overflow when
+ * (sampleSize * average sample value)/scaleFactor > Long.MAX_VALUE
+ */
+ public LatencyTimer(int sampleSize, int scaleFactor) {
+ if (scaleFactor == 0) {
+ scaleFactor = 1;
+ }
+ mScaleFactor = scaleFactor;
+ mSampleSize = sampleSize;
+ }
+
+ /**
+ * Add a sample delay for averaging.
+ * @param tag string used for printing out the result. This should be unique at each point of
+ * this called.
+ * @param delta time difference from an unique point of reference for a particular iteration
+ */
+ public void sample(String tag, long delta) {
+ long[] array = getArray(tag);
+
+ // array[mSampleSize] holds the number of used entries
+ final int index = (int) array[mSampleSize]++;
+ array[index] = delta;
+ if (array[mSampleSize] == mSampleSize) {
+ long totalDelta = 0;
+ for (long d : array) {
+ totalDelta += d/mScaleFactor;
+ }
+ array[mSampleSize] = 0;
+ Log.i(TAG, tag + " average = " + totalDelta / mSampleSize);
+ }
+ }
+
+ private long[] getArray(String tag) {
+ long[] data = store.get(tag);
+ if (data == null) {
+ synchronized(store) {
+ data = store.get(tag);
+ if (data == null) {
+ data = new long[mSampleSize + 1];
+ store.put(tag, data);
+ data[mSampleSize] = 0;
+ }
+ }
+ }
+ return data;
+ }
+}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index c14925c..03542dd 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -52,7 +52,7 @@ public class MemoryFile
private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
- private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException;
+ private static native int native_get_mapped_size(FileDescriptor fd) throws IOException;
private FileDescriptor mFD; // ashmem file descriptor
private int mAddress; // address of ashmem memory
@@ -300,7 +300,20 @@ public class MemoryFile
* @hide
*/
public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
- return native_is_ashmem_region(fd);
+ return (native_get_mapped_size(fd) >= 0);
+ }
+
+ /**
+ * Returns the size of the memory file, rounded up to a page boundary, that
+ * the file descriptor refers to, or -1 if the file descriptor does not
+ * refer to a memory file.
+ *
+ * @throws IOException If <code>fd</code> is not a valid file descriptor.
+ *
+ * @hide
+ */
+ public static int getMappedSize(FileDescriptor fd) throws IOException {
+ return native_get_mapped_size(fd);
}
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index bfcf2fc..d5934102 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -114,12 +114,14 @@ public class PowerManager
private static final int WAKE_BIT_SCREEN_DIM = 4;
private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
+ private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32;
private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
| WAKE_BIT_CPU_WEAK
| WAKE_BIT_SCREEN_DIM
| WAKE_BIT_SCREEN_BRIGHT
- | WAKE_BIT_KEYBOARD_BRIGHT;
+ | WAKE_BIT_KEYBOARD_BRIGHT
+ | WAKE_BIT_PROXIMITY_SCREEN_OFF;
/**
* Wake lock that ensures that the CPU is running. The screen might
@@ -147,6 +149,16 @@ public class PowerManager
public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
/**
+ * Wake lock that turns the screen off when the proximity sensor activates.
+ * Since not all devices have proximity sensors, use
+ * {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
+ * this wake lock mode is supported.
+ *
+ * {@hide}
+ */
+ public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
+
+ /**
* Normally wake locks don't actually wake the device, they just cause
* it to remain on once it's already on. Think of the video player
* app as the normal behavior. Notifications that pop up and want
@@ -196,6 +208,7 @@ public class PowerManager
case SCREEN_DIM_WAKE_LOCK:
case SCREEN_BRIGHT_WAKE_LOCK:
case FULL_WAKE_LOCK:
+ case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
break;
default:
throw new IllegalArgumentException();
@@ -365,7 +378,33 @@ public class PowerManager
} catch (RemoteException e) {
}
}
-
+
+ /**
+ * Returns the set of flags for {@link #newWakeLock(int, String) newWakeLock()}
+ * that are supported on the device.
+ * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK}
+ * is supported:
+ *
+ * {@samplecode
+ * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+ * int supportedFlags = pm.getSupportedWakeLockFlags();
+ * boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
+ * == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
+ * }
+ *
+ * @return the set of supported WakeLock flags.
+ *
+ * {@hide}
+ */
+ public int getSupportedWakeLockFlags()
+ {
+ try {
+ return mService.getSupportedWakeLockFlags();
+ } catch (RemoteException e) {
+ return 0;
+ }
+ }
+
private PowerManager()
{
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 4805193..699ddb2 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -74,6 +74,12 @@ public class Process {
public static final int SHELL_UID = 2000;
/**
+ * Defines the UID/GID for the log group.
+ * @hide
+ */
+ public static final int LOG_UID = 1007;
+
+ /**
* Defines the UID/GID for the WIFI supplicant process.
* @hide
*/
@@ -739,7 +745,7 @@ public class Process {
public static final native void sendSignal(int pid, int signal);
/** @hide */
- public static final native int getFreeMemory();
+ public static final native long getFreeMemory();
/** @hide */
public static final native void readProcLines(String path,
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 584224f..b74af16 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -22,7 +22,7 @@ import java.util.HashMap;
* Takes care of the grunt work of maintaining a list of remote interfaces,
* typically for the use of performing callbacks from a
* {@link android.app.Service} to its clients. In particular, this:
- *
+ *
* <ul>
* <li> Keeps track of a set of registered {@link IInterface} callbacks,
* taking care to identify them through their underlying unique {@link IBinder}
@@ -34,13 +34,13 @@ import java.util.HashMap;
* multithreaded incoming calls, and a thread-safe way to iterate over a
* snapshot of the list without holding its lock.
* </ul>
- *
+ *
* <p>To use this class, simply create a single instance along with your
* service, and call its {@link #register} and {@link #unregister} methods
* as client register and unregister with your service. To call back on to
* the registered clients, use {@link #beginBroadcast},
* {@link #getBroadcastItem}, and {@link #finishBroadcast}.
- *
+ *
* <p>If a registered callback's process goes away, this class will take
* care of automatically removing it from the list. If you want to do
* additional work in this situation, you can create a subclass that
@@ -52,7 +52,7 @@ public class RemoteCallbackList<E extends IInterface> {
private Object[] mActiveBroadcast;
private int mBroadcastCount = -1;
private boolean mKilled = false;
-
+
private final class Callback implements IBinder.DeathRecipient {
final E mCallback;
final Object mCookie;
@@ -61,7 +61,7 @@ public class RemoteCallbackList<E extends IInterface> {
mCallback = callback;
mCookie = cookie;
}
-
+
public void binderDied() {
synchronized (mCallbacks) {
mCallbacks.remove(mCallback.asBinder());
@@ -69,7 +69,7 @@ public class RemoteCallbackList<E extends IInterface> {
onCallbackDied(mCallback, mCookie);
}
}
-
+
/**
* Simple version of {@link RemoteCallbackList#register(E, Object)}
* that does not take a cookie object.
@@ -86,19 +86,20 @@ public class RemoteCallbackList<E extends IInterface> {
* object is already in the list), then it will be left as-is.
* Registrations are not counted; a single call to {@link #unregister}
* will remove a callback after any number calls to register it.
- *
+ *
* @param callback The callback interface to be added to the list. Must
* not be null -- passing null here will cause a NullPointerException.
* Most services will want to check for null before calling this with
* an object given from a client, so that clients can't crash the
* service with bad data.
+ *
* @param cookie Optional additional data to be associated with this
* callback.
*
* @return Returns true if the callback was successfully added to the list.
* Returns false if it was not added, either because {@link #kill} had
* previously been called or the callback's process has gone away.
- *
+ *
* @see #unregister
* @see #kill
* @see #onCallbackDied
@@ -119,7 +120,7 @@ public class RemoteCallbackList<E extends IInterface> {
}
}
}
-
+
/**
* Remove from the list a callback that was previously added with
* {@link #register}. This uses the
@@ -127,14 +128,14 @@ public class RemoteCallbackList<E extends IInterface> {
* find the previous registration.
* Registrations are not counted; a single unregister call will remove
* a callback after any number calls to {@link #register} for it.
- *
+ *
* @param callback The callback to be removed from the list. Passing
* null here will cause a NullPointerException, so you will generally want
* to check for null before calling.
- *
+ *
* @return Returns true if the callback was found and unregistered. Returns
* false if the given callback was not found on the list.
- *
+ *
* @see #register
*/
public boolean unregister(E callback) {
@@ -147,13 +148,13 @@ public class RemoteCallbackList<E extends IInterface> {
return false;
}
}
-
+
/**
* Disable this callback list. All registered callbacks are unregistered,
* and the list is disabled so that future calls to {@link #register} will
* fail. This should be used when a Service is stopping, to prevent clients
* from registering callbacks after it is stopped.
- *
+ *
* @see #register
*/
public void kill() {
@@ -165,7 +166,7 @@ public class RemoteCallbackList<E extends IInterface> {
mKilled = true;
}
}
-
+
/**
* Old version of {@link #onCallbackDied(E, Object)} that
* does not provide a cookie.
@@ -190,7 +191,7 @@ public class RemoteCallbackList<E extends IInterface> {
public void onCallbackDied(E callback, Object cookie) {
onCallbackDied(callback);
}
-
+
/**
* Prepare to start making calls to the currently registered callbacks.
* This creates a copy of the callback list, which you can retrieve items
@@ -199,12 +200,12 @@ public class RemoteCallbackList<E extends IInterface> {
* same thread (usually by scheduling with {@link Handler}) or
* do your own synchronization. You must call {@link #finishBroadcast}
* when done.
- *
+ *
* <p>A typical loop delivering a broadcast looks like this:
- *
+ *
* <pre>
* int i = callbacks.beginBroadcast();
- * while (i > 0) {
+ * while (i &gt; 0) {
* i--;
* try {
* callbacks.getBroadcastItem(i).somethingHappened();
@@ -214,11 +215,11 @@ public class RemoteCallbackList<E extends IInterface> {
* }
* }
* callbacks.finishBroadcast();</pre>
- *
+ *
* @return Returns the number of callbacks in the broadcast, to be used
* with {@link #getBroadcastItem} to determine the range of indices you
* can supply.
- *
+ *
* @see #getBroadcastItem
* @see #finishBroadcast
*/
@@ -244,26 +245,26 @@ public class RemoteCallbackList<E extends IInterface> {
return i;
}
}
-
+
/**
* Retrieve an item in the active broadcast that was previously started
* with {@link #beginBroadcast}. This can <em>only</em> be called after
* the broadcast is started, and its data is no longer valid after
* calling {@link #finishBroadcast}.
- *
+ *
* <p>Note that it is possible for the process of one of the returned
* callbacks to go away before you call it, so you will need to catch
* {@link RemoteException} when calling on to the returned object.
* The callback list itself, however, will take care of unregistering
* these objects once it detects that it is no longer valid, so you can
* handle such an exception by simply ignoring it.
- *
+ *
* @param index Which of the registered callbacks you would like to
* retrieve. Ranges from 0 to 1-{@link #beginBroadcast}.
- *
+ *
* @return Returns the callback interface that you can call. This will
* always be non-null.
- *
+ *
* @see #beginBroadcast
*/
public E getBroadcastItem(int index) {
@@ -279,12 +280,12 @@ public class RemoteCallbackList<E extends IInterface> {
public Object getBroadcastCookie(int index) {
return ((Callback)mActiveBroadcast[index]).mCookie;
}
-
+
/**
* Clean up the state of a broadcast previously initiated by calling
* {@link #beginBroadcast}. This must always be called when you are done
* with a broadcast.
- *
+ *
* @see #beginBroadcast
*/
public void finishBroadcast() {
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 2b57b39..2dd6749 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -30,7 +30,13 @@ package android.os;
* backwards or forwards unpredictably. This clock should only be used
* when correspondence with real-world dates and times is important, such
* as in a calendar or alarm clock application. Interval or elapsed
- * time measurements should use a different clock.
+ * time measurements should use a different clock. If you are using
+ * System.currentTimeMillis(), consider listening to the
+ * {@link android.content.Intent#ACTION_TIME_TICK ACTION_TIME_TICK},
+ * {@link android.content.Intent#ACTION_TIME_CHANGED ACTION_TIME_CHANGED}
+ * and {@link android.content.Intent#ACTION_TIMEZONE_CHANGED
+ * ACTION_TIMEZONE_CHANGED} {@link android.content.Intent Intent}
+ * broadcasts to find out when the time changes.
*
* <li> <p> {@link #uptimeMillis} is counted in milliseconds since the
* system was booted. This clock stops when the system enters deep
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index c3ae3c2..4a036ec 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -30,6 +30,9 @@ public class SystemProperties
private static native String native_get(String key);
private static native String native_get(String key, String def);
+ private static native int native_get_int(String key, int def);
+ private static native long native_get_long(String key, long def);
+ private static native boolean native_get_boolean(String key, boolean def);
private static native void native_set(String key, String def);
/**
@@ -65,11 +68,10 @@ public class SystemProperties
* @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static int getInt(String key, int def) {
- try {
- return Integer.parseInt(get(key));
- } catch (NumberFormatException e) {
- return def;
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
+ return native_get_int(key, def);
}
/**
@@ -81,11 +83,10 @@ public class SystemProperties
* @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static long getLong(String key, long def) {
- try {
- return Long.parseLong(get(key));
- } catch (NumberFormatException e) {
- return def;
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
+ return native_get_long(key, def);
}
/**
@@ -102,27 +103,10 @@ public class SystemProperties
* @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static boolean getBoolean(String key, boolean def) {
- String value = get(key);
- // Deal with these quick cases first: not found, 0 and 1
- if (value.equals("")) {
- return def;
- } else if (value.equals("0")) {
- return false;
- } else if (value.equals("1")) {
- return true;
- // now for slower (and hopefully less common) cases
- } else if (value.equalsIgnoreCase("n") ||
- value.equalsIgnoreCase("no") ||
- value.equalsIgnoreCase("false") ||
- value.equalsIgnoreCase("off")) {
- return false;
- } else if (value.equalsIgnoreCase("y") ||
- value.equalsIgnoreCase("yes") ||
- value.equalsIgnoreCase("true") ||
- value.equalsIgnoreCase("on")) {
- return true;
+ if (key.length() > PROP_NAME_MAX) {
+ throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
- return def;
+ return native_get_boolean(key, def);
}
/**
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 0f75289..51dcff1 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -24,6 +24,7 @@ package android.os;
public class Vibrator
{
IHardwareService mService;
+ private final Binder mToken = new Binder();
/** @hide */
public Vibrator()
@@ -40,7 +41,7 @@ public class Vibrator
public void vibrate(long milliseconds)
{
try {
- mService.vibrate(milliseconds);
+ mService.vibrate(milliseconds, mToken);
} catch (RemoteException e) {
}
}
@@ -65,7 +66,7 @@ public class Vibrator
// anyway
if (repeat < pattern.length) {
try {
- mService.vibratePattern(pattern, repeat, new Binder());
+ mService.vibratePattern(pattern, repeat, mToken);
} catch (RemoteException e) {
}
} else {
@@ -79,7 +80,7 @@ public class Vibrator
public void cancel()
{
try {
- mService.cancelVibrate();
+ mService.cancelVibrate(mToken);
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/pim/ContactsAsyncHelper.java b/core/java/android/pim/ContactsAsyncHelper.java
index a21281e..7c78a81 100644
--- a/core/java/android/pim/ContactsAsyncHelper.java
+++ b/core/java/android/pim/ContactsAsyncHelper.java
@@ -27,8 +27,7 @@ import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.provider.Contacts;
-import android.provider.Contacts.People;
+import android.provider.ContactsContract.Contacts;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -39,35 +38,35 @@ import java.io.InputStream;
* Helper class for async access of images.
*/
public class ContactsAsyncHelper extends Handler {
-
+
private static final boolean DBG = false;
private static final String LOG_TAG = "ContactsAsyncHelper";
-
+
/**
* Interface for a WorkerHandler result return.
*/
public interface OnImageLoadCompleteListener {
/**
* Called when the image load is complete.
- *
+ *
* @param imagePresent true if an image was found
- */
+ */
public void onImageLoadComplete(int token, Object cookie, ImageView iView,
boolean imagePresent);
}
-
+
// constants
private static final int EVENT_LOAD_IMAGE = 1;
private static final int DEFAULT_TOKEN = -1;
-
+
// static objects
private static Handler sThreadHandler;
private static ContactsAsyncHelper sInstance;
-
+
static {
sInstance = new ContactsAsyncHelper();
}
-
+
private static final class WorkerArgs {
public Context context;
public ImageView view;
@@ -78,12 +77,12 @@ public class ContactsAsyncHelper extends Handler {
public OnImageLoadCompleteListener listener;
public CallerInfo info;
}
-
+
/**
- * public inner class to help out the ContactsAsyncHelper callers
- * with tracking the state of the CallerInfo Queries and image
+ * public inner class to help out the ContactsAsyncHelper callers
+ * with tracking the state of the CallerInfo Queries and image
* loading.
- *
+ *
* Logic contained herein is used to remove the race conditions
* that exist as the CallerInfo queries run and mix with the image
* loads, which then mix with the Phone state changes.
@@ -94,11 +93,11 @@ public class ContactsAsyncHelper extends Handler {
public static final int DISPLAY_UNDEFINED = 0;
public static final int DISPLAY_IMAGE = -1;
public static final int DISPLAY_DEFAULT = -2;
-
+
// State of the image on the imageview.
private CallerInfo mCurrentCallerInfo;
private int displayMode;
-
+
public ImageTracker() {
mCurrentCallerInfo = null;
displayMode = DISPLAY_UNDEFINED;
@@ -107,17 +106,17 @@ public class ContactsAsyncHelper extends Handler {
/**
* Used to see if the requested call / connection has a
* different caller attached to it than the one we currently
- * have in the CallCard.
+ * have in the CallCard.
*/
public boolean isDifferentImageRequest(CallerInfo ci) {
// note, since the connections are around for the lifetime of the
- // call, and the CallerInfo-related items as well, we can
+ // call, and the CallerInfo-related items as well, we can
// definitely use a simple != comparison.
return (mCurrentCallerInfo != ci);
}
-
+
public boolean isDifferentImageRequest(Connection connection) {
- // if the connection does not exist, see if the
+ // if the connection does not exist, see if the
// mCurrentCallerInfo is also null to match.
if (connection == null) {
if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null");
@@ -133,58 +132,65 @@ public class ContactsAsyncHelper extends Handler {
}
return runQuery;
}
-
+
/**
- * Simple setter for the CallerInfo object.
+ * Simple setter for the CallerInfo object.
*/
public void setPhotoRequest(CallerInfo ci) {
- mCurrentCallerInfo = ci;
+ mCurrentCallerInfo = ci;
}
-
+
/**
- * Convenience method used to retrieve the URI
- * representing the Photo file recorded in the attached
- * CallerInfo Object.
+ * Convenience method used to retrieve the URI
+ * representing the Photo file recorded in the attached
+ * CallerInfo Object.
*/
public Uri getPhotoUri() {
if (mCurrentCallerInfo != null) {
- return ContentUris.withAppendedId(People.CONTENT_URI,
+ return ContentUris.withAppendedId(Contacts.CONTENT_URI,
mCurrentCallerInfo.person_id);
}
- return null;
+ return null;
}
-
+
/**
- * Simple setter for the Photo state.
+ * Simple setter for the Photo state.
*/
public void setPhotoState(int state) {
displayMode = state;
}
-
+
/**
- * Simple getter for the Photo state.
+ * Simple getter for the Photo state.
*/
public int getPhotoState() {
return displayMode;
}
}
-
+
/**
- * Thread worker class that handles the task of opening the stream and loading
+ * Thread worker class that handles the task of opening the stream and loading
* the images.
*/
private class WorkerHandler extends Handler {
public WorkerHandler(Looper looper) {
super(looper);
}
-
+
+ @Override
public void handleMessage(Message msg) {
WorkerArgs args = (WorkerArgs) msg.obj;
-
+
switch (msg.arg1) {
case EVENT_LOAD_IMAGE:
- InputStream inputStream = Contacts.People.openContactPhotoInputStream(
- args.context.getContentResolver(), args.uri);
+ InputStream inputStream = null;
+ try {
+ inputStream = Contacts.openContactPhotoInputStream(
+ args.context.getContentResolver(), args.uri);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error opening photo input stream", e);
+ }
+
if (inputStream != null) {
args.result = Drawable.createFromStream(inputStream, args.uri.toString());
@@ -192,22 +198,22 @@ public class ContactsAsyncHelper extends Handler {
" token: " + msg.what + " image URI: " + args.uri);
} else {
args.result = null;
- if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
- " token: " + msg.what + " image URI: " + args.uri +
+ if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 +
+ " token: " + msg.what + " image URI: " + args.uri +
", using default image.");
}
break;
default:
}
-
- // send the reply to the enclosing class.
+
+ // send the reply to the enclosing class.
Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what);
reply.arg1 = msg.arg1;
reply.obj = msg.obj;
reply.sendToTarget();
}
}
-
+
/**
* Private constructor for static class
*/
@@ -216,14 +222,14 @@ public class ContactsAsyncHelper extends Handler {
thread.start();
sThreadHandler = new WorkerHandler(thread.getLooper());
}
-
+
/**
* Convenience method for calls that do not want to deal with listeners and tokens.
*/
- public static final void updateImageViewWithContactPhotoAsync(Context context,
+ public static final void updateImageViewWithContactPhotoAsync(Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
// Added additional Cookie field in the callee.
- updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
+ updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context,
imageView, person, placeholderImageResource);
}
@@ -231,24 +237,24 @@ public class ContactsAsyncHelper extends Handler {
* Convenience method for calls that do not want to deal with listeners and tokens, but have
* a CallerInfo object to cache the image to.
*/
- public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
+ public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
// Added additional Cookie field in the callee.
- updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
+ updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context,
imageView, person, placeholderImageResource);
}
-
+
/**
* Start an image load, attach the result to the specified CallerInfo object.
* Note, when the query is started, we make the ImageView INVISIBLE if the
* placeholderImageResource value is -1. When we're given a valid (!= -1)
* placeholderImageResource value, we make sure the image is visible.
*/
- public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
- OnImageLoadCompleteListener listener, Object cookie, Context context,
+ public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token,
+ OnImageLoadCompleteListener listener, Object cookie, Context context,
ImageView imageView, Uri person, int placeholderImageResource) {
-
+
// in case the source caller info is null, the URI will be null as well.
// just update using the placeholder image in this case.
if (person == null) {
@@ -257,10 +263,10 @@ public class ContactsAsyncHelper extends Handler {
imageView.setImageResource(placeholderImageResource);
return;
}
-
+
// Added additional Cookie field in the callee to handle arguments
// sent to the callback function.
-
+
// setup arguments
WorkerArgs args = new WorkerArgs();
args.cookie = cookie;
@@ -270,15 +276,15 @@ public class ContactsAsyncHelper extends Handler {
args.defaultResource = placeholderImageResource;
args.listener = listener;
args.info = info;
-
+
// setup message arguments
Message msg = sThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_LOAD_IMAGE;
msg.obj = args;
-
- if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
+
+ if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri +
", displaying default image for now.");
-
+
// set the default image first, when the query is complete, we will
// replace the image with the correct one.
if (placeholderImageResource != -1) {
@@ -287,11 +293,11 @@ public class ContactsAsyncHelper extends Handler {
} else {
imageView.setVisibility(View.INVISIBLE);
}
-
+
// notify the thread to begin working
sThreadHandler.sendMessage(msg);
}
-
+
/**
* Called when loading is done.
*/
@@ -316,21 +322,21 @@ public class ContactsAsyncHelper extends Handler {
args.view.setVisibility(View.VISIBLE);
args.view.setImageResource(args.defaultResource);
}
-
+
// Note that the data is cached.
if (args.info != null) {
args.info.isCachedPhotoCurrent = true;
}
-
+
// notify the listener if it is there.
if (args.listener != null) {
- if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
+ if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() +
" image: " + args.uri + " completed");
args.listener.onImageLoadComplete(msg.what, args.cookie, args.view,
imagePresent);
}
break;
- default:
+ default:
}
}
}
diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java
index 1a287c8..bd7924a 100644
--- a/core/java/android/pim/RecurrenceSet.java
+++ b/core/java/android/pim/RecurrenceSet.java
@@ -223,6 +223,7 @@ public class RecurrenceSet {
return true;
}
+ // This can be removed when the old CalendarSyncAdapter is removed.
public static boolean populateComponent(Cursor cursor,
ICalendar.Component component) {
@@ -292,6 +293,64 @@ public class RecurrenceSet {
return true;
}
+public static boolean populateComponent(ContentValues values,
+ ICalendar.Component component) {
+ long dtstart = -1;
+ if (values.containsKey(Calendar.Events.DTSTART)) {
+ dtstart = values.getAsLong(Calendar.Events.DTSTART);
+ }
+ String duration = values.getAsString(Calendar.Events.DURATION);
+ String tzid = values.getAsString(Calendar.Events.EVENT_TIMEZONE);
+ String rruleStr = values.getAsString(Calendar.Events.RRULE);
+ String rdateStr = values.getAsString(Calendar.Events.RDATE);
+ String exruleStr = values.getAsString(Calendar.Events.EXRULE);
+ String exdateStr = values.getAsString(Calendar.Events.EXDATE);
+ boolean allDay = values.getAsInteger(Calendar.Events.ALL_DAY) == 1;
+
+ if ((dtstart == -1) ||
+ (TextUtils.isEmpty(duration))||
+ ((TextUtils.isEmpty(rruleStr))&&
+ (TextUtils.isEmpty(rdateStr)))) {
+ // no recurrence.
+ return false;
+ }
+
+ ICalendar.Property dtstartProp = new ICalendar.Property("DTSTART");
+ Time dtstartTime = null;
+ if (!TextUtils.isEmpty(tzid)) {
+ if (!allDay) {
+ dtstartProp.addParameter(new ICalendar.Parameter("TZID", tzid));
+ }
+ dtstartTime = new Time(tzid);
+ } else {
+ // use the "floating" timezone
+ dtstartTime = new Time(Time.TIMEZONE_UTC);
+ }
+
+ dtstartTime.set(dtstart);
+ // make sure the time is printed just as a date, if all day.
+ // TODO: android.pim.Time really should take care of this for us.
+ if (allDay) {
+ dtstartProp.addParameter(new ICalendar.Parameter("VALUE", "DATE"));
+ dtstartTime.allDay = true;
+ dtstartTime.hour = 0;
+ dtstartTime.minute = 0;
+ dtstartTime.second = 0;
+ }
+
+ dtstartProp.setValue(dtstartTime.format2445());
+ component.addProperty(dtstartProp);
+ ICalendar.Property durationProp = new ICalendar.Property("DURATION");
+ durationProp.setValue(duration);
+ component.addProperty(durationProp);
+
+ addPropertiesForRuleStr(component, "RRULE", rruleStr);
+ addPropertyForDateStr(component, "RDATE", rdateStr);
+ addPropertiesForRuleStr(component, "EXRULE", exruleStr);
+ addPropertyForDateStr(component, "EXDATE", exdateStr);
+ return true;
+ }
+
private static void addPropertiesForRuleStr(ICalendar.Component component,
String propertyName,
String ruleStr) {
@@ -351,10 +410,14 @@ public class RecurrenceSet {
Time end = new Time(endTzid);
end.parse(dtendProperty.getValue());
- long durationMillis = end.toMillis(false /* use isDst */)
+ long durationMillis = end.toMillis(false /* use isDst */)
- start.toMillis(false /* use isDst */);
long durationSeconds = (durationMillis / 1000);
- return "P" + durationSeconds + "S";
+ if (start.allDay && (durationSeconds % 86400) == 0) {
+ return "P" + (durationSeconds / 86400) + "D"; // Server wants this instead of P86400S
+ } else {
+ return "P" + durationSeconds + "S";
+ }
}
private static String flattenProperties(ICalendar.Component component,
diff --git a/core/java/android/pim/vcard/Constants.java b/core/java/android/pim/vcard/Constants.java
new file mode 100644
index 0000000..ca41ce5
--- /dev/null
+++ b/core/java/android/pim/vcard/Constants.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+/**
+ * Constants used in both composer and parser.
+ */
+/* package */ class Constants {
+
+ public static final String ATTR_TYPE = "TYPE";
+
+ public static final String VERSION_V21 = "2.1";
+ public static final String VERSION_V30 = "3.0";
+
+ // Properties both the current (as of 2009-08-17) ContactsStruct and de-fact vCard extensions
+ // shown in http://en.wikipedia.org/wiki/VCard support are defined here.
+ public static final String PROPERTY_X_AIM = "X-AIM";
+ public static final String PROPERTY_X_MSN = "X-MSN";
+ public static final String PROPERTY_X_YAHOO = "X-YAHOO";
+ public static final String PROPERTY_X_ICQ = "X-ICQ";
+ public static final String PROPERTY_X_JABBER = "X-JABBER";
+ public static final String PROPERTY_X_GOOGLE_TALK = "X-GOOGLE-TALK";
+ public static final String PROPERTY_X_SKYPE_USERNAME = "X-SKYPE-USERNAME";
+ // Phone number for Skype, available as usual phone.
+ public static final String PROPERTY_X_SKYPE_PSTNNUMBER = "X-SKYPE-PSTNNUMBER";
+ // Some device emits this "X-" attribute, which is specifically invalid but should be
+ // always properly accepted, and emitted in some special case (for that device/application).
+ public static final String PROPERTY_X_GOOGLE_TALK_WITH_SPACE = "X-GOOGLE TALK";
+
+ // How more than one TYPE fields are expressed is different between vCard 2.1 and vCard 3.0
+ //
+ // e.g.
+ // 1) Probably valid in both vCard 2.1 and vCard 3.0: "ADR;TYPE=DOM;TYPE=HOME:..."
+ // 2) Valid in vCard 2.1 but not in vCard 3.0: "ADR;DOM;HOME:..."
+ // 3) Valid in vCard 3.0 but not in vCard 2.1: "ADR;TYPE=DOM,HOME:..."
+ //
+ // 2) has been the default of VCard exporter/importer in Android, but we can see the other
+ // formats in vCard data emitted by the other softwares/devices.
+ //
+ // So we are currently not sure which type is the best; probably we will have to change which
+ // type should be emitted depending on the device.
+ public static final String ATTR_TYPE_HOME = "HOME";
+ public static final String ATTR_TYPE_WORK = "WORK";
+ public static final String ATTR_TYPE_FAX = "FAX";
+ public static final String ATTR_TYPE_CELL = "CELL";
+ public static final String ATTR_TYPE_VOICE = "VOICE";
+ public static final String ATTR_TYPE_INTERNET = "INTERNET";
+
+ public static final String ATTR_TYPE_PREF = "PREF";
+
+ // Phone types valid in vCard and known to ContactsContract, but not so common.
+ public static final String ATTR_TYPE_CAR = "CAR";
+ public static final String ATTR_TYPE_ISDN = "ISDN";
+ public static final String ATTR_TYPE_PAGER = "PAGER";
+
+ // Phone types existing in vCard 2.1 but not known to ContactsContract.
+ // TODO: should make parser make these TYPE_CUSTOM.
+ public static final String ATTR_TYPE_MODEM = "MODEM";
+ public static final String ATTR_TYPE_MSG = "MSG";
+ public static final String ATTR_TYPE_BBS = "BBS";
+ public static final String ATTR_TYPE_VIDEO = "VIDEO";
+
+ // Phone types existing in the current Contacts structure but not valid in vCard (at least 2.1)
+ // These types are encoded to "X-" attributes when composing vCard for now.
+ // Parser passes these even if "X-" is added to the attribute.
+ public static final String ATTR_TYPE_PHONE_EXTRA_OTHER = "OTHER";
+ public static final String ATTR_TYPE_PHONE_EXTRA_CALLBACK = "CALLBACK";
+ // TODO: may be "TYPE=COMPANY,PREF", not "COMPANY-MAIN".
+ public static final String ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN = "COMPANY-MAIN";
+ public static final String ATTR_TYPE_PHONE_EXTRA_RADIO = "RADIO";
+ public static final String ATTR_TYPE_PHONE_EXTRA_TELEX = "TELEX";
+ public static final String ATTR_TYPE_PHONE_EXTRA_TTY_TDD = "TTY-TDD";
+ public static final String ATTR_TYPE_PHONE_EXTRA_ASSISTANT = "ASSISTANT";
+
+ // DoCoMo specific attribute. Used with "SOUND" property, which is alternate of SORT-STRING in
+ // vCard 3.0.
+ public static final String ATTR_TYPE_X_IRMC_N = "X-IRMC-N";
+
+ private Constants() {
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/ContactStruct.java b/core/java/android/pim/vcard/ContactStruct.java
new file mode 100644
index 0000000..0064bf2
--- /dev/null
+++ b/core/java/android/pim/vcard/ContactStruct.java
@@ -0,0 +1,1218 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Miscellaneous;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class bridges between data structure of Contact app and VCard data.
+ */
+public class ContactStruct {
+ private static final String LOG_TAG = "vcard.ContactStruct";
+
+ // Key: the name shown in VCard. e.g. "X-AIM", "X-ICQ"
+ // Value: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol}
+ private static final Map<String, Integer> sImMap = new HashMap<String, Integer>();
+
+ static {
+ sImMap.put(Constants.PROPERTY_X_AIM, Im.PROTOCOL_AIM);
+ sImMap.put(Constants.PROPERTY_X_MSN, Im.PROTOCOL_MSN);
+ sImMap.put(Constants.PROPERTY_X_YAHOO, Im.PROTOCOL_YAHOO);
+ sImMap.put(Constants.PROPERTY_X_ICQ, Im.PROTOCOL_ICQ);
+ sImMap.put(Constants.PROPERTY_X_JABBER, Im.PROTOCOL_JABBER);
+ sImMap.put(Constants.PROPERTY_X_SKYPE_USERNAME, Im.PROTOCOL_SKYPE);
+ sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK, Im.PROTOCOL_GOOGLE_TALK);
+ sImMap.put(Constants.PROPERTY_X_GOOGLE_TALK_WITH_SPACE, Im.PROTOCOL_GOOGLE_TALK);
+ }
+
+ /**
+ * @hide only for testing
+ */
+ static public class PhoneData {
+ public final int type;
+ public final String data;
+ public final String label;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public PhoneData(int type, String data, String label, boolean isPrimary) {
+ this.type = type;
+ this.data = data;
+ this.label = label;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PhoneData) {
+ return false;
+ }
+ PhoneData phoneData = (PhoneData)obj;
+ return (type == phoneData.type && data.equals(phoneData.data) &&
+ label.equals(phoneData.label) && isPrimary == phoneData.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
+ type, data, label, isPrimary);
+ }
+ }
+
+ /**
+ * @hide only for testing
+ */
+ static public class EmailData {
+ public final int type;
+ public final String data;
+ // Used only when TYPE is TYPE_CUSTOM.
+ public final String label;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public EmailData(int type, String data, String label, boolean isPrimary) {
+ this.type = type;
+ this.data = data;
+ this.label = label;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof EmailData) {
+ return false;
+ }
+ EmailData emailData = (EmailData)obj;
+ return (type == emailData.type && data.equals(emailData.data) &&
+ label.equals(emailData.label) && isPrimary == emailData.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
+ type, data, label, isPrimary);
+ }
+ }
+
+ static public class PostalData {
+ // Determined by vCard spec.
+ // PO Box, Extended Addr, Street, Locality, Region, Postal Code, Country Name
+ public static final int ADDR_MAX_DATA_SIZE = 7;
+ private final String[] dataArray;
+ public final String pobox;
+ public final String extendedAddress;
+ public final String street;
+ public final String localty;
+ public final String region;
+ public final String postalCode;
+ public final String country;
+
+ public final int type;
+
+ // Used only when type variable is TYPE_CUSTOM.
+ public final String label;
+
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public PostalData(int type, List<String> propValueList,
+ String label, boolean isPrimary) {
+ this.type = type;
+ dataArray = new String[ADDR_MAX_DATA_SIZE];
+
+ int size = propValueList.size();
+ if (size > ADDR_MAX_DATA_SIZE) {
+ size = ADDR_MAX_DATA_SIZE;
+ }
+
+ // adr-value = 0*6(text-value ";") text-value
+ // ; PO Box, Extended Address, Street, Locality, Region, Postal
+ // ; Code, Country Name
+ //
+ // Use Iterator assuming List may be LinkedList, though actually it is
+ // always ArrayList in the current implementation.
+ int i = 0;
+ for (String addressElement : propValueList) {
+ dataArray[i] = addressElement;
+ if (++i >= size) {
+ break;
+ }
+ }
+ while (i < ADDR_MAX_DATA_SIZE) {
+ dataArray[i++] = null;
+ }
+
+ this.pobox = dataArray[0];
+ this.extendedAddress = dataArray[1];
+ this.street = dataArray[2];
+ this.localty = dataArray[3];
+ this.region = dataArray[4];
+ this.postalCode = dataArray[5];
+ this.country = dataArray[6];
+
+ this.label = label;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PostalData) {
+ return false;
+ }
+ PostalData postalData = (PostalData)obj;
+ return (Arrays.equals(dataArray, postalData.dataArray) &&
+ (type == postalData.type &&
+ (type == StructuredPostal.TYPE_CUSTOM ?
+ (label == postalData.label) : true)) &&
+ (isPrimary == postalData.isPrimary));
+ }
+
+ public String getFormattedAddress(int vcardType) {
+ StringBuilder builder = new StringBuilder();
+ boolean empty = true;
+ if (VCardConfig.isJapaneseDevice(vcardType)) {
+ // In Japan, the order is reversed.
+ for (int i = ADDR_MAX_DATA_SIZE - 1; i >= 0; i--) {
+ String addressPart = dataArray[i];
+ if (!TextUtils.isEmpty(addressPart)) {
+ if (!empty) {
+ builder.append(' ');
+ }
+ builder.append(addressPart);
+ empty = false;
+ }
+ }
+ } else {
+ for (int i = 0; i < ADDR_MAX_DATA_SIZE; i++) {
+ String addressPart = dataArray[i];
+ if (!TextUtils.isEmpty(addressPart)) {
+ if (!empty) {
+ builder.append(' ');
+ }
+ builder.append(addressPart);
+ empty = false;
+ }
+ }
+ }
+
+ return builder.toString().trim();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, label: %s, isPrimary: %s",
+ type, label, isPrimary);
+ }
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ static public class OrganizationData {
+ public final int type;
+ public final String companyName;
+ // can be changed in some VCard format.
+ public String positionName;
+ // isPrimary is changable only when there's no appropriate one existing in
+ // the original VCard.
+ public boolean isPrimary;
+ public OrganizationData(int type, String companyName, String positionName,
+ boolean isPrimary) {
+ this.type = type;
+ this.companyName = companyName;
+ this.positionName = positionName;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof OrganizationData) {
+ return false;
+ }
+ OrganizationData organization = (OrganizationData)obj;
+ return (type == organization.type && companyName.equals(organization.companyName) &&
+ positionName.equals(organization.positionName) &&
+ isPrimary == organization.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, company: %s, position: %s, isPrimary: %s",
+ type, companyName, positionName, isPrimary);
+ }
+ }
+
+ static public class ImData {
+ public final int type;
+ public final String data;
+ public final String label;
+ public final boolean isPrimary;
+
+ // TODO: ContactsConstant#PROTOCOL, ContactsConstant#CUSTOM_PROTOCOL should be used?
+ public ImData(int type, String data, String label, boolean isPrimary) {
+ this.type = type;
+ this.data = data;
+ this.label = label;
+ this.isPrimary = isPrimary;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ImData) {
+ return false;
+ }
+ ImData imData = (ImData)obj;
+ return (type == imData.type && data.equals(imData.data) &&
+ label.equals(imData.label) && isPrimary == imData.isPrimary);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("type: %d, data: %s, label: %s, isPrimary: %s",
+ type, data, label, isPrimary);
+ }
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ static public class PhotoData {
+ public static final String FORMAT_FLASH = "SWF";
+ public final int type;
+ public final String formatName; // used when type is not defined in ContactsContract.
+ public final byte[] photoBytes;
+
+ public PhotoData(int type, String formatName, byte[] photoBytes) {
+ this.type = type;
+ this.formatName = formatName;
+ this.photoBytes = photoBytes;
+ }
+ }
+
+ static /* package */ class Property {
+ private String mPropertyName;
+ private Map<String, Collection<String>> mParameterMap =
+ new HashMap<String, Collection<String>>();
+ private List<String> mPropertyValueList = new ArrayList<String>();
+ private byte[] mPropertyBytes;
+
+ public Property() {
+ clear();
+ }
+
+ public void setPropertyName(final String propertyName) {
+ mPropertyName = propertyName;
+ }
+
+ public void addParameter(final String paramName, final String paramValue) {
+ Collection<String> values;
+ if (!mParameterMap.containsKey(paramName)) {
+ if (paramName.equals("TYPE")) {
+ values = new HashSet<String>();
+ } else {
+ values = new ArrayList<String>();
+ }
+ mParameterMap.put(paramName, values);
+ } else {
+ values = mParameterMap.get(paramName);
+ }
+ values.add(paramValue);
+ }
+
+ public void addToPropertyValueList(final String propertyValue) {
+ mPropertyValueList.add(propertyValue);
+ }
+
+ public void setPropertyBytes(final byte[] propertyBytes) {
+ mPropertyBytes = propertyBytes;
+ }
+
+ public final Collection<String> getParameters(String type) {
+ return mParameterMap.get(type);
+ }
+
+ public final List<String> getPropertyValueList() {
+ return mPropertyValueList;
+ }
+
+ public void clear() {
+ mPropertyName = null;
+ mParameterMap.clear();
+ mPropertyValueList.clear();
+ }
+ }
+
+ private String mFamilyName;
+ private String mGivenName;
+ private String mMiddleName;
+ private String mPrefix;
+ private String mSuffix;
+
+ // Used only when no family nor given name is found.
+ private String mFullName;
+
+ private String mPhoneticFamilyName;
+ private String mPhoneticGivenName;
+ private String mPhoneticMiddleName;
+
+ private String mPhoneticFullName;
+
+ private List<String> mNickNameList;
+
+ private String mDisplayName;
+
+ private String mBirthday;
+
+ private List<String> mNoteList;
+ private List<PhoneData> mPhoneList;
+ private List<EmailData> mEmailList;
+ private List<PostalData> mPostalList;
+ private List<OrganizationData> mOrganizationList;
+ private List<ImData> mImList;
+ private List<PhotoData> mPhotoList;
+ private List<String> mWebsiteList;
+
+ private final int mVCardType;
+
+ // Each Column of four properties has ISPRIMARY field
+ // (See android.provider.Contacts)
+ // If false even after the parsing loop, we choose the first entry as a "primary"
+ // entry.
+ private boolean mPrefIsSet_Address;
+ private boolean mPrefIsSet_Phone;
+ private boolean mPrefIsSet_Email;
+ private boolean mPrefIsSet_Organization;
+
+ public ContactStruct() {
+ this(VCardConfig.VCARD_TYPE_V21_GENERIC);
+ }
+
+ public ContactStruct(int vcardType) {
+ mVCardType = vcardType;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public ContactStruct(String givenName,
+ String familyName,
+ String middleName,
+ String prefix,
+ String suffix,
+ String phoneticGivenName,
+ String pheneticFamilyName,
+ String phoneticMiddleName,
+ List<byte[]> photoBytesList,
+ List<String> notes,
+ List<PhoneData> phoneList,
+ List<EmailData> emailList,
+ List<PostalData> postalList,
+ List<OrganizationData> organizationList,
+ List<PhotoData> photoList,
+ List<String> websiteList) {
+ this(VCardConfig.VCARD_TYPE_DEFAULT);
+ mGivenName = givenName;
+ mFamilyName = familyName;
+ mPrefix = prefix;
+ mSuffix = suffix;
+ mPhoneticGivenName = givenName;
+ mPhoneticFamilyName = familyName;
+ mPhoneticMiddleName = middleName;
+ mEmailList = emailList;
+ mPostalList = postalList;
+ mOrganizationList = organizationList;
+ mPhotoList = photoList;
+ mWebsiteList = websiteList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<PhotoData> getPhotoList() {
+ return mPhotoList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<String> getNotes() {
+ return mNoteList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<PhoneData> getPhoneList() {
+ return mPhoneList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<EmailData> getEmailList() {
+ return mEmailList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<PostalData> getPostalList() {
+ return mPostalList;
+ }
+
+ /**
+ * @hide only for testing.
+ */
+ public final List<OrganizationData> getOrganizationList() {
+ return mOrganizationList;
+ }
+
+ /**
+ * Add a phone info to phoneList.
+ * @param data phone number
+ * @param type type col of content://contacts/phones
+ * @param label lable col of content://contacts/phones
+ */
+ private void addPhone(int type, String data, String label, boolean isPrimary){
+ if (mPhoneList == null) {
+ mPhoneList = new ArrayList<PhoneData>();
+ }
+ StringBuilder builder = new StringBuilder();
+ String trimed = data.trim();
+ int length = trimed.length();
+ for (int i = 0; i < length; i++) {
+ char ch = trimed.charAt(i);
+ if (('0' <= ch && ch <= '9') || (i == 0 && ch == '+')) {
+ builder.append(ch);
+ }
+ }
+
+ PhoneData phoneData = new PhoneData(type,
+ PhoneNumberUtils.formatNumber(builder.toString()),
+ label, isPrimary);
+
+ mPhoneList.add(phoneData);
+ }
+
+ private void addNickName(final String nickName) {
+ if (mNickNameList == null) {
+ mNickNameList = new ArrayList<String>();
+ }
+ mNickNameList.add(nickName);
+ }
+
+ private void addEmail(int type, String data, String label, boolean isPrimary){
+ if (mEmailList == null) {
+ mEmailList = new ArrayList<EmailData>();
+ }
+ mEmailList.add(new EmailData(type, data, label, isPrimary));
+ }
+
+ private void addPostal(int type, List<String> propValueList, String label, boolean isPrimary){
+ if (mPostalList == null) {
+ mPostalList = new ArrayList<PostalData>();
+ }
+ mPostalList.add(new PostalData(type, propValueList, label, isPrimary));
+ }
+
+ private void addOrganization(int type, final String companyName,
+ final String positionName, boolean isPrimary) {
+ if (mOrganizationList == null) {
+ mOrganizationList = new ArrayList<OrganizationData>();
+ }
+ mOrganizationList.add(new OrganizationData(type, companyName, positionName, isPrimary));
+ }
+
+ private void addIm(int type, String data, String label, boolean isPrimary) {
+ if (mImList == null) {
+ mImList = new ArrayList<ImData>();
+ }
+ mImList.add(new ImData(type, data, label, isPrimary));
+ }
+
+ private void addNote(final String note) {
+ if (mNoteList == null) {
+ mNoteList = new ArrayList<String>(1);
+ }
+ mNoteList.add(note);
+ }
+
+ private void addPhotoBytes(String formatName, byte[] photoBytes) {
+ if (mPhotoList == null) {
+ mPhotoList = new ArrayList<PhotoData>(1);
+ }
+ final PhotoData photoData = new PhotoData(0, null, photoBytes);
+ }
+
+ /**
+ * Set "position" value to the appropriate data. If there's more than one
+ * OrganizationData objects, the value is set to the last one. If there's no
+ * OrganizationData object, a new OrganizationData is created, whose company name is
+ * empty.
+ *
+ * TODO: incomplete logic. fix this:
+ *
+ * e.g. This assumes ORG comes earlier, but TITLE may come earlier like this, though we do not
+ * know how to handle it in general cases...
+ * ----
+ * TITLE:Software Engineer
+ * ORG:Google
+ * ----
+ */
+ private void setPosition(String positionValue) {
+ if (mOrganizationList == null) {
+ mOrganizationList = new ArrayList<OrganizationData>();
+ }
+ int size = mOrganizationList.size();
+ if (size == 0) {
+ addOrganization(ContactsContract.CommonDataKinds.Organization.TYPE_OTHER,
+ "", null, false);
+ size = 1;
+ }
+ OrganizationData lastData = mOrganizationList.get(size - 1);
+ lastData.positionName = positionValue;
+ }
+
+ @SuppressWarnings("fallthrough")
+ private void handleNProperty(List<String> elems) {
+ // Family, Given, Middle, Prefix, Suffix. (1 - 5)
+ int size;
+ if (elems == null || (size = elems.size()) < 1) {
+ return;
+ }
+ if (size > 5) {
+ size = 5;
+ }
+
+ switch (size) {
+ // fallthrough
+ case 5:
+ mSuffix = elems.get(4);
+ case 4:
+ mPrefix = elems.get(3);
+ case 3:
+ mMiddleName = elems.get(2);
+ case 2:
+ mGivenName = elems.get(1);
+ default:
+ mFamilyName = elems.get(0);
+ }
+ }
+
+ /**
+ * Some Japanese mobile phones use this field for phonetic name,
+ * since vCard 2.1 does not have "SORT-STRING" type.
+ * Also, in some cases, the field has some ';'s in it.
+ * Assume the ';' means the same meaning in N property
+ */
+ @SuppressWarnings("fallthrough")
+ private void handlePhoneticNameFromSound(List<String> elems) {
+ // Family, Given, Middle. (1-3)
+ // This is not from specification but mere assumption. Some Japanese phones use this order.
+ int size;
+ if (elems == null || (size = elems.size()) < 1) {
+ return;
+ }
+ if (size > 3) {
+ size = 3;
+ }
+
+ switch (size) {
+ // fallthrough
+ case 3:
+ mPhoneticMiddleName = elems.get(2);
+ case 2:
+ mPhoneticGivenName = elems.get(1);
+ default:
+ mPhoneticFamilyName = elems.get(0);
+ }
+ }
+
+ public void addProperty(Property property) {
+ String propName = property.mPropertyName;
+ final Map<String, Collection<String>> paramMap = property.mParameterMap;
+ final List<String> propValueList = property.mPropertyValueList;
+ byte[] propBytes = property.mPropertyBytes;
+
+ if (propValueList.size() == 0) {
+ return;
+ }
+ final String propValue = listToString(propValueList).trim();
+
+ if (propName.equals("VERSION")) {
+ // vCard version. Ignore this.
+ } else if (propName.equals("FN")) {
+ mFullName = propValue;
+ } else if (propName.equals("NAME") && mFullName == null) {
+ // Only in vCard 3.0. Use this if FN, which must exist in vCard 3.0 but may not
+ // actually exist in the real vCard data, does not exist.
+ mFullName = propValue;
+ } else if (propName.equals("N")) {
+ handleNProperty(propValueList);
+ } else if (propName.equals("SORT-STRING")) {
+ mPhoneticFullName = propValue;
+ } else if (propName.equals("NICKNAME") || propName.equals("X-NICKNAME")) {
+ addNickName(propValue);
+ } else if (propName.equals("SOUND")) {
+ if (Constants.ATTR_TYPE_X_IRMC_N.equals(paramMap.get(Constants.ATTR_TYPE))) {
+ handlePhoneticNameFromSound(propValueList);
+ } else {
+ // Ignore this field since Android cannot understand what it is.
+ }
+ } else if (propName.equals("ADR")) {
+ boolean valuesAreAllEmpty = true;
+ for (String value : propValueList) {
+ if (value.length() > 0) {
+ valuesAreAllEmpty = false;
+ break;
+ }
+ }
+ if (valuesAreAllEmpty) {
+ return;
+ }
+
+ int type = -1;
+ String label = "";
+ boolean isPrimary = false;
+ Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ typeString = typeString.toUpperCase();
+ if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Address) {
+ // Only first "PREF" is considered.
+ mPrefIsSet_Address = true;
+ isPrimary = true;
+ } else if (typeString.equals(Constants.ATTR_TYPE_HOME)) {
+ type = StructuredPostal.TYPE_HOME;
+ label = "";
+ } else if (typeString.equals(Constants.ATTR_TYPE_WORK) ||
+ typeString.equalsIgnoreCase("COMPANY")) {
+ // "COMPANY" seems emitted by Windows Mobile, which is not
+ // specifically supported by vCard 2.1. We assume this is same
+ // as "WORK".
+ type = StructuredPostal.TYPE_WORK;
+ label = "";
+ } else if (typeString.equals("PARCEL") ||
+ typeString.equals("DOM") ||
+ typeString.equals("INTL")) {
+ // We do not have any appropriate way to store this information.
+ } else {
+ if (typeString.startsWith("X-") && type < 0) {
+ typeString = typeString.substring(2);
+ }
+ // vCard 3.0 allows iana-token. Also some vCard 2.1 exporters
+ // emit non-standard types. We do not handle their values now.
+ type = StructuredPostal.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ // We use "HOME" as default
+ if (type < 0) {
+ type = StructuredPostal.TYPE_HOME;
+ }
+
+ addPostal(type, propValueList, label, isPrimary);
+ } else if (propName.equals("EMAIL")) {
+ int type = -1;
+ String label = null;
+ boolean isPrimary = false;
+ Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ typeString = typeString.toUpperCase();
+ if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Email) {
+ // Only first "PREF" is considered.
+ mPrefIsSet_Email = true;
+ isPrimary = true;
+ } else if (typeString.equals(Constants.ATTR_TYPE_HOME)) {
+ type = Email.TYPE_HOME;
+ } else if (typeString.equals(Constants.ATTR_TYPE_WORK)) {
+ type = Email.TYPE_WORK;
+ } else if (typeString.equals(Constants.ATTR_TYPE_CELL)) {
+ type = Email.TYPE_MOBILE;
+ } else {
+ if (typeString.startsWith("X-") && type < 0) {
+ typeString = typeString.substring(2);
+ }
+ // vCard 3.0 allows iana-token.
+ // We may have INTERNET (specified in vCard spec),
+ // SCHOOL, etc.
+ type = Email.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ if (type < 0) {
+ type = Email.TYPE_OTHER;
+ }
+ addEmail(type, propValue, label, isPrimary);
+ } else if (propName.equals("ORG")) {
+ // vCard specification does not specify other types.
+ int type = Organization.TYPE_WORK;
+ boolean isPrimary = false;
+
+ Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals(Constants.ATTR_TYPE_PREF) && !mPrefIsSet_Organization) {
+ // vCard specification officially does not have PREF in ORG.
+ // This is just for safety.
+ mPrefIsSet_Organization = true;
+ isPrimary = true;
+ }
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ for (Iterator<String> iter = propValueList.iterator(); iter.hasNext();) {
+ builder.append(iter.next());
+ if (iter.hasNext()) {
+ builder.append(' ');
+ }
+ }
+ addOrganization(type, builder.toString(), "", isPrimary);
+ } else if (propName.equals("TITLE")) {
+ setPosition(propValue);
+ } else if (propName.equals("ROLE")) {
+ setPosition(propValue);
+ } else if (propName.equals("PHOTO") || propName.equals("LOGO")) {
+ String formatName = null;
+ Collection<String> typeCollection = paramMap.get("TYPE");
+ if (typeCollection != null) {
+ formatName = typeCollection.iterator().next();
+ }
+ Collection<String> paramMapValue = paramMap.get("VALUE");
+ if (paramMapValue != null && paramMapValue.contains("URL")) {
+ // Currently we do not have appropriate example for testing this case.
+ } else {
+ addPhotoBytes(formatName, propBytes);
+ }
+ } else if (propName.equals("TEL")) {
+ Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ Object typeObject = VCardUtils.getPhoneTypeFromStrings(typeCollection);
+ final int type;
+ final String label;
+ if (typeObject instanceof Integer) {
+ type = (Integer)typeObject;
+ label = null;
+ } else {
+ type = Phone.TYPE_CUSTOM;
+ label = typeObject.toString();
+ }
+
+ final boolean isPrimary;
+ if (!mPrefIsSet_Phone && typeCollection != null &&
+ typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
+ mPrefIsSet_Phone = true;
+ isPrimary = true;
+ } else {
+ isPrimary = false;
+ }
+ addPhone(type, propValue, label, isPrimary);
+ } else if (propName.equals(Constants.PROPERTY_X_SKYPE_PSTNNUMBER)) {
+ // The phone number available via Skype.
+ Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ // XXX: should use TYPE_CUSTOM + the label "Skype"? (which may need localization)
+ int type = Phone.TYPE_OTHER;
+ final String label = null;
+ final boolean isPrimary;
+ if (!mPrefIsSet_Phone && typeCollection != null &&
+ typeCollection.contains(Constants.ATTR_TYPE_PREF)) {
+ mPrefIsSet_Phone = true;
+ isPrimary = true;
+ } else {
+ isPrimary = false;
+ }
+ addPhone(type, propValue, label, isPrimary);
+ } else if (sImMap.containsKey(propName)){
+ int type = sImMap.get(propName);
+ boolean isPrimary = false;
+ final Collection<String> typeCollection = paramMap.get(Constants.ATTR_TYPE);
+ if (typeCollection != null) {
+ for (String typeString : typeCollection) {
+ if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
+ isPrimary = true;
+ } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_HOME)) {
+ type = Phone.TYPE_HOME;
+ } else if (typeString.equalsIgnoreCase(Constants.ATTR_TYPE_WORK)) {
+ type = Phone.TYPE_WORK;
+ }
+ }
+ }
+ if (type < 0) {
+ type = Phone.TYPE_HOME;
+ }
+ addIm(type, propValue, null, isPrimary);
+ } else if (propName.equals("NOTE")) {
+ addNote(propValue);
+ } else if (propName.equals("URL")) {
+ if (mWebsiteList == null) {
+ mWebsiteList = new ArrayList<String>(1);
+ }
+ mWebsiteList.add(propValue);
+ } else if (propName.equals("X-PHONETIC-FIRST-NAME")) {
+ mPhoneticGivenName = propValue;
+ } else if (propName.equals("X-PHONETIC-MIDDLE-NAME")) {
+ mPhoneticMiddleName = propValue;
+ } else if (propName.equals("X-PHONETIC-LAST-NAME")) {
+ mPhoneticFamilyName = propValue;
+ } else if (propName.equals("BDAY")) {
+ mBirthday = propValue;
+ /*} else if (propName.equals("REV")) {
+ // Revision of this VCard entry. I think we can ignore this.
+ } else if (propName.equals("UID")) {
+ } else if (propName.equals("KEY")) {
+ // Type is X509 or PGP? I don't know how to handle this...
+ } else if (propName.equals("MAILER")) {
+ } else if (propName.equals("TZ")) {
+ } else if (propName.equals("GEO")) {
+ } else if (propName.equals("CLASS")) {
+ // vCard 3.0 only.
+ // e.g. CLASS:CONFIDENTIAL
+ } else if (propName.equals("PROFILE")) {
+ // VCard 3.0 only. Must be "VCARD". I think we can ignore this.
+ } else if (propName.equals("CATEGORIES")) {
+ // VCard 3.0 only.
+ // e.g. CATEGORIES:INTERNET,IETF,INDUSTRY,INFORMATION TECHNOLOGY
+ } else if (propName.equals("SOURCE")) {
+ // VCard 3.0 only.
+ } else if (propName.equals("PRODID")) {
+ // VCard 3.0 only.
+ // To specify the identifier for the product that created
+ // the vCard object.*/
+ } else {
+ // Unknown X- words and IANA token.
+ }
+ }
+
+ public String getDisplayName() {
+ if (mDisplayName == null) {
+ constructDisplayName();
+ }
+ return mDisplayName;
+ }
+
+ /**
+ * Construct the display name. The constructed data must not be null.
+ */
+ private void constructDisplayName() {
+ if (!(TextUtils.isEmpty(mFamilyName) && TextUtils.isEmpty(mGivenName))) {
+ StringBuilder builder = new StringBuilder();
+ List<String> nameList;
+ switch (VCardConfig.getNameOrderType(mVCardType)) {
+ case VCardConfig.NAME_ORDER_JAPANESE:
+ if (VCardUtils.containsOnlyAscii(mFamilyName) &&
+ VCardUtils.containsOnlyAscii(mGivenName)) {
+ nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix);
+ } else {
+ nameList = Arrays.asList(mPrefix, mFamilyName, mMiddleName, mGivenName, mSuffix);
+ }
+ break;
+ case VCardConfig.NAME_ORDER_EUROPE:
+ nameList = Arrays.asList(mPrefix, mMiddleName, mGivenName, mFamilyName, mSuffix);
+ break;
+ default:
+ nameList = Arrays.asList(mPrefix, mGivenName, mMiddleName, mFamilyName, mSuffix);
+ break;
+ }
+ boolean first = true;
+ for (String namePart : nameList) {
+ if (!TextUtils.isEmpty(namePart)) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(' ');
+ }
+ builder.append(namePart);
+ }
+ }
+ mDisplayName = builder.toString();
+ } else if (!TextUtils.isEmpty(mFullName)) {
+ mDisplayName = mFullName;
+ } else if (!(TextUtils.isEmpty(mPhoneticFamilyName) &&
+ TextUtils.isEmpty(mPhoneticGivenName))) {
+ mDisplayName = VCardUtils.constructNameFromElements(mVCardType,
+ mPhoneticFamilyName, mPhoneticMiddleName, mPhoneticGivenName);
+ } else if (mEmailList != null && mEmailList.size() > 0) {
+ mDisplayName = mEmailList.get(0).data;
+ } else if (mPhoneList != null && mPhoneList.size() > 0) {
+ mDisplayName = mPhoneList.get(0).data;
+ } else if (mPostalList != null && mPostalList.size() > 0) {
+ mDisplayName = mPostalList.get(0).getFormattedAddress(mVCardType);
+ }
+
+ if (mDisplayName == null) {
+ mDisplayName = "";
+ }
+ }
+
+ /**
+ * Consolidate several fielsds (like mName) using name candidates,
+ */
+ public void consolidateFields() {
+ constructDisplayName();
+
+ if (mPhoneticFullName != null) {
+ mPhoneticFullName = mPhoneticFullName.trim();
+ }
+
+ // If there is no "PREF", we choose the first entries as primary.
+ if (!mPrefIsSet_Phone && mPhoneList != null && mPhoneList.size() > 0) {
+ mPhoneList.get(0).isPrimary = true;
+ }
+
+ if (!mPrefIsSet_Address && mPostalList != null && mPostalList.size() > 0) {
+ mPostalList.get(0).isPrimary = true;
+ }
+ if (!mPrefIsSet_Email && mEmailList != null && mEmailList.size() > 0) {
+ mEmailList.get(0).isPrimary = true;
+ }
+ if (!mPrefIsSet_Organization && mOrganizationList != null && mOrganizationList.size() > 0) {
+ mOrganizationList.get(0).isPrimary = true;
+ }
+ }
+
+ public void pushIntoContentResolver(ContentResolver resolver) {
+ ArrayList<ContentProviderOperation> operationList =
+ new ArrayList<ContentProviderOperation>();
+ ContentProviderOperation.Builder builder =
+ ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
+ builder.withValues(new ContentValues());
+ operationList.add(builder.build());
+
+ {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+
+ builder.withValue(StructuredName.GIVEN_NAME, mGivenName);
+ builder.withValue(StructuredName.FAMILY_NAME, mFamilyName);
+ builder.withValue(StructuredName.MIDDLE_NAME, mMiddleName);
+ builder.withValue(StructuredName.PREFIX, mPrefix);
+ builder.withValue(StructuredName.SUFFIX, mSuffix);
+
+ builder.withValue(StructuredName.PHONETIC_GIVEN_NAME, mPhoneticGivenName);
+ builder.withValue(StructuredName.PHONETIC_FAMILY_NAME, mPhoneticFamilyName);
+ builder.withValue(StructuredName.PHONETIC_MIDDLE_NAME, mPhoneticMiddleName);
+
+ builder.withValue(StructuredName.DISPLAY_NAME, getDisplayName());
+ operationList.add(builder.build());
+ }
+
+ if (mNickNameList != null && mNickNameList.size() > 0) {
+ boolean first = true;
+ for (String nickName : mNickNameList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Nickname.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
+
+ builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
+ builder.withValue(Nickname.NAME, nickName);
+ if (first) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ first = false;
+ }
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mPhoneList != null) {
+ for (PhoneData phoneData : mPhoneList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+
+ builder.withValue(Phone.TYPE, phoneData.type);
+ if (phoneData.type == Phone.TYPE_CUSTOM) {
+ builder.withValue(Phone.LABEL, phoneData.label);
+ }
+ builder.withValue(Phone.NUMBER, phoneData.data);
+ if (phoneData.isPrimary) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ }
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mOrganizationList != null) {
+ boolean first = true;
+ for (OrganizationData organizationData : mOrganizationList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Organization.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
+
+ // Currently, we do not use TYPE_CUSTOM.
+ builder.withValue(Organization.TYPE, organizationData.type);
+ builder.withValue(Organization.COMPANY, organizationData.companyName);
+ builder.withValue(Organization.TITLE, organizationData.positionName);
+ if (first) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ }
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mEmailList != null) {
+ for (EmailData emailData : mEmailList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Email.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+
+ builder.withValue(Email.TYPE, emailData.type);
+ if (emailData.type == Email.TYPE_CUSTOM) {
+ builder.withValue(Email.LABEL, emailData.label);
+ }
+ builder.withValue(Email.DATA, emailData.data);
+ if (emailData.isPrimary) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ }
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mPostalList != null) {
+ for (PostalData postalData : mPostalList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ VCardUtils.insertStructuredPostalDataUsingContactsStruct(
+ mVCardType, builder, postalData);
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mImList != null) {
+ for (ImData imData : mImList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Im.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
+
+ builder.withValue(Im.TYPE, imData.type);
+ if (imData.type == Im.TYPE_CUSTOM) {
+ builder.withValue(Im.LABEL, imData.label);
+ }
+ builder.withValue(Im.DATA, imData.data);
+ if (imData.isPrimary) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ }
+ }
+ }
+
+ if (mNoteList != null) {
+ for (String note : mNoteList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Note.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
+
+ builder.withValue(Note.NOTE, note);
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mPhotoList != null) {
+ boolean first = true;
+ for (PhotoData photoData : mPhotoList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Photo.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
+ builder.withValue(Photo.PHOTO, photoData.photoBytes);
+ if (first) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ first = false;
+ }
+ operationList.add(builder.build());
+ }
+ }
+
+ if (mWebsiteList != null) {
+ for (String website : mWebsiteList) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Website.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE);
+ builder.withValue(Website.URL, website);
+ operationList.add(builder.build());
+ }
+ }
+
+ if (!TextUtils.isEmpty(mBirthday)) {
+ builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
+ builder.withValueBackReference(Miscellaneous.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, Miscellaneous.CONTENT_ITEM_TYPE);
+ builder.withValue(Miscellaneous.BIRTHDAY, mBirthday);
+ operationList.add(builder.build());
+ }
+
+ try {
+ resolver.applyBatch(ContactsContract.AUTHORITY, operationList);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ } catch (OperationApplicationException e) {
+ Log.e(LOG_TAG, String.format("%s: %s", e.toString(), e.getMessage()));
+ }
+ }
+
+ public boolean isIgnorable() {
+ return getDisplayName().length() == 0;
+ }
+
+ private String listToString(List<String> list){
+ final int size = list.size();
+ if (size > 1) {
+ StringBuilder builder = new StringBuilder();
+ int i = 0;
+ for (String type : list) {
+ builder.append(type);
+ if (i < size - 1) {
+ builder.append(";");
+ }
+ }
+ return builder.toString();
+ } else if (size == 1) {
+ return list.get(0);
+ } else {
+ return "";
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/EntryCommitter.java b/core/java/android/pim/vcard/EntryCommitter.java
new file mode 100644
index 0000000..3f1655d
--- /dev/null
+++ b/core/java/android/pim/vcard/EntryCommitter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.content.ContentResolver;
+import android.util.Log;
+
+/**
+ * EntryHandler implementation which commits the entry to Contacts Provider
+ */
+public class EntryCommitter implements EntryHandler {
+ public static String LOG_TAG = "vcard.EntryComitter";
+
+ private ContentResolver mContentResolver;
+ private long mTimeToCommit;
+
+ public EntryCommitter(ContentResolver resolver) {
+ mContentResolver = resolver;
+ }
+
+ public void onParsingStart() {
+ }
+
+ public void onParsingEnd() {
+ if (VCardConfig.showPerformanceLog()) {
+ Log.d(LOG_TAG, String.format("time to commit entries: %d ms", mTimeToCommit));
+ }
+ }
+
+ public void onEntryCreated(final ContactStruct contactStruct) {
+ long start = System.currentTimeMillis();
+ contactStruct.pushIntoContentResolver(mContentResolver);
+ mTimeToCommit += System.currentTimeMillis() - start;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/EntryHandler.java b/core/java/android/pim/vcard/EntryHandler.java
new file mode 100644
index 0000000..7fb8114
--- /dev/null
+++ b/core/java/android/pim/vcard/EntryHandler.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+/**
+ * Unlike {@link VCardBuilder}, this (and {@link VCardDataBuilder}) assumes
+ * "each VCard entry should be correctly parsed and passed to each EntryHandler object",
+ */
+public interface EntryHandler {
+ /**
+ * Called when the parsing started.
+ */
+ public void onParsingStart();
+
+ /**
+ * The method called when one VCard entry is successfully created
+ */
+ public void onEntryCreated(final ContactStruct entry);
+
+ /**
+ * Called when the parsing ended.
+ * Able to be use this method for showing performance log, etc.
+ */
+ public void onParsingEnd();
+}
diff --git a/core/java/android/pim/vcard/VCardBuilder.java b/core/java/android/pim/vcard/VCardBuilder.java
new file mode 100644
index 0000000..e1c4b33
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import java.util.List;
+
+public interface VCardBuilder {
+ void start();
+
+ void end();
+
+ /**
+ * BEGIN:VCARD
+ */
+ void startRecord(String type);
+
+ /** END:VXX */
+ void endRecord();
+
+ void startProperty();
+
+ void endProperty();
+
+ /**
+ * @param group
+ */
+ void propertyGroup(String group);
+
+ /**
+ * @param name
+ * N <br>
+ * N
+ */
+ void propertyName(String name);
+
+ /**
+ * @param type
+ * LANGUAGE \ ENCODING <br>
+ * ;LANGUage= \ ;ENCODING=
+ */
+ void propertyParamType(String type);
+
+ /**
+ * @param value
+ * FR-EN \ GBK <br>
+ * FR-EN \ GBK
+ */
+ void propertyParamValue(String value);
+
+ void propertyValues(List<String> values);
+}
diff --git a/core/java/android/pim/vcard/VCardBuilderCollection.java b/core/java/android/pim/vcard/VCardBuilderCollection.java
new file mode 100644
index 0000000..e3985b6
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardBuilderCollection.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import java.util.Collection;
+import java.util.List;
+
+public class VCardBuilderCollection implements VCardBuilder {
+
+ private final Collection<VCardBuilder> mVCardBuilderCollection;
+
+ public VCardBuilderCollection(Collection<VCardBuilder> vBuilderCollection) {
+ mVCardBuilderCollection = vBuilderCollection;
+ }
+
+ public Collection<VCardBuilder> getVCardBuilderBaseCollection() {
+ return mVCardBuilderCollection;
+ }
+
+ public void start() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.start();
+ }
+ }
+
+ public void end() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.end();
+ }
+ }
+
+ public void startRecord(String type) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.startRecord(type);
+ }
+ }
+
+ public void endRecord() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.endRecord();
+ }
+ }
+
+ public void startProperty() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.startProperty();
+ }
+ }
+
+
+ public void endProperty() {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.endProperty();
+ }
+ }
+
+ public void propertyGroup(String group) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyGroup(group);
+ }
+ }
+
+ public void propertyName(String name) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyName(name);
+ }
+ }
+
+ public void propertyParamType(String type) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyParamType(type);
+ }
+ }
+
+ public void propertyParamValue(String value) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyParamValue(value);
+ }
+ }
+
+ public void propertyValues(List<String> values) {
+ for (VCardBuilder builder : mVCardBuilderCollection) {
+ builder.propertyValues(values);
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java
new file mode 100644
index 0000000..3c01a9e
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardComposer.java
@@ -0,0 +1,1445 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.pim.vcard;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.Entity.NamedContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.os.RemoteException;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Miscellaneous;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.text.TextUtils;
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * The class for composing VCard from Contacts information. Note that this is
+ * completely differnt implementation from
+ * android.syncml.pim.vcard.VCardComposer, which is not maintained anymore.
+ * </p>
+ *
+ * <p>
+ * Usually, this class should be used like this.
+ * </p>
+ *
+ * <pre class="prettyprint"> VCardComposer composer = null; try { composer = new
+ * VCardComposer(context); composer.addHandler(composer.new
+ * HandlerForOutputStream(outputStream)); if (!composer.init()) { // Do
+ * something handling the situation. return; } while (!composer.isAfterLast()) {
+ * if (mCanceled) { // Assume a user may cancel this operation during the
+ * export. return; } if (!composer.createOneEntry()) { // Do something handling
+ * the error situation. return; } } } finally { if (composer != null) {
+ * composer.terminate(); } } </pre>
+ */
+public class VCardComposer {
+ private static final String LOG_TAG = "vcard.VCardComposer";
+
+ public static interface OneEntryHandler {
+ public boolean onInit(Context context);
+
+ public boolean onEntryCreated(String vcard);
+
+ public void onTerminate();
+ }
+
+ /**
+ * <p>
+ * An useful example handler, which emits VCard String to outputstream one
+ * by one.
+ * </p>
+ * <p>
+ * The input OutputStream object is closed() on {{@link #onTerminate()}.
+ * Must not close the stream outside.
+ * </p>
+ */
+ public class HandlerForOutputStream implements OneEntryHandler {
+ @SuppressWarnings("hiding")
+ private static final String LOG_TAG = "vcard.VCardComposer.HandlerForOutputStream";
+
+ private OutputStream mOutputStream; // mWriter will close this.
+ private Writer mWriter;
+
+ private boolean mOnTerminateIsCalled = false;
+
+ /**
+ * Input stream will be closed on the detruction of this object.
+ */
+ public HandlerForOutputStream(OutputStream outputStream) {
+ mOutputStream = outputStream;
+ }
+
+ public boolean onInit(Context context) {
+ try {
+ mWriter = new BufferedWriter(new OutputStreamWriter(
+ mOutputStream, mCharsetString));
+ } catch (UnsupportedEncodingException e1) {
+ Log.e(LOG_TAG, "Unsupported charset: " + mCharsetString);
+ mErrorReason = "Encoding is not supported (usually this does not happen!): "
+ + mCharsetString;
+ return false;
+ }
+
+ if (mIsDoCoMo) {
+ try {
+ // Create one empty entry.
+ mWriter.write(createOneEntryInternal("-1"));
+ } catch (IOException e) {
+ Log.e(LOG_TAG,
+ "IOException occurred during exportOneContactData: "
+ + e.getMessage());
+ mErrorReason = "IOException occurred: " + e.getMessage();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean onEntryCreated(String vcard) {
+ try {
+ mWriter.write(vcard);
+ } catch (IOException e) {
+ Log.e(LOG_TAG,
+ "IOException occurred during exportOneContactData: "
+ + e.getMessage());
+ mErrorReason = "IOException occurred: " + e.getMessage();
+ return false;
+ }
+ return true;
+ }
+
+ public void onTerminate() {
+ mOnTerminateIsCalled = true;
+ if (mWriter != null) {
+ try {
+ // Flush and sync the data so that a user is able to pull
+ // the SDCard just after
+ // the export.
+ mWriter.flush();
+ if (mOutputStream != null
+ && mOutputStream instanceof FileOutputStream) {
+ ((FileOutputStream) mOutputStream).getFD().sync();
+ }
+ } catch (IOException e) {
+ Log.d(LOG_TAG,
+ "IOException during closing the output stream: "
+ + e.getMessage());
+ } finally {
+ try {
+ mWriter.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public void finalize() {
+ if (!mOnTerminateIsCalled) {
+ onTerminate();
+ }
+ }
+ }
+
+ public static final String VCARD_TYPE_STRING_DOCOMO = "docomo";
+
+ private static final String VCARD_PROPERTY_ADR = "ADR";
+ private static final String VCARD_PROPERTY_BEGIN = "BEGIN";
+ private static final String VCARD_PROPERTY_EMAIL = "EMAIL";
+ private static final String VCARD_PROPERTY_END = "END";
+ private static final String VCARD_PROPERTY_NAME = "N";
+ private static final String VCARD_PROPERTY_FULL_NAME = "FN";
+ private static final String VCARD_PROPERTY_NOTE = "NOTE";
+ private static final String VCARD_PROPERTY_ORG = "ORG";
+ private static final String VCARD_PROPERTY_SOUND = "SOUND";
+ private static final String VCARD_PROPERTY_SORT_STRING = "SORT-STRING";
+ private static final String VCARD_PROPERTY_NICKNAME = "NICKNAME";
+ private static final String VCARD_PROPERTY_TEL = "TEL";
+ private static final String VCARD_PROPERTY_TITLE = "TITLE";
+ private static final String VCARD_PROPERTY_PHOTO = "PHOTO";
+ private static final String VCARD_PROPERTY_VERSION = "VERSION";
+ private static final String VCARD_PROPERTY_URL = "URL";
+ private static final String VCARD_PROPERTY_BIRTHDAY = "BDAY";
+
+ private static final String VCARD_PROPERTY_X_PHONETIC_FIRST_NAME = "X-PHONETIC-FIRST-NAME";
+ private static final String VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME = "X-PHONETIC-MIDDLE-NAME";
+ private static final String VCARD_PROPERTY_X_PHONETIC_LAST_NAME = "X-PHONETIC-LAST-NAME";
+
+ // Android specific properties
+ private static final String VCARD_PROPERTY_X_PHONETIC_NAME = "X-PHONETIC-NAME";
+ private static final String VCARD_PROPERTY_X_NICKNAME = "X-NICKNAME";
+ // TODO: add properties like X-LATITUDE
+
+ // Properties for DoCoMo vCard.
+ private static final String VCARD_PROPERTY_X_CLASS = "X-CLASS";
+ private static final String VCARD_PROPERTY_X_REDUCTION = "X-REDUCTION";
+ private static final String VCARD_PROPERTY_X_NO = "X-NO";
+ private static final String VCARD_PROPERTY_X_DCM_HMN_MODE = "X-DCM-HMN-MODE";
+
+ private static final String VCARD_DATA_VCARD = "VCARD";
+ private static final String VCARD_DATA_PUBLIC = "PUBLIC";
+
+ private static final String VCARD_ATTR_SEPARATOR = ";";
+ private static final String VCARD_COL_SEPARATOR = "\r\n";
+ private static final String VCARD_DATA_SEPARATOR = ":";
+ private static final String VCARD_ITEM_SEPARATOR = ";";
+ private static final String VCARD_WS = " ";
+
+ // Type strings are now in VCardConstants.java.
+
+ private static final String VCARD_ATTR_ENCODING_QP = "ENCODING=QUOTED-PRINTABLE";
+
+ private static final String VCARD_ATTR_ENCODING_BASE64_V21 = "ENCODING=BASE64";
+ private static final String VCARD_ATTR_ENCODING_BASE64_V30 = "ENCODING=b";
+
+ private static final String SHIFT_JIS = "SHIFT_JIS";
+
+ private final Context mContext;
+ private final int mVCardType;
+ private final boolean mCareHandlerErrors;
+ private final ContentResolver mContentResolver;
+
+ // Convenient member variables about the restriction of the vCard format.
+ // Used for not calling the same methods returning same results.
+ private final boolean mIsV30;
+ private final boolean mIsJapaneseMobilePhone;
+ private final boolean mOnlyOneNoteFieldIsAvailable;
+ private final boolean mIsDoCoMo;
+ private final boolean mUsesQuotedPrintable;
+ private final boolean mUsesAndroidProperty;
+ private final boolean mUsesDefactProperty;
+ private final boolean mUsesShiftJis;
+
+ private Cursor mCursor;
+ private int mIdColumn;
+
+ private String mCharsetString;
+ private static String mVCardAttributeCharset;
+ private boolean mTerminateIsCalled;
+ private List<OneEntryHandler> mHandlerList;
+
+ private String mErrorReason = "No error";
+
+ private static final Map<Integer, String> sImMap;
+
+ static {
+ sImMap = new HashMap<Integer, String>();
+ sImMap.put(Im.PROTOCOL_AIM, Constants.PROPERTY_X_AIM);
+ sImMap.put(Im.PROTOCOL_MSN, Constants.PROPERTY_X_MSN);
+ sImMap.put(Im.PROTOCOL_YAHOO, Constants.PROPERTY_X_YAHOO);
+ sImMap.put(Im.PROTOCOL_ICQ, Constants.PROPERTY_X_ICQ);
+ sImMap.put(Im.PROTOCOL_JABBER, Constants.PROPERTY_X_JABBER);
+ sImMap.put(Im.PROTOCOL_SKYPE, Constants.PROPERTY_X_SKYPE_USERNAME);
+ // Google talk is a special case.
+ }
+
+
+ public VCardComposer(Context context) {
+ this(context, VCardConfig.VCARD_TYPE_DEFAULT, true);
+ }
+
+ public VCardComposer(Context context, String vcardTypeStr,
+ boolean careHandlerErrors) {
+ this(context, VCardConfig.getVCardTypeFromString(vcardTypeStr),
+ careHandlerErrors);
+ }
+
+ public VCardComposer(Context context, int vcardType, boolean careHandlerErrors) {
+ mContext = context;
+ mVCardType = vcardType;
+ mCareHandlerErrors = careHandlerErrors;
+ mContentResolver = context.getContentResolver();
+
+ mIsV30 = VCardConfig.isV30(vcardType);
+ mUsesQuotedPrintable = VCardConfig.usesQuotedPrintable(vcardType);
+ mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
+ mIsJapaneseMobilePhone = VCardConfig
+ .needsToConvertPhoneticString(vcardType);
+ mOnlyOneNoteFieldIsAvailable = VCardConfig
+ .onlyOneNoteFieldIsAvailable(vcardType);
+ mUsesAndroidProperty = VCardConfig
+ .usesAndroidSpecificProperty(vcardType);
+ mUsesDefactProperty = VCardConfig.usesDefactProperty(vcardType);
+ mUsesShiftJis = VCardConfig.usesShiftJis(vcardType);
+
+ if (mIsDoCoMo) {
+ mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS, "docomo").name();
+ // Do not use mCharsetString bellow since it is different from "SHIFT_JIS" but
+ // may be "DOCOMO_SHIFT_JIS" or something like that (internal expression used in
+ // Android, not shown to the public).
+ mVCardAttributeCharset = "CHARSET=" + SHIFT_JIS;
+ } else if (mUsesShiftJis) {
+ mCharsetString = CharsetUtils.charsetForVendor(SHIFT_JIS).name();
+ mVCardAttributeCharset = "CHARSET=" + SHIFT_JIS;
+ } else {
+ mCharsetString = "UTF-8";
+ mVCardAttributeCharset = "CHARSET=UTF-8";
+ }
+ }
+
+ /**
+ * Must call before {{@link #init()}.
+ */
+ public void addHandler(OneEntryHandler handler) {
+ if (mHandlerList == null) {
+ mHandlerList = new ArrayList<OneEntryHandler>();
+ }
+ mHandlerList.add(handler);
+ }
+
+ public boolean init() {
+ return init(null, null);
+ }
+
+ /**
+ * @return Returns true when initialization is successful and all the other
+ * methods are available. Returns false otherwise.
+ */
+ public boolean init(final String selection, final String[] selectionArgs) {
+ if (mCareHandlerErrors) {
+ List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
+ mHandlerList.size());
+ for (OneEntryHandler handler : mHandlerList) {
+ if (!handler.onInit(mContext)) {
+ for (OneEntryHandler finished : finishedList) {
+ finished.onTerminate();
+ }
+ return false;
+ }
+ }
+ } else {
+ // Just ignore the false returned from onInit().
+ for (OneEntryHandler handler : mHandlerList) {
+ handler.onInit(mContext);
+ }
+ }
+
+ final String[] projection = new String[] {Contacts._ID,};
+
+ // TODO: thorow an appropriate exception!
+ mCursor = mContentResolver.query(RawContacts.CONTENT_URI, projection,
+ selection, selectionArgs, null);
+ if (mCursor == null || !mCursor.moveToFirst()) {
+ if (mCursor != null) {
+ try {
+ mCursor.close();
+ } catch (SQLiteException e) {
+ Log.e(LOG_TAG, "SQLiteException on Cursor#close(): "
+ + e.getMessage());
+ }
+ mCursor = null;
+ }
+ mErrorReason = "Getting database information failed.";
+ return false;
+ }
+
+ mIdColumn = mCursor.getColumnIndex(Contacts._ID);
+
+ return true;
+ }
+
+ public boolean createOneEntry() {
+ if (mCursor == null || mCursor.isAfterLast()) {
+ // TODO: ditto
+ mErrorReason = "Not initialized or database has some problem.";
+ return false;
+ }
+ String name = null;
+ String vcard;
+ try {
+ vcard = createOneEntryInternal(mCursor.getString(mIdColumn));
+ } catch (OutOfMemoryError error) {
+ // Maybe some data (e.g. photo) is too big to have in memory. But it
+ // should be rare.
+ Log.e(LOG_TAG, "OutOfMemoryError occured. Ignore the entry: "
+ + name);
+ System.gc();
+ // TODO: should tell users what happened?
+ return true;
+ } finally {
+ mCursor.moveToNext();
+ }
+
+ // This function does not care the OutOfMemoryError on the handler side
+ // :-P
+ if (mCareHandlerErrors) {
+ List<OneEntryHandler> finishedList = new ArrayList<OneEntryHandler>(
+ mHandlerList.size());
+ for (OneEntryHandler handler : mHandlerList) {
+ if (!handler.onEntryCreated(vcard)) {
+ return false;
+ }
+ }
+ } else {
+ for (OneEntryHandler handler : mHandlerList) {
+ handler.onEntryCreated(vcard);
+ }
+ }
+
+ return true;
+ }
+
+ private String createOneEntryInternal(final String contactId) {
+ final StringBuilder builder = new StringBuilder();
+ appendVCardLine(builder, VCARD_PROPERTY_BEGIN, VCARD_DATA_VCARD);
+ if (mIsV30) {
+ appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V30);
+ } else {
+ appendVCardLine(builder, VCARD_PROPERTY_VERSION, Constants.VERSION_V21);
+ }
+
+ final Map<String, List<ContentValues>> contentValuesListMap =
+ new HashMap<String, List<ContentValues>>();
+
+ final String selection = Data.RAW_CONTACT_ID + "=?";
+ final String[] selectionArgs = new String[] {contactId};
+ EntityIterator entityIterator = null;
+ try {
+ entityIterator = mContentResolver.queryEntities(
+ RawContacts.CONTENT_URI, selection, selectionArgs, null);
+ while (entityIterator.hasNext()) {
+ Entity entity = entityIterator.next();
+ for (NamedContentValues namedContentValues : entity
+ .getSubValues()) {
+ ContentValues contentValues = namedContentValues.values;
+ String key = contentValues.getAsString(Data.MIMETYPE);
+ if (key != null) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(key);
+ if (contentValuesList == null) {
+ contentValuesList = new ArrayList<ContentValues>();
+ contentValuesListMap.put(key, contentValuesList);
+ }
+ contentValuesList.add(contentValues);
+ }
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, String.format("RemoteException at id %s (%s)",
+ contactId, e.getMessage()));
+ return "";
+ } finally {
+ if (entityIterator != null) {
+ entityIterator.close();
+ }
+ }
+
+ // TODO: consolidate order? (low priority)
+ appendStructuredNames(builder, contentValuesListMap);
+ appendNickNames(builder, contentValuesListMap);
+ appendPhones(builder, contentValuesListMap);
+ appendEmails(builder, contentValuesListMap);
+ appendPostals(builder, contentValuesListMap);
+ appendIms(builder, contentValuesListMap);
+ appendWebsites(builder, contentValuesListMap);
+ appendBirthday(builder, contentValuesListMap);
+ appendOrganizations(builder, contentValuesListMap);
+ appendPhotos(builder, contentValuesListMap);
+ appendNotes(builder, contentValuesListMap);
+ // TODO: GroupMembership... What?
+
+ if (mIsDoCoMo) {
+ appendVCardLine(builder, VCARD_PROPERTY_X_CLASS, VCARD_DATA_PUBLIC);
+ appendVCardLine(builder, VCARD_PROPERTY_X_REDUCTION, "");
+ appendVCardLine(builder, VCARD_PROPERTY_X_NO, "");
+ appendVCardLine(builder, VCARD_PROPERTY_X_DCM_HMN_MODE, "");
+ }
+
+ appendVCardLine(builder, VCARD_PROPERTY_END, VCARD_DATA_VCARD);
+
+ return builder.toString();
+ }
+
+ public void terminate() {
+ for (OneEntryHandler handler : mHandlerList) {
+ handler.onTerminate();
+ }
+
+ if (mCursor != null) {
+ try {
+ mCursor.close();
+ } catch (SQLiteException e) {
+ Log.e(LOG_TAG, "SQLiteException on Cursor#close(): "
+ + e.getMessage());
+ }
+ mCursor = null;
+ }
+
+ mTerminateIsCalled = true;
+ }
+
+ @Override
+ public void finalize() {
+ if (!mTerminateIsCalled) {
+ terminate();
+ }
+ }
+
+ public int getCount() {
+ if (mCursor == null) {
+ return 0;
+ }
+ return mCursor.getCount();
+ }
+
+ public boolean isAfterLast() {
+ if (mCursor == null) {
+ return false;
+ }
+ return mCursor.isAfterLast();
+ }
+
+ /**
+ * @return Return the error reason if possible.
+ */
+ public String getErrorReason() {
+ return mErrorReason;
+ }
+
+ private void appendStructuredNames(StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(StructuredName.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ appendStructuredNamesInternal(builder, contentValuesList);
+ } else if (mIsDoCoMo) {
+ appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
+ }
+ }
+
+ private void appendStructuredNamesInternal(final StringBuilder builder,
+ final List<ContentValues> contentValuesList) {
+ for (ContentValues contentValues : contentValuesList) {
+ final String familyName = contentValues
+ .getAsString(StructuredName.FAMILY_NAME);
+ final String middleName = contentValues
+ .getAsString(StructuredName.MIDDLE_NAME);
+ final String givenName = contentValues
+ .getAsString(StructuredName.GIVEN_NAME);
+ final String prefix = contentValues
+ .getAsString(StructuredName.PREFIX);
+ final String suffix = contentValues
+ .getAsString(StructuredName.SUFFIX);
+ final String displayName = contentValues
+ .getAsString(StructuredName.DISPLAY_NAME);
+
+ // For now, some primary element is not encoded into Quoted-Printable, which is not
+ // valid in vCard spec strictly. In the future, we may have to have some flag to
+ // enable composer to encode these primary field into Quoted-Printable.
+ if (!TextUtils.isEmpty(familyName) || !TextUtils.isEmpty(givenName)) {
+ final String encodedFamily = escapeCharacters(familyName);
+ final String encodedGiven = escapeCharacters(givenName);
+ final String encodedMiddle = escapeCharacters(middleName);
+ final String encodedPrefix = escapeCharacters(prefix);
+ final String encodedSuffix = escapeCharacters(suffix);
+
+ // N property. This order is specified by vCard spec and does not depend on countries.
+ builder.append(VCARD_PROPERTY_NAME);
+ if (!(VCardUtils.containsOnlyAscii(familyName) &&
+ VCardUtils.containsOnlyAscii(givenName) &&
+ VCardUtils.containsOnlyAscii(middleName) &&
+ VCardUtils.containsOnlyAscii(prefix) &&
+ VCardUtils.containsOnlyAscii(suffix))) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedFamily);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedGiven);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedMiddle);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedPrefix);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(encodedSuffix);
+ builder.append(VCARD_COL_SEPARATOR);
+
+ final String encodedFullname = VCardUtils.constructNameFromElements(
+ VCardConfig.getNameOrderType(mVCardType),
+ encodedFamily, encodedMiddle, encodedGiven, encodedPrefix, encodedSuffix);
+
+ // FN property
+ builder.append(VCARD_PROPERTY_FULL_NAME);
+ if (!VCardUtils.containsOnlyAscii(encodedFullname)) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedFullname);
+ builder.append(VCARD_COL_SEPARATOR);
+ } else if (!TextUtils.isEmpty(displayName)) {
+ builder.append(VCARD_PROPERTY_NAME);
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(escapeCharacters(displayName));
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_COL_SEPARATOR);
+ } else if (mIsDoCoMo) {
+ appendVCardLine(builder, VCARD_PROPERTY_NAME, "");
+ }
+
+ String phoneticFamilyName = contentValues
+ .getAsString(StructuredName.PHONETIC_FAMILY_NAME);
+ String phoneticMiddleName = contentValues
+ .getAsString(StructuredName.PHONETIC_MIDDLE_NAME);
+ String phoneticGivenName = contentValues
+ .getAsString(StructuredName.PHONETIC_GIVEN_NAME);
+ if (!(TextUtils.isEmpty(phoneticFamilyName)
+ && TextUtils.isEmpty(phoneticMiddleName) && TextUtils
+ .isEmpty(phoneticGivenName))) { // if not empty
+ if (mIsJapaneseMobilePhone) {
+ phoneticFamilyName = VCardUtils
+ .toHalfWidthString(phoneticFamilyName);
+ phoneticMiddleName = VCardUtils
+ .toHalfWidthString(phoneticMiddleName);
+ phoneticGivenName = VCardUtils
+ .toHalfWidthString(phoneticGivenName);
+ }
+
+ if (mIsV30) {
+ final String sortString = VCardUtils
+ .constructNameFromElements(mVCardType,
+ phoneticFamilyName, phoneticMiddleName,
+ phoneticGivenName);
+ builder.append(VCARD_PROPERTY_SORT_STRING);
+
+ if (!VCardUtils.containsOnlyAscii(sortString)) {
+ // Strictly, adding charset information is NOT valid in
+ // VCard 3.0,
+ // but we'll add this info since parser side may be able to
+ // use the charset via
+ // this attribute field.
+ //
+ // e.g. Japanese mobile phones use Shift_Jis while RFC 2426
+ // recommends
+ // UTF-8. By adding this field, parsers may be able to know
+ // this text
+ // is NOT UTF-8 but Shift_Jis.
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(sortString);
+ builder.append(VCARD_COL_SEPARATOR);
+ } else {
+ // Note: There is no appropriate property for expressing
+ // phonetic name in
+ // VCard 2.1, while there is in VCard 3.0 (SORT-STRING).
+ // We chose to use DoCoMo's way since it is supported by a
+ // lot of
+ // Japanese mobile phones.
+ //
+ // TODO: should use Quoted-Pritable?
+ builder.append(VCARD_PROPERTY_SOUND);
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(Constants.ATTR_TYPE_X_IRMC_N);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ if (!(VCardUtils.containsOnlyAscii(phoneticFamilyName) &&
+ VCardUtils.containsOnlyAscii(phoneticMiddleName) &&
+ VCardUtils.containsOnlyAscii(phoneticGivenName))) {
+ builder.append(mVCardAttributeCharset);
+ builder.append(VCARD_DATA_SEPARATOR);
+ }
+
+ builder.append(escapeCharacters(phoneticFamilyName));
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(escapeCharacters(phoneticMiddleName));
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(escapeCharacters(phoneticGivenName));
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_COL_SEPARATOR);
+
+ if (mUsesAndroidProperty) {
+ final String phoneticName = VCardUtils
+ .constructNameFromElements(mVCardType,
+ phoneticFamilyName, phoneticMiddleName,
+ phoneticGivenName);
+ builder.append(VCARD_PROPERTY_X_PHONETIC_NAME);
+
+ if (!VCardUtils.containsOnlyAscii(phoneticName)) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ // TODO: may need to make the text quoted-printable.
+ builder.append(phoneticName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ }
+ } else if (mIsDoCoMo) {
+ builder.append(VCARD_PROPERTY_SOUND);
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(Constants.ATTR_TYPE_X_IRMC_N);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ if (mUsesDefactProperty) {
+ if (!TextUtils.isEmpty(phoneticGivenName)) {
+ builder.append(VCARD_PROPERTY_X_PHONETIC_FIRST_NAME);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(phoneticGivenName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ if (!TextUtils.isEmpty(phoneticMiddleName)) {
+ builder.append(VCARD_PROPERTY_X_PHONETIC_MIDDLE_NAME);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(phoneticMiddleName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ if (!TextUtils.isEmpty(phoneticFamilyName)) {
+ builder.append(VCARD_PROPERTY_X_PHONETIC_LAST_NAME);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(phoneticFamilyName);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ }
+ }
+ }
+
+ private void appendNickNames(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Nickname.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ final String propertyNickname;
+ if (mIsV30) {
+ propertyNickname = VCARD_PROPERTY_NICKNAME;
+ } else if (mUsesAndroidProperty) {
+ propertyNickname = VCARD_PROPERTY_X_NICKNAME;
+ } else {
+ // There's no way to add this field.
+ return;
+ }
+
+ for (ContentValues contentValues : contentValuesList) {
+ final String nickname = contentValues
+ .getAsString(Nickname.NAME);
+ if (TextUtils.isEmpty(nickname)) {
+ continue;
+ }
+ builder.append(propertyNickname);
+
+ if (!VCardUtils.containsOnlyAscii(propertyNickname)) {
+ // Strictly, this is not valid in vCard 3.0. See above.
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(escapeCharacters(nickname));
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ }
+ }
+
+ private void appendPhones(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Phone.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ appendVCardTelephoneLine(builder, contentValues
+ .getAsInteger(Phone.TYPE), contentValues
+ .getAsString(Phone.LABEL), contentValues
+ .getAsString(Phone.NUMBER));
+ }
+ } else if (mIsDoCoMo) {
+ appendVCardTelephoneLine(builder, Phone.TYPE_HOME, "", "");
+ }
+ }
+
+ private void appendEmails(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Email.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ appendVCardEmailLine(builder, contentValues
+ .getAsInteger(Email.TYPE), contentValues
+ .getAsString(Email.LABEL), contentValues
+ .getAsString(Email.DATA));
+ }
+ } else if (mIsDoCoMo) {
+ appendVCardEmailLine(builder, Email.TYPE_HOME, "", "");
+ }
+ }
+
+ private void appendPostals(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(StructuredPostal.CONTENT_ITEM_TYPE);
+
+ if (contentValuesList != null) {
+ if (mIsDoCoMo) {
+ appendPostalsForDoCoMo(builder, contentValuesList);
+ } else {
+ appendPostalsForGeneric(builder, contentValuesList);
+ }
+ } else if (mIsDoCoMo) {
+ builder.append(VCARD_PROPERTY_ADR);
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(Constants.ATTR_TYPE_HOME);
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+ }
+
+ /**
+ * Try to append just one line. If there's no appropriate address
+ * information, append an empty line.
+ */
+ private void appendPostalsForDoCoMo(final StringBuilder builder,
+ final List<ContentValues> contentValuesList) {
+ // TODO: from old, inefficient code. fix this.
+ if (appendPostalsForDoCoMoInternal(builder, contentValuesList,
+ StructuredPostal.TYPE_HOME)) {
+ return;
+ }
+ if (appendPostalsForDoCoMoInternal(builder, contentValuesList,
+ StructuredPostal.TYPE_WORK)) {
+ return;
+ }
+ if (appendPostalsForDoCoMoInternal(builder, contentValuesList,
+ StructuredPostal.TYPE_OTHER)) {
+ return;
+ }
+ if (appendPostalsForDoCoMoInternal(builder, contentValuesList,
+ StructuredPostal.TYPE_CUSTOM)) {
+ return;
+ }
+
+ Log.w(LOG_TAG,
+ "Should not come here. Must have at least one postal data.");
+ }
+
+ private boolean appendPostalsForDoCoMoInternal(final StringBuilder builder,
+ final List<ContentValues> contentValuesList, Integer preferedType) {
+ for (ContentValues contentValues : contentValuesList) {
+ final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
+ final String label = contentValues.getAsString(StructuredPostal.LABEL);
+ if (type == preferedType) {
+ appendVCardPostalLine(builder, type, label, contentValues);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void appendPostalsForGeneric(final StringBuilder builder,
+ final List<ContentValues> contentValuesList) {
+ for (ContentValues contentValues : contentValuesList) {
+ final Integer type = contentValues.getAsInteger(StructuredPostal.TYPE);
+ final String label = contentValues.getAsString(StructuredPostal.LABEL);
+ appendVCardPostalLine(builder, type, label, contentValues);
+ }
+ }
+
+ private void appendIms(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Im.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ Integer protocol = contentValues.getAsInteger(Im.PROTOCOL);
+ String data = contentValues.getAsString(Im.DATA);
+
+ if (protocol != null && protocol == Im.PROTOCOL_GOOGLE_TALK) {
+ if (VCardConfig.usesAndroidSpecificProperty(mVCardType)) {
+ appendVCardLine(builder, Constants.PROPERTY_X_GOOGLE_TALK, data);
+ }
+ // TODO: add "X-GOOGLE TALK" case...
+ }
+ }
+ }
+ }
+
+ private void appendWebsites(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Website.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ final String website = contentValues.getAsString(Website.URL);
+ appendVCardLine(builder, VCARD_PROPERTY_URL, website);
+ }
+ }
+ }
+
+ private void appendBirthday(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Website.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null && contentValuesList.size() > 0) {
+ // Theoretically, there must be only one birthday for each vCard data and
+ // we are afraid of some parse error occuring in some devices, so
+ // we emit only one birthday entry for now.
+ final String birthday = contentValuesList.get(0).getAsString(Miscellaneous.BIRTHDAY);
+ appendVCardLine(builder, VCARD_PROPERTY_BIRTHDAY, birthday);
+ }
+ }
+
+ private void appendOrganizations(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Organization.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ final String company = contentValues
+ .getAsString(Organization.COMPANY);
+ final String title = contentValues
+ .getAsString(Organization.TITLE);
+ appendVCardLine(builder, VCARD_PROPERTY_ORG, company, true,
+ mUsesQuotedPrintable);
+ appendVCardLine(builder, VCARD_PROPERTY_TITLE, title, true,
+ mUsesQuotedPrintable);
+ }
+ }
+ }
+
+ private void appendPhotos(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ List<ContentValues> contentValuesList = contentValuesListMap
+ .get(Photo.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ for (ContentValues contentValues : contentValuesList) {
+ byte[] data = contentValues.getAsByteArray(Photo.PHOTO);
+ if (data == null) {
+ continue;
+ }
+ final String photoType;
+ // Use some heuristics for guessing the format of the image.
+ // TODO: there should be some general API for detecting the file format.
+ if (data.length >= 3 && data[0] == 'G' && data[1] == 'I'
+ && data[2] == 'F') {
+ photoType = "GIF";
+ } else if (data.length >= 4 && data[0] == (byte) 0x89
+ && data[1] == 'P' && data[2] == 'N' && data[3] == 'G') {
+ // Note: vCard 2.1 officially does not support PNG, but we
+ // may have it
+ // and using X- word like "X-PNG" may not let importers know
+ // it is
+ // PNG. So we use the String "PNG" as is...
+ photoType = "PNG";
+ } else if (data.length >= 2 && data[0] == (byte) 0xff
+ && data[1] == (byte) 0xd8) {
+ photoType = "JPEG";
+ } else {
+ Log.d(LOG_TAG, "Unknown photo type. Ignore.");
+ continue;
+ }
+ String photoString = VCardUtils.encodeBase64(data);
+ if (photoString.length() > 0) {
+ appendVCardPhotoLine(builder, photoString, photoType);
+ }
+ }
+ }
+ }
+
+ private void appendNotes(final StringBuilder builder,
+ final Map<String, List<ContentValues>> contentValuesListMap) {
+ final List<ContentValues> contentValuesList =
+ contentValuesListMap.get(Note.CONTENT_ITEM_TYPE);
+ if (contentValuesList != null) {
+ if (mOnlyOneNoteFieldIsAvailable) {
+ StringBuilder noteBuilder = new StringBuilder();
+ boolean first = true;
+ for (ContentValues contentValues : contentValuesList) {
+ final String note = contentValues.getAsString(Note.NOTE);
+ if (note.length() > 0) {
+ if (first) {
+ first = false;
+ } else {
+ noteBuilder.append('\n');
+ }
+ noteBuilder.append(note);
+ }
+ }
+ appendVCardLine(builder, VCARD_PROPERTY_NOTE, noteBuilder.toString(),
+ true, mUsesQuotedPrintable);
+ } else {
+ for (ContentValues contentValues : contentValuesList) {
+ final String note = contentValues.getAsString(Note.NOTE);
+ if (!TextUtils.isEmpty(note)) {
+ appendVCardLine(builder, VCARD_PROPERTY_NOTE, note, true,
+ mUsesQuotedPrintable);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Append '\' to the characters which should be escaped. The character set is different
+ * not only between vCard 2.1 and vCard 3.0 but also among each device.
+ *
+ * Note that Quoted-Printable string must not be input here.
+ */
+ @SuppressWarnings("fallthrough")
+ private String escapeCharacters(String unescaped) {
+ if (TextUtils.isEmpty(unescaped)) {
+ return "";
+ }
+
+ StringBuilder builder = new StringBuilder();
+ final int length = unescaped.length();
+ for (int i = 0; i < length; i++) {
+ char ch = unescaped.charAt(i);
+ switch (ch) {
+ case ';':
+ builder.append('\\');
+ builder.append(';');
+ break;
+ case '\r':
+ if (i + 1 < length) {
+ char nextChar = unescaped.charAt(i);
+ if (nextChar == '\n') {
+ continue;
+ } else {
+ // fall through
+ }
+ } else {
+ // fall through
+ }
+ case '\n':
+ // In vCard 2.1, there's no specification about this, while
+ // vCard 3.0 explicitly
+ // requires this should be encoded to "\n".
+ builder.append("\\n");
+ break;
+ case '\\':
+ if (mIsV30) {
+ builder.append("\\\\");
+ break;
+ }
+ case '<':
+ case '>':
+ if (mIsDoCoMo) {
+ builder.append('\\');
+ builder.append(ch);
+ }
+ break;
+ case ',':
+ if (mIsV30) {
+ builder.append("\\,");
+ break;
+ }
+ default:
+ builder.append(ch);
+ break;
+ }
+ }
+ return builder.toString();
+ }
+
+ private void appendVCardPhotoLine(StringBuilder builder,
+ String encodedData, String type) {
+ StringBuilder tmpBuilder = new StringBuilder();
+ tmpBuilder.append(VCARD_PROPERTY_PHOTO);
+ tmpBuilder.append(VCARD_ATTR_SEPARATOR);
+ if (mIsV30) {
+ tmpBuilder.append(VCARD_ATTR_ENCODING_BASE64_V30);
+ } else {
+ tmpBuilder.append(VCARD_ATTR_ENCODING_BASE64_V21);
+ }
+ tmpBuilder.append(VCARD_ATTR_SEPARATOR);
+ tmpBuilder.append("TYPE=");
+ tmpBuilder.append(type);
+ tmpBuilder.append(VCARD_DATA_SEPARATOR);
+ tmpBuilder.append(encodedData);
+
+ String tmpStr = tmpBuilder.toString();
+ tmpBuilder = new StringBuilder();
+ int lineCount = 0;
+ for (int i = 0; i < tmpStr.length(); i++) {
+ tmpBuilder.append(tmpStr.charAt(i));
+ lineCount++;
+ if (lineCount > 72) {
+ tmpBuilder.append(VCARD_COL_SEPARATOR);
+ tmpBuilder.append(VCARD_WS);
+ lineCount = 0;
+ }
+ }
+ builder.append(tmpBuilder.toString());
+ builder.append(VCARD_COL_SEPARATOR);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ private void appendVCardPostalLine(StringBuilder builder, Integer type, String label,
+ final ContentValues contentValues) {
+ builder.append(VCARD_PROPERTY_ADR);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ boolean dataExists = false;
+ String[] dataArray = VCardUtils.getVCardPostalElements(contentValues);
+ int length = dataArray.length;
+ final boolean useQuotedPrintable = mUsesQuotedPrintable;
+ for (int i = 0; i < length; i++) {
+ String data = dataArray[i];
+ if (!TextUtils.isEmpty(data)) {
+ dataExists = true;
+ if (useQuotedPrintable) {
+ dataArray[i] = encodeQuotedPrintable(data);
+ } else {
+ dataArray[i] = escapeCharacters(data);
+ }
+ }
+ }
+
+ if (type == null) {
+ type = StructuredPostal.TYPE_OTHER;
+ }
+
+ boolean typeIsAppended = false;
+ switch (type) {
+ case StructuredPostal.TYPE_HOME:
+ builder.append(Constants.ATTR_TYPE_HOME);
+ typeIsAppended = true;
+ break;
+ case StructuredPostal.TYPE_WORK:
+ builder.append(Constants.ATTR_TYPE_WORK);
+ typeIsAppended = true;
+ break;
+ case StructuredPostal.TYPE_CUSTOM:
+ if (mUsesAndroidProperty && !TextUtils.isEmpty(label)
+ && VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
+ // We're not sure whether the label is valid in the spec ("IANA-token" in the vCard 3.1
+ // is unclear...)
+ // Just for safety, we add "X-" at the beggining of each label.
+ // Also checks the label obeys with vCard 3.0 spec.
+ builder.append("X-");
+ builder.append(label);
+ builder.append(VCARD_DATA_SEPARATOR);
+ }
+ break;
+ case StructuredPostal.TYPE_OTHER:
+ break;
+ default:
+ Log.e(LOG_TAG, "Unknown StructuredPostal type: " + type);
+ break;
+ }
+
+ if (dataExists) {
+ if (typeIsAppended) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ }
+ // Strictly, vCard 3.0 does not allow this, but we add this since
+ // this information
+ // should be useful, Assume no parser does not emit error with this
+ // attribute.
+ builder.append(mVCardAttributeCharset);
+ if (useQuotedPrintable) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ }
+ }
+ builder.append(VCARD_DATA_SEPARATOR);
+ if (dataExists) {
+ // The elements in dataArray are already encoded to quoted printable
+ // if needed.
+ // See above.
+ //
+ // TODO: in vCard 3.0, one line may become too huge. Fix this.
+ builder.append(dataArray[0]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[1]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[2]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[3]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[4]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[5]);
+ builder.append(VCARD_ITEM_SEPARATOR);
+ builder.append(dataArray[6]);
+ }
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ private void appendVCardEmailLine(StringBuilder builder, Integer type, String label, String data) {
+ builder.append(VCARD_PROPERTY_EMAIL);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ if (type == null) {
+ type = Email.TYPE_OTHER;
+ }
+
+ switch (type) {
+ case Email.TYPE_CUSTOM:
+ if (android.provider.Contacts.ContactMethodsColumns.MOBILE_EMAIL_TYPE_NAME
+ .equals(label)) {
+ builder.append(Constants.ATTR_TYPE_CELL);
+ } else if (mUsesAndroidProperty && !TextUtils.isEmpty(label)
+ && VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
+ builder.append("X-");
+ builder.append(label);
+ } else {
+ // Default to INTERNET.
+ builder.append(Constants.ATTR_TYPE_INTERNET);
+ }
+ break;
+ case Email.TYPE_HOME:
+ builder.append(Constants.ATTR_TYPE_HOME);
+ break;
+ case Email.TYPE_WORK:
+ builder.append(Constants.ATTR_TYPE_WORK);
+ break;
+ case Email.TYPE_OTHER:
+ builder.append(Constants.ATTR_TYPE_INTERNET);
+ break;
+ case Email.TYPE_MOBILE:
+ builder.append(Constants.ATTR_TYPE_CELL);
+ break;
+ default:
+ Log.e(LOG_TAG, "Unknown Email type: " + type);
+ builder.append(Constants.ATTR_TYPE_INTERNET);
+ break;
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(data);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ private void appendVCardTelephoneLine(StringBuilder builder, Integer type, String label,
+ String encodedData) {
+ builder.append(VCARD_PROPERTY_TEL);
+ builder.append(VCARD_ATTR_SEPARATOR);
+
+ if (type == null) {
+ type = Phone.TYPE_OTHER;
+ }
+
+ switch (type) {
+ case Phone.TYPE_HOME:
+ appendTypeAttributes(builder, Arrays.asList(
+ Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_VOICE));
+ break;
+ case Phone.TYPE_WORK:
+ appendTypeAttributes(builder, Arrays.asList(
+ Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_VOICE));
+ break;
+ case Phone.TYPE_FAX_HOME:
+ appendTypeAttributes(builder, Arrays.asList(
+ Constants.ATTR_TYPE_HOME, Constants.ATTR_TYPE_FAX));
+ break;
+ case Phone.TYPE_FAX_WORK:
+ appendTypeAttributes(builder, Arrays.asList(
+ Constants.ATTR_TYPE_WORK, Constants.ATTR_TYPE_FAX));
+ break;
+ case Phone.TYPE_MOBILE:
+ builder.append(Constants.ATTR_TYPE_CELL);
+ break;
+ case Phone.TYPE_PAGER:
+ if (mIsDoCoMo) {
+ // Not sure about the reason, but previous implementation had
+ // used "VOICE" instead of "PAGER"
+ builder.append(Constants.ATTR_TYPE_VOICE);
+ } else {
+ builder.append(Constants.ATTR_TYPE_PAGER);
+ }
+ break;
+ case Phone.TYPE_OTHER:
+ builder.append(Constants.ATTR_TYPE_VOICE);
+ break;
+ case Phone.TYPE_CUSTOM:
+ if (mUsesAndroidProperty && !TextUtils.isEmpty(label)
+ && VCardUtils.containsOnlyAlphaDigitHyphen(label)) {
+ builder.append("X-" + label);
+ } else {
+ // Just ignore the custom type.
+ builder.append(Constants.ATTR_TYPE_VOICE);
+ }
+ break;
+ default:
+ appendUncommonPhoneType(builder, type);
+ break;
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedData);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ /**
+ * Appends phone type string which may not be available in some devices.
+ */
+ private void appendUncommonPhoneType(StringBuilder builder, Integer type) {
+ if (mIsDoCoMo) {
+ // The previous implementation for DoCoMo had been conservative
+ // about miscellaneous types.
+ builder.append(Constants.ATTR_TYPE_VOICE);
+ } else {
+ String phoneAttribute = VCardUtils.getPhoneAttributeString(type);
+ if (phoneAttribute != null) {
+ builder.append(phoneAttribute);
+ } else {
+ Log.e(LOG_TAG, "Unknown or unsupported (by vCard) Phone type: " + type);
+ }
+ }
+ }
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String propertyName, final String rawData) {
+ appendVCardLine(builder, propertyName, rawData, false, false);
+ }
+
+ private void appendVCardLine(final StringBuilder builder,
+ final String field, final String rawData, boolean needCharset,
+ boolean needQuotedPrintable) {
+ builder.append(field);
+ if (needCharset) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(mVCardAttributeCharset);
+ }
+
+ final String encodedData;
+ if (needQuotedPrintable) {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ builder.append(VCARD_ATTR_ENCODING_QP);
+ encodedData = encodeQuotedPrintable(rawData);
+ } else {
+ // TODO: one line may be too huge, which may be invalid in vCard spec, though
+ // several (even well-known) applications do not care this.
+ encodedData = escapeCharacters(rawData);
+ }
+
+ builder.append(VCARD_DATA_SEPARATOR);
+ builder.append(encodedData);
+ builder.append(VCARD_COL_SEPARATOR);
+ }
+
+ private void appendTypeAttributes(final StringBuilder builder,
+ final List<String> types) {
+ // We may have to make this comma separated form like "TYPE=DOM,WORK" in the future,
+ // which would be recommended way in vcard 3.0 though not valid in vCard 2.1.
+ boolean first = true;
+ for (String type : types) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(VCARD_ATTR_SEPARATOR);
+ }
+ if (mIsV30) {
+ builder.append(Constants.ATTR_TYPE);
+ builder.append('=');
+ }
+ builder.append(type);
+ }
+ }
+
+ private String encodeQuotedPrintable(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return "";
+ }
+ {
+ // Replace "\n" and "\r" with "\r\n".
+ StringBuilder tmpBuilder = new StringBuilder();
+ int length = str.length();
+ for (int i = 0; i < length; i++) {
+ char ch = str.charAt(i);
+ if (ch == '\r') {
+ if (i + 1 < length && str.charAt(i + 1) == '\n') {
+ i++;
+ }
+ tmpBuilder.append("\r\n");
+ } else if (ch == '\n') {
+ tmpBuilder.append("\r\n");
+ } else {
+ tmpBuilder.append(ch);
+ }
+ }
+ str = tmpBuilder.toString();
+ }
+
+ StringBuilder builder = new StringBuilder();
+ int index = 0;
+ int lineCount = 0;
+ byte[] strArray = null;
+
+ try {
+ strArray = str.getBytes(mCharsetString);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Charset " + mCharsetString + " cannot be used. "
+ + "Try default charset");
+ strArray = str.getBytes();
+ }
+ while (index < strArray.length) {
+ builder.append(String.format("=%02X", strArray[index]));
+ index += 1;
+ lineCount += 3;
+
+ if (lineCount >= 67) {
+ // Specification requires CRLF must be inserted before the
+ // length of the line
+ // becomes more than 76.
+ // Assuming that the next character is a multi-byte character,
+ // it will become
+ // 6 bytes.
+ // 76 - 6 - 3 = 67
+ builder.append("=\r\n");
+ lineCount = 0;
+ }
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardConfig.java b/core/java/android/pim/vcard/VCardConfig.java
new file mode 100644
index 0000000..d87b002
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardConfig.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The class representing VCard related configurations. Useful static methods are not in this class
+ * but in VCardUtils.
+ */
+public class VCardConfig {
+ // TODO: may be better to make the instance of this available and stop using static methods and
+ // one integer.
+
+ /* package */ static final int LOG_LEVEL_NONE = 0;
+ /* package */ static final int LOG_LEVEL_PERFORMANCE_MEASUREMENT = 0x1;
+ /* package */ static final int LOG_LEVEL_SHOW_WARNING = 0x2;
+ /* package */ static final int LOG_LEVEL_VERBOSE =
+ LOG_LEVEL_PERFORMANCE_MEASUREMENT | LOG_LEVEL_SHOW_WARNING;
+
+ /* package */ static final int LOG_LEVEL = LOG_LEVEL_PERFORMANCE_MEASUREMENT;
+
+ // Assumes that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
+ // decode the unicode to the original charset. If not, this setting will cause some bug.
+ public static final String DEFAULT_CHARSET = "iso-8859-1";
+
+ // TODO: make the other codes use this flag
+ public static final boolean IGNORE_CASE_EXCEPT_VALUE = true;
+
+ private static final int FLAG_V21 = 0;
+ private static final int FLAG_V30 = 1;
+
+ // 0x2 is reserved for the future use ...
+
+ public static final int NAME_ORDER_DEFAULT = 0;
+ public static final int NAME_ORDER_EUROPE = 0x4;
+ public static final int NAME_ORDER_JAPANESE = 0x8;
+ private static final int NAME_ORDER_MASK = 0xC;
+
+ // 0x10 is reserved for safety
+
+ private static final int FLAG_CHARSET_UTF8 = 0;
+ private static final int FLAG_CHARSET_SHIFT_JIS = 0x20;
+
+ /**
+ * The flag indicating the vCard composer will add some "X-" properties used only in Android
+ * when the formal vCard specification does not have appropriate fields for that data.
+ *
+ * For example, Android accepts nickname information while vCard 2.1 does not.
+ * When this flag is on, vCard composer emits alternative "X-" property (like "X-NICKNAME")
+ * instead of just dropping it.
+ *
+ * vCard parser code automatically parses the field emitted even when this flag is off.
+ *
+ * Note that this flag does not assure all the information must be hold in the emitted vCard.
+ */
+ private static final int FLAG_USE_ANDROID_PROPERTY = 0x80000000;
+
+ /**
+ * The flag indicating the vCard composer will add some "X-" properties seen in the
+ * vCard data emitted by the other softwares/devices when the formal vCard specification
+ * does not have appropriate field(s) for that data.
+ *
+ * One example is X-PHONETIC-FIRST-NAME/X-PHONETIC-MIDDLE-NAME/X-PHONETIC-LAST-NAME, which are
+ * for phonetic name (how the name is pronounced), seen in the vCard emitted by some other
+ * non-Android devices/softwares. We chose to enable the vCard composer to use those
+ * defact properties since they are also useful for Android devices.
+ *
+ * Note for developers: only "X-" properties should be added with this flag. vCard 2.1/3.0
+ * allows any kind of "X-" properties but does not allow non-"X-" properties (except IANA tokens
+ * in vCard 3.0). Some external parsers may get confused with non-valid, non-"X-" properties.
+ */
+ private static final int FLAG_USE_DEFACT_PROPERTY = 0x40000000;
+
+ /**
+ * The flag indicating some specific dialect seen in vcard of DoCoMo (one of Japanese
+ * mobile careers) should be used. This flag does not include any other information like
+ * that "the vCard is for Japanese". So it is "possible" that "the vCard should have DoCoMo's
+ * dialect but the name order should be European", but it is not recommended.
+ */
+ private static final int FLAG_DOCOMO = 0x20000000;
+
+
+ // VCard types
+
+
+ /**
+ * General vCard format with the version 2.1. Uses UTF-8 for the charset.
+ * When composing a vCard entry, the US convension will be used.
+ *
+ * e.g. The order of the display name would be "Prefix Given Middle Family Suffix",
+ * while in Japan, it should be "Prefix Family Middle Given Suffix".
+ */
+ public static final int VCARD_TYPE_V21_GENERIC =
+ (FLAG_V21 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static String VCARD_TYPE_V21_GENERIC_STR = "v21_generic";
+
+ /**
+ * General vCard format with the version 3.0. Uses UTF-8 for the charset.
+ *
+ * Note that this type is not fully implemented, so probably some bugs remain especially
+ * in parsing part.
+ *
+ * TODO: implement this type.
+ */
+ public static final int VCARD_TYPE_V30_GENERIC =
+ (FLAG_V30 | NAME_ORDER_DEFAULT | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V30_GENERIC_STR = "v30_generic";
+
+ /**
+ * General vCard format with the version 2.1 with some Europe convension. Uses Utf-8.
+ * Currently, only name order is considered ("Prefix Middle Given Family Suffix")
+ */
+ public static final int VCARD_TYPE_V21_EUROPE =
+ (FLAG_V21 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V21_EUROPE_STR = "v21_europe";
+
+ /**
+ * General vCard format with the version 3.0 with some Europe convension. Uses UTF-8
+ */
+ public static final int VCARD_TYPE_V30_EUROPE =
+ (FLAG_V30 | NAME_ORDER_EUROPE | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V30_EUROPE_STR = "v30_europe";
+
+ /**
+ * vCard 2.1 format for miscellaneous Japanese devices. Shift_Jis is used for
+ * parsing/composing the vCard data.
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V21_JAPANESE_STR = "v21_japanese";
+
+ /**
+ * vCard 2.1 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ */
+ public static final int VCARD_TYPE_V21_JAPANESE_UTF8 =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V21_JAPANESE_UTF8_STR = "v21_japanese_utf8";
+
+ /**
+ * vCard format for miscellaneous Japanese devices, using Shift_Jis for
+ * parsing/composing the vCard data.
+ */
+ public static final int VCARD_TYPE_V30_JAPANESE =
+ (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V30_JAPANESE_STR = "v30_japanese";
+
+ /**
+ * vCard 3.0 format for miscellaneous Japanese devices, using UTF-8 as default charset.
+ */
+ public static final int VCARD_TYPE_V30_JAPANESE_UTF8 =
+ (FLAG_V30 | NAME_ORDER_JAPANESE | FLAG_CHARSET_UTF8 |
+ FLAG_USE_DEFACT_PROPERTY | FLAG_USE_ANDROID_PROPERTY);
+
+ /* package */ static final String VCARD_TYPE_V30_JAPANESE_UTF8_STR = "v30_japanese_utf8";
+
+ /**
+ * VCard format used in DoCoMo, which is one of Japanese mobile phone careers.
+ * Base version is vCard 2.1, but the data has several DoCoMo-specific convensions.
+ * No Android-specific property nor defact property is included.
+ */
+ public static final int VCARD_TYPE_DOCOMO =
+ (FLAG_V21 | NAME_ORDER_JAPANESE | FLAG_CHARSET_SHIFT_JIS | FLAG_DOCOMO);
+
+ private static final String VCARD_TYPE_DOCOMO_STR = "docomo";
+
+ public static int VCARD_TYPE_DEFAULT = VCARD_TYPE_V21_GENERIC;
+
+ private static final Map<String, Integer> VCARD_TYPES_MAP;
+
+ static {
+ VCARD_TYPES_MAP = new HashMap<String, Integer>();
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V21_GENERIC_STR, VCARD_TYPE_V21_GENERIC);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V30_GENERIC_STR, VCARD_TYPE_V30_GENERIC);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V21_EUROPE_STR, VCARD_TYPE_V21_EUROPE);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V30_EUROPE_STR, VCARD_TYPE_V30_EUROPE);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_STR, VCARD_TYPE_V21_JAPANESE);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V21_JAPANESE_UTF8_STR, VCARD_TYPE_V21_JAPANESE_UTF8);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_STR, VCARD_TYPE_V30_JAPANESE);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_V30_JAPANESE_UTF8_STR, VCARD_TYPE_V30_JAPANESE_UTF8);
+ VCARD_TYPES_MAP.put(VCARD_TYPE_DOCOMO_STR, VCARD_TYPE_DOCOMO);
+ }
+
+ public static int getVCardTypeFromString(String vcardTypeString) {
+ String loweredKey = vcardTypeString.toLowerCase();
+ if (VCARD_TYPES_MAP.containsKey(loweredKey)) {
+ return VCARD_TYPES_MAP.get(loweredKey);
+ } else {
+ // XXX: should return the value indicating the input is invalid?
+ return VCARD_TYPE_DEFAULT;
+ }
+ }
+
+ public static boolean isV30(int vcardType) {
+ return ((vcardType & FLAG_V30) != 0);
+ }
+
+ public static boolean usesQuotedPrintable(int vcardType) {
+ return !isV30(vcardType);
+ }
+
+ public static boolean isDoCoMo(int vcardType) {
+ return ((vcardType & FLAG_DOCOMO) != 0);
+ }
+
+ /**
+ * @return true if the device is Japanese and some Japanese convension is
+ * applied to creating "formatted" something like FORMATTED_ADDRESS.
+ */
+ public static boolean isJapaneseDevice(int vcardType) {
+ return ((vcardType == VCARD_TYPE_V21_JAPANESE) ||
+ (vcardType == VCARD_TYPE_V21_JAPANESE_UTF8) ||
+ (vcardType == VCARD_TYPE_V30_JAPANESE) ||
+ (vcardType == VCARD_TYPE_V30_JAPANESE_UTF8) ||
+ (vcardType == VCARD_TYPE_DOCOMO));
+ }
+
+ public static boolean usesShiftJis(int vcardType) {
+ return ((vcardType & FLAG_CHARSET_SHIFT_JIS) != 0);
+ }
+
+ /**
+ * @return true when Japanese phonetic string must be converted to a string
+ * containing only half-width katakana. This method exists since Japanese mobile
+ * phones usually use only half-width katakana for expressing phonetic names and
+ * some devices are not ready for parsing other phonetic strings like hiragana and
+ * full-width katakana.
+ */
+ public static boolean needsToConvertPhoneticString(int vcardType) {
+ return (vcardType == VCARD_TYPE_DOCOMO);
+ }
+
+ public static int getNameOrderType(int vcardType) {
+ return vcardType & NAME_ORDER_MASK;
+ }
+
+ public static boolean usesAndroidSpecificProperty(int vcardType) {
+ return ((vcardType & FLAG_USE_ANDROID_PROPERTY) != 0);
+ }
+
+ public static boolean usesDefactProperty(int vcardType) {
+ return ((vcardType & FLAG_USE_DEFACT_PROPERTY) != 0);
+ }
+
+ public static boolean onlyOneNoteFieldIsAvailable(int vcardType) {
+ return vcardType == VCARD_TYPE_DOCOMO;
+ }
+
+ public static boolean showPerformanceLog() {
+ return (VCardConfig.LOG_LEVEL & VCardConfig.LOG_LEVEL_PERFORMANCE_MEASUREMENT) != 0;
+ }
+
+ private VCardConfig() {
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardDataBuilder.java b/core/java/android/pim/vcard/VCardDataBuilder.java
new file mode 100644
index 0000000..fd165e9
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardDataBuilder.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * VBuilder for VCard. VCard may contain big photo images encoded by BASE64,
+ * If we store all VNode entries in memory like VDataBuilder.java,
+ * OutOfMemoryError may be thrown. Thus, this class push each VCard entry into
+ * ContentResolver immediately.
+ */
+public class VCardDataBuilder implements VCardBuilder {
+ static private String LOG_TAG = "VCardDataBuilder";
+
+ /**
+ * If there's no other information available, this class uses this charset for encoding
+ * byte arrays.
+ */
+ static public String TARGET_CHARSET = "UTF-8";
+
+ private ContactStruct.Property mCurrentProperty = new ContactStruct.Property();
+ private ContactStruct mCurrentContactStruct;
+ private String mParamType;
+
+ /**
+ * The charset using which VParser parses the text.
+ */
+ private String mSourceCharset;
+
+ /**
+ * The charset with which byte array is encoded to String.
+ */
+ private String mTargetCharset;
+ private boolean mStrictLineBreakParsing;
+
+ private int mVCardType;
+
+ // Just for testing.
+ private long mTimePushIntoContentResolver;
+
+ private List<EntryHandler> mEntryHandlers = new ArrayList<EntryHandler>();
+
+ public VCardDataBuilder() {
+ this(null, null, false, VCardConfig.VCARD_TYPE_V21_GENERIC);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(int vcardType) {
+ this(null, null, false, vcardType);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(String charset, boolean strictLineBreakParsing, int vcardType) {
+ this(null, charset, strictLineBreakParsing, vcardType);
+ }
+
+ /**
+ * @hide
+ */
+ public VCardDataBuilder(String sourceCharset,
+ String targetCharset,
+ boolean strictLineBreakParsing,
+ int vcardType) {
+ if (sourceCharset != null) {
+ mSourceCharset = sourceCharset;
+ } else {
+ mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+ }
+ if (targetCharset != null) {
+ mTargetCharset = targetCharset;
+ } else {
+ mTargetCharset = TARGET_CHARSET;
+ }
+ mStrictLineBreakParsing = strictLineBreakParsing;
+ mVCardType = vcardType;
+ }
+
+ public void addEntryHandler(EntryHandler entryHandler) {
+ mEntryHandlers.add(entryHandler);
+ }
+
+ public void start() {
+ for (EntryHandler entryHandler : mEntryHandlers) {
+ entryHandler.onParsingStart();
+ }
+ }
+
+ public void end() {
+ for (EntryHandler entryHandler : mEntryHandlers) {
+ entryHandler.onParsingEnd();
+ }
+ }
+
+ /**
+ * Assume that VCard is not nested. In other words, this code does not accept
+ */
+ public void startRecord(String type) {
+ // TODO: add the method clear() instead of using null for reducing GC?
+ if (mCurrentContactStruct != null) {
+ // This means startRecord() is called inside startRecord() - endRecord() block.
+ // TODO: should throw some Exception
+ Log.e(LOG_TAG, "Nested VCard code is not supported now.");
+ }
+ if (!type.equalsIgnoreCase("VCARD")) {
+ // TODO: add test case for this
+ Log.e(LOG_TAG, "This is not VCARD!");
+ }
+
+ mCurrentContactStruct = new ContactStruct(mVCardType);
+ }
+
+ public void endRecord() {
+ mCurrentContactStruct.consolidateFields();
+ for (EntryHandler entryHandler : mEntryHandlers) {
+ entryHandler.onEntryCreated(mCurrentContactStruct);
+ }
+ mCurrentContactStruct = null;
+ }
+
+ public void startProperty() {
+ mCurrentProperty.clear();
+ }
+
+ public void endProperty() {
+ mCurrentContactStruct.addProperty(mCurrentProperty);
+ }
+
+ public void propertyName(String name) {
+ mCurrentProperty.setPropertyName(name);
+ }
+
+ public void propertyGroup(String group) {
+ // ContactStruct does not support Group.
+ }
+
+ public void propertyParamType(String type) {
+ if (mParamType != null) {
+ Log.e(LOG_TAG, "propertyParamType() is called more than once " +
+ "before propertyParamValue() is called");
+ }
+ mParamType = type;
+ }
+
+ public void propertyParamValue(String value) {
+ if (mParamType == null) {
+ // From vCard 2.1 specification. vCard 3.0 formally does not allow this case.
+ mParamType = "TYPE";
+ }
+ mCurrentProperty.addParameter(mParamType, value);
+ mParamType = null;
+ }
+
+ private String encodeString(String originalString, String targetCharset) {
+ if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+ return originalString;
+ }
+ Charset charset = Charset.forName(mSourceCharset);
+ ByteBuffer byteBuffer = charset.encode(originalString);
+ // byteBuffer.array() "may" return byte array which is larger than
+ // byteBuffer.remaining(). Here, we keep on the safe side.
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return null;
+ }
+ }
+
+ private String handleOneValue(String value, String targetCharset, String encoding) {
+ if (encoding != null) {
+ if (encoding.equals("BASE64") || encoding.equals("B")) {
+ mCurrentProperty.setPropertyBytes(Base64.decodeBase64(value.getBytes()));
+ return value;
+ } else if (encoding.equals("QUOTED-PRINTABLE")) {
+ // "= " -> " ", "=\t" -> "\t".
+ // Previous code had done this replacement. Keep on the safe side.
+ StringBuilder builder = new StringBuilder();
+ int length = value.length();
+ for (int i = 0; i < length; i++) {
+ char ch = value.charAt(i);
+ if (ch == '=' && i < length - 1) {
+ char nextCh = value.charAt(i + 1);
+ if (nextCh == ' ' || nextCh == '\t') {
+
+ builder.append(nextCh);
+ i++;
+ continue;
+ }
+ }
+ builder.append(ch);
+ }
+ String quotedPrintable = builder.toString();
+
+ String[] lines;
+ if (mStrictLineBreakParsing) {
+ lines = quotedPrintable.split("\r\n");
+ } else {
+ builder = new StringBuilder();
+ length = quotedPrintable.length();
+ ArrayList<String> list = new ArrayList<String>();
+ for (int i = 0; i < length; i++) {
+ char ch = quotedPrintable.charAt(i);
+ if (ch == '\n') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else if (ch == '\r') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ if (i < length - 1) {
+ char nextCh = quotedPrintable.charAt(i + 1);
+ if (nextCh == '\n') {
+ i++;
+ }
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ String finalLine = builder.toString();
+ if (finalLine.length() > 0) {
+ list.add(finalLine);
+ }
+ lines = list.toArray(new String[0]);
+ }
+
+ builder = new StringBuilder();
+ for (String line : lines) {
+ if (line.endsWith("=")) {
+ line = line.substring(0, line.length() - 1);
+ }
+ builder.append(line);
+ }
+ byte[] bytes;
+ try {
+ bytes = builder.toString().getBytes(mSourceCharset);
+ } catch (UnsupportedEncodingException e1) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
+ bytes = builder.toString().getBytes();
+ }
+
+ try {
+ bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+ } catch (DecoderException e) {
+ Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+ return "";
+ }
+
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return new String(bytes);
+ }
+ }
+ // Unknown encoding. Fall back to default.
+ }
+ return encodeString(value, targetCharset);
+ }
+
+ public void propertyValues(List<String> values) {
+ if (values == null || values.size() == 0) {
+ return;
+ }
+
+ final Collection<String> charsetCollection = mCurrentProperty.getParameters("CHARSET");
+ String charset =
+ ((charsetCollection != null) ? charsetCollection.iterator().next() : null);
+ String targetCharset = CharsetUtils.nameForDefaultVendor(charset);
+
+ final Collection<String> encodingCollection = mCurrentProperty.getParameters("ENCODING");
+ String encoding =
+ ((encodingCollection != null) ? encodingCollection.iterator().next() : null);
+
+ if (targetCharset == null || targetCharset.length() == 0) {
+ targetCharset = mTargetCharset;
+ }
+
+ for (String value : values) {
+ mCurrentProperty.addToPropertyValueList(
+ handleOneValue(value, targetCharset, encoding));
+ }
+ }
+
+ public void showPerformanceInfo() {
+ Log.d(LOG_TAG, "time for insert ContactStruct to database: " +
+ mTimePushIntoContentResolver + " ms");
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardEntryCounter.java b/core/java/android/pim/vcard/VCardEntryCounter.java
new file mode 100644
index 0000000..f99b46c
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardEntryCounter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import java.util.List;
+
+public class VCardEntryCounter implements VCardBuilder {
+ private int mCount;
+
+ public int getCount() {
+ return mCount;
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ public void startRecord(String type) {
+ }
+
+ public void endRecord() {
+ mCount++;
+ }
+
+ public void startProperty() {
+ }
+
+ public void endProperty() {
+ }
+
+ public void propertyGroup(String group) {
+ }
+
+ public void propertyName(String name) {
+ }
+
+ public void propertyParamType(String type) {
+ }
+
+ public void propertyParamValue(String value) {
+ }
+
+ public void propertyValues(List<String> values) {
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/VCardParser.java b/core/java/android/pim/vcard/VCardParser.java
new file mode 100644
index 0000000..b5e5049
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class VCardParser {
+
+ protected boolean mCanceled;
+
+ /**
+ * Parses the given stream and send the VCard data into VCardBuilderBase object.
+ *
+ * Note that vCard 2.1 specification allows "CHARSET" parameter, and some career sets
+ * local encoding to it. For example, Japanese phone career uses Shift_JIS, which is
+ * formally allowed in VCard 2.1, but not recommended in VCard 3.0. In VCard 2.1,
+ * In some exreme case, some VCard may have different charsets in one VCard (though
+ * we do not see any device which emits such kind of malicious data)
+ *
+ * In order to avoid "misunderstanding" charset as much as possible, this method
+ * use "ISO-8859-1" for reading the stream. When charset is specified in some property
+ * (with "CHARSET=..." attribute), the string is decoded to raw bytes and encoded to
+ * the charset. This method assumes that "ISO-8859-1" has 1 to 1 mapping in all 8bit
+ * characters, which is not completely sure. In some cases, this "decoding-encoding"
+ * scheme may fail. To avoid the case,
+ *
+ * We recommend you to use VCardSourceDetector and detect which kind of source the
+ * VCard comes from and explicitly specify a charset using the result.
+ *
+ * @param is The source to parse.
+ * @param builder The VCardBuilderBase object which used to construct data. If you want to
+ * include multiple VCardBuilderBase objects in this field, consider using
+ * {#link VCardBuilderCollection} class.
+ * @return Returns true for success. Otherwise returns false.
+ * @throws IOException, VCardException
+ */
+ public abstract boolean parse(InputStream is, VCardBuilder builder)
+ throws IOException, VCardException;
+
+ /**
+ * The method variants which accept charset.
+ *
+ * RFC 2426 "recommends" (not forces) to use UTF-8, so it may be OK to use
+ * UTF-8 as an encoding when parsing vCard 3.0. But note that some Japanese
+ * phone uses Shift_JIS as a charset (e.g. W61SH), and another uses
+ * "CHARSET=SHIFT_JIS", which is explicitly prohibited in vCard 3.0 specification
+ * (e.g. W53K).
+ *
+ * @param is The source to parse.
+ * @param charset Charset to be used.
+ * @param builder The VCardBuilderBase object.
+ * @return Returns true when successful. Otherwise returns false.
+ * @throws IOException, VCardException
+ */
+ public abstract boolean parse(InputStream is, String charset, VCardBuilder builder)
+ throws IOException, VCardException;
+
+ /**
+ * The method variants which tells this object the operation is already canceled.
+ * XXX: Is this really necessary?
+ * @hide
+ */
+ public abstract void parse(InputStream is, String charset,
+ VCardBuilder builder, boolean canceled)
+ throws IOException, VCardException;
+
+ /**
+ * Cancel parsing.
+ * Actual cancel is done after the end of the current one vcard entry parsing.
+ */
+ public void cancel() {
+ mCanceled = true;
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
new file mode 100644
index 0000000..974fca8
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+import android.pim.vcard.exception.VCardNestedException;
+import android.pim.vcard.exception.VCardNotSupportedException;
+import android.pim.vcard.exception.VCardVersionException;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class is used to parse vcard. Please refer to vCard Specification 2.1.
+ */
+public class VCardParser_V21 extends VCardParser {
+ private static final String LOG_TAG = "vcard.VCardParser_V21";
+
+ /** Store the known-type */
+ private static final HashSet<String> sKnownTypeSet = new HashSet<String>(
+ Arrays.asList("DOM", "INTL", "POSTAL", "PARCEL", "HOME", "WORK",
+ "PREF", "VOICE", "FAX", "MSG", "CELL", "PAGER", "BBS",
+ "MODEM", "CAR", "ISDN", "VIDEO", "AOL", "APPLELINK",
+ "ATTMAIL", "CIS", "EWORLD", "INTERNET", "IBMMAIL",
+ "MCIMAIL", "POWERSHARE", "PRODIGY", "TLX", "X400", "GIF",
+ "CGM", "WMF", "BMP", "MET", "PMB", "DIB", "PICT", "TIFF",
+ "PDF", "PS", "JPEG", "QTIME", "MPEG", "MPEG2", "AVI",
+ "WAVE", "AIFF", "PCM", "X509", "PGP"));
+
+ /** Store the known-value */
+ private static final HashSet<String> sKnownValueSet = new HashSet<String>(
+ Arrays.asList("INLINE", "URL", "CONTENT-ID", "CID"));
+
+ /** Store the property names available in vCard 2.1 */
+ private static final HashSet<String> sAvailablePropertyNameV21 =
+ new HashSet<String>(Arrays.asList(
+ "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
+ "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
+ "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER"));
+
+ /**
+ * Though vCard 2.1 specification does not allow "B" encoding, some data may have it.
+ * We allow it for safety...
+ */
+ private static final HashSet<String> sAvailableEncodingV21 =
+ new HashSet<String>(Arrays.asList(
+ "7BIT", "8BIT", "QUOTED-PRINTABLE", "BASE64", "B"));
+
+ // Used only for parsing END:VCARD.
+ private String mPreviousLine;
+
+ /** The builder to build parsed data */
+ protected VCardBuilder mBuilder = null;
+
+ /**
+ * The encoding type. "Encoding" in vCard is different from "Charset".
+ * e.g. 7BIT, 8BIT, QUOTED-PRINTABLE.
+ */
+ protected String mEncoding = null;
+
+ protected final String sDefaultEncoding = "8BIT";
+
+ // Should not directly read a line from this. Use getLine() instead.
+ protected BufferedReader mReader;
+
+ // In some cases, vCard is nested. Currently, we only consider the most interior vCard data.
+ // See v21_foma_1.vcf in test directory for more information.
+ private int mNestCount;
+
+ // In order to reduce warning message as much as possible, we hold the value which made Logger
+ // emit a warning message.
+ protected HashSet<String> mWarningValueMap = new HashSet<String>();
+
+ // Just for debugging
+ private long mTimeTotal;
+ private long mTimeReadStartRecord;
+ private long mTimeReadEndRecord;
+ private long mTimeStartProperty;
+ private long mTimeEndProperty;
+ private long mTimeParseItems;
+ private long mTimeParseLineAndHandleGroup;
+ private long mTimeParsePropertyValues;
+ private long mTimeParseAdrOrgN;
+ private long mTimeHandleMiscPropertyValue;
+ private long mTimeHandleQuotedPrintable;
+ private long mTimeHandleBase64;
+
+ /**
+ * Create a new VCard parser.
+ */
+ public VCardParser_V21() {
+ super();
+ }
+
+ public VCardParser_V21(VCardSourceDetector detector) {
+ super();
+ if (detector != null && detector.getType() == VCardSourceDetector.TYPE_FOMA) {
+ mNestCount = 1;
+ }
+ }
+
+ /**
+ * Parse the file at the given position
+ * vcard_file = [wsls] vcard [wsls]
+ */
+ protected void parseVCardFile() throws IOException, VCardException {
+ boolean firstReading = true;
+ while (true) {
+ if (mCanceled) {
+ break;
+ }
+ if (!parseOneVCard(firstReading)) {
+ break;
+ }
+ firstReading = false;
+ }
+
+ if (mNestCount > 0) {
+ boolean useCache = true;
+ for (int i = 0; i < mNestCount; i++) {
+ readEndVCard(useCache, true);
+ useCache = false;
+ }
+ }
+ }
+
+ protected String getVersion() {
+ return "2.1";
+ }
+
+ /**
+ * @return true when the propertyName is a valid property name.
+ */
+ protected boolean isValidPropertyName(String propertyName) {
+ if (!(sAvailablePropertyNameV21.contains(propertyName.toUpperCase()) ||
+ propertyName.startsWith("X-")) &&
+ !mWarningValueMap.contains(propertyName)) {
+ mWarningValueMap.add(propertyName);
+ Log.w(LOG_TAG, "Property name unsupported by vCard 2.1: " + propertyName);
+ }
+ return true;
+ }
+
+ /**
+ * @return true when the encoding is a valid encoding.
+ */
+ protected boolean isValidEncoding(String encoding) {
+ return sAvailableEncodingV21.contains(encoding.toUpperCase());
+ }
+
+ /**
+ * @return String. It may be null, or its length may be 0
+ * @throws IOException
+ */
+ protected String getLine() throws IOException {
+ return mReader.readLine();
+ }
+
+ /**
+ * @return String with it's length > 0
+ * @throws IOException
+ * @throws VCardException when the stream reached end of line
+ */
+ protected String getNonEmptyLine() throws IOException, VCardException {
+ String line;
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException("Reached end of buffer.");
+ } else if (line.trim().length() > 0) {
+ return line;
+ }
+ }
+ }
+
+ /**
+ * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
+ * items *CRLF
+ * "END" [ws] ":" [ws] "VCARD"
+ */
+ private boolean parseOneVCard(boolean firstReading) throws IOException, VCardException {
+ boolean allowGarbage = false;
+ if (firstReading) {
+ if (mNestCount > 0) {
+ for (int i = 0; i < mNestCount; i++) {
+ if (!readBeginVCard(allowGarbage)) {
+ return false;
+ }
+ allowGarbage = true;
+ }
+ }
+ }
+
+ if (!readBeginVCard(allowGarbage)) {
+ return false;
+ }
+ long start;
+ if (mBuilder != null) {
+ start = System.currentTimeMillis();
+ mBuilder.startRecord("VCARD");
+ mTimeReadStartRecord += System.currentTimeMillis() - start;
+ }
+ start = System.currentTimeMillis();
+ parseItems();
+ mTimeParseItems += System.currentTimeMillis() - start;
+ readEndVCard(true, false);
+ if (mBuilder != null) {
+ start = System.currentTimeMillis();
+ mBuilder.endRecord();
+ mTimeReadEndRecord += System.currentTimeMillis() - start;
+ }
+ return true;
+ }
+
+ /**
+ * @return True when successful. False when reaching the end of line
+ * @throws IOException
+ * @throws VCardException
+ */
+ protected boolean readBeginVCard(boolean allowGarbage)
+ throws IOException, VCardException {
+ String line;
+ do {
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ return false;
+ } else if (line.trim().length() > 0) {
+ break;
+ }
+ }
+ String[] strArray = line.split(":", 2);
+ int length = strArray.length;
+
+ // Though vCard 2.1/3.0 specification does not allow lower cases,
+ // some data may have them, so we allow it (Actually, previous code
+ // had explicitly allowed "BEGIN:vCard" though there's no example).
+ if (length == 2 &&
+ strArray[0].trim().equalsIgnoreCase("BEGIN") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD")) {
+ return true;
+ } else if (!allowGarbage) {
+ if (mNestCount > 0) {
+ mPreviousLine = line;
+ return false;
+ } else {
+ throw new VCardException(
+ "Expected String \"BEGIN:VCARD\" did not come "
+ + "(Instead, \"" + line + "\" came)");
+ }
+ }
+ } while(allowGarbage);
+
+ throw new VCardException("Reached where must not be reached.");
+ }
+
+ /**
+ * The arguments useCache and allowGarbase are usually true and false accordingly when
+ * this function is called outside this function itself.
+ *
+ * @param useCache When true, line is obtained from mPreviousline. Otherwise, getLine()
+ * is used.
+ * @param allowGarbage When true, ignore non "END:VCARD" line.
+ * @throws IOException
+ * @throws VCardException
+ */
+ protected void readEndVCard(boolean useCache, boolean allowGarbage)
+ throws IOException, VCardException {
+ String line;
+ do {
+ if (useCache) {
+ // Though vCard specification does not allow lower cases,
+ // some data may have them, so we allow it.
+ line = mPreviousLine;
+ } else {
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException("Expected END:VCARD was not found.");
+ } else if (line.trim().length() > 0) {
+ break;
+ }
+ }
+ }
+
+ String[] strArray = line.split(":", 2);
+ if (strArray.length == 2 &&
+ strArray[0].trim().equalsIgnoreCase("END") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD")) {
+ return;
+ } else if (!allowGarbage) {
+ throw new VCardException("END:VCARD != \"" + mPreviousLine + "\"");
+ }
+ useCache = false;
+ } while (allowGarbage);
+ }
+
+ /**
+ * items = *CRLF item
+ * / item
+ */
+ protected void parseItems() throws IOException, VCardException {
+ /* items *CRLF item / item */
+ boolean ended = false;
+
+ if (mBuilder != null) {
+ long start = System.currentTimeMillis();
+ mBuilder.startProperty();
+ mTimeStartProperty += System.currentTimeMillis() - start;
+ }
+ ended = parseItem();
+ if (mBuilder != null && !ended) {
+ long start = System.currentTimeMillis();
+ mBuilder.endProperty();
+ mTimeEndProperty += System.currentTimeMillis() - start;
+ }
+
+ while (!ended) {
+ // follow VCARD ,it wont reach endProperty
+ if (mBuilder != null) {
+ long start = System.currentTimeMillis();
+ mBuilder.startProperty();
+ mTimeStartProperty += System.currentTimeMillis() - start;
+ }
+ ended = parseItem();
+ if (mBuilder != null && !ended) {
+ long start = System.currentTimeMillis();
+ mBuilder.endProperty();
+ mTimeEndProperty += System.currentTimeMillis() - start;
+ }
+ }
+ }
+
+ /**
+ * item = [groups "."] name [params] ":" value CRLF
+ * / [groups "."] "ADR" [params] ":" addressparts CRLF
+ * / [groups "."] "ORG" [params] ":" orgparts CRLF
+ * / [groups "."] "N" [params] ":" nameparts CRLF
+ * / [groups "."] "AGENT" [params] ":" vcard CRLF
+ */
+ protected boolean parseItem() throws IOException, VCardException {
+ mEncoding = sDefaultEncoding;
+
+ String line = getNonEmptyLine();
+ long start = System.currentTimeMillis();
+
+ String[] propertyNameAndValue = separateLineAndHandleGroup(line);
+ if (propertyNameAndValue == null) {
+ return true;
+ }
+ if (propertyNameAndValue.length != 2) {
+ throw new VCardException("Invalid line \"" + line + "\"");
+ }
+ String propertyName = propertyNameAndValue[0].toUpperCase();
+ String propertyValue = propertyNameAndValue[1];
+
+ mTimeParseLineAndHandleGroup += System.currentTimeMillis() - start;
+
+ if (propertyName.equals("ADR") || propertyName.equals("ORG") ||
+ propertyName.equals("N")) {
+ start = System.currentTimeMillis();
+ handleMultiplePropertyValue(propertyName, propertyValue);
+ mTimeParseAdrOrgN += System.currentTimeMillis() - start;
+ return false;
+ } else if (propertyName.equals("AGENT")) {
+ handleAgent(propertyValue);
+ return false;
+ } else if (isValidPropertyName(propertyName)) {
+ if (propertyName.equals("BEGIN")) {
+ if (propertyValue.equals("VCARD")) {
+ throw new VCardNestedException("This vCard has nested vCard data in it.");
+ } else {
+ throw new VCardException("Unknown BEGIN type: " + propertyValue);
+ }
+ } else if (propertyName.equals("VERSION") && !propertyValue.equals(getVersion())) {
+ throw new VCardVersionException("Incompatible version: " +
+ propertyValue + " != " + getVersion());
+ }
+ start = System.currentTimeMillis();
+ handlePropertyValue(propertyName, propertyValue);
+ mTimeParsePropertyValues += System.currentTimeMillis() - start;
+ return false;
+ }
+
+ throw new VCardException("Unknown property name: \"" +
+ propertyName + "\"");
+ }
+
+ static private final int STATE_GROUP_OR_PROPNAME = 0;
+ static private final int STATE_PARAMS = 1;
+ // vCard 3.1 specification allows double-quoted param-value, while vCard 2.1 does not.
+ // This is just for safety.
+ static private final int STATE_PARAMS_IN_DQUOTE = 2;
+
+ protected String[] separateLineAndHandleGroup(String line) throws VCardException {
+ int length = line.length();
+ int state = STATE_GROUP_OR_PROPNAME;
+ int nameIndex = 0;
+
+ String[] propertyNameAndValue = new String[2];
+
+ for (int i = 0; i < length; i++) {
+ char ch = line.charAt(i);
+ switch (state) {
+ case STATE_GROUP_OR_PROPNAME:
+ if (ch == ':') {
+ String propertyName = line.substring(nameIndex, i);
+ if (propertyName.equalsIgnoreCase("END")) {
+ mPreviousLine = line;
+ return null;
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyName(propertyName);
+ }
+ propertyNameAndValue[0] = propertyName;
+ if (i < length - 1) {
+ propertyNameAndValue[1] = line.substring(i + 1);
+ } else {
+ propertyNameAndValue[1] = "";
+ }
+ return propertyNameAndValue;
+ } else if (ch == '.') {
+ String groupName = line.substring(nameIndex, i);
+ if (mBuilder != null) {
+ mBuilder.propertyGroup(groupName);
+ }
+ nameIndex = i + 1;
+ } else if (ch == ';') {
+ String propertyName = line.substring(nameIndex, i);
+ if (propertyName.equalsIgnoreCase("END")) {
+ mPreviousLine = line;
+ return null;
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyName(propertyName);
+ }
+ propertyNameAndValue[0] = propertyName;
+ nameIndex = i + 1;
+ state = STATE_PARAMS;
+ }
+ break;
+ case STATE_PARAMS:
+ if (ch == '"') {
+ state = STATE_PARAMS_IN_DQUOTE;
+ } else if (ch == ';') {
+ handleParams(line.substring(nameIndex, i));
+ nameIndex = i + 1;
+ } else if (ch == ':') {
+ handleParams(line.substring(nameIndex, i));
+ if (i < length - 1) {
+ propertyNameAndValue[1] = line.substring(i + 1);
+ } else {
+ propertyNameAndValue[1] = "";
+ }
+ return propertyNameAndValue;
+ }
+ break;
+ case STATE_PARAMS_IN_DQUOTE:
+ if (ch == '"') {
+ state = STATE_PARAMS;
+ }
+ break;
+ }
+ }
+
+ throw new VCardException("Invalid line: \"" + line + "\"");
+ }
+
+
+ /**
+ * params = ";" [ws] paramlist
+ * paramlist = paramlist [ws] ";" [ws] param
+ * / param
+ * param = "TYPE" [ws] "=" [ws] ptypeval
+ * / "VALUE" [ws] "=" [ws] pvalueval
+ * / "ENCODING" [ws] "=" [ws] pencodingval
+ * / "CHARSET" [ws] "=" [ws] charsetval
+ * / "LANGUAGE" [ws] "=" [ws] langval
+ * / "X-" word [ws] "=" [ws] word
+ * / knowntype
+ */
+ protected void handleParams(String params) throws VCardException {
+ String[] strArray = params.split("=", 2);
+ if (strArray.length == 2) {
+ String paramName = strArray[0].trim();
+ String paramValue = strArray[1].trim();
+ if (paramName.equals("TYPE")) {
+ handleType(paramValue);
+ } else if (paramName.equals("VALUE")) {
+ handleValue(paramValue);
+ } else if (paramName.equals("ENCODING")) {
+ handleEncoding(paramValue);
+ } else if (paramName.equals("CHARSET")) {
+ handleCharset(paramValue);
+ } else if (paramName.equals("LANGUAGE")) {
+ handleLanguage(paramValue);
+ } else if (paramName.startsWith("X-")) {
+ handleAnyParam(paramName, paramValue);
+ } else {
+ throw new VCardException("Unknown type \"" + paramName + "\"");
+ }
+ } else {
+ handleType(strArray[0]);
+ }
+ }
+
+ /**
+ * ptypeval = knowntype / "X-" word
+ */
+ protected void handleType(String ptypeval) {
+ String upperTypeValue = ptypeval;
+ if (!(sKnownTypeSet.contains(upperTypeValue) || upperTypeValue.startsWith("X-")) &&
+ !mWarningValueMap.contains(ptypeval)) {
+ mWarningValueMap.add(ptypeval);
+ Log.w(LOG_TAG, "Type unsupported by vCard 2.1: " + ptypeval);
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("TYPE");
+ mBuilder.propertyParamValue(upperTypeValue);
+ }
+ }
+
+ /**
+ * pvalueval = "INLINE" / "URL" / "CONTENT-ID" / "CID" / "X-" word
+ */
+ protected void handleValue(String pvalueval) throws VCardException {
+ if (sKnownValueSet.contains(pvalueval.toUpperCase()) ||
+ pvalueval.startsWith("X-")) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("VALUE");
+ mBuilder.propertyParamValue(pvalueval);
+ }
+ } else {
+ throw new VCardException("Unknown value \"" + pvalueval + "\"");
+ }
+ }
+
+ /**
+ * pencodingval = "7BIT" / "8BIT" / "QUOTED-PRINTABLE" / "BASE64" / "X-" word
+ */
+ protected void handleEncoding(String pencodingval) throws VCardException {
+ if (isValidEncoding(pencodingval) ||
+ pencodingval.startsWith("X-")) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("ENCODING");
+ mBuilder.propertyParamValue(pencodingval);
+ }
+ mEncoding = pencodingval;
+ } else {
+ throw new VCardException("Unknown encoding \"" + pencodingval + "\"");
+ }
+ }
+
+ /**
+ * vCard specification only allows us-ascii and iso-8859-xxx (See RFC 1521),
+ * but some vCard contains other charset, so we allow them.
+ */
+ protected void handleCharset(String charsetval) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("CHARSET");
+ mBuilder.propertyParamValue(charsetval);
+ }
+ }
+
+ /**
+ * See also Section 7.1 of RFC 1521
+ */
+ protected void handleLanguage(String langval) throws VCardException {
+ String[] strArray = langval.split("-");
+ if (strArray.length != 2) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ String tmp = strArray[0];
+ int length = tmp.length();
+ for (int i = 0; i < length; i++) {
+ if (!isLetter(tmp.charAt(i))) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ }
+ tmp = strArray[1];
+ length = tmp.length();
+ for (int i = 0; i < length; i++) {
+ if (!isLetter(tmp.charAt(i))) {
+ throw new VCardException("Invalid Language: \"" + langval + "\"");
+ }
+ }
+ if (mBuilder != null) {
+ mBuilder.propertyParamType("LANGUAGE");
+ mBuilder.propertyParamValue(langval);
+ }
+ }
+
+ /**
+ * Mainly for "X-" type. This accepts any kind of type without check.
+ */
+ protected void handleAnyParam(String paramName, String paramValue) {
+ if (mBuilder != null) {
+ mBuilder.propertyParamType(paramName);
+ mBuilder.propertyParamValue(paramValue);
+ }
+ }
+
+ protected void handlePropertyValue(String propertyName, String propertyValue) throws
+ IOException, VCardException {
+ if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
+ long start = System.currentTimeMillis();
+ String result = getQuotedPrintable(propertyValue);
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(result);
+ mBuilder.propertyValues(v);
+ }
+ mTimeHandleQuotedPrintable += System.currentTimeMillis() - start;
+ } else if (mEncoding.equalsIgnoreCase("BASE64") ||
+ mEncoding.equalsIgnoreCase("B")) {
+ long start = System.currentTimeMillis();
+ // It is very rare, but some BASE64 data may be so big that
+ // OutOfMemoryError occurs. To ignore such cases, use try-catch.
+ try {
+ String result = getBase64(propertyValue);
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(result);
+ mBuilder.propertyValues(v);
+ }
+ } catch (OutOfMemoryError error) {
+ Log.e(LOG_TAG, "OutOfMemoryError happened during parsing BASE64 data!");
+ if (mBuilder != null) {
+ mBuilder.propertyValues(null);
+ }
+ }
+ mTimeHandleBase64 += System.currentTimeMillis() - start;
+ } else {
+ if (!(mEncoding == null || mEncoding.equalsIgnoreCase("7BIT")
+ || mEncoding.equalsIgnoreCase("8BIT")
+ || mEncoding.toUpperCase().startsWith("X-"))) {
+ Log.w(LOG_TAG, "The encoding unsupported by vCard spec: \"" + mEncoding + "\".");
+ }
+
+ long start = System.currentTimeMillis();
+ if (mBuilder != null) {
+ ArrayList<String> v = new ArrayList<String>();
+ v.add(maybeUnescapeText(propertyValue));
+ mBuilder.propertyValues(v);
+ }
+ mTimeHandleMiscPropertyValue += System.currentTimeMillis() - start;
+ }
+ }
+
+ protected String getQuotedPrintable(String firstString) throws IOException, VCardException {
+ // Specifically, there may be some padding between = and CRLF.
+ // See the following:
+ //
+ // qp-line := *(qp-segment transport-padding CRLF)
+ // qp-part transport-padding
+ // qp-segment := qp-section *(SPACE / TAB) "="
+ // ; Maximum length of 76 characters
+ //
+ // e.g. (from RFC 2045)
+ // Now's the time =
+ // for all folk to come=
+ // to the aid of their country.
+ if (firstString.trim().endsWith("=")) {
+ // remove "transport-padding"
+ int pos = firstString.length() - 1;
+ while(firstString.charAt(pos) != '=') {
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString.substring(0, pos + 1));
+ builder.append("\r\n");
+ String line;
+ while (true) {
+ line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing quoted-printable String");
+ }
+ if (line.trim().endsWith("=")) {
+ // remove "transport-padding"
+ pos = line.length() - 1;
+ while(line.charAt(pos) != '=') {
+ }
+ builder.append(line.substring(0, pos + 1));
+ builder.append("\r\n");
+ } else {
+ builder.append(line);
+ break;
+ }
+ }
+ return builder.toString();
+ } else {
+ return firstString;
+ }
+ }
+
+ protected String getBase64(String firstString) throws IOException, VCardException {
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString);
+
+ while (true) {
+ String line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing BASE64 binary");
+ }
+ if (line.length() == 0) {
+ break;
+ }
+ builder.append(line);
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Mainly for "ADR", "ORG", and "N"
+ * We do not care the number of strnosemi here.
+ *
+ * addressparts = 0*6(strnosemi ";") strnosemi
+ * ; PO Box, Extended Addr, Street, Locality, Region,
+ * Postal Code, Country Name
+ * orgparts = *(strnosemi ";") strnosemi
+ * ; First is Organization Name,
+ * remainder are Organization Units.
+ * nameparts = 0*4(strnosemi ";") strnosemi
+ * ; Family, Given, Middle, Prefix, Suffix.
+ * ; Example:Public;John;Q.;Reverend Dr.;III, Esq.
+ * strnosemi = *(*nonsemi ("\;" / "\" CRLF)) *nonsemi
+ * ; To include a semicolon in this string, it must be escaped
+ * ; with a "\" character.
+ *
+ * We are not sure whether we should add "\" CRLF to each value.
+ * For now, we exclude them.
+ */
+ protected void handleMultiplePropertyValue(String propertyName, String propertyValue)
+ throws IOException, VCardException {
+ // vCard 2.1 does not allow QUOTED-PRINTABLE here,
+ // but some softwares/devices emit such data.
+ if (mEncoding.equalsIgnoreCase("QUOTED-PRINTABLE")) {
+ propertyValue = getQuotedPrintable(propertyValue);
+ }
+
+ if (mBuilder != null) {
+ StringBuilder builder = new StringBuilder();
+ ArrayList<String> list = new ArrayList<String>();
+ int length = propertyValue.length();
+ for (int i = 0; i < length; i++) {
+ char ch = propertyValue.charAt(i);
+ if (ch == '\\' && i < length - 1) {
+ char nextCh = propertyValue.charAt(i + 1);
+ String unescapedString = maybeUnescapeCharacter(nextCh);
+ if (unescapedString != null) {
+ builder.append(unescapedString);
+ i++;
+ } else {
+ builder.append(ch);
+ }
+ } else if (ch == ';') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else {
+ builder.append(ch);
+ }
+ }
+ list.add(builder.toString());
+ mBuilder.propertyValues(list);
+ }
+ }
+
+ /**
+ * vCard 2.1 specifies AGENT allows one vcard entry. It is not encoded at all.
+ *
+ * item = ...
+ * / [groups "."] "AGENT"
+ * [params] ":" vcard CRLF
+ * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF
+ * items *CRLF "END" [ws] ":" [ws] "VCARD"
+ *
+ */
+ protected void handleAgent(String propertyValue) throws VCardException {
+ throw new VCardNotSupportedException("AGENT Property is not supported now.");
+ /* This is insufficient support. Also, AGENT Property is very rare.
+ Ignore it for now.
+
+ String[] strArray = propertyValue.split(":", 2);
+ if (!(strArray.length == 2 ||
+ strArray[0].trim().equalsIgnoreCase("BEGIN") &&
+ strArray[1].trim().equalsIgnoreCase("VCARD"))) {
+ throw new VCardException("BEGIN:VCARD != \"" + propertyValue + "\"");
+ }
+ parseItems();
+ readEndVCard();
+ */
+ }
+
+ /**
+ * For vCard 3.0.
+ */
+ protected String maybeUnescapeText(String text) {
+ return text;
+ }
+
+ /**
+ * Returns unescaped String if the character should be unescaped. Return null otherwise.
+ * e.g. In vCard 2.1, "\;" should be unescaped into ";" while "\x" should not be.
+ */
+ protected String maybeUnescapeCharacter(char ch) {
+ // Original vCard 2.1 specification does not allow transformation
+ // "\:" -> ":", "\," -> ",", and "\\" -> "\", but previous implementation of
+ // this class allowed them, so keep it as is.
+ if (ch == '\\' || ch == ';' || ch == ':' || ch == ',') {
+ return String.valueOf(ch);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public boolean parse(InputStream is, VCardBuilder builder)
+ throws IOException, VCardException {
+ return parse(is, VCardConfig.DEFAULT_CHARSET, builder);
+ }
+
+ @Override
+ public boolean parse(InputStream is, String charset, VCardBuilder builder)
+ throws IOException, VCardException {
+ final InputStreamReader tmpReader = new InputStreamReader(is, charset);
+ if (VCardConfig.showPerformanceLog()) {
+ mReader = new CustomBufferedReader(tmpReader);
+ } else {
+ mReader = new BufferedReader(tmpReader);
+ }
+
+ mBuilder = builder;
+
+ long start = System.currentTimeMillis();
+ if (mBuilder != null) {
+ mBuilder.start();
+ }
+ parseVCardFile();
+ if (mBuilder != null) {
+ mBuilder.end();
+ }
+ mTimeTotal += System.currentTimeMillis() - start;
+
+ if (VCardConfig.showPerformanceLog()) {
+ showPerformanceInfo();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void parse(InputStream is, String charset, VCardBuilder builder, boolean canceled)
+ throws IOException, VCardException {
+ mCanceled = canceled;
+ parse(is, charset, builder);
+ }
+
+ private void showPerformanceInfo() {
+ Log.d(LOG_TAG, "Total parsing time: " + mTimeTotal + " ms");
+ if (mReader instanceof CustomBufferedReader) {
+ Log.d(LOG_TAG, "Total readLine time: " +
+ ((CustomBufferedReader)mReader).getTotalmillisecond() + " ms");
+ }
+ Log.d(LOG_TAG, "Time for handling the beggining of the record: " +
+ mTimeReadStartRecord + " ms");
+ Log.d(LOG_TAG, "Time for handling the end of the record: " +
+ mTimeReadEndRecord + " ms");
+ Log.d(LOG_TAG, "Time for parsing line, and handling group: " +
+ mTimeParseLineAndHandleGroup + " ms");
+ Log.d(LOG_TAG, "Time for parsing ADR, ORG, and N fields:" + mTimeParseAdrOrgN + " ms");
+ Log.d(LOG_TAG, "Time for parsing property values: " + mTimeParsePropertyValues + " ms");
+ Log.d(LOG_TAG, "Time for handling normal property values: " +
+ mTimeHandleMiscPropertyValue + " ms");
+ Log.d(LOG_TAG, "Time for handling Quoted-Printable: " +
+ mTimeHandleQuotedPrintable + " ms");
+ Log.d(LOG_TAG, "Time for handling Base64: " + mTimeHandleBase64 + " ms");
+ }
+
+ private boolean isLetter(char ch) {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+ return true;
+ }
+ return false;
+ }
+}
+
+class CustomBufferedReader extends BufferedReader {
+ private long mTime;
+
+ public CustomBufferedReader(Reader in) {
+ super(in);
+ }
+
+ @Override
+ public String readLine() throws IOException {
+ long start = System.currentTimeMillis();
+ String ret = super.readLine();
+ long end = System.currentTimeMillis();
+ mTime += end - start;
+ return ret;
+ }
+
+ public long getTotalmillisecond() {
+ return mTime;
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardParser_V30.java b/core/java/android/pim/vcard/VCardParser_V30.java
new file mode 100644
index 0000000..384649a
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardParser_V30.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.pim.vcard.exception.VCardException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * This class is used to parse vcard3.0. <br>
+ * Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426)
+ */
+public class VCardParser_V30 extends VCardParser_V21 {
+ private static final String LOG_TAG = "vcard.VCardParser_V30";
+
+ private static final HashSet<String> sAcceptablePropsWithParam = new HashSet<String>(
+ Arrays.asList(
+ "BEGIN", "LOGO", "PHOTO", "LABEL", "FN", "TITLE", "SOUND",
+ "VERSION", "TEL", "EMAIL", "TZ", "GEO", "NOTE", "URL",
+ "BDAY", "ROLE", "REV", "UID", "KEY", "MAILER", // 2.1
+ "NAME", "PROFILE", "SOURCE", "NICKNAME", "CLASS",
+ "SORT-STRING", "CATEGORIES", "PRODID")); // 3.0
+
+ // Although "7bit" and "BASE64" is not allowed in vCard 3.0, we allow it for safety.
+ private static final HashSet<String> sAcceptableEncodingV30 = new HashSet<String>(
+ Arrays.asList("7BIT", "8BIT", "BASE64", "B"));
+
+ // Although RFC 2426 specifies some property must not have parameters, we allow it,
+ // since there may be some careers which violates the RFC...
+ private static final HashSet<String> acceptablePropsWithoutParam = new HashSet<String>();
+
+ private String mPreviousLine;
+
+ private boolean mEmittedAgentWarning = false;
+
+ @Override
+ protected String getVersion() {
+ return Constants.VERSION_V30;
+ }
+
+ @Override
+ protected boolean isValidPropertyName(String propertyName) {
+ if (!(sAcceptablePropsWithParam.contains(propertyName) ||
+ acceptablePropsWithoutParam.contains(propertyName) ||
+ propertyName.startsWith("X-")) &&
+ !mWarningValueMap.contains(propertyName)) {
+ mWarningValueMap.add(propertyName);
+ Log.w(LOG_TAG, "Property name unsupported by vCard 3.0: " + propertyName);
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean isValidEncoding(String encoding) {
+ return sAcceptableEncodingV30.contains(encoding.toUpperCase());
+ }
+
+ @Override
+ protected String getLine() throws IOException {
+ if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ } else {
+ return mReader.readLine();
+ }
+ }
+
+ /**
+ * vCard 3.0 requires that the line with space at the beginning of the line
+ * must be combined with previous line.
+ */
+ @Override
+ protected String getNonEmptyLine() throws IOException, VCardException {
+ String line;
+ StringBuilder builder = null;
+ while (true) {
+ line = mReader.readLine();
+ if (line == null) {
+ if (builder != null) {
+ return builder.toString();
+ } else if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ }
+ throw new VCardException("Reached end of buffer.");
+ } else if (line.length() == 0) {
+ if (builder != null) {
+ return builder.toString();
+ } else if (mPreviousLine != null) {
+ String ret = mPreviousLine;
+ mPreviousLine = null;
+ return ret;
+ }
+ } else if (line.charAt(0) == ' ' || line.charAt(0) == '\t') {
+ if (builder != null) {
+ // See Section 5.8.1 of RFC 2425 (MIME-DIR document).
+ // Following is the excerpts from it.
+ //
+ // DESCRIPTION:This is a long description that exists on a long line.
+ //
+ // Can be represented as:
+ //
+ // DESCRIPTION:This is a long description
+ // that exists on a long line.
+ //
+ // It could also be represented as:
+ //
+ // DESCRIPTION:This is a long descrip
+ // tion that exists o
+ // n a long line.
+ builder.append(line.substring(1));
+ } else if (mPreviousLine != null) {
+ builder = new StringBuilder();
+ builder.append(mPreviousLine);
+ mPreviousLine = null;
+ builder.append(line.substring(1));
+ } else {
+ throw new VCardException("Space exists at the beginning of the line");
+ }
+ } else {
+ if (mPreviousLine == null) {
+ mPreviousLine = line;
+ if (builder != null) {
+ return builder.toString();
+ }
+ } else {
+ String ret = mPreviousLine;
+ mPreviousLine = line;
+ return ret;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * vcard = [group "."] "BEGIN" ":" "VCARD" 1*CRLF
+ * 1*(contentline)
+ * ;A vCard object MUST include the VERSION, FN and N types.
+ * [group "."] "END" ":" "VCARD" 1*CRLF
+ */
+ @Override
+ protected boolean readBeginVCard(boolean allowGarbage) throws IOException, VCardException {
+ // TODO: vCard 3.0 supports group.
+ return super.readBeginVCard(allowGarbage);
+ }
+
+ @Override
+ protected void readEndVCard(boolean useCache, boolean allowGarbage)
+ throws IOException, VCardException {
+ // TODO: vCard 3.0 supports group.
+ super.readEndVCard(useCache, allowGarbage);
+ }
+
+ /**
+ * vCard 3.0 allows iana-token as paramType, while vCard 2.1 does not.
+ */
+ @Override
+ protected void handleParams(String params) throws VCardException {
+ try {
+ super.handleParams(params);
+ } catch (VCardException e) {
+ // maybe IANA type
+ String[] strArray = params.split("=", 2);
+ if (strArray.length == 2) {
+ handleAnyParam(strArray[0], strArray[1]);
+ } else {
+ // Must not come here in the current implementation.
+ throw new VCardException(
+ "Unknown params value: " + params);
+ }
+ }
+ }
+
+ @Override
+ protected void handleAnyParam(String paramName, String paramValue) {
+ // vCard 3.0 accept comma-separated multiple values, but
+ // current PropertyNode does not accept it.
+ // For now, we do not split the values.
+ //
+ // TODO: fix this.
+ super.handleAnyParam(paramName, paramValue);
+ }
+
+ /**
+ * vCard 3.0 defines
+ *
+ * param = param-name "=" param-value *("," param-value)
+ * param-name = iana-token / x-name
+ * param-value = ptext / quoted-string
+ * quoted-string = DQUOTE QSAFE-CHAR DQUOTE
+ */
+ @Override
+ protected void handleType(String ptypevalues) {
+ String[] ptypeArray = ptypevalues.split(",");
+ mBuilder.propertyParamType("TYPE");
+ for (String value : ptypeArray) {
+ int length = value.length();
+ if (length >= 2 && value.startsWith("\"") && value.endsWith("\"")) {
+ mBuilder.propertyParamValue(value.substring(1, value.length() - 1));
+ } else {
+ mBuilder.propertyParamValue(value);
+ }
+ }
+ }
+
+ @Override
+ protected void handleAgent(String propertyValue) {
+ // The way how vCard 3.0 supports "AGENT" is completely different from vCard 2.0.
+ //
+ // e.g.
+ // AGENT:BEGIN:VCARD\nFN:Joe Friday\nTEL:+1-919-555-7878\n
+ // TITLE:Area Administrator\, Assistant\n EMAIL\;TYPE=INTERN\n
+ // ET:jfriday@host.com\nEND:VCARD\n
+ //
+ // TODO: fix this.
+ //
+ // issue:
+ // vCard 3.0 also allows this as an example.
+ //
+ // AGENT;VALUE=uri:
+ // CID:JQPUBLIC.part3.960129T083020.xyzMail@host3.com
+ //
+ // This is not VCARD. Should we support this?
+ // throw new VCardException("AGENT in vCard 3.0 is not supported yet.");
+ if (!mEmittedAgentWarning) {
+ Log.w(LOG_TAG, "AGENT in vCard 3.0 is not supported yet. Ignore it");
+ mEmittedAgentWarning = true;
+ }
+ // Just ignore the line for now, since we cannot know how to handle it...
+ }
+
+ /**
+ * vCard 3.0 does not require two CRLF at the last of BASE64 data.
+ * It only requires that data should be MIME-encoded.
+ */
+ @Override
+ protected String getBase64(String firstString) throws IOException, VCardException {
+ StringBuilder builder = new StringBuilder();
+ builder.append(firstString);
+
+ while (true) {
+ String line = getLine();
+ if (line == null) {
+ throw new VCardException(
+ "File ended during parsing BASE64 binary");
+ }
+ if (line.length() == 0) {
+ break;
+ } else if (!line.startsWith(" ") && !line.startsWith("\t")) {
+ mPreviousLine = line;
+ break;
+ }
+ builder.append(line);
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * ESCAPED-CHAR = "\\" / "\;" / "\," / "\n" / "\N")
+ * ; \\ encodes \, \n or \N encodes newline
+ * ; \; encodes ;, \, encodes ,
+ *
+ * Note: Apple escape ':' into '\:' while does not escape '\'
+ */
+ @Override
+ protected String maybeUnescapeText(String text) {
+ StringBuilder builder = new StringBuilder();
+ int length = text.length();
+ for (int i = 0; i < length; i++) {
+ char ch = text.charAt(i);
+ if (ch == '\\' && i < length - 1) {
+ char next_ch = text.charAt(++i);
+ if (next_ch == 'n' || next_ch == 'N') {
+ builder.append("\n");
+ } else {
+ builder.append(next_ch);
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ return builder.toString();
+ }
+
+ @Override
+ protected String maybeUnescapeCharacter(char ch) {
+ if (ch == 'n' || ch == 'N') {
+ return "\n";
+ } else {
+ return String.valueOf(ch);
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardSourceDetector.java b/core/java/android/pim/vcard/VCardSourceDetector.java
new file mode 100644
index 0000000..7e2be2b
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardSourceDetector.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Class which tries to detects the source of the vCard from its properties.
+ * Currently this implementation is very premature.
+ * @hide
+ */
+public class VCardSourceDetector implements VCardBuilder {
+ // Should only be used in package.
+ static final int TYPE_UNKNOWN = 0;
+ static final int TYPE_APPLE = 1;
+ static final int TYPE_JAPANESE_MOBILE_PHONE = 2; // Used in Japanese mobile phones.
+ static final int TYPE_FOMA = 3; // Used in some Japanese FOMA mobile phones.
+ static final int TYPE_WINDOWS_MOBILE_JP = 4;
+ // TODO: Excel, etc.
+
+ private static Set<String> APPLE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-PHONETIC-FIRST-NAME", "X-PHONETIC-MIDDLE-NAME", "X-PHONETIC-LAST-NAME",
+ "X-ABADR", "X-ABUID"));
+
+ private static Set<String> JAPANESE_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-GNO", "X-GN", "X-REDUCTION"));
+
+ private static Set<String> WINDOWS_MOBILE_PHONE_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-MICROSOFT-ASST_TEL", "X-MICROSOFT-ASSISTANT", "X-MICROSOFT-OFFICELOC"));
+
+ // Note: these signes appears before the signs of the other type (e.g. "X-GN").
+ // In other words, Japanese FOMA mobile phones are detected as FOMA, not JAPANESE_MOBILE_PHONES.
+ private static Set<String> FOMA_SIGNS = new HashSet<String>(Arrays.asList(
+ "X-SD-VERN", "X-SD-FORMAT_VER", "X-SD-CATEGORIES", "X-SD-CLASS", "X-SD-DCREATED",
+ "X-SD-DESCRIPTION"));
+ private static String TYPE_FOMA_CHARSET_SIGN = "X-SD-CHAR_CODE";
+
+ private int mType = TYPE_UNKNOWN;
+ // Some mobile phones (like FOMA) tells us the charset of the data.
+ private boolean mNeedParseSpecifiedCharset;
+ private String mSpecifiedCharset;
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ public void startRecord(String type) {
+ }
+
+ public void startProperty() {
+ mNeedParseSpecifiedCharset = false;
+ }
+
+ public void endProperty() {
+ }
+
+ public void endRecord() {
+ }
+
+ public void propertyGroup(String group) {
+ }
+
+ public void propertyName(String name) {
+ if (name.equalsIgnoreCase(TYPE_FOMA_CHARSET_SIGN)) {
+ mType = TYPE_FOMA;
+ mNeedParseSpecifiedCharset = true;
+ return;
+ }
+ if (mType != TYPE_UNKNOWN) {
+ return;
+ }
+ if (WINDOWS_MOBILE_PHONE_SIGNS.contains(name)) {
+ mType = TYPE_WINDOWS_MOBILE_JP;
+ } else if (FOMA_SIGNS.contains(name)) {
+ mType = TYPE_FOMA;
+ } else if (JAPANESE_MOBILE_PHONE_SIGNS.contains(name)) {
+ mType = TYPE_JAPANESE_MOBILE_PHONE;
+ } else if (APPLE_SIGNS.contains(name)) {
+ mType = TYPE_APPLE;
+ }
+ }
+
+ public void propertyParamType(String type) {
+ }
+
+ public void propertyParamValue(String value) {
+ }
+
+ public void propertyValues(List<String> values) {
+ if (mNeedParseSpecifiedCharset && values.size() > 0) {
+ mSpecifiedCharset = values.get(0);
+ }
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ /**
+ * Return charset String guessed from the source's properties.
+ * This method must be called after parsing target file(s).
+ * @return Charset String. Null is returned if guessing the source fails.
+ */
+ public String getEstimatedCharset() {
+ if (mSpecifiedCharset != null) {
+ return mSpecifiedCharset;
+ }
+ switch (mType) {
+ case TYPE_WINDOWS_MOBILE_JP:
+ case TYPE_FOMA:
+ case TYPE_JAPANESE_MOBILE_PHONE:
+ return "SHIFT_JIS";
+ case TYPE_APPLE:
+ return "UTF-8";
+ default:
+ return null;
+ }
+ }
+}
diff --git a/core/java/android/pim/vcard/VCardUtils.java b/core/java/android/pim/vcard/VCardUtils.java
new file mode 100644
index 0000000..ffceade
--- /dev/null
+++ b/core/java/android/pim/vcard/VCardUtils.java
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.text.TextUtils;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utilities for VCard handling codes.
+ */
+public class VCardUtils {
+ /*
+ * TODO: some of methods in this class should be placed to the more appropriate place...
+ */
+
+ // Note that not all types are included in this map/set, since, for example, TYPE_HOME_FAX is
+ // converted to two attribute Strings. These only contain some minor fields valid in both
+ // vCard and current (as of 2009-08-07) Contacts structure.
+ private static final Map<Integer, String> sKnownPhoneTypesMap_ItoS;
+ private static final Set<String> sPhoneTypesSetUnknownToContacts;
+
+ private static final Map<String, Integer> sKnownPhoneTypesMap_StoI;
+
+ static {
+ sKnownPhoneTypesMap_ItoS = new HashMap<Integer, String>();
+ sKnownPhoneTypesMap_StoI = new HashMap<String, Integer>();
+
+ sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_CAR, Constants.ATTR_TYPE_CAR);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CAR, Phone.TYPE_CAR);
+ sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_PAGER, Constants.ATTR_TYPE_PAGER);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PAGER, Phone.TYPE_PAGER);
+ sKnownPhoneTypesMap_ItoS.put(Phone.TYPE_ISDN, Constants.ATTR_TYPE_ISDN);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_ISDN, Phone.TYPE_ISDN);
+
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_HOME, Phone.TYPE_HOME);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_WORK, Phone.TYPE_WORK);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_CELL, Phone.TYPE_MOBILE);
+
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_OTHER, Phone.TYPE_OTHER);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_CALLBACK, Phone.TYPE_CALLBACK);
+ sKnownPhoneTypesMap_StoI.put(
+ Constants.ATTR_TYPE_PHONE_EXTRA_COMPANY_MAIN, Phone.TYPE_COMPANY_MAIN);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_RADIO, Phone.TYPE_RADIO);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TELEX, Phone.TYPE_TELEX);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_TTY_TDD, Phone.TYPE_TTY_TDD);
+ sKnownPhoneTypesMap_StoI.put(Constants.ATTR_TYPE_PHONE_EXTRA_ASSISTANT, Phone.TYPE_ASSISTANT);
+
+ sPhoneTypesSetUnknownToContacts = new HashSet<String>();
+ sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MODEM);
+ sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_MSG);
+ sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_BBS);
+ sPhoneTypesSetUnknownToContacts.add(Constants.ATTR_TYPE_VIDEO);
+ }
+
+ public static String getPhoneAttributeString(Integer type) {
+ return sKnownPhoneTypesMap_ItoS.get(type);
+ }
+
+ /**
+ * Returns Interger when the given types can be parsed as known type. Returns String object
+ * when not, which should be set to label.
+ */
+ public static Object getPhoneTypeFromStrings(Collection<String> types) {
+ int type = -1;
+ String label = null;
+ boolean isFax = false;
+ boolean hasPref = false;
+
+ if (types != null) {
+ for (String typeString : types) {
+ typeString = typeString.toUpperCase();
+ if (typeString.equals(Constants.ATTR_TYPE_PREF)) {
+ hasPref = true;
+ } else if (typeString.equals(Constants.ATTR_TYPE_FAX)) {
+ isFax = true;
+ } else {
+ if (typeString.startsWith("X-") && type < 0) {
+ typeString = typeString.substring(2);
+ }
+ Integer tmp = sKnownPhoneTypesMap_StoI.get(typeString);
+ if (tmp != null) {
+ type = tmp;
+ } else if (type < 0) {
+ type = Phone.TYPE_CUSTOM;
+ label = typeString;
+ }
+ }
+ }
+ }
+ if (type < 0) {
+ if (hasPref) {
+ type = Phone.TYPE_MAIN;
+ } else {
+ // default to TYPE_HOME
+ type = Phone.TYPE_HOME;
+ }
+ }
+ if (isFax) {
+ if (type == Phone.TYPE_HOME) {
+ type = Phone.TYPE_FAX_HOME;
+ } else if (type == Phone.TYPE_WORK) {
+ type = Phone.TYPE_FAX_WORK;
+ } else if (type == Phone.TYPE_OTHER) {
+ type = Phone.TYPE_OTHER_FAX;
+ }
+ }
+ if (type == Phone.TYPE_CUSTOM) {
+ return label;
+ } else {
+ return type;
+ }
+ }
+
+ public static boolean isValidPhoneAttribute(String phoneAttribute, int vcardType) {
+ // TODO: check the following.
+ // - it may violate vCard spec
+ // - it may contain non-ASCII characters
+ //
+ // TODO: use vcardType
+ return (phoneAttribute.startsWith("X-") || phoneAttribute.startsWith("x-") ||
+ sPhoneTypesSetUnknownToContacts.contains(phoneAttribute));
+ }
+
+ public static String[] sortNameElements(int vcardType,
+ String familyName, String middleName, String givenName) {
+ String[] list = new String[3];
+ switch (VCardConfig.getNameOrderType(vcardType)) {
+ case VCardConfig.NAME_ORDER_JAPANESE:
+ // TODO: Should handle Ascii case?
+ list[0] = familyName;
+ list[1] = middleName;
+ list[2] = givenName;
+ break;
+ case VCardConfig.NAME_ORDER_EUROPE:
+ list[0] = middleName;
+ list[1] = givenName;
+ list[2] = familyName;
+ break;
+ default:
+ list[0] = givenName;
+ list[1] = middleName;
+ list[2] = familyName;
+ break;
+ }
+ return list;
+ }
+
+ /**
+ * Inserts postal data into the builder object.
+ *
+ * Note that the data structure of ContactsContract is different from that defined in vCard.
+ * So some conversion may be performed in this method. See also
+ * {{@link #getVCardPostalElements(ContentValues)}
+ */
+ public static void insertStructuredPostalDataUsingContactsStruct(int vcardType,
+ final ContentProviderOperation.Builder builder,
+ final ContactStruct.PostalData postalData) {
+ builder.withValueBackReference(StructuredPostal.RAW_CONTACT_ID, 0);
+ builder.withValue(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+
+ builder.withValue(StructuredPostal.TYPE, postalData.type);
+ if (postalData.type == StructuredPostal.TYPE_CUSTOM) {
+ builder.withValue(StructuredPostal.LABEL, postalData.label);
+ }
+
+ builder.withValue(StructuredPostal.POBOX, postalData.pobox);
+ // Extended address is dropped since there's no relevant entry in ContactsContract.
+ builder.withValue(StructuredPostal.STREET, postalData.street);
+ builder.withValue(StructuredPostal.CITY, postalData.localty);
+ builder.withValue(StructuredPostal.REGION, postalData.region);
+ builder.withValue(StructuredPostal.POSTCODE, postalData.postalCode);
+ builder.withValue(StructuredPostal.COUNTRY, postalData.country);
+
+ builder.withValue(StructuredPostal.FORMATTED_ADDRESS,
+ postalData.getFormattedAddress(vcardType));
+ if (postalData.isPrimary) {
+ builder.withValue(Data.IS_PRIMARY, 1);
+ }
+ }
+
+ /**
+ * Returns String[] containing address information based on vCard spec
+ * (PO Box, Extended Address, Street, Locality, Region, Postal Code, Country Name).
+ * All String objects are non-null ("" is used when the relevant data is empty).
+ *
+ * Note that the data structure of ContactsContract is different from that defined in vCard.
+ * So some conversion may be performed in this method. See also
+ * {{@link #insertStructuredPostalDataUsingContactsStruct(int,
+ * android.content.ContentProviderOperation.Builder,
+ * android.pim.vcard.ContactStruct.PostalData)}
+ */
+ public static String[] getVCardPostalElements(ContentValues contentValues) {
+ String[] dataArray = new String[7];
+ dataArray[0] = contentValues.getAsString(StructuredPostal.POBOX);
+ if (dataArray[0] == null) {
+ dataArray[0] = "";
+ }
+ // Extended addr. There's no relevant data in ContactsContract.
+ dataArray[1] = "";
+ dataArray[2] = contentValues.getAsString(StructuredPostal.STREET);
+ if (dataArray[2] == null) {
+ dataArray[2] = "";
+ }
+ // Assume that localty == city
+ dataArray[3] = contentValues.getAsString(StructuredPostal.CITY);
+ if (dataArray[3] == null) {
+ dataArray[3] = "";
+ }
+ String region = contentValues.getAsString(StructuredPostal.REGION);
+ if (!TextUtils.isEmpty(region)) {
+ dataArray[4] = region;
+ } else {
+ dataArray[4] = "";
+ }
+ dataArray[5] = contentValues.getAsString(StructuredPostal.POSTCODE);
+ if (dataArray[5] == null) {
+ dataArray[5] = "";
+ }
+ dataArray[6] = contentValues.getAsString(StructuredPostal.COUNTRY);
+ if (dataArray[6] == null) {
+ dataArray[6] = "";
+ }
+
+ return dataArray;
+ }
+
+ public static String constructNameFromElements(int nameOrderType,
+ String familyName, String middleName, String givenName) {
+ return constructNameFromElements(nameOrderType, familyName, middleName, givenName,
+ null, null);
+ }
+
+ public static String constructNameFromElements(int nameOrderType,
+ String familyName, String middleName, String givenName,
+ String prefix, String suffix) {
+ StringBuilder builder = new StringBuilder();
+ String[] nameList = sortNameElements(nameOrderType,
+ familyName, middleName, givenName);
+ boolean first = true;
+ if (!TextUtils.isEmpty(prefix)) {
+ first = false;
+ builder.append(prefix);
+ }
+ for (String namePart : nameList) {
+ if (!TextUtils.isEmpty(namePart)) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(' ');
+ }
+ builder.append(namePart);
+ }
+ }
+ if (!TextUtils.isEmpty(suffix)) {
+ if (!first) {
+ builder.append(' ');
+ }
+ builder.append(suffix);
+ }
+ return builder.toString();
+ }
+
+ public static boolean containsOnlyAscii(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return true;
+ }
+
+ final int length = str.length();
+ final int asciiFirst = 0x20;
+ final int asciiLast = 0x126;
+ for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
+ int c = str.codePointAt(i);
+ if (c < asciiFirst || asciiLast < c) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This is useful since vCard 3.0 often requires the ("X-") properties and groups
+ * should contain only alphabets, digits, and hyphen.
+ *
+ * Note: It is already known some devices (wrongly) outputs properties with characters
+ * which should not be in the field. One example is "X-GOOGLE TALK". We appreciate
+ * such kind of input but must never output it unless the target is very specific
+ * to the device which is able to parse the malformed input.
+ */
+ public static boolean containsOnlyAlphaDigitHyphen(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return true;
+ }
+
+ final int lowerAlphabetFirst = 0x41; // included ('A')
+ final int lowerAlphabetLast = 0x5b; // not included ('[')
+ final int upperAlphabetFirst = 0x61; // included ('a')
+ final int upperAlphabetLast = 0x7b; // included ('{')
+ final int digitFirst = 0x30; // included ('0')
+ final int digitLast = 0x39; // included ('9')
+ final int hyphen = '-';
+ final int length = str.length();
+ for (int i = 0; i < length; i = str.offsetByCodePoints(i, 1)) {
+ int codepoint = str.codePointAt(i);
+ if (!((lowerAlphabetFirst <= codepoint && codepoint < lowerAlphabetLast) ||
+ (upperAlphabetFirst <= codepoint && codepoint < upperAlphabetLast) ||
+ (digitFirst <= codepoint && codepoint < digitLast) ||
+ (codepoint == hyphen))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // TODO: Replace wth the method in Base64 class.
+ private static char PAD = '=';
+ private static final char[] ENCODE64 = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+ };
+
+ static public String encodeBase64(byte[] data) {
+ if (data == null) {
+ return "";
+ }
+
+ char[] charBuffer = new char[(data.length + 2) / 3 * 4];
+ int position = 0;
+ int _3byte = 0;
+ for (int i=0; i<data.length-2; i+=3) {
+ _3byte = ((data[i] & 0xFF) << 16) + ((data[i+1] & 0xFF) << 8) + (data[i+2] & 0xFF);
+ charBuffer[position++] = ENCODE64[_3byte >> 18];
+ charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
+ charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
+ charBuffer[position++] = ENCODE64[_3byte & 0x3F];
+ }
+ switch(data.length % 3) {
+ case 1: // [111111][11 0000][0000 00][000000]
+ _3byte = ((data[data.length-1] & 0xFF) << 16);
+ charBuffer[position++] = ENCODE64[_3byte >> 18];
+ charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
+ charBuffer[position++] = PAD;
+ charBuffer[position++] = PAD;
+ break;
+ case 2: // [111111][11 1111][1111 00][000000]
+ _3byte = ((data[data.length-2] & 0xFF) << 16) + ((data[data.length-1] & 0xFF) << 8);
+ charBuffer[position++] = ENCODE64[_3byte >> 18];
+ charBuffer[position++] = ENCODE64[(_3byte >> 12) & 0x3F];
+ charBuffer[position++] = ENCODE64[(_3byte >> 6) & 0x3F];
+ charBuffer[position++] = PAD;
+ break;
+ }
+
+ return new String(charBuffer);
+ }
+
+ static public String toHalfWidthString(String orgString) {
+ if (TextUtils.isEmpty(orgString)) {
+ return null;
+ }
+ StringBuilder builder = new StringBuilder();
+ int length = orgString.length();
+ for (int i = 0; i < length; i++) {
+ // All Japanese character is able to be expressed by char.
+ // Do not need to use String#codepPointAt().
+ char ch = orgString.charAt(i);
+ CharSequence halfWidthText = JapaneseUtils.tryGetHalfWidthText(ch);
+ if (halfWidthText != null) {
+ builder.append(halfWidthText);
+ } else {
+ builder.append(ch);
+ }
+ }
+ return builder.toString();
+ }
+
+ private VCardUtils() {
+ }
+}
+
+/**
+ * TextUtils especially for Japanese.
+ * TODO: make this in android.text in the future
+ */
+class JapaneseUtils {
+ static private final Map<Character, String> sHalfWidthMap =
+ new HashMap<Character, String>();
+
+ static {
+ // There's no logical mapping rule in Unicode. Sigh.
+ sHalfWidthMap.put('\u3001', "\uFF64");
+ sHalfWidthMap.put('\u3002', "\uFF61");
+ sHalfWidthMap.put('\u300C', "\uFF62");
+ sHalfWidthMap.put('\u300D', "\uFF63");
+ sHalfWidthMap.put('\u301C', "~");
+ sHalfWidthMap.put('\u3041', "\uFF67");
+ sHalfWidthMap.put('\u3042', "\uFF71");
+ sHalfWidthMap.put('\u3043', "\uFF68");
+ sHalfWidthMap.put('\u3044', "\uFF72");
+ sHalfWidthMap.put('\u3045', "\uFF69");
+ sHalfWidthMap.put('\u3046', "\uFF73");
+ sHalfWidthMap.put('\u3047', "\uFF6A");
+ sHalfWidthMap.put('\u3048', "\uFF74");
+ sHalfWidthMap.put('\u3049', "\uFF6B");
+ sHalfWidthMap.put('\u304A', "\uFF75");
+ sHalfWidthMap.put('\u304B', "\uFF76");
+ sHalfWidthMap.put('\u304C', "\uFF76\uFF9E");
+ sHalfWidthMap.put('\u304D', "\uFF77");
+ sHalfWidthMap.put('\u304E', "\uFF77\uFF9E");
+ sHalfWidthMap.put('\u304F', "\uFF78");
+ sHalfWidthMap.put('\u3050', "\uFF78\uFF9E");
+ sHalfWidthMap.put('\u3051', "\uFF79");
+ sHalfWidthMap.put('\u3052', "\uFF79\uFF9E");
+ sHalfWidthMap.put('\u3053', "\uFF7A");
+ sHalfWidthMap.put('\u3054', "\uFF7A\uFF9E");
+ sHalfWidthMap.put('\u3055', "\uFF7B");
+ sHalfWidthMap.put('\u3056', "\uFF7B\uFF9E");
+ sHalfWidthMap.put('\u3057', "\uFF7C");
+ sHalfWidthMap.put('\u3058', "\uFF7C\uFF9E");
+ sHalfWidthMap.put('\u3059', "\uFF7D");
+ sHalfWidthMap.put('\u305A', "\uFF7D\uFF9E");
+ sHalfWidthMap.put('\u305B', "\uFF7E");
+ sHalfWidthMap.put('\u305C', "\uFF7E\uFF9E");
+ sHalfWidthMap.put('\u305D', "\uFF7F");
+ sHalfWidthMap.put('\u305E', "\uFF7F\uFF9E");
+ sHalfWidthMap.put('\u305F', "\uFF80");
+ sHalfWidthMap.put('\u3060', "\uFF80\uFF9E");
+ sHalfWidthMap.put('\u3061', "\uFF81");
+ sHalfWidthMap.put('\u3062', "\uFF81\uFF9E");
+ sHalfWidthMap.put('\u3063', "\uFF6F");
+ sHalfWidthMap.put('\u3064', "\uFF82");
+ sHalfWidthMap.put('\u3065', "\uFF82\uFF9E");
+ sHalfWidthMap.put('\u3066', "\uFF83");
+ sHalfWidthMap.put('\u3067', "\uFF83\uFF9E");
+ sHalfWidthMap.put('\u3068', "\uFF84");
+ sHalfWidthMap.put('\u3069', "\uFF84\uFF9E");
+ sHalfWidthMap.put('\u306A', "\uFF85");
+ sHalfWidthMap.put('\u306B', "\uFF86");
+ sHalfWidthMap.put('\u306C', "\uFF87");
+ sHalfWidthMap.put('\u306D', "\uFF88");
+ sHalfWidthMap.put('\u306E', "\uFF89");
+ sHalfWidthMap.put('\u306F', "\uFF8A");
+ sHalfWidthMap.put('\u3070', "\uFF8A\uFF9E");
+ sHalfWidthMap.put('\u3071', "\uFF8A\uFF9F");
+ sHalfWidthMap.put('\u3072', "\uFF8B");
+ sHalfWidthMap.put('\u3073', "\uFF8B\uFF9E");
+ sHalfWidthMap.put('\u3074', "\uFF8B\uFF9F");
+ sHalfWidthMap.put('\u3075', "\uFF8C");
+ sHalfWidthMap.put('\u3076', "\uFF8C\uFF9E");
+ sHalfWidthMap.put('\u3077', "\uFF8C\uFF9F");
+ sHalfWidthMap.put('\u3078', "\uFF8D");
+ sHalfWidthMap.put('\u3079', "\uFF8D\uFF9E");
+ sHalfWidthMap.put('\u307A', "\uFF8D\uFF9F");
+ sHalfWidthMap.put('\u307B', "\uFF8E");
+ sHalfWidthMap.put('\u307C', "\uFF8E\uFF9E");
+ sHalfWidthMap.put('\u307D', "\uFF8E\uFF9F");
+ sHalfWidthMap.put('\u307E', "\uFF8F");
+ sHalfWidthMap.put('\u307F', "\uFF90");
+ sHalfWidthMap.put('\u3080', "\uFF91");
+ sHalfWidthMap.put('\u3081', "\uFF92");
+ sHalfWidthMap.put('\u3082', "\uFF93");
+ sHalfWidthMap.put('\u3083', "\uFF6C");
+ sHalfWidthMap.put('\u3084', "\uFF94");
+ sHalfWidthMap.put('\u3085', "\uFF6D");
+ sHalfWidthMap.put('\u3086', "\uFF95");
+ sHalfWidthMap.put('\u3087', "\uFF6E");
+ sHalfWidthMap.put('\u3088', "\uFF96");
+ sHalfWidthMap.put('\u3089', "\uFF97");
+ sHalfWidthMap.put('\u308A', "\uFF98");
+ sHalfWidthMap.put('\u308B', "\uFF99");
+ sHalfWidthMap.put('\u308C', "\uFF9A");
+ sHalfWidthMap.put('\u308D', "\uFF9B");
+ sHalfWidthMap.put('\u308E', "\uFF9C");
+ sHalfWidthMap.put('\u308F', "\uFF9C");
+ sHalfWidthMap.put('\u3090', "\uFF72");
+ sHalfWidthMap.put('\u3091', "\uFF74");
+ sHalfWidthMap.put('\u3092', "\uFF66");
+ sHalfWidthMap.put('\u3093', "\uFF9D");
+ sHalfWidthMap.put('\u309B', "\uFF9E");
+ sHalfWidthMap.put('\u309C', "\uFF9F");
+ sHalfWidthMap.put('\u30A1', "\uFF67");
+ sHalfWidthMap.put('\u30A2', "\uFF71");
+ sHalfWidthMap.put('\u30A3', "\uFF68");
+ sHalfWidthMap.put('\u30A4', "\uFF72");
+ sHalfWidthMap.put('\u30A5', "\uFF69");
+ sHalfWidthMap.put('\u30A6', "\uFF73");
+ sHalfWidthMap.put('\u30A7', "\uFF6A");
+ sHalfWidthMap.put('\u30A8', "\uFF74");
+ sHalfWidthMap.put('\u30A9', "\uFF6B");
+ sHalfWidthMap.put('\u30AA', "\uFF75");
+ sHalfWidthMap.put('\u30AB', "\uFF76");
+ sHalfWidthMap.put('\u30AC', "\uFF76\uFF9E");
+ sHalfWidthMap.put('\u30AD', "\uFF77");
+ sHalfWidthMap.put('\u30AE', "\uFF77\uFF9E");
+ sHalfWidthMap.put('\u30AF', "\uFF78");
+ sHalfWidthMap.put('\u30B0', "\uFF78\uFF9E");
+ sHalfWidthMap.put('\u30B1', "\uFF79");
+ sHalfWidthMap.put('\u30B2', "\uFF79\uFF9E");
+ sHalfWidthMap.put('\u30B3', "\uFF7A");
+ sHalfWidthMap.put('\u30B4', "\uFF7A\uFF9E");
+ sHalfWidthMap.put('\u30B5', "\uFF7B");
+ sHalfWidthMap.put('\u30B6', "\uFF7B\uFF9E");
+ sHalfWidthMap.put('\u30B7', "\uFF7C");
+ sHalfWidthMap.put('\u30B8', "\uFF7C\uFF9E");
+ sHalfWidthMap.put('\u30B9', "\uFF7D");
+ sHalfWidthMap.put('\u30BA', "\uFF7D\uFF9E");
+ sHalfWidthMap.put('\u30BB', "\uFF7E");
+ sHalfWidthMap.put('\u30BC', "\uFF7E\uFF9E");
+ sHalfWidthMap.put('\u30BD', "\uFF7F");
+ sHalfWidthMap.put('\u30BE', "\uFF7F\uFF9E");
+ sHalfWidthMap.put('\u30BF', "\uFF80");
+ sHalfWidthMap.put('\u30C0', "\uFF80\uFF9E");
+ sHalfWidthMap.put('\u30C1', "\uFF81");
+ sHalfWidthMap.put('\u30C2', "\uFF81\uFF9E");
+ sHalfWidthMap.put('\u30C3', "\uFF6F");
+ sHalfWidthMap.put('\u30C4', "\uFF82");
+ sHalfWidthMap.put('\u30C5', "\uFF82\uFF9E");
+ sHalfWidthMap.put('\u30C6', "\uFF83");
+ sHalfWidthMap.put('\u30C7', "\uFF83\uFF9E");
+ sHalfWidthMap.put('\u30C8', "\uFF84");
+ sHalfWidthMap.put('\u30C9', "\uFF84\uFF9E");
+ sHalfWidthMap.put('\u30CA', "\uFF85");
+ sHalfWidthMap.put('\u30CB', "\uFF86");
+ sHalfWidthMap.put('\u30CC', "\uFF87");
+ sHalfWidthMap.put('\u30CD', "\uFF88");
+ sHalfWidthMap.put('\u30CE', "\uFF89");
+ sHalfWidthMap.put('\u30CF', "\uFF8A");
+ sHalfWidthMap.put('\u30D0', "\uFF8A\uFF9E");
+ sHalfWidthMap.put('\u30D1', "\uFF8A\uFF9F");
+ sHalfWidthMap.put('\u30D2', "\uFF8B");
+ sHalfWidthMap.put('\u30D3', "\uFF8B\uFF9E");
+ sHalfWidthMap.put('\u30D4', "\uFF8B\uFF9F");
+ sHalfWidthMap.put('\u30D5', "\uFF8C");
+ sHalfWidthMap.put('\u30D6', "\uFF8C\uFF9E");
+ sHalfWidthMap.put('\u30D7', "\uFF8C\uFF9F");
+ sHalfWidthMap.put('\u30D8', "\uFF8D");
+ sHalfWidthMap.put('\u30D9', "\uFF8D\uFF9E");
+ sHalfWidthMap.put('\u30DA', "\uFF8D\uFF9F");
+ sHalfWidthMap.put('\u30DB', "\uFF8E");
+ sHalfWidthMap.put('\u30DC', "\uFF8E\uFF9E");
+ sHalfWidthMap.put('\u30DD', "\uFF8E\uFF9F");
+ sHalfWidthMap.put('\u30DE', "\uFF8F");
+ sHalfWidthMap.put('\u30DF', "\uFF90");
+ sHalfWidthMap.put('\u30E0', "\uFF91");
+ sHalfWidthMap.put('\u30E1', "\uFF92");
+ sHalfWidthMap.put('\u30E2', "\uFF93");
+ sHalfWidthMap.put('\u30E3', "\uFF6C");
+ sHalfWidthMap.put('\u30E4', "\uFF94");
+ sHalfWidthMap.put('\u30E5', "\uFF6D");
+ sHalfWidthMap.put('\u30E6', "\uFF95");
+ sHalfWidthMap.put('\u30E7', "\uFF6E");
+ sHalfWidthMap.put('\u30E8', "\uFF96");
+ sHalfWidthMap.put('\u30E9', "\uFF97");
+ sHalfWidthMap.put('\u30EA', "\uFF98");
+ sHalfWidthMap.put('\u30EB', "\uFF99");
+ sHalfWidthMap.put('\u30EC', "\uFF9A");
+ sHalfWidthMap.put('\u30ED', "\uFF9B");
+ sHalfWidthMap.put('\u30EE', "\uFF9C");
+ sHalfWidthMap.put('\u30EF', "\uFF9C");
+ sHalfWidthMap.put('\u30F0', "\uFF72");
+ sHalfWidthMap.put('\u30F1', "\uFF74");
+ sHalfWidthMap.put('\u30F2', "\uFF66");
+ sHalfWidthMap.put('\u30F3', "\uFF9D");
+ sHalfWidthMap.put('\u30F4', "\uFF73\uFF9E");
+ sHalfWidthMap.put('\u30F5', "\uFF76");
+ sHalfWidthMap.put('\u30F6', "\uFF79");
+ sHalfWidthMap.put('\u30FB', "\uFF65");
+ sHalfWidthMap.put('\u30FC', "\uFF70");
+ sHalfWidthMap.put('\uFF01', "!");
+ sHalfWidthMap.put('\uFF02', "\"");
+ sHalfWidthMap.put('\uFF03', "#");
+ sHalfWidthMap.put('\uFF04', "$");
+ sHalfWidthMap.put('\uFF05', "%");
+ sHalfWidthMap.put('\uFF06', "&");
+ sHalfWidthMap.put('\uFF07', "'");
+ sHalfWidthMap.put('\uFF08', "(");
+ sHalfWidthMap.put('\uFF09', ")");
+ sHalfWidthMap.put('\uFF0A', "*");
+ sHalfWidthMap.put('\uFF0B', "+");
+ sHalfWidthMap.put('\uFF0C', ",");
+ sHalfWidthMap.put('\uFF0D', "-");
+ sHalfWidthMap.put('\uFF0E', ".");
+ sHalfWidthMap.put('\uFF0F', "/");
+ sHalfWidthMap.put('\uFF10', "0");
+ sHalfWidthMap.put('\uFF11', "1");
+ sHalfWidthMap.put('\uFF12', "2");
+ sHalfWidthMap.put('\uFF13', "3");
+ sHalfWidthMap.put('\uFF14', "4");
+ sHalfWidthMap.put('\uFF15', "5");
+ sHalfWidthMap.put('\uFF16', "6");
+ sHalfWidthMap.put('\uFF17', "7");
+ sHalfWidthMap.put('\uFF18', "8");
+ sHalfWidthMap.put('\uFF19', "9");
+ sHalfWidthMap.put('\uFF1A', ":");
+ sHalfWidthMap.put('\uFF1B', ";");
+ sHalfWidthMap.put('\uFF1C', "<");
+ sHalfWidthMap.put('\uFF1D', "=");
+ sHalfWidthMap.put('\uFF1E', ">");
+ sHalfWidthMap.put('\uFF1F', "?");
+ sHalfWidthMap.put('\uFF20', "@");
+ sHalfWidthMap.put('\uFF21', "A");
+ sHalfWidthMap.put('\uFF22', "B");
+ sHalfWidthMap.put('\uFF23', "C");
+ sHalfWidthMap.put('\uFF24', "D");
+ sHalfWidthMap.put('\uFF25', "E");
+ sHalfWidthMap.put('\uFF26', "F");
+ sHalfWidthMap.put('\uFF27', "G");
+ sHalfWidthMap.put('\uFF28', "H");
+ sHalfWidthMap.put('\uFF29', "I");
+ sHalfWidthMap.put('\uFF2A', "J");
+ sHalfWidthMap.put('\uFF2B', "K");
+ sHalfWidthMap.put('\uFF2C', "L");
+ sHalfWidthMap.put('\uFF2D', "M");
+ sHalfWidthMap.put('\uFF2E', "N");
+ sHalfWidthMap.put('\uFF2F', "O");
+ sHalfWidthMap.put('\uFF30', "P");
+ sHalfWidthMap.put('\uFF31', "Q");
+ sHalfWidthMap.put('\uFF32', "R");
+ sHalfWidthMap.put('\uFF33', "S");
+ sHalfWidthMap.put('\uFF34', "T");
+ sHalfWidthMap.put('\uFF35', "U");
+ sHalfWidthMap.put('\uFF36', "V");
+ sHalfWidthMap.put('\uFF37', "W");
+ sHalfWidthMap.put('\uFF38', "X");
+ sHalfWidthMap.put('\uFF39', "Y");
+ sHalfWidthMap.put('\uFF3A', "Z");
+ sHalfWidthMap.put('\uFF3B', "[");
+ sHalfWidthMap.put('\uFF3C', "\\");
+ sHalfWidthMap.put('\uFF3D', "]");
+ sHalfWidthMap.put('\uFF3E', "^");
+ sHalfWidthMap.put('\uFF3F', "_");
+ sHalfWidthMap.put('\uFF41', "a");
+ sHalfWidthMap.put('\uFF42', "b");
+ sHalfWidthMap.put('\uFF43', "c");
+ sHalfWidthMap.put('\uFF44', "d");
+ sHalfWidthMap.put('\uFF45', "e");
+ sHalfWidthMap.put('\uFF46', "f");
+ sHalfWidthMap.put('\uFF47', "g");
+ sHalfWidthMap.put('\uFF48', "h");
+ sHalfWidthMap.put('\uFF49', "i");
+ sHalfWidthMap.put('\uFF4A', "j");
+ sHalfWidthMap.put('\uFF4B', "k");
+ sHalfWidthMap.put('\uFF4C', "l");
+ sHalfWidthMap.put('\uFF4D', "m");
+ sHalfWidthMap.put('\uFF4E', "n");
+ sHalfWidthMap.put('\uFF4F', "o");
+ sHalfWidthMap.put('\uFF50', "p");
+ sHalfWidthMap.put('\uFF51', "q");
+ sHalfWidthMap.put('\uFF52', "r");
+ sHalfWidthMap.put('\uFF53', "s");
+ sHalfWidthMap.put('\uFF54', "t");
+ sHalfWidthMap.put('\uFF55', "u");
+ sHalfWidthMap.put('\uFF56', "v");
+ sHalfWidthMap.put('\uFF57', "w");
+ sHalfWidthMap.put('\uFF58', "x");
+ sHalfWidthMap.put('\uFF59', "y");
+ sHalfWidthMap.put('\uFF5A', "z");
+ sHalfWidthMap.put('\uFF5B', "{");
+ sHalfWidthMap.put('\uFF5C', "|");
+ sHalfWidthMap.put('\uFF5D', "}");
+ sHalfWidthMap.put('\uFF5E', "~");
+ sHalfWidthMap.put('\uFF61', "\uFF61");
+ sHalfWidthMap.put('\uFF62', "\uFF62");
+ sHalfWidthMap.put('\uFF63', "\uFF63");
+ sHalfWidthMap.put('\uFF64', "\uFF64");
+ sHalfWidthMap.put('\uFF65', "\uFF65");
+ sHalfWidthMap.put('\uFF66', "\uFF66");
+ sHalfWidthMap.put('\uFF67', "\uFF67");
+ sHalfWidthMap.put('\uFF68', "\uFF68");
+ sHalfWidthMap.put('\uFF69', "\uFF69");
+ sHalfWidthMap.put('\uFF6A', "\uFF6A");
+ sHalfWidthMap.put('\uFF6B', "\uFF6B");
+ sHalfWidthMap.put('\uFF6C', "\uFF6C");
+ sHalfWidthMap.put('\uFF6D', "\uFF6D");
+ sHalfWidthMap.put('\uFF6E', "\uFF6E");
+ sHalfWidthMap.put('\uFF6F', "\uFF6F");
+ sHalfWidthMap.put('\uFF70', "\uFF70");
+ sHalfWidthMap.put('\uFF71', "\uFF71");
+ sHalfWidthMap.put('\uFF72', "\uFF72");
+ sHalfWidthMap.put('\uFF73', "\uFF73");
+ sHalfWidthMap.put('\uFF74', "\uFF74");
+ sHalfWidthMap.put('\uFF75', "\uFF75");
+ sHalfWidthMap.put('\uFF76', "\uFF76");
+ sHalfWidthMap.put('\uFF77', "\uFF77");
+ sHalfWidthMap.put('\uFF78', "\uFF78");
+ sHalfWidthMap.put('\uFF79', "\uFF79");
+ sHalfWidthMap.put('\uFF7A', "\uFF7A");
+ sHalfWidthMap.put('\uFF7B', "\uFF7B");
+ sHalfWidthMap.put('\uFF7C', "\uFF7C");
+ sHalfWidthMap.put('\uFF7D', "\uFF7D");
+ sHalfWidthMap.put('\uFF7E', "\uFF7E");
+ sHalfWidthMap.put('\uFF7F', "\uFF7F");
+ sHalfWidthMap.put('\uFF80', "\uFF80");
+ sHalfWidthMap.put('\uFF81', "\uFF81");
+ sHalfWidthMap.put('\uFF82', "\uFF82");
+ sHalfWidthMap.put('\uFF83', "\uFF83");
+ sHalfWidthMap.put('\uFF84', "\uFF84");
+ sHalfWidthMap.put('\uFF85', "\uFF85");
+ sHalfWidthMap.put('\uFF86', "\uFF86");
+ sHalfWidthMap.put('\uFF87', "\uFF87");
+ sHalfWidthMap.put('\uFF88', "\uFF88");
+ sHalfWidthMap.put('\uFF89', "\uFF89");
+ sHalfWidthMap.put('\uFF8A', "\uFF8A");
+ sHalfWidthMap.put('\uFF8B', "\uFF8B");
+ sHalfWidthMap.put('\uFF8C', "\uFF8C");
+ sHalfWidthMap.put('\uFF8D', "\uFF8D");
+ sHalfWidthMap.put('\uFF8E', "\uFF8E");
+ sHalfWidthMap.put('\uFF8F', "\uFF8F");
+ sHalfWidthMap.put('\uFF90', "\uFF90");
+ sHalfWidthMap.put('\uFF91', "\uFF91");
+ sHalfWidthMap.put('\uFF92', "\uFF92");
+ sHalfWidthMap.put('\uFF93', "\uFF93");
+ sHalfWidthMap.put('\uFF94', "\uFF94");
+ sHalfWidthMap.put('\uFF95', "\uFF95");
+ sHalfWidthMap.put('\uFF96', "\uFF96");
+ sHalfWidthMap.put('\uFF97', "\uFF97");
+ sHalfWidthMap.put('\uFF98', "\uFF98");
+ sHalfWidthMap.put('\uFF99', "\uFF99");
+ sHalfWidthMap.put('\uFF9A', "\uFF9A");
+ sHalfWidthMap.put('\uFF9B', "\uFF9B");
+ sHalfWidthMap.put('\uFF9C', "\uFF9C");
+ sHalfWidthMap.put('\uFF9D', "\uFF9D");
+ sHalfWidthMap.put('\uFF9E', "\uFF9E");
+ sHalfWidthMap.put('\uFF9F', "\uFF9F");
+ sHalfWidthMap.put('\uFFE5', "\u005C\u005C");
+ }
+
+ /**
+ * Return half-width version of that character if possible. Return null if not possible
+ * @param ch input character
+ * @return CharSequence object if the mapping for ch exists. Return null otherwise.
+ */
+ public static CharSequence tryGetHalfWidthText(char ch) {
+ if (sHalfWidthMap.containsKey(ch)) {
+ return sHalfWidthMap.get(ch);
+ } else {
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/exception/VCardException.java b/core/java/android/pim/vcard/exception/VCardException.java
new file mode 100644
index 0000000..e557219
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard.exception;
+
+public class VCardException extends java.lang.Exception {
+ /**
+ * Constructs a VCardException object
+ */
+ public VCardException() {
+ super();
+ }
+
+ /**
+ * Constructs a VCardException object
+ *
+ * @param message the error message
+ */
+ public VCardException(String message) {
+ super(message);
+ }
+
+}
diff --git a/core/java/android/pim/vcard/exception/VCardNestedException.java b/core/java/android/pim/vcard/exception/VCardNestedException.java
new file mode 100644
index 0000000..503c2fb
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardNestedException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.pim.vcard.exception;
+
+/**
+ * VCardException thrown when VCard is nested without VCardParser's being notified.
+ */
+public class VCardNestedException extends VCardNotSupportedException {
+ public VCardNestedException() {
+ super();
+ }
+ public VCardNestedException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/pim/vcard/exception/VCardNotSupportedException.java b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
new file mode 100644
index 0000000..616aa77
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardNotSupportedException.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.pim.vcard.exception;
+
+/**
+ * The exception which tells that the input VCard is probably valid from the view of
+ * specification but not supported in the current framework for now.
+ *
+ * This is a kind of a good news from the view of development.
+ * It may be good to ask users to send a report with the VCard example
+ * for the future development.
+ */
+public class VCardNotSupportedException extends VCardException {
+ public VCardNotSupportedException() {
+ super();
+ }
+ public VCardNotSupportedException(String message) {
+ super(message);
+ }
+} \ No newline at end of file
diff --git a/core/java/android/pim/vcard/exception/VCardVersionException.java b/core/java/android/pim/vcard/exception/VCardVersionException.java
new file mode 100644
index 0000000..9fe8b7f
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/VCardVersionException.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.pim.vcard.exception;
+
+/**
+ * VCardException used only when the version of the vCard is different.
+ */
+public class VCardVersionException extends VCardException {
+ public VCardVersionException() {
+ super();
+ }
+ public VCardVersionException(String message) {
+ super(message);
+ }
+}
diff --git a/core/java/android/pim/vcard/exception/package.html b/core/java/android/pim/vcard/exception/package.html
new file mode 100644
index 0000000..26b8a32
--- /dev/null
+++ b/core/java/android/pim/vcard/exception/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+{@hide}
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/core/java/android/pim/vcard/package.html b/core/java/android/pim/vcard/package.html
new file mode 100644
index 0000000..26b8a32
--- /dev/null
+++ b/core/java/android/pim/vcard/package.html
@@ -0,0 +1,5 @@
+<HTML>
+<BODY>
+{@hide}
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index cf5664c..f16a7e4 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -149,14 +149,12 @@ public class CheckBoxPreference extends Preference {
* @param checked The checked state.
*/
public void setChecked(boolean checked) {
-
- mChecked = checked;
-
- persistBoolean(checked);
-
- notifyDependencyChange(shouldDisableDependents());
-
- notifyChanged();
+ if (mChecked != checked) {
+ mChecked = checked;
+ persistBoolean(checked);
+ notifyDependencyChange(shouldDisableDependents());
+ notifyChanged();
+ }
}
/**
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index fc39573..08a2a9f 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -1456,8 +1456,10 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
if (!TextUtils.isEmpty(summary)) {
sb.append(summary).append(' ');
}
- // Drop the last space
- sb.setLength(sb.length() - 1);
+ if (sb.length() > 0) {
+ // Drop the last space
+ sb.setLength(sb.length() - 1);
+ }
return sb;
}
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index a7a3eef..fe3471d 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -134,7 +134,10 @@ public class PreferenceManager {
private OnPreferenceTreeClickListener mOnPreferenceTreeClickListener;
- PreferenceManager(Activity activity, int firstRequestCode) {
+ /**
+ * @hide
+ */
+ public PreferenceManager(Activity activity, int firstRequestCode) {
mActivity = activity;
mNextRequestCode = firstRequestCode;
@@ -240,8 +243,9 @@ public class PreferenceManager {
* hierarchies into.
* @return The root hierarchy (if one was not provided, the new hierarchy's
* root).
+ * @hide
*/
- PreferenceScreen inflateFromResource(Context context, int resId,
+ public PreferenceScreen inflateFromResource(Context context, int resId,
PreferenceScreen rootPreferences) {
// Block commits
setNoCommit(true);
diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java
index 6beb06d..b46f180 100644
--- a/core/java/android/preference/RingtonePreference.java
+++ b/core/java/android/preference/RingtonePreference.java
@@ -31,8 +31,9 @@ import android.util.Log;
* The chosen ringtone's URI will be persisted as a string.
* <p>
* If the user chooses the "Default" item, the saved string will be one of
- * {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI}. If the user chooses the "Silent"
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
* item, the saved string will be an empty string.
*
* @attr ref android.R.styleable#RingtonePreference_ringtoneType
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index 20702a1..b337d28 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -16,17 +16,21 @@
package android.preference;
+import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
-import android.media.AudioManager;
+import android.net.Uri;
import android.os.Handler;
-import android.preference.PreferenceManager;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.provider.Settings;
import android.provider.Settings.System;
import android.util.AttributeSet;
+import android.view.KeyEvent;
import android.view.View;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -35,12 +39,12 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
* @hide
*/
public class VolumePreference extends SeekBarPreference implements
- PreferenceManager.OnActivityStopListener {
+ PreferenceManager.OnActivityStopListener, View.OnKeyListener {
private static final String TAG = "VolumePreference";
private int mStreamType;
-
+
/** May be null if the dialog isn't visible. */
private SeekBarVolumizer mSeekBarVolumizer;
@@ -52,7 +56,7 @@ public class VolumePreference extends SeekBarPreference implements
mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0);
a.recycle();
}
-
+
public void setStreamType(int streamType) {
mStreamType = streamType;
}
@@ -63,8 +67,34 @@ public class VolumePreference extends SeekBarPreference implements
final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
-
+
getPreferenceManager().registerOnActivityStopListener(this);
+
+ // grab focus and key events so that pressing the volume buttons in the
+ // dialog doesn't also show the normal volume adjust toast.
+ view.setOnKeyListener(this);
+ view.setFocusableInTouchMode(true);
+ view.requestFocus();
+ }
+
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // If key arrives immediately after the activity has been cleaned up.
+ if (mSeekBarVolumizer == null) return true;
+ boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ if (isdown) {
+ mSeekBarVolumizer.changeVolumeBy(-1);
+ }
+ return true;
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ if (isdown) {
+ mSeekBarVolumizer.changeVolumeBy(1);
+ }
+ return true;
+ default:
+ return false;
+ }
}
@Override
@@ -74,7 +104,7 @@ public class VolumePreference extends SeekBarPreference implements
if (!positiveResult && mSeekBarVolumizer != null) {
mSeekBarVolumizer.revertVolume();
}
-
+
cleanup();
}
@@ -87,19 +117,96 @@ public class VolumePreference extends SeekBarPreference implements
*/
private void cleanup() {
getPreferenceManager().unregisterOnActivityStopListener(this);
-
+
if (mSeekBarVolumizer != null) {
+ Dialog dialog = getDialog();
+ if (dialog != null && dialog.isShowing()) {
+ // Stopped while dialog was showing, revert changes
+ mSeekBarVolumizer.revertVolume();
+ }
mSeekBarVolumizer.stop();
mSeekBarVolumizer = null;
}
+
}
-
+
protected void onSampleStarting(SeekBarVolumizer volumizer) {
if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) {
mSeekBarVolumizer.stopSample();
}
}
-
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final Parcelable superState = super.onSaveInstanceState();
+ if (isPersistent()) {
+ // No need to save instance state since it's persistent
+ return superState;
+ }
+
+ final SavedState myState = new SavedState(superState);
+ if (mSeekBarVolumizer != null) {
+ mSeekBarVolumizer.onSaveInstanceState(myState.getVolumeStore());
+ }
+ return myState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(SavedState.class)) {
+ // Didn't save state for us in onSaveInstanceState
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ SavedState myState = (SavedState) state;
+ super.onRestoreInstanceState(myState.getSuperState());
+ if (mSeekBarVolumizer != null) {
+ mSeekBarVolumizer.onRestoreInstanceState(myState.getVolumeStore());
+ }
+ }
+
+ public static class VolumeStore {
+ public int volume = -1;
+ public int originalVolume = -1;
+ }
+
+ private static class SavedState extends BaseSavedState {
+ VolumeStore mVolumeStore = new VolumeStore();
+
+ public SavedState(Parcel source) {
+ super(source);
+ mVolumeStore.volume = source.readInt();
+ mVolumeStore.originalVolume = source.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mVolumeStore.volume);
+ dest.writeInt(mVolumeStore.originalVolume);
+ }
+
+ VolumeStore getVolumeStore() {
+ return mVolumeStore;
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
/**
* Turns a {@link SeekBar} into a volume control.
*/
@@ -113,7 +220,7 @@ public class VolumePreference extends SeekBarPreference implements
private int mOriginalStreamVolume;
private Ringtone mRingtone;
- private int mLastProgress;
+ private int mLastProgress = -1;
private SeekBar mSeekBar;
private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
@@ -127,7 +234,7 @@ public class VolumePreference extends SeekBarPreference implements
}
}
};
-
+
public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType) {
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -147,11 +254,19 @@ public class VolumePreference extends SeekBarPreference implements
System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
false, mVolumeObserver);
- mRingtone = RingtoneManager.getRingtone(mContext,
- mStreamType == AudioManager.STREAM_NOTIFICATION
- ? Settings.System.DEFAULT_NOTIFICATION_URI
- : Settings.System.DEFAULT_RINGTONE_URI);
- mRingtone.setStreamType(mStreamType);
+ Uri defaultUri = null;
+ if (mStreamType == AudioManager.STREAM_RING) {
+ defaultUri = Settings.System.DEFAULT_RINGTONE_URI;
+ } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) {
+ defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI;
+ } else {
+ defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI;
+ }
+
+ mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
+ if (mRingtone != null) {
+ mRingtone.setStreamType(mStreamType);
+ }
}
public void stop() {
@@ -173,7 +288,7 @@ public class VolumePreference extends SeekBarPreference implements
postSetVolume(progress);
}
- private void postSetVolume(int progress) {
+ void postSetVolume(int progress) {
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeCallbacks(this);
@@ -208,5 +323,27 @@ public class VolumePreference extends SeekBarPreference implements
return mSeekBar;
}
+ public void changeVolumeBy(int amount) {
+ mSeekBar.incrementProgressBy(amount);
+ if (mRingtone != null && !mRingtone.isPlaying()) {
+ sample();
+ }
+ postSetVolume(mSeekBar.getProgress());
+ }
+
+ public void onSaveInstanceState(VolumeStore volumeStore) {
+ if (mLastProgress >= 0) {
+ volumeStore.volume = mLastProgress;
+ volumeStore.originalVolume = mOriginalStreamVolume;
+ }
+ }
+
+ public void onRestoreInstanceState(VolumeStore volumeStore) {
+ if (volumeStore.volume != -1) {
+ mOriginalStreamVolume = volumeStore.originalVolume;
+ mLastProgress = volumeStore.volume;
+ postSetVolume(mLastProgress);
+ }
+ }
}
}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index 1ba5e25..92bc814 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -107,7 +107,8 @@ public class Browser {
public static final String[] HISTORY_PROJECTION = new String[] {
BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
- BookmarkColumns.FAVICON };
+ BookmarkColumns.FAVICON, BookmarkColumns.THUMBNAIL,
+ BookmarkColumns.TOUCH_ICON };
/* these indices dependent on HISTORY_PROJECTION */
public static final int HISTORY_PROJECTION_ID_INDEX = 0;
@@ -117,6 +118,14 @@ public class Browser {
public static final int HISTORY_PROJECTION_BOOKMARK_INDEX = 4;
public static final int HISTORY_PROJECTION_TITLE_INDEX = 5;
public static final int HISTORY_PROJECTION_FAVICON_INDEX = 6;
+ /**
+ * @hide
+ */
+ public static final int HISTORY_PROJECTION_THUMBNAIL_INDEX = 7;
+ /**
+ * @hide
+ */
+ public static final int HISTORY_PROJECTION_TOUCH_ICON_INDEX = 8;
/* columns needed to determine whether to truncate history */
public static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] {
@@ -513,6 +522,14 @@ public class Browser {
public static final String TITLE = "title";
public static final String CREATED = "created";
public static final String FAVICON = "favicon";
+ /**
+ * @hide
+ */
+ public static final String THUMBNAIL = "thumbnail";
+ /**
+ * @hide
+ */
+ public static final String TOUCH_ICON = "touch_icon";
}
public static class SearchColumns implements BaseColumns {
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 4a709f6..d57155c 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -32,6 +32,7 @@ import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Config;
import android.util.Log;
+import android.accounts.Account;
import com.android.internal.database.ArrayListCursor;
import com.google.android.gdata.client.AndroidGDataClient;
import com.google.android.gdata.client.AndroidXmlParserFactory;
@@ -80,6 +81,11 @@ public final class Calendar {
public interface CalendarsColumns
{
/**
+ * A string that uniquely identifies this contact to its source
+ */
+ public static final String SOURCE_ID = "sourceid";
+
+ /**
* The color of the calendar
* <P>Type: INTEGER (color value)</P>
*/
@@ -124,6 +130,12 @@ public final class Calendar {
* <p>Type: INTEGER (boolean)</p>
*/
public static final String SYNC_EVENTS = "sync_events";
+
+ /**
+ * Sync state data.
+ * <p>Type: String (blob)</p>
+ */
+ public static final String SYNC_STATE = "sync_state";
}
/**
@@ -157,11 +169,12 @@ public final class Calendar {
* @param account the account whose rows should be deleted
* @return the count of rows that were deleted
*/
- public static int deleteCalendarsForAccount(ContentResolver cr,
- String account) {
+ public static int deleteCalendarsForAccount(ContentResolver cr, Account account) {
// delete all calendars that match this account
- return Calendar.Calendars.delete(cr, Calendar.Calendars._SYNC_ACCOUNT + "=?",
- new String[] {account});
+ return Calendar.Calendars.delete(cr,
+ Calendar.Calendars._SYNC_ACCOUNT + "=? AND "
+ + Calendar.Calendars._SYNC_ACCOUNT_TYPE + "=?",
+ new String[] {account.name, account.type});
}
/**
@@ -170,9 +183,6 @@ public final class Calendar {
public static final Uri CONTENT_URI =
Uri.parse("content://calendar/calendars");
- public static final Uri LIVE_CONTENT_URI =
- Uri.parse("content://calendar/calendars?update=1");
-
/**
* The default sort order for this table
*/
@@ -207,6 +217,13 @@ public final class Calendar {
* <P>Type: INTEGER (boolean)</P>
*/
public static final String HIDDEN = "hidden";
+
+ /**
+ * The owner account for this calendar, based on the calendar feed.
+ * This will be different from the _SYNC_ACCOUNT for delegated calendars.
+ * <P>Type: String</P>
+ */
+ public static final String OWNER_ACCOUNT = "ownerAccount";
}
public interface AttendeesColumns {
@@ -448,6 +465,47 @@ public final class Calendar {
* <P>Type: INTEGER (long; millis since epoch)</P>
*/
public static final String LAST_DATE = "lastDate";
+
+ /**
+ * Whether the event has attendee information. True if the event
+ * has full attendee data, false if the event has information about
+ * self only.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
+
+ /**
+ * Whether guests can modify the event.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
+
+ /**
+ * Whether guests can invite other guests.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
+
+ /**
+ * Whether guests can see the list of attendees.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
+
+ /**
+ * Email of the organizer (owner) of the event.
+ * <P>Type: STRING</P>
+ */
+ public static final String ORGANIZER = "organizer";
+
+ /**
+ * Whether the user can invite others to the event.
+ * The GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary guest,
+ * while CAN_INVITE_OTHERS indicates if the user can invite others (either through
+ * GUESTS_CAN_INVITE_OTHERS or because the user has modify access to the event).
+ * <P>Type: INTEGER (boolean, readonly)</P>
+ */
+ public static final String CAN_INVITE_OTHERS = "canInviteOthers";
}
/**
@@ -694,6 +752,8 @@ public final class Calendar {
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://calendar/instances/when");
+ public static final Uri CONTENT_BY_DAY_URI =
+ Uri.parse("content://calendar/instances/whenbyday");
/**
* The default sort order for this table.
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index afe219c..b54ad5d 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -73,7 +73,7 @@ public class CallLog {
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
/**
- * The type of the the phone number.
+ * The type of the call (incoming, outgoing or missed).
* <P>Type: INTEGER (int)</P>
*/
public static final String TYPE = "type";
diff --git a/core/java/android/provider/Checkin.java b/core/java/android/provider/Checkin.java
index 6b491ab..84753ee 100644
--- a/core/java/android/provider/Checkin.java
+++ b/core/java/android/provider/Checkin.java
@@ -59,6 +59,8 @@ public final class Checkin {
/** Valid tag values. Extend as necessary for your needs. */
public enum Tag {
+ APANIC_CONSOLE,
+ APANIC_THREADS,
AUTOTEST_FAILURE,
AUTOTEST_SEQUENCE_BEGIN,
AUTOTEST_SUITE_BEGIN,
diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java
index 84fe184..181a529 100644
--- a/core/java/android/provider/Contacts.java
+++ b/core/java/android/provider/Contacts.java
@@ -22,11 +22,11 @@ import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import android.widget.ImageView;
@@ -37,26 +37,34 @@ import java.io.InputStream;
/**
* The Contacts provider stores all information about contacts.
*/
+@Deprecated
public class Contacts {
private static final String TAG = "Contacts";
-
+
+ @Deprecated
public static final String AUTHORITY = "contacts";
/**
* The content:// style URL for this provider
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY);
/** Signifies an email address row that is stored in the ContactMethods table */
+ @Deprecated
public static final int KIND_EMAIL = 1;
/** Signifies a postal address row that is stored in the ContactMethods table */
+ @Deprecated
public static final int KIND_POSTAL = 2;
/** Signifies an IM address row that is stored in the ContactMethods table */
+ @Deprecated
public static final int KIND_IM = 3;
/** Signifies an Organization row that is stored in the Organizations table */
+ @Deprecated
public static final int KIND_ORGANIZATION = 4;
/** Signifies an Phone row that is stored in the Phones table */
+ @Deprecated
public static final int KIND_PHONE = 5;
/**
@@ -67,29 +75,41 @@ public class Contacts {
/**
* Columns from the Settings table that other columns join into themselves.
*/
+ @Deprecated
public interface SettingsColumns {
/**
* The _SYNC_ACCOUNT to which this setting corresponds. This may be null.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String _SYNC_ACCOUNT = "_sync_account";
/**
+ * The _SYNC_ACCOUNT_TYPE to which this setting corresponds. This may be null.
+ * <P>Type: TEXT</P>
+ */
+ @Deprecated
+ public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+
+ /**
* The key of this setting.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String KEY = "key";
/**
* The value of this setting.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String VALUE = "value";
}
/**
* The settings over all of the people
*/
+ @Deprecated
public static final class Settings implements BaseColumns, SettingsColumns {
/**
* no public constructor since this is a utility class
@@ -99,17 +119,20 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/settings");
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "settings";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "key ASC";
/**
@@ -121,8 +144,10 @@ public class Contacts {
* This is a boolean setting. It is true if it is set and it is anything other than the
* emptry string or "0".
*/
+ @Deprecated
public static final String SYNC_EVERYTHING = "syncEverything";
+ @Deprecated
public static String getSetting(ContentResolver cr, String account, String key) {
// For now we only support a single account and the UI doesn't know what
// the account name is, so we're using a global setting for SYNC_EVERYTHING.
@@ -134,6 +159,7 @@ public class Contacts {
selectString = (account == null)
? "_sync_account is null AND key=?"
: "_sync_account=? AND key=?";
+// : "_sync_account=? AND _sync_account_type=? AND key=?";
selectArgs = (account == null)
? new String[]{key}
: new String[]{account, key};
@@ -151,6 +177,7 @@ public class Contacts {
}
}
+ @Deprecated
public static void setSetting(ContentResolver cr, String account, String key,
String value) {
ContentValues values = new ContentValues();
@@ -158,7 +185,8 @@ public class Contacts {
// the account name is, so we're using a global setting for SYNC_EVERYTHING.
// Some day when we add multiple accounts to the UI this should honor the account
// that was asked for.
- //values.put(_SYNC_ACCOUNT, account);
+ //values.put(_SYNC_ACCOUNT, account.mName);
+ //values.put(_SYNC_ACCOUNT_TYPE, account.mType);
values.put(KEY, key);
values.put(VALUE, value);
cr.update(Settings.CONTENT_URI, values, null, null);
@@ -168,11 +196,13 @@ public class Contacts {
/**
* Columns from the People table that other tables join into themselves.
*/
+ @Deprecated
public interface PeopleColumns {
/**
* The person's name.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NAME = "name";
/**
@@ -181,13 +211,15 @@ public class Contacts {
* Used for pronunciation and/or collation in some languages.
* <p>Type: TEXT</P>
*/
+ @Deprecated
public static final String PHONETIC_NAME = "phonetic_name";
-
+
/**
* The display name. If name is not null name, else if number is not null number,
* else if email is not null email.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String DISPLAY_NAME = "display_name";
/**
@@ -196,30 +228,35 @@ public class Contacts {
* <P>Type: TEXT</p>
* @hide Used only in Contacts application for now.
*/
+ @Deprecated
public static final String SORT_STRING = "sort_string";
-
+
/**
* Notes about the person.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NOTES = "notes";
/**
* The number of times a person has been contacted
* <P>Type: INTEGER</P>
*/
+ @Deprecated
public static final String TIMES_CONTACTED = "times_contacted";
/**
* The last time a person was contacted.
* <P>Type: INTEGER</P>
*/
+ @Deprecated
public static final String LAST_TIME_CONTACTED = "last_time_contacted";
/**
* A custom ringtone associated with a person. Not always present.
* <P>Type: TEXT (URI to the ringtone)</P>
*/
+ @Deprecated
public static final String CUSTOM_RINGTONE = "custom_ringtone";
/**
@@ -227,24 +264,28 @@ public class Contacts {
* present.
* <P>Type: INTEGER (0 for false, 1 for true)</P>
*/
+ @Deprecated
public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
/**
* Is the contact starred?
* <P>Type: INTEGER (boolean)</P>
*/
+ @Deprecated
public static final String STARRED = "starred";
/**
* The server version of the photo
* <P>Type: TEXT (the version number portion of the photo URI)</P>
*/
- public static final String PHOTO_VERSION = "photo_version";
+ @Deprecated
+ public static final String PHOTO_VERSION = "photo_version";
}
/**
* This table contains people.
*/
+ @Deprecated
public static final class People implements BaseColumns, SyncConstValue, PeopleColumns,
PhonesColumns, PresenceColumns {
/**
@@ -255,6 +296,7 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/people");
@@ -262,6 +304,7 @@ public class Contacts {
* The content:// style URL for filtering people by name. The filter
* argument should be passed as an additional path segment after this URI.
*/
+ @Deprecated
public static final Uri CONTENT_FILTER_URI =
Uri.parse("content://contacts/people/filter");
@@ -269,6 +312,7 @@ public class Contacts {
* The content:// style URL for the table that holds the deleted
* contacts.
*/
+ @Deprecated
public static final Uri DELETED_CONTENT_URI =
Uri.parse("content://contacts/deleted_people");
@@ -278,35 +322,40 @@ public class Contacts {
* additional path segment after this URI. This matches any people with
* at least one E-mail or IM {@link ContactMethods} that match the
* filter.
- *
+ *
* Not exposed because we expect significant changes in the contacts
* schema and do not want to have to support this.
* @hide
*/
+ @Deprecated
public static final Uri WITH_EMAIL_OR_IM_FILTER_URI =
Uri.parse("content://contacts/people/with_email_or_im_filter");
-
+
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of
* people.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/person";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
* person.
*/
+ @Deprecated
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/person";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = People.NAME + " ASC";
/**
* The ID of the persons preferred phone number.
* <P>Type: INTEGER (foreign key to phones table on the _ID field)</P>
*/
+ @Deprecated
public static final String PRIMARY_PHONE_ID = "primary_phone";
/**
@@ -314,6 +363,7 @@ public class Contacts {
* <P>Type: INTEGER (foreign key to contact_methods table on the
* _ID field)</P>
*/
+ @Deprecated
public static final String PRIMARY_EMAIL_ID = "primary_email";
/**
@@ -321,6 +371,7 @@ public class Contacts {
* <P>Type: INTEGER (foreign key to organizations table on the
* _ID field)</P>
*/
+ @Deprecated
public static final String PRIMARY_ORGANIZATION_ID = "primary_organization";
/**
@@ -329,6 +380,7 @@ public class Contacts {
* @param resolver the ContentResolver to use
* @param personId the person who was contacted
*/
+ @Deprecated
public static void markAsContacted(ContentResolver resolver, long personId) {
Uri uri = ContentUris.withAppendedId(CONTENT_URI, personId);
uri = Uri.withAppendedPath(uri, "update_contact_time");
@@ -342,6 +394,7 @@ public class Contacts {
/**
* @hide Used in vCard parser code.
*/
+ @Deprecated
public static long tryGetMyContactsGroupId(ContentResolver resolver) {
Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
Groups.SYSTEM_ID + "='" + Groups.GROUP_MY_CONTACTS + "'", null, null);
@@ -365,24 +418,26 @@ public class Contacts {
* @return the URI of the group membership row
* @throws IllegalStateException if the My Contacts group can't be found
*/
+ @Deprecated
public static Uri addToMyContactsGroup(ContentResolver resolver, long personId) {
long groupId = tryGetMyContactsGroupId(resolver);
if (groupId == 0) {
throw new IllegalStateException("Failed to find the My Contacts group");
}
-
+
return addToGroup(resolver, personId, groupId);
}
/**
* Adds a person to a group referred to by name.
- *
+ *
* @param resolver the resolver to use
* @param personId the person to add to the group
* @param groupName the name of the group to add the contact to
* @return the URI of the group membership row
* @throws IllegalStateException if the group can't be found
*/
+ @Deprecated
public static Uri addToGroup(ContentResolver resolver, long personId, String groupName) {
long groupId = 0;
Cursor groupsCursor = resolver.query(Groups.CONTENT_URI, GROUPS_PROJECTION,
@@ -400,36 +455,38 @@ public class Contacts {
if (groupId == 0) {
throw new IllegalStateException("Failed to find the My Contacts group");
}
-
+
return addToGroup(resolver, personId, groupId);
}
/**
* Adds a person to a group.
- *
+ *
* @param resolver the resolver to use
* @param personId the person to add to the group
* @param groupId the group to add the person to
* @return the URI of the group membership row
*/
+ @Deprecated
public static Uri addToGroup(ContentResolver resolver, long personId, long groupId) {
ContentValues values = new ContentValues();
values.put(GroupMembership.PERSON_ID, personId);
values.put(GroupMembership.GROUP_ID, groupId);
return resolver.insert(GroupMembership.CONTENT_URI, values);
}
-
+
private static final String[] GROUPS_PROJECTION = new String[] {
Groups._ID,
};
/**
* Creates a new contacts and adds it to the "My Contacts" group.
- *
+ *
* @param resolver the ContentResolver to use
* @param values the values to use when creating the contact
* @return the URI of the contact, or null if the operation fails
*/
+ @Deprecated
public static Uri createPersonInMyContactsGroup(ContentResolver resolver,
ContentValues values) {
@@ -446,6 +503,7 @@ public class Contacts {
return contactUri;
}
+ @Deprecated
public static Cursor queryGroups(ContentResolver resolver, long person) {
return resolver.query(GroupMembership.CONTENT_URI, null, "person=?",
new String[]{String.valueOf(person)}, Groups.DEFAULT_SORT_ORDER);
@@ -457,18 +515,20 @@ public class Contacts {
* @param person the Uri of the person whose photo is to be updated
* @param data the byte[] that represents the photo
*/
+ @Deprecated
public static void setPhotoData(ContentResolver cr, Uri person, byte[] data) {
Uri photoUri = Uri.withAppendedPath(person, Contacts.Photos.CONTENT_DIRECTORY);
ContentValues values = new ContentValues();
values.put(Photos.DATA, data);
cr.update(photoUri, values, null, null);
}
-
+
/**
* Opens an InputStream for the person's photo and returns the photo as a Bitmap.
* If the person's photo isn't present returns the placeholderImageResource instead.
* @param person the person whose photo should be used
*/
+ @Deprecated
public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri person) {
Uri photoUri = Uri.withAppendedPath(person, Contacts.Photos.CONTENT_DIRECTORY);
Cursor cursor = cr.query(photoUri, new String[]{Photos.DATA}, null, null, null);
@@ -495,6 +555,7 @@ public class Contacts {
* have a photo
* @param options the decoding options, can be set to null
*/
+ @Deprecated
public static Bitmap loadContactPhoto(Context context, Uri person,
int placeholderImageResource, BitmapFactory.Options options) {
if (person == null) {
@@ -521,6 +582,7 @@ public class Contacts {
/**
* A sub directory of a single person that contains all of their Phones.
*/
+ @Deprecated
public static final class Phones implements BaseColumns, PhonesColumns,
PeopleColumns {
/**
@@ -531,11 +593,13 @@ public class Contacts {
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "phones";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "number ASC";
}
@@ -543,6 +607,7 @@ public class Contacts {
* A subdirectory of a single person that contains all of their
* ContactMethods.
*/
+ @Deprecated
public static final class ContactMethods
implements BaseColumns, ContactMethodsColumns, PeopleColumns {
/**
@@ -553,17 +618,20 @@ public class Contacts {
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "contact_methods";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "data ASC";
}
/**
* The extensions for a person
*/
+ @Deprecated
public static class Extensions implements BaseColumns, ExtensionsColumns {
/**
* no public constructor since this is a utility class
@@ -573,17 +641,20 @@ public class Contacts {
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "extensions";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "name ASC";
/**
* The ID of the person this phone number is assigned to.
* <P>Type: INTEGER (long)</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
}
}
@@ -591,17 +662,20 @@ public class Contacts {
/**
* Columns from the groups table.
*/
+ @Deprecated
public interface GroupsColumns {
/**
* The group name.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NAME = "name";
/**
* Notes about the group.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NOTES = "notes";
/**
@@ -609,18 +683,21 @@ public class Contacts {
* for this group's account.
* <P>Type: INTEGER (boolean)</P>
*/
+ @Deprecated
public static final String SHOULD_SYNC = "should_sync";
/**
* The ID of this group if it is a System Group, null otherwise.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String SYSTEM_ID = "system_id";
}
/**
* This table contains the groups for an account.
*/
+ @Deprecated
public static final class Groups
implements BaseColumns, SyncConstValue, GroupsColumns {
/**
@@ -631,6 +708,7 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/groups");
@@ -638,6 +716,7 @@ public class Contacts {
* The content:// style URL for the table that holds the deleted
* groups.
*/
+ @Deprecated
public static final Uri DELETED_CONTENT_URI =
Uri.parse("content://contacts/deleted_groups");
@@ -645,71 +724,90 @@ public class Contacts {
* The MIME type of {@link #CONTENT_URI} providing a directory of
* groups.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contactsgroup";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
* group.
*/
+ @Deprecated
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contactsgroup";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = NAME + " ASC";
/**
*
*/
+ @Deprecated
public static final String GROUP_ANDROID_STARRED = "Starred in Android";
/**
* The "My Contacts" system group.
*/
+ @Deprecated
public static final String GROUP_MY_CONTACTS = "Contacts";
}
/**
* Columns from the Phones table that other columns join into themselves.
*/
+ @Deprecated
public interface PhonesColumns {
/**
* The type of the the phone number.
* <P>Type: INTEGER (one of the constants below)</P>
*/
+ @Deprecated
public static final String TYPE = "type";
+ @Deprecated
public static final int TYPE_CUSTOM = 0;
+ @Deprecated
public static final int TYPE_HOME = 1;
+ @Deprecated
public static final int TYPE_MOBILE = 2;
+ @Deprecated
public static final int TYPE_WORK = 3;
+ @Deprecated
public static final int TYPE_FAX_WORK = 4;
+ @Deprecated
public static final int TYPE_FAX_HOME = 5;
+ @Deprecated
public static final int TYPE_PAGER = 6;
+ @Deprecated
public static final int TYPE_OTHER = 7;
/**
* The user provided label for the phone number, only used if TYPE is TYPE_CUSTOM.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String LABEL = "label";
/**
* The phone number as the user entered it.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NUMBER = "number";
/**
* The normalized phone number
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NUMBER_KEY = "number_key";
/**
* Whether this is the primary phone number
* <P>Type: INTEGER (if set, non-0 means true)</P>
*/
+ @Deprecated
public static final String ISPRIMARY = "isprimary";
}
@@ -718,6 +816,7 @@ public class Contacts {
* contact method belongs to. Phone numbers are stored separately from
* other contact methods to make caller ID lookup more efficient.
*/
+ @Deprecated
public static final class Phones
implements BaseColumns, PhonesColumns, PeopleColumns {
/**
@@ -725,12 +824,13 @@ public class Contacts {
*/
private Phones() {}
+ @Deprecated
public static final CharSequence getDisplayLabel(Context context, int type,
CharSequence label, CharSequence[] labelArray) {
CharSequence display = "";
if (type != People.Phones.TYPE_CUSTOM) {
- CharSequence[] labels = labelArray != null? labelArray
+ CharSequence[] labels = labelArray != null? labelArray
: context.getResources().getTextArray(
com.android.internal.R.array.phoneTypes);
try {
@@ -746,20 +846,23 @@ public class Contacts {
return display;
}
+ @Deprecated
public static final CharSequence getDisplayLabel(Context context, int type,
CharSequence label) {
return getDisplayLabel(context, type, label, null);
}
-
+
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/phones");
/**
* The content:// style URL for filtering phone numbers
*/
+ @Deprecated
public static final Uri CONTENT_FILTER_URL =
Uri.parse("content://contacts/phones/filter");
@@ -767,26 +870,31 @@ public class Contacts {
* The MIME type of {@link #CONTENT_URI} providing a directory of
* phones.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
* phone.
*/
+ @Deprecated
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "name ASC";
/**
* The ID of the person this phone number is assigned to.
* <P>Type: INTEGER (long)</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
}
+ @Deprecated
public static final class GroupMembership implements BaseColumns, GroupsColumns {
/**
* no public constructor since this is a utility class
@@ -796,59 +904,77 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/groupmembership");
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri RAW_CONTENT_URI =
Uri.parse("content://contacts/groupmembershipraw");
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "groupmembership";
+
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of all
* person groups.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contactsgroupmembership";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
* person group.
*/
+ @Deprecated
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/contactsgroupmembership";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "group_id ASC";
/**
* The row id of the accounts group.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String GROUP_ID = "group_id";
/**
* The sync id of the group.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String GROUP_SYNC_ID = "group_sync_id";
/**
* The account of the group.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String GROUP_SYNC_ACCOUNT = "group_sync_account";
/**
+ * The account type of the group.
+ * <P>Type: TEXT</P>
+ */
+ @Deprecated
+ public static final String GROUP_SYNC_ACCOUNT_TYPE = "group_sync_account_type";
+
+ /**
* The row id of the person.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
}
@@ -856,57 +982,70 @@ public class Contacts {
* Columns from the ContactMethods table that other tables join into
* themseleves.
*/
+ @Deprecated
public interface ContactMethodsColumns {
/**
* The kind of the the contact method. For example, email address,
* postal address, etc.
* <P>Type: INTEGER (one of the values below)</P>
*/
+ @Deprecated
public static final String KIND = "kind";
/**
* The type of the contact method, must be one of the types below.
* <P>Type: INTEGER (one of the values below)</P>
*/
+ @Deprecated
public static final String TYPE = "type";
+ @Deprecated
public static final int TYPE_CUSTOM = 0;
+ @Deprecated
public static final int TYPE_HOME = 1;
+ @Deprecated
public static final int TYPE_WORK = 2;
+ @Deprecated
public static final int TYPE_OTHER = 3;
/**
* @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future.
*/
+ @Deprecated
public static final int MOBILE_EMAIL_TYPE_INDEX = 2;
/**
* @hide This is temporal. TYPE_MOBILE should be added to TYPE in the future.
* This is not "mobile" but "CELL" since vCard uses it for identifying mobile phone.
*/
+ @Deprecated
public static final String MOBILE_EMAIL_TYPE_NAME = "_AUTO_CELL";
/**
* The user defined label for the the contact method.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String LABEL = "label";
/**
* The data for the contact method.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String DATA = "data";
/**
* Auxiliary data for the contact method.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String AUX_DATA = "aux_data";
/**
* Whether this is the primary organization
* <P>Type: INTEGER (if set, non-0 means true)</P>
*/
+ @Deprecated
public static final String ISPRIMARY = "isprimary";
}
@@ -914,18 +1053,21 @@ public class Contacts {
* This table stores all non-phone contact methods and a reference to the
* person that the contact method belongs to.
*/
+ @Deprecated
public static final class ContactMethods
implements BaseColumns, ContactMethodsColumns, PeopleColumns {
/**
* The column with latitude data for postal locations
* <P>Type: REAL</P>
*/
+ @Deprecated
public static final String POSTAL_LOCATION_LATITUDE = DATA;
/**
* The column with longitude data for postal locations
* <P>Type: REAL</P>
*/
+ @Deprecated
public static final String POSTAL_LOCATION_LONGITUDE = AUX_DATA;
/**
@@ -936,23 +1078,34 @@ public class Contacts {
* - pre:<an integer, one of the protocols below>
* - custom:<a string>
*/
+ @Deprecated
public static final int PROTOCOL_AIM = 0;
+ @Deprecated
public static final int PROTOCOL_MSN = 1;
+ @Deprecated
public static final int PROTOCOL_YAHOO = 2;
+ @Deprecated
public static final int PROTOCOL_SKYPE = 3;
+ @Deprecated
public static final int PROTOCOL_QQ = 4;
+ @Deprecated
public static final int PROTOCOL_GOOGLE_TALK = 5;
+ @Deprecated
public static final int PROTOCOL_ICQ = 6;
+ @Deprecated
public static final int PROTOCOL_JABBER = 7;
+ @Deprecated
public static String encodePredefinedImProtocol(int protocol) {
return "pre:" + protocol;
}
+ @Deprecated
public static String encodeCustomImProtocol(String protocolString) {
return "custom:" + protocolString;
}
+ @Deprecated
public static Object decodeImProtocol(String encodedString) {
if (encodedString == null) {
return null;
@@ -969,7 +1122,7 @@ public class Contacts {
throw new IllegalArgumentException(
"the value is not a valid encoded protocol, " + encodedString);
}
-
+
/**
* This looks up the provider name defined in
* {@link android.provider.Im.ProviderNames} from the predefined IM protocol id.
@@ -980,6 +1133,7 @@ public class Contacts {
* provider is defined for the given protocol
* @hide
*/
+ @Deprecated
public static String lookupProviderNameFromId(int protocol) {
switch (protocol) {
case PROTOCOL_GOOGLE_TALK:
@@ -1007,6 +1161,7 @@ public class Contacts {
*/
private ContactMethods() {}
+ @Deprecated
public static final CharSequence getDisplayLabel(Context context, int kind,
int type, CharSequence label) {
CharSequence display = "";
@@ -1022,13 +1177,7 @@ public class Contacts {
}
} else {
if (!TextUtils.isEmpty(label)) {
- if (label.toString().equals(MOBILE_EMAIL_TYPE_NAME)) {
- display =
- context.getString(
- com.android.internal.R.string.mobileEmailTypeName);
- } else {
- display = label;
- }
+ display = label;
}
}
break;
@@ -1065,6 +1214,7 @@ public class Contacts {
* @param latitude the latitude for the address
* @param longitude the longitude for the address
*/
+ @Deprecated
public void addPostalLocation(Context context, long postalId,
double latitude, double longitude) {
final ContentResolver resolver = context.getContentResolver();
@@ -1084,12 +1234,14 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/contact_methods");
/**
* The content:// style URL for sub-directory of e-mail addresses.
*/
+ @Deprecated
public static final Uri CONTENT_EMAIL_URI =
Uri.parse("content://contacts/contact_methods/email");
@@ -1097,30 +1249,35 @@ public class Contacts {
* The MIME type of {@link #CONTENT_URI} providing a directory of
* phones.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact-methods";
/**
* The MIME type of a {@link #CONTENT_EMAIL_URI} sub-directory of\
* multiple {@link Contacts#KIND_EMAIL} entries.
*/
+ @Deprecated
public static final String CONTENT_EMAIL_TYPE = "vnd.android.cursor.dir/email";
/**
* The MIME type of a {@link #CONTENT_EMAIL_URI} sub-directory of\
* multiple {@link Contacts#KIND_POSTAL} entries.
*/
+ @Deprecated
public static final String CONTENT_POSTAL_TYPE = "vnd.android.cursor.dir/postal-address";
/**
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single
* {@link Contacts#KIND_EMAIL} entry.
*/
+ @Deprecated
public static final String CONTENT_EMAIL_ITEM_TYPE = "vnd.android.cursor.item/email";
/**
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single
* {@link Contacts#KIND_POSTAL} entry.
*/
+ @Deprecated
public static final String CONTENT_POSTAL_ITEM_TYPE
= "vnd.android.cursor.item/postal-address";
@@ -1128,23 +1285,27 @@ public class Contacts {
* The MIME type of a {@link #CONTENT_URI} sub-directory of a single
* {@link Contacts#KIND_IM} entry.
*/
+ @Deprecated
public static final String CONTENT_IM_ITEM_TYPE = "vnd.android.cursor.item/jabber-im";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "name ASC";
/**
* The ID of the person this contact method is assigned to.
* <P>Type: INTEGER (long)</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
}
/**
* The IM presence columns with some contacts specific columns mixed in.
*/
+ @Deprecated
public interface PresenceColumns extends Im.CommonPresenceColumns {
/**
* The IM service the presence is coming from. Formatted using either
@@ -1152,6 +1313,7 @@ public class Contacts {
* {@link Contacts.ContactMethods#encodeCustomImProtocol}.
* <P>Type: STRING</P>
*/
+ @Deprecated
public static final String IM_PROTOCOL = "im_protocol";
/**
@@ -1159,12 +1321,14 @@ public class Contacts {
* the {@link #IM_PROTOCOL}.
* <P>Type: STRING</P>
*/
+ @Deprecated
public static final String IM_HANDLE = "im_handle";
/**
* The IM account for the local user that the presence data came from.
* <P>Type: STRING</P>
*/
+ @Deprecated
public static final String IM_ACCOUNT = "im_account";
}
@@ -1172,11 +1336,13 @@ public class Contacts {
* Contains presence information about contacts.
* @hide
*/
+ @Deprecated
public static final class Presence
implements BaseColumns, PresenceColumns, PeopleColumns {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/presence");
@@ -1184,29 +1350,31 @@ public class Contacts {
* The ID of the person this presence item is assigned to.
* <P>Type: INTEGER (long)</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
/**
* Gets the resource ID for the proper presence icon.
- *
+ *
* @param status the status to get the icon for
* @return the resource ID for the proper presence icon
*/
+ @Deprecated
public static final int getPresenceIconResourceId(int status) {
switch (status) {
case Contacts.People.AVAILABLE:
return com.android.internal.R.drawable.presence_online;
-
+
case Contacts.People.IDLE:
case Contacts.People.AWAY:
return com.android.internal.R.drawable.presence_away;
-
+
case Contacts.People.DO_NOT_DISTURB:
return com.android.internal.R.drawable.presence_busy;
-
+
case Contacts.People.INVISIBLE:
return com.android.internal.R.drawable.presence_invisible;
-
+
case Contacts.People.OFFLINE:
default:
return com.android.internal.R.drawable.presence_offline;
@@ -1219,6 +1387,7 @@ public class Contacts {
* @param icon the icon to to set
* @param serverStatus that status
*/
+ @Deprecated
public static final void setPresenceIcon(ImageView icon, int serverStatus) {
icon.setImageResource(getPresenceIconResourceId(serverStatus));
}
@@ -1227,57 +1396,69 @@ public class Contacts {
/**
* Columns from the Organizations table that other columns join into themselves.
*/
+ @Deprecated
public interface OrganizationColumns {
/**
- * The type of the the phone number.
+ * The type of the organizations.
* <P>Type: INTEGER (one of the constants below)</P>
*/
+ @Deprecated
public static final String TYPE = "type";
+ @Deprecated
public static final int TYPE_CUSTOM = 0;
+ @Deprecated
public static final int TYPE_WORK = 1;
+ @Deprecated
public static final int TYPE_OTHER = 2;
/**
* The user provided label, only used if TYPE is TYPE_CUSTOM.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String LABEL = "label";
/**
* The name of the company for this organization.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String COMPANY = "company";
/**
* The title within this organization.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String TITLE = "title";
/**
* The person this organization is tied to.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
/**
* Whether this is the primary organization
* <P>Type: INTEGER (if set, non-0 means true)</P>
*/
+ @Deprecated
public static final String ISPRIMARY = "isprimary";
}
/**
* A sub directory of a single person that contains all of their Phones.
*/
+ @Deprecated
public static final class Organizations implements BaseColumns, OrganizationColumns {
/**
* no public constructor since this is a utility class
*/
private Organizations() {}
+ @Deprecated
public static final CharSequence getDisplayLabel(Context context, int type,
CharSequence label) {
CharSequence display = "";
@@ -1301,34 +1482,40 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/organizations");
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "organizations";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "company, title, isprimary ASC";
}
/**
* Columns from the Photos table that other columns join into themselves.
*/
+ @Deprecated
public interface PhotosColumns {
/**
* The _SYNC_VERSION of the photo that was last downloaded
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String LOCAL_VERSION = "local_version";
/**
* The person this photo is associated with.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
/**
@@ -1336,12 +1523,14 @@ public class Contacts {
* You must specify this in the columns in order to use it in the where clause.
* <P>Type: INTEGER(boolean)</P>
*/
+ @Deprecated
public static final String DOWNLOAD_REQUIRED = "download_required";
/**
* non-zero if this photo is known to exist on the server
* <P>Type: INTEGER(boolean)</P>
*/
+ @Deprecated
public static final String EXISTS_ON_SERVER = "exists_on_server";
/**
@@ -1349,12 +1538,14 @@ public class Contacts {
* the previous attempt. If null then the previous attempt succeeded.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String SYNC_ERROR = "sync_error";
/**
* The image data, or null if there is no image.
* <P>Type: BLOB</P>
*/
+ @Deprecated
public static final String DATA = "data";
}
@@ -1362,6 +1553,7 @@ public class Contacts {
/**
* The photos over all of the people
*/
+ @Deprecated
public static final class Photos implements BaseColumns, PhotosColumns, SyncConstValue {
/**
* no public constructor since this is a utility class
@@ -1371,37 +1563,44 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/photos");
/**
* The directory twig for this sub-table
*/
+ @Deprecated
public static final String CONTENT_DIRECTORY = "photo";
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "person ASC";
}
+ @Deprecated
public interface ExtensionsColumns {
/**
* The name of this extension. May not be null. There may be at most one row for each name.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String NAME = "name";
/**
* The value of this extension. May not be null.
* <P>Type: TEXT</P>
*/
+ @Deprecated
public static final String VALUE = "value";
}
/**
* The extensions for a person
*/
+ @Deprecated
public static final class Extensions implements BaseColumns, ExtensionsColumns {
/**
* no public constructor since this is a utility class
@@ -1411,6 +1610,7 @@ public class Contacts {
/**
* The content:// style URL for this table
*/
+ @Deprecated
public static final Uri CONTENT_URI =
Uri.parse("content://contacts/extensions");
@@ -1418,22 +1618,27 @@ public class Contacts {
* The MIME type of {@link #CONTENT_URI} providing a directory of
* phones.
*/
+ @Deprecated
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_extensions";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
* phone.
*/
+ @Deprecated
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_extensions";
+
/**
* The default sort order for this table
*/
+ @Deprecated
public static final String DEFAULT_SORT_ORDER = "person, name ASC";
/**
* The ID of the person this phone number is assigned to.
* <P>Type: INTEGER (long)</P>
*/
+ @Deprecated
public static final String PERSON_ID = "person";
}
@@ -1441,33 +1646,41 @@ public class Contacts {
* Contains helper classes used to create or manage {@link android.content.Intent Intents}
* that involve contacts.
*/
+ @Deprecated
public static final class Intents {
+ @Deprecated
+ public Intents() {
+ }
+
/**
* This is the intent that is fired when a search suggestion is clicked on.
*/
+ @Deprecated
public static final String SEARCH_SUGGESTION_CLICKED =
- "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED";
+ ContactsContract.Intents.SEARCH_SUGGESTION_CLICKED;
/**
- * This is the intent that is fired when a search suggestion for dialing a number
+ * This is the intent that is fired when a search suggestion for dialing a number
* is clicked on.
*/
+ @Deprecated
public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED =
- "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED";
+ ContactsContract.Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED;
/**
* This is the intent that is fired when a search suggestion for creating a contact
* is clicked on.
*/
+ @Deprecated
public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED =
- "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
+ ContactsContract.Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED;
/**
* Starts an Activity that lets the user pick a contact to attach an image to.
* After picking the contact it launches the image cropper in face detection mode.
*/
- public static final String ATTACH_IMAGE =
- "com.android.contacts.action.ATTACH_IMAGE";
+ @Deprecated
+ public static final String ATTACH_IMAGE = ContactsContract.Intents.ATTACH_IMAGE;
/**
* Takes as input a data URI with a mailto: or tel: scheme. If a single
@@ -1492,8 +1705,9 @@ public class Contacts {
* Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip
* prompting the user when the contact doesn't exist.
*/
+ @Deprecated
public static final String SHOW_OR_CREATE_CONTACT =
- "com.android.contacts.action.SHOW_OR_CREATE_CONTACT";
+ ContactsContract.Intents.SHOW_OR_CREATE_CONTACT;
/**
* Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
@@ -1502,9 +1716,9 @@ public class Contacts {
* <p>
* Type: BOOLEAN
*/
- public static final String EXTRA_FORCE_CREATE =
- "com.android.contacts.action.FORCE_CREATE";
-
+ @Deprecated
+ public static final String EXTRA_FORCE_CREATE = ContactsContract.Intents.EXTRA_FORCE_CREATE;
+
/**
* Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact
* description to be shown when prompting user about creating a new
@@ -1512,69 +1726,93 @@ public class Contacts {
* <p>
* Type: STRING
*/
+ @Deprecated
public static final String EXTRA_CREATE_DESCRIPTION =
- "com.android.contacts.action.CREATE_DESCRIPTION";
+ ContactsContract.Intents.EXTRA_CREATE_DESCRIPTION;
+
+ /**
+ * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
+ * dialog location using screen coordinates. When not specified, the
+ * dialog will be centered.
+ *
+ * @hide pending API council review
+ */
+ @Deprecated
+ public static final String EXTRA_TARGET_RECT = ContactsContract.Intents.EXTRA_TARGET_RECT;
/**
* Intents related to the Contacts app UI.
*/
+ @Deprecated
public static final class UI {
+ @Deprecated
+ public UI() {
+ }
+
/**
* The action for the default contacts list tab.
*/
- public static final String LIST_DEFAULT =
- "com.android.contacts.action.LIST_DEFAULT";
+ @Deprecated
+ public static final String LIST_DEFAULT = ContactsContract.Intents.UI.LIST_DEFAULT;
/**
* The action for the contacts list tab.
*/
+ @Deprecated
public static final String LIST_GROUP_ACTION =
- "com.android.contacts.action.LIST_GROUP";
+ ContactsContract.Intents.UI.LIST_GROUP_ACTION;
/**
* When in LIST_GROUP_ACTION mode, this is the group to display.
*/
- public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP";
-
+ @Deprecated
+ public static final String GROUP_NAME_EXTRA_KEY =
+ ContactsContract.Intents.UI.GROUP_NAME_EXTRA_KEY;
/**
* The action for the all contacts list tab.
*/
+ @Deprecated
public static final String LIST_ALL_CONTACTS_ACTION =
- "com.android.contacts.action.LIST_ALL_CONTACTS";
+ ContactsContract.Intents.UI.LIST_ALL_CONTACTS_ACTION;
/**
* The action for the contacts with phone numbers list tab.
*/
+ @Deprecated
public static final String LIST_CONTACTS_WITH_PHONES_ACTION =
- "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES";
+ ContactsContract.Intents.UI.LIST_CONTACTS_WITH_PHONES_ACTION;
/**
* The action for the starred contacts list tab.
*/
+ @Deprecated
public static final String LIST_STARRED_ACTION =
- "com.android.contacts.action.LIST_STARRED";
+ ContactsContract.Intents.UI.LIST_STARRED_ACTION;
/**
* The action for the frequent contacts list tab.
*/
+ @Deprecated
public static final String LIST_FREQUENT_ACTION =
- "com.android.contacts.action.LIST_FREQUENT";
+ ContactsContract.Intents.UI.LIST_FREQUENT_ACTION;
/**
* The action for the "strequent" contacts list tab. It first lists the starred
* contacts in alphabetical order and then the frequent contacts in descending
* order of the number of times they have been contacted.
*/
+ @Deprecated
public static final String LIST_STREQUENT_ACTION =
- "com.android.contacts.action.LIST_STREQUENT";
+ ContactsContract.Intents.UI.LIST_STREQUENT_ACTION;
/**
* A key for to be used as an intent extra to set the activity
* title to a custom String value.
*/
+ @Deprecated
public static final String TITLE_EXTRA_KEY =
- "com.android.contacts.extra.TITLE_EXTRA";
-
+ ContactsContract.Intents.UI.TITLE_EXTRA_KEY;
+
/**
* Activity Action: Display a filtered list of contacts
* <p>
@@ -1583,187 +1821,232 @@ public class Contacts {
* <p>
* Output: Nothing.
*/
- public static final String FILTER_CONTACTS_ACTION =
- "com.android.contacts.action.FILTER_CONTACTS";
-
+ @Deprecated
+ public static final String FILTER_CONTACTS_ACTION =
+ ContactsContract.Intents.UI.FILTER_CONTACTS_ACTION;
+
/**
* Used as an int extra field in {@link #FILTER_CONTACTS_ACTION}
* intents to supply the text on which to filter.
*/
- public static final String FILTER_TEXT_EXTRA_KEY =
- "com.android.contacts.extra.FILTER_TEXT";
+ @Deprecated
+ public static final String FILTER_TEXT_EXTRA_KEY =
+ ContactsContract.Intents.UI.FILTER_TEXT_EXTRA_KEY;
}
/**
* Convenience class that contains string constants used
* to create contact {@link android.content.Intent Intents}.
*/
+ @Deprecated
public static final class Insert {
+ @Deprecated
+ public Insert() {
+ }
+
/** The action code to use when adding a contact */
- public static final String ACTION = Intent.ACTION_INSERT;
+ @Deprecated
+ public static final String ACTION = ContactsContract.Intents.Insert.ACTION;
/**
* If present, forces a bypass of quick insert mode.
*/
- public static final String FULL_MODE = "full_mode";
+ @Deprecated
+ public static final String FULL_MODE = ContactsContract.Intents.Insert.FULL_MODE;
/**
* The extra field for the contact name.
* <P>Type: String</P>
*/
- public static final String NAME = "name";
+ @Deprecated
+ public static final String NAME = ContactsContract.Intents.Insert.NAME;
/**
* The extra field for the contact phonetic name.
* <P>Type: String</P>
*/
- public static final String PHONETIC_NAME = "phonetic_name";
+ @Deprecated
+ public static final String PHONETIC_NAME =
+ ContactsContract.Intents.Insert.PHONETIC_NAME;
/**
* The extra field for the contact company.
* <P>Type: String</P>
*/
- public static final String COMPANY = "company";
+ @Deprecated
+ public static final String COMPANY = ContactsContract.Intents.Insert.COMPANY;
/**
* The extra field for the contact job title.
* <P>Type: String</P>
*/
- public static final String JOB_TITLE = "job_title";
+ @Deprecated
+ public static final String JOB_TITLE = ContactsContract.Intents.Insert.JOB_TITLE;
/**
* The extra field for the contact notes.
* <P>Type: String</P>
*/
- public static final String NOTES = "notes";
+ @Deprecated
+ public static final String NOTES = ContactsContract.Intents.Insert.NOTES;
/**
* The extra field for the contact phone number.
* <P>Type: String</P>
*/
- public static final String PHONE = "phone";
+ @Deprecated
+ public static final String PHONE = ContactsContract.Intents.Insert.PHONE;
/**
* The extra field for the contact phone number type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
* or a string specifying a custom label.</P>
*/
- public static final String PHONE_TYPE = "phone_type";
+ @Deprecated
+ public static final String PHONE_TYPE = ContactsContract.Intents.Insert.PHONE_TYPE;
/**
* The extra field for the phone isprimary flag.
* <P>Type: boolean</P>
*/
- public static final String PHONE_ISPRIMARY = "phone_isprimary";
+ @Deprecated
+ public static final String PHONE_ISPRIMARY =
+ ContactsContract.Intents.Insert.PHONE_ISPRIMARY;
/**
* The extra field for an optional second contact phone number.
* <P>Type: String</P>
*/
- public static final String SECONDARY_PHONE = "secondary_phone";
+ @Deprecated
+ public static final String SECONDARY_PHONE =
+ ContactsContract.Intents.Insert.SECONDARY_PHONE;
/**
* The extra field for an optional second contact phone number type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
* or a string specifying a custom label.</P>
*/
- public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type";
+ @Deprecated
+ public static final String SECONDARY_PHONE_TYPE =
+ ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE;
/**
* The extra field for an optional third contact phone number.
* <P>Type: String</P>
*/
- public static final String TERTIARY_PHONE = "tertiary_phone";
+ @Deprecated
+ public static final String TERTIARY_PHONE =
+ ContactsContract.Intents.Insert.TERTIARY_PHONE;
/**
* The extra field for an optional third contact phone number type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns},
* or a string specifying a custom label.</P>
*/
- public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
+ @Deprecated
+ public static final String TERTIARY_PHONE_TYPE =
+ ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE;
/**
* The extra field for the contact email address.
* <P>Type: String</P>
*/
- public static final String EMAIL = "email";
+ @Deprecated
+ public static final String EMAIL = ContactsContract.Intents.Insert.EMAIL;
/**
* The extra field for the contact email type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
* or a string specifying a custom label.</P>
*/
- public static final String EMAIL_TYPE = "email_type";
+ @Deprecated
+ public static final String EMAIL_TYPE = ContactsContract.Intents.Insert.EMAIL_TYPE;
/**
* The extra field for the email isprimary flag.
* <P>Type: boolean</P>
*/
- public static final String EMAIL_ISPRIMARY = "email_isprimary";
+ @Deprecated
+ public static final String EMAIL_ISPRIMARY =
+ ContactsContract.Intents.Insert.EMAIL_ISPRIMARY;
/**
* The extra field for an optional second contact email address.
* <P>Type: String</P>
*/
- public static final String SECONDARY_EMAIL = "secondary_email";
+ @Deprecated
+ public static final String SECONDARY_EMAIL =
+ ContactsContract.Intents.Insert.SECONDARY_EMAIL;
/**
* The extra field for an optional second contact email type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
* or a string specifying a custom label.</P>
*/
- public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type";
+ @Deprecated
+ public static final String SECONDARY_EMAIL_TYPE =
+ ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE;
/**
* The extra field for an optional third contact email address.
* <P>Type: String</P>
*/
- public static final String TERTIARY_EMAIL = "tertiary_email";
+ @Deprecated
+ public static final String TERTIARY_EMAIL =
+ ContactsContract.Intents.Insert.TERTIARY_EMAIL;
/**
* The extra field for an optional third contact email type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
* or a string specifying a custom label.</P>
*/
- public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type";
+ @Deprecated
+ public static final String TERTIARY_EMAIL_TYPE =
+ ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE;
/**
* The extra field for the contact postal address.
* <P>Type: String</P>
*/
- public static final String POSTAL = "postal";
+ @Deprecated
+ public static final String POSTAL = ContactsContract.Intents.Insert.POSTAL;
/**
* The extra field for the contact postal address type.
* <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
* or a string specifying a custom label.</P>
*/
- public static final String POSTAL_TYPE = "postal_type";
+ @Deprecated
+ public static final String POSTAL_TYPE = ContactsContract.Intents.Insert.POSTAL_TYPE;
/**
* The extra field for the postal isprimary flag.
* <P>Type: boolean</P>
*/
- public static final String POSTAL_ISPRIMARY = "postal_isprimary";
+ @Deprecated
+ public static final String POSTAL_ISPRIMARY = ContactsContract.Intents.Insert.POSTAL_ISPRIMARY;
/**
* The extra field for an IM handle.
* <P>Type: String</P>
*/
- public static final String IM_HANDLE = "im_handle";
+ @Deprecated
+ public static final String IM_HANDLE = ContactsContract.Intents.Insert.IM_HANDLE;
/**
* The extra field for the IM protocol
* <P>Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol}
* or {@link Contacts.ContactMethods#encodeCustomImProtocol}.</P>
*/
- public static final String IM_PROTOCOL = "im_protocol";
+ @Deprecated
+ public static final String IM_PROTOCOL = ContactsContract.Intents.Insert.IM_PROTOCOL;
/**
* The extra field for the IM isprimary flag.
* <P>Type: boolean</P>
*/
- public static final String IM_ISPRIMARY = "im_isprimary";
+ @Deprecated
+ public static final String IM_ISPRIMARY = ContactsContract.Intents.Insert.IM_ISPRIMARY;
}
}
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
new file mode 100644
index 0000000..d354ccf
--- /dev/null
+++ b/core/java/android/provider/ContactsContract.java
@@ -0,0 +1,2126 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * The contract between the contacts provider and applications. Contains definitions
+ * for the supported URIs and columns.
+ *
+ * @hide
+ */
+public final class ContactsContract {
+ /** The authority for the contacts provider */
+ public static final String AUTHORITY = "com.android.contacts";
+ /** A content:// style uri to the authority for the contacts provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ public interface SyncStateColumns extends SyncStateContract.Columns {
+ }
+
+ public static final class SyncState {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private SyncState() {}
+
+ public static final String CONTENT_DIRECTORY =
+ SyncStateContract.Constants.CONTENT_DIRECTORY;
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, CONTENT_DIRECTORY);
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static byte[] get(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.get(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#get
+ */
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Account account)
+ throws RemoteException {
+ return SyncStateContract.Helpers.getWithUri(provider, CONTENT_URI, account);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#set
+ */
+ public static void set(ContentProviderClient provider, Account account, byte[] data)
+ throws RemoteException {
+ SyncStateContract.Helpers.set(provider, CONTENT_URI, account, data);
+ }
+
+ /**
+ * @see android.provider.SyncStateContract.Helpers#newSetOperation
+ */
+ public static ContentProviderOperation newSetOperation(Account account, byte[] data) {
+ return SyncStateContract.Helpers.newSetOperation(CONTENT_URI, account, data);
+ }
+ }
+
+ /**
+ * Generic columns for use by sync adapters. The specific functions of
+ * these columns are private to the sync adapter. Other clients of the API
+ * should not attempt to either read or write this column.
+ */
+ private interface BaseSyncColumns {
+
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "sync4";
+ }
+
+ /**
+ * Columns that appear when each row of a table belongs to a specific
+ * account, including sync information that an account may need.
+ */
+ private interface SyncColumns extends BaseSyncColumns {
+ /**
+ * The name of the account instance to which this row belongs.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * String that uniquely identifies this row to its source account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String SOURCE_ID = "sourceid";
+
+ /**
+ * Version number that is updated whenever this row or its related data
+ * changes.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String VERSION = "version";
+
+ /**
+ * Flag indicating that {@link #VERSION} has changed, and this row needs
+ * to be synchronized by its owning account.
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String DIRTY = "dirty";
+ }
+
+ public interface ContactOptionsColumns {
+ /**
+ * The number of times a person has been contacted
+ * <P>Type: INTEGER</P>
+ */
+ public static final String TIMES_CONTACTED = "times_contacted";
+
+ /**
+ * The last time a person was contacted.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String LAST_TIME_CONTACTED = "last_time_contacted";
+
+ /**
+ * Is the contact starred?
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String STARRED = "starred";
+
+ /**
+ * A custom ringtone associated with a person. Not always present.
+ * <P>Type: TEXT (URI to the ringtone)</P>
+ */
+ public static final String CUSTOM_RINGTONE = "custom_ringtone";
+
+ /**
+ * Whether the person should always be sent to voicemail. Not always
+ * present.
+ * <P>Type: INTEGER (0 for false, 1 for true)</P>
+ */
+ public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
+ }
+
+ private interface ContactsColumns {
+ /**
+ * The display name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DISPLAY_NAME = "display_name";
+
+ /**
+ * Reference to the row in the data table holding the photo.
+ * <P>Type: INTEGER REFERENCES data(_id)</P>
+ */
+ public static final String PHOTO_ID = "photo_id";
+
+ /**
+ * Lookup value that reflects the {@link Groups#GROUP_VISIBLE} state of
+ * any {@link CommonDataKinds.GroupMembership} for this contact.
+ */
+ public static final String IN_VISIBLE_GROUP = "in_visible_group";
+
+ /**
+ * Contact presence status. See {@link android.provider.Im.CommonPresenceColumns}
+ * for individual status definitions. This column is only returned if explicitly
+ * requested in the query projection.
+ * <p>Type: NUMBER</p>
+ */
+ public static final String PRESENCE_STATUS = Presence.PRESENCE_STATUS;
+
+ /**
+ * Contact presence custom status. This column is only returned if explicitly
+ * requested in the query projection.
+ * <p>Type: TEXT</p>
+ */
+ public static final String PRESENCE_CUSTOM_STATUS = Presence.PRESENCE_CUSTOM_STATUS;
+
+ /**
+ * An indicator of whether this contact has at least one phone number. "1" if there is
+ * at least one phone number, "0" otherwise.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String HAS_PHONE_NUMBER = "has_phone_number";
+
+ /**
+ * An opaque value that contains hints on how to find the contact if
+ * its row id changed as a result of a sync or aggregation.
+ */
+ public static final String LOOKUP_KEY = "lookup";
+ }
+
+ /**
+ * Constants for the contacts table, which contains a record per group
+ * of raw contact representing the same person.
+ */
+ public static class Contacts implements BaseColumns, ContactsColumns,
+ ContactOptionsColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Contacts() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "contacts");
+
+ /**
+ * A content:// style URI for this table that should be used to create
+ * shortcuts or otherwise create long-term links to contacts. This URI
+ * should always be followed by a "/" and the contact's {@link #LOOKUP_KEY}.
+ * It can optionally also have a "/" and last known contact ID appended after
+ * that. This "complete" format is an important optimization and is highly recommended.
+ * <p>
+ * As long as the contact's row ID remains the same, this URI is
+ * equivalent to {@link #CONTENT_URI}. If the contact's row ID changes
+ * as a result of a sync or aggregation, this URI will look up the
+ * contact using indirect information (sync IDs or constituent raw
+ * contacts).
+ * <p>
+ * Lookup key should be appended unencoded - it is stored in the encoded
+ * form, ready for use in a URI.
+ */
+ public static final Uri CONTENT_LOOKUP_URI = Uri.withAppendedPath(CONTENT_URI,
+ "lookup");
+
+ /**
+ * Computes a complete lookup URI (see {@link #CONTENT_LOOKUP_URI}).
+ * Pass either a basic content URI with a contact ID to obtain an
+ * equivalent lookup URI. Pass a possibly stale lookup URI to get a fresh
+ * lookup URI for the same contact.
+ * <p>
+ * Returns null if the contact cannot be found.
+ */
+ public static Uri getLookupUri(ContentResolver resolver, Uri contentUri) {
+ Cursor c = resolver.query(contentUri,
+ new String[]{Contacts.LOOKUP_KEY, Contacts._ID}, null, null, null);
+ if (c == null) {
+ return null;
+ }
+
+ try {
+ if (c.moveToFirst()) {
+ String lookupKey = c.getString(0);
+ long contactId = c.getLong(1);
+ return ContentUris.withAppendedId(
+ Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+ contactId);
+ }
+ } finally {
+ c.close();
+ }
+ return null;
+ }
+
+ /**
+ * Build a {@link #CONTENT_LOOKUP_URI} lookup {@link Uri} using the
+ * given {@link Contacts#_ID} and {@link Contacts#LOOKUP_KEY}.
+ */
+ public static Uri getLookupUri(long contactId, String lookupKey) {
+ return ContentUris.withAppendedId(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
+ lookupKey), contactId);
+ }
+
+ /**
+ * Computes a content URI (see {@link #CONTENT_URI}) given a lookup URI.
+ * <p>
+ * Returns null if the contact cannot be found.
+ */
+ public static Uri lookupContact(ContentResolver resolver, Uri lookupUri) {
+ if (lookupUri == null) {
+ return null;
+ }
+
+ Cursor c = resolver.query(lookupUri, new String[]{Contacts._ID}, null, null, null);
+ if (c == null) {
+ return null;
+ }
+
+ try {
+ if (c.moveToFirst()) {
+ long contactId = c.getLong(0);
+ return ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+ }
+ } finally {
+ c.close();
+ }
+ return null;
+ }
+
+ /**
+ * The content:// style URI used for "type-to-filter" functionality on the
+ * {@link #CONTENT_URI} URI. The filter string will be used to match
+ * various parts of the contact name. The filter argument should be passed
+ * as an additional path segment after this URI.
+ */
+ public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(
+ CONTENT_URI, "filter");
+
+ /**
+ * The content:// style URI for this table joined with useful data from
+ * {@link Data}, filtered to include only starred contacts
+ * and the most frequently contacted contacts.
+ */
+ public static final Uri CONTENT_STREQUENT_URI = Uri.withAppendedPath(
+ CONTENT_URI, "strequent");
+
+ /**
+ * The content:// style URI used for "type-to-filter" functionality on the
+ * {@link #CONTENT_STREQUENT_URI} URI. The filter string will be used to match
+ * various parts of the contact name. The filter argument should be passed
+ * as an additional path segment after this URI.
+ */
+ public static final Uri CONTENT_STREQUENT_FILTER_URI = Uri.withAppendedPath(
+ CONTENT_STREQUENT_URI, "filter");
+
+ public static final Uri CONTENT_GROUP_URI = Uri.withAppendedPath(
+ CONTENT_URI, "group");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * people.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+ * person.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact";
+
+ /**
+ * A sub-directory of a single contact that contains all of the constituent raw contact
+ * {@link Data} rows.
+ */
+ public static final class Data implements BaseColumns, DataColumns {
+ /**
+ * no public constructor since this is a utility class
+ */
+ private Data() {}
+
+ /**
+ * The directory twig for this sub-table
+ */
+ public static final String CONTENT_DIRECTORY = "data";
+ }
+
+ /**
+ * A sub-directory of a single contact aggregate that contains all aggregation suggestions
+ * (other contacts). The aggregation suggestions are computed based on approximate
+ * data matches with this contact.
+ */
+ public static final class AggregationSuggestions implements BaseColumns, ContactsColumns {
+ /**
+ * No public constructor since this is a utility class
+ */
+ private AggregationSuggestions() {}
+
+ /**
+ * The directory twig for this sub-table
+ */
+ public static final String CONTENT_DIRECTORY = "suggestions";
+ }
+
+ /**
+ * A sub-directory of a single contact that contains the contact's primary photo.
+ */
+ public static final class Photo implements BaseColumns, DataColumns {
+ /**
+ * no public constructor since this is a utility class
+ */
+ private Photo() {}
+
+ /**
+ * The directory twig for this sub-table
+ */
+ public static final String CONTENT_DIRECTORY = "photo";
+ }
+
+ /**
+ * Opens an InputStream for the person's default photo and returns the
+ * photo as a Bitmap stream.
+ *
+ * @param contactUri the contact whose photo should be used
+ */
+ public static InputStream openContactPhotoInputStream(ContentResolver cr, Uri contactUri) {
+ Uri photoUri = Uri.withAppendedPath(contactUri, Photo.CONTENT_DIRECTORY);
+ if (photoUri == null) {
+ return null;
+ }
+ Cursor cursor = cr.query(photoUri,
+ new String[]{ContactsContract.CommonDataKinds.Photo.PHOTO}, null, null, null);
+ try {
+ if (cursor == null || !cursor.moveToNext()) {
+ return null;
+ }
+ byte[] data = cursor.getBlob(0);
+ if (data == null) {
+ return null;
+ }
+ return new ByteArrayInputStream(data);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ private interface RawContactsColumns {
+ /**
+ * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} that this
+ * data belongs to.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String CONTACT_ID = "contact_id";
+
+ /**
+ * Flag indicating that this {@link RawContacts} entry and its children has
+ * been restricted to specific platform apps.
+ * <P>Type: INTEGER (boolean)</P>
+ *
+ * @hide until finalized in future platform release
+ */
+ public static final String IS_RESTRICTED = "is_restricted";
+
+ /**
+ * The aggregation mode for this contact.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String AGGREGATION_MODE = "aggregation_mode";
+
+ /**
+ * The "deleted" flag: "0" by default, "1" if the row has been marked
+ * for deletion. When {@link android.content.ContentResolver#delete} is
+ * called on a raw contact, it is marked for deletion and removed from its
+ * aggregate contact. The sync adaptor deletes the raw contact on the server and
+ * then calls ContactResolver.delete once more, this time passing the
+ * {@link RawContacts#DELETE_PERMANENTLY} query parameter to finalize the data removal.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DELETED = "deleted";
+ }
+
+ /**
+ * Constants for the raw_contacts table, which contains the base contact
+ * information per sync source. Sync adapters and contact management apps
+ * are the primary consumers of this API.
+ */
+ public static final class RawContacts implements BaseColumns, RawContactsColumns,
+ ContactOptionsColumns, SyncColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private RawContacts() {
+ }
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * people.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/raw_contact";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+ * person.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/raw_contact";
+
+ /**
+ * Query parameter that can be passed with the {@link #CONTENT_URI} URI
+ * to the {@link android.content.ContentResolver#delete} method to
+ * indicate that the raw contact can be deleted physically, rather than
+ * merely marked as deleted.
+ */
+ public static final String DELETE_PERMANENTLY = "delete_permanently";
+
+ /**
+ * Aggregation mode: aggregate asynchronously.
+ */
+ public static final int AGGREGATION_MODE_DEFAULT = 0;
+
+ /**
+ * Aggregation mode: aggregate at the time the raw contact is inserted/updated.
+ */
+ public static final int AGGREGATION_MODE_IMMEDIATE = 1;
+
+ /**
+ * If {@link #AGGREGATION_MODE} is {@link #AGGREGATION_MODE_SUSPENDED}, changes
+ * to the raw contact do not cause its aggregation to be revisited. Note that changing
+ * {@link #AGGREGATION_MODE} from {@link #AGGREGATION_MODE_SUSPENDED} to
+ * {@link #AGGREGATION_MODE_DEFAULT} does not trigger an aggregation pass. Any subsequent
+ * change to the raw contact's data will.
+ */
+ public static final int AGGREGATION_MODE_SUSPENDED = 2;
+
+ /**
+ * Aggregation mode: never aggregate this raw contact (note that the raw contact will not
+ * have a corresponding Aggregate and therefore will not be included in Aggregates
+ * query results.)
+ */
+ public static final int AGGREGATION_MODE_DISABLED = 3;
+
+ /**
+ * A sub-directory of a single raw contact that contains all of their {@link Data} rows.
+ * To access this directory append {@link Data#CONTENT_DIRECTORY} to the contact URI.
+ */
+ public static final class Data implements BaseColumns, DataColumns {
+ /**
+ * no public constructor since this is a utility class
+ */
+ private Data() {
+ }
+
+ /**
+ * The directory twig for this sub-table
+ */
+ public static final String CONTENT_DIRECTORY = "data";
+ }
+ }
+
+ private interface DataColumns {
+ /**
+ * The package name to use when creating {@link Resources} objects for
+ * this data row. This value is only designed for use when building user
+ * interfaces, and should not be used to infer the owner.
+ */
+ public static final String RES_PACKAGE = "res_package";
+
+ /**
+ * The MIME type of the item represented by this row.
+ */
+ public static final String MIMETYPE = "mimetype";
+
+ /**
+ * A reference to the {@link RawContacts#_ID}
+ * that this data belongs to.
+ */
+ public static final String RAW_CONTACT_ID = "raw_contact_id";
+
+ /**
+ * Whether this is the primary entry of its kind for the raw contact it belongs to
+ * <P>Type: INTEGER (if set, non-0 means true)</P>
+ */
+ public static final String IS_PRIMARY = "is_primary";
+
+ /**
+ * Whether this is the primary entry of its kind for the aggregate
+ * contact it belongs to. Any data record that is "super primary" must
+ * also be "primary".
+ * <P>Type: INTEGER (if set, non-0 means true)</P>
+ */
+ public static final String IS_SUPER_PRIMARY = "is_super_primary";
+
+ /**
+ * The version of this data record. This is a read-only value. The data column is
+ * guaranteed to not change without the version going up. This value is monotonically
+ * increasing.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA_VERSION = "data_version";
+
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA1 = "data1";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA2 = "data2";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA3 = "data3";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA4 = "data4";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA5 = "data5";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA6 = "data6";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA7 = "data7";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA8 = "data8";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA9 = "data9";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA10 = "data10";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA11 = "data11";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA12 = "data12";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA13 = "data13";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA14 = "data14";
+ /** Generic data column, the meaning is {@link #MIMETYPE} specific */
+ public static final String DATA15 = "data15";
+
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC1 = "data_sync1";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC2 = "data_sync2";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC3 = "data_sync3";
+ /** Generic column for use by sync adapters. */
+ public static final String SYNC4 = "data_sync4";
+
+ /**
+ * An optional insert, update or delete URI parameter that determines if
+ * the corresponding raw contact should be marked as dirty. The default
+ * value is true.
+ */
+ public static final String MARK_AS_DIRTY = "mark_as_dirty";
+ }
+
+ /**
+ * Constants for the data table, which contains data points tied to a raw contact.
+ * For example, a phone number or email address. Each row in this table contains a type
+ * definition and some generic columns. Each data type can define the meaning for each of
+ * the generic columns.
+ */
+ public static final class Data implements BaseColumns, DataColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Data() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
+
+ /**
+ * The content:// style URI for this table joined with {@link Presence}
+ * data where applicable.
+ *
+ * @hide
+ */
+ public static final Uri CONTENT_WITH_PRESENCE_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "data_with_presence");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
+ }
+
+ private interface PhoneLookupColumns {
+ /**
+ * The phone number as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String NUMBER = "number";
+
+ /**
+ * The type of phone number, for example Home or Work.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String TYPE = "type";
+
+ /**
+ * The user defined label for the phone number.
+ * <P>Type: TEXT</P>
+ */
+ public static final String LABEL = "label";
+ }
+
+ /**
+ * A table that represents the result of looking up a phone number, for
+ * example for caller ID. To perform a lookup you must append the number you
+ * want to find to {@link #CONTENT_FILTER_URI}.
+ */
+ public static final class PhoneLookup implements BaseColumns, PhoneLookupColumns,
+ ContactsColumns, ContactOptionsColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private PhoneLookup() {}
+
+ /**
+ * The content:// style URI for this table. Append the phone number you want to lookup
+ * to this URI and query it to perform a lookup. For example:
+ *
+ * {@code
+ * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_URI, phoneNumber);
+ * }
+ */
+ public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "phone_lookup");
+ }
+
+ /**
+ * Additional data mixed in with {@link Im.CommonPresenceColumns} to link
+ * back to specific {@link ContactsContract.Contacts#_ID} entries.
+ */
+ private interface PresenceColumns {
+
+ /**
+ * The unique ID for a row.
+ * <P>Type: INTEGER (long)</P>
+ */
+ public static final String _ID = "presence_id";
+
+ /**
+ * Reference to the {@link Data#_ID} entry that owns this presence.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DATA_ID = "presence_data_id";
+
+ /**
+ * <p>Type: NUMBER</p>
+ */
+ public static final String PROTOCOL = "protocol";
+
+ /**
+ * Name of the custom protocol. Should be supplied along with the {@link #PROTOCOL} value
+ * {@link ContactsContract.CommonDataKinds.Im#PROTOCOL_CUSTOM}. Should be null or
+ * omitted if {@link #PROTOCOL} value is not
+ * {@link ContactsContract.CommonDataKinds.Im#PROTOCOL_CUSTOM}.
+ *
+ * <p>Type: NUMBER</p>
+ */
+ public static final String CUSTOM_PROTOCOL = "custom_protocol";
+
+ /**
+ * The IM handle the presence item is for. The handle is scoped to
+ * {@link #PROTOCOL}.
+ * <P>Type: TEXT</P>
+ */
+ public static final String IM_HANDLE = "im_handle";
+
+ /**
+ * The IM account for the local user that the presence data came from.
+ * <P>Type: TEXT</P>
+ */
+ public static final String IM_ACCOUNT = "im_account";
+ }
+
+ public static final class Presence implements PresenceColumns, Im.CommonPresenceColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Presence() {
+ }
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "presence");
+
+ /**
+ * Gets the resource ID for the proper presence icon.
+ *
+ * @param status the status to get the icon for
+ * @return the resource ID for the proper presence icon
+ */
+ public static final int getPresenceIconResourceId(int status) {
+ switch (status) {
+ case AVAILABLE:
+ return android.R.drawable.presence_online;
+ case IDLE:
+ case AWAY:
+ return android.R.drawable.presence_away;
+ case DO_NOT_DISTURB:
+ return android.R.drawable.presence_busy;
+ case INVISIBLE:
+ return android.R.drawable.presence_invisible;
+ case OFFLINE:
+ default:
+ return android.R.drawable.presence_offline;
+ }
+ }
+
+ /**
+ * Returns the precedence of the status code the higher number being the higher precedence.
+ *
+ * @param status The status code.
+ * @return An integer representing the precedence, 0 being the lowest.
+ */
+ public static final int getPresencePrecedence(int status) {
+ // Keep this function here incase we want to enforce a different precedence than the
+ // natural order of the status constants.
+ return status;
+ }
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * presence details.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-presence";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+ * presence detail.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im-presence";
+ }
+
+ /**
+ * Container for definitions of common data types stored in the {@link Data} table.
+ */
+ public static final class CommonDataKinds {
+ /**
+ * The {@link Data#RES_PACKAGE} value for common data that should be
+ * shown using a default style.
+ */
+ public static final String PACKAGE_COMMON = "common";
+
+ /**
+ * Columns common across the specific types.
+ */
+ private interface BaseCommonColumns {
+ /**
+ * The package name to use when creating {@link Resources} objects for
+ * this data row. This value is only designed for use when building user
+ * interfaces, and should not be used to infer the owner.
+ */
+ public static final String RES_PACKAGE = "res_package";
+
+ /**
+ * The MIME type of the item represented by this row.
+ */
+ public static final String MIMETYPE = "mimetype";
+
+ /**
+ * The {@link RawContacts#_ID} that this data belongs to.
+ */
+ public static final String RAW_CONTACT_ID = "raw_contact_id";
+ }
+
+ /**
+ * The base types that all "Typed" data kinds support.
+ */
+ public interface BaseTypes {
+
+ /**
+ * A custom type. The custom label should be supplied by user.
+ */
+ public static int TYPE_CUSTOM = 0;
+ }
+
+ /**
+ * Columns common across the specific types.
+ */
+ private interface CommonColumns extends BaseTypes{
+ /**
+ * The type of data, for example Home or Work.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String TYPE = "data1";
+
+ /**
+ * The data for the contact method.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DATA = "data2";
+
+ /**
+ * The user defined label for the the contact method.
+ * <P>Type: TEXT</P>
+ */
+ public static final String LABEL = "data3";
+ }
+
+ /**
+ * Parts of the name.
+ */
+ public static final class StructuredName implements BaseCommonColumns {
+ private StructuredName() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name";
+
+ /**
+ * The given name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String GIVEN_NAME = "data1";
+
+ /**
+ * The family name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String FAMILY_NAME = "data2";
+
+ /**
+ * The contact's honorific prefix, e.g. "Sir"
+ * <P>Type: TEXT</P>
+ */
+ public static final String PREFIX = "data3";
+
+ /**
+ * The contact's middle name
+ * <P>Type: TEXT</P>
+ */
+ public static final String MIDDLE_NAME = "data4";
+
+ /**
+ * The contact's honorific suffix, e.g. "Jr"
+ */
+ public static final String SUFFIX = "data5";
+
+ /**
+ * The phonetic version of the given name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONETIC_GIVEN_NAME = "data6";
+
+ /**
+ * The phonetic version of the additional name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONETIC_MIDDLE_NAME = "data7";
+
+ /**
+ * The phonetic version of the family name for the contact.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONETIC_FAMILY_NAME = "data8";
+
+ /**
+ * The name that should be used to display the contact.
+ * <i>Unstructured component of the name should be consistent with
+ * its structured representation.</i>
+ * <p>
+ * Type: TEXT
+ */
+ public static final String DISPLAY_NAME = "data9";
+ }
+
+ /**
+ * A nickname.
+ */
+ public static final class Nickname implements CommonColumns, BaseCommonColumns {
+ private Nickname() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/nickname";
+
+ public static final int TYPE_DEFAULT = 1;
+ public static final int TYPE_OTHER_NAME = 2;
+ public static final int TYPE_MAINDEN_NAME = 3;
+ public static final int TYPE_SHORT_NAME = 4;
+ public static final int TYPE_INITIALS = 5;
+
+ /**
+ * The name itself
+ */
+ public static final String NAME = DATA;
+ }
+
+ /**
+ * Common data definition for telephone numbers.
+ */
+ public static final class Phone implements BaseCommonColumns, CommonColumns {
+ private Phone() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/phone_v2";
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * phones.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/phone_v2";
+
+ /**
+ * The content:// style URI for all data records of the
+ * {@link Phone#CONTENT_ITEM_TYPE} MIME type, combined with the
+ * associated raw contact and aggregate contact data.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+ "phones");
+
+ /**
+ * The content:// style URL for phone lookup using a filter. The filter returns
+ * records of MIME type {@link Phone#CONTENT_ITEM_TYPE}. The filter is applied
+ * to display names as well as phone numbers. The filter argument should be passed
+ * as an additional path segment after this URI.
+ */
+ public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
+ "filter");
+
+ public static final int TYPE_HOME = 1;
+ public static final int TYPE_MOBILE = 2;
+ public static final int TYPE_WORK = 3;
+ public static final int TYPE_FAX_WORK = 4;
+ public static final int TYPE_FAX_HOME = 5;
+ public static final int TYPE_PAGER = 6;
+ public static final int TYPE_OTHER = 7;
+ public static final int TYPE_CALLBACK = 8;
+ public static final int TYPE_CAR = 9;
+ public static final int TYPE_COMPANY_MAIN = 10;
+ public static final int TYPE_ISDN = 11;
+ public static final int TYPE_MAIN = 12;
+ public static final int TYPE_OTHER_FAX = 13;
+ public static final int TYPE_RADIO = 14;
+ public static final int TYPE_TELEX = 15;
+ public static final int TYPE_TTY_TDD = 16;
+ public static final int TYPE_WORK_MOBILE = 17;
+ public static final int TYPE_WORK_PAGER = 18;
+ public static final int TYPE_ASSISTANT = 19;
+
+ /**
+ * The phone number as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String NUMBER = DATA;
+
+ public static final CharSequence getDisplayLabel(Context context, int type,
+ CharSequence label, CharSequence[] labelArray) {
+ CharSequence display = "";
+
+ if (type != Phone.TYPE_CUSTOM) {
+ CharSequence[] labels = labelArray != null? labelArray
+ : context.getResources().getTextArray(
+ com.android.internal.R.array.phoneTypes);
+ try {
+ display = labels[type - 1];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ display = labels[Phone.TYPE_CUSTOM];
+ }
+ } else {
+ if (!TextUtils.isEmpty(label)) {
+ display = label;
+ }
+ }
+ return display;
+ }
+
+ public static final CharSequence getDisplayLabel(Context context, int type,
+ CharSequence label) {
+ return getDisplayLabel(context, type, label, null);
+ }
+ }
+
+ /**
+ * Common data definition for email addresses.
+ */
+ public static final class Email implements BaseCommonColumns, CommonColumns {
+ private Email() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/email_v2";
+
+ /**
+ * The content:// style URI for all data records of the
+ * {@link Email#CONTENT_ITEM_TYPE} MIME type, combined with the
+ * associated raw contact and aggregate contact data.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+ "emails");
+
+ /**
+ * The content:// style URL for looking up data rows by email address. The
+ * lookup argument, an email address, should be passed as an additional path segment
+ * after this URI.
+ */
+ public static final Uri CONTENT_LOOKUP_URI = Uri.withAppendedPath(CONTENT_URI,
+ "lookup");
+
+ @Deprecated
+ public static final Uri CONTENT_FILTER_EMAIL_URI = CONTENT_LOOKUP_URI;
+
+ /**
+ * The content:// style URL for email lookup using a filter. The filter returns
+ * records of MIME type {@link Email#CONTENT_ITEM_TYPE}. The filter is applied
+ * to display names as well as email addresses. The filter argument should be passed
+ * as an additional path segment after this URI.
+ */
+ public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
+ "filter");
+
+ public static final int TYPE_HOME = 1;
+ public static final int TYPE_WORK = 2;
+ public static final int TYPE_OTHER = 3;
+ public static final int TYPE_MOBILE = 4;
+
+ /**
+ * The display name for the email address
+ * <P>Type: TEXT</P>
+ */
+ public static final String DISPLAY_NAME = "data4";
+ }
+
+ /**
+ * Common data definition for postal addresses.
+ */
+ public static final class StructuredPostal implements BaseCommonColumns, CommonColumns {
+ private StructuredPostal() {
+ }
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/postal-address_v2";
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of
+ * postal addresses.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/postal-address_v2";
+
+ /**
+ * The content:// style URI for all data records of the
+ * {@link StructuredPostal#CONTENT_ITEM_TYPE} MIME type.
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+ "postals");
+
+ public static final int TYPE_HOME = 1;
+ public static final int TYPE_WORK = 2;
+ public static final int TYPE_OTHER = 3;
+
+ /**
+ * The full, unstructured postal address. <i>This field must be
+ * consistent with any structured data.</i>
+ * <p>
+ * Type: TEXT
+ */
+ public static final String FORMATTED_ADDRESS = DATA;
+
+ /**
+ * Can be street, avenue, road, etc. This element also includes the
+ * house number and room/apartment/flat/floor number.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String STREET = "data6";
+
+ /**
+ * Covers actual P.O. boxes, drawers, locked bags, etc. This is
+ * usually but not always mutually exclusive with street.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String POBOX = "data7";
+
+ /**
+ * This is used to disambiguate a street address when a city
+ * contains more than one street with the same name, or to specify a
+ * small place whose mail is routed through a larger postal town. In
+ * China it could be a county or a minor city.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String NEIGHBORHOOD = "data8";
+
+ /**
+ * Can be city, village, town, borough, etc. This is the postal town
+ * and not necessarily the place of residence or place of business.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String CITY = "data9";
+
+ /**
+ * A state, province, county (in Ireland), Land (in Germany),
+ * departement (in France), etc.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String REGION = "data11";
+
+ /**
+ * Postal code. Usually country-wide, but sometimes specific to the
+ * city (e.g. "2" in "Dublin 2, Ireland" addresses).
+ * <p>
+ * Type: TEXT
+ */
+ public static final String POSTCODE = "data12";
+
+ /**
+ * The name or code of the country.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String COUNTRY = "data13";
+ }
+
+ /**
+ * Common data definition for IM addresses.
+ */
+ public static final class Im implements BaseCommonColumns, CommonColumns {
+ private Im() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/im";
+
+ public static final int TYPE_HOME = 1;
+ public static final int TYPE_WORK = 2;
+ public static final int TYPE_OTHER = 3;
+
+ /**
+ * This column should be populated with one of the defined
+ * constants, e.g. {@link #PROTOCOL_YAHOO}. If the value of this
+ * column is {@link #PROTOCOL_CUSTOM}, the {@link #CUSTOM_PROTOCOL}
+ * should contain the name of the custom protocol.
+ */
+ public static final String PROTOCOL = "data5";
+
+ public static final String CUSTOM_PROTOCOL = "data6";
+
+ /*
+ * The predefined IM protocol types.
+ */
+ public static final int PROTOCOL_CUSTOM = -1;
+ public static final int PROTOCOL_AIM = 0;
+ public static final int PROTOCOL_MSN = 1;
+ public static final int PROTOCOL_YAHOO = 2;
+ public static final int PROTOCOL_SKYPE = 3;
+ public static final int PROTOCOL_QQ = 4;
+ public static final int PROTOCOL_GOOGLE_TALK = 5;
+ public static final int PROTOCOL_ICQ = 6;
+ public static final int PROTOCOL_JABBER = 7;
+ public static final int PROTOCOL_NETMEETING = 8;
+ }
+
+ /**
+ * Common data definition for organizations.
+ */
+ public static final class Organization implements BaseCommonColumns, CommonColumns {
+ private Organization() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/organization";
+
+ public static final int TYPE_WORK = 1;
+ public static final int TYPE_OTHER = 2;
+
+ /**
+ * The company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String COMPANY = DATA;
+
+ /**
+ * The position title at this company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String TITLE = "data4";
+
+ /**
+ * The department at this company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DEPARTMENT = "data5";
+
+ /**
+ * The job description at this company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String JOB_DESCRIPTION = "data6";
+
+ /**
+ * The symbol of this company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String SYMBOL = "data7";
+
+ /**
+ * The phonetic name of this company as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String PHONETIC_NAME = "data8";
+ }
+
+ /**
+ * Common data definition for miscellaneous information.
+ */
+ public static final class Miscellaneous implements BaseCommonColumns {
+ private Miscellaneous() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/misc";
+
+ /**
+ * The birthday as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String BIRTHDAY = "data1";
+
+ /**
+ * The nickname as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String NICKNAME = "data2";
+ }
+
+ /**
+ * Common data definition for relations.
+ */
+ public static final class Relation implements BaseCommonColumns, CommonColumns {
+ private Relation() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/relation";
+
+ public static final int TYPE_ASSISTANT = 1;
+ public static final int TYPE_BROTHER = 2;
+ public static final int TYPE_CHILD = 3;
+ public static final int TYPE_DOMESTIC_PARTNER = 4;
+ public static final int TYPE_FATHER = 5;
+ public static final int TYPE_FRIEND = 6;
+ public static final int TYPE_MANAGER = 7;
+ public static final int TYPE_MOTHER = 8;
+ public static final int TYPE_PARENT = 9;
+ public static final int TYPE_PARTNER = 10;
+ public static final int TYPE_REFERRED_BY = 11;
+ public static final int TYPE_RELATIVE = 12;
+ public static final int TYPE_SISTER = 13;
+ public static final int TYPE_SPOUSE = 14;
+
+ /**
+ * The name of the relative as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String NAME = DATA;
+ }
+
+ /**
+ * Common data definition for events.
+ */
+ public static final class Event implements BaseCommonColumns, CommonColumns {
+ private Event() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/event";
+
+ public static final int TYPE_ANNIVERSARY = 1;
+ public static final int TYPE_OTHER = 2;
+
+ /**
+ * The event start date as the user entered it.
+ * <P>Type: TEXT</P>
+ */
+ public static final String START_DATE = DATA;
+ }
+
+ /**
+ * Photo of the contact.
+ */
+ public static final class Photo implements BaseCommonColumns {
+ private Photo() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo";
+
+ /**
+ * Thumbnail photo of the raw contact. This is the raw bytes of an image
+ * that could be inflated using {@link BitmapFactory}.
+ * <p>
+ * Type: BLOB
+ */
+ public static final String PHOTO = "data1";
+ }
+
+ /**
+ * Notes about the contact.
+ */
+ public static final class Note implements BaseCommonColumns {
+ private Note() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/note";
+
+ /**
+ * The note text.
+ * <P>Type: TEXT</P>
+ */
+ public static final String NOTE = "data1";
+ }
+
+ /**
+ * Group Membership.
+ */
+ public static final class GroupMembership implements BaseCommonColumns {
+ private GroupMembership() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/group_membership";
+
+ /**
+ * The row id of the group that this group membership refers to. Exactly one of
+ * this or {@link #GROUP_SOURCE_ID} must be set when inserting a row.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String GROUP_ROW_ID = "data1";
+
+ /**
+ * The sourceid of the group that this group membership refers to. Exactly one of
+ * this or {@link #GROUP_ROW_ID} must be set when inserting a row.
+ * <P>Type: TEXT</P>
+ */
+ public static final String GROUP_SOURCE_ID = "group_sourceid";
+ }
+
+ /**
+ * Website related to the contact.
+ */
+ public static final class Website implements BaseCommonColumns, CommonColumns {
+ private Website() {}
+
+ /** MIME type used when storing this in data table. */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/website";
+
+ public static final int TYPE_HOMEPAGE = 1;
+ public static final int TYPE_BLOG = 2;
+ public static final int TYPE_PROFILE = 3;
+ public static final int TYPE_HOME = 4;
+ public static final int TYPE_WORK = 5;
+ public static final int TYPE_FTP = 6;
+ public static final int TYPE_OTHER = 7;
+
+ /**
+ * The website URL string.
+ * <P>Type: TEXT</P>
+ */
+ public static final String URL = "data1";
+ }
+ }
+
+ // TODO: make this private before unhiding
+ public interface GroupsColumns {
+ /**
+ * The display title of this group.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * The package name to use when creating {@link Resources} objects for
+ * this group. This value is only designed for use when building user
+ * interfaces, and should not be used to infer the owner.
+ */
+ public static final String RES_PACKAGE = "res_package";
+
+ /**
+ * The display title of this group to load as a resource from
+ * {@link #RES_PACKAGE}, which may be localized.
+ * <P>Type: TEXT</P>
+ */
+ public static final String TITLE_RES = "title_res";
+
+ /**
+ * Notes about the group.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String NOTES = "notes";
+
+ /**
+ * The ID of this group if it is a System Group, i.e. a group that has a special meaning
+ * to the sync adapter, null otherwise.
+ * <P>Type: TEXT</P>
+ */
+ public static final String SYSTEM_ID = "system_id";
+
+ /**
+ * The total number of {@link Contacts} that have
+ * {@link CommonDataKinds.GroupMembership} in this group. Read-only value that is only
+ * present when querying {@link Groups#CONTENT_SUMMARY_URI}.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String SUMMARY_COUNT = "summ_count";
+
+ /**
+ * The total number of {@link Contacts} that have both
+ * {@link CommonDataKinds.GroupMembership} in this group, and also have phone numbers.
+ * Read-only value that is only present when querying
+ * {@link Groups#CONTENT_SUMMARY_URI}.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String SUMMARY_WITH_PHONES = "summ_phones";
+
+ /**
+ * Flag indicating if the contacts belonging to this group should be
+ * visible in any user interface.
+ * <p>
+ * Type: INTEGER (boolean)
+ */
+ public static final String GROUP_VISIBLE = "group_visible";
+
+ /**
+ * The "deleted" flag: "0" by default, "1" if the row has been marked
+ * for deletion. When {@link android.content.ContentResolver#delete} is
+ * called on a raw contact, it is marked for deletion and removed from its
+ * aggregate contact. The sync adaptor deletes the raw contact on the server and
+ * then calls ContactResolver.delete once more, this time passing the
+ * {@link RawContacts#DELETE_PERMANENTLY} query parameter to finalize the data removal.
+ * <P>Type: INTEGER</P>
+ */
+ public static final String DELETED = "deleted";
+
+ /**
+ * Whether this group should be synced if the SYNC_EVERYTHING settings
+ * is false for this group's account.
+ * <p>
+ * Type: INTEGER (boolean)
+ */
+ public static final String SHOULD_SYNC = "should_sync";
+ }
+
+ /**
+ * Constants for the groups table.
+ */
+ public static final class Groups implements BaseColumns, GroupsColumns, SyncColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Groups() {
+ }
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "groups");
+
+ /**
+ * The content:// style URI for this table joined with details data from
+ * {@link Data}.
+ */
+ public static final Uri CONTENT_SUMMARY_URI = Uri.withAppendedPath(AUTHORITY_URI,
+ "groups_summary");
+
+ /**
+ * The MIME type of a directory of groups.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/group";
+
+ /**
+ * The MIME type of a single group.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/group";
+
+ /**
+ * Query parameter that can be passed with the {@link #CONTENT_URI} URI
+ * to the {@link android.content.ContentResolver#delete} method to
+ * indicate that the raw contact can be deleted physically, rather than
+ * merely marked as deleted.
+ */
+ public static final String DELETE_PERMANENTLY = "delete_permanently";
+
+ /**
+ * An optional update or insert URI parameter that determines if the
+ * group should be marked as dirty. The default value is true.
+ */
+ public static final String MARK_AS_DIRTY = "mark_as_dirty";
+ }
+
+ /**
+ * Constants for the contact aggregation exceptions table, which contains
+ * aggregation rules overriding those used by automatic aggregation. This type only
+ * supports query and update. Neither insert nor delete are supported.
+ */
+ public static final class AggregationExceptions implements BaseColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private AggregationExceptions() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, "aggregation_exceptions");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/aggregation_exception";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of an aggregation exception
+ */
+ public static final String CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/aggregation_exception";
+
+ /**
+ * The type of exception: {@link #TYPE_KEEP_TOGETHER}, {@link #TYPE_KEEP_SEPARATE} or
+ * {@link #TYPE_AUTOMATIC}.
+ *
+ * <P>Type: INTEGER</P>
+ */
+ public static final String TYPE = "type";
+
+ /**
+ * Allows the provider to automatically decide whether the specified raw contacts should
+ * be included in the same aggregate contact or not.
+ */
+ public static final int TYPE_AUTOMATIC = 0;
+
+ /**
+ * Makes sure that the specified raw contacts are included in the same
+ * aggregate contact.
+ */
+ public static final int TYPE_KEEP_TOGETHER = 1;
+
+ @Deprecated
+ public static final int TYPE_KEEP_IN = 1;
+
+ /**
+ * Makes sure that the specified raw contacts are NOT included in the same
+ * aggregate contact.
+ */
+ public static final int TYPE_KEEP_SEPARATE = 2;
+
+ @Deprecated
+ public static final int TYPE_KEEP_OUT = 2;
+
+ @Deprecated
+ public static final String CONTACT_ID = "contact_id";
+
+ @Deprecated
+ public static final String RAW_CONTACT_ID = "raw_contact_id";
+
+ /**
+ * A reference to the {@link RawContacts#_ID} of the raw contact that the rule applies to.
+ */
+ public static final String RAW_CONTACT_ID1 = "raw_contact_id1";
+
+ /**
+ * A reference to the other {@link RawContacts#_ID} of the raw contact that the rule
+ * applies to.
+ */
+ public static final String RAW_CONTACT_ID2 = "raw_contact_id2";
+ }
+
+ private interface SettingsColumns {
+ /**
+ * The name of the account instance to which this row belongs.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * The type of account to which this row belongs, which when paired with
+ * {@link #ACCOUNT_NAME} identifies a specific account.
+ * <P>Type: TEXT</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * Depending on the mode defined by the sync-adapter, this flag controls
+ * the top-level sync behavior for this data source.
+ * <p>
+ * Type: INTEGER (boolean)
+ */
+ public static final String SHOULD_SYNC = "should_sync";
+
+ /**
+ * Flag indicating if contacts without any {@link CommonDataKinds.GroupMembership}
+ * entries should be visible in any user interface.
+ * <p>
+ * Type: INTEGER (boolean)
+ */
+ public static final String UNGROUPED_VISIBLE = "ungrouped_visible";
+
+ /**
+ * Read-only count of {@link Contacts} from a specific source that have
+ * no {@link CommonDataKinds.GroupMembership} entries.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String UNGROUPED_COUNT = "summ_count";
+
+ /**
+ * Read-only count of {@link Contacts} from a specific source that have
+ * no {@link CommonDataKinds.GroupMembership} entries, and also have phone numbers.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String UNGROUPED_WITH_PHONES = "summ_phones";
+ }
+
+ /**
+ * Contacts-specific settings for various {@link Account}.
+ */
+ public static final class Settings implements SettingsColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Settings() {
+ }
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, "settings");
+
+ /**
+ * The MIME-type of {@link #CONTENT_URI} providing a directory of
+ * settings.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/setting";
+
+ /**
+ * The MIME-type of {@link #CONTENT_URI} providing a single setting.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/setting";
+ }
+
+ /**
+ * Contains helper classes used to create or manage {@link android.content.Intent Intents}
+ * that involve contacts.
+ */
+ public static final class Intents {
+ /**
+ * This is the intent that is fired when a search suggestion is clicked on.
+ */
+ public static final String SEARCH_SUGGESTION_CLICKED =
+ "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED";
+
+ /**
+ * This is the intent that is fired when a search suggestion for dialing a number
+ * is clicked on.
+ */
+ public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED =
+ "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED";
+
+ /**
+ * This is the intent that is fired when a search suggestion for creating a contact
+ * is clicked on.
+ */
+ public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED =
+ "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED";
+
+ /**
+ * Starts an Activity that lets the user pick a contact to attach an image to.
+ * After picking the contact it launches the image cropper in face detection mode.
+ */
+ public static final String ATTACH_IMAGE =
+ "com.android.contacts.action.ATTACH_IMAGE";
+
+ /**
+ * Takes as input a data URI with a mailto: or tel: scheme. If a single
+ * contact exists with the given data it will be shown. If no contact
+ * exists, a dialog will ask the user if they want to create a new
+ * contact with the provided details filled in. If multiple contacts
+ * share the data the user will be prompted to pick which contact they
+ * want to view.
+ * <p>
+ * For <code>mailto:</code> URIs, the scheme specific portion must be a
+ * raw email address, such as one built using
+ * {@link Uri#fromParts(String, String, String)}.
+ * <p>
+ * For <code>tel:</code> URIs, the scheme specific portion is compared
+ * to existing numbers using the standard caller ID lookup algorithm.
+ * The number must be properly encoded, for example using
+ * {@link Uri#fromParts(String, String, String)}.
+ * <p>
+ * Any extras from the {@link Insert} class will be passed along to the
+ * create activity if there are no contacts to show.
+ * <p>
+ * Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip
+ * prompting the user when the contact doesn't exist.
+ */
+ public static final String SHOW_OR_CREATE_CONTACT =
+ "com.android.contacts.action.SHOW_OR_CREATE_CONTACT";
+
+ /**
+ * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new
+ * contact if no matching contact found. Otherwise, default behavior is
+ * to prompt user with dialog before creating.
+ * <p>
+ * Type: BOOLEAN
+ */
+ public static final String EXTRA_FORCE_CREATE =
+ "com.android.contacts.action.FORCE_CREATE";
+
+ /**
+ * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact
+ * description to be shown when prompting user about creating a new
+ * contact.
+ * <p>
+ * Type: STRING
+ */
+ public static final String EXTRA_CREATE_DESCRIPTION =
+ "com.android.contacts.action.CREATE_DESCRIPTION";
+
+ /**
+ * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
+ * dialog location using screen coordinates. When not specified, the
+ * dialog will be centered.
+ */
+ public static final String EXTRA_TARGET_RECT = "target_rect";
+
+ /**
+ * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to specify a
+ * desired dialog style, usually a variation on size. One of
+ * {@link #MODE_SMALL}, {@link #MODE_MEDIUM}, or {@link #MODE_LARGE}.
+ */
+ public static final String EXTRA_MODE = "mode";
+
+ /**
+ * Value for {@link #EXTRA_MODE} to show a small-sized dialog.
+ */
+ public static final int MODE_SMALL = 1;
+
+ /**
+ * Value for {@link #EXTRA_MODE} to show a medium-sized dialog.
+ */
+ public static final int MODE_MEDIUM = 2;
+
+ /**
+ * Value for {@link #EXTRA_MODE} to show a large-sized dialog.
+ */
+ public static final int MODE_LARGE = 3;
+
+ /**
+ * Optional extra used with {@link #SHOW_OR_CREATE_CONTACT} to indicate
+ * a list of specific MIME-types to exclude and not display. Stored as a
+ * {@link String} array.
+ */
+ public static final String EXTRA_EXCLUDE_MIMES = "exclude_mimes";
+
+ /**
+ * Intents related to the Contacts app UI.
+ */
+ public static final class UI {
+ /**
+ * The action for the default contacts list tab.
+ */
+ public static final String LIST_DEFAULT =
+ "com.android.contacts.action.LIST_DEFAULT";
+
+ /**
+ * The action for the contacts list tab.
+ */
+ public static final String LIST_GROUP_ACTION =
+ "com.android.contacts.action.LIST_GROUP";
+
+ /**
+ * When in LIST_GROUP_ACTION mode, this is the group to display.
+ */
+ public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP";
+
+ /**
+ * The action for the all contacts list tab.
+ */
+ public static final String LIST_ALL_CONTACTS_ACTION =
+ "com.android.contacts.action.LIST_ALL_CONTACTS";
+
+ /**
+ * The action for the contacts with phone numbers list tab.
+ */
+ public static final String LIST_CONTACTS_WITH_PHONES_ACTION =
+ "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES";
+
+ /**
+ * The action for the starred contacts list tab.
+ */
+ public static final String LIST_STARRED_ACTION =
+ "com.android.contacts.action.LIST_STARRED";
+
+ /**
+ * The action for the frequent contacts list tab.
+ */
+ public static final String LIST_FREQUENT_ACTION =
+ "com.android.contacts.action.LIST_FREQUENT";
+
+ /**
+ * The action for the "strequent" contacts list tab. It first lists the starred
+ * contacts in alphabetical order and then the frequent contacts in descending
+ * order of the number of times they have been contacted.
+ */
+ public static final String LIST_STREQUENT_ACTION =
+ "com.android.contacts.action.LIST_STREQUENT";
+
+ /**
+ * A key for to be used as an intent extra to set the activity
+ * title to a custom String value.
+ */
+ public static final String TITLE_EXTRA_KEY =
+ "com.android.contacts.extra.TITLE_EXTRA";
+
+ /**
+ * Activity Action: Display a filtered list of contacts
+ * <p>
+ * Input: Extra field {@link #FILTER_TEXT_EXTRA_KEY} is the text to use for
+ * filtering
+ * <p>
+ * Output: Nothing.
+ */
+ public static final String FILTER_CONTACTS_ACTION =
+ "com.android.contacts.action.FILTER_CONTACTS";
+
+ /**
+ * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION}
+ * intents to supply the text on which to filter.
+ */
+ public static final String FILTER_TEXT_EXTRA_KEY =
+ "com.android.contacts.extra.FILTER_TEXT";
+ }
+
+ /**
+ * Convenience class that contains string constants used
+ * to create contact {@link android.content.Intent Intents}.
+ */
+ public static final class Insert {
+ /** The action code to use when adding a contact */
+ public static final String ACTION = Intent.ACTION_INSERT;
+
+ /**
+ * If present, forces a bypass of quick insert mode.
+ */
+ public static final String FULL_MODE = "full_mode";
+
+ /**
+ * The extra field for the contact name.
+ * <P>Type: String</P>
+ */
+ public static final String NAME = "name";
+
+ // TODO add structured name values here.
+
+ /**
+ * The extra field for the contact phonetic name.
+ * <P>Type: String</P>
+ */
+ public static final String PHONETIC_NAME = "phonetic_name";
+
+ /**
+ * The extra field for the contact company.
+ * <P>Type: String</P>
+ */
+ public static final String COMPANY = "company";
+
+ /**
+ * The extra field for the contact job title.
+ * <P>Type: String</P>
+ */
+ public static final String JOB_TITLE = "job_title";
+
+ /**
+ * The extra field for the contact notes.
+ * <P>Type: String</P>
+ */
+ public static final String NOTES = "notes";
+
+ /**
+ * The extra field for the contact phone number.
+ * <P>Type: String</P>
+ */
+ public static final String PHONE = "phone";
+
+ /**
+ * The extra field for the contact phone number type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+ * or a string specifying a custom label.</P>
+ */
+ public static final String PHONE_TYPE = "phone_type";
+
+ /**
+ * The extra field for the phone isprimary flag.
+ * <P>Type: boolean</P>
+ */
+ public static final String PHONE_ISPRIMARY = "phone_isprimary";
+
+ /**
+ * The extra field for an optional second contact phone number.
+ * <P>Type: String</P>
+ */
+ public static final String SECONDARY_PHONE = "secondary_phone";
+
+ /**
+ * The extra field for an optional second contact phone number type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+ * or a string specifying a custom label.</P>
+ */
+ public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type";
+
+ /**
+ * The extra field for an optional third contact phone number.
+ * <P>Type: String</P>
+ */
+ public static final String TERTIARY_PHONE = "tertiary_phone";
+
+ /**
+ * The extra field for an optional third contact phone number type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.PhonesColumns PhonesColumns},
+ * or a string specifying a custom label.</P>
+ */
+ public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type";
+
+ /**
+ * The extra field for the contact email address.
+ * <P>Type: String</P>
+ */
+ public static final String EMAIL = "email";
+
+ /**
+ * The extra field for the contact email type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+ * or a string specifying a custom label.</P>
+ */
+ public static final String EMAIL_TYPE = "email_type";
+
+ /**
+ * The extra field for the email isprimary flag.
+ * <P>Type: boolean</P>
+ */
+ public static final String EMAIL_ISPRIMARY = "email_isprimary";
+
+ /**
+ * The extra field for an optional second contact email address.
+ * <P>Type: String</P>
+ */
+ public static final String SECONDARY_EMAIL = "secondary_email";
+
+ /**
+ * The extra field for an optional second contact email type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+ * or a string specifying a custom label.</P>
+ */
+ public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type";
+
+ /**
+ * The extra field for an optional third contact email address.
+ * <P>Type: String</P>
+ */
+ public static final String TERTIARY_EMAIL = "tertiary_email";
+
+ /**
+ * The extra field for an optional third contact email type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+ * or a string specifying a custom label.</P>
+ */
+ public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type";
+
+ /**
+ * The extra field for the contact postal address.
+ * <P>Type: String</P>
+ */
+ public static final String POSTAL = "postal";
+
+ /**
+ * The extra field for the contact postal address type.
+ * <P>Type: Either an integer value from
+ * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns}
+ * or a string specifying a custom label.</P>
+ */
+ public static final String POSTAL_TYPE = "postal_type";
+
+ /**
+ * The extra field for the postal isprimary flag.
+ * <P>Type: boolean</P>
+ */
+ public static final String POSTAL_ISPRIMARY = "postal_isprimary";
+
+ /**
+ * The extra field for an IM handle.
+ * <P>Type: String</P>
+ */
+ public static final String IM_HANDLE = "im_handle";
+
+ /**
+ * The extra field for the IM protocol
+ * <P>Type: the result of {@link CommonDataKinds.Im#encodePredefinedImProtocol(int)}
+ * or {@link CommonDataKinds.Im#encodeCustomImProtocol(String)}.</P>
+ */
+ public static final String IM_PROTOCOL = "im_protocol";
+
+ /**
+ * The extra field for the IM isprimary flag.
+ * <P>Type: boolean</P>
+ */
+ public static final String IM_ISPRIMARY = "im_isprimary";
+ }
+ }
+
+}
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 4c58e0d..790fe5c 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -63,7 +63,7 @@ public final class Downloads implements BaseColumns {
* that had initiated a download when that download completes. The
* download's content: uri is specified in the intent's data.
*/
- public static final String DOWNLOAD_COMPLETED_ACTION =
+ public static final String ACTION_DOWNLOAD_COMPLETED =
"android.intent.action.DOWNLOAD_COMPLETED";
/**
@@ -76,7 +76,7 @@ public final class Downloads implements BaseColumns {
* Note: this is not currently sent for downloads that have completed
* successfully.
*/
- public static final String NOTIFICATION_CLICKED_ACTION =
+ public static final String ACTION_NOTIFICATION_CLICKED =
"android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED";
/**
@@ -84,14 +84,14 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
- public static final String URI = "uri";
+ public static final String COLUMN_URI = "uri";
/**
* The name of the column containing application-specific data.
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
- public static final String APP_DATA = "entity";
+ public static final String COLUMN_APP_DATA = "entity";
/**
* The name of the column containing the flags that indicates whether
@@ -104,7 +104,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: BOOLEAN</P>
* <P>Owner can Init</P>
*/
- public static final String NO_INTEGRITY = "no_integrity";
+ public static final String COLUMN_NO_INTEGRITY = "no_integrity";
/**
* The name of the column containing the filename that the initiating
@@ -113,7 +113,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
- public static final String FILENAME_HINT = "hint";
+ public static final String COLUMN_FILE_NAME_HINT = "hint";
/**
* The name of the column containing the filename where the downloaded data
@@ -128,7 +128,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
- public static final String MIMETYPE = "mimetype";
+ public static final String COLUMN_MIME_TYPE = "mimetype";
/**
* The name of the column containing the flag that controls the destination
@@ -136,7 +136,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
*/
- public static final String DESTINATION = "destination";
+ public static final String COLUMN_DESTINATION = "destination";
/**
* The name of the column containing the flags that controls whether the
@@ -145,7 +145,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Init/Read/Write</P>
*/
- public static final String VISIBILITY = "visibility";
+ public static final String COLUMN_VISIBILITY = "visibility";
/**
* The name of the column containing the current control state of the download.
@@ -154,7 +154,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
- public static final String CONTROL = "control";
+ public static final String COLUMN_CONTROL = "control";
/**
* The name of the column containing the current status of the download.
@@ -163,7 +163,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
- public static final String STATUS = "status";
+ public static final String COLUMN_STATUS = "status";
/**
* The name of the column containing the date at which some interesting
@@ -172,7 +172,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: BIGINT</P>
* <P>Owner can Read</P>
*/
- public static final String LAST_MODIFICATION = "lastmod";
+ public static final String COLUMN_LAST_MODIFICATION = "lastmod";
/**
* The name of the column containing the package name of the application
@@ -181,7 +181,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
- public static final String NOTIFICATION_PACKAGE = "notificationpackage";
+ public static final String COLUMN_NOTIFICATION_PACKAGE = "notificationpackage";
/**
* The name of the column containing the component name of the class that
@@ -191,7 +191,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read</P>
*/
- public static final String NOTIFICATION_CLASS = "notificationclass";
+ public static final String COLUMN_NOTIFICATION_CLASS = "notificationclass";
/**
* If extras are specified when requesting a download they will be provided in the intent that
@@ -199,7 +199,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
- public static final String NOTIFICATION_EXTRAS = "notificationextras";
+ public static final String COLUMN_NOTIFICATION_EXTRAS = "notificationextras";
/**
* The name of the column contain the values of the cookie to be used for
@@ -208,7 +208,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
- public static final String COOKIE_DATA = "cookiedata";
+ public static final String COLUMN_COOKIE_DATA = "cookiedata";
/**
* The name of the column containing the user agent that the initiating
@@ -216,7 +216,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
- public static final String USER_AGENT = "useragent";
+ public static final String COLUMN_USER_AGENT = "useragent";
/**
* The name of the column containing the referer (sic) that the initiating
@@ -224,7 +224,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init</P>
*/
- public static final String REFERER = "referer";
+ public static final String COLUMN_REFERER = "referer";
/**
* The name of the column containing the total size of the file being
@@ -232,7 +232,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
- public static final String TOTAL_BYTES = "total_bytes";
+ public static final String COLUMN_TOTAL_BYTES = "total_bytes";
/**
* The name of the column containing the size of the part of the file that
@@ -240,7 +240,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Read</P>
*/
- public static final String CURRENT_BYTES = "current_bytes";
+ public static final String COLUMN_CURRENT_BYTES = "current_bytes";
/**
* The name of the column where the initiating application can provide the
@@ -252,7 +252,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: INTEGER</P>
* <P>Owner can Init</P>
*/
- public static final String OTHER_UID = "otheruid";
+ public static final String COLUMN_OTHER_UID = "otheruid";
/**
* The name of the column where the initiating application can provided the
@@ -261,7 +261,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
- public static final String TITLE = "title";
+ public static final String COLUMN_TITLE = "title";
/**
* The name of the column where the initiating application can provide the
@@ -270,7 +270,7 @@ public final class Downloads implements BaseColumns {
* <P>Type: TEXT</P>
* <P>Owner can Init/Read/Write</P>
*/
- public static final String DESCRIPTION = "description";
+ public static final String COLUMN_DESCRIPTION = "description";
/*
* Lists the destinations that an application can specify for a download.
diff --git a/core/java/android/provider/DrmStore.java b/core/java/android/provider/DrmStore.java
index db71854..c438ac4 100644
--- a/core/java/android/provider/DrmStore.java
+++ b/core/java/android/provider/DrmStore.java
@@ -35,7 +35,7 @@ import java.io.OutputStream;
/**
* The DRM provider contains forward locked DRM content.
- *
+ *
* @hide
*/
public final class DrmStore
@@ -43,13 +43,13 @@ public final class DrmStore
private static final String TAG = "DrmStore";
public static final String AUTHORITY = "drm";
-
+
/**
* This is in the Manifest class of the drm provider, but that isn't visible
* in the framework.
*/
private static final String ACCESS_DRM_PERMISSION = "android.permission.ACCESS_DRM";
-
+
/**
* Fields for DRM database
*/
@@ -82,18 +82,18 @@ public final class DrmStore
}
public interface Images extends Columns {
-
+
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/images");
}
-
+
public interface Audio extends Columns {
-
+
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/audio");
}
/**
* Utility function for inserting a file into the DRM content provider.
- *
+ *
* @param cr The content resolver to use
* @param file The file to insert
* @param title The title for the content (or null)
@@ -101,12 +101,46 @@ public final class DrmStore
*/
public static final Intent addDrmFile(ContentResolver cr, File file, String title) {
FileInputStream fis = null;
- OutputStream os = null;
Intent result = null;
try {
fis = new FileInputStream(file);
- DrmRawContent content = new DrmRawContent(fis, (int) file.length(),
+ if (title == null) {
+ title = file.getName();
+ int lastDot = title.lastIndexOf('.');
+ if (lastDot > 0) {
+ title = title.substring(0, lastDot);
+ }
+ }
+ result = addDrmFile(cr, fis, title);
+ } catch (Exception e) {
+ Log.e(TAG, "pushing file failed", e);
+ } finally {
+ try {
+ if (fis != null)
+ fis.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException in DrmStore.addDrmFile()", e);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Utility function for inserting a file stream into the DRM content provider.
+ *
+ * @param cr The content resolver to use
+ * @param fileStream The FileInputStream to insert
+ * @param title The title for the content (or null)
+ * @return uri to the DRM record or null
+ */
+ public static final Intent addDrmFile(ContentResolver cr, FileInputStream fis, String title) {
+ OutputStream os = null;
+ Intent result = null;
+
+ try {
+ DrmRawContent content = new DrmRawContent(fis, (int) fis.available(),
DrmRawContent.DRM_MIMETYPE_MESSAGE_STRING);
String mimeType = content.getContentType();
@@ -126,14 +160,6 @@ public final class DrmStore
if (contentUri != null) {
ContentValues values = new ContentValues(3);
- // compute title from file name, if it is not specified
- if (title == null) {
- title = file.getName();
- int lastDot = title.lastIndexOf('.');
- if (lastDot > 0) {
- title = title.substring(0, lastDot);
- }
- }
values.put(DrmStore.Columns.TITLE, title);
values.put(DrmStore.Columns.SIZE, size);
values.put(DrmStore.Columns.MIME_TYPE, mimeType);
@@ -162,7 +188,7 @@ public final class DrmStore
if (os != null)
os.close();
} catch (IOException e) {
- Log.e(TAG, "IOException in DrmTest.onCreate()", e);
+ Log.e(TAG, "IOException in DrmStore.addDrmFile()", e);
}
}
@@ -172,7 +198,7 @@ public final class DrmStore
/**
* Utility function to enforce any permissions required to access DRM
* content.
- *
+ *
* @param context A context used for checking calling permission.
*/
public static void enforceAccessDrmPermission(Context context) {
@@ -181,5 +207,5 @@ public final class DrmStore
throw new SecurityException("Requires DRM permission");
}
}
-
+
}
diff --git a/core/java/android/provider/Gmail.java b/core/java/android/provider/Gmail.java
index c4b29ae..073ae6c 100644
--- a/core/java/android/provider/Gmail.java
+++ b/core/java/android/provider/Gmail.java
@@ -83,7 +83,7 @@ public final class Gmail {
public static final String LABEL_OUTBOX = "^^out";
public static final String AUTHORITY = "gmail-ls";
- private static final String TAG = "gmail-ls";
+ private static final String TAG = "Gmail";
private static final String AUTHORITY_PLUS_CONVERSATIONS =
"content://" + AUTHORITY + "/conversations/";
private static final String AUTHORITY_PLUS_LABELS =
@@ -1521,8 +1521,9 @@ public final class Gmail {
/**
* Returns the number of conversation with a given label.
- * @deprecated
+ * @deprecated Use {@link #getLabelId} instead.
*/
+ @Deprecated
public int getNumConversations(String label) {
return getNumConversations(getLabelId(label));
}
@@ -1534,8 +1535,9 @@ public final class Gmail {
/**
* Returns the number of unread conversation with a given label.
- * @deprecated
+ * @deprecated Use {@link #getLabelId} instead.
*/
+ @Deprecated
public int getNumUnreadConversations(String label) {
return getNumUnreadConversations(getLabelId(label));
}
@@ -1546,11 +1548,12 @@ public final class Gmail {
getLabelIdValues(labelId).getAsInteger(LabelColumns.NUM_UNREAD_CONVERSATIONS);
// There seems to be a race condition here that can get the label maps into a bad
// state and lose state on a particular label.
- if (unreadConversations == null) {
- return 0;
- } else {
- return unreadConversations;
+ int result = 0;
+ if (unreadConversations != null) {
+ result = unreadConversations < 0 ? 0 : unreadConversations;
}
+
+ return result;
}
/**
@@ -2040,8 +2043,9 @@ public final class Gmail {
}
/**
- * @deprecated
+ * @deprecated Always returns true.
*/
+ @Deprecated
public boolean getExpanded() {
return true;
}
diff --git a/core/java/android/provider/Im.java b/core/java/android/provider/Im.java
index 19ad158..f126c4d 100644
--- a/core/java/android/provider/Im.java
+++ b/core/java/android/provider/Im.java
@@ -138,6 +138,7 @@ public class Im {
public static final String ACTIVE_ACCOUNT_USERNAME = "account_username";
public static final String ACTIVE_ACCOUNT_PW = "account_pw";
public static final String ACTIVE_ACCOUNT_LOCKED = "account_locked";
+ public static final String ACTIVE_ACCOUNT_KEEP_SIGNED_IN = "account_keepSignedIn";
public static final String ACCOUNT_PRESENCE_STATUS = "account_presenceStatus";
public static final String ACCOUNT_CONNECTION_STATUS = "account_connStatus";
@@ -871,14 +872,22 @@ public class Im {
}
/**
- * The common columns for both one-to-one chat messages or group chat messages.
+ * The common columns for messages table
*/
- public interface BaseMessageColumns {
+ public interface MessageColumns {
/**
- * The user this message belongs to
- * <P>Type: TEXT</P>
+ * The thread_id column stores the contact id of the contact the message belongs to.
+ * For groupchat messages, the thread_id stores the group id, which is the contact id
+ * of the temporary group contact created for the groupchat. So there should be no
+ * collision between groupchat message thread id and regular message thread id.
*/
- String CONTACT = "contact";
+ String THREAD_ID = "thread_id";
+
+ /**
+ * The nickname. This is used for groupchat messages to indicate the participant's
+ * nickname. For non groupchat messages, this field should be left empty.
+ */
+ String NICKNAME = "nickname";
/**
* The body
@@ -917,68 +926,199 @@ public class Im {
* <P>Type: STRING</P>
*/
String PACKET_ID = "packet_id";
- }
- /**
- * Columns from the Messages table.
- */
- public interface MessagesColumns extends BaseMessageColumns{
/**
- * The provider id
- * <P> Type: INTEGER </P>
+ * Is groupchat message or not
+ * <P>Type: INTEGER</P>
*/
- String PROVIDER = "provider";
+ String IS_GROUP_CHAT = "is_muc";
/**
- * The account id
- * <P> Type: INTEGER </P>
+ * A hint that the UI should show the sent time of this message
+ * <P>Type: INTEGER</P>
*/
- String ACCOUNT = "account";
+ String DISPLAY_SENT_TIME = "show_ts";
}
/**
* This table contains messages.
*/
- public static final class Messages implements BaseColumns, MessagesColumns {
+ public static final class Messages implements BaseColumns, MessageColumns {
/**
* no public constructor since this is a utility class
*/
private Messages() {}
/**
- * Gets the Uri to query messages by contact.
+ * Gets the Uri to query messages by thread id.
+ *
+ * @param threadId the thread id of the message.
+ * @return the Uri
+ */
+ public static final Uri getContentUriByThreadId(long threadId) {
+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_THREAD_ID.buildUpon();
+ ContentUris.appendId(builder, threadId);
+ return builder.build();
+ }
+
+ /**
+ * @deprecated
+ *
+ * Gets the Uri to query messages by account and contact.
*
- * @param providerId the provider id of the contact.
* @param accountId the account id of the contact.
* @param username the user name of the contact.
* @return the Uri
*/
- public static final Uri getContentUriByContact(long providerId,
- long accountId, String username) {
- Uri.Builder builder = CONTENT_URI_MESSAGES_BY.buildUpon();
+ public static final Uri getContentUriByContact(long accountId, String username) {
+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT.buildUpon();
+ ContentUris.appendId(builder, accountId);
+ builder.appendPath(username);
+ return builder.build();
+ }
+
+ /**
+ * Gets the Uri to query messages by provider.
+ *
+ * @param providerId the service provider id.
+ * @return the Uri
+ */
+ public static final Uri getContentUriByProvider(long providerId) {
+ Uri.Builder builder = CONTENT_URI_MESSAGES_BY_PROVIDER.buildUpon();
ContentUris.appendId(builder, providerId);
+ return builder.build();
+ }
+
+ /**
+ * Gets the Uri to query off the record messages by account.
+ *
+ * @param accountId the account id.
+ * @return the Uri
+ */
+ public static final Uri getContentUriByAccount(long accountId) {
+ Uri.Builder builder = CONTENT_URI_BY_ACCOUNT.buildUpon();
+ ContentUris.appendId(builder, accountId);
+ return builder.build();
+ }
+
+ /**
+ * Gets the Uri to query off the record messages by thread id.
+ *
+ * @param threadId the thread id of the message.
+ * @return the Uri
+ */
+ public static final Uri getOtrMessagesContentUriByThreadId(long threadId) {
+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID.buildUpon();
+ ContentUris.appendId(builder, threadId);
+ return builder.build();
+ }
+
+ /**
+ * @deprecated
+ *
+ * Gets the Uri to query off the record messages by account and contact.
+ *
+ * @param accountId the account id of the contact.
+ * @param username the user name of the contact.
+ * @return the Uri
+ */
+ public static final Uri getOtrMessagesContentUriByContact(long accountId, String username) {
+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT.buildUpon();
ContentUris.appendId(builder, accountId);
builder.appendPath(username);
return builder.build();
}
/**
+ * Gets the Uri to query off the record messages by provider.
+ *
+ * @param providerId the service provider id.
+ * @return the Uri
+ */
+ public static final Uri getOtrMessagesContentUriByProvider(long providerId) {
+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_PROVIDER.buildUpon();
+ ContentUris.appendId(builder, providerId);
+ return builder.build();
+ }
+
+ /**
+ * Gets the Uri to query off the record messages by account.
+ *
+ * @param accountId the account id.
+ * @return the Uri
+ */
+ public static final Uri getOtrMessagesContentUriByAccount(long accountId) {
+ Uri.Builder builder = OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT.buildUpon();
+ ContentUris.appendId(builder, accountId);
+ return builder.build();
+ }
+
+ /**
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI =
- Uri.parse("content://im/messages");
+ Uri.parse("content://im/messages");
+
+ /**
+ * The content:// style URL for messages by thread id
+ */
+ public static final Uri CONTENT_URI_MESSAGES_BY_THREAD_ID =
+ Uri.parse("content://im/messagesByThreadId");
+
+ /**
+ * The content:// style URL for messages by account and contact
+ */
+ public static final Uri CONTENT_URI_MESSAGES_BY_ACCOUNT_AND_CONTACT =
+ Uri.parse("content://im/messagesByAcctAndContact");
+
+ /**
+ * The content:// style URL for messages by provider
+ */
+ public static final Uri CONTENT_URI_MESSAGES_BY_PROVIDER =
+ Uri.parse("content://im/messagesByProvider");
+
+ /**
+ * The content:// style URL for messages by account
+ */
+ public static final Uri CONTENT_URI_BY_ACCOUNT =
+ Uri.parse("content://im/messagesByAccount");
+
+ /**
+ * The content:// style url for off the record messages
+ */
+ public static final Uri OTR_MESSAGES_CONTENT_URI =
+ Uri.parse("content://im/otrMessages");
+
+ /**
+ * The content:// style url for off the record messages by thread id
+ */
+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_THREAD_ID =
+ Uri.parse("content://im/otrMessagesByThreadId");
+
+ /**
+ * The content:// style url for off the record messages by account and contact
+ */
+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT_AND_CONTACT =
+ Uri.parse("content://im/otrMessagesByAcctAndContact");
/**
- * The content:// style URL for messages by provider and account
+ * The content:// style URL for off the record messages by provider
*/
- public static final Uri CONTENT_URI_MESSAGES_BY =
- Uri.parse("content://im/messagesBy");
+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_PROVIDER =
+ Uri.parse("content://im/otrMessagesByProvider");
+
+ /**
+ * The content:// style URL for off the record messages by account
+ */
+ public static final Uri OTR_MESSAGES_CONTENT_URI_BY_ACCOUNT =
+ Uri.parse("content://im/otrMessagesByAccount");
/**
* The MIME type of {@link #CONTENT_URI} providing a directory of
* people.
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-messages";
+ public static final String CONTENT_TYPE =
+ "vnd.android.cursor.dir/im-messages";
/**
* The MIME type of a {@link #CONTENT_URI} subdirectory of a single
@@ -992,6 +1132,11 @@ public class Im {
*/
public static final String DEFAULT_SORT_ORDER = "date ASC";
+ /**
+ * The "contact" column. This is not a real column in the messages table, but a
+ * temoprary column created when querying for messages (joined with the contacts table)
+ */
+ public static final String CONTACT = "contact";
}
/**
@@ -1119,67 +1264,6 @@ public class Im {
}
/**
- * Columns from the GroupMessages table
- */
- public interface GroupMessageColumns extends BaseMessageColumns {
- /**
- * The group this message belongs to
- * <p>Type: TEXT</p>
- */
- String GROUP = "groupId";
- }
-
- /**
- * This table contains group messages.
- */
- public final static class GroupMessages implements BaseColumns,
- GroupMessageColumns {
- private GroupMessages() {}
-
- /**
- * Gets the Uri to query group messages by group.
- *
- * @param groupId the group id.
- * @return the Uri
- */
- public static final Uri getContentUriByGroup(long groupId) {
- Uri.Builder builder = CONTENT_URI_GROUP_MESSAGES_BY.buildUpon();
- ContentUris.appendId(builder, groupId);
- return builder.build();
- }
-
- /**
- * The content:// style URL for this table
- */
- public static final Uri CONTENT_URI =
- Uri.parse("content://im/groupMessages");
-
- /**
- * The content:// style URL for group messages by provider and account
- */
- public static final Uri CONTENT_URI_GROUP_MESSAGES_BY =
- Uri.parse("content://im/groupMessagesBy");
-
- /**
- * The MIME type of {@link #CONTENT_URI} providing a directory of
- * group messages.
- */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/im-groupMessages";
-
- /**
- * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
- * group message.
- */
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/im-groupMessages";
-
- /**
- * The default sort order for this table
- */
- public static final String DEFAULT_SORT_ORDER = "date ASC";
- }
-
- /**
* Columns from the Avatars table
*/
public interface AvatarsColumns {
@@ -1384,7 +1468,7 @@ public class Im {
* <P>Type: TEXT</P>
*/
String UNSENT_COMPOSED_MESSAGE = "unsent_composed_message";
-
+
/**
* A value from 0-9 indicating which quick-switch chat screen slot this
* chat is occupying. If none (for instance, this is the 12th active chat)
@@ -1534,6 +1618,18 @@ public class Im {
/** specifies whether or not to show mobile indicator to friends */
public static final String SETTING_SHOW_MOBILE_INDICATOR = "mobile_indicator";
+ /** specifies whether or not to show as away when device is idle */
+ public static final String SETTING_SHOW_AWAY_ON_IDLE = "show_away_on_idle";
+
+ /** specifies whether or not to upload heartbeat stat upon login */
+ public static final String SETTING_UPLOAD_HEARTBEAT_STAT = "upload_heartbeat_stat";
+
+ /** specifies the last heartbeat interval received from the server */
+ public static final String SETTING_HEARTBEAT_INTERVAL = "heartbeat_interval";
+
+ /** specifiy the JID resource used for Google Talk connection */
+ public static final String SETTING_JID_RESOURCE = "jid_resource";
+
/**
* Used for reliable message queue (RMQ). This is for storing the last rmq id received
* from the GTalk server
@@ -1742,6 +1838,47 @@ public class Im {
showMobileIndicator);
}
+ /**
+ * A convenience method to set whether or not to show as away when device is idle.
+ *
+ * @param contentResolver The ContentResolver to use to access the setting table.
+ * @param showAway Whether or not to show as away when device is idle.
+ */
+ public static void setShowAwayOnIdle(ContentResolver contentResolver,
+ long providerId, boolean showAway) {
+ putBooleanValue(contentResolver, providerId, SETTING_SHOW_AWAY_ON_IDLE, showAway);
+ }
+
+ /**
+ * A convenience method to set whether or not to upload heartbeat stat.
+ *
+ * @param contentResolver The ContentResolver to use to access the setting table.
+ * @param uploadStat Whether or not to upload heartbeat stat.
+ */
+ public static void setUploadHeartbeatStat(ContentResolver contentResolver,
+ long providerId, boolean uploadStat) {
+ putBooleanValue(contentResolver, providerId, SETTING_UPLOAD_HEARTBEAT_STAT, uploadStat);
+ }
+
+ /**
+ * A convenience method to set the heartbeat interval last received from the server.
+ *
+ * @param contentResolver The ContentResolver to use to access the setting table.
+ * @param interval The heartbeat interval last received from the server.
+ */
+ public static void setHeartbeatInterval(ContentResolver contentResolver,
+ long providerId, long interval) {
+ putLongValue(contentResolver, providerId, SETTING_HEARTBEAT_INTERVAL, interval);
+ }
+
+ /**
+ * A convenience method to set the jid resource.
+ */
+ public static void setJidResource(ContentResolver contentResolver,
+ long providerId, String jidResource) {
+ putStringValue(contentResolver, providerId, SETTING_JID_RESOURCE, jidResource);
+ }
+
public static class QueryMap extends ContentQueryMap {
private ContentResolver mContentResolver;
private long mProviderId;
@@ -1872,6 +2009,79 @@ public class Im {
}
/**
+ * Set whether or not to show as away when device is idle.
+ *
+ * @param showAway whether or not to show as away when device is idle.
+ */
+ public void setShowAwayOnIdle(boolean showAway) {
+ ProviderSettings.setShowAwayOnIdle(mContentResolver, mProviderId, showAway);
+ }
+
+ /**
+ * Get whether or not to show as away when device is idle.
+ *
+ * @return Whether or not to show as away when device is idle.
+ */
+ public boolean getShowAwayOnIdle() {
+ return getBoolean(SETTING_SHOW_AWAY_ON_IDLE,
+ true /* by default show as away on idle*/);
+ }
+
+ /**
+ * Set whether or not to upload heartbeat stat.
+ *
+ * @param uploadStat whether or not to upload heartbeat stat.
+ */
+ public void setUploadHeartbeatStat(boolean uploadStat) {
+ ProviderSettings.setUploadHeartbeatStat(mContentResolver, mProviderId, uploadStat);
+ }
+
+ /**
+ * Get whether or not to upload heartbeat stat.
+ *
+ * @return Whether or not to upload heartbeat stat.
+ */
+ public boolean getUploadHeartbeatStat() {
+ return getBoolean(SETTING_UPLOAD_HEARTBEAT_STAT,
+ false /* by default do not upload */);
+ }
+
+ /**
+ * Set the last received heartbeat interval from the server.
+ *
+ * @param interval the last received heartbeat interval from the server.
+ */
+ public void setHeartbeatInterval(long interval) {
+ ProviderSettings.setHeartbeatInterval(mContentResolver, mProviderId, interval);
+ }
+
+ /**
+ * Get the last received heartbeat interval from the server.
+ *
+ * @return the last received heartbeat interval from the server.
+ */
+ public long getHeartbeatInterval() {
+ return getLong(SETTING_HEARTBEAT_INTERVAL, 0L /* an invalid default interval */);
+ }
+
+ /**
+ * Set the JID resource.
+ *
+ * @param jidResource the jid resource to be stored.
+ */
+ public void setJidResource(String jidResource) {
+ ProviderSettings.setJidResource(mContentResolver, mProviderId, jidResource);
+ }
+ /**
+ * Get the JID resource used for the Google Talk connection
+ *
+ * @return the JID resource stored.
+ */
+ public String getJidResource() {
+ return getString(SETTING_JID_RESOURCE, null);
+ }
+
+ /**
* Convenience function for retrieving a single settings value
* as a boolean.
*
@@ -1909,21 +2119,77 @@ public class Im {
ContentValues values = getValues(name);
return values != null ? values.getAsInteger(VALUE) : def;
}
+
+ /**
+ * Convenience function for retrieving a single settings value
+ * as a Long.
+ *
+ * @param name The name of the setting to retrieve.
+ * @param def The value to return if the setting is not defined.
+ * @return The setting's current value or 'def' if it is not defined.
+ */
+ private long getLong(String name, long def) {
+ ContentValues values = getValues(name);
+ return values != null ? values.getAsLong(VALUE) : def;
+ }
}
}
+
+ /**
+ * Columns for IM branding resource map cache table. This table caches the result of
+ * loading the branding resources to speed up IM landing page start.
+ */
+ public interface BrandingResourceMapCacheColumns {
+ /**
+ * The provider ID
+ * <P>Type: INTEGER</P>
+ */
+ String PROVIDER_ID = "provider_id";
+ /**
+ * The application resource ID
+ * <P>Type: INTEGER</P>
+ */
+ String APP_RES_ID = "app_res_id";
+ /**
+ * The plugin resource ID
+ * <P>Type: INTEGER</P>
+ */
+ String PLUGIN_RES_ID = "plugin_res_id";
+ }
+
+ /**
+ * The table for caching the result of loading IM branding resources.
+ */
+ public static final class BrandingResourceMapCache
+ implements BaseColumns, BrandingResourceMapCacheColumns {
+ /**
+ * The content:// style URL for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://im/brandingResMapCache");
+ }
+
+
+
+ /**
+ * //TODO: move these to MCS specific provider.
+ * The following are MCS stuff, and should really live in a separate provider specific to
+ * MCS code.
+ */
+
/**
* Columns from OutgoingRmq table
*/
public interface OutgoingRmqColumns {
String RMQ_ID = "rmq_id";
- String TYPE = "type";
String TIMESTAMP = "ts";
String DATA = "data";
+ String PROTOBUF_TAG = "type";
}
/**
+ * //TODO: we should really move these to their own provider and database.
* The table for storing outgoing rmq packets.
*/
public static final class OutgoingRmq implements BaseColumns, OutgoingRmqColumns {
@@ -1986,6 +2252,7 @@ public class Im {
}
/**
+ * //TODO: move these out into their own provider and database
* The table for storing the last client rmq id sent to the server.
*/
public static final class LastRmqId implements BaseColumns, LastRmqIdColumns {
@@ -2046,35 +2313,21 @@ public class Im {
}
/**
- * Columns for IM branding resource map cache table. This table caches the result of
- * loading the branding resources to speed up IM landing page start.
+ * Columns for the s2dRmqIds table, which stores the server-to-device message
+ * persistent ids. These are used in the RMQ2 protocol, where in the login request, the
+ * client selective acks these s2d ids to the server.
*/
- public interface BrandingResourceMapCacheColumns {
- /**
- * The provider ID
- * <P>Type: INTEGER</P>
- */
- String PROVIDER_ID = "provider_id";
- /**
- * The application resource ID
- * <P>Type: INTEGER</P>
- */
- String APP_RES_ID = "app_res_id";
- /**
- * The plugin resource ID
- * <P>Type: INTEGER</P>
- */
- String PLUGIN_RES_ID = "plugin_res_id";
+ public interface ServerToDeviceRmqIdsColumn {
+ String RMQ_ID = "rmq_id";
}
- /**
- * The table for caching the result of loading IM branding resources.
- */
- public static final class BrandingResourceMapCache
- implements BaseColumns, BrandingResourceMapCacheColumns {
+ public static final class ServerToDeviceRmqIds implements BaseColumns,
+ ServerToDeviceRmqIdsColumn {
+
/**
* The content:// style URL for this table.
*/
- public static final Uri CONTENT_URI = Uri.parse("content://im/brandingResMapCache");
+ public static final Uri CONTENT_URI = Uri.parse("content://im/s2dids");
}
+
}
diff --git a/core/java/android/provider/LiveFolders.java b/core/java/android/provider/LiveFolders.java
index 6e95fb7..19f361b 100644
--- a/core/java/android/provider/LiveFolders.java
+++ b/core/java/android/provider/LiveFolders.java
@@ -45,7 +45,7 @@ import android.annotation.SdkConstant;
* public static class MyLiveFolder extends Activity {
* public static final Uri CONTENT_URI = Uri.parse("content://my.app/live");
*
- * @Override
+ * &amp;#064;Override
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
*
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 21e5865..49b5bb1 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -164,6 +164,12 @@ public final class MediaStore
public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
/**
+ * Specify the maximum allowed recording duration in seconds.
+ * @hide
+ */
+ public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
+
+ /**
* The name of the Intent-extra used to indicate a content resolver Uri to be used to
* store the requested image or video.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4a4d2de..97955ae 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -119,6 +119,20 @@ public final class Settings {
"android.settings.AIRPLANE_MODE_SETTINGS";
/**
+ * Activity Action: Show settings for accessibility modules.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ACCESSIBILITY_SETTINGS =
+ "android.settings.ACCESSIBILITY_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of security and
* location privacy.
* <p>
@@ -413,8 +427,6 @@ public final class Settings {
private static final String TAG = "Settings";
- private static String sJidResource = null;
-
public static class SettingNotFoundException extends AndroidException {
public SettingNotFoundException(String msg) {
super(msg);
@@ -879,6 +891,17 @@ public final class Settings {
public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios";
/**
+ * A comma separated list of radios that should to be disabled when airplane mode
+ * is on, but can be manually reenabled by the user. For example, if RADIO_WIFI is
+ * added to both AIRPLANE_MODE_RADIOS and AIRPLANE_MODE_TOGGLEABLE_RADIOS, then Wifi
+ * will be turned off when entering airplane mode, but the user will be able to reenable
+ * Wifi in the Settings app.
+ *
+ * {@hide}
+ */
+ public static final String AIRPLANE_MODE_TOGGLEABLE_RADIOS = "airplane_mode_toggleable_radios";
+
+ /**
* The policy for deciding when Wi-Fi should go to sleep (which will in
* turn switch to using the mobile data as an Internet connection).
* <p>
@@ -1182,6 +1205,22 @@ public final class Settings {
public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);
/**
+ * Persistent store for the system-wide default alarm alert.
+ *
+ * @see #RINGTONE
+ * @see #DEFAULT_ALARM_ALERT_URI
+ */
+ public static final String ALARM_ALERT = "alarm_alert";
+
+ /**
+ * A {@link Uri} that will point to the current default alarm alert at
+ * any given time.
+ *
+ * @see #DEFAULT_ALARM_ALERT_URI
+ */
+ public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
+
+ /**
* Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
*/
public static final String TEXT_AUTO_REPLACE = "auto_replace";
@@ -1497,18 +1536,17 @@ public final class Settings {
@Deprecated
public static final String USE_GOOGLE_MAIL = Secure.USE_GOOGLE_MAIL;
-// /**
-// * @deprecated Use {@link android.provider.Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT}
-// * instead
-// */
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_MAX_DHCP_RETRY_COUNT} instead
+ */
@Deprecated
public static final String WIFI_MAX_DHCP_RETRY_COUNT = Secure.WIFI_MAX_DHCP_RETRY_COUNT;
-// /**
-// * @deprecated Use
-// * {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS}
-// * instead
-// */
+ /**
+ * @deprecated Use
+ * {@link android.provider.Settings.Secure#WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS} instead
+ */
@Deprecated
public static final String WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS =
Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS;
@@ -1968,6 +2006,12 @@ public final class Settings {
public static final String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
/**
+ * Whether assisted GPS should be enabled or not.
+ * @hide
+ */
+ public static final String ASSISTED_GPS_ENABLED = "assisted_gps_enabled";
+
+ /**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
* @deprecated This identifier is poorly initialized and has
@@ -2201,6 +2245,32 @@ public final class Settings {
public static final String BACKGROUND_DATA = "background_data";
/**
+ * The time in msec, when the LAST_KMSG file was send to the checkin server.
+ * We will only send the LAST_KMSG file if it was modified after this time.
+ *
+ * @hide
+ */
+ public static final String CHECKIN_SEND_LAST_KMSG_TIME = "checkin_kmsg_time";
+
+ /**
+ * The time in msec, when the apanic_console file was send to the checkin server.
+ * We will only send the apanic_console file if it was modified after this time.
+ *
+ * @hide
+ */
+ public static final String CHECKIN_SEND_APANIC_CONSOLE_TIME =
+ "checkin_apanic_console_time";
+
+ /**
+ * The time in msec, when the apanic_thread file was send to the checkin server.
+ * We will only send the apanic_thread file if it was modified after this time.
+ *
+ * @hide
+ */
+ public static final String CHECKIN_SEND_APANIC_THREAD_TIME =
+ "checkin_apanic_thread_time";
+
+ /**
* The CDMA roaming mode 0 = Home Networks, CDMA default
* 1 = Roaming on Affiliated networks
* 2 = Roaming on any networks
@@ -2695,7 +2765,14 @@ public final class Settings {
* Controls how many attempts Gmail will try to upload an uphill operations before it
* abandons the operation. Defaults to 20.
*/
- public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_discard_error_uphill_op";
+ public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_num_retry_uphill_op";
+
+ /**
+ * How much time in seconds Gmail will try to upload an uphill operations before it
+ * abandons the operation. Defaults to 36400 (one day).
+ */
+ public static final String GMAIL_WAIT_TIME_RETRY_UPHILL_OP =
+ "gmail_wait_time_retry_uphill_op";
/**
* Controls if the protocol buffer version of the protocol will use a multipart request for
@@ -2804,6 +2881,12 @@ public final class Settings {
"gtalk_nosync_heartbeat_ping_interval_ms";
/**
+ * The maximum heartbeat interval used while on the WIFI network.
+ */
+ public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS =
+ "gtalk_wifi_max_heartbeat_ping_interval_ms";
+
+ /**
* How long we wait to receive a heartbeat ping acknowledgement (or another packet)
* from the GTalk server, before deeming the connection dead.
*/
@@ -2856,6 +2939,113 @@ public final class Settings {
public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms";
/**
+ * This is the threshold of retry number when there is an authentication expired failure
+ * for Google Talk. In some situation, e.g. when a Google Apps account is disabled chat
+ * service, the connection keeps failing. This threshold controls when we should stop
+ * the retrying.
+ */
+ public static final String GTALK_MAX_RETRIES_FOR_AUTH_EXPIRED =
+ "gtalk_max_retries_for_auth_expired";
+
+ /**
+ * a boolean setting indicating whether the GTalkService should use RMQ2 protocol or not.
+ */
+ public static final String GTALK_USE_RMQ2_PROTOCOL =
+ "gtalk_use_rmq2";
+
+ /**
+ * a boolean setting indicating whether the GTalkService should support both RMQ and
+ * RMQ2 protocols. This setting is true for the transitional period when we need to
+ * support both protocols.
+ */
+ public static final String GTALK_SUPPORT_RMQ_AND_RMQ2_PROTOCOLS =
+ "gtalk_support_rmq_and_rmq2";
+
+ /**
+ * a boolean setting controlling whether the rmq2 protocol will include stream ids in
+ * the protobufs. This is used for debugging.
+ */
+ public static final String GTALK_RMQ2_INCLUDE_STREAM_ID =
+ "gtalk_rmq2_include_stream_id";
+
+ /**
+ * when receiving a chat message from the server, the message could be an older message
+ * whose "time sent" is x seconds from now. If x is significant enough, we want to flag
+ * it so the UI can give it some special treatment when displaying the "time sent" for
+ * it. This setting is to control what x is.
+ */
+ public static final String GTALK_OLD_CHAT_MESSAGE_THRESHOLD_IN_SEC =
+ "gtalk_old_chat_msg_threshold_in_sec";
+
+ /**
+ * a setting to control the max connection history record GTalkService stores.
+ */
+ public static final String GTALK_MAX_CONNECTION_HISTORY_RECORDS =
+ "gtalk_max_conn_history_records";
+
+ /**
+ * This is gdata url to lookup album and picture info from picasa web. It also controls
+ * whether url scraping for picasa is enabled (NULL to disable).
+ */
+ public static final String GTALK_PICASA_ALBUM_URL =
+ "gtalk_picasa_album_url";
+
+ /**
+ * This is the url to lookup picture info from flickr. It also controls
+ * whether url scraping for flickr is enabled (NULL to disable).
+ */
+ public static final String GTALK_FLICKR_PHOTO_INFO_URL =
+ "gtalk_flickr_photo_info_url";
+
+ /**
+ * This is the url to lookup an actual picture from flickr.
+ */
+ public static final String GTALK_FLICKR_PHOTO_URL =
+ "gtalk_flickr_photo_url";
+
+ /**
+ * This is the gdata url to lookup info on a youtube video. It also controls
+ * whether url scraping for youtube is enabled (NULL to disable).
+ */
+ public static final String GTALK_YOUTUBE_VIDEO_URL =
+ "gtalk_youtube_video_url";
+
+ /**
+ * Enable/disable GTalk URL scraping for JPG images ("true" to enable).
+ */
+ public static final String GTALK_URL_SCRAPING_FOR_JPG =
+ "gtalk_url_scraping_for_jpg";
+
+ /**
+ * Chat message lifetime (for pruning old chat messages).
+ */
+ public static final String GTALK_CHAT_MESSAGE_LIFETIME =
+ "gtalk_chat_message_lifetime";
+
+ /**
+ * OTR message lifetime (for pruning old otr messages).
+ */
+ public static final String GTALK_OTR_MESSAGE_LIFETIME =
+ "gtalk_otr_message_lifetime";
+
+ /**
+ * Chat expiration time, i.e., time since last message in the chat (for pruning old chats).
+ */
+ public static final String GTALK_CHAT_EXPIRATION_TIME =
+ "gtalk_chat_expiration_time";
+
+ /**
+ * This is the url for getting the app token for server-to-device push messaging.
+ */
+ public static final String PUSH_MESSAGING_REGISTRATION_URL =
+ "push_messaging_registration_url";
+
+ /**
+ * Use android://&lt;it&gt; routing infos for Google Sync Server subcriptions.
+ */
+ public static final String GSYNC_USE_RMQ2_ROUTING_INFO = "gsync_use_rmq2_routing_info";
+
+ /**
* Enable use of ssl session caching.
* 'db' - save each session in a (per process) database
* 'file' - save each session in a (per process) file
@@ -2907,6 +3097,12 @@ public final class Settings {
"vending_require_sim_for_purchase";
/**
+ * Indicates the Vending Machine backup state. It is set if the
+ * Vending application has been backed up at least once.
+ */
+ public static final String VENDING_BACKUP_STATE = "vending_backup_state";
+
+ /**
* The current version id of the Vending Machine terms of service.
*/
public static final String VENDING_TOS_VERSION = "vending_tos_version";
@@ -2992,6 +3188,13 @@ public final class Settings {
"vending_promo_refresh_freq_ms";
/**
+ * Frequency in milliseconds when we should refresh the provisioning information from
+ * the carrier backend.
+ */
+ public static final String VENDING_CARRIER_PROVISIONING_REFRESH_FREQUENCY_MS =
+ "vending_carrier_ref_freq_ms";
+
+ /**
* URL that points to the legal terms of service to display in Settings.
* <p>
* This should be a https URL. For a pretty user-friendly URL, use
@@ -3225,39 +3428,6 @@ public final class Settings {
"short_keylight_delay_ms";
/**
- * URL that points to the voice search servers. To be factored out of this class.
- */
- public static final String VOICE_SEARCH_URL = "voice_search_url";
-
- /**
- * Speech encoding used with voice search on 3G networks. To be factored out of this class.
- */
- public static final String VOICE_SEARCH_ENCODING_THREE_G = "voice_search_encoding_three_g";
-
- /**
- * Speech encoding used with voice search on WIFI networks. To be factored out of this class.
- */
- public static final String VOICE_SEARCH_ENCODING_WIFI = "voice_search_encoding_wifi";
-
- /**
- * Whether to use automatic gain control in voice search (0 = disable, 1 = enable).
- * To be factored out of this class.
- */
- public static final String VOICE_SEARCH_ENABLE_AGC = "voice_search_enable_agc";
-
- /**
- * Whether to use noise suppression in voice search (0 = disable, 1 = enable).
- * To be factored out of this class.
- */
- public static final String VOICE_SEARCH_ENABLE_NS = "voice_search_enable_ns";
-
- /**
- * Whether to use the IIR filter in voice search (0 = disable, 1 = enable).
- * To be factored out of this class.
- */
- public static final String VOICE_SEARCH_ENABLE_IIR = "voice_search_enable_iir";
-
- /**
* List of test suites (local disk filename) for the automatic instrumentation test runner.
* The file format is similar to automated_suites.xml, see AutoTesterService.
* If this setting is missing or empty, the automatic test runner will not start.
@@ -3300,6 +3470,108 @@ public final class Settings {
public static final String USE_LOCATION_FOR_SERVICES = "use_location";
/**
+ * The length of the calendar sync window into the future.
+ * This specifies the number of days into the future for the sliding window sync.
+ * Setting this to zero will disable sliding sync.
+ */
+ public static final String GOOGLE_CALENDAR_SYNC_WINDOW_DAYS =
+ "google_calendar_sync_window_days";
+
+ /**
+ * How often to update the calendar sync window.
+ * The window will be advanced every n days.
+ */
+ public static final String GOOGLE_CALENDAR_SYNC_WINDOW_UPDATE_DAYS =
+ "google_calendar_sync_window_update_days";
+
+ /**
+ * The number of promoted sources in GlobalSearch.
+ */
+ public static final String SEARCH_NUM_PROMOTED_SOURCES = "search_num_promoted_sources";
+ /**
+ * The maximum number of suggestions returned by GlobalSearch.
+ */
+ public static final String SEARCH_MAX_RESULTS_TO_DISPLAY = "search_max_results_to_display";
+ /**
+ * The number of suggestions GlobalSearch will ask each non-web search source for.
+ */
+ public static final String SEARCH_MAX_RESULTS_PER_SOURCE = "search_max_results_per_source";
+ /**
+ * The number of suggestions the GlobalSearch will ask the web search source for.
+ */
+ public static final String SEARCH_WEB_RESULTS_OVERRIDE_LIMIT =
+ "search_web_results_override_limit";
+ /**
+ * The number of milliseconds that GlobalSearch will wait for suggestions from
+ * promoted sources before continuing with all other sources.
+ */
+ public static final String SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS =
+ "search_promoted_source_deadline_millis";
+ /**
+ * The number of milliseconds before GlobalSearch aborts search suggesiton queries.
+ */
+ public static final String SEARCH_SOURCE_TIMEOUT_MILLIS = "search_source_timeout_millis";
+ /**
+ * The maximum number of milliseconds that GlobalSearch shows the previous results
+ * after receiving a new query.
+ */
+ public static final String SEARCH_PREFILL_MILLIS = "search_prefill_millis";
+ /**
+ * The maximum age of log data used for shortcuts in GlobalSearch.
+ */
+ public static final String SEARCH_MAX_STAT_AGE_MILLIS = "search_max_stat_age_millis";
+ /**
+ * The maximum age of log data used for source ranking in GlobalSearch.
+ */
+ public static final String SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS =
+ "search_max_source_event_age_millis";
+ /**
+ * The minimum number of impressions needed to rank a source in GlobalSearch.
+ */
+ public static final String SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING =
+ "search_min_impressions_for_source_ranking";
+ /**
+ * The minimum number of clicks needed to rank a source in GlobalSearch.
+ */
+ public static final String SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING =
+ "search_min_clicks_for_source_ranking";
+ /**
+ * The maximum number of shortcuts shown by GlobalSearch.
+ */
+ public static final String SEARCH_MAX_SHORTCUTS_RETURNED = "search_max_shortcuts_returned";
+ /**
+ * The size of the core thread pool for suggestion queries in GlobalSearch.
+ */
+ public static final String SEARCH_QUERY_THREAD_CORE_POOL_SIZE =
+ "search_query_thread_core_pool_size";
+ /**
+ * The maximum size of the thread pool for suggestion queries in GlobalSearch.
+ */
+ public static final String SEARCH_QUERY_THREAD_MAX_POOL_SIZE =
+ "search_query_thread_max_pool_size";
+ /**
+ * The size of the core thread pool for shortcut refreshing in GlobalSearch.
+ */
+ public static final String SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE =
+ "search_shortcut_refresh_core_pool_size";
+ /**
+ * The maximum size of the thread pool for shortcut refreshing in GlobalSearch.
+ */
+ public static final String SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE =
+ "search_shortcut_refresh_max_pool_size";
+ /**
+ * The maximun time that excess threads in the GlobalSeach thread pools will
+ * wait before terminating.
+ */
+ public static final String SEARCH_THREAD_KEEPALIVE_SECONDS =
+ "search_thread_keepalive_seconds";
+ /**
+ * The maximum number of concurrent suggestion queries to each source.
+ */
+ public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT =
+ "search_per_source_concurrent_query_limit";
+
+ /**
* @deprecated
* @hide
*/
@@ -3542,42 +3814,6 @@ public final class Settings {
}
/**
- * Returns the GTalk JID resource associated with this device.
- *
- * @return String the JID resource of the device. It uses the device IMEI in the computation
- * of the JID resource. If IMEI is not ready (i.e. telephony module not ready), we'll return
- * an empty string.
- * @hide
- */
- // TODO: we shouldn't not have a permenant Jid resource, as that's an easy target for
- // spams. We should change it once a while, like when we resubscribe to the subscription feeds
- // server.
- // (also, should this live in GTalkService?)
- public static synchronized String getJidResource() {
- if (sJidResource != null) {
- return sJidResource;
- }
-
- MessageDigest digest;
- try {
- digest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("this should never happen");
- }
-
- String deviceId = TelephonyManager.getDefault().getDeviceId();
- if (TextUtils.isEmpty(deviceId)) {
- return "";
- }
-
- byte[] hashedDeviceId = digest.digest(deviceId.getBytes());
- String id = new String(Base64.encodeBase64(hashedDeviceId), 0, 12);
- id = id.replaceAll("/", "_");
- sJidResource = JID_RESOURCE_PREFIX + id;
- return sJidResource;
- }
-
- /**
* Returns the device ID that we should use when connecting to the mobile gtalk server.
* This is a string like "android-0x1242", where the hex string is the Android ID obtained
* from the GoogleLoginService.
diff --git a/core/java/android/provider/SocialContract.java b/core/java/android/provider/SocialContract.java
new file mode 100644
index 0000000..ee271ba
--- /dev/null
+++ b/core/java/android/provider/SocialContract.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+
+/**
+ * The contract between the social provider and applications. Contains
+ * definitions for the supported URIs and columns.
+ *
+ * @hide
+ */
+public class SocialContract {
+ /** The authority for the social provider */
+ public static final String AUTHORITY = "com.android.social";
+
+ /** A content:// style uri to the authority for the contacts provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ private interface ActivitiesColumns {
+ /**
+ * The package name to use when creating {@link Resources} objects for
+ * this data row. This value is only designed for use when building user
+ * interfaces, and should not be used to infer the owner.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String RES_PACKAGE = "res_package";
+
+ /**
+ * The mime-type of this social activity.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String MIMETYPE = "mimetype";
+
+ /**
+ * Internal raw identifier for this social activity. This field is
+ * analogous to the <code>atom:id</code> element defined in RFC 4287.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String RAW_ID = "raw_id";
+
+ /**
+ * Reference to another {@link Activities#RAW_ID} that this social activity
+ * is replying to. This field is analogous to the
+ * <code>thr:in-reply-to</code> element defined in RFC 4685.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String IN_REPLY_TO = "in_reply_to";
+
+ /**
+ * Reference to the {@link android.provider.ContactsContract.Contacts#_ID} that authored
+ * this social activity. This field is analogous to the <code>atom:author</code>
+ * element defined in RFC 4287.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String AUTHOR_CONTACT_ID = "author_contact_id";
+
+ /**
+ * Optional reference to the {@link android.provider.ContactsContract.Contacts#_ID} this
+ * social activity is targeted towards. If more than one direct target, this field may
+ * be left undefined. This field is analogous to the
+ * <code>activity:target</code> element defined in the Atom Activity
+ * Extensions Internet-Draft.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String TARGET_CONTACT_ID = "target_contact_id";
+
+ /**
+ * Timestamp when this social activity was published, in a
+ * {@link System#currentTimeMillis()} time base. This field is analogous
+ * to the <code>atom:published</code> element defined in RFC 4287.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String PUBLISHED = "published";
+
+ /**
+ * Timestamp when the original social activity in a thread was
+ * published. For activities that have an in-reply-to field specified, the
+ * content provider will automatically populate this field with the
+ * timestamp of the original activity.
+ * <p>
+ * This field is useful for sorting order of activities that keeps together all
+ * messages in each thread.
+ * <p>
+ * Type: INTEGER
+ */
+ public static final String THREAD_PUBLISHED = "thread_published";
+
+ /**
+ * Title of this social activity. This field is analogous to the
+ * <code>atom:title</code> element defined in RFC 4287.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * Summary of this social activity. This field is analogous to the
+ * <code>atom:summary</code> element defined in RFC 4287.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String SUMMARY = "summary";
+
+ /**
+ * A URI associated this social activity. This field is analogous to the
+ * <code>atom:link rel="alternate"</code> element defined in RFC 4287.
+ * <p>
+ * Type: TEXT
+ */
+ public static final String LINK = "link";
+
+ /**
+ * Optional thumbnail specific to this social activity. This is the raw
+ * bytes of an image that could be inflated using {@link BitmapFactory}.
+ * <p>
+ * Type: BLOB
+ */
+ public static final String THUMBNAIL = "thumbnail";
+ }
+
+ public static final class Activities implements BaseColumns, ActivitiesColumns {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private Activities() {
+ }
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "activities");
+
+ /**
+ * The content:// URI for this table filtered to the set of social activities
+ * authored by a specific {@link android.provider.ContactsContract.Contacts#_ID}.
+ */
+ public static final Uri CONTENT_AUTHORED_BY_URI =
+ Uri.withAppendedPath(CONTENT_URI, "authored_by");
+
+ /**
+ * The {@link Uri} for the latest social activity performed by any
+ * raw contact aggregated under the specified {@link Contacts#_ID}. Will
+ * also join with most-present {@link Presence} for this aggregate.
+ */
+ public static final Uri CONTENT_CONTACT_STATUS_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, "contact_status");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of social
+ * activities.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/activity";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single
+ * social activity.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/activity";
+ }
+
+}
diff --git a/core/java/android/provider/SubscribedFeeds.java b/core/java/android/provider/SubscribedFeeds.java
index 4d430d5..8e9f402 100644
--- a/core/java/android/provider/SubscribedFeeds.java
+++ b/core/java/android/provider/SubscribedFeeds.java
@@ -20,6 +20,7 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
+import android.accounts.Account;
/**
* The SubscribedFeeds provider stores all information about subscribed feeds.
@@ -99,7 +100,7 @@ public class SubscribedFeeds {
/**
* The default sort order for this table
*/
- public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT ASC";
+ public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT_TYPE, _SYNC_ACCOUNT ASC";
}
/**
@@ -114,38 +115,36 @@ public class SubscribedFeeds {
* @return the Uri of the feed that was added
*/
public static Uri addFeed(ContentResolver resolver,
- String feed, String account,
+ String feed, Account account,
String authority, String service) {
ContentValues values = new ContentValues();
values.put(SubscribedFeeds.Feeds.FEED, feed);
- values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account);
+ values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT, account.name);
+ values.put(SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, account.type);
values.put(SubscribedFeeds.Feeds.AUTHORITY, authority);
values.put(SubscribedFeeds.Feeds.SERVICE, service);
return resolver.insert(SubscribedFeeds.Feeds.CONTENT_URI, values);
}
public static int deleteFeed(ContentResolver resolver,
- String feed, String account, String authority) {
+ String feed, Account account, String authority) {
StringBuilder where = new StringBuilder();
where.append(SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?");
+ where.append(" AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?");
where.append(" AND " + SubscribedFeeds.Feeds.FEED + "=?");
where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?");
return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI,
- where.toString(), new String[] {account, feed, authority});
+ where.toString(), new String[] {account.name, account.type, feed, authority});
}
public static int deleteFeeds(ContentResolver resolver,
- String account, String authority) {
+ Account account, String authority) {
StringBuilder where = new StringBuilder();
where.append(SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?");
+ where.append(" AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?");
where.append(" AND " + SubscribedFeeds.Feeds.AUTHORITY + "=?");
return resolver.delete(SubscribedFeeds.Feeds.CONTENT_URI,
- where.toString(), new String[] {account, authority});
- }
-
- public static String gtalkServiceRoutingInfoFromAccountAndResource(
- String account, String res) {
- return Uri.parse("gtalk://" + account + "/" + res).toString();
+ where.toString(), new String[] {account.name, account.type, authority});
}
/**
@@ -157,6 +156,12 @@ public class SubscribedFeeds {
* <P>Type: TEXT</P>
*/
public static final String _SYNC_ACCOUNT = SyncConstValue._SYNC_ACCOUNT;
+
+ /**
+ * The account type.
+ * <P>Type: TEXT</P>
+ */
+ public static final String _SYNC_ACCOUNT_TYPE = SyncConstValue._SYNC_ACCOUNT_TYPE;
}
/**
@@ -199,6 +204,6 @@ public class SubscribedFeeds {
/**
* The default sort order for this table
*/
- public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT ASC";
+ public static final String DEFAULT_SORT_ORDER = "_SYNC_ACCOUNT_TYPE, _SYNC_ACCOUNT ASC";
}
}
diff --git a/core/java/android/provider/SyncConstValue.java b/core/java/android/provider/SyncConstValue.java
index 6eb4398..30966eb 100644
--- a/core/java/android/provider/SyncConstValue.java
+++ b/core/java/android/provider/SyncConstValue.java
@@ -29,6 +29,12 @@ public interface SyncConstValue
public static final String _SYNC_ACCOUNT = "_sync_account";
/**
+ * The type of the account that was used to sync the entry to the device.
+ * <P>Type: TEXT</P>
+ */
+ public static final String _SYNC_ACCOUNT_TYPE = "_sync_account_type";
+
+ /**
* The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
* <P>Type: TEXT</P>
*/
@@ -68,4 +74,9 @@ public interface SyncConstValue
* Used to indicate that this account is not synced
*/
public static final String NON_SYNCABLE_ACCOUNT = "non_syncable";
+
+ /**
+ * Used to indicate that this account is not synced
+ */
+ public static final String NON_SYNCABLE_ACCOUNT_TYPE = "android.local";
}
diff --git a/core/java/android/provider/SyncStateContract.java b/core/java/android/provider/SyncStateContract.java
new file mode 100644
index 0000000..e8177ca
--- /dev/null
+++ b/core/java/android/provider/SyncStateContract.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.provider;
+
+import android.net.Uri;
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.content.ContentProviderOperation;
+import android.content.ContentUris;
+import android.accounts.Account;
+import android.database.Cursor;
+import android.os.RemoteException;
+import android.util.Pair;
+
+/**
+ * The ContentProvider contract for associating data with ana data array account.
+ * This may be used by providers that want to store this data in a standard way.
+ */
+public class SyncStateContract {
+ public interface Columns extends BaseColumns {
+ /**
+ * A reference to the name of the account to which this data belongs
+ * <P>Type: STRING</P>
+ */
+ public static final String ACCOUNT_NAME = "account_name";
+
+ /**
+ * A reference to the type of the account to which this data belongs
+ * <P>Type: STRING</P>
+ */
+ public static final String ACCOUNT_TYPE = "account_type";
+
+ /**
+ * The sync data associated with this account.
+ * <P>Type: NONE</P>
+ */
+ public static final String DATA = "data";
+ }
+
+ public static class Constants implements Columns {
+ public static final String CONTENT_DIRECTORY = "syncstate";
+ }
+
+ public static final class Helpers {
+ private static final String[] DATA_PROJECTION = new String[]{Columns.DATA, Columns._ID};
+ private static final String SELECT_BY_ACCOUNT =
+ Columns.ACCOUNT_NAME + "=? AND " + Columns.ACCOUNT_TYPE + "=?";
+
+ /**
+ * Get the sync state that is associated with the account or null.
+ * @param provider the {@link ContentProviderClient} that is to be used to communicate
+ * with the {@link android.content.ContentProvider} that contains the sync state.
+ * @param uri the uri of the sync state
+ * @param account the {@link Account} whose sync state should be returned
+ * @return the sync state or null if there is no sync state associated with the account
+ * @throws RemoteException if there is a failure communicating with the remote
+ * {@link android.content.ContentProvider}
+ */
+ public static byte[] get(ContentProviderClient provider, Uri uri,
+ Account account) throws RemoteException {
+ Cursor c = provider.query(uri, DATA_PROJECTION, SELECT_BY_ACCOUNT,
+ new String[]{account.name, account.type}, null);
+ try {
+ if (c.moveToNext()) {
+ return c.getBlob(c.getColumnIndexOrThrow(Columns.DATA));
+ }
+ } finally {
+ c.close();
+ }
+ return null;
+ }
+
+ /**
+ * Assigns the data array as the sync state for the given account.
+ * @param provider the {@link ContentProviderClient} that is to be used to communicate
+ * with the {@link android.content.ContentProvider} that contains the sync state.
+ * @param uri the uri of the sync state
+ * @param account the {@link Account} whose sync state should be set
+ * @param data the byte[] that contains the sync state
+ * @throws RemoteException if there is a failure communicating with the remote
+ * {@link android.content.ContentProvider}
+ */
+ public static void set(ContentProviderClient provider, Uri uri,
+ Account account, byte[] data) throws RemoteException {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ values.put(Columns.ACCOUNT_NAME, account.name);
+ values.put(Columns.ACCOUNT_TYPE, account.type);
+ provider.insert(uri, values);
+ }
+
+ public static Uri insert(ContentProviderClient provider, Uri uri,
+ Account account, byte[] data) throws RemoteException {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ values.put(Columns.ACCOUNT_NAME, account.name);
+ values.put(Columns.ACCOUNT_TYPE, account.type);
+ return provider.insert(uri, values);
+ }
+
+ public static void update(ContentProviderClient provider, Uri uri, byte[] data)
+ throws RemoteException {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ provider.update(uri, values, null, null);
+ }
+
+ public static Pair<Uri, byte[]> getWithUri(ContentProviderClient provider, Uri uri,
+ Account account) throws RemoteException {
+ Cursor c = provider.query(uri, DATA_PROJECTION, SELECT_BY_ACCOUNT,
+ new String[]{account.name, account.type}, null);
+ try {
+ if (c.moveToNext()) {
+ long rowId = c.getLong(1);
+ byte[] blob = c.getBlob(c.getColumnIndexOrThrow(Columns.DATA));
+ return Pair.create(ContentUris.withAppendedId(uri, rowId), blob);
+ }
+ } finally {
+ c.close();
+ }
+ return null;
+ }
+
+ /**
+ * Creates and returns a ContentProviderOperation that assigns the data array as the
+ * sync state for the given account.
+ * @param uri the uri of the sync state
+ * @param account the {@link Account} whose sync state should be set
+ * @param data the byte[] that contains the sync state
+ * @return the new ContentProviderOperation that assigns the data array as the
+ * account's sync state
+ */
+ public static ContentProviderOperation newSetOperation(Uri uri,
+ Account account, byte[] data) {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ return ContentProviderOperation
+ .newInsert(uri)
+ .withValue(Columns.ACCOUNT_NAME, account.name)
+ .withValue(Columns.ACCOUNT_TYPE, account.type)
+ .withValues(values)
+ .build();
+ }
+
+ /**
+ * Creates and returns a ContentProviderOperation that assigns the data array as the
+ * sync state for the given account.
+ * @param uri the uri of the specific sync state to set
+ * @param data the byte[] that contains the sync state
+ * @return the new ContentProviderOperation that assigns the data array as the
+ * account's sync state
+ */
+ public static ContentProviderOperation newUpdateOperation(Uri uri, byte[] data) {
+ ContentValues values = new ContentValues();
+ values.put(Columns.DATA, data);
+ return ContentProviderOperation
+ .newUpdate(uri)
+ .withValues(values)
+ .build();
+ }
+ }
+}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index c292c53..c637e02 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -146,7 +146,13 @@ public final class Telephony {
* <P>Type: TEXT</P>
*/
public static final String SERVICE_CENTER = "service_center";
- }
+
+ /**
+ * Has the message been locked?
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String LOCKED = "locked";
+}
/**
* Contains all text based SMS messages.
@@ -484,6 +490,13 @@ public final class Telephony {
public static final int RESULT_SMS_OUT_OF_MEMORY = 3;
/**
+ * Set by BroadcastReceiver. Indicates the message, while
+ * possibly valid, is of a format or encoding that is not
+ * supported.
+ */
+ public static final int RESULT_SMS_UNSUPPORTED = 4;
+
+ /**
* Broadcast Action: A new text based SMS message has been received
* by the device. The intent will have the following extra
* values:</p>
@@ -552,6 +565,23 @@ public final class Telephony {
"android.provider.Telephony.SIM_FULL";
/**
+ * Broadcast Action: An incoming SMS has been rejected by the
+ * telephony framework. This intent is sent in lieu of any
+ * of the RECEIVED_ACTION intents. The intent will have the
+ * following extra value:</p>
+ *
+ * <ul>
+ * <li><em>result</em> - An int result code, eg,
+ * <code>{@link #RESULT_SMS_OUT_OF_MEMORY}</code>,
+ * indicating the error returned to the network.</li>
+ * </ul>
+
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SMS_REJECTED_ACTION =
+ "android.provider.Telephony.SMS_REJECTED";
+
+ /**
* Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
* {@link #DATA_SMS_RECEIVED_ACTION} intent.
*
@@ -1008,6 +1038,12 @@ public final class Telephony {
* <P>Type: INTEGER</P>
*/
public static final String THREAD_ID = "thread_id";
+
+ /**
+ * Has the message been locked?
+ * <P>Type: INTEGER (boolean)</P>
+ */
+ public static final String LOCKED = "locked";
}
/**
@@ -1252,6 +1288,21 @@ public final class Telephony {
}
/**
+ * Returns true if the number is a Phone number
+ *
+ * @param number the input number to be tested
+ * @return true if number is a Phone number
+ */
+ public static boolean isPhoneNumber(String number) {
+ if (TextUtils.isEmpty(number)) {
+ return false;
+ }
+
+ Matcher match = Regex.PHONE_PATTERN.matcher(number);
+ return match.matches();
+ }
+
+ /**
* Contains all MMS messages in the MMS app's inbox.
*/
public static final class Inbox implements BaseMmsColumns {
@@ -1416,6 +1467,8 @@ public final class Telephony {
*/
public static final String _DATA = "_data";
+ public static final String TEXT = "text";
+
}
public static final class Rate {
@@ -1493,6 +1546,17 @@ public final class Telephony {
public static final Uri CONTENT_DRAFT_URI = Uri.parse(
"content://mms-sms/draft");
+ public static final Uri CONTENT_LOCKED_URI = Uri.parse(
+ "content://mms-sms/locked");
+
+ /***
+ * Pass in a query parameter called "pattern" which is the text
+ * to search for.
+ * The sort order is fixed to be thread_id ASC,date DESC.
+ */
+ public static final Uri SEARCH_URI = Uri.parse(
+ "content://mms-sms/search");
+
// Constants for message protocol types.
public static final int SMS_PROTO = 0;
public static final int MMS_PROTO = 1;
@@ -1639,7 +1703,3 @@ public final class Telephony {
public static final String EXTRA_SPN = "spn";
}
}
-
-
-
-
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index 5c4e56d..a24e0d2 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -23,17 +23,16 @@
package android.server;
import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetoothA2dp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.media.AudioManager;
-import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
@@ -41,9 +40,10 @@ import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String TAG = "BluetoothA2dpService";
@@ -54,386 +54,383 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
- private static final String A2DP_SINK_ADDRESS = "a2dp_sink_address";
private static final String BLUETOOTH_ENABLED = "bluetooth_enabled";
private static final int MESSAGE_CONNECT_TO = 1;
- private static final int MESSAGE_DISCONNECT = 2;
- private final Context mContext;
- private final IntentFilter mIntentFilter;
- private HashMap<String, SinkState> mAudioDevices;
- private final AudioManager mAudioManager;
- private final BluetoothDevice mBluetooth;
-
- // list of disconnected sinks to process after a delay
- private final ArrayList<String> mPendingDisconnects = new ArrayList<String>();
- // number of active sinks
- private int mSinkCount = 0;
-
- private class SinkState {
- public String address;
- public int state;
- public SinkState(String a, int s) {address = a; state = s;}
- }
-
- public BluetoothA2dpService(Context context) {
- mContext = context;
-
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ private static final String PROPERTY_STATE = "State";
- mBluetooth = (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
- if (mBluetooth == null) {
- throw new RuntimeException("Platform does not support Bluetooth");
- }
+ private static final String SINK_STATE_DISCONNECTED = "disconnected";
+ private static final String SINK_STATE_CONNECTING = "connecting";
+ private static final String SINK_STATE_CONNECTED = "connected";
+ private static final String SINK_STATE_PLAYING = "playing";
- if (!initNative()) {
- throw new RuntimeException("Could not init BluetoothA2dpService");
- }
+ private static int mSinkCount;
- mIntentFilter = new IntentFilter(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
- mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
- mContext.registerReceiver(mReceiver, mIntentFilter);
-
- if (mBluetooth.isEnabled()) {
- onBluetoothEnable();
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- cleanupNative();
- } finally {
- super.finalize();
- }
- }
+ private final Context mContext;
+ private final IntentFilter mIntentFilter;
+ private HashMap<BluetoothDevice, Integer> mAudioDevices;
+ private final AudioManager mAudioManager;
+ private final BluetoothService mBluetoothService;
+ private final BluetoothAdapter mAdapter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
- if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) {
- int state = intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE,
- BluetoothError.ERROR);
+ BluetoothDevice device =
+ intent.getParcelableExtra(BluetoothIntent.DEVICE);
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ BluetoothAdapter.ERROR);
switch (state) {
- case BluetoothDevice.BLUETOOTH_STATE_ON:
+ case BluetoothAdapter.STATE_ON:
onBluetoothEnable();
break;
- case BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF:
+ case BluetoothAdapter.STATE_TURNING_OFF:
onBluetoothDisable();
break;
}
} else if (action.equals(BluetoothIntent.BOND_STATE_CHANGED_ACTION)) {
int bondState = intent.getIntExtra(BluetoothIntent.BOND_STATE,
- BluetoothError.ERROR);
+ BluetoothDevice.ERROR);
switch(bondState) {
case BluetoothDevice.BOND_BONDED:
- setSinkPriority(address, BluetoothA2dp.PRIORITY_AUTO);
+ setSinkPriority(device, BluetoothA2dp.PRIORITY_AUTO);
break;
case BluetoothDevice.BOND_BONDING:
case BluetoothDevice.BOND_NOT_BONDED:
- setSinkPriority(address, BluetoothA2dp.PRIORITY_OFF);
+ setSinkPriority(device, BluetoothA2dp.PRIORITY_OFF);
break;
}
} else if (action.equals(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION)) {
- if (getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF) {
+ if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
+ isSinkDevice(device)) {
// This device is a preferred sink. Make an A2DP connection
// after a delay. We delay to avoid connection collisions,
// and to give other profiles such as HFP a chance to
// connect first.
- Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, address);
+ Message msg = Message.obtain(mHandler, MESSAGE_CONNECT_TO, device);
mHandler.sendMessageDelayed(msg, 6000);
}
}
}
};
+ public BluetoothA2dpService(Context context, BluetoothService bluetoothService) {
+ mContext = context;
+
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+ mBluetoothService = bluetoothService;
+ if (mBluetoothService == null) {
+ throw new RuntimeException("Platform does not support Bluetooth");
+ }
+
+ if (!initNative()) {
+ throw new RuntimeException("Could not init BluetoothA2dpService");
+ }
+
+ mAdapter = (BluetoothAdapter) context.getSystemService(Context.BLUETOOTH_SERVICE);
+
+ mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ mIntentFilter.addAction(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
+ mContext.registerReceiver(mReceiver, mIntentFilter);
+
+ mAudioDevices = new HashMap<BluetoothDevice, Integer>();
+
+ if (mBluetoothService.isEnabled())
+ onBluetoothEnable();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ cleanupNative();
+ } finally {
+ super.finalize();
+ }
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_CONNECT_TO:
- String address = (String)msg.obj;
+ BluetoothDevice device = (BluetoothDevice) msg.obj;
// check bluetooth is still on, device is still preferred, and
// nothing is currently connected
- if (mBluetooth.isEnabled() &&
- getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF &&
+ if (mBluetoothService.isEnabled() &&
+ getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF &&
lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
BluetoothA2dp.STATE_CONNECTED,
BluetoothA2dp.STATE_PLAYING,
BluetoothA2dp.STATE_DISCONNECTING}).size() == 0) {
- log("Auto-connecting A2DP to sink " + address);
- connectSink(address);
+ log("Auto-connecting A2DP to sink " + device);
+ connectSink(device);
}
break;
- case MESSAGE_DISCONNECT:
- handleDeferredDisconnect((String)msg.obj);
- break;
}
}
};
+ private int convertBluezSinkStringtoState(String value) {
+ if (value.equalsIgnoreCase("disconnected"))
+ return BluetoothA2dp.STATE_DISCONNECTED;
+ if (value.equalsIgnoreCase("connecting"))
+ return BluetoothA2dp.STATE_CONNECTING;
+ if (value.equalsIgnoreCase("connected"))
+ return BluetoothA2dp.STATE_CONNECTED;
+ if (value.equalsIgnoreCase("playing"))
+ return BluetoothA2dp.STATE_PLAYING;
+ return -1;
+ }
+
+ private boolean isSinkDevice(BluetoothDevice device) {
+ String uuids[] = mBluetoothService.getRemoteUuids(device.getAddress());
+ UUID uuid;
+ if (uuids != null) {
+ for (String deviceUuid: uuids) {
+ uuid = UUID.fromString(deviceUuid);
+ if (BluetoothUuid.isAudioSink(uuid)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private synchronized boolean addAudioSink (BluetoothDevice device) {
+ String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ String propValues[] = (String []) getSinkPropertiesNative(path);
+ if (propValues == null) {
+ Log.e(TAG, "Error while getting AudioSink properties for device: " + device);
+ return false;
+ }
+ Integer state = null;
+ // Properties are name-value pairs
+ for (int i = 0; i < propValues.length; i+=2) {
+ if (propValues[i].equals(PROPERTY_STATE)) {
+ state = new Integer(convertBluezSinkStringtoState(propValues[i+1]));
+ break;
+ }
+ }
+ mAudioDevices.put(device, state);
+ handleSinkStateChange(device, BluetoothA2dp.STATE_DISCONNECTED, state);
+ return true;
+ }
+
private synchronized void onBluetoothEnable() {
- mAudioDevices = new HashMap<String, SinkState>();
- String[] paths = (String[])listHeadsetsNative();
- if (paths != null) {
- for (String path : paths) {
- mAudioDevices.put(path, new SinkState(getAddressNative(path),
- isSinkConnectedNative(path) ? BluetoothA2dp.STATE_CONNECTED :
- BluetoothA2dp.STATE_DISCONNECTED));
+ String devices = mBluetoothService.getProperty("Devices");
+ mSinkCount = 0;
+ if (devices != null) {
+ String [] paths = devices.split(",");
+ for (String path: paths) {
+ String address = mBluetoothService.getAddressFromObjectPath(path);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ String []uuids = mBluetoothService.getRemoteUuids(address);
+ if (uuids != null)
+ for (String uuid: uuids) {
+ UUID remoteUuid = UUID.fromString(uuid);
+ if (BluetoothUuid.isAudioSink(remoteUuid) ||
+ BluetoothUuid.isAudioSource(remoteUuid) ||
+ BluetoothUuid.isAdvAudioDist(remoteUuid)) {
+ addAudioSink(device);
+ break;
+ }
+ }
}
}
- mAudioManager.setParameter(BLUETOOTH_ENABLED, "true");
+ mAudioManager.setParameters(BLUETOOTH_ENABLED+"=true");
}
private synchronized void onBluetoothDisable() {
- if (mAudioDevices != null) {
- // copy to allow modification during iteration
- String[] paths = new String[mAudioDevices.size()];
- paths = mAudioDevices.keySet().toArray(paths);
- for (String path : paths) {
- switch (mAudioDevices.get(path).state) {
+ if (!mAudioDevices.isEmpty()) {
+ BluetoothDevice[] devices = new BluetoothDevice[mAudioDevices.size()];
+ devices = mAudioDevices.keySet().toArray(devices);
+ for (BluetoothDevice device : devices) {
+ int state = getSinkState(device);
+ switch (state) {
case BluetoothA2dp.STATE_CONNECTING:
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
- disconnectSinkNative(path);
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
+ disconnectSinkNative(mBluetoothService.getObjectPathFromAddress(
+ device.getAddress()));
+ handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
break;
case BluetoothA2dp.STATE_DISCONNECTING:
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
+ handleSinkStateChange(device, BluetoothA2dp.STATE_DISCONNECTING,
+ BluetoothA2dp.STATE_DISCONNECTED);
break;
}
}
- mAudioDevices = null;
+ mAudioDevices.clear();
}
- mAudioManager.setBluetoothA2dpOn(false);
- mAudioManager.setParameter(BLUETOOTH_ENABLED, "false");
+
+ mAudioManager.setParameters(BLUETOOTH_ENABLED + "=false");
}
- public synchronized int connectSink(String address) {
+ public synchronized boolean connectSink(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (DBG) log("connectSink(" + address + ")");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
- }
- if (mAudioDevices == null) {
- return BluetoothError.ERROR;
- }
+ if (DBG) log("connectSink(" + device + ")");
+
// ignore if there are any active sinks
if (lookupSinksMatchingStates(new int[] {
BluetoothA2dp.STATE_CONNECTING,
BluetoothA2dp.STATE_CONNECTED,
BluetoothA2dp.STATE_PLAYING,
BluetoothA2dp.STATE_DISCONNECTING}).size() != 0) {
- return BluetoothError.ERROR;
+ return false;
}
- String path = lookupPath(address);
- if (path == null) {
- path = createHeadsetNative(address);
- if (DBG) log("new bluez sink: " + address + " (" + path + ")");
- }
- if (path == null) {
- return BluetoothError.ERROR;
- }
+ if (mAudioDevices.get(device) == null && !addAudioSink(device))
+ return false;
+
+ int state = mAudioDevices.get(device);
- SinkState sink = mAudioDevices.get(path);
- int state = BluetoothA2dp.STATE_DISCONNECTED;
- if (sink != null) {
- state = sink.state;
- }
switch (state) {
case BluetoothA2dp.STATE_CONNECTED:
case BluetoothA2dp.STATE_PLAYING:
case BluetoothA2dp.STATE_DISCONNECTING:
- return BluetoothError.ERROR;
+ return false;
case BluetoothA2dp.STATE_CONNECTING:
- return BluetoothError.SUCCESS;
+ return true;
}
+ String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
+ if (path == null)
+ return false;
+
// State is DISCONNECTED
if (!connectSinkNative(path)) {
- return BluetoothError.ERROR;
+ return false;
}
- updateState(path, BluetoothA2dp.STATE_CONNECTING);
- return BluetoothError.SUCCESS;
+ return true;
}
- public synchronized int disconnectSink(String address) {
+ public synchronized boolean disconnectSink(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (DBG) log("disconnectSink(" + address + ")");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
- }
- if (mAudioDevices == null) {
- return BluetoothError.ERROR;
- }
- String path = lookupPath(address);
+ if (DBG) log("disconnectSink(" + device + ")");
+
+ String path = mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (path == null) {
- return BluetoothError.ERROR;
+ return false;
}
- switch (mAudioDevices.get(path).state) {
+
+ switch (getSinkState(device)) {
case BluetoothA2dp.STATE_DISCONNECTED:
- return BluetoothError.ERROR;
+ return false;
case BluetoothA2dp.STATE_DISCONNECTING:
- return BluetoothError.SUCCESS;
+ return true;
}
// State is CONNECTING or CONNECTED or PLAYING
if (!disconnectSinkNative(path)) {
- return BluetoothError.ERROR;
+ return false;
} else {
- updateState(path, BluetoothA2dp.STATE_DISCONNECTING);
- return BluetoothError.SUCCESS;
+ return true;
}
}
- public synchronized List<String> listConnectedSinks() {
+ public synchronized BluetoothDevice[] getConnectedSinks() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return lookupSinksMatchingStates(new int[] {BluetoothA2dp.STATE_CONNECTED,
- BluetoothA2dp.STATE_PLAYING});
+ Set<BluetoothDevice> sinks = lookupSinksMatchingStates(
+ new int[] {BluetoothA2dp.STATE_CONNECTED, BluetoothA2dp.STATE_PLAYING});
+ return sinks.toArray(new BluetoothDevice[sinks.size()]);
}
- public synchronized int getSinkState(String address) {
+ public synchronized int getSinkState(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
- }
- if (mAudioDevices == null) {
+ Integer state = mAudioDevices.get(device);
+ if (state == null)
return BluetoothA2dp.STATE_DISCONNECTED;
- }
- for (SinkState sink : mAudioDevices.values()) {
- if (address.equals(sink.address)) {
- return sink.state;
- }
- }
- return BluetoothA2dp.STATE_DISCONNECTED;
+ return state;
}
- public synchronized int getSinkPriority(String address) {
+ public synchronized int getSinkPriority(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
- }
return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.getBluetoothA2dpSinkPriorityKey(address),
+ Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
BluetoothA2dp.PRIORITY_OFF);
}
- public synchronized int setSinkPriority(String address, int priority) {
+ public synchronized boolean setSinkPriority(BluetoothDevice device, int priority) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
+ if (!BluetoothDevice.checkBluetoothAddress(device.getAddress())) {
+ return false;
}
return Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.getBluetoothA2dpSinkPriorityKey(address), priority) ?
- BluetoothError.SUCCESS : BluetoothError.ERROR;
- }
-
- private synchronized void onHeadsetCreated(String path) {
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
- }
-
- private synchronized void onHeadsetRemoved(String path) {
- if (mAudioDevices == null) return;
- mAudioDevices.remove(path);
+ Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), priority);
}
- private synchronized void onSinkConnected(String path) {
- // if we are reconnected, do not process previous disconnect event.
- mPendingDisconnects.remove(path);
-
- if (mAudioDevices == null) return;
- // bluez 3.36 quietly disconnects the previous sink when a new sink
- // is connected, so we need to mark all previously connected sinks as
- // disconnected
-
- // copy to allow modification during iteration
- String[] paths = new String[mAudioDevices.size()];
- paths = mAudioDevices.keySet().toArray(paths);
- for (String oldPath : paths) {
- if (path.equals(oldPath)) {
- continue;
- }
- int state = mAudioDevices.get(oldPath).state;
- if (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING) {
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
- }
+ private synchronized void onSinkPropertyChanged(String path, String []propValues) {
+ if (!mBluetoothService.isEnabled()) {
+ return;
}
- updateState(path, BluetoothA2dp.STATE_CONNECTING);
- mAudioManager.setParameter(A2DP_SINK_ADDRESS, lookupAddress(path));
- mAudioManager.setBluetoothA2dpOn(true);
- updateState(path, BluetoothA2dp.STATE_CONNECTED);
- }
+ String name = propValues[0];
+ String address = mBluetoothService.getAddressFromObjectPath(path);
+ if (address == null) {
+ Log.e(TAG, "onSinkPropertyChanged: Address of the remote device in null");
+ return;
+ }
- private synchronized void onSinkDisconnected(String path) {
- // This is to work around a problem in bluez that results
- // sink disconnect events being sent, immediately followed by a reconnect.
- // To avoid unnecessary audio routing changes, we defer handling
- // sink disconnects until after a short delay.
- mPendingDisconnects.add(path);
- Message msg = Message.obtain(mHandler, MESSAGE_DISCONNECT, path);
- mHandler.sendMessageDelayed(msg, 2000);
- }
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
- private synchronized void handleDeferredDisconnect(String path) {
- if (mPendingDisconnects.contains(path)) {
- mPendingDisconnects.remove(path);
- if (mSinkCount == 1) {
- mAudioManager.setBluetoothA2dpOn(false);
+ if (name.equals(PROPERTY_STATE)) {
+ int state = convertBluezSinkStringtoState(propValues[1]);
+ if (mAudioDevices.get(device) == null) {
+ // This is for an incoming connection for a device not known to us.
+ // We have authorized it and bluez state has changed.
+ addAudioSink(device);
+ } else {
+ int prevState = mAudioDevices.get(device);
+ handleSinkStateChange(device, prevState, state);
}
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
}
}
- private synchronized void onSinkPlaying(String path) {
- updateState(path, BluetoothA2dp.STATE_PLAYING);
- }
-
- private synchronized void onSinkStopped(String path) {
- updateState(path, BluetoothA2dp.STATE_CONNECTED);
- }
-
- private synchronized final String lookupAddress(String path) {
- if (mAudioDevices == null) return null;
- SinkState sink = mAudioDevices.get(path);
- if (sink == null) {
- Log.w(TAG, "lookupAddress() called for unknown device " + path);
- updateState(path, BluetoothA2dp.STATE_DISCONNECTED);
- }
- String address = mAudioDevices.get(path).address;
- if (address == null) Log.e(TAG, "Can't find address for " + path);
- return address;
- }
+ private void handleSinkStateChange(BluetoothDevice device, int prevState, int state) {
+ if (state != prevState) {
+ if (state == BluetoothA2dp.STATE_DISCONNECTED ||
+ state == BluetoothA2dp.STATE_DISCONNECTING) {
+ if (prevState == BluetoothA2dp.STATE_CONNECTED ||
+ prevState == BluetoothA2dp.STATE_PLAYING) {
+ // disconnecting or disconnected
+ Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+ }
+ mSinkCount--;
+ } else if (state == BluetoothA2dp.STATE_CONNECTED) {
+ mSinkCount ++;
+ }
+ mAudioDevices.put(device, state);
- private synchronized final String lookupPath(String address) {
- if (mAudioDevices == null) return null;
+ Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, device);
+ intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState);
+ intent.putExtra(BluetoothA2dp.SINK_STATE, state);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- for (String path : mAudioDevices.keySet()) {
- if (address.equals(mAudioDevices.get(path).address)) {
- return path;
- }
+ if (DBG) log("A2DP state : device: " + device + " State:" + prevState + "->" + state);
}
- return null;
}
- private synchronized List<String> lookupSinksMatchingStates(int[] states) {
- List<String> sinks = new ArrayList<String>();
- if (mAudioDevices == null) {
+ private synchronized Set<BluetoothDevice> lookupSinksMatchingStates(int[] states) {
+ Set<BluetoothDevice> sinks = new HashSet<BluetoothDevice>();
+ if (mAudioDevices.isEmpty()) {
return sinks;
}
- for (SinkState sink : mAudioDevices.values()) {
+ for (BluetoothDevice device: mAudioDevices.keySet()) {
+ int sinkState = getSinkState(device);
for (int state : states) {
- if (sink.state == state) {
- sinks.add(sink.address);
+ if (state == sinkState) {
+ sinks.add(device);
break;
}
}
@@ -441,57 +438,13 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
return sinks;
}
- private synchronized void updateState(String path, int state) {
- if (mAudioDevices == null) return;
-
- SinkState s = mAudioDevices.get(path);
- int prevState;
- String address;
- if (s == null) {
- address = getAddressNative(path);
- mAudioDevices.put(path, new SinkState(address, state));
- prevState = BluetoothA2dp.STATE_DISCONNECTED;
- } else {
- address = lookupAddress(path);
- prevState = s.state;
- s.state = state;
- }
-
- if (state != prevState) {
- if (DBG) log("state " + address + " (" + path + ") " + prevState + "->" + state);
-
- // keep track of the number of active sinks
- if (prevState == BluetoothA2dp.STATE_DISCONNECTED) {
- mSinkCount++;
- } else if (state == BluetoothA2dp.STATE_DISCONNECTED) {
- mSinkCount--;
- }
-
- Intent intent = new Intent(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothA2dp.SINK_PREVIOUS_STATE, prevState);
- intent.putExtra(BluetoothA2dp.SINK_STATE, state);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
- if ((prevState == BluetoothA2dp.STATE_CONNECTED ||
- prevState == BluetoothA2dp.STATE_PLAYING) &&
- (state != BluetoothA2dp.STATE_CONNECTING &&
- state != BluetoothA2dp.STATE_CONNECTED &&
- state != BluetoothA2dp.STATE_PLAYING)) {
- // disconnected
- intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
- mContext.sendBroadcast(intent);
- }
- }
- }
-
@Override
protected synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mAudioDevices == null) return;
+ if (mAudioDevices.isEmpty()) return;
pw.println("Cached audio devices:");
- for (String path : mAudioDevices.keySet()) {
- SinkState sink = mAudioDevices.get(path);
- pw.println(path + " " + sink.address + " " + BluetoothA2dp.stateToString(sink.state));
+ for (BluetoothDevice device : mAudioDevices.keySet()) {
+ int state = mAudioDevices.get(device);
+ pw.println(device + " " + BluetoothA2dp.stateToString(state));
}
}
@@ -501,11 +454,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
private native boolean initNative();
private native void cleanupNative();
- private synchronized native String[] listHeadsetsNative();
- private synchronized native String createHeadsetNative(String address);
- private synchronized native boolean removeHeadsetNative(String path);
- private synchronized native String getAddressNative(String path);
private synchronized native boolean connectSinkNative(String path);
private synchronized native boolean disconnectSinkNative(String path);
- private synchronized native boolean isSinkConnectedNative(String path);
+ private synchronized native Object []getSinkPropertiesNative(String path);
}
diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java
deleted file mode 100644
index 8c843ef..0000000
--- a/core/java/android/server/BluetoothDeviceService.java
+++ /dev/null
@@ -1,1263 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * TODO: Move this to
- * java/services/com/android/server/BluetoothDeviceService.java
- * and make the contructor package private again.
- *
- * @hide
- */
-
-package android.server;
-
-import android.bluetooth.BluetoothClass;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
-import android.bluetooth.BluetoothHeadset;
-import android.bluetooth.BluetoothIntent;
-import android.bluetooth.IBluetoothDevice;
-import android.bluetooth.IBluetoothDeviceCallback;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemService;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import com.android.internal.app.IBatteryStats;
-
-public class BluetoothDeviceService extends IBluetoothDevice.Stub {
- private static final String TAG = "BluetoothDeviceService";
- private static final boolean DBG = true;
-
- private int mNativeData;
- private BluetoothEventLoop mEventLoop;
- private IntentFilter mIntentFilter;
- private boolean mIsAirplaneSensitive;
- private int mBluetoothState;
- private boolean mRestart = false; // need to call enable() after disable()
-
- private final BondState mBondState = new BondState(); // local cache of bondings
- private boolean mIsDiscovering;
- private final IBatteryStats mBatteryStats;
-
- 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;
-
- private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
- private static final int MESSAGE_FINISH_DISABLE = 2;
-
- static {
- classInitNative();
- }
- private native static void classInitNative();
-
- public BluetoothDeviceService(Context context) {
- mContext = context;
-
- // Need to do this in place of:
- // mBatteryStats = BatteryStatsService.getService();
- // Since we can not import BatteryStatsService from here. This class really needs to be
- // moved to java/services/com/android/server/
- mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
- }
-
- /** Must be called after construction, and before any other method.
- */
- public synchronized void init() {
- initializeNativeDataNative();
-
- if (isEnabledNative() == 1) {
- Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
- disableNative();
- }
-
- setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF);
- mIsDiscovering = false;
- mEventLoop = new BluetoothEventLoop(mContext, this);
- registerForAirplaneMode();
- }
- private native void initializeNativeDataNative();
-
- @Override
- protected void finalize() throws Throwable {
- if (mIsAirplaneSensitive) {
- mContext.unregisterReceiver(mReceiver);
- }
- try {
- cleanupNativeDataNative();
- } finally {
- super.finalize();
- }
- }
- private native void cleanupNativeDataNative();
-
- public boolean isEnabled() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON;
- }
- private native int isEnabledNative();
-
- public int getBluetoothState() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBluetoothState;
- }
-
-
- /**
- * Bring down bluetooth and disable BT in settings. Returns true on success.
- */
- public boolean disable() {
- return disable(true);
- }
-
- /**
- * Bring down bluetooth. Returns true on success.
- *
- * @param saveSetting If true, disable BT in settings
- */
- public synchronized boolean disable(boolean saveSetting) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
-
- switch (mBluetoothState) {
- case BluetoothDevice.BLUETOOTH_STATE_OFF:
- return true;
- case BluetoothDevice.BLUETOOTH_STATE_ON:
- break;
- default:
- return false;
- }
- if (mEnableThread != null && mEnableThread.isAlive()) {
- return false;
- }
- setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF);
-
- // Allow 3 seconds for profiles to gracefully disconnect
- // TODO: Introduce a callback mechanism so that each profile can notify
- // BluetoothDeviceService when it is done shutting down
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
- return true;
- }
-
-
- private synchronized void finishDisable(boolean saveSetting) {
- if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
- return;
- }
- mEventLoop.stop();
- disableNative();
-
- // mark in progress bondings as cancelled
- for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
- mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
- BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
- }
-
- // Remove remoteServiceChannelCallbacks
- HashMap<String, IBluetoothDeviceCallback> callbacksMap =
- mEventLoop.getRemoteServiceChannelCallbacks();
-
- for (Iterator<String> i = callbacksMap.keySet().iterator(); i.hasNext();) {
- String address = i.next();
- IBluetoothDeviceCallback callback = callbacksMap.get(address);
- try {
- callback.onGetRemoteServiceChannelResult(address, BluetoothError.ERROR_DISABLED);
- } catch (RemoteException e) {}
- i.remove();
- }
-
- // update mode
- Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
-
- mIsDiscovering = false;
-
- if (saveSetting) {
- persistBluetoothOnSetting(false);
- }
-
- setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF);
-
- // Log bluetooth off to battery stats.
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteBluetoothOff();
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mRestart) {
- mRestart = false;
- enable();
- }
- }
-
- /** Bring up BT and persist BT on in settings */
- public boolean enable() {
- return enable(true);
- }
-
- /**
- * Enable this Bluetooth device, asynchronously.
- * This turns on/off the underlying hardware.
- *
- * @param saveSetting If true, persist the new state of BT in settings
- * @return True on success (so far)
- */
- public synchronized boolean enable(boolean saveSetting) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
-
- // Airplane mode can prevent Bluetooth radio from being turned on.
- if (mIsAirplaneSensitive && isAirplaneModeOn()) {
- return false;
- }
- if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_OFF) {
- return false;
- }
- if (mEnableThread != null && mEnableThread.isAlive()) {
- return false;
- }
- setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_ON);
- mEnableThread = new EnableThread(saveSetting);
- mEnableThread.start();
- return true;
- }
-
- /** Forcibly restart Bluetooth if it is on */
- /* package */ synchronized void restart() {
- if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_ON) {
- return;
- }
- mRestart = true;
- if (!disable(false)) {
- mRestart = false;
- }
- }
-
- private synchronized void setBluetoothState(int state) {
- if (state == mBluetoothState) {
- return;
- }
-
- if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
-
- Intent intent = new Intent(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.BLUETOOTH_PREVIOUS_STATE, mBluetoothState);
- intent.putExtra(BluetoothIntent.BLUETOOTH_STATE, state);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- mBluetoothState = state;
-
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
-
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_REGISTER_SDP_RECORDS:
- //TODO: Don't assume HSP/HFP is running, don't use sdptool,
- if (isEnabled()) {
- SystemService.start("hsag");
- SystemService.start("hfag");
- }
- break;
- case MESSAGE_FINISH_DISABLE:
- finishDisable(msg.arg1 != 0);
- break;
- }
- }
- };
-
- private EnableThread mEnableThread;
-
- private class EnableThread extends Thread {
- private final boolean mSaveSetting;
- public EnableThread(boolean saveSetting) {
- mSaveSetting = saveSetting;
- }
- public void run() {
- boolean res = (enableNative() == 0);
- if (res) {
- int retryCount = 2;
- boolean running = false;
- while ((retryCount-- > 0) && !running) {
- mEventLoop.start();
- // it may take a momement for the other thread to do its
- // thing. Check periodically for a while.
- int pollCount = 5;
- while ((pollCount-- > 0) && !running) {
- if (mEventLoop.isEventLoopRunning()) {
- running = true;
- break;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {}
- }
- }
- if (!running) {
- log("bt EnableThread giving up");
- res = false;
- disableNative();
- }
- }
-
-
- if (res) {
- if (mSaveSetting) {
- persistBluetoothOnSetting(true);
- }
- mIsDiscovering = false;
- mBondState.loadBondState();
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
- 3000);
-
- // Log bluetooth on to battery stats.
- long ident = Binder.clearCallingIdentity();
- try {
- mBatteryStats.noteBluetoothOn();
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- mEnableThread = null;
-
- setBluetoothState(res ?
- BluetoothDevice.BLUETOOTH_STATE_ON :
- BluetoothDevice.BLUETOOTH_STATE_OFF);
-
- if (res) {
- // Update mode
- mEventLoop.onModeChanged(getModeNative());
- }
-
- if (mIsAirplaneSensitive && isAirplaneModeOn()) {
- disable(false);
- }
-
- }
- }
-
- private void persistBluetoothOnSetting(boolean bluetoothOn) {
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
- bluetoothOn ? 1 : 0);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
- }
-
- private native int enableNative();
- private native int disableNative();
-
- /* package */ BondState getBondState() {
- return mBondState;
- }
-
- /** local cache of bonding state.
- /* we keep our own state to track the intermediate state BONDING, which
- /* bluez does not track.
- * All addreses must be passed in upper case.
- */
- public class BondState {
- private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
- private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
- private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
- // List of all the vendor_id prefix of Bluetooth addresses for
- // which auto pairing is not attempted.
- // The following companies are included in the list below:
- // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
- // Parrot, Zhongshan General K-mate Electronics, Great Well
- // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
- // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
- // Continental Automotive, Harman/Becker
- private final ArrayList<String> mAutoPairingBlacklisted =
- new ArrayList<String>(Arrays.asList(
- "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
- "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
- "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
- "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59",
- "00:0A:30", "00:1E:AE", "00:1C:D7"
- ));
-
- public synchronized void loadBondState() {
- if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) {
- return;
- }
- String[] bonds = listBondingsNative();
- if (bonds == null) {
- return;
- }
- mState.clear();
- if (DBG) log("found " + bonds.length + " bonded devices");
- for (String address : bonds) {
- mState.put(address.toUpperCase(), BluetoothDevice.BOND_BONDED);
- }
- }
-
- public synchronized void setBondState(String address, int state) {
- setBondState(address, state, 0);
- }
-
- /** reason is ignored unless state == BOND_NOT_BONDED */
- public synchronized void setBondState(String address, int state, int reason) {
- int oldState = getBondState(address);
- if (oldState == state) {
- return;
- }
- if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
- reason + ")");
- Intent intent = new Intent(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.BOND_STATE, state);
- intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState);
- if (state == BluetoothDevice.BOND_NOT_BONDED) {
- if (reason <= 0) {
- Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
- "invalid. Overriding reason code with BOND_RESULT_REMOVED");
- reason = BluetoothDevice.UNBOND_REASON_REMOVED;
- }
- intent.putExtra(BluetoothIntent.REASON, reason);
- mState.remove(address);
- } else {
- mState.put(address, state);
- }
-
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
-
- public boolean isAutoPairingBlacklisted(String address) {
- for (String blacklistAddress : mAutoPairingBlacklisted) {
- if (address.startsWith(blacklistAddress)) return true;
- }
- return false;
- }
-
- public synchronized int getBondState(String address) {
- Integer state = mState.get(address);
- if (state == null) {
- return BluetoothDevice.BOND_NOT_BONDED;
- }
- return state.intValue();
- }
-
- private synchronized String[] listInState(int state) {
- ArrayList<String> result = new ArrayList<String>(mState.size());
- for (Map.Entry<String, Integer> e : mState.entrySet()) {
- if (e.getValue().intValue() == state) {
- result.add(e.getKey());
- }
- }
- return result.toArray(new String[result.size()]);
- }
-
- public synchronized void addAutoPairingFailure(String address) {
- if (!mAutoPairingFailures.contains(address)) {
- mAutoPairingFailures.add(address);
- }
- }
-
- public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
- return getAttempt(address) != 0;
- }
-
- public synchronized void clearPinAttempts(String address) {
- mPinAttempt.remove(address);
- }
-
- public synchronized boolean hasAutoPairingFailed(String address) {
- return mAutoPairingFailures.contains(address);
- }
-
- public synchronized int getAttempt(String address) {
- Integer attempt = mPinAttempt.get(address);
- if (attempt == null) {
- return 0;
- }
- return attempt.intValue();
- }
-
- public synchronized void attempt(String address) {
- Integer attempt = mPinAttempt.get(address);
- int newAttempt;
- if (attempt == null) {
- newAttempt = 1;
- } else {
- newAttempt = attempt.intValue() + 1;
- }
- mPinAttempt.put(address, new Integer(newAttempt));
- }
-
- }
- private native String[] listBondingsNative();
-
- private static String toBondStateString(int bondState) {
- switch (bondState) {
- case BluetoothDevice.BOND_NOT_BONDED:
- return "not bonded";
- case BluetoothDevice.BOND_BONDING:
- return "bonding";
- case BluetoothDevice.BOND_BONDED:
- return "bonded";
- default:
- return "??????";
- }
- }
-
- public synchronized String getAddress() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getAddressNative();
- }
- private native String getAddressNative();
-
- public synchronized String getName() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getNameNative();
- }
- private native String getNameNative();
-
- public synchronized boolean setName(String name) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (name == null) {
- return false;
- }
- // hcid handles persistance of the bluetooth name
- return setNameNative(name);
- }
- private native boolean setNameNative(String name);
-
- /**
- * Returns the user-friendly name of a remote device. This value is
- * retrned from our local cache, which is updated during device discovery.
- * Do not expect to retrieve the updated remote name immediately after
- * changing the name on the remote device.
- *
- * @param address Bluetooth address of remote device.
- *
- * @return The user-friendly name of the specified remote device.
- */
- public synchronized String getRemoteName(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteNameNative(address);
- }
- private native String getRemoteNameNative(String address);
-
- /* pacakge */ native String getAdapterPathNative();
-
- public synchronized boolean startDiscovery(boolean resolveNames) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- return startDiscoveryNative(resolveNames);
- }
- private native boolean startDiscoveryNative(boolean resolveNames);
-
- public synchronized boolean cancelDiscovery() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- return cancelDiscoveryNative();
- }
- private native boolean cancelDiscoveryNative();
-
- public synchronized boolean isDiscovering() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mIsDiscovering;
- }
-
- /* package */ void setIsDiscovering(boolean isDiscovering) {
- mIsDiscovering = isDiscovering;
- }
-
- public synchronized boolean startPeriodicDiscovery() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- return startPeriodicDiscoveryNative();
- }
- private native boolean startPeriodicDiscoveryNative();
-
- public synchronized boolean stopPeriodicDiscovery() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- return stopPeriodicDiscoveryNative();
- }
- private native boolean stopPeriodicDiscoveryNative();
-
- public synchronized boolean isPeriodicDiscovery() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return isPeriodicDiscoveryNative();
- }
- private native boolean isPeriodicDiscoveryNative();
-
- /**
- * Set the discoverability window for the device. A timeout of zero
- * makes the device permanently discoverable (if the device is
- * discoverable). Setting the timeout to a nonzero value does not make
- * a device discoverable; you need to call setMode() to make the device
- * explicitly discoverable.
- *
- * @param timeout_s The discoverable timeout in seconds.
- */
- public synchronized boolean setDiscoverableTimeout(int timeout) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- return setDiscoverableTimeoutNative(timeout);
- }
- private native boolean setDiscoverableTimeoutNative(int timeout_s);
-
- /**
- * Get the discoverability window for the device. A timeout of zero
- * means that the device is permanently discoverable (if the device is
- * in the discoverable mode).
- *
- * @return The discoverability window of the device, in seconds. A negative
- * value indicates an error.
- */
- public synchronized int getDiscoverableTimeout() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getDiscoverableTimeoutNative();
- }
- private native int getDiscoverableTimeoutNative();
-
- public synchronized boolean isAclConnected(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- return isConnectedNative(address);
- }
- private native boolean isConnectedNative(String address);
-
- public synchronized int getScanMode() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return bluezStringToScanMode(getModeNative());
- }
- private native String getModeNative();
-
- public synchronized boolean setScanMode(int mode) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- String bluezMode = scanModeToBluezString(mode);
- if (bluezMode != null) {
- return setModeNative(bluezMode);
- }
- return false;
- }
- private native boolean setModeNative(String mode);
-
- public synchronized boolean disconnectRemoteDeviceAcl(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- return disconnectRemoteDeviceNative(address);
- }
- private native boolean disconnectRemoteDeviceNative(String address);
-
- public synchronized boolean createBond(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- address = address.toUpperCase();
-
- String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING);
- if (bonding.length > 0 && !bonding[0].equals(address)) {
- log("Ignoring createBond(): another device is bonding");
- // a different device is currently bonding, fail
- return false;
- }
-
- // Check for bond state only if we are not performing auto
- // pairing exponential back-off attempts.
- if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
- mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) {
- log("Ignoring createBond(): this device is already bonding or bonded");
- return false;
- }
-
- if (!createBondingNative(address, 60000 /* 1 minute */)) {
- return false;
- }
-
- mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
- return true;
- }
- private native boolean createBondingNative(String address, int timeout_ms);
-
- public synchronized boolean cancelBondProcess(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- address = address.toUpperCase();
- if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
- return false;
- }
-
- mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
- BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
- cancelBondingProcessNative(address);
- return true;
- }
- private native boolean cancelBondingProcessNative(String address);
-
- public synchronized boolean removeBond(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- return removeBondingNative(address);
- }
- private native boolean removeBondingNative(String address);
-
- public synchronized String[] listBonds() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return mBondState.listInState(BluetoothDevice.BOND_BONDED);
- }
-
- public synchronized int getBondState(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return BluetoothError.ERROR;
- }
- return mBondState.getBondState(address.toUpperCase());
- }
-
- public synchronized String[] listAclConnections() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return listConnectionsNative();
- }
- private native String[] listConnectionsNative();
-
- /**
- * This method lists all remote devices that this adapter is aware of.
- * This is a list not only of all most-recently discovered devices, but of
- * all devices discovered by this adapter up to some point in the past.
- * Note that many of these devices may not be in the neighborhood anymore,
- * and attempting to connect to them will result in an error.
- *
- * @return An array of strings representing the Bluetooth addresses of all
- * remote devices that this adapter is aware of.
- */
- public synchronized String[] listRemoteDevices() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return listRemoteDevicesNative();
- }
- private native String[] listRemoteDevicesNative();
-
- /**
- * Returns the version of the Bluetooth chip. This version is compiled from
- * the LMP version. In case of EDR the features attribute must be checked.
- * Example: "Bluetooth 2.0 + EDR".
- *
- * @return a String representation of the this Adapter's underlying
- * Bluetooth-chip version.
- */
- public synchronized String getVersion() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getVersionNative();
- }
- private native String getVersionNative();
-
- /**
- * Returns the revision of the Bluetooth chip. This is a vendor-specific
- * value and in most cases it represents the firmware version. This might
- * derive from the HCI revision and LMP subversion values or via extra
- * vendord specific commands.
- * In case the revision of a chip is not available. This method should
- * return the LMP subversion value as a string.
- * Example: "HCI 19.2"
- *
- * @return The HCI revision of this adapter.
- */
- public synchronized String getRevision() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getRevisionNative();
- }
- private native String getRevisionNative();
-
- /**
- * Returns the manufacturer of the Bluetooth chip. If the company id is not
- * known the sting "Company ID %d" where %d should be replaced with the
- * numeric value from the manufacturer field.
- * Example: "Cambridge Silicon Radio"
- *
- * @return Manufacturer name.
- */
- public synchronized String getManufacturer() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getManufacturerNative();
- }
- private native String getManufacturerNative();
-
- /**
- * Returns the company name from the OUI database of the Bluetooth device
- * address. This function will need a valid and up-to-date oui.txt from
- * the IEEE. This value will be different from the manufacturer string in
- * the most cases.
- * If the oui.txt file is not present or the OUI part of the Bluetooth
- * address is not listed, it should return the string "OUI %s" where %s is
- * the actual OUI.
- *
- * Example: "Apple Computer"
- *
- * @return company name
- */
- public synchronized String getCompany() {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getCompanyNative();
- }
- private native String getCompanyNative();
-
- /**
- * Like getVersion(), but for a remote device.
- *
- * @param address The Bluetooth address of the remote device.
- *
- * @return remote-device Bluetooth version
- *
- * @see #getVersion
- */
- public synchronized String getRemoteVersion(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteVersionNative(address);
- }
- private native String getRemoteVersionNative(String address);
-
- /**
- * Like getRevision(), but for a remote device.
- *
- * @param address The Bluetooth address of the remote device.
- *
- * @return remote-device HCI revision
- *
- * @see #getRevision
- */
- public synchronized String getRemoteRevision(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteRevisionNative(address);
- }
- private native String getRemoteRevisionNative(String address);
-
- /**
- * Like getManufacturer(), but for a remote device.
- *
- * @param address The Bluetooth address of the remote device.
- *
- * @return remote-device Bluetooth chip manufacturer
- *
- * @see #getManufacturer
- */
- public synchronized String getRemoteManufacturer(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteManufacturerNative(address);
- }
- private native String getRemoteManufacturerNative(String address);
-
- /**
- * Like getCompany(), but for a remote device.
- *
- * @param address The Bluetooth address of the remote device.
- *
- * @return remote-device company
- *
- * @see #getCompany
- */
- public synchronized String getRemoteCompany(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteCompanyNative(address);
- }
- private native String getRemoteCompanyNative(String address);
-
- /**
- * Returns the date and time when the specified remote device has been seen
- * by a discover procedure.
- * Example: "2006-02-08 12:00:00 GMT"
- *
- * @return a String with the timestamp.
- */
- public synchronized String lastSeen(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return lastSeenNative(address);
- }
- private native String lastSeenNative(String address);
-
- /**
- * Returns the date and time when the specified remote device has last been
- * connected to
- * Example: "2006-02-08 12:00:00 GMT"
- *
- * @return a String with the timestamp.
- */
- public synchronized String lastUsed(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return lastUsedNative(address);
- }
- private native String lastUsedNative(String address);
-
- /**
- * Gets the remote major, minor, and service classes encoded as a 32-bit
- * integer.
- *
- * Note: this value is retrieved from cache, because we get it during
- * remote-device discovery.
- *
- * @return 32-bit integer encoding the remote major, minor, and service
- * classes.
- *
- * @see #getRemoteMajorClass
- * @see #getRemoteMinorClass
- * @see #getRemoteServiceClasses
- */
- public synchronized int getRemoteClass(String address) {
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return BluetoothClass.ERROR;
- }
- return getRemoteClassNative(address);
- }
- private native int getRemoteClassNative(String address);
-
- /**
- * Gets the remote features encoded as bit mask.
- *
- * Note: This method may be obsoleted soon.
- *
- * @return byte array of features.
- */
- public synchronized byte[] getRemoteFeatures(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteFeaturesNative(address);
- }
- private native byte[] getRemoteFeaturesNative(String address);
-
- /**
- * This method and {@link #getRemoteServiceRecord} query the SDP service
- * on a remote device. They do not interpret the data, but simply return
- * it raw to the user. To read more about SDP service handles and records,
- * consult the Bluetooth core documentation (www.bluetooth.com).
- *
- * @param address Bluetooth address of remote device.
- * @param match a String match to narrow down the service-handle search.
- * The only supported value currently is "hsp" for the headset
- * profile. To retrieve all service handles, simply pass an empty
- * match string.
- *
- * @return all service handles corresponding to the string match.
- *
- * @see #getRemoteServiceRecord
- */
- public synchronized int[] getRemoteServiceHandles(String address, String match) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- if (match == null) {
- match = "";
- }
- return getRemoteServiceHandlesNative(address, match);
- }
- private native int[] getRemoteServiceHandlesNative(String address, String match);
-
- /**
- * This method retrieves the service records corresponding to a given
- * service handle (method {@link #getRemoteServiceHandles} retrieves the
- * service handles.)
- *
- * This method and {@link #getRemoteServiceHandles} do not interpret their
- * data, but simply return it raw to the user. To read more about SDP
- * service handles and records, consult the Bluetooth core documentation
- * (www.bluetooth.com).
- *
- * @param address Bluetooth address of remote device.
- * @param handle Service handle returned by {@link #getRemoteServiceHandles}
- *
- * @return a byte array of all service records corresponding to the
- * specified service handle.
- *
- * @see #getRemoteServiceHandles
- */
- public synchronized byte[] getRemoteServiceRecord(String address, int handle) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return null;
- }
- return getRemoteServiceRecordNative(address, handle);
- }
- private native byte[] getRemoteServiceRecordNative(String address, int handle);
-
- private static final int MAX_OUTSTANDING_ASYNC = 32;
-
- // AIDL does not yet support short's
- public synchronized boolean getRemoteServiceChannel(String address, int uuid16,
- IBluetoothDeviceCallback callback) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- HashMap<String, IBluetoothDeviceCallback> callbacks =
- mEventLoop.getRemoteServiceChannelCallbacks();
- if (callbacks.containsKey(address)) {
- Log.w(TAG, "SDP request already in progress for " + address);
- return false;
- }
- // Protect from malicious clients - only allow 32 bonding requests per minute.
- if (callbacks.size() > MAX_OUTSTANDING_ASYNC) {
- Log.w(TAG, "Too many outstanding SDP requests, dropping request for " + address);
- return false;
- }
- callbacks.put(address, callback);
-
- if (!getRemoteServiceChannelNative(address, (short)uuid16)) {
- callbacks.remove(address);
- return false;
- }
- return true;
- }
- private native boolean getRemoteServiceChannelNative(String address, short uuid16);
-
- public synchronized boolean setPin(String address, byte[] pin) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (pin == null || pin.length <= 0 || pin.length > 16 ||
- !BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- address = address.toUpperCase();
- Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
- if (data == null) {
- Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
- "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
- " or by bluez.\n");
- return false;
- }
- // bluez API wants pin as a string
- String pinString;
- try {
- pinString = new String(pin, "UTF8");
- } catch (UnsupportedEncodingException uee) {
- Log.e(TAG, "UTF8 not supported?!?");
- return false;
- }
- return setPinNative(address, pinString, data.intValue());
- }
- private native boolean setPinNative(String address, String pin, int nativeData);
-
- public synchronized boolean cancelPin(String address) {
- mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
- "Need BLUETOOTH_ADMIN permission");
- if (!BluetoothDevice.checkBluetoothAddress(address)) {
- return false;
- }
- address = address.toUpperCase();
- Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
- if (data == null) {
- Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " +
- "ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " +
- "or by bluez.\n");
- return false;
- }
- return cancelPinNative(address, data.intValue());
- }
- private native boolean cancelPinNative(String address, int natveiData);
-
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
- ContentResolver resolver = context.getContentResolver();
- // Query the airplane mode from Settings.System just to make sure that
- // some random app is not sending this intent and disabling bluetooth
- boolean enabled = !isAirplaneModeOn();
- // If bluetooth is currently expected to be on, then enable or disable bluetooth
- if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
- if (enabled) {
- enable(false);
- } else {
- disable(false);
- }
- }
- }
- }
- };
-
- private void registerForAirplaneMode() {
- String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_RADIOS);
- mIsAirplaneSensitive = airplaneModeRadios == null
- ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
- if (mIsAirplaneSensitive) {
- mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mContext.registerReceiver(mReceiver, mIntentFilter);
- }
- }
-
- /* Returns true if airplane mode is currently on */
- private final boolean isAirplaneModeOn() {
- return Settings.System.getInt(mContext.getContentResolver(),
- Settings.System.AIRPLANE_MODE_ON, 0) == 1;
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
-
- switch(mBluetoothState) {
- case BluetoothDevice.BLUETOOTH_STATE_OFF:
- pw.println("\nBluetooth OFF\n");
- return;
- case BluetoothDevice.BLUETOOTH_STATE_TURNING_ON:
- pw.println("\nBluetooth TURNING ON\n");
- return;
- case BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF:
- pw.println("\nBluetooth TURNING OFF\n");
- return;
- case BluetoothDevice.BLUETOOTH_STATE_ON:
- pw.println("\nBluetooth ON\n");
- }
-
- pw.println("\nLocal address = " + getAddress());
- pw.println("\nLocal name = " + getName());
- pw.println("\nisDiscovering() = " + isDiscovering());
-
- BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
-
- String[] addresses = listRemoteDevices();
-
- pw.println("\n--Known devices--");
- for (String address : addresses) {
- pw.printf("%s %10s (%d) %s\n", address,
- toBondStateString(mBondState.getBondState(address)),
- mBondState.getAttempt(address),
- getRemoteName(address));
- }
-
- addresses = listAclConnections();
- pw.println("\n--ACL connected devices--");
- for (String address : addresses) {
- pw.println(address);
- }
-
- // Rather not do this from here, but no-where else and I need this
- // dump
- pw.println("\n--Headset Service--");
- switch (headset.getState()) {
- case BluetoothHeadset.STATE_DISCONNECTED:
- pw.println("getState() = STATE_DISCONNECTED");
- break;
- case BluetoothHeadset.STATE_CONNECTING:
- pw.println("getState() = STATE_CONNECTING");
- break;
- case BluetoothHeadset.STATE_CONNECTED:
- pw.println("getState() = STATE_CONNECTED");
- break;
- case BluetoothHeadset.STATE_ERROR:
- pw.println("getState() = STATE_ERROR");
- break;
- }
- pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress());
- pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
-
- headset.close();
- }
-
- /* package */ static int bluezStringToScanMode(String mode) {
- if (mode == null) {
- return BluetoothError.ERROR;
- }
- mode = mode.toLowerCase();
- if (mode.equals("off")) {
- return BluetoothDevice.SCAN_MODE_NONE;
- } else if (mode.equals("connectable")) {
- return BluetoothDevice.SCAN_MODE_CONNECTABLE;
- } else if (mode.equals("discoverable")) {
- return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
- } else {
- return BluetoothError.ERROR;
- }
- }
-
- /* package */ static String scanModeToBluezString(int mode) {
- switch (mode) {
- case BluetoothDevice.SCAN_MODE_NONE:
- return "off";
- case BluetoothDevice.SCAN_MODE_CONNECTABLE:
- return "connectable";
- case BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
- return "discoverable";
- }
- return null;
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 8cc229b..b5eb9ac 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -18,18 +18,18 @@ package android.server;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
import android.bluetooth.BluetoothIntent;
-import android.bluetooth.IBluetoothDeviceCallback;
+import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.util.Log;
import java.util.HashMap;
+import java.util.UUID;
/**
* TODO: Move this to
@@ -46,9 +46,10 @@ class BluetoothEventLoop {
private Thread mThread;
private boolean mStarted;
private boolean mInterrupted;
+
private final HashMap<String, Integer> mPasskeyAgentRequestData;
- private final HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
- private final BluetoothDeviceService mBluetoothService;
+ private final BluetoothService mBluetoothService;
+ private final BluetoothAdapter mAdapter;
private final Context mContext;
private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1;
@@ -85,14 +86,14 @@ class BluetoothEventLoop {
static { classInitNative(); }
private static native void classInitNative();
- /* pacakge */ BluetoothEventLoop(Context context, BluetoothDeviceService bluetoothService) {
+ /* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
+ BluetoothService bluetoothService) {
mBluetoothService = bluetoothService;
mContext = context;
mPasskeyAgentRequestData = new HashMap();
- mGetRemoteServiceChannelCallbacks = new HashMap();
+ mAdapter = adapter;
initializeNativeDataNative();
}
- private native void initializeNativeDataNative();
protected void finalize() throws Throwable {
try {
@@ -101,20 +102,11 @@ class BluetoothEventLoop {
super.finalize();
}
}
- private native void cleanupNativeDataNative();
-
- /* pacakge */ HashMap<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() {
- return mGetRemoteServiceChannelCallbacks;
- }
- /* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() {
+ /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
return mPasskeyAgentRequestData;
}
- private native void startEventLoopNative();
- private native void stopEventLoopNative();
- private native boolean isEventLoopRunningNative();
-
/* package */ void start() {
if (!isEventLoopRunningNative()) {
@@ -134,81 +126,49 @@ class BluetoothEventLoop {
return isEventLoopRunningNative();
}
- /*package*/ void onModeChanged(String bluezMode) {
- int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode);
- if (mode >= 0) {
- Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ private void addDevice(String address, String[] properties) {
+ mBluetoothService.addRemoteDeviceProperties(address, properties);
+ String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
+ String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
+ String name = mBluetoothService.getRemoteDeviceProperty(address, "Name");
+ short rssiValue;
+ // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE.
+ // If we accept the pairing, we will automatically show it at the top of the list.
+ if (rssi != null) {
+ rssiValue = (short)Integer.valueOf(rssi).intValue();
+ } else {
+ rssiValue = Short.MIN_VALUE;
+ }
+ if (classValue != null) {
+ Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothIntent.CLASS, Integer.valueOf(classValue));
+ intent.putExtra(BluetoothIntent.RSSI, rssiValue);
+ intent.putExtra(BluetoothIntent.NAME, name);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ } else {
+ log ("ClassValue: " + classValue + " for remote device: " + address + " is null");
}
}
- private void onDiscoveryStarted() {
- mBluetoothService.setIsDiscovering(true);
- Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onDiscoveryCompleted() {
- mBluetoothService.setIsDiscovering(false);
- Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ private void onDeviceFound(String address, String[] properties) {
+ if (properties == null) {
+ Log.e(TAG, "ERROR: Remote device properties are null");
+ return;
+ }
+ addDevice(address, properties);
}
- private void onRemoteDeviceFound(String address, int deviceClass, short rssi) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.CLASS, deviceClass);
- intent.putExtra(BluetoothIntent.RSSI, rssi);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceDisappeared(String address) {
+ private void onDeviceDisappeared(String address) {
Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteClassUpdated(String address, int deviceClass) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.CLASS, deviceClass);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceConnected(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceDisconnectRequested(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceDisconnected(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameUpdated(String address, String name) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.NAME, name);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameFailed(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameChanged(String address, String name) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.NAME, name);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
- private void onCreateBondingResult(String address, int result) {
+ private void onCreatePairedDeviceResult(String address, int result) {
address = address.toUpperCase();
- if (result == BluetoothError.SUCCESS) {
+ if (result == BluetoothDevice.BOND_SUCCESS) {
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
mBluetoothService.getBondState().clearPinAttempts(address);
@@ -259,31 +219,192 @@ class BluetoothEventLoop {
mBluetoothService.getBondState().attempt(address);
}
- private void onBondingCreated(String address) {
- mBluetoothService.getBondState().setBondState(address.toUpperCase(),
- BluetoothDevice.BOND_BONDED);
+ private void onDeviceCreated(String deviceObjectPath) {
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (!mBluetoothService.isRemoteDeviceInCache(address)) {
+ // Incoming connection, we haven't seen this device, add to cache.
+ String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
+ if (properties != null) {
+ addDevice(address, properties);
+ }
+ }
+ return;
}
- private void onBondingRemoved(String address) {
- mBluetoothService.getBondState().setBondState(address.toUpperCase(),
- BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
+ private void onDeviceRemoved(String deviceObjectPath) {
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address != null)
+ mBluetoothService.getBondState().setBondState(address.toUpperCase(),
+ BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
}
- private void onNameChanged(String name) {
- Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.NAME, name);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ /*package*/ void onPropertyChanged(String[] propValues) {
+ if (mBluetoothService.isAdapterPropertiesEmpty()) {
+ // We have got a property change before
+ // we filled up our cache.
+ mBluetoothService.getAllProperties();
+ }
+ String name = propValues[0];
+ if (name.equals("Name")) {
+ Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
+ intent.putExtra(BluetoothIntent.NAME, propValues[1]);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Pairable") || name.equals("Discoverable")) {
+ String pairable = name.equals("Pairable") ? propValues[1] :
+ mBluetoothService.getProperty("Pairable");
+ String discoverable = name.equals("Discoverable") ? propValues[1] :
+ mBluetoothService.getProperty("Discoverable");
+
+ // This shouldn't happen, unless Adapter Properties are null.
+ if (pairable == null || discoverable == null)
+ return;
+
+ int mode = BluetoothService.bluezStringToScanMode(
+ pairable.equals("true"),
+ discoverable.equals("true"));
+ if (mode >= 0) {
+ Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ }
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Discovering")) {
+ Intent intent;
+ if (propValues[1].equals("true")) {
+ mBluetoothService.setIsDiscovering(true);
+ intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
+ } else {
+ // Stop the discovery.
+ mBluetoothService.cancelDiscovery();
+ mBluetoothService.setIsDiscovering(false);
+ intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
+ }
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Devices")) {
+ String value = null;
+ int len = Integer.valueOf(propValues[1]);
+ if (len > 0) {
+ StringBuilder str = new StringBuilder();
+ for (int i = 2; i < propValues.length; i++) {
+ str.append(propValues[i]);
+ str.append(",");
+ }
+ value = str.toString();
+ }
+ mBluetoothService.setProperty(name, value);
+ } else if (name.equals("Powered")) {
+ // bluetoothd has restarted, re-read all our properties.
+ // Note: bluez only sends this property change when it restarts.
+ if (propValues[1].equals("true"))
+ onRestartRequired();
+ }
}
- private void onPasskeyAgentRequest(String address, int nativeData) {
+ private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
+ String name = propValues[0];
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) {
+ Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
+ return;
+ }
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (name.equals("Name")) {
+ Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, device);
+ intent.putExtra(BluetoothIntent.NAME, propValues[1]);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("Class")) {
+ Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, device);
+ intent.putExtra(BluetoothIntent.CLASS, propValues[1]);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("Connected")) {
+ Intent intent = null;
+ if (propValues[1].equals("true")) {
+ intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
+ } else {
+ intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
+ }
+ intent.putExtra(BluetoothIntent.DEVICE, device);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("UUIDs")) {
+ String uuid = null;
+ int len = Integer.valueOf(propValues[1]);
+ if (len > 0) {
+ StringBuilder str = new StringBuilder();
+ for (int i = 2; i < propValues.length; i++) {
+ str.append(propValues[i]);
+ str.append(",");
+ }
+ uuid = str.toString();
+ }
+ mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
+ } else if (name.equals("Paired")) {
+ if (propValues[1].equals("true")) {
+ mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
+ } else {
+ mBluetoothService.getBondState().setBondState(address,
+ BluetoothDevice.BOND_NOT_BONDED);
+ mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
+ }
+ } else if (name.equals("Trusted")) {
+ if (DBG)
+ log("set trust state succeded, value is " + propValues[1]);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ }
+ }
+
+ private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
+ String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+ if (address == null) {
+ Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
+ "returning null");
+ return null;
+ }
address = address.toUpperCase();
mPasskeyAgentRequestData.put(address, new Integer(nativeData));
- if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
+ if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
// shutdown path
- mBluetoothService.cancelPin(address);
- return;
+ mBluetoothService.cancelPairingUserInput(address);
+ return null;
}
+ return address;
+ }
+
+ private void onRequestConfirmation(String objectPath, int passkey, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothIntent.PASSKEY, passkey);
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_CONFIRMATION);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPasskey(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PASSKEY);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPinCode(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
if (mBluetoothService.getBondState().getBondState(address) ==
BluetoothDevice.BOND_BONDING) {
@@ -307,55 +428,51 @@ class BluetoothEventLoop {
}
}
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
}
- private void onPasskeyAgentCancel(String address) {
- address = address.toUpperCase();
- mBluetoothService.cancelPin(address);
- Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
- mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
- BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
- }
+ private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
+ String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+ if (address == null) {
+ Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
+ return false;
+ }
- private boolean onAuthAgentAuthorize(String address, String service, String uuid) {
boolean authorized = false;
- if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) {
+ UUID uuid = UUID.fromString(deviceUuid);
+ // Bluez sends the UUID of the local service being accessed, _not_ the
+ // remote service
+ if (mBluetoothService.isEnabled() &&
+ (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
+ || BluetoothUuid.isAdvAudioDist(uuid))) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
- Log.i(TAG, "Allowing incoming A2DP connection from " + address);
+ Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
} else {
- Log.i(TAG, "Rejecting incoming A2DP connection from " + address);
+ Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
}
} else {
- Log.i(TAG, "Rejecting incoming " + service + " connection from " + address);
+ Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
+ log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
return authorized;
}
- private void onAuthAgentCancel(String address, String service, String uuid) {
- // We immediately response to DBUS Authorize() so this should not
- // usually happen
- log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")");
- }
-
- private void onGetRemoteServiceChannelResult(String address, int channel) {
- IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address);
- if (callback != null) {
- mGetRemoteServiceChannelCallbacks.remove(address);
- try {
- callback.onGetRemoteServiceChannelResult(address, channel);
- } catch (RemoteException e) {}
- }
+ private void onAgentCancel() {
+ Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
}
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
- Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***");
+ Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
+ "restarting Bluetooth ***");
mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
}
}
@@ -363,4 +480,10 @@ class BluetoothEventLoop {
private static void log(String msg) {
Log.d(TAG, msg);
}
+
+ private native void initializeNativeDataNative();
+ private native void startEventLoopNative();
+ private native void stopEventLoopNative();
+ private native boolean isEventLoopRunningNative();
+ private native void cleanupNativeDataNative();
}
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
new file mode 100644
index 0000000..6482c4c
--- /dev/null
+++ b/core/java/android/server/BluetoothService.java
@@ -0,0 +1,1269 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * TODO: Move this to
+ * java/services/com/android/server/BluetoothService.java
+ * and make the contructor package private again.
+ *
+ * @hide
+ */
+
+package android.server;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothIntent;
+import android.bluetooth.IBluetooth;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemService;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.app.IBatteryStats;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public class BluetoothService extends IBluetooth.Stub {
+ private static final String TAG = "BluetoothService";
+ private static final boolean DBG = true;
+
+ private int mNativeData;
+ private BluetoothEventLoop mEventLoop;
+ private IntentFilter mIntentFilter;
+ private boolean mIsAirplaneSensitive;
+ private int mBluetoothState;
+ private boolean mRestart = false; // need to call enable() after disable()
+ private boolean mIsDiscovering;
+
+ private BluetoothAdapter mAdapter; // constant after init()
+ private final BondState mBondState = new BondState(); // local cache of bondings
+ private final IBatteryStats mBatteryStats;
+ 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;
+
+ private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
+ private static final int MESSAGE_FINISH_DISABLE = 2;
+
+ private final Map<String, String> mAdapterProperties;
+ private final HashMap <String, Map<String, String>> mDeviceProperties;
+
+ static {
+ classInitNative();
+ }
+
+ public BluetoothService(Context context) {
+ mContext = context;
+
+ // Need to do this in place of:
+ // mBatteryStats = BatteryStatsService.getService();
+ // Since we can not import BatteryStatsService from here. This class really needs to be
+ // moved to java/services/com/android/server/
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
+ initializeNativeDataNative();
+
+ if (isEnabledNative() == 1) {
+ Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
+ disableNative();
+ }
+
+ mBluetoothState = BluetoothAdapter.STATE_OFF;
+ mIsDiscovering = false;
+ mAdapterProperties = new HashMap<String, String>();
+ mDeviceProperties = new HashMap<String, Map<String,String>>();
+ registerForAirplaneMode();
+ }
+
+ public synchronized void initAfterRegistration() {
+ mAdapter = (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+ mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mIsAirplaneSensitive) {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ try {
+ cleanupNativeDataNative();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public boolean isEnabled() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothState == BluetoothAdapter.STATE_ON;
+ }
+
+ public int getBluetoothState() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBluetoothState;
+ }
+
+
+ /**
+ * Bring down bluetooth and disable BT in settings. Returns true on success.
+ */
+ public boolean disable() {
+ return disable(true);
+ }
+
+ /**
+ * Bring down bluetooth. Returns true on success.
+ *
+ * @param saveSetting If true, disable BT in settings
+ */
+ public synchronized boolean disable(boolean saveSetting) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+ switch (mBluetoothState) {
+ case BluetoothAdapter.STATE_OFF:
+ return true;
+ case BluetoothAdapter.STATE_ON:
+ break;
+ default:
+ return false;
+ }
+ if (mEnableThread != null && mEnableThread.isAlive()) {
+ return false;
+ }
+ setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
+
+ // Allow 3 seconds for profiles to gracefully disconnect
+ // TODO: Introduce a callback mechanism so that each profile can notify
+ // BluetoothService when it is done shutting down
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
+ return true;
+ }
+
+
+ private synchronized void finishDisable(boolean saveSetting) {
+ if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
+ return;
+ }
+ mEventLoop.stop();
+ tearDownNativeDataNative();
+ disableNative();
+
+ // mark in progress bondings as cancelled
+ for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
+ mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
+ BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
+ }
+
+ // update mode
+ Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+ mIsDiscovering = false;
+ mAdapterProperties.clear();
+
+ if (saveSetting) {
+ persistBluetoothOnSetting(false);
+ }
+
+ setBluetoothState(BluetoothAdapter.STATE_OFF);
+
+ // Log bluetooth off to battery stats.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteBluetoothOff();
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (mRestart) {
+ mRestart = false;
+ enable();
+ }
+ }
+
+ /** Bring up BT and persist BT on in settings */
+ public boolean enable() {
+ return enable(true);
+ }
+
+ /**
+ * Enable this Bluetooth device, asynchronously.
+ * This turns on/off the underlying hardware.
+ *
+ * @param saveSetting If true, persist the new state of BT in settings
+ * @return True on success (so far)
+ */
+ public synchronized boolean enable(boolean saveSetting) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+
+ // Airplane mode can prevent Bluetooth radio from being turned on.
+ if (mIsAirplaneSensitive && isAirplaneModeOn()) {
+ return false;
+ }
+ if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
+ return false;
+ }
+ if (mEnableThread != null && mEnableThread.isAlive()) {
+ return false;
+ }
+ setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
+ mEnableThread = new EnableThread(saveSetting);
+ mEnableThread.start();
+ return true;
+ }
+
+ /** Forcibly restart Bluetooth if it is on */
+ /* package */ synchronized void restart() {
+ if (mBluetoothState != BluetoothAdapter.STATE_ON) {
+ return;
+ }
+ mRestart = true;
+ if (!disable(false)) {
+ mRestart = false;
+ }
+ }
+
+ private synchronized void setBluetoothState(int state) {
+ if (state == mBluetoothState) {
+ return;
+ }
+
+ if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
+
+ Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
+ intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ mBluetoothState = state;
+
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MESSAGE_REGISTER_SDP_RECORDS:
+ //TODO: Don't assume HSP/HFP is running, don't use sdptool,
+ if (isEnabled()) {
+ SystemService.start("hsag");
+ SystemService.start("hfag");
+ SystemService.start("opush");
+ SystemService.start("pbap");
+ }
+ break;
+ case MESSAGE_FINISH_DISABLE:
+ finishDisable(msg.arg1 != 0);
+ break;
+ }
+ }
+ };
+
+ private EnableThread mEnableThread;
+
+ private class EnableThread extends Thread {
+ private final boolean mSaveSetting;
+ public EnableThread(boolean saveSetting) {
+ mSaveSetting = saveSetting;
+ }
+ public void run() {
+ boolean res = (enableNative() == 0);
+ if (res) {
+ int retryCount = 2;
+ boolean running = false;
+ while ((retryCount-- > 0) && !running) {
+ mEventLoop.start();
+ // it may take a momement for the other thread to do its
+ // thing. Check periodically for a while.
+ int pollCount = 5;
+ while ((pollCount-- > 0) && !running) {
+ if (mEventLoop.isEventLoopRunning()) {
+ running = true;
+ break;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {}
+ }
+ }
+ if (!running) {
+ log("bt EnableThread giving up");
+ res = false;
+ disableNative();
+ }
+ }
+
+
+ if (res) {
+ if (!setupNativeDataNative()) {
+ return;
+ }
+ if (mSaveSetting) {
+ persistBluetoothOnSetting(true);
+ }
+ mIsDiscovering = false;
+ mBondState.loadBondState();
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
+ 3000);
+
+ // Log bluetooth on to battery stats.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.noteBluetoothOn();
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ mEnableThread = null;
+
+ setBluetoothState(res ?
+ BluetoothAdapter.STATE_ON :
+ BluetoothAdapter.STATE_OFF);
+
+ if (res) {
+ // Update mode
+ String[] propVal = {"Pairable", getProperty("Pairable")};
+ mEventLoop.onPropertyChanged(propVal);
+ }
+
+ if (mIsAirplaneSensitive && isAirplaneModeOn()) {
+ disable(false);
+ }
+
+ }
+ }
+
+ private void persistBluetoothOnSetting(boolean bluetoothOn) {
+ long origCallerIdentityToken = Binder.clearCallingIdentity();
+ Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
+ bluetoothOn ? 1 : 0);
+ Binder.restoreCallingIdentity(origCallerIdentityToken);
+ }
+
+ /* package */ BondState getBondState() {
+ return mBondState;
+ }
+
+ /** local cache of bonding state.
+ /* we keep our own state to track the intermediate state BONDING, which
+ /* bluez does not track.
+ * All addreses must be passed in upper case.
+ */
+ public class BondState {
+ private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
+ private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
+ private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
+ // List of all the vendor_id prefix of Bluetooth addresses for
+ // which auto pairing is not attempted.
+ // The following companies are included in the list below:
+ // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
+ // Parrot, Zhongshan General K-mate Electronics, Great Well
+ // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
+ // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
+ // Continental Automotive, Harman/Becker
+ private final ArrayList<String> mAutoPairingBlacklisted =
+ new ArrayList<String>(Arrays.asList(
+ "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
+ "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
+ "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
+ "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59",
+ "00:0A:30", "00:1E:AE", "00:1C:D7"
+ ));
+
+ public synchronized void loadBondState() {
+ if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
+ return;
+ }
+ String []bonds = null;
+ String val = getProperty("Devices");
+ if (val != null) {
+ bonds = val.split(",");
+ }
+ if (bonds == null) {
+ return;
+ }
+ mState.clear();
+ if (DBG) log("found " + bonds.length + " bonded devices");
+ for (String device : bonds) {
+ mState.put(getAddressFromObjectPath(device).toUpperCase(),
+ BluetoothDevice.BOND_BONDED);
+ }
+ }
+
+ public synchronized void setBondState(String address, int state) {
+ setBondState(address, state, 0);
+ }
+
+ /** reason is ignored unless state == BOND_NOT_BONDED */
+ public synchronized void setBondState(String address, int state, int reason) {
+ int oldState = getBondState(address);
+ if (oldState == state) {
+ return;
+ }
+ if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
+ reason + ")");
+ Intent intent = new Intent(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
+ intent.putExtra(BluetoothIntent.DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothIntent.BOND_STATE, state);
+ intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState);
+ if (state == BluetoothDevice.BOND_NOT_BONDED) {
+ if (reason <= 0) {
+ Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
+ "invalid. Overriding reason code with BOND_RESULT_REMOVED");
+ reason = BluetoothDevice.UNBOND_REASON_REMOVED;
+ }
+ intent.putExtra(BluetoothIntent.REASON, reason);
+ mState.remove(address);
+ } else {
+ mState.put(address, state);
+ }
+
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ }
+
+ public boolean isAutoPairingBlacklisted(String address) {
+ for (String blacklistAddress : mAutoPairingBlacklisted) {
+ if (address.startsWith(blacklistAddress)) return true;
+ }
+ return false;
+ }
+
+ public synchronized int getBondState(String address) {
+ Integer state = mState.get(address);
+ if (state == null) {
+ return BluetoothDevice.BOND_NOT_BONDED;
+ }
+ return state.intValue();
+ }
+
+ private synchronized String[] listInState(int state) {
+ ArrayList<String> result = new ArrayList<String>(mState.size());
+ for (Map.Entry<String, Integer> e : mState.entrySet()) {
+ if (e.getValue().intValue() == state) {
+ result.add(e.getKey());
+ }
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ public synchronized void addAutoPairingFailure(String address) {
+ if (!mAutoPairingFailures.contains(address)) {
+ mAutoPairingFailures.add(address);
+ }
+ }
+
+ public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
+ return getAttempt(address) != 0;
+ }
+
+ public synchronized void clearPinAttempts(String address) {
+ mPinAttempt.remove(address);
+ }
+
+ public synchronized boolean hasAutoPairingFailed(String address) {
+ return mAutoPairingFailures.contains(address);
+ }
+
+ public synchronized int getAttempt(String address) {
+ Integer attempt = mPinAttempt.get(address);
+ if (attempt == null) {
+ return 0;
+ }
+ return attempt.intValue();
+ }
+
+ public synchronized void attempt(String address) {
+ Integer attempt = mPinAttempt.get(address);
+ int newAttempt;
+ if (attempt == null) {
+ newAttempt = 1;
+ } else {
+ newAttempt = attempt.intValue() + 1;
+ }
+ mPinAttempt.put(address, new Integer(newAttempt));
+ }
+
+ }
+
+ private static String toBondStateString(int bondState) {
+ switch (bondState) {
+ case BluetoothDevice.BOND_NOT_BONDED:
+ return "not bonded";
+ case BluetoothDevice.BOND_BONDING:
+ return "bonding";
+ case BluetoothDevice.BOND_BONDED:
+ return "bonded";
+ default:
+ return "??????";
+ }
+ }
+
+ /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
+ return mAdapterProperties.isEmpty();
+ }
+
+ /*package*/synchronized void getAllProperties() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ mAdapterProperties.clear();
+
+ String properties[] = (String [])getAdapterPropertiesNative();
+ // The String Array consists of key-value pairs.
+ if (properties == null) {
+ Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
+ return;
+ }
+
+ for (int i = 0; i < properties.length; i++) {
+ String name = properties[i];
+ String newValue = null;
+ int len;
+ if (name == null) {
+ Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
+ continue;
+ }
+ if (name.equals("Devices")) {
+ StringBuilder str = new StringBuilder();
+ len = Integer.valueOf(properties[++i]);
+ for (int j = 0; j < len; j++) {
+ str.append(properties[++i]);
+ str.append(",");
+ }
+ if (len > 0) {
+ newValue = str.toString();
+ }
+ } else {
+ newValue = properties[++i];
+ }
+ mAdapterProperties.put(name, newValue);
+ }
+
+ // Add adapter object path property.
+ String adapterPath = getAdapterPathNative();
+ if (adapterPath != null)
+ mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
+ }
+
+ /* package */ synchronized void setProperty(String name, String value) {
+ mAdapterProperties.put(name, value);
+ }
+
+ public synchronized boolean setName(String name) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (name == null) {
+ return false;
+ }
+ return setPropertyString("Name", name);
+ }
+
+ //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
+ // Either have a single property function with Object as the parameter
+ // or have a function for each property and then obfuscate in the JNI layer.
+ // The following looks dirty.
+ private boolean setPropertyString(String key, String value) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return setAdapterPropertyStringNative(key, value);
+ }
+
+ private boolean setPropertyInteger(String key, int value) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return setAdapterPropertyIntegerNative(key, value);
+ }
+
+ private boolean setPropertyBoolean(String key, boolean value) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
+ }
+
+ /**
+ * Set the discoverability window for the device. A timeout of zero
+ * makes the device permanently discoverable (if the device is
+ * discoverable). Setting the timeout to a nonzero value does not make
+ * a device discoverable; you need to call setMode() to make the device
+ * explicitly discoverable.
+ *
+ * @param timeout_s The discoverable timeout in seconds.
+ */
+ public synchronized boolean setDiscoverableTimeout(int timeout) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ return setPropertyInteger("DiscoverableTimeout", timeout);
+ }
+
+ public synchronized boolean setScanMode(int mode) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ boolean pairable = false;
+ boolean discoverable = false;
+ switch (mode) {
+ case BluetoothAdapter.SCAN_MODE_NONE:
+ pairable = false;
+ discoverable = false;
+ break;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+ pairable = true;
+ discoverable = false;
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+ pairable = true;
+ discoverable = true;
+ default:
+ Log.w(TAG, "Requested invalid scan mode " + mode);
+ return false;
+ }
+ setPropertyBoolean("Pairable", pairable);
+ setPropertyBoolean("Discoverable", discoverable);
+
+ return true;
+ }
+
+ /*package*/ synchronized String getProperty (String name) {
+ if (!mAdapterProperties.isEmpty())
+ return mAdapterProperties.get(name);
+ getAllProperties();
+ return mAdapterProperties.get(name);
+ }
+
+ public synchronized String getAddress() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return getProperty("Address");
+ }
+
+ public synchronized String getName() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return getProperty("Name");
+ }
+
+ /**
+ * Returns the user-friendly name of a remote device. This value is
+ * returned from our local cache, which is updated when onPropertyChange
+ * event is received.
+ * Do not expect to retrieve the updated remote name immediately after
+ * changing the name on the remote device.
+ *
+ * @param address Bluetooth address of remote device.
+ *
+ * @return The user-friendly name of the specified remote device.
+ */
+ public synchronized String getRemoteName(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return null;
+ }
+ Map <String, String> properties = mDeviceProperties.get(address);
+ if (properties != null) return properties.get("Name");
+ return null;
+ }
+
+ /**
+ * Get the discoverability window for the device. A timeout of zero
+ * means that the device is permanently discoverable (if the device is
+ * in the discoverable mode).
+ *
+ * @return The discoverability window of the device, in seconds. A negative
+ * value indicates an error.
+ */
+ public synchronized int getDiscoverableTimeout() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ String timeout = getProperty("DiscoverableTimeout");
+ if (timeout != null)
+ return Integer.valueOf(timeout);
+ else
+ return -1;
+ }
+
+ public synchronized int getScanMode() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!isEnabled())
+ return BluetoothAdapter.SCAN_MODE_NONE;
+
+ boolean pairable = getProperty("Pairable").equals("true");
+ boolean discoverable = getProperty("Discoverable").equals("true");
+ return bluezStringToScanMode (pairable, discoverable);
+ }
+
+ public synchronized boolean startDiscovery() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!isEnabled()) {
+ return false;
+ }
+ return startDiscoveryNative();
+ }
+
+ public synchronized boolean cancelDiscovery() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ return stopDiscoveryNative();
+ }
+
+ public synchronized boolean isDiscovering() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mIsDiscovering;
+ }
+
+ /* package */ void setIsDiscovering(boolean isDiscovering) {
+ mIsDiscovering = isDiscovering;
+ }
+
+ public synchronized boolean createBond(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ address = address.toUpperCase();
+
+ String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING);
+ if (bonding.length > 0 && !bonding[0].equals(address)) {
+ log("Ignoring createBond(): another device is bonding");
+ // a different device is currently bonding, fail
+ return false;
+ }
+
+ // Check for bond state only if we are not performing auto
+ // pairing exponential back-off attempts.
+ if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
+ mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) {
+ log("Ignoring createBond(): this device is already bonding or bonded");
+ return false;
+ }
+
+ if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
+ return false;
+ }
+
+ mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
+ return true;
+ }
+
+ public synchronized boolean cancelBondProcess(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ address = address.toUpperCase();
+ if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
+ return false;
+ }
+
+ mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
+ BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
+ cancelDeviceCreationNative(address);
+ return true;
+ }
+
+ public synchronized boolean removeBond(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ return removeDeviceNative(getObjectPathFromAddress(address));
+ }
+
+ public synchronized String[] listBonds() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return mBondState.listInState(BluetoothDevice.BOND_BONDED);
+ }
+
+ public synchronized int getBondState(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return BluetoothDevice.ERROR;
+ }
+ return mBondState.getBondState(address.toUpperCase());
+ }
+
+ /*package*/ boolean isRemoteDeviceInCache(String address) {
+ return (mDeviceProperties.get(address) != null);
+ }
+
+ /*package*/ String[] getRemoteDeviceProperties(String address) {
+ String objectPath = getObjectPathFromAddress(address);
+ return (String [])getDevicePropertiesNative(objectPath);
+ }
+
+ /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
+ Map<String, String> properties = mDeviceProperties.get(address);
+ if (properties != null) {
+ return properties.get(property);
+ } else {
+ // Query for remote device properties, again.
+ // We will need to reload the cache when we switch Bluetooth on / off
+ // or if we crash.
+ String[] propValues = getRemoteDeviceProperties(address);
+ if (propValues != null) {
+ addRemoteDeviceProperties(address, propValues);
+ return getRemoteDeviceProperty(address, property);
+ }
+ }
+ Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
+ return null;
+ }
+
+ /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
+ /*
+ * We get a DeviceFound signal every time RSSI changes or name changes.
+ * Don't create a new Map object every time */
+ Map<String, String> propertyValues = mDeviceProperties.get(address);
+ if (propertyValues == null) {
+ propertyValues = new HashMap<String, String>();
+ }
+
+ for (int i = 0; i < properties.length; i++) {
+ String name = properties[i];
+ String newValue = null;
+ int len;
+ if (name == null) {
+ Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
+ continue;
+ }
+ if (name.equals("UUIDs") || name.equals("Nodes")) {
+ StringBuilder str = new StringBuilder();
+ len = Integer.valueOf(properties[++i]);
+ for (int j = 0; j < len; j++) {
+ str.append(properties[++i]);
+ str.append(",");
+ }
+ if (len > 0) {
+ newValue = str.toString();
+ }
+ } else {
+ newValue = properties[++i];
+ }
+
+ propertyValues.put(name, newValue);
+ }
+ mDeviceProperties.put(address, propertyValues);
+ }
+
+ /* package */ void removeRemoteDeviceProperties(String address) {
+ mDeviceProperties.remove(address);
+ }
+
+ /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
+ String value) {
+ Map <String, String> propVal = mDeviceProperties.get(address);
+ if (propVal != null) {
+ propVal.put(name, value);
+ mDeviceProperties.put(address, propVal);
+ } else {
+ Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
+ }
+ }
+
+ /**
+ * Sets the remote device trust state.
+ *
+ * @return boolean to indicate operation success or fail
+ */
+ public synchronized boolean setTrust(String address, boolean value) {
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return false;
+ }
+
+ return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
+ value ? 1 : 0);
+ }
+
+ /**
+ * Gets the remote device trust state as boolean.
+ * Note: this value may be
+ * retrieved from cache if we retrieved the data before *
+ *
+ * @return boolean to indicate trust or untrust state
+ */
+ public synchronized boolean getTrustState(String address) {
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return false;
+ }
+
+ String val = getRemoteDeviceProperty(address, "Trusted");
+ if (val == null) {
+ return false;
+ } else {
+ return val.equals("true") ? true : false;
+ }
+ }
+
+ /**
+ * Gets the remote major, minor classes encoded as a 32-bit
+ * integer.
+ *
+ * Note: this value is retrieved from cache, because we get it during
+ * remote-device discovery.
+ *
+ * @return 32-bit integer encoding the remote major, minor, and service
+ * classes.
+ */
+ public synchronized int getRemoteClass(String address) {
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ return BluetoothClass.ERROR;
+ }
+ String val = getRemoteDeviceProperty(address, "Class");
+ if (val == null)
+ return BluetoothClass.ERROR;
+ else {
+ return Integer.valueOf(val);
+ }
+ }
+
+
+ /**
+ * Gets the remote features encoded as bit mask.
+ *
+ * Note: This method may be obsoleted soon.
+ *
+ * @return String array of 128bit UUIDs
+ */
+ public synchronized String[] getRemoteUuids(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return null;
+ }
+ String value = getRemoteDeviceProperty(address, "UUIDs");
+ String[] uuids = null;
+ // The UUIDs are stored as a "," separated string.
+ if (value != null)
+ uuids = value.split(",");
+ return uuids;
+ }
+
+ /**
+ * Gets the rfcomm channel associated with the UUID.
+ *
+ * @param address Address of the remote device
+ * @param uuid UUID of the service attribute
+ *
+ * @return rfcomm channel associated with the service attribute
+ */
+ public int getRemoteServiceChannel(String address, String uuid) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return BluetoothDevice.ERROR;
+ }
+ return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004);
+ }
+
+ public synchronized boolean setPin(String address, byte[] pin) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (pin == null || pin.length <= 0 || pin.length > 16 ||
+ !BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+ // bluez API wants pin as a string
+ String pinString;
+ try {
+ pinString = new String(pin, "UTF8");
+ } catch (UnsupportedEncodingException uee) {
+ Log.e(TAG, "UTF8 not supported?!?");
+ return false;
+ }
+ return setPinNative(address, pinString, data.intValue());
+ }
+
+ public synchronized boolean setPasskey(String address, int passkey) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (passkey < 0 || passkey > 999999 || !BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+ return setPasskeyNative(address, passkey, data.intValue());
+ }
+
+ public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
+ "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+ " or by bluez.\n");
+ return false;
+ }
+ return setPairingConfirmationNative(address, confirm, data.intValue());
+ }
+
+ public synchronized boolean cancelPairingUserInput(String address) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ if (!BluetoothDevice.checkBluetoothAddress(address)) {
+ return false;
+ }
+ mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
+ BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
+ address = address.toUpperCase();
+ Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+ if (data == null) {
+ Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
+ "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
+ "by the remote or by bluez.\n");
+ return false;
+ }
+ return cancelPairingUserInputNative(address, data.intValue());
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+ ContentResolver resolver = context.getContentResolver();
+ // Query the airplane mode from Settings.System just to make sure that
+ // some random app is not sending this intent and disabling bluetooth
+ boolean enabled = !isAirplaneModeOn();
+ // If bluetooth is currently expected to be on, then enable or disable bluetooth
+ if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
+ if (enabled) {
+ enable(false);
+ } else {
+ disable(false);
+ }
+ }
+ }
+ }
+ };
+
+ private void registerForAirplaneMode() {
+ String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_RADIOS);
+ mIsAirplaneSensitive = airplaneModeRadios == null
+ ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
+ if (mIsAirplaneSensitive) {
+ mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ mContext.registerReceiver(mReceiver, mIntentFilter);
+ }
+ }
+
+ /* Returns true if airplane mode is currently on */
+ private final boolean isAirplaneModeOn() {
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON, 0) == 1;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
+
+ switch(mBluetoothState) {
+ case BluetoothAdapter.STATE_OFF:
+ pw.println("\nBluetooth OFF\n");
+ return;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ pw.println("\nBluetooth TURNING ON\n");
+ return;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ pw.println("\nBluetooth TURNING OFF\n");
+ return;
+ case BluetoothAdapter.STATE_ON:
+ pw.println("\nBluetooth ON\n");
+ }
+
+ pw.println("\nLocal address = " + getAddress());
+ pw.println("\nLocal name = " + getName());
+ pw.println("\nisDiscovering() = " + isDiscovering());
+
+ BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
+
+ pw.println("\n--Known devices--");
+ for (String address : mDeviceProperties.keySet()) {
+ int bondState = mBondState.getBondState(address);
+ pw.printf("%s %10s (%d) %s\n", address,
+ toBondStateString(bondState),
+ mBondState.getAttempt(address),
+ getRemoteName(address));
+ if (bondState == BluetoothDevice.BOND_BONDED) {
+ String[] uuids = getRemoteUuids(address);
+ if (uuids == null) {
+ pw.printf("\tuuids = null\n");
+ } else {
+ for (String uuid : uuids) {
+ pw.printf("\t" + uuid + "\n");
+ }
+ }
+ }
+ }
+
+ String value = getProperty("Devices");
+ String[] devicesObjectPath = null;
+ if (value != null) {
+ devicesObjectPath = value.split(",");
+ }
+ pw.println("\n--ACL connected devices--");
+ for (String device : devicesObjectPath) {
+ pw.println(getAddressFromObjectPath(device));
+ }
+
+ // Rather not do this from here, but no-where else and I need this
+ // dump
+ pw.println("\n--Headset Service--");
+ switch (headset.getState()) {
+ case BluetoothHeadset.STATE_DISCONNECTED:
+ pw.println("getState() = STATE_DISCONNECTED");
+ break;
+ case BluetoothHeadset.STATE_CONNECTING:
+ pw.println("getState() = STATE_CONNECTING");
+ break;
+ case BluetoothHeadset.STATE_CONNECTED:
+ pw.println("getState() = STATE_CONNECTED");
+ break;
+ case BluetoothHeadset.STATE_ERROR:
+ pw.println("getState() = STATE_ERROR");
+ break;
+ }
+ pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset());
+ pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
+
+ headset.close();
+ }
+
+ /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
+ if (pairable && discoverable)
+ return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
+ else if (pairable && !discoverable)
+ return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
+ else
+ return BluetoothAdapter.SCAN_MODE_NONE;
+ }
+
+ /* package */ static String scanModeToBluezString(int mode) {
+ switch (mode) {
+ case BluetoothAdapter.SCAN_MODE_NONE:
+ return "off";
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
+ return "connectable";
+ case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+ return "discoverable";
+ }
+ return null;
+ }
+
+ /*package*/ String getAddressFromObjectPath(String objectPath) {
+ String adapterObjectPath = getProperty("ObjectPath");
+ if (adapterObjectPath == null || objectPath == null) {
+ Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+ " or deviceObjectPath:" + objectPath + " is null");
+ return null;
+ }
+ if (!objectPath.startsWith(adapterObjectPath)) {
+ Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
+ " is not a prefix of deviceObjectPath:" + objectPath +
+ "bluetoothd crashed ?");
+ return null;
+ }
+ String address = objectPath.substring(adapterObjectPath.length());
+ if (address != null) return address.replace('_', ':');
+
+ Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
+ return null;
+ }
+
+ /*package*/ String getObjectPathFromAddress(String address) {
+ String path = getProperty("ObjectPath");
+ if (path == null) {
+ Log.e(TAG, "Error: Object Path is null");
+ return null;
+ }
+ path = path + address.replace(":", "_");
+ return path;
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ private native static void classInitNative();
+ private native void initializeNativeDataNative();
+ private native boolean setupNativeDataNative();
+ private native boolean tearDownNativeDataNative();
+ private native void cleanupNativeDataNative();
+ private native String getAdapterPathNative();
+
+ private native int isEnabledNative();
+ private native int enableNative();
+ private native int disableNative();
+
+ private native Object[] getAdapterPropertiesNative();
+ private native Object[] getDevicePropertiesNative(String objectPath);
+ private native boolean setAdapterPropertyStringNative(String key, String value);
+ private native boolean setAdapterPropertyIntegerNative(String key, int value);
+ private native boolean setAdapterPropertyBooleanNative(String key, int value);
+
+ private native boolean startDiscoveryNative();
+ private native boolean stopDiscoveryNative();
+
+ private native boolean createPairedDeviceNative(String address, int timeout_ms);
+ private native boolean cancelDeviceCreationNative(String address);
+ private native boolean removeDeviceNative(String objectPath);
+ private native int getDeviceServiceChannelNative(String objectPath, String uuid,
+ int attributeId);
+
+ private native boolean cancelPairingUserInputNative(String address, int nativeData);
+ private native boolean setPinNative(String address, String pin, int nativeData);
+ private native boolean setPasskeyNative(String address, int passkey, int nativeData);
+ private native boolean setPairingConfirmationNative(String address, boolean confirm,
+ int nativeData);
+ private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
+
+}
diff --git a/core/java/android/server/search/SearchDialogWrapper.java b/core/java/android/server/search/SearchDialogWrapper.java
index 49718cb..9ee64af 100644
--- a/core/java/android/server/search/SearchDialogWrapper.java
+++ b/core/java/android/server/search/SearchDialogWrapper.java
@@ -58,6 +58,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
// data[KEY_INITIAL_QUERY]: initial query
// data[KEY_LAUNCH_ACTIVITY]: launch activity
// data[KEY_APP_SEARCH_DATA]: app search data
+ // data[KEY_TRIGGER]: 0 = false, 1 = true
private static final int MSG_START_SEARCH = 1;
// Takes no arguments
private static final int MSG_STOP_SEARCH = 2;
@@ -69,7 +70,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
private static final String KEY_INITIAL_QUERY = "q";
private static final String KEY_LAUNCH_ACTIVITY = "a";
private static final String KEY_APP_SEARCH_DATA = "d";
- private static final String KEY_IDENT= "i";
+ private static final String KEY_IDENT = "i";
+ private static final String KEY_TRIGGER = "t";
// Context used for getting search UI resources
private final Context mContext;
@@ -173,7 +175,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
final Bundle appSearchData,
final boolean globalSearch,
final ISearchManagerCallback searchManagerCallback,
- int ident) {
+ int ident,
+ boolean trigger) {
if (DBG) debug("startSearch()");
Message msg = Message.obtain();
msg.what = MSG_START_SEARCH;
@@ -185,6 +188,7 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
msgData.putParcelable(KEY_LAUNCH_ACTIVITY, launchActivity);
msgData.putBundle(KEY_APP_SEARCH_DATA, appSearchData);
msgData.putInt(KEY_IDENT, ident);
+ msgData.putInt(KEY_TRIGGER, trigger ? 1 : 0);
mSearchUiThread.sendMessage(msg);
// be a little more eager in setting this so isVisible will return the correct value if
// called immediately after startSearch
@@ -268,8 +272,9 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
boolean globalSearch = msg.arg2 != 0;
ISearchManagerCallback searchManagerCallback = (ISearchManagerCallback) msg.obj;
int ident = msgData.getInt(KEY_IDENT);
+ boolean trigger = msgData.getInt(KEY_TRIGGER) != 0;
performStartSearch(initialQuery, selectInitialQuery, launchActivity,
- appSearchData, globalSearch, searchManagerCallback, ident);
+ appSearchData, globalSearch, searchManagerCallback, ident, trigger);
}
}
@@ -284,7 +289,8 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
Bundle appSearchData,
boolean globalSearch,
ISearchManagerCallback searchManagerCallback,
- int ident) {
+ int ident,
+ boolean trigger) {
if (DBG) debug("performStartSearch()");
registerBroadcastReceiver();
@@ -301,6 +307,9 @@ implements DialogInterface.OnCancelListener, DialogInterface.OnDismissListener {
mSearchDialog.show(initialQuery, selectInitialQuery, launchActivity, appSearchData,
globalSearch);
mVisible = true;
+ if (trigger) {
+ mSearchDialog.launchQuerySearch();
+ }
}
/**
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index afed4a4..5e0b3d2 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -233,7 +233,31 @@ public class SearchManagerService extends ISearchManager.Stub {
appSearchData,
globalSearch,
searchManagerCallback,
- ident);
+ ident,
+ false); // don't trigger
+ }
+
+ /**
+ * Launches the search UI and triggers the search, as if the user had clicked on the
+ * search button within the dialog.
+ *
+ * @see SearchManager#triggerSearch(String, android.content.ComponentName, android.os.Bundle)
+ */
+ public void triggerSearch(String query,
+ ComponentName launchActivity,
+ Bundle appSearchData,
+ ISearchManagerCallback searchManagerCallback,
+ boolean globalSearch,
+ int ident) {
+ getSearchDialog().startSearch(
+ query,
+ false,
+ launchActivity,
+ appSearchData,
+ globalSearch,
+ searchManagerCallback,
+ ident,
+ true); // triger search after launching
}
/**
diff --git a/core/java/android/service/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
new file mode 100644
index 0000000..b09ccab
--- /dev/null
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+import android.os.ParcelFileDescriptor;
+import android.service.wallpaper.IWallpaperEngine;
+
+/**
+ * @hide
+ */
+interface IWallpaperConnection {
+ void attachEngine(IWallpaperEngine engine);
+ ParcelFileDescriptor setWallpaper(String name);
+}
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
new file mode 100644
index 0000000..bbd9dde
--- /dev/null
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+/**
+ * @hide
+ */
+oneway interface IWallpaperEngine {
+ void setDesiredSize(int width, int height);
+ void setVisibility(boolean visible);
+ void destroy();
+}
diff --git a/core/java/android/service/wallpaper/IWallpaperService.aidl b/core/java/android/service/wallpaper/IWallpaperService.aidl
new file mode 100644
index 0000000..bc7a1d7
--- /dev/null
+++ b/core/java/android/service/wallpaper/IWallpaperService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+import android.service.wallpaper.IWallpaperConnection;
+
+/**
+ * @hide
+ */
+oneway interface IWallpaperService {
+ void attach(IWallpaperConnection connection,
+ IBinder windowToken, int windowType, boolean isPreview,
+ int reqWidth, int reqHeight);
+}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
new file mode 100644
index 0000000..e5659d5
--- /dev/null
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -0,0 +1,740 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.wallpaper;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.view.BaseIWindow;
+import com.android.internal.view.BaseSurfaceHolder;
+
+import android.app.Service;
+import android.app.WallpaperManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.IWindowSession;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewRoot;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+
+/**
+ * A wallpaper service is responsible for showing a live wallpaper behind
+ * applications that would like to sit on top of it.
+ * @hide Live Wallpaper
+ */
+public abstract class WallpaperService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.wallpaper.WallpaperService";
+
+ /**
+ * Name under which a WallpaperService component publishes information
+ * about itself. This meta-data must reference an XML resource containing
+ * a <code>&lt;{@link android.R.styleable#Wallpaper wallpaper}&gt;</code>
+ * tag.
+ */
+ public static final String SERVICE_META_DATA = "android.service.wallpaper";
+
+ static final String TAG = "WallpaperService";
+ static final boolean DEBUG = false;
+
+ private static final int DO_ATTACH = 10;
+ private static final int DO_DETACH = 20;
+ private static final int DO_SET_DESIRED_SIZE = 30;
+
+ private static final int MSG_UPDATE_SURFACE = 10000;
+ private static final int MSG_VISIBILITY_CHANGED = 10010;
+ private static final int MSG_WALLPAPER_OFFSETS = 10020;
+ private static final int MSG_WINDOW_RESIZED = 10030;
+ private static final int MSG_TOUCH_EVENT = 10040;
+
+ /**
+ * The actual implementation of a wallpaper. A wallpaper service may
+ * have multiple instances running (for example as a real wallpaper
+ * and as a preview), each of which is represented by its own Engine
+ * instance. You must implement {@link WallpaperService#onCreateEngine()}
+ * to return your concrete Engine implementation.
+ */
+ public class Engine {
+ IWallpaperEngineWrapper mIWallpaperEngine;
+
+ // Copies from mIWallpaperEngine.
+ HandlerCaller mCaller;
+ IWallpaperConnection mConnection;
+ IBinder mWindowToken;
+
+ boolean mInitializing = true;
+ boolean mVisible;
+ boolean mScreenOn = true;
+ boolean mReportedVisible;
+ boolean mDestroyed;
+
+ // Current window state.
+ boolean mCreated;
+ boolean mIsCreating;
+ boolean mDrawingAllowed;
+ int mWidth;
+ int mHeight;
+ int mFormat;
+ int mType;
+ int mCurWidth;
+ int mCurHeight;
+ int mWindowFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ int mCurWindowFlags = mWindowFlags;
+ boolean mDestroyReportNeeded;
+ final Rect mVisibleInsets = new Rect();
+ final Rect mWinFrame = new Rect();
+ final Rect mContentInsets = new Rect();
+
+ final WindowManager.LayoutParams mLayout
+ = new WindowManager.LayoutParams();
+ IWindowSession mSession;
+
+ final Object mLock = new Object();
+ boolean mOffsetMessageEnqueued;
+ float mPendingXOffset;
+ float mPendingYOffset;
+ MotionEvent mPendingMove;
+
+ final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+ mScreenOn = true;
+ reportVisibility();
+ } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+ mScreenOn = false;
+ reportVisibility();
+ }
+ }
+ };
+
+ final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
+
+ @Override
+ public boolean onAllowLockCanvas() {
+ return mDrawingAllowed;
+ }
+
+ @Override
+ public void onRelayoutContainer() {
+ Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
+ mCaller.sendMessage(msg);
+ }
+
+ @Override
+ public void onUpdateSurface() {
+ Message msg = mCaller.obtainMessage(MSG_UPDATE_SURFACE);
+ mCaller.sendMessage(msg);
+ }
+
+ public boolean isCreating() {
+ return mIsCreating;
+ }
+
+ @Override
+ public void setFixedSize(int width, int height) {
+ throw new UnsupportedOperationException(
+ "Wallpapers currently only support sizing from layout");
+ }
+
+ public void setKeepScreenOn(boolean screenOn) {
+ throw new UnsupportedOperationException(
+ "Wallpapers do not support keep screen on");
+ }
+
+ };
+
+ final BaseIWindow mWindow = new BaseIWindow() {
+ @Override
+ public boolean onDispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
+ synchronized (mLock) {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (mPendingMove != null) {
+ mCaller.removeMessages(MSG_TOUCH_EVENT, mPendingMove);
+ mPendingMove.recycle();
+ }
+ mPendingMove = event;
+ } else {
+ mPendingMove = null;
+ }
+ Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT,
+ event);
+ mCaller.sendMessage(msg);
+ }
+ return false;
+ }
+
+ @Override
+ public void resized(int w, int h, Rect coveredInsets,
+ Rect visibleInsets, boolean reportDraw) {
+ Message msg = mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
+ reportDraw ? 1 : 0);
+ mCaller.sendMessage(msg);
+ }
+
+ @Override
+ public void dispatchAppVisibility(boolean visible) {
+ // We don't do this in preview mode; we'll let the preview
+ // activity tell us when to run.
+ if (!mIWallpaperEngine.mIsPreview) {
+ Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
+ visible ? 1 : 0);
+ mCaller.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void dispatchWallpaperOffsets(float x, float y) {
+ synchronized (mLock) {
+ mPendingXOffset = x;
+ mPendingYOffset = y;
+ if (!mOffsetMessageEnqueued) {
+ mOffsetMessageEnqueued = true;
+ Message msg = mCaller.obtainMessage(MSG_WALLPAPER_OFFSETS);
+ mCaller.sendMessage(msg);
+ }
+ }
+ }
+
+ };
+
+ /**
+ * Provides access to the surface in which this wallpaper is drawn.
+ */
+ public SurfaceHolder getSurfaceHolder() {
+ return mSurfaceHolder;
+ }
+
+ /**
+ * Convenience for {@link WallpaperManager#getDesiredMinimumWidth()
+ * WallpaperManager.getDesiredMinimumWidth()}, returning the width
+ * that the system would like this wallpaper to run in.
+ */
+ public int getDesiredMinimumWidth() {
+ return mIWallpaperEngine.mReqWidth;
+ }
+
+ /**
+ * Convenience for {@link WallpaperManager#getDesiredMinimumHeight()
+ * WallpaperManager.getDesiredMinimumHeight()}, returning the height
+ * that the system would like this wallpaper to run in.
+ */
+ public int getDesiredMinimumHeight() {
+ return mIWallpaperEngine.mReqHeight;
+ }
+
+ /**
+ * Return whether the wallpaper is currently visible to the user,
+ * this is the last value supplied to
+ * {@link #onVisibilityChanged(boolean)}.
+ */
+ public boolean isVisible() {
+ return mReportedVisible;
+ }
+
+ /**
+ * Returns true if this engine is running in preview mode -- that is,
+ * it is being shown to the user before they select it as the actual
+ * wallpaper.
+ */
+ public boolean isPreview() {
+ return mIWallpaperEngine.mIsPreview;
+ }
+
+ /**
+ * Control whether this wallpaper will receive raw touch events
+ * from the window manager as the user interacts with the window
+ * that is currently displaying the wallpaper. By default they
+ * are turned off. If enabled, the events will be received in
+ * {@link #onTouchEvent(MotionEvent)}.
+ */
+ public void setTouchEventsEnabled(boolean enabled) {
+ mWindowFlags = enabled
+ ? (mWindowFlags&~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+ : (mWindowFlags|WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+ if (mCreated) {
+ updateSurface(false, false);
+ }
+ }
+
+ /**
+ * Called once to initialize the engine. After returning, the
+ * engine's surface will be created by the framework.
+ */
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ }
+
+ /**
+ * Called right before the engine is going away. After this the
+ * surface will be destroyed and this Engine object is no longer
+ * valid.
+ */
+ public void onDestroy() {
+ }
+
+ /**
+ * Called to inform you of the wallpaper becoming visible or
+ * hidden. <em>It is very important that a wallpaper only use
+ * CPU while it is visible.</em>.
+ */
+ public void onVisibilityChanged(boolean visible) {
+ }
+
+ /**
+ * Called as the user performs touch-screen interaction with the
+ * window that is currently showing this wallpaper. Note that the
+ * events you receive here are driven by the actual application the
+ * user is interacting with, so if it is slow you will get viewer
+ * move events.
+ */
+ public void onTouchEvent(MotionEvent event) {
+ }
+
+ /**
+ * Called to inform you of the wallpaper's offsets changing
+ * within its contain, corresponding to the container's
+ * call to {@link WallpaperManager#setWallpaperOffsets(IBinder, float, float)
+ * WallpaperManager.setWallpaperOffsets()}.
+ */
+ public void onOffsetsChanged(float xOffset, float yOffset,
+ int xPixelOffset, int yPixelOffset) {
+ }
+
+ /**
+ * Called when an application has changed the desired virtual size of
+ * the wallpaper.
+ */
+ public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
+ }
+
+ /**
+ * Convenience for {@link SurfaceHolder.Callback#surfaceChanged
+ * SurfaceHolder.Callback.surfaceChanged()}.
+ */
+ public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ /**
+ * Convenience for {@link SurfaceHolder.Callback#surfaceCreated
+ * SurfaceHolder.Callback.surfaceCreated()}.
+ */
+ public void onSurfaceCreated(SurfaceHolder holder) {
+ }
+
+ /**
+ * Convenience for {@link SurfaceHolder.Callback#surfaceDestroyed
+ * SurfaceHolder.Callback.surfaceDestroyed()}.
+ */
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ }
+
+ void updateSurface(boolean forceRelayout, boolean forceReport) {
+ if (mDestroyed) {
+ Log.w(TAG, "Ignoring updateSurface: destroyed");
+ }
+
+ int myWidth = mSurfaceHolder.getRequestedWidth();
+ if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT;
+ int myHeight = mSurfaceHolder.getRequestedHeight();
+ if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT;
+
+ final boolean creating = !mCreated;
+ final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat();
+ boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
+ final boolean typeChanged = mType != mSurfaceHolder.getRequestedType();
+ final boolean flagsChanged = mCurWindowFlags != mWindowFlags;
+ if (forceRelayout || creating || formatChanged || sizeChanged
+ || typeChanged || flagsChanged) {
+
+ if (DEBUG) Log.v(TAG, "Changes: creating=" + creating
+ + " format=" + formatChanged + " size=" + sizeChanged);
+
+ try {
+ mWidth = myWidth;
+ mHeight = myHeight;
+ mFormat = mSurfaceHolder.getRequestedFormat();
+ mType = mSurfaceHolder.getRequestedType();
+
+ mLayout.x = 0;
+ mLayout.y = 0;
+ mLayout.width = myWidth;
+ mLayout.height = myHeight;
+
+ mLayout.format = mFormat;
+
+ mCurWindowFlags = mWindowFlags;
+ mLayout.flags = mWindowFlags
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ ;
+
+ mLayout.memoryType = mType;
+ mLayout.token = mWindowToken;
+
+ if (!mCreated) {
+ mLayout.type = mIWallpaperEngine.mWindowType;
+ mLayout.gravity = Gravity.LEFT|Gravity.TOP;
+ mLayout.setTitle(WallpaperService.this.getClass().getName());
+ mLayout.windowAnimations =
+ com.android.internal.R.style.Animation_Wallpaper;
+ mSession.add(mWindow, mLayout, View.VISIBLE, mContentInsets);
+ }
+
+ mSurfaceHolder.mSurfaceLock.lock();
+ mDrawingAllowed = true;
+
+ final int relayoutResult = mSession.relayout(
+ mWindow, mLayout, mWidth, mHeight,
+ View.VISIBLE, false, mWinFrame, mContentInsets,
+ mVisibleInsets, mSurfaceHolder.mSurface);
+
+ if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ + ", frame=" + mWinFrame);
+
+ int w = mWinFrame.width();
+ if (mCurWidth != w) {
+ sizeChanged = true;
+ mCurWidth = w;
+ }
+ int h = mWinFrame.height();
+ if (mCurHeight != h) {
+ sizeChanged = true;
+ mCurHeight = h;
+ }
+
+ mSurfaceHolder.mSurfaceLock.unlock();
+
+ try {
+ mDestroyReportNeeded = true;
+
+ SurfaceHolder.Callback callbacks[] = null;
+ synchronized (mSurfaceHolder.mCallbacks) {
+ final int N = mSurfaceHolder.mCallbacks.size();
+ if (N > 0) {
+ callbacks = new SurfaceHolder.Callback[N];
+ mSurfaceHolder.mCallbacks.toArray(callbacks);
+ }
+ }
+
+ if (!mCreated) {
+ mIsCreating = true;
+ if (DEBUG) Log.v(TAG, "onSurfaceCreated("
+ + mSurfaceHolder + "): " + this);
+ onSurfaceCreated(mSurfaceHolder);
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceCreated(mSurfaceHolder);
+ }
+ }
+ }
+ if (forceReport || creating || formatChanged || sizeChanged) {
+ if (DEBUG) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.w(TAG, "forceReport=" + forceReport + " creating=" + creating
+ + " formatChanged=" + formatChanged
+ + " sizeChanged=" + sizeChanged, e);
+ }
+ if (DEBUG) Log.v(TAG, "onSurfaceChanged("
+ + mSurfaceHolder + ", " + mFormat
+ + ", " + mCurWidth + ", " + mCurHeight
+ + "): " + this);
+ onSurfaceChanged(mSurfaceHolder, mFormat,
+ mCurWidth, mCurHeight);
+ if (callbacks != null) {
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceChanged(mSurfaceHolder, mFormat,
+ mCurWidth, mCurHeight);
+ }
+ }
+ }
+ } finally {
+ mIsCreating = false;
+ mCreated = true;
+ if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+ mSession.finishDrawing(mWindow);
+ }
+ }
+ } catch (RemoteException ex) {
+ }
+ if (DEBUG) Log.v(
+ TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+ " w=" + mLayout.width + " h=" + mLayout.height);
+ }
+ }
+
+ void attach(IWallpaperEngineWrapper wrapper) {
+ if (DEBUG) Log.v(TAG, "attach: " + this + " wrapper=" + wrapper);
+ if (mDestroyed) {
+ return;
+ }
+
+ mIWallpaperEngine = wrapper;
+ mCaller = wrapper.mCaller;
+ mConnection = wrapper.mConnection;
+ mWindowToken = wrapper.mWindowToken;
+ mSurfaceHolder.setSizeFromLayout();
+ mInitializing = true;
+ mSession = ViewRoot.getWindowSession(getMainLooper());
+ mWindow.setSession(mSession);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ registerReceiver(mReceiver, filter);
+
+ if (DEBUG) Log.v(TAG, "onCreate(): " + this);
+ onCreate(mSurfaceHolder);
+
+ mInitializing = false;
+ updateSurface(false, false);
+ }
+
+ void doDesiredSizeChanged(int desiredWidth, int desiredHeight) {
+ if (!mDestroyed) {
+ if (DEBUG) Log.v(TAG, "onDesiredSizeChanged("
+ + desiredWidth + "," + desiredHeight + "): " + this);
+ onDesiredSizeChanged(desiredWidth, desiredHeight);
+ }
+ }
+
+ void doVisibilityChanged(boolean visible) {
+ mVisible = visible;
+ reportVisibility();
+ }
+
+ void reportVisibility() {
+ if (!mDestroyed) {
+ boolean visible = mVisible && mScreenOn;
+ if (mReportedVisible != visible) {
+ mReportedVisible = visible;
+ if (DEBUG) Log.v(TAG, "onVisibilityChanged(" + visible
+ + "): " + this);
+ onVisibilityChanged(visible);
+ }
+ }
+ }
+
+ void doOffsetsChanged() {
+ if (mDestroyed) {
+ return;
+ }
+
+ float xOffset;
+ float yOffset;
+ synchronized (mLock) {
+ xOffset = mPendingXOffset;
+ yOffset = mPendingYOffset;
+ mOffsetMessageEnqueued = false;
+ }
+ if (DEBUG) Log.v(TAG, "Offsets change in " + this
+ + ": " + xOffset + "," + yOffset);
+ final int availw = mIWallpaperEngine.mReqWidth-mCurWidth;
+ final int xPixels = availw > 0 ? -(int)(availw*xOffset+.5f) : 0;
+ final int availh = mIWallpaperEngine.mReqHeight-mCurHeight;
+ final int yPixels = availh > 0 ? -(int)(availh*yOffset+.5f) : 0;
+ onOffsetsChanged(xOffset, yOffset, xPixels, yPixels);
+ }
+
+ void detach() {
+ mDestroyed = true;
+
+ if (mVisible) {
+ mVisible = false;
+ if (DEBUG) Log.v(TAG, "onVisibilityChanged(false): " + this);
+ onVisibilityChanged(false);
+ }
+
+ if (mDestroyReportNeeded) {
+ mDestroyReportNeeded = false;
+ SurfaceHolder.Callback callbacks[];
+ synchronized (mSurfaceHolder.mCallbacks) {
+ callbacks = new SurfaceHolder.Callback[
+ mSurfaceHolder.mCallbacks.size()];
+ mSurfaceHolder.mCallbacks.toArray(callbacks);
+ }
+ for (SurfaceHolder.Callback c : callbacks) {
+ c.surfaceDestroyed(mSurfaceHolder);
+ }
+ if (DEBUG) Log.v(TAG, "onSurfaceDestroyed("
+ + mSurfaceHolder + "): " + this);
+ onSurfaceDestroyed(mSurfaceHolder);
+ }
+
+ if (DEBUG) Log.v(TAG, "onDestroy(): " + this);
+ onDestroy();
+
+ unregisterReceiver(mReceiver);
+
+ if (mCreated) {
+ try {
+ mSession.remove(mWindow);
+ } catch (RemoteException e) {
+ }
+ mSurfaceHolder.mSurface.release();
+ mCreated = false;
+ }
+ }
+ }
+
+ class IWallpaperEngineWrapper extends IWallpaperEngine.Stub
+ implements HandlerCaller.Callback {
+ private final HandlerCaller mCaller;
+
+ final IWallpaperConnection mConnection;
+ final IBinder mWindowToken;
+ final int mWindowType;
+ final boolean mIsPreview;
+ int mReqWidth;
+ int mReqHeight;
+
+ Engine mEngine;
+
+ IWallpaperEngineWrapper(WallpaperService context,
+ IWallpaperConnection conn, IBinder windowToken,
+ int windowType, boolean isPreview, int reqWidth, int reqHeight) {
+ mCaller = new HandlerCaller(context, this);
+ mConnection = conn;
+ mWindowToken = windowToken;
+ mWindowType = windowType;
+ mIsPreview = isPreview;
+ mReqWidth = reqWidth;
+ mReqHeight = reqHeight;
+
+ Message msg = mCaller.obtainMessage(DO_ATTACH);
+ mCaller.sendMessage(msg);
+ }
+
+ public void setDesiredSize(int width, int height) {
+ Message msg = mCaller.obtainMessageII(DO_SET_DESIRED_SIZE, width, height);
+ mCaller.sendMessage(msg);
+ }
+
+ public void setVisibility(boolean visible) {
+ Message msg = mCaller.obtainMessageI(MSG_VISIBILITY_CHANGED,
+ visible ? 1 : 0);
+ mCaller.sendMessage(msg);
+ }
+
+ public void destroy() {
+ Message msg = mCaller.obtainMessage(DO_DETACH);
+ mCaller.sendMessage(msg);
+ }
+
+ public void executeMessage(Message message) {
+ switch (message.what) {
+ case DO_ATTACH: {
+ try {
+ mConnection.attachEngine(this);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Wallpaper host disappeared", e);
+ return;
+ }
+ Engine engine = onCreateEngine();
+ mEngine = engine;
+ engine.attach(this);
+ return;
+ }
+ case DO_DETACH: {
+ mEngine.detach();
+ return;
+ }
+ case DO_SET_DESIRED_SIZE: {
+ mEngine.doDesiredSizeChanged(message.arg1, message.arg2);
+ return;
+ }
+ case MSG_UPDATE_SURFACE:
+ mEngine.updateSurface(true, false);
+ break;
+ case MSG_VISIBILITY_CHANGED:
+ if (DEBUG) Log.v(TAG, "Visibility change in " + mEngine
+ + ": " + message.arg1);
+ mEngine.doVisibilityChanged(message.arg1 != 0);
+ break;
+ case MSG_WALLPAPER_OFFSETS: {
+ mEngine.doOffsetsChanged();
+ } break;
+ case MSG_WINDOW_RESIZED: {
+ final boolean reportDraw = message.arg1 != 0;
+ mEngine.updateSurface(true, false);
+ if (reportDraw) {
+ try {
+ mEngine.mSession.finishDrawing(mEngine.mWindow);
+ } catch (RemoteException e) {
+ }
+ }
+ } break;
+ case MSG_TOUCH_EVENT: {
+ MotionEvent ev = (MotionEvent)message.obj;
+ synchronized (mEngine.mLock) {
+ if (mEngine.mPendingMove == ev) {
+ mEngine.mPendingMove = null;
+ }
+ }
+ mEngine.onTouchEvent(ev);
+ ev.recycle();
+ } break;
+ default :
+ Log.w(TAG, "Unknown message type " + message.what);
+ }
+ }
+ }
+
+ /**
+ * Implements the internal {@link IWallpaperService} interface to convert
+ * incoming calls to it back to calls on an {@link WallpaperService}.
+ */
+ class IWallpaperServiceWrapper extends IWallpaperService.Stub {
+ private final WallpaperService mTarget;
+
+ public IWallpaperServiceWrapper(WallpaperService context) {
+ mTarget = context;
+ }
+
+ public void attach(IWallpaperConnection conn, IBinder windowToken,
+ int windowType, boolean isPreview, int reqWidth, int reqHeight) {
+ new IWallpaperEngineWrapper(mTarget, conn, windowToken,
+ windowType, isPreview, reqWidth, reqHeight);
+ }
+ }
+
+ /**
+ * Implement to return the implementation of the internal accessibility
+ * service interface. Subclasses should not override.
+ */
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IWallpaperServiceWrapper(this);
+ }
+
+ public abstract Engine onCreateEngine();
+}
diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl
deleted file mode 100644
index 2da2258..0000000
--- a/core/java/android/speech/IRecognitionListener.aidl
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.speech;
-
-import android.os.Bundle;
-import android.speech.RecognitionResult;
-
-/**
- * Listener for speech recognition events, used with RecognitionService.
- * This gives you both the final recognition results, as well as various
- * intermediate events that can be used to show visual feedback to the user.
- * {@hide}
- */
-interface IRecognitionListener {
- /** Called when the endpointer is ready for the user to start speaking. */
- void onReadyForSpeech(in Bundle noiseParams);
-
- /** The user has started to speak. */
- void onBeginningOfSpeech();
-
- /** The sound level in the audio stream has changed. */
- void onRmsChanged(in float rmsdB);
-
- /**
- * More sound has been received. Buffer is a byte buffer containing
- * a sequence of 16-bit shorts.
- */
- void onBufferReceived(in byte[] buffer);
-
- /** Called after the user stops speaking. */
- void onEndOfSpeech();
-
- /**
- * A network or recognition error occurred. The code is defined in
- * {@link android.speech.RecognitionResult}
- */
- void onError(in int error);
-
- /**
- * Called when recognition results are ready.
- * @param results: an ordered list of the most likely results (N-best list).
- * @param key: a key associated with the results. The same results can
- * be retrieved asynchronously later using the key, if available.
- */
- void onResults(in List<RecognitionResult> results, long key);
-}
diff --git a/core/java/android/speech/IRecognitionService.aidl b/core/java/android/speech/IRecognitionService.aidl
deleted file mode 100644
index a18c380..0000000
--- a/core/java/android/speech/IRecognitionService.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.speech;
-
-import android.content.Intent;
-import android.speech.IRecognitionListener;
-import android.speech.RecognitionResult;
-
-// A Service interface to speech recognition. Call startListening when
-// you want to begin capturing audio; RecognitionService will automatically
-// determine when the user has finished speaking, stream the audio to the
-// recognition servers, and notify you when results are ready.
-/** {@hide} */
-interface IRecognitionService {
- // Start listening for speech. Can only call this from one thread at once.
- // see RecognizerIntent.java for constants used to specify the intent.
- void startListening(in Intent recognizerIntent,
- in IRecognitionListener listener);
-
- List<RecognitionResult> getRecognitionResults(in long key);
-
- void cancel();
-}
diff --git a/core/java/android/speech/RecognitionResult.aidl b/core/java/android/speech/RecognitionResult.aidl
deleted file mode 100644
index 59e53ab..0000000
--- a/core/java/android/speech/RecognitionResult.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.speech;
-
-parcelable RecognitionResult;
diff --git a/core/java/android/speech/RecognitionResult.java b/core/java/android/speech/RecognitionResult.java
deleted file mode 100644
index 978106b..0000000
--- a/core/java/android/speech/RecognitionResult.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2008 Google Inc.
- *
- * 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.speech;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * RecognitionResult is a passive object that stores a single recognized
- * query and its search result.
- * TODO: revisit and improve. May be we should have a separate result
- * object for each type, and put them (type/value) in bundle?
- *
- * {@hide}
- */
-public class RecognitionResult implements Parcelable {
- /**
- * Status of the recognize request.
- */
- public static final int NETWORK_TIMEOUT = 1; // Network operation timed out.
- public static final int NETWORK_ERROR = 2; // Other networkrelated errors.
- public static final int AUDIO_ERROR = 3; // Audio recording error.
- public static final int SERVER_ERROR = 4; // Server sends error status.
- public static final int CLIENT_ERROR = 5; // Other client side errors.
- public static final int SPEECH_TIMEOUT = 6; // No speech input
- public static final int NO_MATCH = 7; // No recognition result matched.
- public static final int SERVICE_BUSY = 8; // RecognitionService busy.
-
- /**
- * Type of the recognition results.
- */
- public static final int RAW_RECOGNITION_RESULT = 0;
- public static final int WEB_SEARCH_RESULT = 1;
- public static final int CONTACT_RESULT = 2;
-
- /**
- * A factory method to create a raw RecognitionResult
- *
- * @param sentence the recognized text.
- */
- public static RecognitionResult newRawRecognitionResult(String sentence) {
- return new RecognitionResult(RAW_RECOGNITION_RESULT, sentence, null, null);
- }
-
- /**
- * A factory method to create RecognitionResult for contacts.
- *
- * @param contact the contact name.
- * @param phoneType the phone type.
- * @param callAction whether this result included a command to "call", or just the contact name.
- */
- public static RecognitionResult newContactResult(String contact, int phoneType,
- boolean callAction) {
- return new RecognitionResult(CONTACT_RESULT, contact, phoneType, callAction);
- }
-
- /**
- * A factory method to create a RecognitionResult for Web Search Query.
- *
- * @param query the query string.
- * @param html the html page of the search result.
- * @param url the url that performs the search with the query.
- */
- public static RecognitionResult newWebResult(String query, String html, String url) {
- return new RecognitionResult(WEB_SEARCH_RESULT, query, html, url);
- }
-
- public static final Parcelable.Creator<RecognitionResult> CREATOR
- = new Parcelable.Creator<RecognitionResult>() {
-
- public RecognitionResult createFromParcel(Parcel in) {
- return new RecognitionResult(in);
- }
-
- public RecognitionResult[] newArray(int size) {
- return new RecognitionResult[size];
- }
- };
-
- /**
- * Result type.
- */
- public final int mResultType;
-
- /**
- * The recognized string when mResultType is WEB_SEARCH_RESULT.
- * The name of the contact when mResultType is CONTACT_RESULT.
- */
- public final String mText;
-
- /**
- * The HTML result page for the query. If this is null, then the
- * application must use the url field to get the HTML result page.
- */
- public final String mHtml;
-
- /**
- * The url to get the result page for the query string. The
- * application must use this url instead of performing the search
- * with the query.
- */
- public final String mUrl;
-
- /**
- * Phone number type. This is valid only when mResultType == CONTACT_RESULT.
- */
- public final int mPhoneType;
-
- /**
- * Whether a contact recognition result included a command to "call". This is valid only
- * when mResultType == CONTACT_RESULT.
- */
- public final boolean mCallAction;
-
- private RecognitionResult(int type, String query, String html, String url) {
- mResultType = type;
- mText = query;
- mHtml = html;
- mUrl = url;
- mPhoneType = -1;
- mCallAction = false;
- }
-
- private RecognitionResult(int type, String query, int phoneType, boolean callAction) {
- mResultType = type;
- mText = query;
- mPhoneType = phoneType;
- mHtml = null;
- mUrl = null;
- mCallAction = callAction;
- }
-
- private RecognitionResult(Parcel in) {
- mResultType = in.readInt();
- mText = in.readString();
- mHtml= in.readString();
- mUrl= in.readString();
- mPhoneType = in.readInt();
- mCallAction = (in.readInt() == 1);
- }
-
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(mResultType);
- out.writeString(mText);
- out.writeString(mHtml);
- out.writeString(mUrl);
- out.writeInt(mPhoneType);
- out.writeInt(mCallAction ? 1 : 0);
- }
-
-
- @Override
- public String toString() {
- String resultType[] = { "RAW", "WEB", "CONTACT" };
- return "[type=" + resultType[mResultType] +
- ", text=" + mText+ ", mUrl=" + mUrl + ", html=" + mHtml + "]";
- }
-
- public int describeContents() {
- // no special description
- return 0;
- }
-}
diff --git a/core/java/android/speech/RecognitionServiceUtil.java b/core/java/android/speech/RecognitionServiceUtil.java
deleted file mode 100644
index a8c7868..0000000
--- a/core/java/android/speech/RecognitionServiceUtil.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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.speech;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.speech.RecognitionResult;
-import android.util.Log;
-
-import java.util.List;
-
-/**
- * Utils for Google's network-based speech recognizer, which lets you perform
- * speech-to-text translation through RecognitionService. IRecognitionService
- * and IRecognitionListener are the core interfaces; you begin recognition
- * through IRecognitionService and subscribe to callbacks about when the user
- * stopped speaking, results come in, errors, etc. through IRecognitionListener.
- * RecognitionServiceUtil includes default IRecognitionListener and
- * ServiceConnection implementations to reduce the amount of boilerplate.
- *
- * The Service provides no user interface. See RecognitionActivity if you
- * want the standard voice search UI.
- *
- * Below is a small skeleton of how to use the recognizer:
- *
- * ServiceConnection conn = new RecognitionServiceUtil.Connection();
- * mContext.bindService(RecognitionServiceUtil.sDefaultIntent,
- * conn, Context.BIND_AUTO_CREATE);
- * IRecognitionListener listener = new RecognitionServiceWrapper.NullListener() {
- * public void onResults(List<String> results) {
- * // Do something with recognition transcripts
- * }
- * }
- *
- * // Must wait for conn.mService to be populated, then call below
- * conn.mService.startListening(null, listener);
- *
- * {@hide}
- */
-public class RecognitionServiceUtil {
- public static final Intent sDefaultIntent = new Intent(
- RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
-
- // Recognize request parameters
- public static final String USE_LOCATION = "useLocation";
- public static final String CONTACT_AUTH_TOKEN = "contactAuthToken";
-
- // Bundles
- public static final String NOISE_LEVEL = "NoiseLevel";
- public static final String SIGNAL_NOISE_RATIO = "SignalNoiseRatio";
-
- private RecognitionServiceUtil() {}
-
- /**
- * IRecognitionListener which does nothing in response to recognition
- * callbacks. You can subclass from this and override only the methods
- * whose events you want to respond to.
- */
- public static class NullListener extends IRecognitionListener.Stub {
- public void onReadyForSpeech(Bundle bundle) {}
- public void onBeginningOfSpeech() {}
- public void onRmsChanged(float rmsdB) {}
- public void onBufferReceived(byte[] buf) {}
- public void onEndOfSpeech() {}
- public void onError(int error) {}
- public void onResults(List<RecognitionResult> results, long key) {}
- }
-
- /**
- * Basic ServiceConnection which just records mService variable.
- */
- public static class Connection implements ServiceConnection {
- public IRecognitionService mService;
-
- public synchronized void onServiceConnected(ComponentName name, IBinder service) {
- mService = IRecognitionService.Stub.asInterface(service);
- }
-
- public void onServiceDisconnected(ComponentName name) {
- mService = null;
- }
- }
-}
diff --git a/core/java/android/syncml/pim/PropertyNode.java b/core/java/android/syncml/pim/PropertyNode.java
index 983ecb8..3a5c994 100644
--- a/core/java/android/syncml/pim/PropertyNode.java
+++ b/core/java/android/syncml/pim/PropertyNode.java
@@ -28,6 +28,7 @@ import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Pattern;
+@Deprecated
public class PropertyNode {
public String propName;
diff --git a/core/java/android/syncml/pim/VBuilder.java b/core/java/android/syncml/pim/VBuilder.java
index 4528645..b6cb674 100644
--- a/core/java/android/syncml/pim/VBuilder.java
+++ b/core/java/android/syncml/pim/VBuilder.java
@@ -18,6 +18,7 @@ package android.syncml.pim;
import java.util.List;
+@Deprecated
public interface VBuilder {
void start();
diff --git a/core/java/android/syncml/pim/VBuilderCollection.java b/core/java/android/syncml/pim/VBuilderCollection.java
index f09c1c4..06e3100 100644
--- a/core/java/android/syncml/pim/VBuilderCollection.java
+++ b/core/java/android/syncml/pim/VBuilderCollection.java
@@ -19,6 +19,7 @@ package android.syncml.pim;
import java.util.Collection;
import java.util.List;
+@Deprecated
public class VBuilderCollection implements VBuilder {
private final Collection<VBuilder> mVBuilderCollection;
diff --git a/core/java/android/syncml/pim/VDataBuilder.java b/core/java/android/syncml/pim/VDataBuilder.java
index f6e5b65..db8a299 100644
--- a/core/java/android/syncml/pim/VDataBuilder.java
+++ b/core/java/android/syncml/pim/VDataBuilder.java
@@ -36,6 +36,7 @@ import java.util.List;
* VNode: standy by a vcard instance.
* PropertyNode: standy by a property line of a card.
*/
+@Deprecated
public class VDataBuilder implements VBuilder {
static private String LOG_TAG = "VDATABuilder";
diff --git a/core/java/android/syncml/pim/VNode.java b/core/java/android/syncml/pim/VNode.java
index 9015415..378a9d1 100644
--- a/core/java/android/syncml/pim/VNode.java
+++ b/core/java/android/syncml/pim/VNode.java
@@ -18,6 +18,7 @@ package android.syncml.pim;
import java.util.ArrayList;
+@Deprecated
public class VNode {
public String VName;
diff --git a/core/java/android/syncml/pim/VParser.java b/core/java/android/syncml/pim/VParser.java
index 57c5f7a..14d2875 100644
--- a/core/java/android/syncml/pim/VParser.java
+++ b/core/java/android/syncml/pim/VParser.java
@@ -25,6 +25,7 @@ import java.io.UnsupportedEncodingException;
* This interface is used to parse the V format files, such as VCard & VCal
*
*/
+@Deprecated
abstract public class VParser {
// Assume that "iso-8859-1" is able to map "all" 8bit characters to some unicode and
// decode the unicode to the original charset. If not, this setting will cause some bug.
diff --git a/core/java/android/syncml/pim/vcalendar/CalendarStruct.java b/core/java/android/syncml/pim/vcalendar/CalendarStruct.java
deleted file mode 100644
index 3388ada..0000000
--- a/core/java/android/syncml/pim/vcalendar/CalendarStruct.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-import java.util.List;
-import java.util.ArrayList;
-
-/**
- * Same comment as ContactStruct.
- */
-public class CalendarStruct{
-
- public static class EventStruct{
- public String description;
- public String dtend;
- public String dtstart;
- public String duration;
- public String has_alarm;
- public String last_date;
- public String rrule;
- public String status;
- public String title;
- public String event_location;
- public String uid;
- public List<String> reminderList;
-
- public void addReminderList(String method){
- if(reminderList == null)
- reminderList = new ArrayList<String>();
- reminderList.add(method);
- }
- }
-
- public String timezone;
- public List<EventStruct> eventList;
-
- public void addEventList(EventStruct stru){
- if(eventList == null)
- eventList = new ArrayList<EventStruct>();
- eventList.add(stru);
- }
-}
diff --git a/core/java/android/syncml/pim/vcalendar/VCalComposer.java b/core/java/android/syncml/pim/vcalendar/VCalComposer.java
deleted file mode 100644
index 18b6719..0000000
--- a/core/java/android/syncml/pim/vcalendar/VCalComposer.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-/**
- * vCalendar string composer class
- */
-public class VCalComposer {
-
- public final static String VERSION_VCALENDAR10 = "vcalendar1.0";
- public final static String VERSION_VCALENDAR20 = "vcalendar2.0";
-
- public final static int VERSION_VCAL10_INT = 1;
- public final static int VERSION_VCAL20_INT = 2;
-
- private static String mNewLine = "\r\n";
- private String mVersion = null;
-
- public VCalComposer() {
- }
-
- /**
- * Create a vCalendar String.
- * @param struct see more from CalendarStruct class
- * @param vcalversion MUST be VERSION_VCAL10 /VERSION_VCAL20
- * @return vCalendar string
- * @throws VcalException if version is invalid or create failed
- */
- public String createVCal(CalendarStruct struct, int vcalversion)
- throws VCalException{
-
- StringBuilder returnStr = new StringBuilder();
-
- //Version check
- if(vcalversion != 1 && vcalversion != 2)
- throw new VCalException("version not match 1.0 or 2.0.");
- if (vcalversion == 1)
- mVersion = VERSION_VCALENDAR10;
- else
- mVersion = VERSION_VCALENDAR20;
-
- //Build vCalendar:
- returnStr.append("BEGIN:VCALENDAR").append(mNewLine);
-
- if(vcalversion == VERSION_VCAL10_INT)
- returnStr.append("VERSION:1.0").append(mNewLine);
- else
- returnStr.append("VERSION:2.0").append(mNewLine);
-
- returnStr.append("PRODID:vCal ID default").append(mNewLine);
-
- if(!isNull(struct.timezone)){
- if(vcalversion == VERSION_VCAL10_INT)
- returnStr.append("TZ:").append(struct.timezone).append(mNewLine);
- else//down here MUST have
- returnStr.append("BEGIN:VTIMEZONE").append(mNewLine).
- append("TZID:vCal default").append(mNewLine).
- append("BEGIN:STANDARD").append(mNewLine).
- append("DTSTART:16010101T000000").append(mNewLine).
- append("TZOFFSETFROM:").append(struct.timezone).append(mNewLine).
- append("TZOFFSETTO:").append(struct.timezone).append(mNewLine).
- append("END:STANDARD").append(mNewLine).
- append("END:VTIMEZONE").append(mNewLine);
- }
- //Build VEVNET
- for(int i = 0; i < struct.eventList.size(); i++){
- String str = buildEventStr( struct.eventList.get(i) );
- returnStr.append(str);
- }
-
- //Build VTODO
- //TODO
-
- returnStr.append("END:VCALENDAR").append(mNewLine).append(mNewLine);
-
- return returnStr.toString();
- }
-
- private String buildEventStr(CalendarStruct.EventStruct stru){
-
- StringBuilder strbuf = new StringBuilder();
-
- strbuf.append("BEGIN:VEVENT").append(mNewLine);
-
- if(!isNull(stru.uid))
- strbuf.append("UID:").append(stru.uid).append(mNewLine);
-
- if(!isNull(stru.description))
- strbuf.append("DESCRIPTION:").
- append(foldingString(stru.description)).append(mNewLine);
-
- if(!isNull(stru.dtend))
- strbuf.append("DTEND:").append(stru.dtend).append(mNewLine);
-
- if(!isNull(stru.dtstart))
- strbuf.append("DTSTART:").append(stru.dtstart).append(mNewLine);
-
- if(!isNull(stru.duration))
- strbuf.append("DUE:").append(stru.duration).append(mNewLine);
-
- if(!isNull(stru.event_location))
- strbuf.append("LOCATION:").append(stru.event_location).append(mNewLine);
-
- if(!isNull(stru.last_date))
- strbuf.append("COMPLETED:").append(stru.last_date).append(mNewLine);
-
- if(!isNull(stru.rrule))
- strbuf.append("RRULE:").append(stru.rrule).append(mNewLine);
-
- if(!isNull(stru.title))
- strbuf.append("SUMMARY:").append(stru.title).append(mNewLine);
-
- if(!isNull(stru.status)){
- String stat = "TENTATIVE";
- switch (Integer.parseInt(stru.status)){
- case 0://Calendar.Calendars.STATUS_TENTATIVE
- stat = "TENTATIVE";
- break;
- case 1://Calendar.Calendars.STATUS_CONFIRMED
- stat = "CONFIRMED";
- break;
- case 2://Calendar.Calendars.STATUS_CANCELED
- stat = "CANCELLED";
- break;
- }
- strbuf.append("STATUS:").append(stat).append(mNewLine);
- }
- //Alarm
- if(!isNull(stru.has_alarm)
- && stru.reminderList != null
- && stru.reminderList.size() > 0){
-
- if (mVersion.equals(VERSION_VCALENDAR10)){
- String prefix = "";
- for(String method : stru.reminderList){
- switch (Integer.parseInt(method)){
- case 0:
- prefix = "DALARM";
- break;
- case 1:
- prefix = "AALARM";
- break;
- case 2:
- prefix = "MALARM";
- break;
- case 3:
- default:
- prefix = "DALARM";
- break;
- }
- strbuf.append(prefix).append(":default").append(mNewLine);
- }
- }else {//version 2.0 only support audio-method now.
- strbuf.append("BEGIN:VALARM").append(mNewLine).
- append("ACTION:AUDIO").append(mNewLine).
- append("TRIGGER:-PT10M").append(mNewLine).
- append("END:VALARM").append(mNewLine);
- }
- }
- strbuf.append("END:VEVENT").append(mNewLine);
- return strbuf.toString();
- }
-
- /** Alter str to folding supported format. */
- private String foldingString(String str){
- return str.replaceAll("\r\n", "\n").replaceAll("\n", "\r\n ");
- }
-
- /** is null */
- private boolean isNull(String str){
- if(str == null || str.trim().equals(""))
- return true;
- return false;
- }
-}
diff --git a/core/java/android/syncml/pim/vcalendar/VCalException.java b/core/java/android/syncml/pim/vcalendar/VCalException.java
deleted file mode 100644
index 48ea134..0000000
--- a/core/java/android/syncml/pim/vcalendar/VCalException.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-public class VCalException extends java.lang.Exception{
- // constructors
-
- /**
- * Constructs a VCalException object
- */
-
- public VCalException()
- {
- }
-
- /**
- * Constructs a VCalException object
- *
- * @param message the error message
- */
-
- public VCalException( String message )
- {
- super( message );
- }
-
-}
diff --git a/core/java/android/syncml/pim/vcalendar/VCalParser.java b/core/java/android/syncml/pim/vcalendar/VCalParser.java
deleted file mode 100644
index bc2d598..0000000
--- a/core/java/android/syncml/pim/vcalendar/VCalParser.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import android.util.Config;
-import android.util.Log;
-
-import android.syncml.pim.VDataBuilder;
-import android.syncml.pim.VParser;
-
-public class VCalParser{
-
- private final static String TAG = "VCalParser";
-
- public final static String VERSION_VCALENDAR10 = "vcalendar1.0";
- public final static String VERSION_VCALENDAR20 = "vcalendar2.0";
-
- private VParser mParser = null;
- private String mVersion = null;
-
- public VCalParser() {
- }
-
- public boolean parse(String vcalendarStr, VDataBuilder builder)
- throws VCalException {
-
- vcalendarStr = verifyVCal(vcalendarStr);
- try{
- boolean isSuccess = mParser.parse(
- new ByteArrayInputStream(vcalendarStr.getBytes()),
- "US-ASCII", builder);
-
- if (!isSuccess) {
- if (mVersion.equals(VERSION_VCALENDAR10)) {
- if(Config.LOGD)
- Log.d(TAG, "Parse failed for vCal 1.0 parser."
- + " Try to use 2.0 parser.");
- mVersion = VERSION_VCALENDAR20;
- return parse(vcalendarStr, builder);
- }else
- throw new VCalException("parse failed.(even use 2.0 parser)");
- }
- }catch (IOException e){
- throw new VCalException(e.getMessage());
- }
- return true;
- }
-
- /**
- * Verify vCalendar string, and initialize mVersion according to it.
- * */
- private String verifyVCal(String vcalStr) {
-
- //Version check
- judgeVersion(vcalStr);
-
- vcalStr = vcalStr.replaceAll("\r\n", "\n");
- String[] strlist = vcalStr.split("\n");
-
- StringBuilder replacedStr = new StringBuilder();
-
- for (int i = 0; i < strlist.length; i++) {
- if (strlist[i].indexOf(":") < 0) {
- if (strlist[i].length() == 0 && strlist[i + 1].indexOf(":") > 0)
- replacedStr.append(strlist[i]).append("\r\n");
- else
- replacedStr.append(" ").append(strlist[i]).append("\r\n");
- } else
- replacedStr.append(strlist[i]).append("\r\n");
- }
- if(Config.LOGD)Log.d(TAG, "After verify:\r\n" + replacedStr.toString());
-
- return replacedStr.toString();
- }
-
- /**
- * If version not given. Search from vcal string of the VERSION property.
- * Then instance mParser to appropriate parser.
- */
- private void judgeVersion(String vcalStr) {
-
- if (mVersion == null) {
- int versionIdx = vcalStr.indexOf("\nVERSION:");
-
- mVersion = VERSION_VCALENDAR10;
-
- if (versionIdx != -1){
- String versionStr = vcalStr.substring(
- versionIdx, vcalStr.indexOf("\n", versionIdx + 1));
- if (versionStr.indexOf("2.0") > 0)
- mVersion = VERSION_VCALENDAR20;
- }
- }
- if (mVersion.equals(VERSION_VCALENDAR10))
- mParser = new VCalParser_V10();
- if (mVersion.equals(VERSION_VCALENDAR20))
- mParser = new VCalParser_V20();
- }
-}
-
diff --git a/core/java/android/syncml/pim/vcalendar/VCalParser_V10.java b/core/java/android/syncml/pim/vcalendar/VCalParser_V10.java
deleted file mode 100644
index 1b251f3..0000000
--- a/core/java/android/syncml/pim/vcalendar/VCalParser_V10.java
+++ /dev/null
@@ -1,1628 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import android.syncml.pim.VParser;
-
-public class VCalParser_V10 extends VParser {
-
- /*
- * The names of the properties whose value are not separated by ";"
- */
- private static final HashSet<String> mEvtPropNameGroup1 = new HashSet<String>(
- Arrays.asList("ATTACH", "ATTENDEE", "DCREATED", "COMPLETED",
- "DESCRIPTION", "DUE", "DTEND", "EXRULE", "LAST-MODIFIED",
- "LOCATION", "RNUM", "PRIORITY", "RELATED-TO", "RRULE",
- "SEQUENCE", "DTSTART", "SUMMARY", "TRANSP", "URL", "UID",
- // above belong to simprop
- "CLASS", "STATUS"));
-
- /*
- * The names of properties whose value are separated by ";"
- */
- private static final HashSet<String> mEvtPropNameGroup2 = new HashSet<String>(
- Arrays.asList("AALARM", "CATEGORIES", "DALARM", "EXDATE", "MALARM",
- "PALARM", "RDATE", "RESOURCES"));
-
- private static final HashSet<String> mValueCAT = new HashSet<String>(Arrays
- .asList("APPOINTMENT", "BUSINESS", "EDUCATION", "HOLIDAY",
- "MEETING", "MISCELLANEOUS", "PERSONAL", "PHONE CALL",
- "SICK DAY", "SPECIAL OCCASION", "TRAVEL", "VACATION"));
-
- private static final HashSet<String> mValueCLASS = new HashSet<String>(Arrays
- .asList("PUBLIC", "PRIVATE", "CONFIDENTIAL"));
-
- private static final HashSet<String> mValueRES = new HashSet<String>(Arrays
- .asList("CATERING", "CHAIRS", "EASEL", "PROJECTOR", "VCR",
- "VEHICLE"));
-
- private static final HashSet<String> mValueSTAT = new HashSet<String>(Arrays
- .asList("ACCEPTED", "NEEDS ACTION", "SENT", "TENTATIVE",
- "CONFIRMED", "DECLINED", "COMPLETED", "DELEGATED"));
-
- /*
- * The names of properties whose value can contain escape characters
- */
- private static final HashSet<String> mEscAllowedProps = new HashSet<String>(
- Arrays.asList("DESCRIPTION", "SUMMARY", "AALARM", "DALARM",
- "MALARM", "PALARM"));
-
- private static final HashMap<String, HashSet<String>> mSpecialValueSetMap =
- new HashMap<String, HashSet<String>>();
-
- static {
- mSpecialValueSetMap.put("CATEGORIES", mValueCAT);
- mSpecialValueSetMap.put("CLASS", mValueCLASS);
- mSpecialValueSetMap.put("RESOURCES", mValueRES);
- mSpecialValueSetMap.put("STATUS", mValueSTAT);
- }
-
- public VCalParser_V10() {
- }
-
- protected int parseVFile(int offset) {
- return parseVCalFile(offset);
- }
-
- private int parseVCalFile(int offset) {
- int ret = 0, sum = 0;
-
- /* remove wsls */
- while (PARSE_ERROR != (ret = parseWsls(offset))) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseVCal(offset); // BEGIN:VCAL ... END:VCAL
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- } else {
- return PARSE_ERROR;
- }
-
- /* remove wsls */
- while (PARSE_ERROR != (ret = parseWsls(offset))) {
- offset += ret;
- sum += ret;
- }
- return sum;
- }
-
- /**
- * "BEGIN" [ws] ":" [ws] "VCALENDAR" [ws] 1*crlf calprop calentities [ws]
- * *crlf "END" [ws] ":" [ws] "VCALENDAR" [ws] 1*CRLF
- */
- private int parseVCal(int offset) {
- int ret = 0, sum = 0;
-
- /* BEGIN */
- ret = parseString(offset, "BEGIN", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VCALENDAR
- ret = parseString(offset, "VCALENDAR", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.startRecord("VCALENDAR");
- }
-
- /* [ws] */
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // 1*CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- // calprop
- ret = parseCalprops(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // calentities
- ret = parseCalentities(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // *CRLF
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- // "END"
- ret = parseString(offset, "END", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VCALENDAR"
- ret = parseString(offset, "VCALENDAR", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endRecord();
- }
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // 1 * CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- return sum;
- }
-
- /**
- * calprops * CRLF calprop / calprop
- */
- private int parseCalprops(int offset) {
- int ret = 0, sum = 0;
-
- if (mBuilder != null) {
- mBuilder.startProperty();
- }
- ret = parseCalprop(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endProperty();
- }
-
- for (;;) {
- /* *CRLF */
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
- // follow VEVENT ,it wont reach endProperty
- if (mBuilder != null) {
- mBuilder.startProperty();
- }
- ret = parseCalprop(offset);
- if (PARSE_ERROR == ret) {
- break;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endProperty();
- }
- }
-
- return sum;
- }
-
- /**
- * calentities *CRLF calentity / calentity
- */
- private int parseCalentities(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseCalentity(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- for (;;) {
- /* *CRLF */
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseCalentity(offset);
- if (PARSE_ERROR == ret) {
- break;
- }
- offset += ret;
- sum += ret;
- }
-
- return sum;
- }
-
- /**
- * calprop = DAYLIGHT/ GEO/ PRODID/ TZ/ VERSION
- */
- private int parseCalprop(int offset) {
- int ret = 0;
-
- ret = parseCalprop0(offset, "DAYLIGHT");
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseCalprop0(offset, "GEO");
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseCalprop0(offset, "PRODID");
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseCalprop0(offset, "TZ");
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseCalprop1(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- return PARSE_ERROR;
- }
-
- /**
- * evententity / todoentity
- */
- private int parseCalentity(int offset) {
- int ret = 0;
-
- ret = parseEvententity(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseTodoentity(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- return PARSE_ERROR;
-
- }
-
- /**
- * propName [params] ":" value CRLF
- */
- private int parseCalprop0(int offset, String propName) {
- int ret = 0, sum = 0, start = 0;
-
- ret = parseString(offset, propName, true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyName(propName);
- }
-
- ret = parseParams(offset);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseString(offset, ":", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseValue(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- ArrayList<String> v = new ArrayList<String>();
- v.add(mBuffer.substring(start, offset));
- mBuilder.propertyValues(v);
- }
-
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- sum += ret;
-
- return sum;
- }
-
- /**
- * "VERSION" [params] ":" "1.0" CRLF
- */
- private int parseCalprop1(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseString(offset, "VERSION", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyName("VERSION");
- }
-
- ret = parseParams(offset);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseString(offset, ":", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "1.0", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- ArrayList<String> v = new ArrayList<String>();
- v.add("1.0");
- mBuilder.propertyValues(v);
- }
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- sum += ret;
-
- return sum;
- }
-
- /**
- * "BEGIN" [ws] ":" [ws] "VEVENT" [ws] 1*CRLF entprops [ws] *CRLF "END" [ws]
- * ":" [ws] "VEVENT" [ws] 1*CRLF
- */
- private int parseEvententity(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseString(offset, "BEGIN", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VEVNET"
- ret = parseString(offset, "VEVENT", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.startRecord("VEVENT");
- }
-
- /* [ws] */
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // 1*CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseEntprops(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // *CRLF
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- // "END"
- ret = parseString(offset, "END", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VEVENT"
- ret = parseString(offset, "VEVENT", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endRecord();
- }
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // 1 * CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- return sum;
- }
-
- /**
- * "BEGIN" [ws] ":" [ws] "VTODO" [ws] 1*CRLF entprops [ws] *CRLF "END" [ws]
- * ":" [ws] "VTODO" [ws] 1*CRLF
- */
- private int parseTodoentity(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseString(offset, "BEGIN", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VTODO"
- ret = parseString(offset, "VTODO", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.startRecord("VTODO");
- }
-
- // 1*CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseEntprops(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // *CRLF
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- // "END"
- ret = parseString(offset, "END", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // ":"
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // "VTODO"
- ret = parseString(offset, "VTODO", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endRecord();
- }
-
- // [ws]
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- // 1 * CRLF
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
-
- return sum;
- }
-
- /**
- * entprops *CRLF entprop / entprop
- */
- private int parseEntprops(int offset) {
- int ret = 0, sum = 0;
- if (mBuilder != null) {
- mBuilder.startProperty();
- }
-
- ret = parseEntprop(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endProperty();
- }
-
- for (;;) {
- while (PARSE_ERROR != (ret = parseCrlf(offset))) {
- offset += ret;
- sum += ret;
- }
- if (mBuilder != null) {
- mBuilder.startProperty();
- }
-
- ret = parseEntprop(offset);
- if (PARSE_ERROR == ret) {
- break;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.endProperty();
- }
- }
- return sum;
- }
-
- /**
- * for VEVENT,VTODO prop. entprop0 / entprop1
- */
- private int parseEntprop(int offset) {
- int ret = 0;
- ret = parseEntprop0(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseEntprop1(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- return PARSE_ERROR;
- }
-
- /**
- * Same with card. ";" [ws] paramlist
- */
- private int parseParams(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseString(offset, ";", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseParamlist(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- sum += ret;
-
- return sum;
- }
-
- /**
- * Same with card. paramlist [ws] ";" [ws] param / param
- */
- private int parseParamlist(int offset) {
- int ret = 0, sum = 0;
-
- ret = parseParam(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- int offsetTemp = offset;
- int sumTemp = sum;
- for (;;) {
- ret = removeWs(offsetTemp);
- offsetTemp += ret;
- sumTemp += ret;
-
- ret = parseString(offsetTemp, ";", false);
- if (PARSE_ERROR == ret) {
- return sum;
- }
- offsetTemp += ret;
- sumTemp += ret;
-
- ret = removeWs(offsetTemp);
- offsetTemp += ret;
- sumTemp += ret;
-
- ret = parseParam(offsetTemp);
- if (PARSE_ERROR == ret) {
- break;
- }
- offsetTemp += ret;
- sumTemp += ret;
-
- // offset = offsetTemp;
- sum = sumTemp;
- }
- return sum;
- }
-
- /**
- * param0 - param7 / knowntype
- */
- private int parseParam(int offset) {
- int ret = 0;
-
- ret = parseParam0(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam1(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam2(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam3(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam4(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam5(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam6(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseParam7(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- int start = offset;
- ret = parseKnownType(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(null);
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return ret;
- }
-
- /**
- * simprop AND "CLASS" AND "STATUS" The value of these properties are not
- * seperated by ";"
- *
- * [ws] simprop [params] ":" value CRLF
- */
- private int parseEntprop0(int offset) {
- int ret = 0, sum = 0, start = 0;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- String propName = getWord(offset).toUpperCase();
- if (!mEvtPropNameGroup1.contains(propName)) {
- if (PARSE_ERROR == parseXWord(offset))
- return PARSE_ERROR;
- }
- ret = propName.length();
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyName(propName);
- }
-
- ret = parseParams(offset);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseValue(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- ArrayList<String> v = new ArrayList<String>();
- v.add(exportEntpropValue(propName, mBuffer.substring(start,
- offset)));
- mBuilder.propertyValues(v);
- // Filter value,match string, REFER:RFC
- if (PARSE_ERROR == valueFilter(propName, v))
- return PARSE_ERROR;
- }
-
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- sum += ret;
- return sum;
- }
-
- /**
- * other event prop names except simprop AND "CLASS" AND "STATUS" The value
- * of these properties are seperated by ";" [ws] proper name [params] ":"
- * value CRLF
- */
- private int parseEntprop1(int offset) {
- int ret = 0, sum = 0;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- String propName = getWord(offset).toUpperCase();
- if (!mEvtPropNameGroup2.contains(propName)) {
- return PARSE_ERROR;
- }
- ret = propName.length();
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyName(propName);
- }
-
- ret = parseParams(offset);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- }
-
- ret = parseString(offset, ":", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- int start = offset;
- ret = parseValue(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- // mutil-values
- if (mBuilder != null) {
- int end = 0;
- ArrayList<String> v = new ArrayList<String>();
- Pattern p = Pattern
- .compile("([^;\\\\]*(\\\\[\\\\;:,])*[^;\\\\]*)(;?)");
- Matcher m = p.matcher(mBuffer.substring(start, offset));
- while (m.find()) {
- String s = exportEntpropValue(propName, m.group(1));
- v.add(s);
- end = m.end();
- if (offset == start + end) {
- String endValue = m.group(3);
- if (";".equals(endValue)) {
- v.add("");
- }
- break;
- }
- }
- mBuilder.propertyValues(v);
- // Filter value,match string, REFER:RFC
- if (PARSE_ERROR == valueFilter(propName, v))
- return PARSE_ERROR;
- }
-
- ret = parseCrlf(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- sum += ret;
- return sum;
- }
-
- /**
- * "TYPE" [ws] = [ws] ptypeval
- */
- private int parseParam0(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseString(offset, "TYPE", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", false);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parsePtypeval(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
- return sum;
- }
-
- /**
- * ["VALUE" [ws] "=" [ws]] pvalueval
- */
- private int parseParam1(int offset) {
- int ret = 0, sum = 0, start = offset;
- boolean flag = false;
-
- ret = parseString(offset, "VALUE", true);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- flag = true;
- }
- if (flag == true && mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR != ret) {
- if (flag == false) { // "VALUE" does not exist
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- } else {
- if (flag == true) { // "VALUE" exists
- return PARSE_ERROR;
- }
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parsePValueVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
- }
-
- /** ["ENCODING" [ws] "=" [ws]] pencodingval */
- private int parseParam2(int offset) {
- int ret = 0, sum = 0, start = offset;
- boolean flag = false;
-
- ret = parseString(offset, "ENCODING", true);
- if (PARSE_ERROR != ret) {
- offset += ret;
- sum += ret;
- flag = true;
- }
- if (flag == true && mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR != ret) {
- if (flag == false) { // "VALUE" does not exist
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- } else {
- if (flag == true) { // "VALUE" exists
- return PARSE_ERROR;
- }
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parsePEncodingVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
- }
-
- /**
- * "CHARSET" [WS] "=" [WS] charsetval
- */
- private int parseParam3(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseString(offset, "CHARSET", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseCharsetVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
- }
-
- /**
- * "LANGUAGE" [ws] "=" [ws] langval
- */
- private int parseParam4(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseString(offset, "LANGUAGE", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseLangVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
- }
-
- /**
- * "ROLE" [ws] "=" [ws] roleval
- */
- private int parseParam5(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseString(offset, "ROLE", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseRoleVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
- }
-
- /**
- * "STATUS" [ws] = [ws] statuval
- */
- private int parseParam6(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseString(offset, "STATUS", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseStatuVal(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
-
- }
-
- /**
- * XWord [ws] "=" [ws] word
- */
- private int parseParam7(int offset) {
- int ret = 0, sum = 0, start = offset;
-
- ret = parseXWord(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamType(mBuffer.substring(start, offset));
- }
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- ret = parseString(offset, "=", true);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
-
- ret = removeWs(offset);
- offset += ret;
- sum += ret;
-
- start = offset;
- ret = parseWord(offset);
- if (PARSE_ERROR == ret) {
- return PARSE_ERROR;
- }
- offset += ret;
- sum += ret;
- if (mBuilder != null) {
- mBuilder.propertyParamValue(mBuffer.substring(start, offset));
- }
-
- return sum;
-
- }
-
- /*
- * "WAVE" / "PCM" / "VCARD" / XWORD
- */
- private int parseKnownType(int offset) {
- int ret = 0;
-
- ret = parseString(offset, "WAVE", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "PCM", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "VCARD", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseXWord(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- return PARSE_ERROR;
- }
-
- /*
- * knowntype / Xword
- */
- private int parsePtypeval(int offset) {
- int ret = 0;
-
- ret = parseKnownType(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseXWord(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- return PARSE_ERROR;
- }
-
- /**
- * "ATTENDEE" / "ORGANIZER" / "OWNER" / XWORD
- */
- private int parseRoleVal(int offset) {
- int ret = 0;
-
- ret = parseString(offset, "ATTENDEE", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "ORGANIZER", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "OWNER", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseXWord(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- return PARSE_ERROR;
- }
-
- /**
- * "ACCEPTED" / "NEED ACTION" / "SENT" / "TENTATIVE" / "CONFIRMED" /
- * "DECLINED" / "COMPLETED" / "DELEGATED / XWORD
- */
- private int parseStatuVal(int offset) {
- int ret = 0;
-
- ret = parseString(offset, "ACCEPTED", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "NEED ACTION", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseString(offset, "TENTATIVE", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- ret = parseString(offset, "CONFIRMED", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- ret = parseString(offset, "DECLINED", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- ret = parseString(offset, "COMPLETED", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
- ret = parseString(offset, "DELEGATED", true);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- ret = parseXWord(offset);
- if (PARSE_ERROR != ret) {
- return ret;
- }
-
- return PARSE_ERROR;
- }
-
- /**
- * Check 4 special propName and it's value to match Hash.
- *
- * @return PARSE_ERROR:value not match. 1:go on,like nothing happen.
- */
- private int valueFilter(String propName, ArrayList<String> values) {
- if (propName == null || propName.equals("") || values == null
- || values.isEmpty())
- return 1; // go on, like nothing happen.
-
- if (mSpecialValueSetMap.containsKey(propName)) {
- for (String value : values) {
- if (!mSpecialValueSetMap.get(propName).contains(value)) {
- if (!value.startsWith("X-"))
- return PARSE_ERROR;
- }
- }
- }
-
- return 1;
- }
-
- /**
- *
- * Translate escape characters("\\", "\;") which define in vcalendar1.0
- * spec. But for fault tolerance, we will translate "\:" and "\,", which
- * isn't define in vcalendar1.0 explicitly, as the same behavior as other
- * client.
- *
- * Though vcalendar1.0 spec does not defined the value of property
- * "description", "summary", "aalarm", "dalarm", "malarm" and "palarm" could
- * contain escape characters, we do support escape characters in these
- * properties.
- *
- * @param str:
- * the value string will be translated.
- * @return the string which do not contain any escape character in
- * vcalendar1.0
- */
- private String exportEntpropValue(String propName, String str) {
- if (null == propName || null == str)
- return null;
- if ("".equals(propName) || "".equals(str))
- return "";
-
- if (!mEscAllowedProps.contains(propName))
- return str;
-
- String tmp = str.replace("\\\\", "\n\r\n");
- tmp = tmp.replace("\\;", ";");
- tmp = tmp.replace("\\:", ":");
- tmp = tmp.replace("\\,", ",");
- tmp = tmp.replace("\n\r\n", "\\");
- return tmp;
- }
-}
diff --git a/core/java/android/syncml/pim/vcalendar/VCalParser_V20.java b/core/java/android/syncml/pim/vcalendar/VCalParser_V20.java
deleted file mode 100644
index 5748379..0000000
--- a/core/java/android/syncml/pim/vcalendar/VCalParser_V20.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.syncml.pim.vcalendar;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashSet;
-
-import android.syncml.pim.VBuilder;
-
-public class VCalParser_V20 extends VCalParser_V10 {
- private static final String V10LINEBREAKER = "\r\n";
-
- private static final HashSet<String> acceptableComponents = new HashSet<String>(
- Arrays.asList("VEVENT", "VTODO", "VALARM", "VTIMEZONE"));
-
- private static final HashSet<String> acceptableV20Props = new HashSet<String>(
- Arrays.asList("DESCRIPTION", "DTEND", "DTSTART", "DUE",
- "COMPLETED", "RRULE", "STATUS", "SUMMARY", "LOCATION"));
-
- private boolean hasTZ = false; // MUST only have one TZ property
-
- private String[] lines;
-
- private int index;
-
- @Override
- public boolean parse(InputStream is, String encoding, VBuilder builder)
- throws IOException {
- // get useful info for android calendar, and alter to vcal 1.0
- byte[] bytes = new byte[is.available()];
- is.read(bytes);
- String scStr = new String(bytes);
- StringBuilder v10str = new StringBuilder("");
-
- lines = splitProperty(scStr);
- index = 0;
-
- if ("BEGIN:VCALENDAR".equals(lines[index]))
- v10str.append("BEGIN:VCALENDAR" + V10LINEBREAKER);
- else
- return false;
- index++;
- if (false == parseV20Calbody(lines, v10str)
- || index > lines.length - 1)
- return false;
-
- if (lines.length - 1 == index && "END:VCALENDAR".equals(lines[index]))
- v10str.append("END:VCALENDAR" + V10LINEBREAKER);
- else
- return false;
-
- return super.parse(
- // use vCal 1.0 parser
- new ByteArrayInputStream(v10str.toString().getBytes()),
- encoding, builder);
- }
-
- /**
- * Parse and pick acceptable iCalendar body and translate it to
- * calendarV1.0 format.
- * @param lines iCalendar components/properties line list.
- * @param buffer calendarV10 format string buffer
- * @return true for success, or false
- */
- private boolean parseV20Calbody(String[] lines, StringBuilder buffer) {
- try {
- while (!"VERSION:2.0".equals(lines[index]))
- index++;
- buffer.append("VERSION:1.0" + V10LINEBREAKER);
-
- index++;
- for (; index < lines.length - 1; index++) {
- String[] keyAndValue = lines[index].split(":", 2);
- String key = keyAndValue[0];
- String value = keyAndValue[1];
-
- if ("BEGIN".equals(key.trim())) {
- if (!key.equals(key.trim()))
- return false; // MUST be "BEGIN:componentname"
- index++;
- if (false == parseV20Component(value, buffer))
- return false;
- }
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Parse and pick acceptable calendar V2.0's component and translate it to
- * V1.0 format.
- * @param compName component name
- * @param buffer calendarV10 format string buffer
- * @return true for success, or false
- * @throws ArrayIndexOutOfBoundsException
- */
- private boolean parseV20Component(String compName, StringBuilder buffer)
- throws ArrayIndexOutOfBoundsException {
- String endTag = "END:" + compName;
- String[] propAndValue;
- String propName, value;
-
- if (acceptableComponents.contains(compName)) {
- if ("VEVENT".equals(compName) || "VTODO".equals(compName)) {
- buffer.append("BEGIN:" + compName + V10LINEBREAKER);
- while (!endTag.equals(lines[index])) {
- propAndValue = lines[index].split(":", 2);
- propName = propAndValue[0].split(";", 2)[0];
- value = propAndValue[1];
-
- if ("".equals(lines[index]))
- buffer.append(V10LINEBREAKER);
- else if (acceptableV20Props.contains(propName)) {
- buffer.append(propName + ":" + value + V10LINEBREAKER);
- } else if ("BEGIN".equals(propName.trim())) {
- // MUST be BEGIN:VALARM
- if (propName.equals(propName.trim())
- && "VALARM".equals(value)) {
- buffer.append("AALARM:default" + V10LINEBREAKER);
- while (!"END:VALARM".equals(lines[index]))
- index++;
- } else
- return false;
- }
- index++;
- } // end while
- buffer.append(endTag + V10LINEBREAKER);
- } else if ("VALARM".equals(compName)) { // VALARM component MUST
- // only appear within either VEVENT or VTODO
- return false;
- } else if ("VTIMEZONE".equals(compName)) {
- do {
- if (false == hasTZ) {// MUST only have 1 time TZ property
- propAndValue = lines[index].split(":", 2);
- propName = propAndValue[0].split(";", 2)[0];
-
- if ("TZOFFSETFROM".equals(propName)) {
- value = propAndValue[1];
- buffer.append("TZ" + ":" + value + V10LINEBREAKER);
- hasTZ = true;
- }
- }
- index++;
- } while (!endTag.equals(lines[index]));
- } else
- return false;
- } else {
- while (!endTag.equals(lines[index]))
- index++;
- }
-
- return true;
- }
-
- /** split ever property line to String[], not split folding line. */
- private String[] splitProperty(String scStr) {
- /*
- * Property splitted by \n, and unfold folding lines by removing
- * CRLF+LWSP-char
- */
- scStr = scStr.replaceAll("\r\n", "\n").replaceAll("\n ", "")
- .replaceAll("\n\t", "");
- String[] strs = scStr.split("\n");
- return strs;
- }
-}
diff --git a/core/java/android/syncml/pim/vcalendar/package.html b/core/java/android/syncml/pim/vcalendar/package.html
deleted file mode 100644
index cb4ca46..0000000
--- a/core/java/android/syncml/pim/vcalendar/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Support classes for SyncML.
-{@hide}
-</BODY>
-</HTML> \ No newline at end of file
diff --git a/core/java/android/syncml/pim/vcard/ContactStruct.java b/core/java/android/syncml/pim/vcard/ContactStruct.java
index ecd719d..9782111 100644
--- a/core/java/android/syncml/pim/vcard/ContactStruct.java
+++ b/core/java/android/syncml/pim/vcard/ContactStruct.java
@@ -17,11 +17,14 @@
package android.syncml.pim.vcard;
import android.content.AbstractSyncableContentProvider;
+import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.net.Uri;
+import android.provider.CallLog;
import android.provider.Contacts;
+import android.provider.CallLog.Calls;
import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.Extensions;
import android.provider.Contacts.GroupMembership;
@@ -48,8 +51,11 @@ import java.util.Map.Entry;
* This class standy by the person-contact in
* Android system, we must use this class instance as parameter to transmit to
* VCardComposer so that create vCard string.
+ *
+ * @deprecated Please use the new code in android.pim.vcard
*/
// TODO: rename the class name, next step
+@Deprecated
public class ContactStruct {
private static final String LOG_TAG = "ContactStruct";
@@ -84,6 +90,8 @@ public class ContactStruct {
/** Only for GET. Use addExtension() to PUT */
public Map<String, List<String>> extensionMap;
+ public String timeStamp;
+
// Use organizationList instead when handling ORG.
@Deprecated
public String company;
@@ -144,6 +152,32 @@ public class ContactStruct {
}
/**
+ * Add call history time stamp and call type.
+ * @param type call type
+ * @param timeStamp timeStamp
+ */
+ public void addCallHistoryTimeStamp(int type, String date) {
+ // Extension for call history as defined in
+ // in the Specification for Ic Mobile Communcation - ver 1.1,
+ // Oct 2000. This is used to send the details of the call
+ // history - missed, incoming, outgoing along with date and time
+ // to the requesting device (For example, transferring phone book
+ // when connected over bluetooth)
+ // X-IRMC-CALL-DATETIME;MISSED:20050320T100000
+ String strCallType;
+ if (type == Calls.INCOMING_TYPE) {
+ strCallType = "INCOMING";
+ } else if (type == Calls.OUTGOING_TYPE) {
+ strCallType = "OUTGOING";
+ } else if (type == Calls.MISSED_TYPE) {
+ strCallType = "MISSED";
+ } else {
+ strCallType = "";
+ }
+ timeStamp = "X-IRMC-CALL-DATETIME;" + strCallType + ":" + date;
+ }
+
+ /**
* Add a contactmethod info to contactmethodList.
* @param kind integer value defined in Contacts.java
* (e.g. Contacts.KIND_EMAIL)
@@ -780,17 +814,16 @@ public class ContactStruct {
personId = ContentUris.parseId(personUri);
}
} else {
- personUri = provider.nonTransactionalInsert(People.CONTENT_URI, contentValues);
+ personUri = provider.insert(People.CONTENT_URI, contentValues);
if (personUri != null) {
personId = ContentUris.parseId(personUri);
ContentValues values = new ContentValues();
values.put(GroupMembership.PERSON_ID, personId);
values.put(GroupMembership.GROUP_ID, myContactsGroupId);
- Uri resultUri = provider.nonTransactionalInsert(
- GroupMembership.CONTENT_URI, values);
+ Uri resultUri = provider.insert(GroupMembership.CONTENT_URI, values);
if (resultUri == null) {
Log.e(LOG_TAG, "Faild to insert the person to MyContact.");
- provider.nonTransactionalDelete(personUri, null, null);
+ provider.delete(personUri, null, null);
personUri = null;
}
}
@@ -830,7 +863,7 @@ public class ContactStruct {
if (resolver != null) {
phoneUri = resolver.insert(Phones.CONTENT_URI, values);
} else {
- phoneUri = provider.nonTransactionalInsert(Phones.CONTENT_URI, values);
+ phoneUri = provider.insert(Phones.CONTENT_URI, values);
}
if (phoneData.isPrimary) {
primaryPhoneId = Long.parseLong(phoneUri.getLastPathSegment());
@@ -856,8 +889,7 @@ public class ContactStruct {
if (resolver != null) {
organizationUri = resolver.insert(Organizations.CONTENT_URI, values);
} else {
- organizationUri = provider.nonTransactionalInsert(
- Organizations.CONTENT_URI, values);
+ organizationUri = provider.insert(Organizations.CONTENT_URI, values);
}
if (organizationData.isPrimary) {
primaryOrganizationId = Long.parseLong(organizationUri.getLastPathSegment());
@@ -883,8 +915,7 @@ public class ContactStruct {
if (resolver != null) {
emailUri = resolver.insert(ContactMethods.CONTENT_URI, values);
} else {
- emailUri = provider.nonTransactionalInsert(
- ContactMethods.CONTENT_URI, values);
+ emailUri = provider.insert(ContactMethods.CONTENT_URI, values);
}
if (contactMethod.isPrimary) {
primaryEmailId = Long.parseLong(emailUri.getLastPathSegment());
@@ -893,8 +924,7 @@ public class ContactStruct {
if (resolver != null) {
resolver.insert(ContactMethods.CONTENT_URI, values);
} else {
- provider.nonTransactionalInsert(
- ContactMethods.CONTENT_URI, values);
+ provider.insert(ContactMethods.CONTENT_URI, values);
}
}
}
@@ -918,7 +948,7 @@ public class ContactStruct {
if (resolver != null) {
contentValuesArray.add(values);
} else {
- provider.nonTransactionalInsert(Extensions.CONTENT_URI, values);
+ provider.insert(Extensions.CONTENT_URI, values);
}
}
}
@@ -942,7 +972,7 @@ public class ContactStruct {
if (resolver != null) {
resolver.update(personUri, values, null, null);
} else {
- provider.nonTransactionalUpdate(personUri, values, null, null);
+ provider.update(personUri, values, null, null);
}
}
}
@@ -960,12 +990,12 @@ public class ContactStruct {
public void pushIntoAbstractSyncableContentProvider(
AbstractSyncableContentProvider provider, long myContactsGroupId) {
boolean successful = false;
- provider.beginTransaction();
+ provider.beginBatch();
try {
pushIntoContentProviderOrResolver(provider, myContactsGroupId);
successful = true;
} finally {
- provider.endTransaction(successful);
+ provider.endBatch(successful);
}
}
diff --git a/core/java/android/syncml/pim/vcard/VCardComposer.java b/core/java/android/syncml/pim/vcard/VCardComposer.java
index 192736a..a5dd053 100644
--- a/core/java/android/syncml/pim/vcard/VCardComposer.java
+++ b/core/java/android/syncml/pim/vcard/VCardComposer.java
@@ -29,7 +29,10 @@ import android.syncml.pim.vcard.ContactStruct.PhoneData;
/**
* Compose VCard string
+ *
+ * @depricated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardComposer {
final public static int VERSION_VCARD21_INT = 1;
@@ -146,6 +149,10 @@ public class VCardComposer {
appendContactMethodStr(struct.contactmethodList, vcardversion);
}
+ if (!isNull(struct.timeStamp)) {
+ mResult.append(struct.timeStamp).append(mNewline);
+ }
+
mResult.append("END:VCARD").append(mNewline);
return mResult.toString();
}
diff --git a/core/java/android/syncml/pim/vcard/VCardDataBuilder.java b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java
index a0513f1..5fd8fdf 100644
--- a/core/java/android/syncml/pim/vcard/VCardDataBuilder.java
+++ b/core/java/android/syncml/pim/vcard/VCardDataBuilder.java
@@ -28,6 +28,7 @@ import android.syncml.pim.PropertyNode;
import android.syncml.pim.VBuilder;
import android.syncml.pim.VNode;
import android.syncml.pim.VParser;
+import android.text.TextUtils;
import android.util.CharsetUtils;
import android.util.Log;
@@ -46,7 +47,10 @@ import java.util.List;
* If we store all VNode entries in memory like VDataBuilder.java,
* OutOfMemoryError may be thrown. Thus, this class push each VCard entry into
* ContentResolver immediately.
+ *
+ * @depricated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardDataBuilder implements VBuilder {
static private String LOG_TAG = "VCardDataBuilder";
@@ -403,7 +407,10 @@ public class VCardDataBuilder implements VBuilder {
String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
String encoding = paramMap.getAsString("ENCODING");
- if (targetCharset == null || targetCharset.length() == 0) {
+ Log.d("@@@", String.format("targetCharset: \"%s\", encoding: \"%s\"",
+ targetCharset, encoding));
+
+ if (TextUtils.isEmpty(targetCharset)) {
targetCharset = mTargetCharset;
}
diff --git a/core/java/android/syncml/pim/vcard/VCardEntryCounter.java b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java
index 03cd1d9..11372ce 100644
--- a/core/java/android/syncml/pim/vcard/VCardEntryCounter.java
+++ b/core/java/android/syncml/pim/vcard/VCardEntryCounter.java
@@ -20,6 +20,10 @@ import java.util.List;
import android.syncml.pim.VBuilder;
+/**
+ * @depricated Please use the code in android.pim.vcard
+ */
+@Deprecated
public class VCardEntryCounter implements VBuilder {
private int mCount;
diff --git a/core/java/android/syncml/pim/vcard/VCardException.java b/core/java/android/syncml/pim/vcard/VCardException.java
index 35b31ec..326aa7f 100644
--- a/core/java/android/syncml/pim/vcard/VCardException.java
+++ b/core/java/android/syncml/pim/vcard/VCardException.java
@@ -16,6 +16,10 @@
package android.syncml.pim.vcard;
+/**
+ * @depricated Please use the code in android.pim.vcard
+ */
+@Deprecated
public class VCardException extends java.lang.Exception{
// constructors
diff --git a/core/java/android/syncml/pim/vcard/VCardNestedException.java b/core/java/android/syncml/pim/vcard/VCardNestedException.java
index def6f3b..5c49e40 100644
--- a/core/java/android/syncml/pim/vcard/VCardNestedException.java
+++ b/core/java/android/syncml/pim/vcard/VCardNestedException.java
@@ -18,7 +18,10 @@ package android.syncml.pim.vcard;
/**
* VCardException thrown when VCard is nested without VCardParser's being notified.
+ *
+ * @depricated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardNestedException extends VCardException {
public VCardNestedException() {}
public VCardNestedException(String message) {
diff --git a/core/java/android/syncml/pim/vcard/VCardParser.java b/core/java/android/syncml/pim/vcard/VCardParser.java
index 6dad852d..a562973 100644
--- a/core/java/android/syncml/pim/vcard/VCardParser.java
+++ b/core/java/android/syncml/pim/vcard/VCardParser.java
@@ -17,13 +17,16 @@
package android.syncml.pim.vcard;
import android.syncml.pim.VDataBuilder;
-import android.syncml.pim.VParser;
import android.util.Config;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.IOException;
+/**
+ * @deprecated Please use the code in android.pim.vcard
+ */
+@Deprecated
public class VCardParser {
// TODO: fix this.
diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V21.java b/core/java/android/syncml/pim/vcard/VCardParser_V21.java
index d865668..75ce564 100644
--- a/core/java/android/syncml/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/syncml/pim/vcard/VCardParser_V21.java
@@ -31,7 +31,10 @@ import java.util.HashSet;
/**
* This class is used to parse vcard. Please refer to vCard Specification 2.1.
+ *
+ * @deprecated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardParser_V21 {
private static final String LOG_TAG = "VCardParser_V21";
diff --git a/core/java/android/syncml/pim/vcard/VCardParser_V30.java b/core/java/android/syncml/pim/vcard/VCardParser_V30.java
index e67525e..3aca258 100644
--- a/core/java/android/syncml/pim/vcard/VCardParser_V30.java
+++ b/core/java/android/syncml/pim/vcard/VCardParser_V30.java
@@ -25,7 +25,10 @@ import java.util.HashSet;
/**
* This class is used to parse vcard3.0. <br>
* Please refer to vCard Specification 3.0 (http://tools.ietf.org/html/rfc2426)
+ *
+ * @deprecated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardParser_V30 extends VCardParser_V21 {
private static final String LOG_TAG = "VCardParser_V30";
diff --git a/core/java/android/syncml/pim/vcard/VCardSourceDetector.java b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java
index 8c48391..6873f04 100644
--- a/core/java/android/syncml/pim/vcard/VCardSourceDetector.java
+++ b/core/java/android/syncml/pim/vcard/VCardSourceDetector.java
@@ -26,8 +26,10 @@ import java.util.Set;
/**
* Class which tries to detects the source of the vCard from its properties.
* Currently this implementation is very premature.
- * @hide
+ *
+ * @deprecated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardSourceDetector implements VBuilder {
// Should only be used in package.
static final int TYPE_UNKNOWN = 0;
diff --git a/core/java/android/syncml/pim/vcard/VCardVersionException.java b/core/java/android/syncml/pim/vcard/VCardVersionException.java
index 1ca88d1..14bb45b 100644
--- a/core/java/android/syncml/pim/vcard/VCardVersionException.java
+++ b/core/java/android/syncml/pim/vcard/VCardVersionException.java
@@ -17,8 +17,11 @@
package android.syncml.pim.vcard;
/**
- * VCardException used only when the version of the vCard is different.
+ * VCardException used only when the version of the vCard is different.
+ *
+ * @deprecated Please use the code in android.pim.vcard
*/
+@Deprecated
public class VCardVersionException extends VCardException {
public VCardVersionException() {
}
diff --git a/core/java/android/test/AndroidTestCase.java b/core/java/android/test/AndroidTestCase.java
index de0587a..1015506 100644
--- a/core/java/android/test/AndroidTestCase.java
+++ b/core/java/android/test/AndroidTestCase.java
@@ -30,6 +30,7 @@ import java.lang.reflect.Field;
public class AndroidTestCase extends TestCase {
protected Context mContext;
+ private Context mTestContext;
@Override
protected void setUp() throws Exception {
@@ -43,7 +44,7 @@ public class AndroidTestCase extends TestCase {
public void testAndroidTestCaseSetupProperly() {
assertNotNull("Context is null. setContext should be called before tests are run",
- mContext);
+ mContext);
}
public void setContext(Context context) {
@@ -55,6 +56,25 @@ public class AndroidTestCase extends TestCase {
}
/**
+ * Test context can be used to access resources from the test's own package
+ * as opposed to the resources from the test target package. Access to the
+ * latter is provided by the context set with the {@link #setContext}
+ * method.
+ *
+ * @hide
+ */
+ public void setTestContext(Context context) {
+ mTestContext = context;
+ }
+
+ /**
+ * @hide
+ */
+ public Context getTestContext() {
+ return mTestContext;
+ }
+
+ /**
* Asserts that launching a given activity is protected by a particular permission by
* attempting to start the activity and validating that a {@link SecurityException}
* is thrown that mentions the permission in its error message.
@@ -125,9 +145,9 @@ public class AndroidTestCase extends TestCase {
* to scrub out any class variables. This protects against memory leaks in the case where a
* test case creates a non-static inner class (thus referencing the test case) and gives it to
* someone else to hold onto.
- *
+ *
* @param testCaseClass The class of the derived TestCase implementation.
- *
+ *
* @throws IllegalAccessException
*/
protected void scrubClass(final Class<?> testCaseClass)
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
index 2145d7c..22d95d1 100644
--- a/core/java/android/test/InstrumentationTestCase.java
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -43,11 +43,25 @@ public class InstrumentationTestCase extends TestCase {
*
* @param instrumentation the instrumentation to use with this instance
*/
- public void injectInsrumentation(Instrumentation instrumentation) {
+ public void injectInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
}
/**
+ * Injects instrumentation into this test case. This method is
+ * called by the test runner during test setup.
+ *
+ * @param instrumentation the instrumentation to use with this instance
+ *
+ * @deprecated Incorrect spelling,
+ * use {@link #injectInstrumentation(android.app.Instrumentation) instead.
+ */
+ @Deprecated
+ public void injectInsrumentation(Instrumentation instrumentation) {
+ injectInstrumentation(instrumentation);
+ }
+
+ /**
* Inheritors can access the instrumentation using this.
* @return instrumentation
*/
diff --git a/core/java/android/test/InstrumentationTestSuite.java b/core/java/android/test/InstrumentationTestSuite.java
index 2ab949e..7a78ffb 100644
--- a/core/java/android/test/InstrumentationTestSuite.java
+++ b/core/java/android/test/InstrumentationTestSuite.java
@@ -65,7 +65,7 @@ public class InstrumentationTestSuite extends TestSuite {
public void runTest(Test test, TestResult result) {
if (test instanceof InstrumentationTestCase) {
- ((InstrumentationTestCase) test).injectInsrumentation(mInstrumentation);
+ ((InstrumentationTestCase) test).injectInstrumentation(mInstrumentation);
}
// run the test as usual
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 843754b..944f735 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -19,6 +19,7 @@ package android.text;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.text.style.ParagraphStyle;
import android.util.FloatMath;
/**
@@ -262,6 +263,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
TextUtils.recycle(temp);
+ if (boring && text instanceof Spanned) {
+ Spanned sp = (Spanned) text;
+ Object[] styles = sp.getSpans(0, text.length(), ParagraphStyle.class);
+ if (styles.length > 0) {
+ boring = false;
+ }
+ }
+
if (boring) {
Metrics fm = metrics;
if (fm == null) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index c133cf2..f0a5ffd 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -968,7 +968,13 @@ extends Layout
fm.bottom = bottom;
for (int i = 0; i < chooseht.length; i++) {
- chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm);
+ if (chooseht[i] instanceof LineHeightSpan.WithDensity) {
+ ((LineHeightSpan.WithDensity) chooseht[i]).
+ chooseHeight(text, start, end, choosehtv[i], v, fm, paint);
+
+ } else {
+ chooseht[i].chooseHeight(text, start, end, choosehtv[i], v, fm);
+ }
}
above = fm.ascent;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index f13820d..f9e7cac 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -27,6 +27,7 @@ public class TextPaint extends Paint {
public int baselineShift;
public int linkColor;
public int[] drawableState;
+ public float density = 1.0f;
public TextPaint() {
super();
@@ -51,5 +52,6 @@ public class TextPaint extends Paint {
baselineShift = tp.baselineShift;
linkColor = tp.linkColor;
drawableState = tp.drawableState;
+ density = tp.density;
}
}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 1a4eb69..9dd8ceb 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -25,7 +25,9 @@ import android.pim.DateException;
import java.util.Calendar;
import java.util.Date;
+import java.util.Formatter;
import java.util.GregorianCalendar;
+import java.util.Locale;
import java.util.TimeZone;
/**
@@ -1040,6 +1042,31 @@ public class DateUtils
/**
* Formats a date or a time range according to the local conventions.
+ * <p>
+ * Note that this is a convenience method. Using it involves creating an
+ * internal {@link java.util.Formatter} instance on-the-fly, which is
+ * somewhat costly in terms of memory and time. This is probably acceptable
+ * if you use the method only rarely, but if you rely on it for formatting a
+ * large number of dates, consider creating and reusing your own
+ * {@link java.util.Formatter} instance and use the version of
+ * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+ * that takes a {@link java.util.Formatter}.
+ *
+ * @param context the context is required only if the time is shown
+ * @param startMillis the start time in UTC milliseconds
+ * @param endMillis the end time in UTC milliseconds
+ * @param flags a bit mask of options See
+ * {@link #formatDateRange(Context, long, long, int) formatDateRange}
+ * @return a string containing the formatted date/time range.
+ */
+ public static String formatDateRange(Context context, long startMillis,
+ long endMillis, int flags) {
+ Formatter f = new Formatter(new StringBuilder(50), Locale.getDefault());
+ return formatDateRange(context, f, startMillis, endMillis, flags).toString();
+ }
+
+ /**
+ * Formats a date or a time range according to the local conventions.
*
* <p>
* Example output strings (date formats in these examples are shown using
@@ -1181,14 +1208,17 @@ public class DateUtils
* instead of "December 31, 2008".
*
* @param context the context is required only if the time is shown
+ * @param formatter the Formatter used for formatting the date range.
+ * Note: be sure to call setLength(0) on StringBuilder passed to
+ * the Formatter constructor unless you want the results to accumulate.
* @param startMillis the start time in UTC milliseconds
* @param endMillis the end time in UTC milliseconds
* @param flags a bit mask of options
*
- * @return a string containing the formatted date/time range.
+ * @return the formatter with the formatted date/time range appended to the string buffer.
*/
- public static String formatDateRange(Context context, long startMillis,
- long endMillis, int flags) {
+ public static Formatter formatDateRange(Context context, Formatter formatter, long startMillis,
+ long endMillis, int flags) {
Resources res = Resources.getSystem();
boolean showTime = (flags & FORMAT_SHOW_TIME) != 0;
boolean showWeekDay = (flags & FORMAT_SHOW_WEEKDAY) != 0;
@@ -1423,8 +1453,7 @@ public class DateUtils
if (noMonthDay && startMonthNum == endMonthNum) {
// Example: "January, 2008"
- String startDateString = startDate.format(defaultDateFormat);
- return startDateString;
+ return formatter.format("%s", startDate.format(defaultDateFormat));
}
if (startYear != endYear || noMonthDay) {
@@ -1436,10 +1465,9 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startDateString, startTimeString,
endWeekDayString, endDateString, endTimeString);
- return dateRange;
}
// Get the month, day, and year strings for the start and end dates
@@ -1476,12 +1504,11 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startMonthString, startMonthDayString,
startYearString, startTimeString,
endWeekDayString, endMonthString, endMonthDayString,
endYearString, endTimeString);
- return dateRange;
}
if (startDay != endDay) {
@@ -1496,12 +1523,11 @@ public class DateUtils
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat,
+ return formatter.format(fullFormat,
startWeekDayString, startMonthString, startMonthDayString,
startYearString, startTimeString,
endWeekDayString, endMonthString, endMonthDayString,
endYearString, endTimeString);
- return dateRange;
}
// Same start and end day
@@ -1522,6 +1548,7 @@ public class DateUtils
} else {
// Example: "10:00 - 11:00 am"
String timeFormat = res.getString(com.android.internal.R.string.time1_time2);
+ // Don't use the user supplied Formatter because the result will pollute the buffer.
timeString = String.format(timeFormat, startTimeString, endTimeString);
}
}
@@ -1545,7 +1572,7 @@ public class DateUtils
fullFormat = res.getString(com.android.internal.R.string.time_date);
} else {
// Example: "Oct 9"
- return dateString;
+ return formatter.format("%s", dateString);
}
}
} else if (showWeekDay) {
@@ -1554,16 +1581,15 @@ public class DateUtils
fullFormat = res.getString(com.android.internal.R.string.time_wday);
} else {
// Example: "Tue"
- return startWeekDayString;
+ return formatter.format("%s", startWeekDayString);
}
} else if (showTime) {
- return timeString;
+ return formatter.format("%s", timeString);
}
// The values that are used in a fullFormat string are specified
// by position.
- dateRange = String.format(fullFormat, timeString, startWeekDayString, dateString);
- return dateRange;
+ return formatter.format(fullFormat, timeString, startWeekDayString, dateString);
}
/**
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 92f6289..ab33cb3 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -22,6 +22,7 @@ import android.graphics.Rect;
import android.text.*;
import android.widget.TextView;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.MotionEvent;
// XXX this doesn't extend MetaKeyKeyListener because the signatures
@@ -256,8 +257,32 @@ implements MovementMethod
(MetaKeyKeyListener.getMetaState(buffer,
MetaKeyKeyListener.META_SELECTING) != 0);
+ DoubleTapState[] tap = buffer.getSpans(0, buffer.length(),
+ DoubleTapState.class);
+ boolean doubletap = false;
+
+ if (tap.length > 0) {
+ if (event.getEventTime() - tap[0].mWhen <=
+ ViewConfiguration.getDoubleTapTimeout()) {
+ if (sameWord(buffer, off, Selection.getSelectionEnd(buffer))) {
+ doubletap = true;
+ }
+ }
+
+ tap[0].mWhen = event.getEventTime();
+ } else {
+ DoubleTapState newtap = new DoubleTapState();
+ newtap.mWhen = event.getEventTime();
+ buffer.setSpan(newtap, 0, buffer.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
if (cap) {
Selection.extendSelection(buffer, off);
+ } else if (doubletap) {
+ Selection.setSelection(buffer,
+ findWordStart(buffer, off),
+ findWordEnd(buffer, off));
} else {
Selection.setSelection(buffer, off);
}
@@ -272,6 +297,62 @@ implements MovementMethod
return handled;
}
+ private static class DoubleTapState implements NoCopySpan {
+ long mWhen;
+ }
+
+ private static boolean sameWord(CharSequence text, int one, int two) {
+ int start = findWordStart(text, one);
+ int end = findWordEnd(text, one);
+
+ if (end == start) {
+ return false;
+ }
+
+ return start == findWordStart(text, two) &&
+ end == findWordEnd(text, two);
+ }
+
+ // TODO: Unify with TextView.getWordForDictionary()
+ private static int findWordStart(CharSequence text, int start) {
+ for (; start > 0; start--) {
+ char c = text.charAt(start - 1);
+ int type = Character.getType(c);
+
+ if (c != '\'' &&
+ type != Character.UPPERCASE_LETTER &&
+ type != Character.LOWERCASE_LETTER &&
+ type != Character.TITLECASE_LETTER &&
+ type != Character.MODIFIER_LETTER &&
+ type != Character.DECIMAL_DIGIT_NUMBER) {
+ break;
+ }
+ }
+
+ return start;
+ }
+
+ // TODO: Unify with TextView.getWordForDictionary()
+ private static int findWordEnd(CharSequence text, int end) {
+ int len = text.length();
+
+ for (; end < len; end++) {
+ char c = text.charAt(end);
+ int type = Character.getType(c);
+
+ if (c != '\'' &&
+ type != Character.UPPERCASE_LETTER &&
+ type != Character.LOWERCASE_LETTER &&
+ type != Character.TITLECASE_LETTER &&
+ type != Character.MODIFIER_LETTER &&
+ type != Character.DECIMAL_DIGIT_NUMBER) {
+ break;
+ }
+ }
+
+ return end;
+ }
+
public boolean canSelectArbitrarily() {
return true;
}
diff --git a/core/java/android/text/method/CharacterPickerDialog.java b/core/java/android/text/method/CharacterPickerDialog.java
index 3c406751..880e46d 100644
--- a/core/java/android/text/method/CharacterPickerDialog.java
+++ b/core/java/android/text/method/CharacterPickerDialog.java
@@ -25,15 +25,14 @@ import android.text.*;
import android.view.LayoutInflater;
import android.view.View.OnClickListener;
import android.view.View;
-import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup;
+import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
-import android.widget.TextView;
/**
* Dialog for choosing accented characters related to a base character.
@@ -45,6 +44,7 @@ public class CharacterPickerDialog extends Dialog
private String mOptions;
private boolean mInsert;
private LayoutInflater mInflater;
+ private Button mCancelButton;
/**
* Creates a new CharacterPickerDialog that presents the specified
@@ -54,7 +54,7 @@ public class CharacterPickerDialog extends Dialog
public CharacterPickerDialog(Context context, View view,
Editable text, String options,
boolean insert) {
- super(context);
+ super(context, com.android.internal.R.style.Theme_Panel);
mView = view;
mText = text;
@@ -70,28 +70,32 @@ public class CharacterPickerDialog extends Dialog
WindowManager.LayoutParams params = getWindow().getAttributes();
params.token = mView.getApplicationWindowToken();
params.type = params.TYPE_APPLICATION_ATTACHED_DIALOG;
+ params.flags = params.flags | Window.FEATURE_NO_TITLE;
- setTitle(R.string.select_character);
setContentView(R.layout.character_picker);
GridView grid = (GridView) findViewById(R.id.characterPicker);
grid.setAdapter(new OptionsAdapter(getContext()));
grid.setOnItemClickListener(this);
- findViewById(R.id.cancel).setOnClickListener(this);
+ mCancelButton = (Button) findViewById(R.id.cancel);
+ mCancelButton.setOnClickListener(this);
}
/**
* Handles clicks on the character buttons.
*/
public void onItemClick(AdapterView parent, View view, int position, long id) {
- int selEnd = Selection.getSelectionEnd(mText);
String result = String.valueOf(mOptions.charAt(position));
+ replaceCharacterAndClose(result);
+ }
+ private void replaceCharacterAndClose(CharSequence replace) {
+ int selEnd = Selection.getSelectionEnd(mText);
if (mInsert || selEnd == 0) {
- mText.insert(selEnd, result);
+ mText.insert(selEnd, replace);
} else {
- mText.replace(selEnd - 1, selEnd, result);
+ mText.replace(selEnd - 1, selEnd, replace);
}
dismiss();
@@ -101,21 +105,25 @@ public class CharacterPickerDialog extends Dialog
* Handles clicks on the Cancel button.
*/
public void onClick(View v) {
- dismiss();
+ if (v == mCancelButton) {
+ dismiss();
+ } else if (v instanceof Button) {
+ CharSequence result = ((Button) v).getText();
+ replaceCharacterAndClose(result);
+ }
}
private class OptionsAdapter extends BaseAdapter {
- private Context mContext;
public OptionsAdapter(Context context) {
super();
- mContext = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
Button b = (Button)
mInflater.inflate(R.layout.character_picker_button, null);
b.setText(String.valueOf(mOptions.charAt(position)));
+ b.setOnClickListener(CharacterPickerDialog.this);
return b;
}
diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java
index e420c27..172e9ac 100644
--- a/core/java/android/text/method/QwertyKeyListener.java
+++ b/core/java/android/text/method/QwertyKeyListener.java
@@ -434,7 +434,7 @@ public class QwertyKeyListener extends BaseKeyListener {
PICKER_SETS.put('y', "\u00FD\u00FF");
PICKER_SETS.put('z', "\u017A\u017C\u017E");
PICKER_SETS.put(KeyCharacterMap.PICKER_DIALOG_INPUT,
- "\u2026\u00A5\u2022\u00AE\u00A9\u00B1");
+ "\u2026\u00A5\u2022\u00AE\u00A9\u00B1[]{}\\");
};
private boolean showCharacterPicker(View view, Editable content, char c,
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 484f8ce..1214040 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -24,13 +24,28 @@ import android.text.TextUtils;
public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableSpan {
private final int mSize;
+ private boolean mDip;
+ /**
+ * Set the text size to <code>size</code> physical pixels.
+ */
public AbsoluteSizeSpan(int size) {
mSize = size;
}
+ /**
+ * Set the text size to <code>size</code> physical pixels,
+ * or to <code>size</code> device-independent pixels if
+ * <code>dip</code> is true.
+ */
+ public AbsoluteSizeSpan(int size, boolean dip) {
+ mSize = size;
+ mDip = dip;
+ }
+
public AbsoluteSizeSpan(Parcel src) {
mSize = src.readInt();
+ mDip = src.readInt() != 0;
}
public int getSpanTypeId() {
@@ -43,19 +58,32 @@ public class AbsoluteSizeSpan extends MetricAffectingSpan implements ParcelableS
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mSize);
+ dest.writeInt(mDip ? 1 : 0);
}
public int getSize() {
return mSize;
}
+ public boolean getDip() {
+ return mDip;
+ }
+
@Override
public void updateDrawState(TextPaint ds) {
- ds.setTextSize(mSize);
+ if (mDip) {
+ ds.setTextSize(mSize * ds.density);
+ } else {
+ ds.setTextSize(mSize);
+ }
}
@Override
public void updateMeasureState(TextPaint ds) {
- ds.setTextSize(mSize);
+ if (mDip) {
+ ds.setTextSize(mSize * ds.density);
+ } else {
+ ds.setTextSize(mSize);
+ }
}
}
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 86ef5f6..74b9463 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -36,6 +36,7 @@ public class ImageSpan extends DynamicDrawableSpan {
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap)} instead.
*/
+ @Deprecated
public ImageSpan(Bitmap b) {
this(null, b, ALIGN_BOTTOM);
}
@@ -43,6 +44,7 @@ public class ImageSpan extends DynamicDrawableSpan {
/**
* @deprecated Use {@link #ImageSpan(Context, Bitmap, int) instead.
*/
+ @Deprecated
public ImageSpan(Bitmap b, int verticalAlignment) {
this(null, b, verticalAlignment);
}
diff --git a/core/java/android/text/style/LineHeightSpan.java b/core/java/android/text/style/LineHeightSpan.java
index c0ef97c..44a1706 100644
--- a/core/java/android/text/style/LineHeightSpan.java
+++ b/core/java/android/text/style/LineHeightSpan.java
@@ -19,6 +19,7 @@ package android.text.style;
import android.graphics.Paint;
import android.graphics.Canvas;
import android.text.Layout;
+import android.text.TextPaint;
public interface LineHeightSpan
extends ParagraphStyle, WrapTogetherSpan
@@ -26,4 +27,10 @@ extends ParagraphStyle, WrapTogetherSpan
public void chooseHeight(CharSequence text, int start, int end,
int spanstartv, int v,
Paint.FontMetricsInt fm);
+
+ public interface WithDensity extends LineHeightSpan {
+ public void chooseHeight(CharSequence text, int start, int end,
+ int spanstartv, int v,
+ Paint.FontMetricsInt fm, TextPaint paint);
+ }
}
diff --git a/core/java/android/util/Config.java b/core/java/android/util/Config.java
index 9571041..924b49d 100644
--- a/core/java/android/util/Config.java
+++ b/core/java/android/util/Config.java
@@ -34,25 +34,25 @@ public final class Config
*/
/**
- * Always the inverse of DEBUG.
+ * @deprecated Use {@link #DEBUG} instead.
*/
@Deprecated
public static final boolean RELEASE = !DEBUG;
/**
- * Always false.
+ * @deprecated Always false.
*/
@Deprecated
public static final boolean PROFILE = false;
/**
- * Always false.
+ * @deprecated Always false.
*/
@Deprecated
public static final boolean LOGV = false;
/**
- * Always true.
+ * @deprecated Always true.
*/
@Deprecated
public static final boolean LOGD = true;
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 24b4f73..81dd96e 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -73,7 +73,7 @@ import java.util.List;
* </ul>
* </li>
* <li> '\n': 1 byte - an automatically generated newline, used to help detect and recover from log
- * corruption and enable stansard unix tools like grep, tail and wc to operate
+ * corruption and enable standard unix tools like grep, tail and wc to operate
* on event logs. </li>
* </ul>
*
@@ -124,10 +124,6 @@ public class EventLog {
"A List must have fewer than "
+ Byte.MAX_VALUE + " items in it.");
}
- if (items.length < 1) {
- throw new IllegalArgumentException(
- "A List must have at least one item in it.");
- }
for (int i = 0; i < items.length; i++) {
final Object item = items[i];
if (item == null) {
@@ -192,17 +188,21 @@ public class EventLog {
return decodeObject();
}
+ public byte[] getRawData() {
+ return mBuffer.array();
+ }
+
/** @return the loggable item at the current position in mBuffer. */
private Object decodeObject() {
if (mBuffer.remaining() < 1) return null;
switch (mBuffer.get()) {
case INT:
if (mBuffer.remaining() < 4) return null;
- return mBuffer.getInt();
+ return (Integer) mBuffer.getInt();
case LONG:
if (mBuffer.remaining() < 8) return null;
- return mBuffer.getLong();
+ return (Long) mBuffer.getLong();
case STRING:
try {
@@ -219,7 +219,7 @@ public class EventLog {
case LIST:
if (mBuffer.remaining() < 1) return null;
int length = mBuffer.get();
- if (length <= 0) return null;
+ if (length < 0) return null;
Object[] array = new Object[length];
for (int i = 0; i < length; ++i) {
array[i] = decodeObject();
@@ -285,4 +285,13 @@ public class EventLog {
*/
public static native void readEvents(int[] tags, Collection<Event> output)
throws IOException;
+
+ /**
+ * Read events from a file.
+ * @param path to read from
+ * @param output container to add events into
+ * @throws IOException if something goes wrong reading events
+ */
+ public static native void readEvents(String path, Collection<Event> output)
+ throws IOException;
}
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
new file mode 100644
index 0000000..b35dd1e
--- /dev/null
+++ b/core/java/android/util/MathUtils.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.util.Random;
+
+/**
+ * A class that contains utility methods related to numbers.
+ *
+ * @hide Pending API council approval
+ */
+public final class MathUtils {
+ private static final Random sRandom = new Random();
+ private static final float DEG_TO_RAD = 3.1415926f / 180.0f;
+ private static final float RAD_TO_DEG = 180.0f / 3.1415926f;
+
+ private MathUtils() {
+ }
+
+ public static float abs(float v) {
+ return v > 0 ? v : -v;
+ }
+
+ public static int constrain(int amount, int low, int high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ public static float constrain(float amount, float low, float high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ public static float log(float a) {
+ return (float) Math.log(a);
+ }
+
+ public static float exp(float a) {
+ return (float) Math.exp(a);
+ }
+
+ public static float pow(float a, float b) {
+ return (float) Math.pow(a, b);
+ }
+
+ public static float max(float a, float b) {
+ return a > b ? a : b;
+ }
+
+ public static float max(int a, int b) {
+ return a > b ? a : b;
+ }
+
+ public static float max(float a, float b, float c) {
+ return a > b ? (a > c ? a : c) : (b > c ? b : c);
+ }
+
+ public static float max(int a, int b, int c) {
+ return a > b ? (a > c ? a : c) : (b > c ? b : c);
+ }
+
+ public static float min(float a, float b) {
+ return a < b ? a : b;
+ }
+
+ public static float min(int a, int b) {
+ return a < b ? a : b;
+ }
+
+ public static float min(float a, float b, float c) {
+ return a < b ? (a < c ? a : c) : (b < c ? b : c);
+ }
+
+ public static float min(int a, int b, int c) {
+ return a < b ? (a < c ? a : c) : (b < c ? b : c);
+ }
+
+ public static float dist(float x1, float y1, float x2, float y2) {
+ final float x = (x2 - x1);
+ final float y = (y2 - y1);
+ return (float) Math.sqrt(x * x + y * y);
+ }
+
+ public static float dist(float x1, float y1, float z1, float x2, float y2, float z2) {
+ final float x = (x2 - x1);
+ final float y = (y2 - y1);
+ final float z = (z2 - z1);
+ return (float) Math.sqrt(x * x + y * y + z * z);
+ }
+
+ public static float mag(float a, float b) {
+ return (float) Math.sqrt(a * a + b * b);
+ }
+
+ public static float mag(float a, float b, float c) {
+ return (float) Math.sqrt(a * a + b * b + c * c);
+ }
+
+ public static float sq(float v) {
+ return v * v;
+ }
+
+ public static float radians(float degrees) {
+ return degrees * DEG_TO_RAD;
+ }
+
+ public static float degrees(float radians) {
+ return radians * RAD_TO_DEG;
+ }
+
+ public static float acos(float value) {
+ return (float) Math.acos(value);
+ }
+
+ public static float asin(float value) {
+ return (float) Math.asin(value);
+ }
+
+ public static float atan(float value) {
+ return (float) Math.atan(value);
+ }
+
+ public static float atan2(float a, float b) {
+ return (float) Math.atan2(a, b);
+ }
+
+ public static float tan(float angle) {
+ return (float) Math.tan(angle);
+ }
+
+ public static float lerp(float start, float stop, float amount) {
+ return start + (stop - start) * amount;
+ }
+
+ public static float norm(float start, float stop, float value) {
+ return (value - start) / (stop - start);
+ }
+
+ public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
+ return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
+ }
+
+ public static int random(int howbig) {
+ return (int) (sRandom.nextFloat() * howbig);
+ }
+
+ public static int random(int howsmall, int howbig) {
+ if (howsmall >= howbig) return howsmall;
+ return (int) (sRandom.nextFloat() * (howbig - howsmall) + howsmall);
+ }
+
+ public static float random(float howbig) {
+ return sRandom.nextFloat() * howbig;
+ }
+
+ public static float random(float howsmall, float howbig) {
+ if (howsmall >= howbig) return howsmall;
+ return sRandom.nextFloat() * (howbig - howsmall) + howsmall;
+ }
+
+ public static void randomSeed(long seed) {
+ sRandom.setSeed(seed);
+ }
+}
diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java
new file mode 100644
index 0000000..bf25306
--- /dev/null
+++ b/core/java/android/util/Pair.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Container to ease passing around a tuple of two objects. This object provides a sensible
+ * implementation of equals(), returning true if equals() is true on each of the contained
+ * objects.
+ */
+public class Pair<F, S> {
+ public final F first;
+ public final S second;
+
+ /**
+ * Constructor for a Pair. If either are null then equals() and hashCode() will throw
+ * a NullPointerException.
+ * @param first the first object in the Pair
+ * @param second the second object in the pair
+ */
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+
+ /**
+ * Checks the two objects for equality by delegating to their respective equals() methods.
+ * @param o the Pair to which this one is to be checked for equality
+ * @return true if the underlying objects of the Pair are both considered equals()
+ */
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof Pair)) return false;
+ final Pair<F, S> other;
+ try {
+ other = (Pair<F, S>) o;
+ } catch (ClassCastException e) {
+ return false;
+ }
+ return first.equals(other.first) && second.equals(other.second);
+ }
+
+ /**
+ * Compute a hash code using the hash codes of the underlying objects
+ * @return a hashcode of the Pair
+ */
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + first.hashCode();
+ result = 31 * result + second.hashCode();
+ return result;
+ }
+
+ /**
+ * Convenience method for creating an appropriately typed pair.
+ * @param a the first object in the Pair
+ * @param b the second object in the pair
+ * @return a Pair that is templatized with the types of a and b
+ */
+ public static <A, B> Pair <A, B> create(A a, B b) {
+ return new Pair<A, B>(a, b);
+ }
+}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 841066c..f936f65 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -24,9 +24,18 @@ public class HapticFeedbackConstants {
private HapticFeedbackConstants() {}
+ /**
+ * The user has performed a long press on an object that is resulting
+ * in an action being performed.
+ */
public static final int LONG_PRESS = 0;
/**
+ * The user has pressed on a virtual on-screen key.
+ */
+ public static final int VIRTUAL_KEY = 1;
+
+ /**
* Flag for {@link View#performHapticFeedback(int, int)
* View.performHapticFeedback(int, int)}: Ignore the setting in the
* view for whether to perform haptic feedback, do it always.
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 99d5c0c..ebc5f7b 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -46,8 +46,8 @@ oneway interface IWindow {
void resized(int w, int h, in Rect coveredInsets, in Rect visibleInsets,
boolean reportDraw);
void dispatchKey(in KeyEvent event);
- void dispatchPointer(in MotionEvent event, long eventTime);
- void dispatchTrackball(in MotionEvent event, long eventTime);
+ void dispatchPointer(in MotionEvent event, long eventTime, boolean callWhenDone);
+ void dispatchTrackball(in MotionEvent event, long eventTime, boolean callWhenDone);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
@@ -56,4 +56,9 @@ oneway interface IWindow {
* to date on the current state showing navigational focus (touch mode) too.
*/
void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+
+ /**
+ * Called for wallpaper windows when their offsets change.
+ */
+ void dispatchWallpaperOffsets(float x, float y);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 5607d4b..3e6cdc2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -90,7 +90,6 @@ interface IWindowManager
void exitKeyguardSecurely(IOnKeyguardExitResult callback);
boolean inKeyguardRestrictedInputMode();
-
// These can only be called with the SET_ANIMATON_SCALE permission.
float getAnimationScale(int which);
float[] getAnimationScales();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1156856..4d662d2 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -108,4 +108,10 @@ interface IWindowSession {
boolean getInTouchMode();
boolean performHapticFeedback(IWindow window, int effectId, boolean always);
+
+ /**
+ * For windows with the wallpaper behind them, and the wallpaper is
+ * larger than the screen, set the offset within the screen.
+ */
+ void setWallpaperPosition(IBinder windowToken, float x, float y);
}
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 6349288..f9b16fc 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -258,6 +258,25 @@ public class KeyEvent implements Parcelable {
public static final int FLAG_EDITOR_ACTION = 0x10;
/**
+ * When associated with up key events, this indicates that the key press
+ * has been canceled. Typically this is used with virtual touch screen
+ * keys, where the user can slide from the virtual key area on to the
+ * display: in that case, the application will receive a canceled up
+ * event and should not perform the action normally associated with the
+ * key. Note that for this to work, the application can not perform an
+ * action for a key until it receives an up or the long press timeout has
+ * expired.
+ */
+ public static final int FLAG_CANCELED = 0x20;
+
+ /**
+ * This key event was generated by a virtual (on-screen) hard key area.
+ * Typically this is an area of the touchscreen, outside of the regular
+ * display, dedicated to "hardware" buttons.
+ */
+ public static final int FLAG_VIRTUAL_HARD_KEY = 0x40;
+
+ /**
* Returns the maximum keycode.
*/
public static int getMaxKeyCode() {
@@ -694,6 +713,14 @@ public class KeyEvent implements Parcelable {
}
/**
+ * For {@link #ACTION_UP} events, indicates that the event has been
+ * canceled as per {@link #FLAG_CANCELED}.
+ */
+ public final boolean isCanceled() {
+ return (mFlags&FLAG_CANCELED) != 0;
+ }
+
+ /**
* Retrieve the key code of the key event. This is the physical key that
* was pressed, <em>not</em> the Unicode character.
*
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index a224ed3..b2f0c60 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -19,7 +19,7 @@ package android.view;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.util.Config;
+import android.util.Log;
/**
* Object used to report movement (mouse, pen, finger, trackball) events. This
@@ -27,17 +27,26 @@ import android.util.Config;
* it is being used for.
*/
public final class MotionEvent implements Parcelable {
+ static final boolean DEBUG_POINTERS = false;
+
+ /**
+ * Bit mask of the parts of the action code that are the action itself.
+ */
+ public static final int ACTION_MASK = 0xff;
+
/**
* Constant for {@link #getAction}: A pressed gesture has started, the
* motion contains the initial starting location.
*/
public static final int ACTION_DOWN = 0;
+
/**
* Constant for {@link #getAction}: A pressed gesture has finished, the
* motion contains the final release location as well as any intermediate
* points since the last down or move event.
*/
public static final int ACTION_UP = 1;
+
/**
* Constant for {@link #getAction}: A change has happened during a
* press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}).
@@ -45,12 +54,14 @@ public final class MotionEvent implements Parcelable {
* points since the last down or move event.
*/
public static final int ACTION_MOVE = 2;
+
/**
* Constant for {@link #getAction}: The current gesture has been aborted.
* You will not receive any more points in it. You should treat this as
* an up event, but not perform any action that you normally would.
*/
public static final int ACTION_CANCEL = 3;
+
/**
* Constant for {@link #getAction}: A movement has happened outside of the
* normal bounds of the UI element. This does not provide a full gesture,
@@ -58,6 +69,70 @@ public final class MotionEvent implements Parcelable {
*/
public static final int ACTION_OUTSIDE = 4;
+ /**
+ * A non-primary pointer has gone down. The bits in
+ * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed.
+ */
+ public static final int ACTION_POINTER_DOWN = 5;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_DOWN} with
+ * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone done.
+ */
+ public static final int ACTION_POINTER_1_DOWN = ACTION_POINTER_DOWN | 0x0000;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_DOWN} with
+ * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone done.
+ */
+ public static final int ACTION_POINTER_2_DOWN = ACTION_POINTER_DOWN | 0x0100;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_DOWN} with
+ * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone done.
+ */
+ public static final int ACTION_POINTER_3_DOWN = ACTION_POINTER_DOWN | 0x0200;
+
+ /**
+ * A non-primary pointer has gone up. The bits in
+ * {@link #ACTION_POINTER_ID_MASK} indicate which pointer changed.
+ */
+ public static final int ACTION_POINTER_UP = 6;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_UP} with
+ * {@link #ACTION_POINTER_ID_MASK} of 0: the primary pointer has gone up.
+ */
+ public static final int ACTION_POINTER_1_UP = ACTION_POINTER_UP | 0x0000;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_UP} with
+ * {@link #ACTION_POINTER_ID_MASK} of 1: the secondary pointer has gone up.
+ */
+ public static final int ACTION_POINTER_2_UP = ACTION_POINTER_UP | 0x0100;
+
+ /**
+ * Synonym for {@link #ACTION_POINTER_UP} with
+ * {@link #ACTION_POINTER_ID_MASK} of 2: the tertiary pointer has gone up.
+ */
+ public static final int ACTION_POINTER_3_UP = ACTION_POINTER_UP | 0x0200;
+
+ /**
+ * Bits in the action code that represent a pointer ID, used with
+ * {@link #ACTION_POINTER_DOWN} and {@link #ACTION_POINTER_UP}. Pointer IDs
+ * start at 0, with 0 being the primary (first) pointer in the motion. Note
+ * that this not <em>not</em> an index into the array of pointer values,
+ * which is compacted to only contain pointers that are down; the pointer
+ * ID for a particular index can be found with {@link #findPointerIndex}.
+ */
+ public static final int ACTION_POINTER_ID_MASK = 0xff00;
+
+ /**
+ * Bit shift for the action bits holding the pointer identifier as
+ * defined by {@link #ACTION_POINTER_ID_MASK}.
+ */
+ public static final int ACTION_POINTER_ID_SHIFT = 8;
+
private static final boolean TRACK_RECYCLED_LOCATION = false;
/**
@@ -80,34 +155,83 @@ public final class MotionEvent implements Parcelable {
*/
public static final int EDGE_RIGHT = 0x00000008;
+ /**
+ * Offset for the sample's X coordinate.
+ * @hide
+ */
+ static public final int SAMPLE_X = 0;
+
+ /**
+ * Offset for the sample's Y coordinate.
+ * @hide
+ */
+ static public final int SAMPLE_Y = 1;
+
+ /**
+ * Offset for the sample's X coordinate.
+ * @hide
+ */
+ static public final int SAMPLE_PRESSURE = 2;
+
+ /**
+ * Offset for the sample's X coordinate.
+ * @hide
+ */
+ static public final int SAMPLE_SIZE = 3;
+
+ /**
+ * Number of data items for each sample.
+ * @hide
+ */
+ static public final int NUM_SAMPLE_DATA = 4;
+
+ /**
+ * Number of possible pointers.
+ * @hide
+ */
+ static public final int BASE_AVAIL_POINTERS = 5;
+
+ static private final int BASE_AVAIL_SAMPLES = 8;
+
static private final int MAX_RECYCLED = 10;
static private Object gRecyclerLock = new Object();
static private int gRecyclerUsed = 0;
static private MotionEvent gRecyclerTop = null;
private long mDownTime;
- private long mEventTime;
+ private long mEventTimeNano;
private int mAction;
- private float mX;
- private float mY;
private float mRawX;
private float mRawY;
- private float mPressure;
- private float mSize;
- private int mMetaState;
- private int mNumHistory;
- private float[] mHistory;
- private long[] mHistoryTimes;
private float mXPrecision;
private float mYPrecision;
private int mDeviceId;
private int mEdgeFlags;
+ private int mMetaState;
+
+ // Here is the actual event data. Note that the order of the array
+ // is a little odd: the first entry is the most recent, and the ones
+ // following it are the historical data from oldest to newest. This
+ // allows us to easily retrieve the most recent data, without having
+ // to copy the arrays every time a new sample is added.
+
+ private int mNumPointers;
+ private int mNumSamples;
+ // Array of mNumPointers size of identifiers for each pointer of data.
+ private int[] mPointerIdentifiers;
+ // Array of (mNumSamples * mNumPointers * NUM_SAMPLE_DATA) size of event data.
+ private float[] mDataSamples;
+ // Array of mNumSamples size of time stamps.
+ private long[] mTimeSamples;
private MotionEvent mNext;
private RuntimeException mRecycledLocation;
private boolean mRecycled;
private MotionEvent() {
+ mPointerIdentifiers = new int[BASE_AVAIL_POINTERS];
+ mDataSamples = new float[BASE_AVAIL_POINTERS*BASE_AVAIL_SAMPLES*NUM_SAMPLE_DATA];
+ mTimeSamples = new long[BASE_AVAIL_SAMPLES];
}
static private MotionEvent obtain() {
@@ -127,6 +251,86 @@ public final class MotionEvent implements Parcelable {
/**
* Create a new MotionEvent, filling in all of the basic values that
* define the motion.
+ *
+ * @param downTime The time (in ms) when the user originally pressed down to start
+ * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param eventTime The the time (in ms) when this specific event was generated. This
+ * must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param eventTimeNano The the time (in ns) when this specific event was generated. This
+ * must be obtained from {@link System#nanoTime()}.
+ * @param action The kind of action being performed -- one of either
+ * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+ * {@link #ACTION_CANCEL}.
+ * @param pointers The number of points that will be in this event.
+ * @param inPointerIds An array of <em>pointers</em> values providing
+ * an identifier for each pointer.
+ * @param inData An array of <em>pointers*NUM_SAMPLE_DATA</em> of initial
+ * data samples for the event.
+ * @param metaState The state of any meta / modifier keys that were in effect when
+ * the event was generated.
+ * @param xPrecision The precision of the X coordinate being reported.
+ * @param yPrecision The precision of the Y coordinate being reported.
+ * @param deviceId The id for the device that this event came from. An id of
+ * zero indicates that the event didn't come from a physical device; other
+ * numbers are arbitrary and you shouldn't depend on the values.
+ * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+ * MotionEvent.
+ *
+ * @hide
+ */
+ static public MotionEvent obtainNano(long downTime, long eventTime, long eventTimeNano,
+ int action, int pointers, int[] inPointerIds, float[] inData, int metaState,
+ float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+ MotionEvent ev = obtain();
+ ev.mDeviceId = deviceId;
+ ev.mEdgeFlags = edgeFlags;
+ ev.mDownTime = downTime;
+ ev.mEventTimeNano = eventTimeNano;
+ ev.mAction = action;
+ ev.mMetaState = metaState;
+ ev.mRawX = inData[SAMPLE_X];
+ ev.mRawY = inData[SAMPLE_Y];
+ ev.mXPrecision = xPrecision;
+ ev.mYPrecision = yPrecision;
+ ev.mNumPointers = pointers;
+ ev.mNumSamples = 1;
+
+ int[] pointerIdentifiers = ev.mPointerIdentifiers;
+ if (pointerIdentifiers.length < pointers) {
+ ev.mPointerIdentifiers = pointerIdentifiers = new int[pointers];
+ }
+ System.arraycopy(inPointerIds, 0, pointerIdentifiers, 0, pointers);
+
+ final int ND = pointers * NUM_SAMPLE_DATA;
+ float[] dataSamples = ev.mDataSamples;
+ if (dataSamples.length < ND) {
+ ev.mDataSamples = dataSamples = new float[ND];
+ }
+ System.arraycopy(inData, 0, dataSamples, 0, ND);
+
+ ev.mTimeSamples[0] = eventTime;
+
+ if (DEBUG_POINTERS) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("New:");
+ for (int i=0; i<pointers; i++) {
+ sb.append(" #");
+ sb.append(ev.mPointerIdentifiers[i]);
+ sb.append("(");
+ sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]);
+ sb.append(",");
+ sb.append(ev.mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]);
+ sb.append(")");
+ }
+ Log.v("MotionEvent", sb.toString());
+ }
+
+ return ev;
+ }
+
+ /**
+ * Create a new MotionEvent, filling in all of the basic values that
+ * define the motion.
*
* @param downTime The time (in ms) when the user originally pressed down to start
* a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
@@ -162,16 +366,83 @@ public final class MotionEvent implements Parcelable {
ev.mDeviceId = deviceId;
ev.mEdgeFlags = edgeFlags;
ev.mDownTime = downTime;
- ev.mEventTime = eventTime;
+ ev.mEventTimeNano = eventTime * 1000000;
ev.mAction = action;
- ev.mX = ev.mRawX = x;
- ev.mY = ev.mRawY = y;
- ev.mPressure = pressure;
- ev.mSize = size;
ev.mMetaState = metaState;
ev.mXPrecision = xPrecision;
ev.mYPrecision = yPrecision;
+ ev.mNumPointers = 1;
+ ev.mNumSamples = 1;
+ int[] pointerIds = ev.mPointerIdentifiers;
+ pointerIds[0] = 0;
+ float[] data = ev.mDataSamples;
+ data[SAMPLE_X] = ev.mRawX = x;
+ data[SAMPLE_Y] = ev.mRawY = y;
+ data[SAMPLE_PRESSURE] = pressure;
+ data[SAMPLE_SIZE] = size;
+ ev.mTimeSamples[0] = eventTime;
+
+ return ev;
+ }
+
+ /**
+ * Create a new MotionEvent, filling in all of the basic values that
+ * define the motion.
+ *
+ * @param downTime The time (in ms) when the user originally pressed down to start
+ * a stream of position events. This must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param eventTime The the time (in ms) when this specific event was generated. This
+ * must be obtained from {@link SystemClock#uptimeMillis()}.
+ * @param action The kind of action being performed -- one of either
+ * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+ * {@link #ACTION_CANCEL}.
+ * @param pointers The number of pointers that are active in this event.
+ * @param x The X coordinate of this event.
+ * @param y The Y coordinate of this event.
+ * @param pressure The current pressure of this event. The pressure generally
+ * ranges from 0 (no pressure at all) to 1 (normal pressure), however
+ * values higher than 1 may be generated depending on the calibration of
+ * the input device.
+ * @param size A scaled value of the approximate size of the area being pressed when
+ * touched with the finger. The actual value in pixels corresponding to the finger
+ * touch is normalized with a device specific range of values
+ * and scaled to a value between 0 and 1.
+ * @param metaState The state of any meta / modifier keys that were in effect when
+ * the event was generated.
+ * @param xPrecision The precision of the X coordinate being reported.
+ * @param yPrecision The precision of the Y coordinate being reported.
+ * @param deviceId The id for the device that this event came from. An id of
+ * zero indicates that the event didn't come from a physical device; other
+ * numbers are arbitrary and you shouldn't depend on the values.
+ * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+ * MotionEvent.
+ */
+ static public MotionEvent obtain(long downTime, long eventTime, int action,
+ int pointers, float x, float y, float pressure, float size, int metaState,
+ float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+ MotionEvent ev = obtain();
+ ev.mDeviceId = deviceId;
+ ev.mEdgeFlags = edgeFlags;
+ ev.mDownTime = downTime;
+ ev.mEventTimeNano = eventTime * 1000000;
+ ev.mAction = action;
+ ev.mNumPointers = pointers;
+ ev.mMetaState = metaState;
+ ev.mXPrecision = xPrecision;
+ ev.mYPrecision = yPrecision;
+
+ ev.mNumPointers = 1;
+ ev.mNumSamples = 1;
+ int[] pointerIds = ev.mPointerIdentifiers;
+ pointerIds[0] = 0;
+ float[] data = ev.mDataSamples;
+ data[SAMPLE_X] = ev.mRawX = x;
+ data[SAMPLE_Y] = ev.mRawY = y;
+ data[SAMPLE_PRESSURE] = pressure;
+ data[SAMPLE_SIZE] = size;
+ ev.mTimeSamples[0] = eventTime;
+
return ev;
}
@@ -198,16 +469,24 @@ public final class MotionEvent implements Parcelable {
ev.mDeviceId = 0;
ev.mEdgeFlags = 0;
ev.mDownTime = downTime;
- ev.mEventTime = eventTime;
+ ev.mEventTimeNano = eventTime * 1000000;
ev.mAction = action;
- ev.mX = ev.mRawX = x;
- ev.mY = ev.mRawY = y;
- ev.mPressure = 1.0f;
- ev.mSize = 1.0f;
+ ev.mNumPointers = 1;
ev.mMetaState = metaState;
ev.mXPrecision = 1.0f;
ev.mYPrecision = 1.0f;
+ ev.mNumPointers = 1;
+ ev.mNumSamples = 1;
+ int[] pointerIds = ev.mPointerIdentifiers;
+ pointerIds[0] = 0;
+ float[] data = ev.mDataSamples;
+ data[SAMPLE_X] = ev.mRawX = x;
+ data[SAMPLE_Y] = ev.mRawY = y;
+ data[SAMPLE_PRESSURE] = 1.0f;
+ data[SAMPLE_SIZE] = 1.0f;
+ ev.mTimeSamples[0] = eventTime;
+
return ev;
}
@@ -217,72 +496,96 @@ public final class MotionEvent implements Parcelable {
* @hide
*/
public void scale(float scale) {
- mX *= scale;
- mY *= scale;
mRawX *= scale;
mRawY *= scale;
- mSize *= scale;
mXPrecision *= scale;
mYPrecision *= scale;
- if (mHistory != null) {
- float[] history = mHistory;
- int length = history.length;
- for (int i = 0; i < length; i += 4) {
- history[i] *= scale; // X
- history[i + 1] *= scale; // Y
- // no need to scale pressure ([i+2])
- history[i + 3] *= scale; // Size, TODO: square this?
- }
+ float[] history = mDataSamples;
+ final int length = mNumPointers * mNumSamples * NUM_SAMPLE_DATA;
+ for (int i = 0; i < length; i += NUM_SAMPLE_DATA) {
+ history[i + SAMPLE_X] *= scale;
+ history[i + SAMPLE_Y] *= scale;
+ // no need to scale pressure
+ history[i + SAMPLE_SIZE] *= scale; // TODO: square this?
}
}
/**
- * Translate the coordination of the event by given x and y.
- *
- * @hide
+ * Create a new MotionEvent, copying from an existing one.
*/
- public void translate(float dx, float dy) {
- mX += dx;
- mY += dy;
- mRawX += dx;
- mRawY += dx;
- if (mHistory != null) {
- float[] history = mHistory;
- int length = history.length;
- for (int i = 0; i < length; i += 4) {
- history[i] += dx; // X
- history[i + 1] += dy; // Y
- // no need to translate pressure (i+2) and size (i+3)
- }
+ static public MotionEvent obtain(MotionEvent o) {
+ MotionEvent ev = obtain();
+ ev.mDeviceId = o.mDeviceId;
+ ev.mEdgeFlags = o.mEdgeFlags;
+ ev.mDownTime = o.mDownTime;
+ ev.mEventTimeNano = o.mEventTimeNano;
+ ev.mAction = o.mAction;
+ ev.mNumPointers = o.mNumPointers;
+ ev.mRawX = o.mRawX;
+ ev.mRawY = o.mRawY;
+ ev.mMetaState = o.mMetaState;
+ ev.mXPrecision = o.mXPrecision;
+ ev.mYPrecision = o.mYPrecision;
+
+ final int NS = ev.mNumSamples = o.mNumSamples;
+ if (ev.mTimeSamples.length >= NS) {
+ System.arraycopy(o.mTimeSamples, 0, ev.mTimeSamples, 0, NS);
+ } else {
+ ev.mTimeSamples = (long[])o.mTimeSamples.clone();
}
+
+ final int NP = (ev.mNumPointers=o.mNumPointers);
+ if (ev.mPointerIdentifiers.length >= NP) {
+ System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP);
+ } else {
+ ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone();
+ }
+
+ final int ND = NP * NS * NUM_SAMPLE_DATA;
+ if (ev.mDataSamples.length >= ND) {
+ System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND);
+ } else {
+ ev.mDataSamples = (float[])o.mDataSamples.clone();
+ }
+
+ return ev;
}
/**
- * Create a new MotionEvent, copying from an existing one.
+ * Create a new MotionEvent, copying from an existing one, but not including
+ * any historical point information.
*/
- static public MotionEvent obtain(MotionEvent o) {
+ static public MotionEvent obtainNoHistory(MotionEvent o) {
MotionEvent ev = obtain();
ev.mDeviceId = o.mDeviceId;
ev.mEdgeFlags = o.mEdgeFlags;
ev.mDownTime = o.mDownTime;
- ev.mEventTime = o.mEventTime;
+ ev.mEventTimeNano = o.mEventTimeNano;
ev.mAction = o.mAction;
- ev.mX = o.mX;
+ ev.mNumPointers = o.mNumPointers;
ev.mRawX = o.mRawX;
- ev.mY = o.mY;
ev.mRawY = o.mRawY;
- ev.mPressure = o.mPressure;
- ev.mSize = o.mSize;
ev.mMetaState = o.mMetaState;
ev.mXPrecision = o.mXPrecision;
ev.mYPrecision = o.mYPrecision;
- final int N = o.mNumHistory;
- ev.mNumHistory = N;
- if (N > 0) {
- // could be more efficient about this...
- ev.mHistory = (float[])o.mHistory.clone();
- ev.mHistoryTimes = (long[])o.mHistoryTimes.clone();
+
+ ev.mNumSamples = 1;
+ ev.mTimeSamples[0] = o.mTimeSamples[0];
+
+ final int NP = (ev.mNumPointers=o.mNumPointers);
+ if (ev.mPointerIdentifiers.length >= NP) {
+ System.arraycopy(o.mPointerIdentifiers, 0, ev.mPointerIdentifiers, 0, NP);
+ } else {
+ ev.mPointerIdentifiers = (int[])o.mPointerIdentifiers.clone();
}
+
+ final int ND = NP * NUM_SAMPLE_DATA;
+ if (ev.mDataSamples.length >= ND) {
+ System.arraycopy(o.mDataSamples, 0, ev.mDataSamples, 0, ND);
+ } else {
+ ev.mDataSamples = (float[])o.mDataSamples.clone();
+ }
+
return ev;
}
@@ -305,7 +608,7 @@ public final class MotionEvent implements Parcelable {
synchronized (gRecyclerLock) {
if (gRecyclerUsed < MAX_RECYCLED) {
gRecyclerUsed++;
- mNumHistory = 0;
+ mNumSamples = 0;
mNext = gRecyclerTop;
gRecyclerTop = this;
}
@@ -333,44 +636,145 @@ public final class MotionEvent implements Parcelable {
* Returns the time (in ms) when this specific event was generated.
*/
public final long getEventTime() {
- return mEventTime;
+ return mTimeSamples[0];
}
/**
- * Returns the X coordinate of this event. Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
+ * Returns the time (in ns) when this specific event was generated.
+ * The value is in nanosecond precision but it may not have nanosecond accuracy.
+ *
+ * @hide
+ */
+ public final long getEventTimeNano() {
+ return mEventTimeNano;
+ }
+
+ /**
+ * {@link #getX(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
*/
public final float getX() {
- return mX;
+ return mDataSamples[SAMPLE_X];
}
/**
- * Returns the Y coordinate of this event. Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
+ * {@link #getY(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
*/
public final float getY() {
- return mY;
+ return mDataSamples[SAMPLE_Y];
+ }
+
+ /**
+ * {@link #getPressure(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getPressure() {
+ return mDataSamples[SAMPLE_PRESSURE];
+ }
+
+ /**
+ * {@link #getSize(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getSize() {
+ return mDataSamples[SAMPLE_SIZE];
+ }
+
+ /**
+ * The number of pointers of data contained in this event. Always
+ * >= 1.
+ */
+ public final int getPointerCount() {
+ return mNumPointers;
+ }
+
+ /**
+ * Return the pointer identifier associated with a particular pointer
+ * data index is this event. The identifier tells you the actual pointer
+ * number associated with the data, accounting for individual pointers
+ * going up and down since the start of the current gesture.
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ */
+ public final int getPointerId(int pointerIndex) {
+ return mPointerIdentifiers[pointerIndex];
+ }
+
+ /**
+ * Given a pointer identifier, find the index of its data in the event.
+ *
+ * @param pointerId The identifier of the pointer to be found.
+ * @return Returns either the index of the pointer (for use with
+ * {@link #getX(int) et al.), or -1 if there is no data available for
+ * that pointer identifier.
+ */
+ public final int findPointerIndex(int pointerId) {
+ int i = mNumPointers;
+ while (i > 0) {
+ i--;
+ if (mPointerIdentifiers[i] == pointerId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the X coordinate of this event for the given pointer
+ * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
+ * identifier for this index).
+ * Whole numbers are pixels; the
+ * value may have a fraction for input devices that are sub-pixel precise.
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ */
+ public final float getX(int pointerIndex) {
+ return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_X];
}
/**
- * Returns the current pressure of this event. The pressure generally
+ * Returns the Y coordinate of this event for the given pointer
+ * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
+ * identifier for this index).
+ * Whole numbers are pixels; the
+ * value may have a fraction for input devices that are sub-pixel precise.
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ */
+ public final float getY(int pointerIndex) {
+ return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_Y];
+ }
+
+ /**
+ * Returns the current pressure of this event for the given pointer
+ * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
+ * identifier for this index).
+ * The pressure generally
* ranges from 0 (no pressure at all) to 1 (normal pressure), however
* values higher than 1 may be generated depending on the calibration of
* the input device.
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
- public final float getPressure() {
- return mPressure;
+ public final float getPressure(int pointerIndex) {
+ return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
}
/**
- * Returns a scaled value of the approximate size, of the area being pressed when
- * touched with the finger. The actual value in pixels corresponding to the finger
- * touch is normalized with the device specific range of values
+ * Returns a scaled value of the approximate size for the given pointer
+ * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
+ * identifier for this index).
+ * This represents some approximation of the area of the screen being
+ * pressed; the actual value in pixels corresponding to the
+ * touch is normalized with the device specific range of values
* and scaled to a value between 0 and 1. The value of size can be used to
* determine fat touch events.
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
*/
- public final float getSize() {
- return mSize;
+ public final float getSize(int pointerIndex) {
+ return mDataSamples[(pointerIndex*NUM_SAMPLE_DATA) + SAMPLE_SIZE];
}
/**
@@ -436,7 +840,7 @@ public final class MotionEvent implements Parcelable {
* @return Returns the number of historical points in the event.
*/
public final int getHistorySize() {
- return mNumHistory;
+ return mNumSamples - 1;
}
/**
@@ -450,63 +854,111 @@ public final class MotionEvent implements Parcelable {
* @see #getEventTime
*/
public final long getHistoricalEventTime(int pos) {
- return mHistoryTimes[pos];
+ return mTimeSamples[pos + 1];
}
/**
- * Returns a historical X coordinate that occurred between this event
- * and the previous event. Only applies to ACTION_MOVE events.
+ * {@link #getHistoricalX(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getHistoricalX(int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_X];
+ }
+
+ /**
+ * {@link #getHistoricalY(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getHistoricalY(int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_Y];
+ }
+
+ /**
+ * {@link #getHistoricalPressure(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getHistoricalPressure(int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_PRESSURE];
+ }
+
+ /**
+ * {@link #getHistoricalSize(int)} for the first pointer index (may be an
+ * arbitrary pointer identifier).
+ */
+ public final float getHistoricalSize(int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers) + SAMPLE_SIZE];
+ }
+
+ /**
+ * Returns a historical X coordinate, as per {@link #getX(int)}, that
+ * occurred between this event and the previous event for the given pointer.
+ * Only applies to ACTION_MOVE events.
*
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
* @param pos Which historical value to return; must be less than
* {@link #getHistorySize}
*
* @see #getHistorySize
* @see #getX
*/
- public final float getHistoricalX(int pos) {
- return mHistory[pos*4];
+ public final float getHistoricalX(int pointerIndex, int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_X];
}
/**
- * Returns a historical Y coordinate that occurred between this event
- * and the previous event. Only applies to ACTION_MOVE events.
+ * Returns a historical Y coordinate, as per {@link #getY(int)}, that
+ * occurred between this event and the previous event for the given pointer.
+ * Only applies to ACTION_MOVE events.
*
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
* @param pos Which historical value to return; must be less than
* {@link #getHistorySize}
*
* @see #getHistorySize
* @see #getY
*/
- public final float getHistoricalY(int pos) {
- return mHistory[pos*4 + 1];
+ public final float getHistoricalY(int pointerIndex, int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_Y];
}
/**
- * Returns a historical pressure coordinate that occurred between this event
- * and the previous event. Only applies to ACTION_MOVE events.
+ * Returns a historical pressure coordinate, as per {@link #getPressure(int)},
+ * that occurred between this event and the previous event for the given
+ * pointer. Only applies to ACTION_MOVE events.
*
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
* @param pos Which historical value to return; must be less than
* {@link #getHistorySize}
- *
+ *
* @see #getHistorySize
* @see #getPressure
*/
- public final float getHistoricalPressure(int pos) {
- return mHistory[pos*4 + 2];
+ public final float getHistoricalPressure(int pointerIndex, int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_PRESSURE];
}
/**
- * Returns a historical size coordinate that occurred between this event
- * and the previous event. Only applies to ACTION_MOVE events.
+ * Returns a historical size coordinate, as per {@link #getSize(int)}, that
+ * occurred between this event and the previous event for the given pointer.
+ * Only applies to ACTION_MOVE events.
*
+ * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
+ * (the first pointer that is down) to {@link #getPointerCount()}-1.
* @param pos Which historical value to return; must be less than
* {@link #getHistorySize}
- *
+ *
* @see #getHistorySize
* @see #getSize
*/
- public final float getHistoricalSize(int pos) {
- return mHistory[pos*4 + 3];
+ public final float getHistoricalSize(int pointerIndex, int pos) {
+ return mDataSamples[((pos + 1) * NUM_SAMPLE_DATA * mNumPointers)
+ + (pointerIndex * NUM_SAMPLE_DATA) + SAMPLE_SIZE];
}
/**
@@ -556,16 +1008,11 @@ public final class MotionEvent implements Parcelable {
* @param deltaY Amount to add to the current Y coordinate of the event.
*/
public final void offsetLocation(float deltaX, float deltaY) {
- mX += deltaX;
- mY += deltaY;
- final int N = mNumHistory*4;
- if (N <= 0) {
- return;
- }
- final float[] pos = mHistory;
- for (int i=0; i<N; i+=4) {
- pos[i] += deltaX;
- pos[i+1] += deltaY;
+ final int N = mNumPointers*mNumSamples*4;
+ final float[] pos = mDataSamples;
+ for (int i=0; i<N; i+=NUM_SAMPLE_DATA) {
+ pos[i+SAMPLE_X] += deltaX;
+ pos[i+SAMPLE_Y] += deltaY;
}
}
@@ -577,8 +1024,8 @@ public final class MotionEvent implements Parcelable {
* @param y New absolute Y location.
*/
public final void setLocation(float x, float y) {
- float deltaX = x-mX;
- float deltaY = y-mY;
+ float deltaX = x-mDataSamples[SAMPLE_X];
+ float deltaY = y-mDataSamples[SAMPLE_Y];
if (deltaX != 0 || deltaY != 0) {
offsetLocation(deltaX, deltaY);
}
@@ -590,58 +1037,119 @@ public final class MotionEvent implements Parcelable {
* the future, the current values in the event will be added to a list of
* historic values.
*
+ * @param eventTime The time stamp for this data.
* @param x The new X position.
* @param y The new Y position.
* @param pressure The new pressure.
* @param size The new size.
+ * @param metaState Meta key state.
*/
public final void addBatch(long eventTime, float x, float y,
float pressure, float size, int metaState) {
- float[] history = mHistory;
- long[] historyTimes = mHistoryTimes;
- int N;
- int avail;
- if (history == null) {
- mHistory = history = new float[8*4];
- mHistoryTimes = historyTimes = new long[8];
- mNumHistory = N = 0;
- avail = 8;
- } else {
- N = mNumHistory;
- avail = history.length/4;
- if (N == avail) {
- avail += 8;
- float[] newHistory = new float[avail*4];
- System.arraycopy(history, 0, newHistory, 0, N*4);
- mHistory = history = newHistory;
- long[] newHistoryTimes = new long[avail];
- System.arraycopy(historyTimes, 0, newHistoryTimes, 0, N);
- mHistoryTimes = historyTimes = newHistoryTimes;
- }
+ float[] data = mDataSamples;
+ long[] times = mTimeSamples;
+
+ final int NP = mNumPointers;
+ final int NS = mNumSamples;
+ final int NI = NP*NS;
+ final int ND = NI * NUM_SAMPLE_DATA;
+ if (data.length <= ND) {
+ final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA));
+ float[] newData = new float[NEW_ND];
+ System.arraycopy(data, 0, newData, 0, ND);
+ mDataSamples = data = newData;
+ }
+ if (times.length <= NS) {
+ final int NEW_NS = NS + BASE_AVAIL_SAMPLES;
+ long[] newHistoryTimes = new long[NEW_NS];
+ System.arraycopy(times, 0, newHistoryTimes, 0, NS);
+ mTimeSamples = times = newHistoryTimes;
}
+
+ times[NS] = times[0];
+ times[0] = eventTime;
+
+ final int pos = NS*NUM_SAMPLE_DATA;
+ data[pos+SAMPLE_X] = data[SAMPLE_X];
+ data[pos+SAMPLE_Y] = data[SAMPLE_Y];
+ data[pos+SAMPLE_PRESSURE] = data[SAMPLE_PRESSURE];
+ data[pos+SAMPLE_SIZE] = data[SAMPLE_SIZE];
+ data[SAMPLE_X] = x;
+ data[SAMPLE_Y] = y;
+ data[SAMPLE_PRESSURE] = pressure;
+ data[SAMPLE_SIZE] = size;
+ mNumSamples = NS+1;
- historyTimes[N] = mEventTime;
+ mRawX = x;
+ mRawY = y;
+ mMetaState |= metaState;
+ }
- final int pos = N*4;
- history[pos] = mX;
- history[pos+1] = mY;
- history[pos+2] = mPressure;
- history[pos+3] = mSize;
- mNumHistory = N+1;
+ /**
+ * Add a new movement to the batch of movements in this event. The
+ * input data must contain (NUM_SAMPLE_DATA * {@link #getPointerCount()})
+ * samples of data.
+ *
+ * @param eventTime The time stamp for this data.
+ * @param inData The actual data.
+ * @param metaState Meta key state.
+ *
+ * @hide
+ */
+ public final void addBatch(long eventTime, float[] inData, int metaState) {
+ float[] data = mDataSamples;
+ long[] times = mTimeSamples;
+
+ final int NP = mNumPointers;
+ final int NS = mNumSamples;
+ final int NI = NP*NS;
+ final int ND = NI * NUM_SAMPLE_DATA;
+ if (data.length <= ND) {
+ final int NEW_ND = ND + (NP * (BASE_AVAIL_SAMPLES * NUM_SAMPLE_DATA));
+ float[] newData = new float[NEW_ND];
+ System.arraycopy(data, 0, newData, 0, ND);
+ mDataSamples = data = newData;
+ }
+ if (times.length <= NS) {
+ final int NEW_NS = NS + BASE_AVAIL_SAMPLES;
+ long[] newHistoryTimes = new long[NEW_NS];
+ System.arraycopy(times, 0, newHistoryTimes, 0, NS);
+ mTimeSamples = times = newHistoryTimes;
+ }
+
+ times[NS] = times[0];
+ times[0] = eventTime;
+
+ System.arraycopy(data, 0, data, ND, mNumPointers*NUM_SAMPLE_DATA);
+ System.arraycopy(inData, 0, data, 0, mNumPointers*NUM_SAMPLE_DATA);
+
+ mNumSamples = NS+1;
- mEventTime = eventTime;
- mX = mRawX = x;
- mY = mRawY = y;
- mPressure = pressure;
- mSize = size;
+ mRawX = inData[SAMPLE_X];
+ mRawY = inData[SAMPLE_Y];
mMetaState |= metaState;
+
+ if (DEBUG_POINTERS) {
+ StringBuilder sb = new StringBuilder(128);
+ sb.append("Add:");
+ for (int i=0; i<mNumPointers; i++) {
+ sb.append(" #");
+ sb.append(mPointerIdentifiers[i]);
+ sb.append("(");
+ sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_X]);
+ sb.append(",");
+ sb.append(mDataSamples[(i*NUM_SAMPLE_DATA) + SAMPLE_Y]);
+ sb.append(")");
+ }
+ Log.v("MotionEvent", sb.toString());
+ }
}
@Override
public String toString() {
return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
- + " action=" + mAction + " x=" + mX
- + " y=" + mY + " pressure=" + mPressure + " size=" + mSize + "}";
+ + " action=" + mAction + " x=" + getX()
+ + " y=" + getY() + " pressure=" + getPressure() + " size=" + getSize() + "}";
}
public static final Parcelable.Creator<MotionEvent> CREATOR
@@ -663,26 +1171,29 @@ public final class MotionEvent implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeLong(mDownTime);
- out.writeLong(mEventTime);
+ out.writeLong(mEventTimeNano);
out.writeInt(mAction);
- out.writeFloat(mX);
- out.writeFloat(mY);
- out.writeFloat(mPressure);
- out.writeFloat(mSize);
out.writeInt(mMetaState);
out.writeFloat(mRawX);
out.writeFloat(mRawY);
- final int N = mNumHistory;
- out.writeInt(N);
- if (N > 0) {
- final int N4 = N*4;
+ final int NP = mNumPointers;
+ out.writeInt(NP);
+ final int NS = mNumSamples;
+ out.writeInt(NS);
+ final int NI = NP*NS;
+ if (NI > 0) {
int i;
- float[] history = mHistory;
- for (i=0; i<N4; i++) {
+ int[] state = mPointerIdentifiers;
+ for (i=0; i<NP; i++) {
+ out.writeInt(state[i]);
+ }
+ final int ND = NI*NUM_SAMPLE_DATA;
+ float[] history = mDataSamples;
+ for (i=0; i<ND; i++) {
out.writeFloat(history[i]);
}
- long[] times = mHistoryTimes;
- for (i=0; i<N; i++) {
+ long[] times = mTimeSamples;
+ for (i=0; i<NS; i++) {
out.writeLong(times[i]);
}
}
@@ -694,30 +1205,37 @@ public final class MotionEvent implements Parcelable {
private void readFromParcel(Parcel in) {
mDownTime = in.readLong();
- mEventTime = in.readLong();
+ mEventTimeNano = in.readLong();
mAction = in.readInt();
- mX = in.readFloat();
- mY = in.readFloat();
- mPressure = in.readFloat();
- mSize = in.readFloat();
mMetaState = in.readInt();
mRawX = in.readFloat();
mRawY = in.readFloat();
- final int N = in.readInt();
- if ((mNumHistory=N) > 0) {
- final int N4 = N*4;
- float[] history = mHistory;
- if (history == null || history.length < N4) {
- mHistory = history = new float[N4 + (4*4)];
+ final int NP = in.readInt();
+ mNumPointers = NP;
+ final int NS = in.readInt();
+ mNumSamples = NS;
+ final int NI = NP*NS;
+ if (NI > 0) {
+ int[] ids = mPointerIdentifiers;
+ if (ids.length < NP) {
+ mPointerIdentifiers = ids = new int[NP];
+ }
+ for (int i=0; i<NP; i++) {
+ ids[i] = in.readInt();
+ }
+ float[] history = mDataSamples;
+ final int ND = NI*NUM_SAMPLE_DATA;
+ if (history.length < ND) {
+ mDataSamples = history = new float[ND];
}
- for (int i=0; i<N4; i++) {
+ for (int i=0; i<ND; i++) {
history[i] = in.readFloat();
}
- long[] times = mHistoryTimes;
- if (times == null || times.length < N) {
- mHistoryTimes = times = new long[N + 4];
+ long[] times = mTimeSamples;
+ if (times == null || times.length < NS) {
+ mTimeSamples = times = new long[NS];
}
- for (int i=0; i<N; i++) {
+ for (int i=0; i<NS; i++) {
times[i] = in.readLong();
}
}
diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java
index 30da83e..8b3cdd4 100644
--- a/core/java/android/view/RawInputEvent.java
+++ b/core/java/android/view/RawInputEvent.java
@@ -13,6 +13,8 @@ public class RawInputEvent {
public static final int CLASS_ALPHAKEY = 0x00000002;
public static final int CLASS_TOUCHSCREEN = 0x00000004;
public static final int CLASS_TRACKBALL = 0x00000008;
+ public static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
+ public static final int CLASS_DPAD = 0x00000020;
// More special classes for QueuedEvent below.
public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000;
@@ -158,8 +160,24 @@ public class RawInputEvent {
public static final int ABS_TOOL_WIDTH = 0x1c;
public static final int ABS_VOLUME = 0x20;
public static final int ABS_MISC = 0x28;
+ public static final int ABS_MT_TOUCH_MAJOR = 0x30;
+ public static final int ABS_MT_TOUCH_MINOR = 0x31;
+ public static final int ABS_MT_WIDTH_MAJOR = 0x32;
+ public static final int ABS_MT_WIDTH_MINOR = 0x33;
+ public static final int ABS_MT_ORIENTATION = 0x34;
+ public static final int ABS_MT_POSITION_X = 0x35;
+ public static final int ABS_MT_POSITION_Y = 0x36;
+ public static final int ABS_MT_TOOL_TYPE = 0x37;
+ public static final int ABS_MT_BLOB_ID = 0x38;
public static final int ABS_MAX = 0x3f;
+ // Switch events
+ public static final int SW_LID = 0x00;
+
+ public static final int SYN_REPORT = 0;
+ public static final int SYN_CONFIG = 1;
+ public static final int SYN_MT_REPORT = 2;
+
public int deviceId;
public int type;
public int scancode;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 5100fff..b85667b 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -34,12 +34,18 @@ public class Surface implements Parcelable {
/** Surface is created hidden */
public static final int HIDDEN = 0x00000004;
- /** The surface is to be used by hardware accelerators or DMA engines */
+ /** The surface is to be used by hardware accelerators or DMA engines
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int HARDWARE = 0x00000010;
/** Implies "HARDWARE", the surface is to be used by the GPU
* additionally the backbuffer is never preserved for these
- * surfaces. */
+ * surfaces.
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int GPU = 0x00000028;
/** The surface contains secure content, special measures will
@@ -135,6 +141,8 @@ public class Surface implements Parcelable {
@SuppressWarnings("unused")
private int mSurface;
@SuppressWarnings("unused")
+ private int mSurfaceControl;
+ @SuppressWarnings("unused")
private int mSaveCount;
@SuppressWarnings("unused")
private Canvas mCanvas;
@@ -173,7 +181,7 @@ public class Surface implements Parcelable {
public Surface(SurfaceSession s,
int pid, int display, int w, int h, int format, int flags)
throws OutOfResourcesException {
- mCanvas = new Canvas();
+ mCanvas = new CompatibleCanvas();
init(s,pid,display,w,h,format,flags);
}
@@ -238,7 +246,7 @@ public class Surface implements Parcelable {
};
/**
- * Sets the display metrics used to provide canva's width/height in comaptibility mode.
+ * Sets the display metrics used to provide canva's width/height in compatibility mode.
*/
void setCompatibleDisplayMetrics(DisplayMetrics metrics, Translator translator) {
mCompatibleDisplayMetrics = metrics;
@@ -263,11 +271,16 @@ public class Surface implements Parcelable {
*/
public native boolean isValid();
- /** Call this free the surface up. {@hide} */
- public native void clear();
+ /** Free all server-side state associated with this surface and
+ * release this object's reference. {@hide} */
+ public native void destroy();
+
+ /** Release the local reference to the server-side surface. @hide */
+ public native void release();
/** draw into a surface */
- public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException {
+ public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException, IllegalArgumentException
+ {
/* the dirty rectangle may be expanded to the surface's size, if
* for instance it has been resized or if the bits were lost, since
* the last call.
@@ -349,7 +362,7 @@ public class Surface implements Parcelable {
@Override
public String toString() {
- return "Surface(native-token=" + mSurface + ")";
+ return "Surface(native-token=" + mSurfaceControl + ")";
}
private Surface(Parcel source) throws OutOfResourcesException {
@@ -362,7 +375,7 @@ public class Surface implements Parcelable {
public native void readFromParcel(Parcel source);
public native void writeToParcel(Parcel dest, int flags);
-
+
public static final Parcelable.Creator<Surface> CREATOR
= new Parcelable.Creator<Surface>()
{
@@ -383,7 +396,7 @@ public class Surface implements Parcelable {
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
- clear();
+ release();
}
private native void init(SurfaceSession s,
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
index 3d0dda3..64a10d1 100644
--- a/core/java/android/view/SurfaceHolder.java
+++ b/core/java/android/view/SurfaceHolder.java
@@ -38,8 +38,6 @@ public interface SurfaceHolder {
* Surface type.
*
* @see #SURFACE_TYPE_NORMAL
- * @see #SURFACE_TYPE_HARDWARE
- * @see #SURFACE_TYPE_GPU
* @see #SURFACE_TYPE_PUSH_BUFFERS
*/
@@ -47,9 +45,15 @@ public interface SurfaceHolder {
* contiguous, cached/buffered RAM. */
public static final int SURFACE_TYPE_NORMAL = MEMORY_TYPE_NORMAL;
/** Surface type: creates a suited to be used with DMA engines and
- * hardware accelerators. */
+ * hardware accelerators.
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int SURFACE_TYPE_HARDWARE = MEMORY_TYPE_HARDWARE;
- /** Surface type: creates a surface suited to be used with the GPU */
+ /** Surface type: creates a surface suited to be used with the GPU
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int SURFACE_TYPE_GPU = MEMORY_TYPE_GPU;
/** Surface type: creates a "push" surface, that is a surface that
* doesn't owns its buffers. With such a surface lockCanvas will fail. */
@@ -139,11 +143,7 @@ public interface SurfaceHolder {
public boolean isCreating();
/**
- * Sets the surface's type. Surfaces intended to be used with OpenGL ES
- * should be of SURFACE_TYPE_GPU, surfaces accessed by DMA engines and
- * hardware accelerators should be of type SURFACE_TYPE_HARDWARE.
- * Failing to set the surface's type appropriately could result in
- * degraded performance or failure.
+ * Sets the surface's type.
*
* @param type The surface's memory type.
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9cf7092..1426aef 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,6 +16,8 @@
package android.view;
+import com.android.internal.view.BaseIWindow;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.CompatibilityInfo.Translator;
@@ -32,8 +34,9 @@ import android.os.ParcelFileDescriptor;
import android.util.AttributeSet;
import android.util.Config;
import android.util.Log;
-import java.util.ArrayList;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.lang.ref.WeakReference;
@@ -121,6 +124,8 @@ public class SurfaceView extends View {
};
boolean mRequestedVisible = false;
+ boolean mWindowVisibility = false;
+ boolean mViewVisibility = false;
int mRequestedWidth = -1;
int mRequestedHeight = -1;
int mRequestedFormat = PixelFormat.OPAQUE;
@@ -173,12 +178,22 @@ public class SurfaceView extends View {
mSession = getWindowSession();
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView");
+ mViewVisibility = getVisibility() == VISIBLE;
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
- mRequestedVisible = visibility == VISIBLE;
+ mWindowVisibility = visibility == VISIBLE;
+ mRequestedVisible = mWindowVisibility && mViewVisibility;
+ updateWindow(false);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ mViewVisibility = visibility == VISIBLE;
+ mRequestedVisible = mWindowVisibility && mViewVisibility;
updateWindow(false);
}
@@ -279,7 +294,9 @@ public class SurfaceView extends View {
return;
}
ViewRoot viewRoot = (ViewRoot) getRootView().getParent();
- mTranslator = viewRoot.mTranslator;
+ if (viewRoot != null) {
+ mTranslator = viewRoot.mTranslator;
+ }
Resources res = getContext().getResources();
if (mTranslator != null || !res.getCompatibilityInfo().supportsScreen()) {
@@ -434,7 +451,7 @@ public class SurfaceView extends View {
updateWindow(false);
}
- private static class MyWindow extends IWindow.Stub {
+ private static class MyWindow extends BaseIWindow {
private final WeakReference<SurfaceView> mSurfaceView;
public MyWindow(SurfaceView surfaceView) {
@@ -476,7 +493,8 @@ public class SurfaceView extends View {
}
}
- public void dispatchPointer(MotionEvent event, long eventTime) {
+ public void dispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
//if (mSession != null && mSurface != null) {
// try {
@@ -486,7 +504,8 @@ public class SurfaceView extends View {
//}
}
- public void dispatchTrackball(MotionEvent event, long eventTime) {
+ public void dispatchTrackball(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
//if (mSession != null && mSurface != null) {
// try {
@@ -568,9 +587,14 @@ public class SurfaceView extends View {
public void setType(int type) {
switch (type) {
- case SURFACE_TYPE_NORMAL:
case SURFACE_TYPE_HARDWARE:
case SURFACE_TYPE_GPU:
+ // these are deprecated, treat as "NORMAL"
+ type = SURFACE_TYPE_NORMAL;
+ break;
+ }
+ switch (type) {
+ case SURFACE_TYPE_NORMAL:
case SURFACE_TYPE_PUSH_BUFFERS:
mRequestedType = type;
if (mWindow != null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7ed2712..2f17bbc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -46,7 +46,6 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Config;
-import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Pool;
@@ -2983,6 +2982,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* @param enabled True if this view is enabled, false otherwise.
*/
public void setEnabled(boolean enabled) {
+ if (enabled == isEnabled()) return;
+
setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
/*
@@ -5342,12 +5343,36 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
size = cache.scrollBarSize;
}
+ final int scrollX = mScrollX;
+ final int scrollY = mScrollY;
+ final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+
if (drawHorizontalScrollBar) {
- onDrawHorizontalScrollBar(canvas, scrollBar, width, height, size);
+ scrollBar.setParameters(
+ computeHorizontalScrollRange(),
+ computeHorizontalScrollOffset(),
+ computeHorizontalScrollExtent(), false);
+ final int top = scrollY + height - size - (mUserPaddingBottom & inside);
+ final int verticalScrollBarGap = drawVerticalScrollBar ?
+ getVerticalScrollbarWidth() : 0;
+ onDrawHorizontalScrollBar(canvas, scrollBar,
+ scrollX + (mPaddingLeft & inside),
+ top,
+ scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap,
+ top + size);
}
if (drawVerticalScrollBar) {
- onDrawVerticalScrollBar(canvas, scrollBar, width, height, size);
+ scrollBar.setParameters(computeVerticalScrollRange(),
+ computeVerticalScrollOffset(),
+ computeVerticalScrollExtent(), true);
+ // TODO: Deal with RTL languages to position scrollbar on left
+ final int left = scrollX + width - size - (mUserPaddingRight & inside);
+ onDrawVerticalScrollBar(canvas, scrollBar,
+ left,
+ scrollY + (mPaddingTop & inside),
+ left + size,
+ scrollY + height - (mUserPaddingBottom & inside));
}
}
}
@@ -5367,44 +5392,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* <p>Draw the horizontal scrollbar if
* {@link #isHorizontalScrollBarEnabled()} returns true.</p>
*
- * <p>The length of the scrollbar and its thumb is computed according to the
- * values returned by {@link #computeHorizontalScrollRange()},
- * {@link #computeHorizontalScrollExtent()} and
- * {@link #computeHorizontalScrollOffset()}. Refer to
- * {@link android.widget.ScrollBarDrawable} for more information about how
- * these values relate to each other.</p>
- *
* @param canvas the canvas on which to draw the scrollbar
* @param scrollBar the scrollbar's drawable
- * @param width the width of the drawing surface
- * @param height the height of the drawing surface
- * @param size the size of the scrollbar
*
* @see #isHorizontalScrollBarEnabled()
* @see #computeHorizontalScrollRange()
* @see #computeHorizontalScrollExtent()
* @see #computeHorizontalScrollOffset()
* @see android.widget.ScrollBarDrawable
+ * @hide
*/
- private void onDrawHorizontalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width,
- int height, int size) {
-
- final int viewFlags = mViewFlags;
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
- final int top = scrollY + height - size - (mUserPaddingBottom & inside);
-
- final int verticalScrollBarGap =
- (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ?
- getVerticalScrollbarWidth() : 0;
-
- scrollBar.setBounds(scrollX + (mPaddingLeft & inside), top,
- scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size);
- scrollBar.setParameters(
- computeHorizontalScrollRange(),
- computeHorizontalScrollOffset(),
- computeHorizontalScrollExtent(), false);
+ protected void onDrawHorizontalScrollBar(Canvas canvas,
+ Drawable scrollBar,
+ int l, int t, int r, int b) {
+ scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -5412,40 +5413,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
* <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()}
* returns true.</p>
*
- * <p>The length of the scrollbar and its thumb is computed according to the
- * values returned by {@link #computeVerticalScrollRange()},
- * {@link #computeVerticalScrollExtent()} and
- * {@link #computeVerticalScrollOffset()}. Refer to
- * {@link android.widget.ScrollBarDrawable} for more information about how
- * these values relate to each other.</p>
- *
* @param canvas the canvas on which to draw the scrollbar
* @param scrollBar the scrollbar's drawable
- * @param width the width of the drawing surface
- * @param height the height of the drawing surface
- * @param size the size of the scrollbar
*
* @see #isVerticalScrollBarEnabled()
* @see #computeVerticalScrollRange()
* @see #computeVerticalScrollExtent()
* @see #computeVerticalScrollOffset()
* @see android.widget.ScrollBarDrawable
+ * @hide
*/
- private void onDrawVerticalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width,
- int height, int size) {
-
- final int scrollX = mScrollX;
- final int scrollY = mScrollY;
- final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
- // TODO: Deal with RTL languages to position scrollbar on left
- final int left = scrollX + width - size - (mUserPaddingRight & inside);
-
- scrollBar.setBounds(left, scrollY + (mPaddingTop & inside),
- left + size, scrollY + height - (mUserPaddingBottom & inside));
- scrollBar.setParameters(
- computeVerticalScrollRange(),
- computeVerticalScrollOffset(),
- computeVerticalScrollExtent(), true);
+ protected void onDrawVerticalScrollBar(Canvas canvas,
+ Drawable scrollBar,
+ int l, int t, int r, int b) {
+ scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
@@ -6911,7 +6892,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
/**
* Set the background to a given resource. The resource should refer to
- * a Drawable object.
+ * a Drawable object or 0 to remove the background.
* @param resid The identifier of the resource.
* @attr ref android.R.styleable#View_background
*/
@@ -8740,7 +8721,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
mLastColor = color;
color |= 0xFF000000;
- shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP);
+ shader = new LinearGradient(0, 0, 0, 1, color | 0xFF000000,
+ color & 0x00FFFFFF, Shader.TileMode.CLAMP);
paint.setShader(shader);
// Restore the default transfer mode (src_over)
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 0c5d853..b61465a 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -79,6 +79,9 @@ public final class ViewRoot extends Handler implements ViewParent,
private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
private static final boolean WATCH_POINTER = false;
+ private static final boolean MEASURE_LATENCY = false;
+ private static LatencyTimer lt;
+
/**
* Maximum time we allow the user to roll the trackball enough to generate
* a key event, before resetting the counters.
@@ -148,7 +151,8 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean mWindowAttributesChanged = false;
// These can be accessed by any thread, must be protected with a lock.
- Surface mSurface;
+ // Surface can never be reassigned or cleared (use Surface.clear()).
+ private final Surface mSurface = new Surface();
boolean mAdded;
boolean mAddedTouchMode;
@@ -188,18 +192,11 @@ public final class ViewRoot extends Handler implements ViewParent,
private final int mDensity;
- public ViewRoot(Context context) {
- super();
-
- ++sInstanceCount;
-
- // Initialize the statics when this class is first instantiated. This is
- // done here instead of in the static block because Zygote does not
- // allow the spawning of threads.
+ public static IWindowSession getWindowSession(Looper mainLooper) {
synchronized (mStaticInit) {
if (!mInitialized) {
try {
- InputMethodManager imm = InputMethodManager.getInstance(context);
+ InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
sWindowSession = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"))
.openSession(imm.getClient(), imm.getInputContext());
@@ -207,8 +204,24 @@ public final class ViewRoot extends Handler implements ViewParent,
} catch (RemoteException e) {
}
}
+ return sWindowSession;
}
+ }
+
+ public ViewRoot(Context context) {
+ super();
+
+ if (MEASURE_LATENCY && lt == null) {
+ lt = new LatencyTimer(100, 1000);
+ }
+
+ ++sInstanceCount;
+ // Initialize the statics when this class is first instantiated. This is
+ // done here instead of in the static block because Zygote does not
+ // allow the spawning of threads.
+ getWindowSession(context.getMainLooper());
+
mThread = Thread.currentThread();
mLocation = new WindowLeaked(null);
mLocation.fillInStackTrace();
@@ -224,7 +237,6 @@ public final class ViewRoot extends Handler implements ViewParent,
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
- mSurface = new Surface();
mAdded = false;
mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
mViewConfiguration = ViewConfiguration.get(context);
@@ -674,7 +686,6 @@ public final class ViewRoot extends Handler implements ViewParent,
attachInfo.mKeepScreenOn = false;
viewVisibilityChanged = false;
host.dispatchAttachedToWindow(attachInfo, 0);
- getRunQueue().executeActions(attachInfo.mHandler);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
} else {
@@ -707,6 +718,10 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean insetsChanged = false;
if (mLayoutRequested) {
+ // Execute enqueued actions on every layout in case a view that was detached
+ // enqueued an action after being detached
+ getRunQueue().executeActions(attachInfo.mHandler);
+
if (mFirst) {
host.fitSystemWindows(mAttachInfo.mContentInsets);
// make sure touch mode code executes by setting cached value
@@ -1555,10 +1570,12 @@ public final class ViewRoot extends Handler implements ViewParent,
mView = null;
mAttachInfo.mRootView = null;
+ mAttachInfo.mSurface = null;
if (mUseGL) {
destroyGL();
}
+ mSurface.release();
try {
sWindowSession.remove(mWindow);
@@ -1628,16 +1645,24 @@ public final class ViewRoot extends Handler implements ViewParent,
break;
case DISPATCH_POINTER: {
MotionEvent event = (MotionEvent)msg.obj;
-
- boolean didFinish;
+ boolean callWhenDone = msg.arg1 != 0;
+
if (event == null) {
try {
+ long timeBeforeGettingEvents;
+ if (MEASURE_LATENCY) {
+ timeBeforeGettingEvents = System.nanoTime();
+ }
+
event = sWindowSession.getPendingPointerMove(mWindow);
+
+ if (MEASURE_LATENCY && event != null) {
+ lt.sample("9 Client got events ", System.nanoTime() - event.getEventTimeNano());
+ lt.sample("8 Client getting events ", timeBeforeGettingEvents - event.getEventTimeNano());
+ }
} catch (RemoteException e) {
}
- didFinish = true;
- } else {
- didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
+ callWhenDone = false;
}
if (event != null && mTranslator != null) {
mTranslator.translateEventInScreenToAppWindow(event);
@@ -1654,8 +1679,16 @@ public final class ViewRoot extends Handler implements ViewParent,
if(Config.LOGV) {
captureMotionLog("captureDispatchPointer", event);
}
- event.offsetLocation(0, mCurScrollY);
+ if (mCurScrollY != 0) {
+ event.offsetLocation(0, mCurScrollY);
+ }
+ if (MEASURE_LATENCY) {
+ lt.sample("A Dispatching TouchEvents", System.nanoTime() - event.getEventTimeNano());
+ }
handled = mView.dispatchTouchEvent(event);
+ if (MEASURE_LATENCY) {
+ lt.sample("B Dispatched TouchEvents ", System.nanoTime() - event.getEventTimeNano());
+ }
if (!handled && isDown) {
int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
@@ -1701,7 +1734,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
} finally {
- if (!didFinish) {
+ if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
@@ -1716,7 +1749,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} break;
case DISPATCH_TRACKBALL:
- deliverTrackballEvent((MotionEvent)msg.obj);
+ deliverTrackballEvent((MotionEvent)msg.obj, msg.arg1 != 0);
break;
case DISPATCH_APP_VISIBILITY:
handleAppVisibility(msg.arg1 != 0);
@@ -1958,16 +1991,13 @@ public final class ViewRoot extends Handler implements ViewParent,
}
- private void deliverTrackballEvent(MotionEvent event) {
- boolean didFinish;
+ private void deliverTrackballEvent(MotionEvent event, boolean callWhenDone) {
if (event == null) {
try {
event = sWindowSession.getPendingTrackballMove(mWindow);
} catch (RemoteException e) {
}
- didFinish = true;
- } else {
- didFinish = false;
+ callWhenDone = false;
}
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
@@ -1985,7 +2015,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
} finally {
if (handled) {
- if (!didFinish) {
+ if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
@@ -2101,7 +2131,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mLastTrackballTime = curTime;
}
} finally {
- if (!didFinish) {
+ if (callWhenDone) {
try {
sWindowSession.finishKey(mWindow);
} catch (RemoteException e) {
@@ -2502,7 +2532,7 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- mSurface = null;
+ mSurface.release();
}
if (mAdded) {
mAdded = false;
@@ -2564,15 +2594,19 @@ public final class ViewRoot extends Handler implements ViewParent,
sendMessageAtTime(msg, event.getEventTime());
}
- public void dispatchPointer(MotionEvent event, long eventTime) {
+ public void dispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
Message msg = obtainMessage(DISPATCH_POINTER);
msg.obj = event;
+ msg.arg1 = callWhenDone ? 1 : 0;
sendMessageAtTime(msg, eventTime);
}
- public void dispatchTrackball(MotionEvent event, long eventTime) {
+ public void dispatchTrackball(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
Message msg = obtainMessage(DISPATCH_TRACKBALL);
msg.obj = event;
+ msg.arg1 = callWhenDone ? 1 : 0;
sendMessageAtTime(msg, eventTime);
}
@@ -2745,19 +2779,25 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
- public void dispatchPointer(MotionEvent event, long eventTime) {
+ public void dispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
final ViewRoot viewRoot = mViewRoot.get();
- if (viewRoot != null) {
- viewRoot.dispatchPointer(event, eventTime);
+ if (viewRoot != null) {
+ if (MEASURE_LATENCY) {
+ // Note: eventTime is in milliseconds
+ ViewRoot.lt.sample("* ViewRoot b4 dispatchPtr", System.nanoTime() - eventTime * 1000000);
+ }
+ viewRoot.dispatchPointer(event, eventTime, callWhenDone);
} else {
new EventCompletion(mMainLooper, this, null, true, event);
}
}
- public void dispatchTrackball(MotionEvent event, long eventTime) {
+ public void dispatchTrackball(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
final ViewRoot viewRoot = mViewRoot.get();
if (viewRoot != null) {
- viewRoot.dispatchTrackball(event, eventTime);
+ viewRoot.dispatchTrackball(event, eventTime, callWhenDone);
} else {
new EventCompletion(mMainLooper, this, null, false, event);
}
@@ -2827,6 +2867,9 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
}
+
+ public void dispatchWallpaperOffsets(float x, float y) {
+ }
}
/**
@@ -3107,7 +3150,7 @@ public final class ViewRoot extends Handler implements ViewParent,
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
- mActions.clear();
+ actions.clear();
}
}
@@ -3121,7 +3164,6 @@ public final class ViewRoot extends Handler implements ViewParent,
if (o == null || getClass() != o.getClass()) return false;
HandlerAction that = (HandlerAction) o;
-
return !(action != null ? !action.equals(that.action) : that.action != null);
}
diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java
index e159de4..703a38f 100644
--- a/core/java/android/view/ViewStub.java
+++ b/core/java/android/view/ViewStub.java
@@ -23,6 +23,8 @@ import android.util.AttributeSet;
import com.android.internal.R;
+import java.lang.ref.WeakReference;
+
/**
* A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
* layout resources at runtime.
@@ -68,6 +70,8 @@ public final class ViewStub extends View {
private int mLayoutResource = 0;
private int mInflatedId;
+ private WeakReference<View> mInflatedViewRef;
+
private OnInflateListener mInflateListener;
public ViewStub(Context context) {
@@ -196,9 +200,15 @@ public final class ViewStub extends View {
*/
@Override
public void setVisibility(int visibility) {
- super.setVisibility(visibility);
-
- if (visibility == VISIBLE || visibility == INVISIBLE) {
+ if (mInflatedViewRef != null) {
+ View view = mInflatedViewRef.get();
+ if (view != null) {
+ view.setVisibility(visibility);
+ } else {
+ throw new IllegalStateException("setVisibility called on un-referenced view");
+ }
+ } else if (visibility == VISIBLE || visibility == INVISIBLE) {
+ super.setVisibility(visibility);
inflate();
}
}
@@ -234,6 +244,8 @@ public final class ViewStub extends View {
parent.addView(view, index);
}
+ mInflatedViewRef = new WeakReference(view);
+
if (mInflateListener != null) {
mInflateListener.onInflate(this, view);
}
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index a573983..e21824e 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -23,7 +23,9 @@ import android.content.res.Resources;
import android.media.AudioManager;
import android.media.AudioService;
import android.media.AudioSystem;
+import android.media.RingtoneManager;
import android.media.ToneGenerator;
+import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
@@ -44,7 +46,7 @@ import android.widget.Toast;
public class VolumePanel extends Handler
{
private static final String TAG = "VolumePanel";
- private static boolean LOGD = false || Config.LOGD;
+ private static boolean LOGD = false;
/**
* The delay before playing a sound. This small period exists so the user
@@ -86,6 +88,7 @@ public class VolumePanel extends Handler
protected Context mContext;
private AudioManager mAudioManager;
protected AudioService mAudioService;
+ private boolean mRingIsSilent;
private final Toast mToast;
private final View mView;
@@ -138,7 +141,7 @@ public class VolumePanel extends Handler
onShowVolumeChanged(streamType, flags);
}
- if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
+ if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
removeMessages(MSG_PLAY_SOUND);
sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
}
@@ -157,6 +160,7 @@ public class VolumePanel extends Handler
int index = mAudioService.getStreamVolume(streamType);
int message = UNKNOWN_VOLUME_TEXT;
int additionalMessage = 0;
+ mRingIsSilent = false;
if (LOGD) {
Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
@@ -169,8 +173,15 @@ public class VolumePanel extends Handler
switch (streamType) {
case AudioManager.STREAM_RING: {
+ setRingerIcon();
message = RINGTONE_VOLUME_TEXT;
- setRingerIcon(index);
+ Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+ mContext, RingtoneManager.TYPE_RINGTONE);
+ if (ringuri == null) {
+ additionalMessage =
+ com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+ mRingIsSilent = true;
+ }
break;
}
@@ -208,6 +219,13 @@ public class VolumePanel extends Handler
case AudioManager.STREAM_NOTIFICATION: {
message = NOTIFICATION_VOLUME_TEXT;
setSmallIcon(index);
+ Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+ mContext, RingtoneManager.TYPE_NOTIFICATION);
+ if (ringuri == null) {
+ additionalMessage =
+ com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+ mRingIsSilent = true;
+ }
break;
}
@@ -254,7 +272,6 @@ public class VolumePanel extends Handler
mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
}
-
}
protected void onPlaySound(int streamType, int flags) {
@@ -337,17 +354,15 @@ public class VolumePanel extends Handler
/**
* Makes the ringer icon visible with an icon that is chosen
* based on the current ringer mode.
- *
- * @param index
*/
- private void setRingerIcon(int index) {
+ private void setRingerIcon() {
mSmallStreamIcon.setVisibility(View.GONE);
mLargeStreamIcon.setVisibility(View.VISIBLE);
int ringerMode = mAudioService.getRingerMode();
int icon;
- if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode);
+ if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode);
if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
icon = com.android.internal.R.drawable.ic_volume_off;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 576c8c1..1932765 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,11 +56,11 @@ public abstract class Window {
public static final int FEATURE_CONTEXT_MENU = 6;
/** Flag for custom title. You cannot combine this feature with other title features. */
public static final int FEATURE_CUSTOM_TITLE = 7;
- /* Flag for asking for an OpenGL enabled window.
+ /** Flag for asking for an OpenGL enabled window.
All 2D graphics will be handled by OpenGL ES.
- Private for now, until it is better tested (not shipping in 1.0)
+ @hide
*/
- private static final int FEATURE_OPENGL = 8;
+ public static final int FEATURE_OPENGL = 8;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
@@ -237,7 +237,6 @@ public abstract class Window {
/**
* This is called whenever the current window attributes change.
*
-
*/
public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
@@ -252,13 +251,29 @@ public abstract class Window {
public void onContentChanged();
/**
- * This hook is called whenever the window focus changes.
+ * This hook is called whenever the window focus changes. See
+ * {@link View#onWindowFocusChanged(boolean)
+ * View.onWindowFocusChanged(boolean)} for more information.
*
* @param hasFocus Whether the window now has focus.
*/
public void onWindowFocusChanged(boolean hasFocus);
/**
+ * Called when the window has been attached to the window manager.
+ * See {@link View#onAttachedToWindow() View.onAttachedToWindow()}
+ * for more information.
+ */
+ public void onAttachedToWindow();
+
+ /**
+ * Called when the window has been attached to the window manager.
+ * See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
+ * for more information.
+ */
+ public void onDetachedFromWindow();
+
+ /**
* Called when a panel is being closed. If another logical subsequent
* panel is being opened (and this panel is being closed to make room for the subsequent
* panel), this method will NOT be called.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c0be9e8..8c12656 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -314,6 +314,12 @@ public interface WindowManager extends ViewManager {
public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
/**
+ * Window type: wallpaper window, placed behind any window that wants
+ * to sit on top of the wallpaper.
+ */
+ public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13;
+
+ /**
* End of types of system windows.
*/
public static final int LAST_SYSTEM_WINDOW = 2999;
@@ -323,8 +329,6 @@ public interface WindowManager extends ViewManager {
* Default is normal.
*
* @see #MEMORY_TYPE_NORMAL
- * @see #MEMORY_TYPE_HARDWARE
- * @see #MEMORY_TYPE_GPU
* @see #MEMORY_TYPE_PUSH_BUFFERS
*/
public int memoryType;
@@ -332,10 +336,16 @@ public interface WindowManager extends ViewManager {
/** Memory type: The window's surface is allocated in main memory. */
public static final int MEMORY_TYPE_NORMAL = 0;
/** Memory type: The window's surface is configured to be accessible
- * by DMA engines and hardware accelerators. */
+ * by DMA engines and hardware accelerators.
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int MEMORY_TYPE_HARDWARE = 1;
/** Memory type: The window's surface is configured to be accessible
- * by graphics accelerators. */
+ * by graphics accelerators.
+ * @deprecated this is ignored, this value is set automatically when needed.
+ */
+ @Deprecated
public static final int MEMORY_TYPE_GPU = 2;
/** Memory type: The window's surface doesn't own its buffers and
* therefore cannot be locked. Instead the buffers are pushed to
@@ -479,16 +489,23 @@ public interface WindowManager extends ViewManager {
* key guard or any other lock screens. Can be used with
* {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows
* directly before showing the key guard window
- *
- * {@hide} */
+ */
public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
+ /** Window flag: ask that the system wallpaper be shown behind
+ * your window. The window surface must be translucent to be able
+ * to actually see the wallpaper behind it; this flag just ensures
+ * that the wallpaper surface will be there if this window actually
+ * has translucent regions.
+ */
+ public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
+
/** Window flag: special flag to limit the size of the window to be
* original size ([320x480] x density). Used to create window for applications
* running under compatibility mode.
*
* {@hide} */
- public static final int FLAG_COMPATIBLE_WINDOW = 0x00100000;
+ public static final int FLAG_COMPATIBLE_WINDOW = 0x20000000;
/** Window flag: a special option intended for system dialogs. When
* this flag is set, the window will demand focus unconditionally when
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1371932..c40107b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -344,11 +344,25 @@ public interface WindowManagerPolicy {
public final int TRANSIT_TASK_TO_FRONT = 10;
/** A window in an existing task is being put below all other tasks. */
public final int TRANSIT_TASK_TO_BACK = 11;
+ /** A window in a new activity that doesn't have a wallpaper is being
+ * opened on top of one that does, effectively closing the wallpaper. */
+ public final int TRANSIT_WALLPAPER_CLOSE = 12;
+ /** A window in a new activity that does have a wallpaper is being
+ * opened on one that didn't, effectively opening the wallpaper. */
+ public final int TRANSIT_WALLPAPER_OPEN = 13;
+ /** A window in a new activity is being opened on top of an existing one,
+ * and both are on top of the wallpaper. */
+ public final int TRANSIT_WALLPAPER_INTRA_OPEN = 14;
+ /** The window in the top-most activity is being closed to reveal the
+ * previous activity, and both are on top of he wallpaper. */
+ public final int TRANSIT_WALLPAPER_INTRA_CLOSE = 15;
/** Screen turned off because of power button */
public final int OFF_BECAUSE_OF_USER = 1;
/** Screen turned off because of timeout */
public final int OFF_BECAUSE_OF_TIMEOUT = 2;
+ /** Screen turned off because of proximity sensor */
+ public final int OFF_BECAUSE_OF_PROXIMITY_SENSOR = 3;
/**
* Magic constant to {@link IWindowManager#setRotation} to not actually
@@ -533,14 +547,15 @@ public interface WindowManagerPolicy {
* @param win The window that currently has focus. This is where the key
* event will normally go.
* @param code Key code.
- * @param metaKeys TODO
+ * @param metaKeys bit mask of meta keys that are held.
* @param down Is this a key press (true) or release (false)?
* @param repeatCount Number of times a key down has repeated.
+ * @param flags event's flags.
* @return Returns true if the policy consumed the event and it should
* not be further dispatched.
*/
public boolean interceptKeyTi(WindowState win, int code,
- int metaKeys, boolean down, int repeatCount);
+ int metaKeys, boolean down, int repeatCount, int flags);
/**
* Called when layout of the windows is about to start.
@@ -792,6 +807,13 @@ public interface WindowManagerPolicy {
public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always);
/**
+ * A special function that is called from the very low-level input queue
+ * to provide feedback to the user. Currently only called for virtual
+ * keys.
+ */
+ public void keyFeedbackFromInput(KeyEvent event);
+
+ /**
* Called when we have stopped keeping the screen on because a window
* requesting this is no longer visible.
*/
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index a662760..2f5e601 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -319,7 +319,7 @@ public abstract class Animation implements Cloneable {
*
* @param durationMillis Duration in milliseconds
*
- * @throw java.lang.IllegalArgumentException if the duration is < 0
+ * @throws java.lang.IllegalArgumentException if the duration is < 0
*
* @attr ref android.R.styleable#Animation_duration
*/
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index b4c5b72..316bcd6 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -40,7 +40,7 @@ import java.io.IOException;
* This class is used to specify meta information of an input method.
*/
public final class InputMethodInfo implements Parcelable {
- static final String TAG = "InputMethodMetaInfo";
+ static final String TAG = "InputMethodInfo";
/**
* The Service that implements this input method component.
@@ -244,7 +244,7 @@ public final class InputMethodInfo implements Parcelable {
@Override
public String toString() {
- return "InputMethodMetaInfo{" + mId
+ return "InputMethodInfo{" + mId
+ ", settings: "
+ mSettingsActivityName + "}";
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index d797890..e30687f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -446,13 +446,22 @@ public final class InputMethodManager {
* @hide
*/
static public InputMethodManager getInstance(Context context) {
+ return getInstance(context.getMainLooper());
+ }
+
+ /**
+ * Internally, the input method manager can't be context-dependent, so
+ * we have this here for the places that need it.
+ * @hide
+ */
+ static public InputMethodManager getInstance(Looper mainLooper) {
synchronized (mInstanceSync) {
if (mInstance != null) {
return mInstance;
}
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
- mInstance = new InputMethodManager(service, context.getMainLooper());
+ mInstance = new InputMethodManager(service, mainLooper);
}
return mInstance;
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index dbd2682..db6b74f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -31,6 +31,7 @@ import junit.framework.Assert;
import java.net.URLEncoder;
import java.util.HashMap;
+import java.util.Map;
import java.util.Iterator;
class BrowserFrame extends Handler {
@@ -59,7 +60,7 @@ class BrowserFrame extends Handler {
private boolean mIsMainFrame;
// Attached Javascript interfaces
- private HashMap mJSInterfaceMap;
+ private Map<String, Object> mJSInterfaceMap;
// message ids
// a message posted when a frame loading is completed
@@ -98,7 +99,7 @@ class BrowserFrame extends Handler {
* XXX: Called by WebCore thread.
*/
public BrowserFrame(Context context, WebViewCore w, CallbackProxy proxy,
- WebSettings settings) {
+ WebSettings settings, Map<String, Object> javascriptInterfaces) {
// Create a global JWebCoreJavaBridge to handle timers and
// cookies in the WebCore thread.
if (sJavaBridge == null) {
@@ -109,9 +110,10 @@ class BrowserFrame extends Handler {
CacheManager.init(context);
// create CookieSyncManager with current Context
CookieSyncManager.createInstance(context);
+ // create PluginManager with current Context
+ PluginManager.getInstance(context);
}
- AssetManager am = context.getAssets();
- nativeCreateFrame(w, am, proxy.getBackForwardList());
+ mJSInterfaceMap = javascriptInterfaces;
mSettings = settings;
mContext = context;
@@ -119,7 +121,10 @@ class BrowserFrame extends Handler {
mDatabase = WebViewDatabase.getInstance(context);
mWebViewCore = w;
- if (WebView.LOGV_ENABLED) {
+ AssetManager am = context.getAssets();
+ nativeCreateFrame(w, am, proxy.getBackForwardList());
+
+ if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
}
}
@@ -341,17 +346,16 @@ class BrowserFrame extends Handler {
switch (msg.what) {
case FRAME_COMPLETED: {
if (mSettings.getSavePassword() && hasPasswordField()) {
- if (WebView.DEBUG) {
- Assert.assertNotNull(mCallbackProxy.getBackForwardList()
- .getCurrentItem());
- }
- WebAddress uri = new WebAddress(
- mCallbackProxy.getBackForwardList().getCurrentItem()
- .getUrl());
- String schemePlusHost = uri.mScheme + uri.mHost;
- String[] up = mDatabase.getUsernamePassword(schemePlusHost);
- if (up != null && up[0] != null) {
- setUsernamePassword(up[0], up[1]);
+ WebHistoryItem item = mCallbackProxy.getBackForwardList()
+ .getCurrentItem();
+ if (item != null) {
+ WebAddress uri = new WebAddress(item.getUrl());
+ String schemePlusHost = uri.mScheme + uri.mHost;
+ String[] up =
+ mDatabase.getUsernamePassword(schemePlusHost);
+ if (up != null && up[0] != null) {
+ setUsernamePassword(up[0], up[1]);
+ }
}
}
CacheManager.trimCacheIfNeeded();
@@ -463,8 +467,6 @@ class BrowserFrame extends Handler {
* @param postData If the method is "POST" postData is sent as the request
* body. Is null when empty.
* @param cacheMode The cache mode to use when loading this resource.
- * @param isHighPriority True if this resource needs to be put at the front
- * of the network queue.
* @param synchronous True if the load is synchronous.
* @return A newly created LoadListener object.
*/
@@ -474,7 +476,6 @@ class BrowserFrame extends Handler {
HashMap headers,
byte[] postData,
int cacheMode,
- boolean isHighPriority,
boolean synchronous) {
PerfChecker checker = new PerfChecker();
@@ -490,7 +491,7 @@ class BrowserFrame extends Handler {
}
if (mSettings.getSavePassword() && hasPasswordField()) {
try {
- if (WebView.DEBUG) {
+ if (DebugFlags.BROWSER_FRAME) {
Assert.assertNotNull(mCallbackProxy.getBackForwardList()
.getCurrentItem());
}
@@ -538,10 +539,10 @@ class BrowserFrame extends Handler {
// is this resource the main-frame top-level page?
boolean isMainFramePage = mIsMainFrame;
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
- + method + ", postData=" + postData + ", isHighPriority="
- + isHighPriority + ", isMainFramePage=" + isMainFramePage);
+ + method + ", postData=" + postData + ", isMainFramePage="
+ + isMainFramePage);
}
// Create a LoadListener
@@ -551,23 +552,17 @@ class BrowserFrame extends Handler {
mCallbackProxy.onLoadResource(url);
if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) {
+ // send an error message, so that loadListener can be deleted
+ // after this is returned. This is important as LoadListener's
+ // nativeError will remove the request from its DocLoader's request
+ // list. But the set up is not done until this method is returned.
loadListener.error(
android.net.http.EventHandler.ERROR, mContext.getString(
com.android.internal.R.string.httpErrorTooManyRequests));
- loadListener.notifyError();
- loadListener.tearDown();
- return null;
+ return loadListener;
}
- // during synchronous load, the WebViewCore thread is blocked, so we
- // need to endCacheTransaction first so that http thread won't be
- // blocked in setupFile() when createCacheFile.
- if (synchronous) {
- CacheManager.endCacheTransaction();
- }
-
- FrameLoader loader = new FrameLoader(loadListener, mSettings,
- method, isHighPriority);
+ FrameLoader loader = new FrameLoader(loadListener, mSettings, method);
loader.setHeaders(headers);
loader.setPostData(postData);
// Set the load mode to the mode used for the current page.
@@ -581,10 +576,6 @@ class BrowserFrame extends Handler {
}
checker.responseAlert("startLoadingResource succeed");
- if (synchronous) {
- CacheManager.startCacheTransaction();
- }
-
return !synchronous ? loadListener : null;
}
@@ -615,6 +606,11 @@ class BrowserFrame extends Handler {
mCallbackProxy.onReceivedIcon(icon);
}
+ // Called by JNI when an apple-touch-icon attribute was found.
+ private void didReceiveTouchIconUrl(String url) {
+ mCallbackProxy.onReceivedTouchIconUrl(url);
+ }
+
/**
* Request a new window from the client.
* @return The BrowserFrame object stored in the new WebView.
@@ -677,6 +673,7 @@ class BrowserFrame extends Handler {
// these ids need to be in sync with enum RAW_RES_ID in WebFrame
private static final int NODOMAIN = 1;
private static final int LOADERROR = 2;
+ private static final int DRAWABLEDIR = 3;
String getRawResFilename(int id) {
int resid;
@@ -689,12 +686,26 @@ class BrowserFrame extends Handler {
resid = com.android.internal.R.raw.loaderror;
break;
+ case DRAWABLEDIR:
+ // use one known resource to find the drawable directory
+ resid = com.android.internal.R.drawable.btn_check_off;
+ break;
+
default:
Log.e(LOGTAG, "getRawResFilename got incompatible resource ID");
- return new String();
+ return "";
}
TypedValue value = new TypedValue();
mContext.getResources().getValue(resid, value, true);
+ if (id == DRAWABLEDIR) {
+ String path = value.string.toString();
+ int index = path.lastIndexOf('/');
+ if (index < 0) {
+ Log.e(LOGTAG, "Can't find drawable directory.");
+ return "";
+ }
+ return path.substring(0, index + 1);
+ }
return value.string.toString();
}
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index 3e1b602..de8f888 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.net.http.Headers;
+import android.text.TextUtils;
/**
* This class is a concrete implementation of StreamLoader that uses a
@@ -49,17 +50,22 @@ class CacheLoader extends StreamLoader {
@Override
protected void buildHeaders(Headers headers) {
StringBuilder sb = new StringBuilder(mCacheResult.mimeType);
- if (mCacheResult.encoding != null &&
- mCacheResult.encoding.length() > 0) {
+ if (!TextUtils.isEmpty(mCacheResult.encoding)) {
sb.append(';');
sb.append(mCacheResult.encoding);
}
headers.setContentType(sb.toString());
- if (mCacheResult.location != null &&
- mCacheResult.location.length() > 0) {
+ if (!TextUtils.isEmpty(mCacheResult.location)) {
headers.setLocation(mCacheResult.location);
}
- }
+ if (!TextUtils.isEmpty(mCacheResult.expiresString)) {
+ headers.setExpires(mCacheResult.expiresString);
+ }
+
+ if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) {
+ headers.setContentDisposition(mCacheResult.contentdisposition);
+ }
+ }
}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 7897435..02e8d6f 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -51,7 +51,6 @@ public final class CacheManager {
private static final String NO_STORE = "no-store";
private static final String NO_CACHE = "no-cache";
- private static final String PRIVATE = "private";
private static final String MAX_AGE = "max-age";
private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
@@ -80,12 +79,14 @@ public final class CacheManager {
int httpStatusCode;
long contentLength;
long expires;
+ String expiresString;
String localPath;
String lastModified;
String etag;
String mimeType;
String location;
String encoding;
+ String contentdisposition;
// these fields are NOT saved to the database
InputStream inStream;
@@ -108,6 +109,13 @@ public final class CacheManager {
return expires;
}
+ /**
+ * @hide Pending API council approval
+ */
+ public String getExpiresString() {
+ return expiresString;
+ }
+
public String getLastModified() {
return lastModified;
}
@@ -128,6 +136,13 @@ public final class CacheManager {
return encoding;
}
+ /**
+ * @hide Pending API council approval
+ */
+ public String getContentDisposition() {
+ return contentdisposition;
+ }
+
// For out-of-package access to the underlying streams.
public InputStream getInputStream() {
return inStream;
@@ -321,7 +336,7 @@ public final class CacheManager {
}
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.CACHE_MANAGER) {
Log.v(LOGTAG, "getCacheFile for url " + url);
}
@@ -340,7 +355,7 @@ public final class CacheManager {
* @hide - hide createCacheFile since it has a parameter of type headers, which is
* in a hidden package.
*/
- // can be called from any thread
+ // only called from WebCore thread
public static CacheResult createCacheFile(String url, int statusCode,
Headers headers, String mimeType, boolean forceCache) {
if (!forceCache && mDisabled) {
@@ -349,17 +364,25 @@ public final class CacheManager {
// according to the rfc 2616, the 303 response MUST NOT be cached.
if (statusCode == 303) {
+ // remove the saved cache if there is any
+ mDataBase.removeCache(url);
return null;
}
// like the other browsers, do not cache redirects containing a cookie
// header.
if (checkCacheRedirect(statusCode) && !headers.getSetCookie().isEmpty()) {
+ // remove the saved cache if there is any
+ mDataBase.removeCache(url);
return null;
}
CacheResult ret = parseHeaders(statusCode, headers, mimeType);
- if (ret != null) {
+ if (ret == null) {
+ // this should only happen if the headers has "no-store" in the
+ // cache-control. remove the saved cache if there is any
+ mDataBase.removeCache(url);
+ } else {
setupFiles(url, ret);
try {
ret.outStream = new FileOutputStream(ret.outFile);
@@ -403,19 +426,23 @@ public final class CacheManager {
}
cacheRet.contentLength = cacheRet.outFile.length();
- if (checkCacheRedirect(cacheRet.httpStatusCode)) {
+ boolean redirect = checkCacheRedirect(cacheRet.httpStatusCode);
+ if (redirect) {
// location is in database, no need to keep the file
cacheRet.contentLength = 0;
- cacheRet.localPath = new String();
- cacheRet.outFile.delete();
- } else if (cacheRet.contentLength == 0) {
- cacheRet.outFile.delete();
+ cacheRet.localPath = "";
+ }
+ if ((redirect || cacheRet.contentLength == 0)
+ && !cacheRet.outFile.delete()) {
+ Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed.");
+ }
+ if (cacheRet.contentLength == 0) {
return;
}
mDataBase.addCache(url, cacheRet);
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.CACHE_MANAGER) {
Log.v(LOGTAG, "saveCacheFile for url " + url);
}
}
@@ -444,7 +471,10 @@ public final class CacheManager {
// if mBaseDir doesn't exist, files can be null.
if (files != null) {
for (int i = 0; i < files.length; i++) {
- new File(mBaseDir, files[i]).delete();
+ File f = new File(mBaseDir, files[i]);
+ if (!f.delete()) {
+ Log.e(LOGTAG, f.getPath() + " delete failed.");
+ }
}
}
} catch (SecurityException e) {
@@ -472,7 +502,10 @@ public final class CacheManager {
ArrayList<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
int size = pathList.size();
for (int i = 0; i < size; i++) {
- new File(mBaseDir, pathList.get(i)).delete();
+ File f = new File(mBaseDir, pathList.get(i));
+ if (!f.delete()) {
+ Log.e(LOGTAG, f.getPath() + " delete failed.");
+ }
}
}
}
@@ -511,12 +544,7 @@ public final class CacheManager {
// cache file. If it is not, resolve the collision.
while (file.exists()) {
if (checkOldPath) {
- // as this is called from http thread through
- // createCacheFile, we need endCacheTransaction before
- // database access.
- WebViewCore.endCacheTransaction();
CacheResult oldResult = mDataBase.getCache(url);
- WebViewCore.startCacheTransaction();
if (oldResult != null && oldResult.contentLength > 0) {
if (path.equals(oldResult.localPath)) {
path = oldResult.localPath;
@@ -596,21 +624,27 @@ public final class CacheManager {
if (location != null) ret.location = location;
ret.expires = -1;
- String expires = headers.getExpires();
- if (expires != null) {
+ ret.expiresString = headers.getExpires();
+ if (ret.expiresString != null) {
try {
- ret.expires = HttpDateTime.parse(expires);
+ ret.expires = HttpDateTime.parse(ret.expiresString);
} catch (IllegalArgumentException ex) {
// Take care of the special "-1" and "0" cases
- if ("-1".equals(expires) || "0".equals(expires)) {
+ if ("-1".equals(ret.expiresString)
+ || "0".equals(ret.expiresString)) {
// make it expired, but can be used for history navigation
ret.expires = 0;
} else {
- Log.e(LOGTAG, "illegal expires: " + expires);
+ Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
}
}
}
+ String contentDisposition = headers.getContentDisposition();
+ if (contentDisposition != null) {
+ ret.contentdisposition = contentDisposition;
+ }
+
String lastModified = headers.getLastModified();
if (lastModified != null) ret.lastModified = lastModified;
@@ -628,7 +662,7 @@ public final class CacheManager {
// must be re-validated on every load. It does not mean that
// the content can not be cached. set to expire 0 means it
// can only be used in CACHE_MODE_CACHE_ONLY case
- if (NO_CACHE.equals(controls[i]) || PRIVATE.equals(controls[i])) {
+ if (NO_CACHE.equals(controls[i])) {
ret.expires = 0;
} else if (controls[i].startsWith(MAX_AGE)) {
int separator = controls[i].indexOf('=');
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 17d3f94..e504591 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -72,40 +72,46 @@ class CallbackProxy extends Handler {
private final Context mContext;
// Message Ids
- private static final int PAGE_STARTED = 100;
- private static final int RECEIVED_ICON = 101;
- private static final int RECEIVED_TITLE = 102;
- private static final int OVERRIDE_URL = 103;
- private static final int AUTH_REQUEST = 104;
- private static final int SSL_ERROR = 105;
- private static final int PROGRESS = 106;
- private static final int UPDATE_VISITED = 107;
- private static final int LOAD_RESOURCE = 108;
- private static final int CREATE_WINDOW = 109;
- private static final int CLOSE_WINDOW = 110;
- private static final int SAVE_PASSWORD = 111;
- private static final int JS_ALERT = 112;
- private static final int JS_CONFIRM = 113;
- private static final int JS_PROMPT = 114;
- private static final int JS_UNLOAD = 115;
- private static final int ASYNC_KEYEVENTS = 116;
- private static final int TOO_MANY_REDIRECTS = 117;
- private static final int DOWNLOAD_FILE = 118;
- private static final int REPORT_ERROR = 119;
- private static final int RESEND_POST_DATA = 120;
- private static final int PAGE_FINISHED = 121;
- private static final int REQUEST_FOCUS = 122;
- private static final int SCALE_CHANGED = 123;
- private static final int RECEIVED_CERTIFICATE = 124;
- private static final int SWITCH_OUT_HISTORY = 125;
- private static final int JS_TIMEOUT = 126;
+ private static final int PAGE_STARTED = 100;
+ private static final int RECEIVED_ICON = 101;
+ private static final int RECEIVED_TITLE = 102;
+ private static final int OVERRIDE_URL = 103;
+ private static final int AUTH_REQUEST = 104;
+ private static final int SSL_ERROR = 105;
+ private static final int PROGRESS = 106;
+ private static final int UPDATE_VISITED = 107;
+ private static final int LOAD_RESOURCE = 108;
+ private static final int CREATE_WINDOW = 109;
+ private static final int CLOSE_WINDOW = 110;
+ private static final int SAVE_PASSWORD = 111;
+ private static final int JS_ALERT = 112;
+ private static final int JS_CONFIRM = 113;
+ private static final int JS_PROMPT = 114;
+ private static final int JS_UNLOAD = 115;
+ private static final int ASYNC_KEYEVENTS = 116;
+ private static final int TOO_MANY_REDIRECTS = 117;
+ private static final int DOWNLOAD_FILE = 118;
+ private static final int REPORT_ERROR = 119;
+ private static final int RESEND_POST_DATA = 120;
+ private static final int PAGE_FINISHED = 121;
+ private static final int REQUEST_FOCUS = 122;
+ private static final int SCALE_CHANGED = 123;
+ private static final int RECEIVED_CERTIFICATE = 124;
+ private static final int SWITCH_OUT_HISTORY = 125;
+ private static final int EXCEEDED_DATABASE_QUOTA = 126;
+ private static final int REACHED_APPCACHE_MAXSIZE = 127;
+ private static final int JS_TIMEOUT = 128;
+ private static final int ADD_MESSAGE_TO_CONSOLE = 129;
+ private static final int GEOLOCATION_PERMISSIONS_SHOW_PROMPT = 130;
+ private static final int GEOLOCATION_PERMISSIONS_HIDE_PROMPT = 131;
+ private static final int RECEIVED_TOUCH_ICON_URL = 132;
// Message triggered by the client to resume execution
- private static final int NOTIFY = 200;
+ private static final int NOTIFY = 200;
// Result transportation object for returning results across thread
// boundaries.
- private class ResultTransport<E> {
+ private static class ResultTransport<E> {
// Private result object
private E mResult;
@@ -145,6 +151,16 @@ class CallbackProxy extends Handler {
}
/**
+ * Get the WebChromeClient.
+ * @return the current WebChromeClient instance.
+ *
+ *@hide pending API council approval.
+ */
+ public WebChromeClient getWebChromeClient() {
+ return mWebChromeClient;
+ }
+
+ /**
* Set the client DownloadListener.
* @param client An implementation of DownloadListener.
*/
@@ -229,6 +245,13 @@ class CallbackProxy extends Handler {
}
break;
+ case RECEIVED_TOUCH_ICON_URL:
+ if (mWebChromeClient != null) {
+ mWebChromeClient.onReceivedTouchIconUrl(mWebView,
+ (String) msg.obj);
+ }
+ break;
+
case RECEIVED_TITLE:
if (mWebChromeClient != null) {
mWebChromeClient.onReceivedTitle(mWebView,
@@ -389,6 +412,63 @@ class CallbackProxy extends Handler {
}
break;
+ case EXCEEDED_DATABASE_QUOTA:
+ if (mWebChromeClient != null) {
+ HashMap<String, Object> map =
+ (HashMap<String, Object>) msg.obj;
+ String databaseIdentifier =
+ (String) map.get("databaseIdentifier");
+ String url = (String) map.get("url");
+ long currentQuota =
+ ((Long) map.get("currentQuota")).longValue();
+ long totalUsedQuota =
+ ((Long) map.get("totalUsedQuota")).longValue();
+ long estimatedSize =
+ ((Long) map.get("estimatedSize")).longValue();
+ WebStorage.QuotaUpdater quotaUpdater =
+ (WebStorage.QuotaUpdater) map.get("quotaUpdater");
+
+ mWebChromeClient.onExceededDatabaseQuota(url,
+ databaseIdentifier, currentQuota, estimatedSize,
+ totalUsedQuota, quotaUpdater);
+ }
+ break;
+
+ case REACHED_APPCACHE_MAXSIZE:
+ if (mWebChromeClient != null) {
+ HashMap<String, Object> map =
+ (HashMap<String, Object>) msg.obj;
+ long spaceNeeded =
+ ((Long) map.get("spaceNeeded")).longValue();
+ long totalUsedQuota =
+ ((Long) map.get("totalUsedQuota")).longValue();
+ WebStorage.QuotaUpdater quotaUpdater =
+ (WebStorage.QuotaUpdater) map.get("quotaUpdater");
+
+ mWebChromeClient.onReachedMaxAppCacheSize(spaceNeeded,
+ totalUsedQuota, quotaUpdater);
+ }
+ break;
+
+ case GEOLOCATION_PERMISSIONS_SHOW_PROMPT:
+ if (mWebChromeClient != null) {
+ HashMap<String, Object> map =
+ (HashMap<String, Object>) msg.obj;
+ String origin = (String) map.get("origin");
+ GeolocationPermissions.Callback callback =
+ (GeolocationPermissions.Callback)
+ map.get("callback");
+ mWebChromeClient.onGeolocationPermissionsShowPrompt(origin,
+ callback);
+ }
+ break;
+
+ case GEOLOCATION_PERMISSIONS_HIDE_PROMPT:
+ if (mWebChromeClient != null) {
+ mWebChromeClient.onGeolocationPermissionsHidePrompt();
+ }
+ break;
+
case JS_ALERT:
if (mWebChromeClient != null) {
final JsResult res = (JsResult) msg.obj;
@@ -563,6 +643,13 @@ class CallbackProxy extends Handler {
case SWITCH_OUT_HISTORY:
mWebView.switchOutDrawHistory();
break;
+
+ case ADD_MESSAGE_TO_CONSOLE:
+ String message = msg.getData().getString("message");
+ String sourceID = msg.getData().getString("sourceID");
+ int lineNumber = msg.getData().getInt("lineNumber");
+ mWebChromeClient.addMessageToConsole(message, lineNumber, sourceID);
+ break;
}
}
@@ -605,7 +692,40 @@ class CallbackProxy extends Handler {
//--------------------------------------------------------------------------
// Performance probe
+ private static final boolean PERF_PROBE = false;
private long mWebCoreThreadTime;
+ private long mWebCoreIdleTime;
+
+ /*
+ * If PERF_PROBE is true, this block needs to be added to MessageQueue.java.
+ * startWait() and finishWait() should be called before and after wait().
+
+ private WaitCallback mWaitCallback = null;
+ public static interface WaitCallback {
+ void startWait();
+ void finishWait();
+ }
+ public final void setWaitCallback(WaitCallback callback) {
+ mWaitCallback = callback;
+ }
+ */
+
+ // un-comment this block if PERF_PROBE is true
+ /*
+ private IdleCallback mIdleCallback = new IdleCallback();
+
+ private final class IdleCallback implements MessageQueue.WaitCallback {
+ private long mStartTime = 0;
+
+ public void finishWait() {
+ mWebCoreIdleTime += SystemClock.uptimeMillis() - mStartTime;
+ }
+
+ public void startWait() {
+ mStartTime = SystemClock.uptimeMillis();
+ }
+ }
+ */
public void onPageStarted(String url, Bitmap favicon) {
// Do an unsynchronized quick check to avoid posting if no callback has
@@ -614,9 +734,12 @@ class CallbackProxy extends Handler {
return;
}
// Performance probe
- if (false) {
+ if (PERF_PROBE) {
mWebCoreThreadTime = SystemClock.currentThreadTimeMillis();
+ mWebCoreIdleTime = 0;
Network.getInstance(mContext).startTiming();
+ // un-comment this if PERF_PROBE is true
+// Looper.myQueue().setWaitCallback(mIdleCallback);
}
Message msg = obtainMessage(PAGE_STARTED);
msg.obj = favicon;
@@ -631,10 +754,12 @@ class CallbackProxy extends Handler {
return;
}
// Performance probe
- if (false) {
+ if (PERF_PROBE) {
+ // un-comment this if PERF_PROBE is true
+// Looper.myQueue().setWaitCallback(null);
Log.d("WebCore", "WebCore thread used " +
(SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
- + " ms");
+ + " ms and idled " + mWebCoreIdleTime + " ms");
Network.getInstance(mContext).stopTiming();
}
Message msg = obtainMessage(PAGE_FINISHED, url);
@@ -834,7 +959,7 @@ class CallbackProxy extends Handler {
String password, Message resumeMsg) {
// resumeMsg should be null at this point because we want to create it
// within the CallbackProxy.
- if (WebView.DEBUG) {
+ if (DebugFlags.CALLBACK_PROXY) {
junit.framework.Assert.assertNull(resumeMsg);
}
resumeMsg = obtainMessage(NOTIFY);
@@ -939,6 +1064,21 @@ class CallbackProxy extends Handler {
sendMessage(obtainMessage(RECEIVED_ICON, icon));
}
+ /* package */ void onReceivedTouchIconUrl(String url) {
+ // We should have a current item but we do not want to crash so check
+ // for null.
+ WebHistoryItem i = mBackForwardList.getCurrentItem();
+ if (i != null) {
+ i.setTouchIconUrl(url);
+ }
+ // Do an unsynchronized quick check to avoid posting if no callback has
+ // been set.
+ if (mWebChromeClient == null) {
+ return;
+ }
+ sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, url));
+ }
+
public void onReceivedTitle(String title) {
// Do an unsynchronized quick check to avoid posting if no callback has
// been set.
@@ -1037,6 +1177,130 @@ class CallbackProxy extends Handler {
}
/**
+ * Called by WebViewCore to inform the Java side that the current origin
+ * has overflowed it's database quota. Called in the WebCore thread so
+ * posts a message to the UI thread that will prompt the WebChromeClient
+ * for what to do. On return back to C++ side, the WebCore thread will
+ * sleep pending a new quota value.
+ * @param url The URL that caused the quota overflow.
+ * @param databaseIdentifier The identifier of the database that the
+ * transaction that caused the overflow was running on.
+ * @param currentQuota The current quota the origin is allowed.
+ * @param estimatedSize The estimated size of the database.
+ * @param totalUsedQuota is the sum of all origins' quota.
+ * @param quotaUpdater An instance of a class encapsulating a callback
+ * to WebViewCore to run when the decision to allow or deny more
+ * quota has been made.
+ */
+ public void onExceededDatabaseQuota(
+ String url, String databaseIdentifier, long currentQuota,
+ long estimatedSize, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ if (mWebChromeClient == null) {
+ quotaUpdater.updateQuota(currentQuota);
+ return;
+ }
+
+ Message exceededQuota = obtainMessage(EXCEEDED_DATABASE_QUOTA);
+ HashMap<String, Object> map = new HashMap();
+ map.put("databaseIdentifier", databaseIdentifier);
+ map.put("url", url);
+ map.put("currentQuota", currentQuota);
+ map.put("estimatedSize", estimatedSize);
+ map.put("totalUsedQuota", totalUsedQuota);
+ map.put("quotaUpdater", quotaUpdater);
+ exceededQuota.obj = map;
+ sendMessage(exceededQuota);
+ }
+
+ /**
+ * Called by WebViewCore to inform the Java side that the appcache has
+ * exceeded its max size.
+ * @param spaceNeeded is the amount of disk space that would be needed
+ * in order for the last appcache operation to succeed.
+ * @param totalUsedQuota is the sum of all origins' quota.
+ * @param quotaUpdater An instance of a class encapsulating a callback
+ * to WebViewCore to run when the decision to allow or deny a bigger
+ * app cache size has been made.
+ * @hide pending API council approval.
+ */
+ public void onReachedMaxAppCacheSize(long spaceNeeded,
+ long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
+ if (mWebChromeClient == null) {
+ quotaUpdater.updateQuota(0);
+ return;
+ }
+
+ Message msg = obtainMessage(REACHED_APPCACHE_MAXSIZE);
+ HashMap<String, Object> map = new HashMap();
+ map.put("spaceNeeded", spaceNeeded);
+ map.put("totalUsedQuota", totalUsedQuota);
+ map.put("quotaUpdater", quotaUpdater);
+ msg.obj = map;
+ sendMessage(msg);
+ }
+
+ /**
+ * Called by WebViewCore to instruct the browser to display a prompt to ask
+ * the user to set the Geolocation permission state for the given origin.
+ * @param origin The origin requesting Geolocation permsissions.
+ * @param callback The callback to call once a permission state has been
+ * obtained.
+ * @hide pending API council review.
+ */
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ if (mWebChromeClient == null) {
+ return;
+ }
+
+ Message showMessage =
+ obtainMessage(GEOLOCATION_PERMISSIONS_SHOW_PROMPT);
+ HashMap<String, Object> map = new HashMap();
+ map.put("origin", origin);
+ map.put("callback", callback);
+ showMessage.obj = map;
+ sendMessage(showMessage);
+ }
+
+ /**
+ * Called by WebViewCore to instruct the browser to hide the Geolocation
+ * permissions prompt.
+ * origin.
+ * @hide pending API council review.
+ */
+ public void onGeolocationPermissionsHidePrompt() {
+ if (mWebChromeClient == null) {
+ return;
+ }
+
+ Message hideMessage = obtainMessage(GEOLOCATION_PERMISSIONS_HIDE_PROMPT);
+ sendMessage(hideMessage);
+ }
+
+ /**
+ * Called by WebViewCore when we have a message to be added to the JavaScript
+ * error console. Sends a message to the Java side with the details.
+ * @param message The message to add to the console.
+ * @param lineNumber The lineNumber of the source file on which the error
+ * occurred.
+ * @param sourceID The filename of the source file in which the error
+ * occurred.
+ * @hide pending API counsel.
+ */
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ if (mWebChromeClient == null) {
+ return;
+ }
+
+ Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
+ msg.getData().putString("message", message);
+ msg.getData().putString("sourceID", sourceID);
+ msg.getData().putInt("lineNumber", lineNumber);
+ sendMessage(msg);
+ }
+
+ /**
* @hide pending API council approval
*/
public boolean onJsTimeout() {
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
index f6d7f69..19aa087 100644
--- a/core/java/android/webkit/ContentLoader.java
+++ b/core/java/android/webkit/ContentLoader.java
@@ -57,6 +57,16 @@ class ContentLoader extends StreamLoader {
}
+ private String errString(Exception ex) {
+ String exMessage = ex.getMessage();
+ String errString = mContext.getString(
+ com.android.internal.R.string.httpErrorFileNotFound);
+ if (exMessage != null) {
+ errString += " " + exMessage;
+ }
+ return errString;
+ }
+
@Override
protected boolean setupStreamAndSendStatus() {
Uri uri = Uri.parse(mUrl);
@@ -73,28 +83,16 @@ class ContentLoader extends StreamLoader {
mDataStream = mContext.getContentResolver().openInputStream(uri);
mHandler.status(1, 1, 0, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(
- EventHandler.FILE_NOT_FOUND_ERROR,
- mContext.getString(
- com.android.internal.R.string.httpErrorFileNotFound) +
- " " + ex.getMessage());
+ mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (java.io.IOException ex) {
- mHandler.error(
- EventHandler.FILE_ERROR,
- mContext.getString(
- com.android.internal.R.string.httpErrorFileNotFound) +
- " " + ex.getMessage());
+ mHandler.error(EventHandler.FILE_ERROR, errString(ex));
return false;
} catch (RuntimeException ex) {
// readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils
// can throw a serial of RuntimeException. Catch them all here.
- mHandler.error(
- EventHandler.FILE_ERROR,
- mContext.getString(
- com.android.internal.R.string.httpErrorFileNotFound) +
- " " + ex.getMessage());
+ mHandler.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
@@ -105,8 +103,7 @@ class ContentLoader extends StreamLoader {
if (mContentType != null) {
headers.setContentType("text/html");
}
- // override the cache-control header set by StreamLoader as content can
- // change, we don't want WebKit to cache it
+ // content can change, we don't want WebKit to cache it
headers.setCacheControl("no-store, no-cache");
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index e8c2279..fca591f 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -23,9 +23,12 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
/**
* CookieManager manages cookies according to RFC2109 spec.
@@ -190,6 +193,31 @@ public final class CookieManager {
}
}
+ private static final CookieComparator COMPARATOR = new CookieComparator();
+
+ private static final class CookieComparator implements Comparator<Cookie> {
+ public int compare(Cookie cookie1, Cookie cookie2) {
+ // According to RFC 2109, multiple cookies are ordered in a way such
+ // that those with more specific Path attributes precede those with
+ // less specific. Ordering with respect to other attributes (e.g.,
+ // Domain) is unspecified.
+ // As Set is not modified if the two objects are same, we do want to
+ // assign different value for each cookie.
+ int diff = cookie2.path.length() - cookie1.path.length();
+ if (diff == 0) {
+ diff = cookie2.domain.length() - cookie1.domain.length();
+ if (diff == 0) {
+ diff = cookie2.name.hashCode() - cookie1.name.hashCode();
+ if (diff == 0) {
+ Log.w(LOGTAG, "Found two cookies with the same value." +
+ "cookie1=" + cookie1 + " , cookie2=" + cookie2);
+ }
+ }
+ }
+ return diff;
+ }
+ }
+
private CookieManager() {
}
@@ -262,7 +290,7 @@ public final class CookieManager {
if (!mAcceptCookie || uri == null) {
return;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
}
@@ -401,8 +429,8 @@ public final class CookieManager {
long now = System.currentTimeMillis();
boolean secure = HTTPS.equals(uri.mScheme);
Iterator<Cookie> iter = cookieList.iterator();
- StringBuilder ret = new StringBuilder(256);
+ SortedSet<Cookie> cookieSet = new TreeSet<Cookie>(COMPARATOR);
while (iter.hasNext()) {
Cookie cookie = iter.next();
if (cookie.domainMatch(hostAndPath[0]) &&
@@ -413,26 +441,33 @@ public final class CookieManager {
&& (!cookie.secure || secure)
&& cookie.mode != Cookie.MODE_DELETED) {
cookie.lastAcessTime = now;
+ cookieSet.add(cookie);
+ }
+ }
- if (ret.length() > 0) {
- ret.append(SEMICOLON);
- // according to RC2109, SEMICOLON is office separator,
- // but when log in yahoo.com, it needs WHITE_SPACE too.
- ret.append(WHITE_SPACE);
- }
-
- ret.append(cookie.name);
- ret.append(EQUAL);
- ret.append(cookie.value);
+ StringBuilder ret = new StringBuilder(256);
+ Iterator<Cookie> setIter = cookieSet.iterator();
+ while (setIter.hasNext()) {
+ Cookie cookie = setIter.next();
+ if (ret.length() > 0) {
+ ret.append(SEMICOLON);
+ // according to RC2109, SEMICOLON is official separator,
+ // but when log in yahoo.com, it needs WHITE_SPACE too.
+ ret.append(WHITE_SPACE);
}
+
+ ret.append(cookie.name);
+ ret.append(EQUAL);
+ ret.append(cookie.value);
}
+
if (ret.length() > 0) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
}
return ret.toString();
} else {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "getCookie: uri: " + uri
+ " But can't find cookie.");
}
@@ -588,7 +623,7 @@ public final class CookieManager {
Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator();
while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
ArrayList<Cookie> list = listIter.next();
- if (WebView.DEBUG) {
+ if (DebugFlags.COOKIE_MANAGER) {
Iterator<Cookie> iter = list.iterator();
while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
Cookie cookie = iter.next();
@@ -608,7 +643,7 @@ public final class CookieManager {
ArrayList<Cookie> retlist = new ArrayList<Cookie>();
if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
- if (WebView.DEBUG) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, count + " cookies used " + byteCount
+ " bytes with " + mapSize + " domains");
}
@@ -616,7 +651,7 @@ public final class CookieManager {
int toGo = mapSize / 10 + 1;
while (toGo-- > 0){
String domain = domains[toGo].toString();
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "delete domain: " + domain
+ " from RAM cache");
}
@@ -798,22 +833,24 @@ public final class CookieManager {
// "secure" is a known attribute doesn't use "=";
// while sites like live.com uses "secure="
- if (length - index > SECURE_LENGTH
+ if (length - index >= SECURE_LENGTH
&& cookieString.substring(index, index + SECURE_LENGTH).
equalsIgnoreCase(SECURE)) {
index += SECURE_LENGTH;
cookie.secure = true;
+ if (index == length) break;
if (cookieString.charAt(index) == EQUAL) index++;
continue;
}
// "httponly" is a known attribute doesn't use "=";
// while sites like live.com uses "httponly="
- if (length - index > HTTP_ONLY_LENGTH
+ if (length - index >= HTTP_ONLY_LENGTH
&& cookieString.substring(index,
index + HTTP_ONLY_LENGTH).
equalsIgnoreCase(HTTP_ONLY)) {
index += HTTP_ONLY_LENGTH;
+ if (index == length) break;
if (cookieString.charAt(index) == EQUAL) index++;
// FIXME: currently only parse the attribute
continue;
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index 8d66529..14375d2 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -24,30 +24,39 @@ import java.util.ArrayList;
import java.util.Iterator;
/**
- * The class CookieSyncManager is used to synchronize the browser cookies
- * between RAM and FLASH. To get the best performance, browser cookie is saved
- * in RAM. We use a separate thread to sync the cookies between RAM and FLASH on
- * a timer base.
+ * The CookieSyncManager is used to synchronize the browser cookie store
+ * between RAM and permanent storage. To get the best performance, browser cookies are
+ * saved in RAM. A separate thread saves the cookies between, driven by a timer.
* <p>
+ *
* To use the CookieSyncManager, the host application has to call the following
- * when the application starts.
- * <p>
- * CookieSyncManager.createInstance(context)
- * <p>
- * To set up for sync, the host application has to call
- * <p>
- * CookieSyncManager.getInstance().startSync()
+ * when the application starts:
* <p>
- * in its Activity.onResume(), and call
+ *
+ * <pre class="prettyprint">CookieSyncManager.createInstance(context)</pre><p>
+ *
+ * To set up for sync, the host application has to call<p>
+ * <pre class="prettyprint">CookieSyncManager.getInstance().startSync()</pre><p>
+ *
+ * in Activity.onResume(), and call
* <p>
+ *
+ * <pre class="prettyprint">
* CookieSyncManager.getInstance().stopSync()
- * <p>
- * in its Activity.onStop().
- * <p>
+ * </pre><p>
+ *
+ * in Activity.onPause().<p>
+ *
* To get instant sync instead of waiting for the timer to trigger, the host can
* call
* <p>
- * CookieSyncManager.getInstance().sync()
+ * <pre class="prettyprint">CookieSyncManager.getInstance().sync()</pre><p>
+ *
+ * The sync interval is 5 minutes, so you will want to force syncs
+ * manually anyway, for instance in {@link
+ * WebViewClient#onPageFinished}. Note that even sync() happens
+ * asynchronously, so don't do it just as your activity is shutting
+ * down.
*/
public final class CookieSyncManager extends WebSyncManager {
@@ -90,7 +99,7 @@ public final class CookieSyncManager extends WebSyncManager {
}
/**
- * Package level api, called from CookieManager Get all the cookies which
+ * Package level api, called from CookieManager. Get all the cookies which
* matches a given base domain.
* @param domain
* @return A list of Cookie
@@ -161,7 +170,7 @@ public final class CookieSyncManager extends WebSyncManager {
}
protected void syncFromRamToFlash() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_SYNC_MANAGER) {
Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
}
@@ -178,7 +187,7 @@ public final class CookieSyncManager extends WebSyncManager {
CookieManager.getInstance().deleteLRUDomain();
syncFromRamToFlash(lruList);
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_SYNC_MANAGER) {
Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE");
}
}
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index dcdc949..6c5d10d 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -16,12 +16,10 @@
package android.webkit;
-import org.apache.http.protocol.HTTP;
-
-import android.net.http.Headers;
-
import java.io.ByteArrayInputStream;
+import org.apache.harmony.luni.util.Base64;
+
/**
* This class is a concrete implementation of StreamLoader that uses the
* content supplied as a URL as the source for the stream. The mimetype
@@ -30,8 +28,6 @@ import java.io.ByteArrayInputStream;
*/
class DataLoader extends StreamLoader {
- private String mContentType; // Content mimetype, if supplied in URL
-
/**
* Constructor uses the dataURL as the source for an InputStream
* @param dataUrl data: URL string optionally containing a mimetype
@@ -41,16 +37,20 @@ class DataLoader extends StreamLoader {
super(loadListener);
String url = dataUrl.substring("data:".length());
- String content;
+ byte[] data = null;
int commaIndex = url.indexOf(',');
if (commaIndex != -1) {
- mContentType = url.substring(0, commaIndex);
- content = url.substring(commaIndex + 1);
+ String contentType = url.substring(0, commaIndex);
+ data = url.substring(commaIndex + 1).getBytes();
+ loadListener.parseContentTypeHeader(contentType);
+ if ("base64".equals(loadListener.transferEncoding())) {
+ data = Base64.decode(data);
+ }
} else {
- content = url;
+ data = url.getBytes();
}
- mDataStream = new ByteArrayInputStream(content.getBytes());
- mContentLength = content.length();
+ mDataStream = new ByteArrayInputStream(data);
+ mContentLength = data.length;
}
@Override
@@ -60,10 +60,7 @@ class DataLoader extends StreamLoader {
}
@Override
- protected void buildHeaders(Headers headers) {
- if (mContentType != null) {
- headers.setContentType(mContentType);
- }
+ protected void buildHeaders(android.net.http.Headers h) {
}
/**
diff --git a/core/java/android/webkit/DateSorter.java b/core/java/android/webkit/DateSorter.java
index 750403b..c46702e 100644
--- a/core/java/android/webkit/DateSorter.java
+++ b/core/java/android/webkit/DateSorter.java
@@ -43,9 +43,6 @@ public class DateSorter {
private static final int NUM_DAYS_AGO = 5;
- Date mDate = new Date();
- Calendar mCal = Calendar.getInstance();
-
/**
* @param context Application context
*/
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
new file mode 100644
index 0000000..8e25395
--- /dev/null
+++ b/core/java/android/webkit/DebugFlags.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is a container for all of the debug flags used in the Java
+ * components of webkit. These flags must be final in order to ensure that
+ * the compiler optimizes the code that uses them out of the final executable.
+ *
+ * The name of each flags maps directly to the name of the class in which that
+ * flag is used.
+ *
+ */
+class DebugFlags {
+
+ public static final boolean BROWSER_FRAME = false;
+ public static final boolean CACHE_MANAGER = false;
+ public static final boolean CALLBACK_PROXY = false;
+ public static final boolean COOKIE_MANAGER = false;
+ public static final boolean COOKIE_SYNC_MANAGER = false;
+ public static final boolean FRAME_LOADER = false;
+ public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
+ public static final boolean LOAD_LISTENER = false;
+ public static final boolean NETWORK = false;
+ public static final boolean SSL_ERROR_HANDLER = false;
+ public static final boolean STREAM_LOADER = false;
+ public static final boolean URL_UTIL = false;
+ public static final boolean WEB_BACK_FORWARD_LIST = false;
+ public static final boolean WEB_SETTINGS = false;
+ public static final boolean WEB_SYNC_MANAGER = false;
+ public static final boolean WEB_TEXT_VIEW = false;
+ public static final boolean WEB_VIEW = false;
+ public static final boolean WEB_VIEW_CORE = false;
+
+}
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
index 54a4c1d..085f1f4 100644
--- a/core/java/android/webkit/FileLoader.java
+++ b/core/java/android/webkit/FileLoader.java
@@ -72,6 +72,15 @@ class FileLoader extends StreamLoader {
}
}
+ private String errString(Exception ex) {
+ String exMessage = ex.getMessage();
+ String errString = mContext.getString(R.string.httpErrorFileNotFound);
+ if (exMessage != null) {
+ errString += " " + exMessage;
+ }
+ return errString;
+ }
+
@Override
protected boolean setupStreamAndSendStatus() {
try {
@@ -95,16 +104,11 @@ class FileLoader extends StreamLoader {
mHandler.status(1, 1, 0, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(
- EventHandler.FILE_NOT_FOUND_ERROR,
- mContext.getString(R.string.httpErrorFileNotFound) +
- " " + ex.getMessage());
+ mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (java.io.IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR,
- mContext.getString(R.string.httpErrorFileNotFound) +
- " " + ex.getMessage());
+ mHandler.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 66ab021..c1eeb3b 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -28,7 +28,6 @@ class FrameLoader {
private final LoadListener mListener;
private final String mMethod;
- private final boolean mIsHighPriority;
private final WebSettings mSettings;
private Map<String, String> mHeaders;
private byte[] mPostData;
@@ -52,11 +51,10 @@ class FrameLoader {
private static final String LOGTAG = "webkit";
FrameLoader(LoadListener listener, WebSettings settings,
- String method, boolean highPriority) {
+ String method) {
mListener = listener;
mHeaders = null;
mMethod = method;
- mIsHighPriority = highPriority;
mCacheMode = WebSettings.LOAD_NORMAL;
mSettings = settings;
}
@@ -97,17 +95,6 @@ class FrameLoader {
public boolean executeLoad() {
String url = mListener.url();
- // Attempt to decode the percent-encoded url.
- try {
- url = new String(URLUtil.decode(url.getBytes()));
- } catch (IllegalArgumentException e) {
- // Fail with a bad url error if the decode fails.
- mListener.error(EventHandler.ERROR_BAD_URL,
- mListener.getContext().getString(
- com.android.internal.R.string.httpErrorBadUrl));
- return false;
- }
-
if (URLUtil.isNetworkUrl(url)){
if (mSettings.getBlockNetworkLoads()) {
mListener.error(EventHandler.ERROR_BAD_URL,
@@ -115,12 +102,19 @@ class FrameLoader {
com.android.internal.R.string.httpErrorBadUrl));
return false;
}
+ // Make sure it is correctly URL encoded before sending the request
+ if (!URLUtil.verifyURLEncoding(url)) {
+ mListener.error(EventHandler.ERROR_BAD_URL,
+ mListener.getContext().getString(
+ com.android.internal.R.string.httpErrorBadUrl));
+ return false;
+ }
mNetwork = Network.getInstance(mListener.getContext());
return handleHTTPLoad();
} else if (handleLocalFile(url, mListener, mSettings)) {
return true;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:"
+ mListener.url());
}
@@ -134,6 +128,18 @@ class FrameLoader {
/* package */
static boolean handleLocalFile(String url, LoadListener loadListener,
WebSettings settings) {
+ // Attempt to decode the percent-encoded url before passing to the
+ // local loaders.
+ try {
+ url = new String(URLUtil.decode(url.getBytes()));
+ } catch (IllegalArgumentException e) {
+ loadListener.error(EventHandler.ERROR_BAD_URL,
+ loadListener.getContext().getString(
+ com.android.internal.R.string.httpErrorBadUrl));
+ // Return true here so we do not trigger an unsupported scheme
+ // error.
+ return true;
+ }
if (URLUtil.isAssetUrl(url)) {
FileLoader.requestUrl(url, loadListener, loadListener.getContext(),
true, settings.getAllowFileAccess());
@@ -166,21 +172,17 @@ class FrameLoader {
populateStaticHeaders();
populateHeaders();
- // response was handled by UrlIntercept, don't issue HTTP request
- if (handleUrlIntercept()) return true;
-
// response was handled by Cache, don't issue HTTP request
if (handleCache()) {
// push the request data down to the LoadListener
// as response from the cache could be a redirect
// and we may need to initiate a network request if the cache
// can't satisfy redirect URL
- mListener.setRequestData(mMethod, mHeaders, mPostData,
- mIsHighPriority);
+ mListener.setRequestData(mMethod, mHeaders, mPostData);
return true;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: "
+ mListener.url());
}
@@ -190,7 +192,7 @@ class FrameLoader {
try {
ret = mNetwork.requestURL(mMethod, mHeaders,
- mPostData, mListener, mIsHighPriority);
+ mPostData, mListener);
} catch (android.net.ParseException ex) {
error = EventHandler.ERROR_BAD_URL;
} catch (java.lang.RuntimeException ex) {
@@ -207,11 +209,11 @@ class FrameLoader {
}
/*
- * This function is used by handleUrlInterecpt and handleCache to
+ * This function is used by handleCache to
* setup a load from the byte stream in a CacheResult.
*/
private void startCacheLoad(CacheResult result) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: loading from cache: "
+ mListener.url());
}
@@ -223,30 +225,6 @@ class FrameLoader {
}
/*
- * This function is used by handleHTTPLoad to allow URL
- * interception. This can be used to provide alternative load
- * methods such as locally stored versions or for debugging.
- *
- * Returns true if the response was handled by UrlIntercept.
- */
- private boolean handleUrlIntercept() {
- // Check if the URL can be served from UrlIntercept. If
- // successful, return the data just like a cache hit.
-
- PluginData data = UrlInterceptRegistry.getPluginData(
- mListener.url(), mHeaders);
-
- if(data != null) {
- PluginContentLoader loader =
- new PluginContentLoader(mListener, data);
- loader.load();
- return true;
- }
- // Not intercepted. Carry on as normal.
- return false;
- }
-
- /*
* This function is used by the handleHTTPLoad to setup the cache headers
* correctly.
* Returns true if the response was handled from the cache
@@ -285,7 +263,7 @@ class FrameLoader {
// of it's state. If it is not in the cache, then go to the
// network.
case WebSettings.LOAD_CACHE_ELSE_NETWORK: {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: checking cache: "
+ mListener.url());
}
diff --git a/core/java/android/webkit/GearsPermissionsManager.java b/core/java/android/webkit/GearsPermissionsManager.java
deleted file mode 100644
index 6549cb8..0000000
--- a/core/java/android/webkit/GearsPermissionsManager.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.database.ContentObserver;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
-import android.os.Handler;
-import android.preference.PreferenceManager;
-import android.provider.Settings;
-import android.util.Log;
-
-import java.io.File;
-import java.util.HashSet;
-
-/**
- * Donut-specific hack to keep Gears permissions in sync with the
- * system location setting.
- */
-class GearsPermissionsManager {
- // The application context.
- Context mContext;
- // The path to gears.so.
- private String mGearsPath;
-
- // The Gears permissions database directory.
- private final static String GEARS_DATABASE_DIR = "gears";
- // The Gears permissions database file name.
- private final static String GEARS_DATABASE_FILE = "permissions.db";
- // The Gears location permissions table.
- private final static String GEARS_LOCATION_ACCESS_TABLE_NAME =
- "LocationAccess";
- // The Gears storage access permissions table.
- private final static String GEARS_STORAGE_ACCESS_TABLE_NAME = "Access";
- // The Gears permissions db schema version table.
- private final static String GEARS_SCHEMA_VERSION_TABLE_NAME =
- "VersionInfo";
- // The Gears permission value that denotes "allow access to location".
- private static final int GEARS_ALLOW_LOCATION_ACCESS = 1;
- // The shared pref name.
- private static final String LAST_KNOWN_LOCATION_SETTING =
- "lastKnownLocationSystemSetting";
- // The Browser package name.
- private static final String BROWSER_PACKAGE_NAME = "com.android.browser";
- // The Secure Settings observer that will be notified when the system
- // location setting changes.
- private SecureSettingsObserver mSettingsObserver;
- // The Google URLs whitelisted for Gears location access.
- private static HashSet<String> sGearsWhiteList;
-
- static {
- sGearsWhiteList = new HashSet<String>();
- // NOTE: DO NOT ADD A "/" AT THE END!
- sGearsWhiteList.add("http://www.google.com");
- sGearsWhiteList.add("http://www.google.co.uk");
- }
-
- private static final String LOGTAG = "webcore";
- static final boolean DEBUG = false;
- static final boolean LOGV_ENABLED = DEBUG;
-
- GearsPermissionsManager(Context context, String gearsPath) {
- mContext = context;
- mGearsPath = gearsPath;
- }
-
- public void doCheckAndStartObserver() {
- // Are we running in the browser?
- if (!BROWSER_PACKAGE_NAME.equals(mContext.getPackageName())) {
- return;
- }
- // Do the check.
- checkGearsPermissions();
- // Install the observer.
- mSettingsObserver = new SecureSettingsObserver();
- mSettingsObserver.observe();
- }
-
- private void checkGearsPermissions() {
- // Get the current system settings.
- int setting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.USE_LOCATION_FOR_SERVICES, -1);
- // Check if we need to set the Gears permissions.
- if (setting != -1 && locationSystemSettingChanged(setting)) {
- setGearsPermissionForGoogleDomains(setting);
- }
- }
-
- private boolean locationSystemSettingChanged(int newSetting) {
- SharedPreferences prefs =
- PreferenceManager.getDefaultSharedPreferences(mContext);
- int oldSetting = 0;
- oldSetting = prefs.getInt(LAST_KNOWN_LOCATION_SETTING, oldSetting);
- if (oldSetting == newSetting) {
- return false;
- }
- Editor ed = prefs.edit();
- ed.putInt(LAST_KNOWN_LOCATION_SETTING, newSetting);
- ed.commit();
- return true;
- }
-
- private void setGearsPermissionForGoogleDomains(int systemPermission) {
- // Transform the system permission into a boolean flag. When this
- // flag is true, it means the origins in gGearsWhiteList are added
- // to the Gears location permission table with permission 1 (allowed).
- // When the flag is false, the origins in gGearsWhiteList are removed
- // from the Gears location permission table. Next time the user
- // navigates to one of these origins, she will see the normal Gears
- // permission prompt.
- boolean addToGearsLocationTable = (systemPermission == 1 ? true : false);
- // Build the path to the Gears library.
-
- File file = new File(mGearsPath).getParentFile();
- if (file == null) {
- return;
- }
- // Build the Gears database file name.
- file = new File(file.getAbsolutePath() + File.separator
- + GEARS_DATABASE_DIR + File.separator + GEARS_DATABASE_FILE);
- // Remember whether or not we need to create the LocationAccess table.
- boolean needToCreateTables = false;
- if (!file.exists()) {
- needToCreateTables = true;
- // Create the path or else SQLiteDatabase.openOrCreateDatabase()
- // may throw on the device.
- file.getParentFile().mkdirs();
- }
- // If the database file does not yet exist and the system location
- // setting says that the Gears origins need to be removed from the
- // location permission table, it means that we don't actually need
- // to do anything at all.
- if (needToCreateTables && !addToGearsLocationTable) {
- return;
- }
- // Try opening the Gears database.
- SQLiteDatabase permissions;
- try {
- permissions = SQLiteDatabase.openOrCreateDatabase(file, null);
- } catch (SQLiteException e) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "Could not open Gears permission DB: "
- + e.getMessage());
- }
- // Just bail out.
- return;
- }
- // We now have a database open. Begin a transaction.
- permissions.beginTransaction();
- try {
- if (needToCreateTables) {
- // Create the tables. Note that this creates the
- // Gears tables for the permissions DB schema version 2.
- // The Gears schema upgrade process will take care of the rest.
- // First, the storage access table.
- SQLiteStatement statement = permissions.compileStatement(
- "CREATE TABLE IF NOT EXISTS "
- + GEARS_STORAGE_ACCESS_TABLE_NAME
- + " (Name TEXT UNIQUE, Value)");
- statement.execute();
- // Next the location access table.
- statement = permissions.compileStatement(
- "CREATE TABLE IF NOT EXISTS "
- + GEARS_LOCATION_ACCESS_TABLE_NAME
- + " (Name TEXT UNIQUE, Value)");
- statement.execute();
- // Finally, the schema version table.
- statement = permissions.compileStatement(
- "CREATE TABLE IF NOT EXISTS "
- + GEARS_SCHEMA_VERSION_TABLE_NAME
- + " (Name TEXT UNIQUE, Value)");
- statement.execute();
- // Set the schema version to 2.
- ContentValues schema = new ContentValues();
- schema.put("Name", "Version");
- schema.put("Value", 2);
- permissions.insert(GEARS_SCHEMA_VERSION_TABLE_NAME, null,
- schema);
- }
-
- if (addToGearsLocationTable) {
- ContentValues permissionValues = new ContentValues();
-
- for (String url : sGearsWhiteList) {
- permissionValues.put("Name", url);
- permissionValues.put("Value", GEARS_ALLOW_LOCATION_ACCESS);
- permissions.replace(GEARS_LOCATION_ACCESS_TABLE_NAME, null,
- permissionValues);
- permissionValues.clear();
- }
- } else {
- for (String url : sGearsWhiteList) {
- permissions.delete(GEARS_LOCATION_ACCESS_TABLE_NAME, "Name=?",
- new String[] { url });
- }
- }
- // Commit the transaction.
- permissions.setTransactionSuccessful();
- } catch (SQLiteException e) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "Could not set the Gears permissions: "
- + e.getMessage());
- }
- } finally {
- permissions.endTransaction();
- permissions.close();
- }
- }
-
- class SecureSettingsObserver extends ContentObserver {
- SecureSettingsObserver() {
- super(new Handler());
- }
-
- void observe() {
- ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(Settings.Secure.getUriFor(
- Settings.Secure.USE_LOCATION_FOR_SERVICES), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- checkGearsPermissions();
- }
- }
-}
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
new file mode 100755
index 0000000..e985ccc
--- /dev/null
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Implements the Java side of GeolocationPermissions. Simply marshalls calls
+ * from the UI thread to the WebKit thread.
+ * @hide
+ */
+public final class GeolocationPermissions {
+ /**
+ * Callback interface used by the browser to report a Geolocation permission
+ * state set by the user in response to a permissions prompt.
+ */
+ public interface Callback {
+ public void invoke(String origin, boolean allow, boolean remember);
+ };
+
+ // Log tag
+ private static final String TAG = "geolocationPermissions";
+
+ // Global instance
+ private static GeolocationPermissions sInstance;
+
+ private Handler mHandler;
+
+ // Members used to transfer the origins and permissions between threads.
+ private Set<String> mOrigins;
+ private boolean mAllowed;
+ private Set<String> mOriginsToClear;
+ private Set<String> mOriginsToAllow;
+ private static Lock mLock = new ReentrantLock();
+ private static boolean mUpdated;
+ private static Condition mUpdatedCondition = mLock.newCondition();
+
+ // Message ids
+ static final int GET_ORIGINS = 0;
+ static final int GET_ALLOWED = 1;
+ static final int CLEAR = 2;
+ static final int ALLOW = 3;
+ static final int CLEAR_ALL = 4;
+
+ /**
+ * Gets the singleton instance of the class.
+ */
+ public static GeolocationPermissions getInstance() {
+ if (sInstance == null) {
+ sInstance = new GeolocationPermissions();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Creates the message handler. Must be called on the WebKit thread.
+ */
+ public void createHandler() {
+ mLock.lock();
+ if (mHandler == null) {
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ // Runs on the WebKit thread.
+ switch (msg.what) {
+ case GET_ORIGINS:
+ getOriginsImpl();
+ break;
+ case GET_ALLOWED:
+ getAllowedImpl((String) msg.obj);
+ break;
+ case CLEAR:
+ nativeClear((String) msg.obj);
+ break;
+ case ALLOW:
+ nativeAllow((String) msg.obj);
+ break;
+ case CLEAR_ALL:
+ nativeClearAll();
+ break;
+ }
+ }
+ };
+
+ if (mOriginsToClear != null) {
+ for (String origin : mOriginsToClear) {
+ nativeClear(origin);
+ }
+ }
+ if (mOriginsToAllow != null) {
+ for (String origin : mOriginsToAllow) {
+ nativeAllow(origin);
+ }
+ }
+ }
+ mLock.unlock();
+ }
+
+ /**
+ * Utility function to send a message to our handler.
+ */
+ private void postMessage(Message msg) {
+ assert(mHandler != null);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Gets the set of origins for which Geolocation permissions are stored.
+ * Note that we represent the origins as strings. These are created using
+ * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
+ * (Database, Geolocation etc) do so, it's safe to match up origins for the
+ * purposes of displaying UI.
+ */
+ public Set getOrigins() {
+ // Called on the UI thread.
+ Set origins = null;
+ mLock.lock();
+ try {
+ mUpdated = false;
+ postMessage(Message.obtain(null, GET_ORIGINS));
+ while (!mUpdated) {
+ mUpdatedCondition.await();
+ }
+ origins = mOrigins;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Exception while waiting for update", e);
+ } finally {
+ mLock.unlock();
+ }
+ return origins;
+ }
+
+ /**
+ * Helper method to get the set of origins.
+ */
+ private void getOriginsImpl() {
+ // Called on the WebKit thread.
+ mLock.lock();
+ mOrigins = nativeGetOrigins();
+ mUpdated = true;
+ mUpdatedCondition.signal();
+ mLock.unlock();
+ }
+
+ /**
+ * Gets the permission state for the specified origin.
+ */
+ public boolean getAllowed(String origin) {
+ // Called on the UI thread.
+ boolean allowed = false;
+ mLock.lock();
+ try {
+ mUpdated = false;
+ postMessage(Message.obtain(null, GET_ALLOWED, origin));
+ while (!mUpdated) {
+ mUpdatedCondition.await();
+ }
+ allowed = mAllowed;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Exception while waiting for update", e);
+ } finally {
+ mLock.unlock();
+ }
+ return allowed;
+ }
+
+ /**
+ * Helper method to get the permission state.
+ */
+ private void getAllowedImpl(String origin) {
+ // Called on the WebKit thread.
+ mLock.lock();
+ mAllowed = nativeGetAllowed(origin);
+ mUpdated = true;
+ mUpdatedCondition.signal();
+ mLock.unlock();
+ }
+
+ /**
+ * Clears the permission state for the specified origin. This method may be
+ * called before the WebKit thread has intialized the message handler.
+ * Messages will be queued until this time.
+ */
+ public void clear(String origin) {
+ // Called on the UI thread.
+ mLock.lock();
+ if (mHandler == null) {
+ if (mOriginsToClear == null) {
+ mOriginsToClear = new HashSet<String>();
+ }
+ mOriginsToClear.add(origin);
+ if (mOriginsToAllow != null) {
+ mOriginsToAllow.remove(origin);
+ }
+ } else {
+ postMessage(Message.obtain(null, CLEAR, origin));
+ }
+ mLock.unlock();
+ }
+
+ /**
+ * Allows the specified origin. This method may be called before the WebKit
+ * thread has intialized the message handler. Messages will be queued until
+ * this time.
+ */
+ public void allow(String origin) {
+ // Called on the UI thread.
+ mLock.lock();
+ if (mHandler == null) {
+ if (mOriginsToAllow == null) {
+ mOriginsToAllow = new HashSet<String>();
+ }
+ mOriginsToAllow.add(origin);
+ if (mOriginsToClear != null) {
+ mOriginsToClear.remove(origin);
+ }
+ } else {
+ postMessage(Message.obtain(null, ALLOW, origin));
+ }
+ mLock.unlock();
+ }
+
+ /**
+ * Clears the permission state for all origins.
+ */
+ public void clearAll() {
+ // Called on the UI thread.
+ postMessage(Message.obtain(null, CLEAR_ALL));
+ }
+
+ // Native functions, run on the WebKit thread.
+ private static native Set nativeGetOrigins();
+ private static native boolean nativeGetAllowed(String origin);
+ private static native void nativeClear(String origin);
+ private static native void nativeAllow(String origin);
+ private static native void nativeClearAll();
+}
diff --git a/core/java/android/webkit/GeolocationService.java b/core/java/android/webkit/GeolocationService.java
new file mode 100755
index 0000000..646f8c5
--- /dev/null
+++ b/core/java/android/webkit/GeolocationService.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.app.ActivityThread;
+import android.content.Context;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.util.Log;
+import android.webkit.WebView;
+import android.webkit.WebViewCore;
+
+
+/**
+ * Implements the Java side of GeolocationServiceAndroid.
+ * @hide Pending API council review.
+ */
+public final class GeolocationService implements LocationListener {
+
+ // Log tag
+ private static final String TAG = "geolocationService";
+
+ private long mNativeObject;
+ private LocationManager mLocationManager;
+ private boolean mIsGpsEnabled;
+ private boolean mIsRunning;
+ private boolean mIsNetworkProviderAvailable;
+ private boolean mIsGpsProviderAvailable;
+
+ /**
+ * Constructor
+ * @param nativeObject The native object to which this object will report position updates and
+ * errors.
+ */
+ public GeolocationService(long nativeObject) {
+ mNativeObject = nativeObject;
+ // Register newLocationAvailable with platform service.
+ ActivityThread thread = ActivityThread.systemMain();
+ Context context = thread.getApplication();
+ mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ if (mLocationManager == null) {
+ Log.e(TAG, "Could not get location manager.");
+ }
+ }
+
+ /**
+ * Start listening for location updates.
+ */
+ public void start() {
+ registerForLocationUpdates();
+ mIsRunning = true;
+ }
+
+ /**
+ * Stop listening for location updates.
+ */
+ public void stop() {
+ unregisterFromLocationUpdates();
+ mIsRunning = false;
+ }
+
+ /**
+ * Sets whether to use the GPS.
+ * @param enable Whether to use the GPS.
+ */
+ public void setEnableGps(boolean enable) {
+ if (mIsGpsEnabled != enable) {
+ mIsGpsEnabled = enable;
+ if (mIsRunning) {
+ // There's no way to unregister from a single provider, so we can
+ // only unregister from all, then reregister with all but the GPS.
+ unregisterFromLocationUpdates();
+ registerForLocationUpdates();
+ }
+ }
+ }
+
+ /**
+ * LocationListener implementation.
+ * Called when the location has changed.
+ * @param location The new location, as a Location object.
+ */
+ public void onLocationChanged(Location location) {
+ // Callbacks from the system location sevice are queued to this thread, so it's possible
+ // that we receive callbacks after unregistering. At this point, the native object will no
+ // longer exist.
+ if (mIsRunning) {
+ nativeNewLocationAvailable(mNativeObject, location);
+ }
+ }
+
+ /**
+ * LocationListener implementation.
+ * Called when the provider status changes.
+ * @param provider The name of the provider.
+ * @param status The new status of the provider.
+ * @param extras an optional Bundle with provider specific data.
+ */
+ public void onStatusChanged(String providerName, int status, Bundle extras) {
+ boolean isAvailable = (status == LocationProvider.AVAILABLE);
+ if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
+ mIsNetworkProviderAvailable = isAvailable;
+ } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
+ mIsGpsProviderAvailable = isAvailable;
+ }
+ maybeReportError("The last location provider is no longer available");
+ }
+
+ /**
+ * LocationListener implementation.
+ * Called when the provider is enabled.
+ * @param provider The name of the location provider that is now enabled.
+ */
+ public void onProviderEnabled(String providerName) {
+ // No need to notify the native side. It's enough to start sending
+ // valid position fixes again.
+ if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
+ mIsNetworkProviderAvailable = true;
+ } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
+ mIsGpsProviderAvailable = true;
+ }
+ }
+
+ /**
+ * LocationListener implementation.
+ * Called when the provider is disabled.
+ * @param provider The name of the location provider that is now disabled.
+ */
+ public void onProviderDisabled(String providerName) {
+ if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
+ mIsNetworkProviderAvailable = false;
+ } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
+ mIsGpsProviderAvailable = false;
+ }
+ maybeReportError("The last location provider was disabled");
+ }
+
+ /**
+ * Registers this object with the location service.
+ */
+ private void registerForLocationUpdates() {
+ try {
+ mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
+ mIsNetworkProviderAvailable = true;
+ if (mIsGpsEnabled) {
+ mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
+ mIsGpsProviderAvailable = true;
+ }
+ } catch(SecurityException e) {
+ Log.e(TAG, "Caught security exception registering for location updates from system. " +
+ "This should only happen in DumpRenderTree.");
+ }
+ }
+
+ /**
+ * Unregisters this object from the location service.
+ */
+ private void unregisterFromLocationUpdates() {
+ mLocationManager.removeUpdates(this);
+ }
+
+ /**
+ * Reports an error if neither the network nor the GPS provider is available.
+ */
+ private void maybeReportError(String message) {
+ // Callbacks from the system location sevice are queued to this thread, so it's possible
+ // that we receive callbacks after unregistering. At this point, the native object will no
+ // longer exist.
+ if (mIsRunning && !mIsNetworkProviderAvailable && !mIsGpsProviderAvailable) {
+ nativeNewErrorAvailable(mNativeObject, message);
+ }
+ }
+
+ // Native functions
+ private static native void nativeNewLocationAvailable(long nativeObject, Location location);
+ private static native void nativeNewErrorAvailable(long nativeObject, String message);
+}
diff --git a/core/java/android/webkit/GoogleLocationSettingManager.java b/core/java/android/webkit/GoogleLocationSettingManager.java
new file mode 100644
index 0000000..fe7fce6
--- /dev/null
+++ b/core/java/android/webkit/GoogleLocationSettingManager.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+
+import java.util.HashSet;
+
+/**
+ * A class to manage the interaction between the system setting 'Location &
+ * Security - Share with Google' and the browser. When this setting is set
+ * to true, we allow Geolocation for Google origins. When this setting is
+ * set to false, we clear Geolocation permissions for Google origins.
+ * @hide pending API council review
+ */
+class GoogleLocationSettingManager {
+ // The application context.
+ private Context mContext;
+ // The observer used to listen to the system setting.
+ private GoogleLocationSettingObserver mSettingObserver;
+
+ // The value of the system setting that indicates true.
+ private final static int sSystemSettingTrue = 1;
+ // The value of the system setting that indicates false.
+ private final static int sSystemSettingFalse = 0;
+ // The value of the USE_LOCATION_FOR_SERVICES system setting last read
+ // by the browser.
+ private final static String LAST_READ_USE_LOCATION_FOR_SERVICES =
+ "lastReadUseLocationForServices";
+ // The Google origins we consider.
+ private static HashSet<String> sGoogleOrigins;
+ static {
+ sGoogleOrigins = new HashSet<String>();
+ // NOTE: DO NOT ADD A "/" AT THE END!
+ sGoogleOrigins.add("http://www.google.com");
+ sGoogleOrigins.add("http://www.google.co.uk");
+ }
+
+ GoogleLocationSettingManager(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Starts the manager. Checks whether the setting has changed and
+ * installs an observer to listen for future changes.
+ */
+ public void start() {
+ maybeApplySetting();
+
+ mSettingObserver = new GoogleLocationSettingObserver();
+ mSettingObserver.observe();
+ }
+
+ /**
+ * Checks to see if the system setting has changed and if so,
+ * updates the Geolocation permissions accordingly.
+ */
+ private void maybeApplySetting() {
+ int setting = getSystemSetting();
+ if (settingChanged(setting)) {
+ applySetting(setting);
+ }
+ }
+
+ /**
+ * Gets the current system setting for 'Use location for Google services'.
+ * @return The system setting.
+ */
+ private int getSystemSetting() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USE_LOCATION_FOR_SERVICES,
+ sSystemSettingFalse);
+ }
+
+ /**
+ * Determines whether the supplied setting has changed from the last
+ * value read by the browser.
+ * @param setting The setting.
+ * @return Whether the setting has changed from the last value read
+ * by the browser.
+ */
+ private boolean settingChanged(int setting) {
+ SharedPreferences preferences =
+ PreferenceManager.getDefaultSharedPreferences(mContext);
+ // Default to false. If the system setting is false the first time it is ever read by the
+ // browser, there's nothing to do.
+ int lastReadSetting = sSystemSettingFalse;
+ lastReadSetting = preferences.getInt(LAST_READ_USE_LOCATION_FOR_SERVICES,
+ lastReadSetting);
+
+ if (lastReadSetting == setting) {
+ return false;
+ }
+
+ Editor editor = preferences.edit();
+ editor.putInt(LAST_READ_USE_LOCATION_FOR_SERVICES, setting);
+ editor.commit();
+ return true;
+ }
+
+ /**
+ * Applies the supplied setting to the Geolocation permissions.
+ * @param setting The setting.
+ */
+ private void applySetting(int setting) {
+ for (String origin : sGoogleOrigins) {
+ if (setting == sSystemSettingTrue) {
+ GeolocationPermissions.getInstance().allow(origin);
+ } else {
+ GeolocationPermissions.getInstance().clear(origin);
+ }
+ }
+ }
+
+ /**
+ * This class implements an observer to listen for changes to the
+ * system setting.
+ */
+ class GoogleLocationSettingObserver extends ContentObserver {
+ GoogleLocationSettingObserver() {
+ super(new Handler());
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.USE_LOCATION_FOR_SERVICES), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ maybeApplySetting();
+ }
+ }
+}
diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java
new file mode 100644
index 0000000..5a164f8
--- /dev/null
+++ b/core/java/android/webkit/HTML5VideoViewProxy.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.ViewManager.ChildView;
+import android.widget.AbsoluteLayout;
+import android.widget.MediaController;
+import android.widget.VideoView;
+
+import java.util.HashMap;
+
+/**
+ * <p>Proxy for HTML5 video views.
+ */
+class HTML5VideoViewProxy extends Handler {
+ // Logging tag.
+ private static final String LOGTAG = "HTML5VideoViewProxy";
+
+ // Message Ids for WebCore thread -> UI thread communication.
+ private static final int INIT = 100;
+ private static final int PLAY = 101;
+
+ // The WebView instance that created this view.
+ private WebView mWebView;
+ // The ChildView instance used by the ViewManager.
+ private ChildView mChildView;
+ // The VideoView instance. Note that we could
+ // also access this via mChildView.mView but it would
+ // always require cast, so it is more convenient to store
+ // it here as well.
+ private HTML5VideoView mVideoView;
+
+ // A VideoView subclass that responds to double-tap
+ // events by going fullscreen.
+ class HTML5VideoView extends VideoView {
+ // Used to save the layout parameters if the view
+ // is changed to fullscreen.
+ private AbsoluteLayout.LayoutParams mEmbeddedLayoutParams;
+ // Flag that denotes whether the view is fullscreen or not.
+ private boolean mIsFullscreen;
+ // Used to save the current playback position when
+ // transitioning to/from fullscreen.
+ private int mPlaybackPosition;
+ // The callback object passed to the host application. This callback
+ // is invoked when the host application dismisses our VideoView
+ // (e.g. the user presses the back key).
+ private WebChromeClient.CustomViewCallback mCallback =
+ new WebChromeClient.CustomViewCallback() {
+ public void onCustomViewHidden() {
+ playEmbedded();
+ }
+ };
+
+ // The OnPreparedListener, used to automatically resume
+ // playback when transitioning to/from fullscreen.
+ private MediaPlayer.OnPreparedListener mPreparedListener =
+ new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer mp) {
+ resumePlayback();
+ }
+ };
+
+ HTML5VideoView(Context context) {
+ super(context);
+ }
+
+ void savePlaybackPosition() {
+ if (isPlaying()) {
+ mPlaybackPosition = getCurrentPosition();
+ }
+ }
+
+ void resumePlayback() {
+ seekTo(mPlaybackPosition);
+ start();
+ setOnPreparedListener(null);
+ }
+
+ void playEmbedded() {
+ // Attach to the WebView.
+ mChildView.attachViewOnUIThread(mEmbeddedLayoutParams);
+ // Make sure we're visible
+ setVisibility(View.VISIBLE);
+ // Set the onPrepared listener so we start
+ // playing when the video view is reattached
+ // and its surface is recreated.
+ setOnPreparedListener(mPreparedListener);
+ mIsFullscreen = false;
+ }
+
+ void playFullScreen() {
+ WebChromeClient client = mWebView.getWebChromeClient();
+ if (client == null) {
+ return;
+ }
+ // Save the current layout params.
+ mEmbeddedLayoutParams =
+ (AbsoluteLayout.LayoutParams) getLayoutParams();
+ // Detach from the WebView.
+ mChildView.removeViewOnUIThread();
+ // Attach to the browser UI.
+ client.onShowCustomView(this, mCallback);
+ // Set the onPrepared listener so we start
+ // playing when after the video view is reattached
+ // and its surface is recreated.
+ setOnPreparedListener(mPreparedListener);
+ mIsFullscreen = true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // TODO: implement properly (i.e. detect double tap)
+ if (mIsFullscreen || !isPlaying()) {
+ return super.onTouchEvent(ev);
+ }
+ playFullScreen();
+ return true;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ savePlaybackPosition();
+ }
+ }
+
+ /**
+ * Private constructor.
+ * @param context is the application context.
+ */
+ private HTML5VideoViewProxy(WebView webView) {
+ // This handler is for the main (UI) thread.
+ super(Looper.getMainLooper());
+ // Save the WebView object.
+ mWebView = webView;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ // This executes on the UI thread.
+ switch (msg.what) {
+ case INIT:
+ // Create the video view and set a default controller.
+ mVideoView = new HTML5VideoView(mWebView.getContext());
+ // This is needed because otherwise there will be a black square
+ // stuck on the screen.
+ mVideoView.setWillNotDraw(false);
+ mVideoView.setMediaController(new MediaController(mWebView.getContext()));
+ mChildView.mView = mVideoView;
+ break;
+ case PLAY:
+ if (mVideoView == null) {
+ return;
+ }
+ HashMap<String, Object> map =
+ (HashMap<String, Object>) msg.obj;
+ String url = (String) map.get("url");
+ mVideoView.setVideoURI(Uri.parse(url));
+ mVideoView.start();
+ break;
+ }
+ }
+
+ /**
+ * Play a video stream.
+ * @param url is the URL of the video stream.
+ * @param webview is the WebViewCore that is requesting the playback.
+ */
+ public void play(String url) {
+ // We need to know the webview that is requesting the playback.
+ Message message = obtainMessage(PLAY);
+ HashMap<String, Object> map = new HashMap();
+ map.put("url", url);
+ message.obj = map;
+ sendMessage(message);
+ }
+
+ public void createView() {
+ mChildView = mWebView.mViewManager.createView();
+ sendMessage(obtainMessage(INIT));
+ }
+
+ public void attachView(int x, int y, int width, int height) {
+ if (mChildView == null) {
+ return;
+ }
+ mChildView.attachView(x, y, width, height);
+ }
+
+ public void removeView() {
+ if (mChildView == null) {
+ return;
+ }
+ mChildView.removeView();
+ }
+
+ /**
+ * The factory for HTML5VideoViewProxy instances.
+ * @param webViewCore is the WebViewCore that is requesting the proxy.
+ *
+ * @return a new HTML5VideoViewProxy object.
+ */
+ public static HTML5VideoViewProxy getInstance(WebViewCore webViewCore) {
+ return new HTML5VideoViewProxy(webViewCore.getWebView());
+ }
+}
diff --git a/core/java/android/webkit/HttpAuthHandler.java b/core/java/android/webkit/HttpAuthHandler.java
index 84dc9f0..1c17575 100644
--- a/core/java/android/webkit/HttpAuthHandler.java
+++ b/core/java/android/webkit/HttpAuthHandler.java
@@ -49,8 +49,8 @@ public class HttpAuthHandler extends Handler {
// Message id for handling the user response
- private final int AUTH_PROCEED = 100;
- private final int AUTH_CANCEL = 200;
+ private static final int AUTH_PROCEED = 100;
+ private static final int AUTH_CANCEL = 200;
/**
* Creates a new HTTP authentication handler with an empty
diff --git a/core/java/android/webkit/HttpDateTime.java b/core/java/android/webkit/HttpDateTime.java
index c6ec2d2..00b2731 100644
--- a/core/java/android/webkit/HttpDateTime.java
+++ b/core/java/android/webkit/HttpDateTime.java
@@ -23,7 +23,8 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
-class HttpDateTime {
+/** {@hide} */
+public final class HttpDateTime {
/*
* Regular expression for parsing HTTP-date.
@@ -47,14 +48,16 @@ class HttpDateTime {
* Wdy, DD Mon YYYY HH:MM:SS
* Wdy Mon (SP)D HH:MM:SS YYYY
* Wdy Mon DD HH:MM:SS YYYY GMT
+ *
+ * HH can be H if the first digit is zero.
*/
private static final String HTTP_DATE_RFC_REGEXP =
"([0-9]{1,2})[- ]([A-Za-z]{3,3})[- ]([0-9]{2,4})[ ]"
- + "([0-9][0-9]:[0-9][0-9]:[0-9][0-9])";
+ + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
private static final String HTTP_DATE_ANSIC_REGEXP =
"[ ]([A-Za-z]{3,3})[ ]+([0-9]{1,2})[ ]"
- + "([0-9][0-9]:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
+ + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
/**
* The compiled version of the HTTP-date regular expressions.
@@ -65,6 +68,12 @@ class HttpDateTime {
Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
private static class TimeOfDay {
+ TimeOfDay(int h, int m, int s) {
+ this.hour = h;
+ this.minute = m;
+ this.second = s;
+ }
+
int hour;
int minute;
int second;
@@ -76,7 +85,7 @@ class HttpDateTime {
int date = 1;
int month = Calendar.JANUARY;
int year = 1970;
- TimeOfDay timeOfDay = new TimeOfDay();
+ TimeOfDay timeOfDay;
Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
if (rfcMatcher.find()) {
@@ -183,13 +192,22 @@ class HttpDateTime {
}
private static TimeOfDay getTime(String timeString) {
- TimeOfDay time = new TimeOfDay();
- time.hour = (timeString.charAt(0) - '0') * 10
- + (timeString.charAt(1) - '0');
- time.minute = (timeString.charAt(3) - '0') * 10
- + (timeString.charAt(4) - '0');
- time.second = (timeString.charAt(6) - '0') * 10
- + (timeString.charAt(7) - '0');
- return time;
+ // HH might be H
+ int i = 0;
+ int hour = timeString.charAt(i++) - '0';
+ if (timeString.charAt(i) != ':')
+ hour = hour * 10 + (timeString.charAt(i++) - '0');
+ // Skip ':'
+ i++;
+
+ int minute = (timeString.charAt(i++) - '0') * 10
+ + (timeString.charAt(i++) - '0');
+ // Skip ':'
+ i++;
+
+ int second = (timeString.charAt(i++) - '0') * 10
+ + (timeString.charAt(i++) - '0');
+
+ return new TimeOfDay(hour, minute, second);
}
}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 1dbd007..ddc2da1 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -34,9 +34,16 @@ final class JWebCoreJavaBridge extends Handler {
// Instant timer is used to implement a timer that needs to fire almost
// immediately.
private boolean mHasInstantTimer;
+
// Reference count the pause/resume of timers
private int mPauseTimerRefCount;
+ private boolean mTimerPaused;
+ private boolean mHasDeferredTimers;
+
+ /* package */
+ static final int REFRESH_PLUGINS = 100;
+
/**
* Construct a new JWebCoreJavaBridge to interface with
* WebCore timers and cookies.
@@ -51,6 +58,17 @@ final class JWebCoreJavaBridge extends Handler {
}
/**
+ * Call native timer callbacks.
+ */
+ private void fireSharedTimer() {
+ PerfChecker checker = new PerfChecker();
+ // clear the flag so that sharedTimerFired() can set a new timer
+ mHasInstantTimer = false;
+ sharedTimerFired();
+ checker.responseAlert("sharedTimer");
+ }
+
+ /**
* handleMessage
* @param msg The dispatched message.
*
@@ -60,16 +78,21 @@ final class JWebCoreJavaBridge extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case TIMER_MESSAGE: {
- PerfChecker checker = new PerfChecker();
- // clear the flag so that sharedTimerFired() can set a new timer
- mHasInstantTimer = false;
- sharedTimerFired();
- checker.responseAlert("sharedTimer");
+ if (mTimerPaused) {
+ mHasDeferredTimers = true;
+ } else {
+ fireSharedTimer();
+ }
break;
}
case FUNCPTR_MESSAGE:
nativeServiceFuncPtrQueue();
break;
+ case REFRESH_PLUGINS:
+ nativeUpdatePluginDirectories(PluginManager.getInstance(null)
+ .getPluginDirectories(), ((Boolean) msg.obj)
+ .booleanValue());
+ break;
}
}
@@ -86,7 +109,8 @@ final class JWebCoreJavaBridge extends Handler {
*/
public void pause() {
if (--mPauseTimerRefCount == 0) {
- setDeferringTimers(true);
+ mTimerPaused = true;
+ mHasDeferredTimers = false;
}
}
@@ -95,7 +119,11 @@ final class JWebCoreJavaBridge extends Handler {
*/
public void resume() {
if (++mPauseTimerRefCount == 1) {
- setDeferringTimers(false);
+ mTimerPaused = false;
+ if (mHasDeferredTimers) {
+ mHasDeferredTimers = false;
+ fireSharedTimer();
+ }
}
}
@@ -108,10 +136,9 @@ final class JWebCoreJavaBridge extends Handler {
/**
* Store a cookie string associated with a url.
* @param url The url to be used as a key for the cookie.
- * @param docUrl The policy base url used by WebCore.
* @param value The cookie string to be stored.
*/
- private void setCookies(String url, String docUrl, String value) {
+ private void setCookies(String url, String value) {
if (value.contains("\r") || value.contains("\n")) {
// for security reason, filter out '\r' and '\n' from the cookie
int size = value.length();
@@ -152,11 +179,25 @@ final class JWebCoreJavaBridge extends Handler {
}
/**
+ * Returns an array of plugin directoies
+ */
+ private String[] getPluginDirectories() {
+ return PluginManager.getInstance(null).getPluginDirectories();
+ }
+
+ /**
+ * Returns the path of the plugin data directory
+ */
+ private String getPluginSharedDataDirectory() {
+ return PluginManager.getInstance(null).getPluginSharedDataDirectory();
+ }
+
+ /**
* setSharedTimer
* @param timemillis The relative time when the timer should fire
*/
private void setSharedTimer(long timemillis) {
- if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "setSharedTimer " + timemillis);
+ if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
if (timemillis <= 0) {
// we don't accumulate the sharedTimer unless it is a delayed
@@ -180,11 +221,12 @@ final class JWebCoreJavaBridge extends Handler {
* Stop the shared timer.
*/
private void stopSharedTimer() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
Log.v(LOGTAG, "stopSharedTimer removing all timers");
}
removeMessages(TIMER_MESSAGE);
mHasInstantTimer = false;
+ mHasDeferredTimers = false;
}
private String[] getKeyStrengthList() {
@@ -199,6 +241,7 @@ final class JWebCoreJavaBridge extends Handler {
private native void nativeConstructor();
private native void nativeFinalize();
private native void sharedTimerFired();
- private native void setDeferringTimers(boolean defer);
+ private native void nativeUpdatePluginDirectories(String[] directories,
+ boolean reload);
public native void setNetworkOnLine(boolean online);
}
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index c3f3594..aee8a6d 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -43,8 +43,6 @@ import java.util.Vector;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
-import org.apache.commons.codec.binary.Base64;
-
class LoadListener extends Handler implements EventHandler {
private static final String LOGTAG = "webkit";
@@ -101,6 +99,7 @@ class LoadListener extends Handler implements EventHandler {
private boolean mAuthFailed; // indicates that the prev. auth failed
private CacheLoader mCacheLoader;
private CacheManager.CacheResult mCacheResult;
+ private boolean mFromCache = false;
private HttpAuthHeader mAuthHeader;
private int mErrorID = OK;
private String mErrorDescription;
@@ -113,7 +112,6 @@ class LoadListener extends Handler implements EventHandler {
private String mMethod;
private Map<String, String> mRequestHeaders;
private byte[] mPostData;
- private boolean mIsHighPriority;
// Flag to indicate that this load is synchronous.
private boolean mSynchronous;
private Vector<Message> mMessageQueue;
@@ -142,15 +140,13 @@ class LoadListener extends Handler implements EventHandler {
LoadListener(Context context, BrowserFrame frame, String url,
int nativeLoader, boolean synchronous, boolean isMainPageLoader) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener constructor url=" + url);
}
mContext = context;
mBrowserFrame = frame;
setUrl(url);
mNativeLoader = nativeLoader;
- mMimeType = "";
- mEncoding = "";
mSynchronous = synchronous;
if (synchronous) {
mMessageQueue = new Vector<Message>();
@@ -293,7 +289,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void headers(Headers headers) {
- if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "LoadListener.headers");
+ if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
}
@@ -301,8 +297,6 @@ class LoadListener extends Handler implements EventHandler {
private void handleHeaders(Headers headers) {
if (mCancelled) return;
mHeaders = headers;
- mMimeType = "";
- mEncoding = "";
ArrayList<String> cookies = headers.getSetCookie();
for (int i = 0; i < cookies.size(); ++i) {
@@ -322,8 +316,8 @@ class LoadListener extends Handler implements EventHandler {
// If we have one of "generic" MIME types, try to deduce
// the right MIME type from the file extension (if any):
- if (mMimeType.equalsIgnoreCase("text/plain") ||
- mMimeType.equalsIgnoreCase("application/octet-stream")) {
+ if (mMimeType.equals("text/plain") ||
+ mMimeType.equals("application/octet-stream")) {
// for attachment, use the filename in the Content-Disposition
// to guess the mimetype
@@ -339,17 +333,14 @@ class LoadListener extends Handler implements EventHandler {
if (newMimeType != null) {
mMimeType = newMimeType;
}
- } else if (mMimeType.equalsIgnoreCase("text/vnd.wap.wml")) {
+ } else if (mMimeType.equals("text/vnd.wap.wml")) {
// As we don't support wml, render it as plain text
mMimeType = "text/plain";
} else {
- // XXX: Until the servers send us either correct xhtml or
- // text/html, treat application/xhtml+xml as text/html.
// It seems that xhtml+xml and vnd.wap.xhtml+xml mime
// subtypes are used interchangeably. So treat them the same.
- if (mMimeType.equalsIgnoreCase("application/xhtml+xml") ||
- mMimeType.equals("application/vnd.wap.xhtml+xml")) {
- mMimeType = "text/html";
+ if (mMimeType.equals("application/vnd.wap.xhtml+xml")) {
+ mMimeType = "application/xhtml+xml";
}
}
} else {
@@ -419,11 +410,11 @@ class LoadListener extends Handler implements EventHandler {
mStatusCode == HTTP_MOVED_PERMANENTLY ||
mStatusCode == HTTP_TEMPORARY_REDIRECT) &&
mNativeLoader != 0) {
- // Content arriving from a StreamLoader (eg File, Cache or Data)
- // will not be cached as they have the header:
- // cache-control: no-store
- mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode,
- headers, mMimeType, false);
+ if (!mFromCache && mRequestHandle != null
+ && !mRequestHandle.getMethod().equals("POST")) {
+ mCacheResult = CacheManager.createCacheFile(mUrl, mStatusCode,
+ headers, mMimeType, false);
+ }
if (mCacheResult != null) {
mCacheResult.encoding = mEncoding;
}
@@ -450,7 +441,7 @@ class LoadListener extends Handler implements EventHandler {
*/
public void status(int majorVersion, int minorVersion,
int code, /* Status-Code value */ String reasonPhrase) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener: from: " + mUrl
+ " major: " + majorVersion
+ " minor: " + minorVersion
@@ -464,6 +455,9 @@ class LoadListener extends Handler implements EventHandler {
status.put("reason", reasonPhrase);
// New status means new data. Clear the old.
mDataBuilder.clear();
+ mMimeType = "";
+ mEncoding = "";
+ mTransferEncoding = "";
sendMessageInternal(obtainMessage(MSG_STATUS, status));
}
@@ -507,7 +501,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void error(int id, String description) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.error url:" +
url() + " id:" + id + " description:" + description);
}
@@ -535,23 +529,10 @@ class LoadListener extends Handler implements EventHandler {
* mDataBuilder is a thread-safe structure.
*/
public void data(byte[] data, int length) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.data(): url: " + url());
}
- // Decode base64 data
- // Note: It's fine that we only decode base64 here and not in the other
- // data call because the only caller of the stream version is not
- // base64 encoded.
- if ("base64".equalsIgnoreCase(mTransferEncoding)) {
- if (length < data.length) {
- byte[] trimmedData = new byte[length];
- System.arraycopy(data, 0, trimmedData, 0, length);
- data = trimmedData;
- }
- data = Base64.decodeBase64(data);
- length = data.length;
- }
// Synchronize on mData because commitLoad may write mData to WebCore
// and we don't want to replace mData or mDataLength at the same time
// as a write.
@@ -573,7 +554,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void endData() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.endData(): url: " + url());
}
sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED));
@@ -626,7 +607,8 @@ class LoadListener extends Handler implements EventHandler {
// before calling it.
if (mCacheLoader != null) {
mCacheLoader.load();
- if (WebView.LOGV_ENABLED) {
+ mFromCache = true;
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener cache load url=" + url());
}
return;
@@ -646,6 +628,7 @@ class LoadListener extends Handler implements EventHandler {
* serviced by the Cache. */
/* package */ void setCacheLoader(CacheLoader c) {
mCacheLoader = c;
+ mFromCache = true;
}
/**
@@ -662,6 +645,8 @@ class LoadListener extends Handler implements EventHandler {
// Go ahead and set the cache loader to null in case the result is
// null.
mCacheLoader = null;
+ // reset the flag
+ mFromCache = false;
if (result != null) {
// The contents of the cache may need to be revalidated so just
@@ -676,12 +661,13 @@ class LoadListener extends Handler implements EventHandler {
CacheManager.HEADER_KEY_IFNONEMATCH) &&
!headers.containsKey(
CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
"and usable: " + url());
}
// Load the cached file
mCacheLoader.load();
+ mFromCache = true;
return true;
}
}
@@ -695,12 +681,23 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public boolean handleSslErrorRequest(SslError error) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG,
"LoadListener.handleSslErrorRequest(): url:" + url() +
" primary error: " + error.getPrimaryError() +
" certificate: " + error.getCertificate());
}
+ // Check the cached preference table before sending a message. This
+ // will prevent waiting for an already available answer.
+ if (Network.getInstance(mContext).checkSslPrefTable(this, error)) {
+ return true;
+ }
+ // Do not post a message for a synchronous request. This will cause a
+ // deadlock. Just bail on the request.
+ if (isSynchronous()) {
+ mRequestHandle.handleSslErrorResponse(false);
+ return true;
+ }
sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error));
// if it has been canceled, return false so that the network thread
// won't be blocked. If it is not canceled, save the mRequestHandle
@@ -773,7 +770,7 @@ class LoadListener extends Handler implements EventHandler {
* are null, cancel the request.
*/
void handleAuthResponse(String username, String password) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl
+ " username: " + username
+ " password: " + password);
@@ -823,14 +820,12 @@ class LoadListener extends Handler implements EventHandler {
* @param method
* @param headers
* @param postData
- * @param isHighPriority
*/
void setRequestData(String method, Map<String, String> headers,
- byte[] postData, boolean isHighPriority) {
+ byte[] postData) {
mMethod = method;
mRequestHeaders = headers;
mPostData = postData;
- mIsHighPriority = isHighPriority;
}
/**
@@ -870,7 +865,7 @@ class LoadListener extends Handler implements EventHandler {
}
void attachRequestHandle(RequestHandle requestHandle) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " +
"requestHandle: " + requestHandle);
}
@@ -878,7 +873,7 @@ class LoadListener extends Handler implements EventHandler {
}
void detachRequestHandle() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " +
"requestHandle: " + mRequestHandle);
}
@@ -917,7 +912,7 @@ class LoadListener extends Handler implements EventHandler {
*/
static boolean willLoadFromCache(String url) {
boolean inCache = CacheManager.getCacheFile(url, null) != null;
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " +
inCache);
}
@@ -938,6 +933,10 @@ class LoadListener extends Handler implements EventHandler {
return mMimeType;
}
+ String transferEncoding() {
+ return mTransferEncoding;
+ }
+
/*
* Return the size of the content being downloaded. This represents the
* full content size, even under the situation where the download has been
@@ -992,8 +991,7 @@ class LoadListener extends Handler implements EventHandler {
// pass content-type content-length and content-encoding
final int nativeResponse = nativeCreateResponse(
mUrl, statusCode, mStatusText,
- mMimeType, mContentLength, mEncoding,
- mCacheResult == null ? 0 : mCacheResult.expires / 1000);
+ mMimeType, mContentLength, mEncoding);
if (mHeaders != null) {
mHeaders.getHeaders(new Headers.HeaderCallback() {
public void header(String name, String value) {
@@ -1115,7 +1113,7 @@ class LoadListener extends Handler implements EventHandler {
* EventHandler's method call.
*/
public void cancel() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
if (mRequestHandle == null) {
Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle");
} else {
@@ -1221,7 +1219,7 @@ class LoadListener extends Handler implements EventHandler {
// Network.requestURL.
Network network = Network.getInstance(getContext());
if (!network.requestURL(mMethod, mRequestHeaders,
- mPostData, this, mIsHighPriority)) {
+ mPostData, this)) {
// Signal a bad url error if we could not load the
// redirection.
handleError(EventHandler.ERROR_BAD_URL,
@@ -1247,7 +1245,7 @@ class LoadListener extends Handler implements EventHandler {
tearDown();
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " +
redirectTo);
}
@@ -1260,8 +1258,8 @@ class LoadListener extends Handler implements EventHandler {
private static final Pattern CONTENT_TYPE_PATTERN =
Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$");
- private void parseContentTypeHeader(String contentType) {
- if (WebView.LOGV_ENABLED) {
+ /* package */ void parseContentTypeHeader(String contentType) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " +
"contentType: " + contentType);
}
@@ -1282,13 +1280,14 @@ class LoadListener extends Handler implements EventHandler {
mEncoding = contentType.substring(i + 1);
}
// Trim excess whitespace.
- mEncoding = mEncoding.trim();
+ mEncoding = mEncoding.trim().toLowerCase();
if (i < contentType.length() - 1) {
// for data: uri the mimeType and encoding have
// the form image/jpeg;base64 or text/plain;charset=utf-8
// or text/html;charset=utf-8;base64
- mTransferEncoding = contentType.substring(i + 1).trim();
+ mTransferEncoding =
+ contentType.substring(i + 1).trim().toLowerCase();
}
} else {
mMimeType = contentType;
@@ -1308,6 +1307,8 @@ class LoadListener extends Handler implements EventHandler {
guessMimeType();
}
}
+ // Ensure mMimeType is lower case.
+ mMimeType = mMimeType.toLowerCase();
}
/**
@@ -1397,7 +1398,8 @@ class LoadListener extends Handler implements EventHandler {
*/
private boolean ignoreCallbacks() {
return (mCancelled || mAuthHeader != null ||
- (mStatusCode > 300 && mStatusCode < 400));
+ // Allow 305 (Use Proxy) to call through.
+ (mStatusCode > 300 && mStatusCode < 400 && mStatusCode != 305));
}
/**
@@ -1438,7 +1440,7 @@ class LoadListener extends Handler implements EventHandler {
mMimeType = "text/html";
String newMimeType = guessMimeTypeFromExtension(mUrl);
if (newMimeType != null) {
- mMimeType = newMimeType;
+ mMimeType = newMimeType;
}
}
}
@@ -1448,23 +1450,12 @@ class LoadListener extends Handler implements EventHandler {
*/
private String guessMimeTypeFromExtension(String url) {
// PENDING: need to normalize url
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "guessMimeTypeFromExtension: url = " + url);
}
- String mimeType =
- MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- MimeTypeMap.getFileExtensionFromUrl(url));
-
- if (mimeType != null) {
- // XXX: Until the servers send us either correct xhtml or
- // text/html, treat application/xhtml+xml as text/html.
- if (mimeType.equals("application/xhtml+xml")) {
- mimeType = "text/html";
- }
- }
-
- return mimeType;
+ return MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ MimeTypeMap.getFileExtensionFromUrl(url));
}
/**
@@ -1483,7 +1474,7 @@ class LoadListener extends Handler implements EventHandler {
* Cycle through our messages for synchronous loads.
*/
/* package */ void loadSynchronousMessages() {
- if (WebView.DEBUG && !mSynchronous) {
+ if (DebugFlags.LOAD_LISTENER && !mSynchronous) {
throw new AssertionError();
}
// Note: this can be called twice if it is a synchronous network load,
@@ -1510,12 +1501,11 @@ class LoadListener extends Handler implements EventHandler {
* @param expectedLength An estimate of the content length or the length
* given by the server.
* @param encoding HTTP encoding.
- * @param expireTime HTTP expires converted to seconds since the epoch.
* @return The native response pointer.
*/
private native int nativeCreateResponse(String url, int statusCode,
String statusText, String mimeType, long expectedLength,
- String encoding, long expireTime);
+ String encoding);
/**
* Add a response header to the native object.
diff --git a/core/java/android/webkit/MimeTypeMap.java b/core/java/android/webkit/MimeTypeMap.java
index 9fdde61..a55dbc8 100644
--- a/core/java/android/webkit/MimeTypeMap.java
+++ b/core/java/android/webkit/MimeTypeMap.java
@@ -22,7 +22,7 @@ import java.util.regex.Pattern;
/**
* Two-way map that maps MIME-types to file extensions and vice versa.
*/
-public /* package */ class MimeTypeMap {
+public class MimeTypeMap {
/**
* Singleton MIME-type map instance:
@@ -39,7 +39,6 @@ public /* package */ class MimeTypeMap {
*/
private HashMap<String, String> mExtensionToMimeTypeMap;
-
/**
* Creates a new MIME-type map.
*/
@@ -50,7 +49,10 @@ public /* package */ class MimeTypeMap {
/**
* Returns the file extension or an empty string iff there is no
- * extension.
+ * extension. This method is a convenience method for obtaining the
+ * extension of a url and has undefined results for other Strings.
+ * @param url
+ * @return The file extension of the given url.
*/
public static String getFileExtensionFromUrl(String url) {
if (url != null && url.length() > 0) {
@@ -80,8 +82,7 @@ public /* package */ class MimeTypeMap {
* Load an entry into the map. This does not check if the item already
* exists, it trusts the caller!
*/
- private void loadEntry(String mimeType, String extension,
- boolean textType) {
+ private void loadEntry(String mimeType, String extension) {
//
// if we have an existing x --> y mapping, we do not want to
// override it with another mapping x --> ?
@@ -94,18 +95,12 @@ public /* package */ class MimeTypeMap {
mMimeTypeToExtensionMap.put(mimeType, extension);
}
- //
- // here, we don't want to map extensions to text MIME types;
- // otherwise, we will start replacing generic text/plain and
- // text/html with text MIME types that our platform does not
- // understand.
- //
- if (!textType) {
- mExtensionToMimeTypeMap.put(extension, mimeType);
- }
+ mExtensionToMimeTypeMap.put(extension, mimeType);
}
/**
+ * Return true if the given MIME type has an entry in the map.
+ * @param mimeType A MIME type (i.e. text/plain)
* @return True iff there is a mimeType entry in the map.
*/
public boolean hasMimeType(String mimeType) {
@@ -117,7 +112,9 @@ public /* package */ class MimeTypeMap {
}
/**
- * @return The extension for the MIME type or null iff there is none.
+ * Return the MIME type for the given extension.
+ * @param extension A file extension without the leading '.'
+ * @return The MIME type for the given extension or null iff there is none.
*/
public String getMimeTypeFromExtension(String extension) {
if (extension != null && extension.length() > 0) {
@@ -128,18 +125,23 @@ public /* package */ class MimeTypeMap {
}
/**
+ * Return true if the given extension has a registered MIME type.
+ * @param extension A file extension without the leading '.'
* @return True iff there is an extension entry in the map.
*/
public boolean hasExtension(String extension) {
if (extension != null && extension.length() > 0) {
return mExtensionToMimeTypeMap.containsKey(extension);
}
-
return false;
}
/**
- * @return The MIME type for the extension or null iff there is none.
+ * Return the registered extension for the given MIME type. Note that some
+ * MIME types map to multiple extensions. This call will return the most
+ * common extension for the given MIME type.
+ * @param mimeType A MIME type (i.e. text/plain)
+ * @return The extension for the given MIME type or null iff there is none.
*/
public String getExtensionFromMimeType(String mimeType) {
if (mimeType != null && mimeType.length() > 0) {
@@ -150,6 +152,7 @@ public /* package */ class MimeTypeMap {
}
/**
+ * Get the singleton instance of MimeTypeMap.
* @return The singleton instance of the MIME-type map.
*/
public static MimeTypeMap getSingleton() {
@@ -164,341 +167,312 @@ public /* package */ class MimeTypeMap {
// mail.google.com/a/google.com
//
// and "active" MIME types (due to potential security issues).
- //
- // Also, notice that not all data from this table is actually
- // added (see loadEntry method for more details).
- sMimeTypeMap.loadEntry("application/andrew-inset", "ez", false);
- sMimeTypeMap.loadEntry("application/dsptype", "tsp", false);
- sMimeTypeMap.loadEntry("application/futuresplash", "spl", false);
- sMimeTypeMap.loadEntry("application/hta", "hta", false);
- sMimeTypeMap.loadEntry("application/mac-binhex40", "hqx", false);
- sMimeTypeMap.loadEntry("application/mac-compactpro", "cpt", false);
- sMimeTypeMap.loadEntry("application/mathematica", "nb", false);
- sMimeTypeMap.loadEntry("application/msaccess", "mdb", false);
- sMimeTypeMap.loadEntry("application/oda", "oda", false);
- sMimeTypeMap.loadEntry("application/ogg", "ogg", false);
- sMimeTypeMap.loadEntry("application/pdf", "pdf", false);
- sMimeTypeMap.loadEntry("application/pgp-keys", "key", false);
- sMimeTypeMap.loadEntry("application/pgp-signature", "pgp", false);
- sMimeTypeMap.loadEntry("application/pics-rules", "prf", false);
- sMimeTypeMap.loadEntry("application/rar", "rar", false);
- sMimeTypeMap.loadEntry("application/rdf+xml", "rdf", false);
- sMimeTypeMap.loadEntry("application/rss+xml", "rss", false);
- sMimeTypeMap.loadEntry("application/zip", "zip", false);
+ sMimeTypeMap.loadEntry("application/andrew-inset", "ez");
+ sMimeTypeMap.loadEntry("application/dsptype", "tsp");
+ sMimeTypeMap.loadEntry("application/futuresplash", "spl");
+ sMimeTypeMap.loadEntry("application/hta", "hta");
+ sMimeTypeMap.loadEntry("application/mac-binhex40", "hqx");
+ sMimeTypeMap.loadEntry("application/mac-compactpro", "cpt");
+ sMimeTypeMap.loadEntry("application/mathematica", "nb");
+ sMimeTypeMap.loadEntry("application/msaccess", "mdb");
+ sMimeTypeMap.loadEntry("application/oda", "oda");
+ sMimeTypeMap.loadEntry("application/ogg", "ogg");
+ sMimeTypeMap.loadEntry("application/pdf", "pdf");
+ sMimeTypeMap.loadEntry("application/pgp-keys", "key");
+ sMimeTypeMap.loadEntry("application/pgp-signature", "pgp");
+ sMimeTypeMap.loadEntry("application/pics-rules", "prf");
+ sMimeTypeMap.loadEntry("application/rar", "rar");
+ sMimeTypeMap.loadEntry("application/rdf+xml", "rdf");
+ sMimeTypeMap.loadEntry("application/rss+xml", "rss");
+ sMimeTypeMap.loadEntry("application/zip", "zip");
sMimeTypeMap.loadEntry("application/vnd.android.package-archive",
- "apk", false);
- sMimeTypeMap.loadEntry("application/vnd.cinderella", "cdy", false);
- sMimeTypeMap.loadEntry("application/vnd.ms-pki.stl", "stl", false);
+ "apk");
+ sMimeTypeMap.loadEntry("application/vnd.cinderella", "cdy");
+ sMimeTypeMap.loadEntry("application/vnd.ms-pki.stl", "stl");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.database", "odb",
- false);
+ "application/vnd.oasis.opendocument.database", "odb");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.formula", "odf",
- false);
+ "application/vnd.oasis.opendocument.formula", "odf");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.graphics", "odg",
- false);
+ "application/vnd.oasis.opendocument.graphics", "odg");
sMimeTypeMap.loadEntry(
"application/vnd.oasis.opendocument.graphics-template",
- "otg", false);
+ "otg");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.image", "odi", false);
+ "application/vnd.oasis.opendocument.image", "odi");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.spreadsheet", "ods",
- false);
+ "application/vnd.oasis.opendocument.spreadsheet", "ods");
sMimeTypeMap.loadEntry(
"application/vnd.oasis.opendocument.spreadsheet-template",
- "ots", false);
- sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text", "odt", false);
+ "ots");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-master", "odm",
- false);
+ "application/vnd.oasis.opendocument.text", "odt");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-template", "ott",
- false);
+ "application/vnd.oasis.opendocument.text-master", "odm");
sMimeTypeMap.loadEntry(
- "application/vnd.oasis.opendocument.text-web", "oth",
- false);
- sMimeTypeMap.loadEntry("application/vnd.rim.cod", "cod", false);
- sMimeTypeMap.loadEntry("application/vnd.smaf", "mmf", false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.calc", "sdc",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.draw", "sda",
- false);
+ "application/vnd.oasis.opendocument.text-template", "ott");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.impress", "sdd", false);
+ "application/vnd.oasis.opendocument.text-web", "oth");
+ sMimeTypeMap.loadEntry("application/vnd.rim.cod", "cod");
+ sMimeTypeMap.loadEntry("application/vnd.smaf", "mmf");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.calc", "sdc");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.draw", "sda");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.impress", "sdp", false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.math", "smf",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.writer", "sdw",
- false);
- sMimeTypeMap.loadEntry("application/vnd.stardivision.writer", "vor",
- false);
+ "application/vnd.stardivision.impress", "sdd");
sMimeTypeMap.loadEntry(
- "application/vnd.stardivision.writer-global", "sgl", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.calc", "sxc",
- false);
+ "application/vnd.stardivision.impress", "sdp");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.math", "smf");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
+ "sdw");
+ sMimeTypeMap.loadEntry("application/vnd.stardivision.writer",
+ "vor");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.calc.template", "stc", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.draw", "sxd",
- false);
+ "application/vnd.stardivision.writer-global", "sgl");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.calc", "sxc");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.draw.template", "std", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.impress", "sxi",
- false);
+ "application/vnd.sun.xml.calc.template", "stc");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.draw", "sxd");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.impress.template", "sti", false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.math", "sxm",
- false);
- sMimeTypeMap.loadEntry("application/vnd.sun.xml.writer", "sxw",
- false);
+ "application/vnd.sun.xml.draw.template", "std");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.impress", "sxi");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.writer.global", "sxg", false);
+ "application/vnd.sun.xml.impress.template", "sti");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.math", "sxm");
+ sMimeTypeMap.loadEntry("application/vnd.sun.xml.writer", "sxw");
sMimeTypeMap.loadEntry(
- "application/vnd.sun.xml.writer.template", "stw", false);
- sMimeTypeMap.loadEntry("application/vnd.visio", "vsd", false);
- sMimeTypeMap.loadEntry("application/x-abiword", "abw", false);
- sMimeTypeMap.loadEntry("application/x-apple-diskimage", "dmg",
- false);
- sMimeTypeMap.loadEntry("application/x-bcpio", "bcpio", false);
- sMimeTypeMap.loadEntry("application/x-bittorrent", "torrent",
- false);
- sMimeTypeMap.loadEntry("application/x-cdf", "cdf", false);
- sMimeTypeMap.loadEntry("application/x-cdlink", "vcd", false);
- sMimeTypeMap.loadEntry("application/x-chess-pgn", "pgn", false);
- sMimeTypeMap.loadEntry("application/x-cpio", "cpio", false);
- sMimeTypeMap.loadEntry("application/x-debian-package", "deb",
- false);
- sMimeTypeMap.loadEntry("application/x-debian-package", "udeb",
- false);
- sMimeTypeMap.loadEntry("application/x-director", "dcr", false);
- sMimeTypeMap.loadEntry("application/x-director", "dir", false);
- sMimeTypeMap.loadEntry("application/x-director", "dxr", false);
- sMimeTypeMap.loadEntry("application/x-dms", "dms", false);
- sMimeTypeMap.loadEntry("application/x-doom", "wad", false);
- sMimeTypeMap.loadEntry("application/x-dvi", "dvi", false);
- sMimeTypeMap.loadEntry("application/x-flac", "flac", false);
- sMimeTypeMap.loadEntry("application/x-font", "pfa", false);
- sMimeTypeMap.loadEntry("application/x-font", "pfb", false);
- sMimeTypeMap.loadEntry("application/x-font", "gsf", false);
- sMimeTypeMap.loadEntry("application/x-font", "pcf", false);
- sMimeTypeMap.loadEntry("application/x-font", "pcf.Z", false);
- sMimeTypeMap.loadEntry("application/x-freemind", "mm", false);
- sMimeTypeMap.loadEntry("application/x-futuresplash", "spl", false);
- sMimeTypeMap.loadEntry("application/x-gnumeric", "gnumeric", false);
- sMimeTypeMap.loadEntry("application/x-go-sgf", "sgf", false);
- sMimeTypeMap.loadEntry("application/x-graphing-calculator", "gcf",
- false);
- sMimeTypeMap.loadEntry("application/x-gtar", "gtar", false);
- sMimeTypeMap.loadEntry("application/x-gtar", "tgz", false);
- sMimeTypeMap.loadEntry("application/x-gtar", "taz", false);
- sMimeTypeMap.loadEntry("application/x-hdf", "hdf", false);
- sMimeTypeMap.loadEntry("application/x-ica", "ica", false);
- sMimeTypeMap.loadEntry("application/x-internet-signup", "ins",
- false);
- sMimeTypeMap.loadEntry("application/x-internet-signup", "isp",
- false);
- sMimeTypeMap.loadEntry("application/x-iphone", "iii", false);
- sMimeTypeMap.loadEntry("application/x-iso9660-image", "iso", false);
- sMimeTypeMap.loadEntry("application/x-jmol", "jmz", false);
- sMimeTypeMap.loadEntry("application/x-kchart", "chrt", false);
- sMimeTypeMap.loadEntry("application/x-killustrator", "kil", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skp", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skd", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skt", false);
- sMimeTypeMap.loadEntry("application/x-koan", "skm", false);
- sMimeTypeMap.loadEntry("application/x-kpresenter", "kpr", false);
- sMimeTypeMap.loadEntry("application/x-kpresenter", "kpt", false);
- sMimeTypeMap.loadEntry("application/x-kspread", "ksp", false);
- sMimeTypeMap.loadEntry("application/x-kword", "kwd", false);
- sMimeTypeMap.loadEntry("application/x-kword", "kwt", false);
- sMimeTypeMap.loadEntry("application/x-latex", "latex", false);
- sMimeTypeMap.loadEntry("application/x-lha", "lha", false);
- sMimeTypeMap.loadEntry("application/x-lzh", "lzh", false);
- sMimeTypeMap.loadEntry("application/x-lzx", "lzx", false);
- sMimeTypeMap.loadEntry("application/x-maker", "frm", false);
- sMimeTypeMap.loadEntry("application/x-maker", "maker", false);
- sMimeTypeMap.loadEntry("application/x-maker", "frame", false);
- sMimeTypeMap.loadEntry("application/x-maker", "fb", false);
- sMimeTypeMap.loadEntry("application/x-maker", "book", false);
- sMimeTypeMap.loadEntry("application/x-maker", "fbdoc", false);
- sMimeTypeMap.loadEntry("application/x-mif", "mif", false);
- sMimeTypeMap.loadEntry("application/x-ms-wmd", "wmd", false);
- sMimeTypeMap.loadEntry("application/x-ms-wmz", "wmz", false);
- sMimeTypeMap.loadEntry("application/x-msi", "msi", false);
- sMimeTypeMap.loadEntry("application/x-ns-proxy-autoconfig", "pac",
- false);
- sMimeTypeMap.loadEntry("application/x-nwc", "nwc", false);
- sMimeTypeMap.loadEntry("application/x-object", "o", false);
- sMimeTypeMap.loadEntry("application/x-oz-application", "oza",
- false);
- sMimeTypeMap.loadEntry("application/x-pkcs12", "p12", false);
- sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r",
- false);
- sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl", false);
- sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl",
- false);
- sMimeTypeMap.loadEntry("application/x-shar", "shar", false);
- sMimeTypeMap.loadEntry("application/x-stuffit", "sit", false);
- sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio", false);
- sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc", false);
- sMimeTypeMap.loadEntry("application/x-tar", "tar", false);
- sMimeTypeMap.loadEntry("application/x-texinfo", "texinfo", false);
- sMimeTypeMap.loadEntry("application/x-texinfo", "texi", false);
- sMimeTypeMap.loadEntry("application/x-troff", "t", false);
- sMimeTypeMap.loadEntry("application/x-troff", "roff", false);
- sMimeTypeMap.loadEntry("application/x-troff-man", "man", false);
- sMimeTypeMap.loadEntry("application/x-ustar", "ustar", false);
- sMimeTypeMap.loadEntry("application/x-wais-source", "src", false);
- sMimeTypeMap.loadEntry("application/x-wingz", "wz", false);
+ "application/vnd.sun.xml.writer.global", "sxg");
sMimeTypeMap.loadEntry(
- "application/x-webarchive", "webarchive", false); // added
- sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt", false);
- sMimeTypeMap.loadEntry("application/x-x509-user-cert", "crt", false);
- sMimeTypeMap.loadEntry("application/x-xcf", "xcf", false);
- sMimeTypeMap.loadEntry("application/x-xfig", "fig", false);
- sMimeTypeMap.loadEntry("audio/basic", "snd", false);
- sMimeTypeMap.loadEntry("audio/midi", "mid", false);
- sMimeTypeMap.loadEntry("audio/midi", "midi", false);
- sMimeTypeMap.loadEntry("audio/midi", "kar", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mpga", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mpega", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mp2", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "mp3", false);
- sMimeTypeMap.loadEntry("audio/mpeg", "m4a", false);
- sMimeTypeMap.loadEntry("audio/mpegurl", "m3u", false);
- sMimeTypeMap.loadEntry("audio/prs.sid", "sid", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aif", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aiff", false);
- sMimeTypeMap.loadEntry("audio/x-aiff", "aifc", false);
- sMimeTypeMap.loadEntry("audio/x-gsm", "gsm", false);
- sMimeTypeMap.loadEntry("audio/x-mpegurl", "m3u", false);
- sMimeTypeMap.loadEntry("audio/x-ms-wma", "wma", false);
- sMimeTypeMap.loadEntry("audio/x-ms-wax", "wax", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ra", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "rm", false);
- sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ram", false);
- sMimeTypeMap.loadEntry("audio/x-realaudio", "ra", false);
- sMimeTypeMap.loadEntry("audio/x-scpls", "pls", false);
- sMimeTypeMap.loadEntry("audio/x-sd2", "sd2", false);
- sMimeTypeMap.loadEntry("audio/x-wav", "wav", false);
- sMimeTypeMap.loadEntry("image/bmp", "bmp", false); // added
- sMimeTypeMap.loadEntry("image/gif", "gif", false);
- sMimeTypeMap.loadEntry("image/ico", "cur", false); // added
- sMimeTypeMap.loadEntry("image/ico", "ico", false); // added
- sMimeTypeMap.loadEntry("image/ief", "ief", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpeg", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpg", false);
- sMimeTypeMap.loadEntry("image/jpeg", "jpe", false);
- sMimeTypeMap.loadEntry("image/pcx", "pcx", false);
- sMimeTypeMap.loadEntry("image/png", "png", false);
- sMimeTypeMap.loadEntry("image/svg+xml", "svg", false);
- sMimeTypeMap.loadEntry("image/svg+xml", "svgz", false);
- sMimeTypeMap.loadEntry("image/tiff", "tiff", false);
- sMimeTypeMap.loadEntry("image/tiff", "tif", false);
- sMimeTypeMap.loadEntry("image/vnd.djvu", "djvu", false);
- sMimeTypeMap.loadEntry("image/vnd.djvu", "djv", false);
- sMimeTypeMap.loadEntry("image/vnd.wap.wbmp", "wbmp", false);
- sMimeTypeMap.loadEntry("image/x-cmu-raster", "ras", false);
- sMimeTypeMap.loadEntry("image/x-coreldraw", "cdr", false);
- sMimeTypeMap.loadEntry("image/x-coreldrawpattern", "pat", false);
- sMimeTypeMap.loadEntry("image/x-coreldrawtemplate", "cdt", false);
- sMimeTypeMap.loadEntry("image/x-corelphotopaint", "cpt", false);
- sMimeTypeMap.loadEntry("image/x-icon", "ico", false);
- sMimeTypeMap.loadEntry("image/x-jg", "art", false);
- sMimeTypeMap.loadEntry("image/x-jng", "jng", false);
- sMimeTypeMap.loadEntry("image/x-ms-bmp", "bmp", false);
- sMimeTypeMap.loadEntry("image/x-photoshop", "psd", false);
- sMimeTypeMap.loadEntry("image/x-portable-anymap", "pnm", false);
- sMimeTypeMap.loadEntry("image/x-portable-bitmap", "pbm", false);
- sMimeTypeMap.loadEntry("image/x-portable-graymap", "pgm", false);
- sMimeTypeMap.loadEntry("image/x-portable-pixmap", "ppm", false);
- sMimeTypeMap.loadEntry("image/x-rgb", "rgb", false);
- sMimeTypeMap.loadEntry("image/x-xbitmap", "xbm", false);
- sMimeTypeMap.loadEntry("image/x-xpixmap", "xpm", false);
- sMimeTypeMap.loadEntry("image/x-xwindowdump", "xwd", false);
- sMimeTypeMap.loadEntry("model/iges", "igs", false);
- sMimeTypeMap.loadEntry("model/iges", "iges", false);
- sMimeTypeMap.loadEntry("model/mesh", "msh", false);
- sMimeTypeMap.loadEntry("model/mesh", "mesh", false);
- sMimeTypeMap.loadEntry("model/mesh", "silo", false);
- sMimeTypeMap.loadEntry("text/calendar", "ics", true);
- sMimeTypeMap.loadEntry("text/calendar", "icz", true);
- sMimeTypeMap.loadEntry("text/comma-separated-values", "csv", true);
- sMimeTypeMap.loadEntry("text/css", "css", true);
- sMimeTypeMap.loadEntry("text/h323", "323", true);
- sMimeTypeMap.loadEntry("text/iuls", "uls", true);
- sMimeTypeMap.loadEntry("text/mathml", "mml", true);
+ "application/vnd.sun.xml.writer.template", "stw");
+ sMimeTypeMap.loadEntry("application/vnd.visio", "vsd");
+ sMimeTypeMap.loadEntry("application/x-abiword", "abw");
+ sMimeTypeMap.loadEntry("application/x-apple-diskimage", "dmg");
+ sMimeTypeMap.loadEntry("application/x-bcpio", "bcpio");
+ sMimeTypeMap.loadEntry("application/x-bittorrent", "torrent");
+ sMimeTypeMap.loadEntry("application/x-cdf", "cdf");
+ sMimeTypeMap.loadEntry("application/x-cdlink", "vcd");
+ sMimeTypeMap.loadEntry("application/x-chess-pgn", "pgn");
+ sMimeTypeMap.loadEntry("application/x-cpio", "cpio");
+ sMimeTypeMap.loadEntry("application/x-debian-package", "deb");
+ sMimeTypeMap.loadEntry("application/x-debian-package", "udeb");
+ sMimeTypeMap.loadEntry("application/x-director", "dcr");
+ sMimeTypeMap.loadEntry("application/x-director", "dir");
+ sMimeTypeMap.loadEntry("application/x-director", "dxr");
+ sMimeTypeMap.loadEntry("application/x-dms", "dms");
+ sMimeTypeMap.loadEntry("application/x-doom", "wad");
+ sMimeTypeMap.loadEntry("application/x-dvi", "dvi");
+ sMimeTypeMap.loadEntry("application/x-flac", "flac");
+ sMimeTypeMap.loadEntry("application/x-font", "pfa");
+ sMimeTypeMap.loadEntry("application/x-font", "pfb");
+ sMimeTypeMap.loadEntry("application/x-font", "gsf");
+ sMimeTypeMap.loadEntry("application/x-font", "pcf");
+ sMimeTypeMap.loadEntry("application/x-font", "pcf.Z");
+ sMimeTypeMap.loadEntry("application/x-freemind", "mm");
+ sMimeTypeMap.loadEntry("application/x-futuresplash", "spl");
+ sMimeTypeMap.loadEntry("application/x-gnumeric", "gnumeric");
+ sMimeTypeMap.loadEntry("application/x-go-sgf", "sgf");
+ sMimeTypeMap.loadEntry("application/x-graphing-calculator", "gcf");
+ sMimeTypeMap.loadEntry("application/x-gtar", "gtar");
+ sMimeTypeMap.loadEntry("application/x-gtar", "tgz");
+ sMimeTypeMap.loadEntry("application/x-gtar", "taz");
+ sMimeTypeMap.loadEntry("application/x-hdf", "hdf");
+ sMimeTypeMap.loadEntry("application/x-ica", "ica");
+ sMimeTypeMap.loadEntry("application/x-internet-signup", "ins");
+ sMimeTypeMap.loadEntry("application/x-internet-signup", "isp");
+ sMimeTypeMap.loadEntry("application/x-iphone", "iii");
+ sMimeTypeMap.loadEntry("application/x-iso9660-image", "iso");
+ sMimeTypeMap.loadEntry("application/x-jmol", "jmz");
+ sMimeTypeMap.loadEntry("application/x-kchart", "chrt");
+ sMimeTypeMap.loadEntry("application/x-killustrator", "kil");
+ sMimeTypeMap.loadEntry("application/x-koan", "skp");
+ sMimeTypeMap.loadEntry("application/x-koan", "skd");
+ sMimeTypeMap.loadEntry("application/x-koan", "skt");
+ sMimeTypeMap.loadEntry("application/x-koan", "skm");
+ sMimeTypeMap.loadEntry("application/x-kpresenter", "kpr");
+ sMimeTypeMap.loadEntry("application/x-kpresenter", "kpt");
+ sMimeTypeMap.loadEntry("application/x-kspread", "ksp");
+ sMimeTypeMap.loadEntry("application/x-kword", "kwd");
+ sMimeTypeMap.loadEntry("application/x-kword", "kwt");
+ sMimeTypeMap.loadEntry("application/x-latex", "latex");
+ sMimeTypeMap.loadEntry("application/x-lha", "lha");
+ sMimeTypeMap.loadEntry("application/x-lzh", "lzh");
+ sMimeTypeMap.loadEntry("application/x-lzx", "lzx");
+ sMimeTypeMap.loadEntry("application/x-maker", "frm");
+ sMimeTypeMap.loadEntry("application/x-maker", "maker");
+ sMimeTypeMap.loadEntry("application/x-maker", "frame");
+ sMimeTypeMap.loadEntry("application/x-maker", "fb");
+ sMimeTypeMap.loadEntry("application/x-maker", "book");
+ sMimeTypeMap.loadEntry("application/x-maker", "fbdoc");
+ sMimeTypeMap.loadEntry("application/x-mif", "mif");
+ sMimeTypeMap.loadEntry("application/x-ms-wmd", "wmd");
+ sMimeTypeMap.loadEntry("application/x-ms-wmz", "wmz");
+ sMimeTypeMap.loadEntry("application/x-msi", "msi");
+ sMimeTypeMap.loadEntry("application/x-ns-proxy-autoconfig", "pac");
+ sMimeTypeMap.loadEntry("application/x-nwc", "nwc");
+ sMimeTypeMap.loadEntry("application/x-object", "o");
+ sMimeTypeMap.loadEntry("application/x-oz-application", "oza");
+ sMimeTypeMap.loadEntry("application/x-pkcs12", "p12");
+ sMimeTypeMap.loadEntry("application/x-pkcs7-certreqresp", "p7r");
+ sMimeTypeMap.loadEntry("application/x-pkcs7-crl", "crl");
+ sMimeTypeMap.loadEntry("application/x-quicktimeplayer", "qtl");
+ sMimeTypeMap.loadEntry("application/x-shar", "shar");
+ sMimeTypeMap.loadEntry("application/x-stuffit", "sit");
+ sMimeTypeMap.loadEntry("application/x-sv4cpio", "sv4cpio");
+ sMimeTypeMap.loadEntry("application/x-sv4crc", "sv4crc");
+ sMimeTypeMap.loadEntry("application/x-tar", "tar");
+ sMimeTypeMap.loadEntry("application/x-texinfo", "texinfo");
+ sMimeTypeMap.loadEntry("application/x-texinfo", "texi");
+ sMimeTypeMap.loadEntry("application/x-troff", "t");
+ sMimeTypeMap.loadEntry("application/x-troff", "roff");
+ sMimeTypeMap.loadEntry("application/x-troff-man", "man");
+ sMimeTypeMap.loadEntry("application/x-ustar", "ustar");
+ sMimeTypeMap.loadEntry("application/x-wais-source", "src");
+ sMimeTypeMap.loadEntry("application/x-wingz", "wz");
+ sMimeTypeMap.loadEntry("application/x-webarchive", "webarchive");
+ sMimeTypeMap.loadEntry("application/x-x509-ca-cert", "crt");
+ sMimeTypeMap.loadEntry("application/x-x509-user-cert", "crt");
+ sMimeTypeMap.loadEntry("application/x-xcf", "xcf");
+ sMimeTypeMap.loadEntry("application/x-xfig", "fig");
+ sMimeTypeMap.loadEntry("application/xhtml+xml", "xhtml");
+ sMimeTypeMap.loadEntry("audio/basic", "snd");
+ sMimeTypeMap.loadEntry("audio/midi", "mid");
+ sMimeTypeMap.loadEntry("audio/midi", "midi");
+ sMimeTypeMap.loadEntry("audio/midi", "kar");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mpga");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mpega");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mp2");
+ sMimeTypeMap.loadEntry("audio/mpeg", "mp3");
+ sMimeTypeMap.loadEntry("audio/mpeg", "m4a");
+ sMimeTypeMap.loadEntry("audio/mpegurl", "m3u");
+ sMimeTypeMap.loadEntry("audio/prs.sid", "sid");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aif");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aiff");
+ sMimeTypeMap.loadEntry("audio/x-aiff", "aifc");
+ sMimeTypeMap.loadEntry("audio/x-gsm", "gsm");
+ sMimeTypeMap.loadEntry("audio/x-mpegurl", "m3u");
+ sMimeTypeMap.loadEntry("audio/x-ms-wma", "wma");
+ sMimeTypeMap.loadEntry("audio/x-ms-wax", "wax");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ra");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "rm");
+ sMimeTypeMap.loadEntry("audio/x-pn-realaudio", "ram");
+ sMimeTypeMap.loadEntry("audio/x-realaudio", "ra");
+ sMimeTypeMap.loadEntry("audio/x-scpls", "pls");
+ sMimeTypeMap.loadEntry("audio/x-sd2", "sd2");
+ sMimeTypeMap.loadEntry("audio/x-wav", "wav");
+ sMimeTypeMap.loadEntry("image/bmp", "bmp");
+ sMimeTypeMap.loadEntry("image/gif", "gif");
+ sMimeTypeMap.loadEntry("image/ico", "cur");
+ sMimeTypeMap.loadEntry("image/ico", "ico");
+ sMimeTypeMap.loadEntry("image/ief", "ief");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpeg");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpg");
+ sMimeTypeMap.loadEntry("image/jpeg", "jpe");
+ sMimeTypeMap.loadEntry("image/pcx", "pcx");
+ sMimeTypeMap.loadEntry("image/png", "png");
+ sMimeTypeMap.loadEntry("image/svg+xml", "svg");
+ sMimeTypeMap.loadEntry("image/svg+xml", "svgz");
+ sMimeTypeMap.loadEntry("image/tiff", "tiff");
+ sMimeTypeMap.loadEntry("image/tiff", "tif");
+ sMimeTypeMap.loadEntry("image/vnd.djvu", "djvu");
+ sMimeTypeMap.loadEntry("image/vnd.djvu", "djv");
+ sMimeTypeMap.loadEntry("image/vnd.wap.wbmp", "wbmp");
+ sMimeTypeMap.loadEntry("image/x-cmu-raster", "ras");
+ sMimeTypeMap.loadEntry("image/x-coreldraw", "cdr");
+ sMimeTypeMap.loadEntry("image/x-coreldrawpattern", "pat");
+ sMimeTypeMap.loadEntry("image/x-coreldrawtemplate", "cdt");
+ sMimeTypeMap.loadEntry("image/x-corelphotopaint", "cpt");
+ sMimeTypeMap.loadEntry("image/x-icon", "ico");
+ sMimeTypeMap.loadEntry("image/x-jg", "art");
+ sMimeTypeMap.loadEntry("image/x-jng", "jng");
+ sMimeTypeMap.loadEntry("image/x-ms-bmp", "bmp");
+ sMimeTypeMap.loadEntry("image/x-photoshop", "psd");
+ sMimeTypeMap.loadEntry("image/x-portable-anymap", "pnm");
+ sMimeTypeMap.loadEntry("image/x-portable-bitmap", "pbm");
+ sMimeTypeMap.loadEntry("image/x-portable-graymap", "pgm");
+ sMimeTypeMap.loadEntry("image/x-portable-pixmap", "ppm");
+ sMimeTypeMap.loadEntry("image/x-rgb", "rgb");
+ sMimeTypeMap.loadEntry("image/x-xbitmap", "xbm");
+ sMimeTypeMap.loadEntry("image/x-xpixmap", "xpm");
+ sMimeTypeMap.loadEntry("image/x-xwindowdump", "xwd");
+ sMimeTypeMap.loadEntry("model/iges", "igs");
+ sMimeTypeMap.loadEntry("model/iges", "iges");
+ sMimeTypeMap.loadEntry("model/mesh", "msh");
+ sMimeTypeMap.loadEntry("model/mesh", "mesh");
+ sMimeTypeMap.loadEntry("model/mesh", "silo");
+ sMimeTypeMap.loadEntry("text/calendar", "ics");
+ sMimeTypeMap.loadEntry("text/calendar", "icz");
+ sMimeTypeMap.loadEntry("text/comma-separated-values", "csv");
+ sMimeTypeMap.loadEntry("text/css", "css");
+ sMimeTypeMap.loadEntry("text/h323", "323");
+ sMimeTypeMap.loadEntry("text/iuls", "uls");
+ sMimeTypeMap.loadEntry("text/mathml", "mml");
// add it first so it will be the default for ExtensionFromMimeType
- sMimeTypeMap.loadEntry("text/plain", "txt", true);
- sMimeTypeMap.loadEntry("text/plain", "asc", true);
- sMimeTypeMap.loadEntry("text/plain", "text", true);
- sMimeTypeMap.loadEntry("text/plain", "diff", true);
- sMimeTypeMap.loadEntry("text/plain", "pot", true);
- sMimeTypeMap.loadEntry("text/richtext", "rtx", true);
- sMimeTypeMap.loadEntry("text/rtf", "rtf", true);
- sMimeTypeMap.loadEntry("text/texmacs", "ts", true);
- sMimeTypeMap.loadEntry("text/text", "phps", true);
- sMimeTypeMap.loadEntry("text/tab-separated-values", "tsv", true);
- sMimeTypeMap.loadEntry("text/x-bibtex", "bib", true);
- sMimeTypeMap.loadEntry("text/x-boo", "boo", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "h++", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hpp", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hxx", true);
- sMimeTypeMap.loadEntry("text/x-c++hdr", "hh", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "c++", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "cpp", true);
- sMimeTypeMap.loadEntry("text/x-c++src", "cxx", true);
- sMimeTypeMap.loadEntry("text/x-chdr", "h", true);
- sMimeTypeMap.loadEntry("text/x-component", "htc", true);
- sMimeTypeMap.loadEntry("text/x-csh", "csh", true);
- sMimeTypeMap.loadEntry("text/x-csrc", "c", true);
- sMimeTypeMap.loadEntry("text/x-dsrc", "d", true);
- sMimeTypeMap.loadEntry("text/x-haskell", "hs", true);
- sMimeTypeMap.loadEntry("text/x-java", "java", true);
- sMimeTypeMap.loadEntry("text/x-literate-haskell", "lhs", true);
- sMimeTypeMap.loadEntry("text/x-moc", "moc", true);
- sMimeTypeMap.loadEntry("text/x-pascal", "p", true);
- sMimeTypeMap.loadEntry("text/x-pascal", "pas", true);
- sMimeTypeMap.loadEntry("text/x-pcs-gcd", "gcd", true);
- sMimeTypeMap.loadEntry("text/x-setext", "etx", true);
- sMimeTypeMap.loadEntry("text/x-tcl", "tcl", true);
- sMimeTypeMap.loadEntry("text/x-tex", "tex", true);
- sMimeTypeMap.loadEntry("text/x-tex", "ltx", true);
- sMimeTypeMap.loadEntry("text/x-tex", "sty", true);
- sMimeTypeMap.loadEntry("text/x-tex", "cls", true);
- sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs", true);
- sMimeTypeMap.loadEntry("text/x-vcard", "vcf", true);
- sMimeTypeMap.loadEntry("video/3gpp", "3gp", false);
- sMimeTypeMap.loadEntry("video/3gpp", "3g2", false);
- sMimeTypeMap.loadEntry("video/dl", "dl", false);
- sMimeTypeMap.loadEntry("video/dv", "dif", false);
- sMimeTypeMap.loadEntry("video/dv", "dv", false);
- sMimeTypeMap.loadEntry("video/fli", "fli", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpeg", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpg", false);
- sMimeTypeMap.loadEntry("video/mpeg", "mpe", false);
- sMimeTypeMap.loadEntry("video/mp4", "mp4", false);
- sMimeTypeMap.loadEntry("video/mpeg", "VOB", false);
- sMimeTypeMap.loadEntry("video/quicktime", "qt", false);
- sMimeTypeMap.loadEntry("video/quicktime", "mov", false);
- sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu", false);
- sMimeTypeMap.loadEntry("video/x-la-asf", "lsf", false);
- sMimeTypeMap.loadEntry("video/x-la-asf", "lsx", false);
- sMimeTypeMap.loadEntry("video/x-mng", "mng", false);
- sMimeTypeMap.loadEntry("video/x-ms-asf", "asf", false);
- sMimeTypeMap.loadEntry("video/x-ms-asf", "asx", false);
- sMimeTypeMap.loadEntry("video/x-ms-wm", "wm", false);
- sMimeTypeMap.loadEntry("video/x-ms-wmv", "wmv", false);
- sMimeTypeMap.loadEntry("video/x-ms-wmx", "wmx", false);
- sMimeTypeMap.loadEntry("video/x-ms-wvx", "wvx", false);
- sMimeTypeMap.loadEntry("video/x-msvideo", "avi", false);
- sMimeTypeMap.loadEntry("video/x-sgi-movie", "movie", false);
- sMimeTypeMap.loadEntry("x-conference/x-cooltalk", "ice", false);
- sMimeTypeMap.loadEntry("x-epoc/x-sisx-app", "sisx", false);
+ sMimeTypeMap.loadEntry("text/plain", "txt");
+ sMimeTypeMap.loadEntry("text/plain", "asc");
+ sMimeTypeMap.loadEntry("text/plain", "text");
+ sMimeTypeMap.loadEntry("text/plain", "diff");
+ sMimeTypeMap.loadEntry("text/plain", "pot");
+ sMimeTypeMap.loadEntry("text/richtext", "rtx");
+ sMimeTypeMap.loadEntry("text/rtf", "rtf");
+ sMimeTypeMap.loadEntry("text/texmacs", "ts");
+ sMimeTypeMap.loadEntry("text/text", "phps");
+ sMimeTypeMap.loadEntry("text/tab-separated-values", "tsv");
+ sMimeTypeMap.loadEntry("text/x-bibtex", "bib");
+ sMimeTypeMap.loadEntry("text/x-boo", "boo");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "h++");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hpp");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hxx");
+ sMimeTypeMap.loadEntry("text/x-c++hdr", "hh");
+ sMimeTypeMap.loadEntry("text/x-c++src", "c++");
+ sMimeTypeMap.loadEntry("text/x-c++src", "cpp");
+ sMimeTypeMap.loadEntry("text/x-c++src", "cxx");
+ sMimeTypeMap.loadEntry("text/x-chdr", "h");
+ sMimeTypeMap.loadEntry("text/x-component", "htc");
+ sMimeTypeMap.loadEntry("text/x-csh", "csh");
+ sMimeTypeMap.loadEntry("text/x-csrc", "c");
+ sMimeTypeMap.loadEntry("text/x-dsrc", "d");
+ sMimeTypeMap.loadEntry("text/x-haskell", "hs");
+ sMimeTypeMap.loadEntry("text/x-java", "java");
+ sMimeTypeMap.loadEntry("text/x-literate-haskell", "lhs");
+ sMimeTypeMap.loadEntry("text/x-moc", "moc");
+ sMimeTypeMap.loadEntry("text/x-pascal", "p");
+ sMimeTypeMap.loadEntry("text/x-pascal", "pas");
+ sMimeTypeMap.loadEntry("text/x-pcs-gcd", "gcd");
+ sMimeTypeMap.loadEntry("text/x-setext", "etx");
+ sMimeTypeMap.loadEntry("text/x-tcl", "tcl");
+ sMimeTypeMap.loadEntry("text/x-tex", "tex");
+ sMimeTypeMap.loadEntry("text/x-tex", "ltx");
+ sMimeTypeMap.loadEntry("text/x-tex", "sty");
+ sMimeTypeMap.loadEntry("text/x-tex", "cls");
+ sMimeTypeMap.loadEntry("text/x-vcalendar", "vcs");
+ sMimeTypeMap.loadEntry("text/x-vcard", "vcf");
+ sMimeTypeMap.loadEntry("video/3gpp", "3gp");
+ sMimeTypeMap.loadEntry("video/3gpp", "3g2");
+ sMimeTypeMap.loadEntry("video/dl", "dl");
+ sMimeTypeMap.loadEntry("video/dv", "dif");
+ sMimeTypeMap.loadEntry("video/dv", "dv");
+ sMimeTypeMap.loadEntry("video/fli", "fli");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpeg");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpg");
+ sMimeTypeMap.loadEntry("video/mpeg", "mpe");
+ sMimeTypeMap.loadEntry("video/mp4", "mp4");
+ sMimeTypeMap.loadEntry("video/mpeg", "VOB");
+ sMimeTypeMap.loadEntry("video/quicktime", "qt");
+ sMimeTypeMap.loadEntry("video/quicktime", "mov");
+ sMimeTypeMap.loadEntry("video/vnd.mpegurl", "mxu");
+ sMimeTypeMap.loadEntry("video/x-la-asf", "lsf");
+ sMimeTypeMap.loadEntry("video/x-la-asf", "lsx");
+ sMimeTypeMap.loadEntry("video/x-mng", "mng");
+ sMimeTypeMap.loadEntry("video/x-ms-asf", "asf");
+ sMimeTypeMap.loadEntry("video/x-ms-asf", "asx");
+ sMimeTypeMap.loadEntry("video/x-ms-wm", "wm");
+ sMimeTypeMap.loadEntry("video/x-ms-wmv", "wmv");
+ sMimeTypeMap.loadEntry("video/x-ms-wmx", "wmx");
+ sMimeTypeMap.loadEntry("video/x-ms-wvx", "wvx");
+ sMimeTypeMap.loadEntry("video/x-msvideo", "avi");
+ sMimeTypeMap.loadEntry("video/x-sgi-movie", "movie");
+ sMimeTypeMap.loadEntry("x-conference/x-cooltalk", "ice");
+ sMimeTypeMap.loadEntry("x-epoc/x-sisx-app", "sisx");
}
return sMimeTypeMap;
diff --git a/core/java/android/webkit/MockGeolocation.java b/core/java/android/webkit/MockGeolocation.java
new file mode 100644
index 0000000..028cb19
--- /dev/null
+++ b/core/java/android/webkit/MockGeolocation.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * This class is simply a container for the methods used to configure WebKit's
+ * mock Geolocation service for use in LayoutTests.
+ * @hide Pending API council review.
+ */
+public final class MockGeolocation {
+
+ // Global instance of a MockGeolocation
+ private static MockGeolocation sMockGeolocation;
+
+ /**
+ * Set the position for the mock Geolocation service.
+ */
+ public void setPosition(double latitude, double longitude, double accuracy) {
+ // This should only ever be called on the WebKit thread.
+ nativeSetPosition(latitude, longitude, accuracy);
+ }
+
+ /**
+ * Set the error for the mock Geolocation service.
+ */
+ public void setError(int code, String message) {
+ // This should only ever be called on the WebKit thread.
+ nativeSetError(code, message);
+ }
+
+ /**
+ * Get the global instance of MockGeolocation.
+ * @return The global MockGeolocation instance.
+ */
+ public static MockGeolocation getInstance() {
+ if (sMockGeolocation == null) {
+ sMockGeolocation = new MockGeolocation();
+ }
+ return sMockGeolocation;
+ }
+
+ // Native functions
+ private static native void nativeSetPosition(double latitude, double longitude, double accuracy);
+ private static native void nativeSetError(int code, String message);
+}
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index c9b80ce..af0cb1e 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -132,11 +132,11 @@ class Network {
* XXX: Must be created in the same thread as WebCore!!!!!
*/
private Network(Context context) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(Thread.currentThread().
getName().equals(WebViewCore.THREAD_NAME));
}
- mSslErrorHandler = new SslErrorHandler(this);
+ mSslErrorHandler = new SslErrorHandler();
mHttpAuthHandler = new HttpAuthHandler(this);
mRequestQueue = new RequestQueue(context);
@@ -149,14 +149,12 @@ class Network {
* @param headers The http headers.
* @param postData The body of the request.
* @param loader A LoadListener for receiving the results of the request.
- * @param isHighPriority True if this is high priority request.
* @return True if the request was successfully queued.
*/
public boolean requestURL(String method,
Map<String, String> headers,
byte [] postData,
- LoadListener loader,
- boolean isHighPriority) {
+ LoadListener loader) {
String url = loader.url();
@@ -188,7 +186,7 @@ class Network {
RequestHandle handle = q.queueRequest(
url, loader.getWebAddress(), method, headers, loader,
- bodyProvider, bodyLength, isHighPriority);
+ bodyProvider, bodyLength);
loader.attachRequestHandle(handle);
if (loader.isSynchronous()) {
@@ -232,7 +230,7 @@ class Network {
* connecting through the proxy.
*/
public synchronized void setProxyUsername(String proxyUsername) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(isValidProxySet());
}
@@ -252,7 +250,7 @@ class Network {
* connecting through the proxy.
*/
public synchronized void setProxyPassword(String proxyPassword) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(isValidProxySet());
}
@@ -266,7 +264,7 @@ class Network {
* @return True iff succeeds.
*/
public boolean saveState(Bundle outState) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.NETWORK) {
Log.v(LOGTAG, "Network.saveState()");
}
@@ -280,7 +278,7 @@ class Network {
* @return True iff succeeds.
*/
public boolean restoreState(Bundle inState) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.NETWORK) {
Log.v(LOGTAG, "Network.restoreState()");
}
@@ -300,12 +298,20 @@ class Network {
* @param loader The loader that resulted in SSL errors.
*/
public void handleSslErrorRequest(LoadListener loader) {
- if (WebView.DEBUG) Assert.assertNotNull(loader);
+ if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
if (loader != null) {
mSslErrorHandler.handleSslErrorRequest(loader);
}
}
+ /* package */ boolean checkSslPrefTable(LoadListener loader,
+ SslError error) {
+ if (loader != null && error != null) {
+ return mSslErrorHandler.checkSslPrefTable(loader, error);
+ }
+ return false;
+ }
+
/**
* Handles authentication requests on their way up to the user (the user
* must provide credentials).
@@ -313,7 +319,7 @@ class Network {
* authentication request.
*/
public void handleAuthRequest(LoadListener loader) {
- if (WebView.DEBUG) Assert.assertNotNull(loader);
+ if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
if (loader != null) {
mHttpAuthHandler.handleAuthRequest(loader);
}
diff --git a/core/java/android/webkit/Plugin.java b/core/java/android/webkit/Plugin.java
index f83da99..34a30a9 100644
--- a/core/java/android/webkit/Plugin.java
+++ b/core/java/android/webkit/Plugin.java
@@ -26,7 +26,11 @@ import android.webkit.WebView;
/**
* Represents a plugin (Java equivalent of the PluginPackageAndroid
* C++ class in libs/WebKitLib/WebKit/WebCore/plugins/android/)
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+@Deprecated
public class Plugin {
public interface PreferencesClickHandler {
public void handleClickEvent(Context context);
@@ -38,6 +42,11 @@ public class Plugin {
private String mDescription;
private PreferencesClickHandler mHandler;
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public Plugin(String name,
String path,
String fileName,
@@ -49,49 +58,103 @@ public class Plugin {
mHandler = new DefaultClickHandler();
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public String toString() {
return mName;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public String getName() {
return mName;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public String getPath() {
return mPath;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public String getFileName() {
return mFileName;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public String getDescription() {
return mDescription;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void setName(String name) {
mName = name;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void setPath(String path) {
mPath = path;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void setFileName(String fileName) {
mFileName = fileName;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void setDescription(String description) {
mDescription = description;
}
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void setClickHandler(PreferencesClickHandler handler) {
mHandler = handler;
}
/**
* Invokes the click handler for this plugin.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public void dispatchClickEvent(Context context) {
if (mHandler != null) {
mHandler.handleClickEvent(context);
@@ -100,11 +163,15 @@ public class Plugin {
/**
* Default click handler. The plugins should implement their own.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
private class DefaultClickHandler implements PreferencesClickHandler,
DialogInterface.OnClickListener {
private AlertDialog mDialog;
-
+ @Deprecated
public void handleClickEvent(Context context) {
// Show a simple popup dialog containing the description
// string of the plugin.
@@ -117,7 +184,11 @@ public class Plugin {
.show();
}
}
-
+ /**
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+ @Deprecated
public void onClick(DialogInterface dialog, int which) {
mDialog.dismiss();
mDialog = null;
diff --git a/core/java/android/webkit/PluginActivity.java b/core/java/android/webkit/PluginActivity.java
new file mode 100644
index 0000000..f9e3080
--- /dev/null
+++ b/core/java/android/webkit/PluginActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * This activity is invoked when a plugin elects to go into full screen mode.
+ * @hide
+ */
+public class PluginActivity extends Activity {
+
+ /* package */ static final String INTENT_EXTRA_PACKAGE_NAME =
+ "android.webkit.plugin.PACKAGE_NAME";
+ /* package */ static final String INTENT_EXTRA_CLASS_NAME =
+ "android.webkit.plugin.CLASS_NAME";
+ /* package */ static final String INTENT_EXTRA_NPP_INSTANCE =
+ "android.webkit.plugin.NPP_INSTANCE";
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ if (intent == null) {
+ // No intent means no class to lookup.
+ finish();
+ }
+ final String packageName =
+ intent.getStringExtra(INTENT_EXTRA_PACKAGE_NAME);
+ final String className = intent.getStringExtra(INTENT_EXTRA_CLASS_NAME);
+ final int npp = intent.getIntExtra(INTENT_EXTRA_NPP_INSTANCE, -1);
+ // Retrieve the PluginStub implemented in packageName.className
+ PluginStub stub =
+ PluginUtil.getPluginStub(this, packageName, className, npp);
+
+ if (stub != null) {
+ View pluginView = stub.getFullScreenView(this);
+ if (pluginView != null) {
+ setContentView(pluginView);
+ } else {
+ // No custom full-sreen view returned by the plugin, odd but
+ // just in case, finish the activity.
+ finish();
+ }
+ } else {
+ finish();
+ }
+ }
+}
diff --git a/core/java/android/webkit/PluginContentLoader.java b/core/java/android/webkit/PluginContentLoader.java
deleted file mode 100644
index 2069599..0000000
--- a/core/java/android/webkit/PluginContentLoader.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.net.http.Headers;
-
-import java.io.InputStream;
-import java.util.*;
-
-import org.apache.http.util.CharArrayBuffer;
-
-/**
- * This class is a concrete implementation of StreamLoader that uses a
- * PluginData object as the source for the stream.
- */
-class PluginContentLoader extends StreamLoader {
-
- private PluginData mData; // Content source
-
- /**
- * Constructs a PluginDataLoader for use when loading content from
- * a plugin.
- *
- * @param loadListener LoadListener to pass the content to
- * @param data PluginData used as the source for the content.
- */
- PluginContentLoader(LoadListener loadListener, PluginData data) {
- super(loadListener);
- mData = data;
- }
-
- @Override
- protected boolean setupStreamAndSendStatus() {
- mDataStream = mData.getInputStream();
- mContentLength = mData.getContentLength();
- mHandler.status(1, 1, mData.getStatusCode(), "OK");
- return true;
- }
-
- @Override
- protected void buildHeaders(Headers headers) {
- // Crate a CharArrayBuffer with an arbitrary initial capacity.
- CharArrayBuffer buffer = new CharArrayBuffer(100);
- Iterator<Map.Entry<String, String[]>> responseHeadersIt =
- mData.getHeaders().entrySet().iterator();
- while (responseHeadersIt.hasNext()) {
- Map.Entry<String, String[]> entry = responseHeadersIt.next();
- // Headers.parseHeader() expects lowercase keys, so keys
- // such as "Accept-Ranges" will fail to parse.
- //
- // UrlInterceptHandler instances supply a mapping of
- // lowercase key to [ unmodified key, value ], so for
- // Headers.parseHeader() to succeed, we need to construct
- // a string using the key (i.e. entry.getKey()) and the
- // element denoting the header value in the
- // [ unmodified key, value ] pair (i.e. entry.getValue()[1).
- //
- // The reason why UrlInterceptHandler instances supply such a
- // mapping in the first place is historical. Early versions of
- // the Gears plugin used java.net.HttpURLConnection, which always
- // returned headers names as capitalized strings. When these were
- // fed back into webkit, they failed to parse.
- //
- // Mewanwhile, Gears was modified to use Apache HTTP library
- // instead, so this design is now obsolete. Changing it however,
- // would require changes to the Gears C++ codebase and QA-ing and
- // submitting a new binary to the Android tree. Given the
- // timelines for the next Android release, we will not do this
- // for now.
- //
- // TODO: fix C++ Gears to remove the need for this
- // design.
- String keyValue = entry.getKey() + ": " + entry.getValue()[1];
- buffer.ensureCapacity(keyValue.length());
- buffer.append(keyValue);
- // Parse it into the header container.
- headers.parseHeader(buffer);
- // Clear the buffer
- buffer.clear();
- }
- }
-}
diff --git a/core/java/android/webkit/PluginData.java b/core/java/android/webkit/PluginData.java
index 2b539fe..2dd445e 100644
--- a/core/java/android/webkit/PluginData.java
+++ b/core/java/android/webkit/PluginData.java
@@ -28,7 +28,10 @@ import java.util.Map;
* status code. The PluginData class is the container for all these
* parts.
*
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+@Deprecated
public final class PluginData {
/**
* The content stream.
@@ -47,10 +50,6 @@ public final class PluginData {
private Map<String, String[]> mHeaders;
/**
- * The index of the header value in the above mapping.
- */
- private int mHeaderValueIndex;
- /**
* The associated HTTP response code.
*/
private int mStatusCode;
@@ -63,7 +62,11 @@ public final class PluginData {
* @param headers The response headers. Map of
* lowercase header name to [ unmodified header name, header value]
* @param length The HTTP response status code.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public PluginData(
InputStream stream,
long length,
@@ -79,7 +82,11 @@ public final class PluginData {
* Returns the input stream that contains the plugin content.
*
* @return An InputStream instance with the plugin content.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public InputStream getInputStream() {
return mStream;
}
@@ -88,7 +95,11 @@ public final class PluginData {
* Returns the length of the plugin content.
*
* @return the length of the plugin content.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public long getContentLength() {
return mContentLength;
}
@@ -100,7 +111,11 @@ public final class PluginData {
* @return A Map<String, String[]> containing all headers. The
* mapping is 'lowercase header name' to ['unmodified header
* name', header value].
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public Map<String, String[]> getHeaders() {
return mHeaders;
}
@@ -109,7 +124,11 @@ public final class PluginData {
* Returns the HTTP status code for the response.
*
* @return The HTTP statue code, e.g 200.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public int getStatusCode() {
return mStatusCode;
}
diff --git a/core/java/android/webkit/PluginList.java b/core/java/android/webkit/PluginList.java
index a9d3d8c..a61b07b 100644
--- a/core/java/android/webkit/PluginList.java
+++ b/core/java/android/webkit/PluginList.java
@@ -24,27 +24,43 @@ import java.util.List;
* A simple list of initialized plugins. This list gets
* populated when the plugins are initialized (at
* browser startup, at the moment).
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+@Deprecated
public class PluginList {
private ArrayList<Plugin> mPlugins;
/**
* Public constructor. Initializes the list of plugins.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public PluginList() {
mPlugins = new ArrayList<Plugin>();
}
/**
* Returns the list of plugins as a java.util.List.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public synchronized List getList() {
return mPlugins;
}
/**
* Adds a plugin to the list.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public synchronized void addPlugin(Plugin plugin) {
if (!mPlugins.contains(plugin)) {
mPlugins.add(plugin);
@@ -53,7 +69,11 @@ public class PluginList {
/**
* Removes a plugin from the list.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public synchronized void removePlugin(Plugin plugin) {
int location = mPlugins.indexOf(plugin);
if (location != -1) {
@@ -63,14 +83,22 @@ public class PluginList {
/**
* Clears the plugin list.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public synchronized void clear() {
mPlugins.clear();
}
/**
* Dispatches the click event to the appropriate plugin.
+ *
+ * @deprecated This interface was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public synchronized void pluginClicked(Context context, int position) {
try {
Plugin plugin = mPlugins.get(position);
diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java
new file mode 100644
index 0000000..32eea5f
--- /dev/null
+++ b/core/java/android/webkit/PluginManager.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.Signature;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+/**
+ * Class for managing the relationship between the {@link WebView} and installed
+ * plugins in the system. You can find this class through
+ * {@link PluginManager#getInstance}.
+ *
+ * @hide pending API solidification
+ */
+public class PluginManager {
+
+ /**
+ * Service Action: A plugin wishes to be loaded in the WebView must provide
+ * {@link android.content.IntentFilter IntentFilter} that accepts this
+ * action in their AndroidManifest.xml.
+ * <p>
+ * TODO: we may change this to a new PLUGIN_ACTION if this is going to be
+ * public.
+ */
+ @SdkConstant(SdkConstantType.SERVICE_ACTION)
+ public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
+
+ /**
+ * A plugin wishes to be loaded in the WebView must provide this permission
+ * in their AndroidManifest.xml.
+ */
+ public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
+
+ private static final String LOGTAG = "webkit";
+
+ private static PluginManager mInstance = null;
+
+ private final Context mContext;
+
+ private PluginManager(Context context) {
+ mContext = context;
+ }
+
+ public static synchronized PluginManager getInstance(Context context) {
+ if (mInstance == null) {
+ if (context == null) {
+ throw new IllegalStateException(
+ "First call to PluginManager need a valid context.");
+ }
+ mInstance = new PluginManager(context);
+ }
+ return mInstance;
+ }
+
+ /**
+ * Signal the WebCore thread to refresh its list of plugins. Use this if the
+ * directory contents of one of the plugin directories has been modified and
+ * needs its changes reflecting. May cause plugin load and/or unload.
+ *
+ * @param reloadOpenPages Set to true to reload all open pages.
+ */
+ public void refreshPlugins(boolean reloadOpenPages) {
+ BrowserFrame.sJavaBridge.obtainMessage(
+ JWebCoreJavaBridge.REFRESH_PLUGINS, reloadOpenPages)
+ .sendToTarget();
+ }
+
+ String[] getPluginDirectories() {
+ ArrayList<String> directories = new ArrayList<String>();
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(
+ PLUGIN_ACTION), PackageManager.GET_SERVICES);
+ for (ResolveInfo info : plugins) {
+ ServiceInfo serviceInfo = info.serviceInfo;
+ if (serviceInfo == null) {
+ Log.w(LOGTAG, "Ignore bad plugin");
+ continue;
+ }
+ PackageInfo pkgInfo;
+ try {
+ pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
+ PackageManager.GET_PERMISSIONS
+ | PackageManager.GET_SIGNATURES);
+ } catch (NameNotFoundException e) {
+ Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName);
+ continue;
+ }
+ if (pkgInfo == null) {
+ continue;
+ }
+ String directory = pkgInfo.applicationInfo.dataDir + "/lib";
+ if (directories.contains(directory)) {
+ continue;
+ }
+ String permissions[] = pkgInfo.requestedPermissions;
+ if (permissions == null) {
+ continue;
+ }
+ boolean permissionOk = false;
+ for (String permit : permissions) {
+ if (PLUGIN_PERMISSION.equals(permit)) {
+ permissionOk = true;
+ break;
+ }
+ }
+ if (!permissionOk) {
+ continue;
+ }
+ Signature signatures[] = pkgInfo.signatures;
+ if (signatures == null) {
+ continue;
+ }
+ boolean signatureMatch = false;
+ for (Signature signature : signatures) {
+ // TODO: check signature against Google provided one
+ signatureMatch = true;
+ break;
+ }
+ if (!signatureMatch) {
+ continue;
+ }
+ directories.add(directory);
+ }
+
+ return directories.toArray(new String[directories.size()]);
+ }
+
+ String getPluginSharedDataDirectory() {
+ return mContext.getDir("plugins", 0).getPath();
+ }
+}
diff --git a/core/java/android/webkit/PluginStub.java b/core/java/android/webkit/PluginStub.java
new file mode 100644
index 0000000..c24da8d
--- /dev/null
+++ b/core/java/android/webkit/PluginStub.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.content.Context;
+import android.view.View;
+
+/**
+ * This abstract class is used to implement plugins in a WebView. A plugin
+ * package may extend this class and implement the abstract functions to create
+ * embedded or fullscreeen views displayed in a WebView. The PluginStub
+ * implementation will be provided the same NPP instance that is created
+ * through the native interface.
+ */
+public abstract class PluginStub {
+ /**
+ * Construct a new PluginStub implementation for the given NPP instance.
+ * @param npp The native NPP instance.
+ */
+ public PluginStub(int npp) { }
+
+ /**
+ * Return a custom embedded view to draw the plugin.
+ * @param context The current application's Context.
+ * @return A custom View that will be managed by WebView.
+ */
+ public abstract View getEmbeddedView(Context context);
+
+ /**
+ * Return a custom full-screen view to be displayed when the user requests
+ * a plugin display as full-screen. Note that the application may choose not
+ * to display this View as completely full-screen.
+ * @param context The current application's Context.
+ * @return A custom View that will be managed by the application.
+ */
+ public abstract View getFullScreenView(Context context);
+}
diff --git a/core/java/android/webkit/PluginUtil.java b/core/java/android/webkit/PluginUtil.java
new file mode 100644
index 0000000..c0a7375
--- /dev/null
+++ b/core/java/android/webkit/PluginUtil.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.webkit;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+class PluginUtil {
+
+ private static final String LOGTAG = "PluginUtil";
+
+ /**
+ *
+ * @param packageName the name of the apk where the class can be found
+ * @param className the fully qualified name of a subclass of PluginStub
+ */
+ /* package */
+ static PluginStub getPluginStub(Context context, String packageName,
+ String className, int NPP) {
+ try {
+ Context pluginContext = context.createPackageContext(packageName,
+ Context.CONTEXT_INCLUDE_CODE |
+ Context.CONTEXT_IGNORE_SECURITY);
+ ClassLoader pluginCL = pluginContext.getClassLoader();
+
+ Class<?> stubClass =
+ pluginCL.loadClass(className);
+ Constructor<?> stubConstructor =
+ stubClass.getConstructor(int.class);
+ Object stubObject = stubConstructor.newInstance(NPP);
+
+ if (stubObject instanceof PluginStub) {
+ return (PluginStub) stubObject;
+ } else {
+ Log.e(LOGTAG, "The plugin class is not of type PluginStub");
+ }
+ } catch (Exception e) {
+ // Any number of things could have happened. Log the exception and
+ // return null. Careful not to use Log.e(LOGTAG, "String", e)
+ // because that reports the exception to the checkin service.
+ Log.e(LOGTAG, Log.getStackTraceString(e));
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 5f84bbe..90ed65d 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -42,11 +42,6 @@ public class SslErrorHandler extends Handler {
private static final String LOGTAG = "network";
/**
- * Network.
- */
- private Network mNetwork;
-
- /**
* Queue of loaders that experience SSL-related problems.
*/
private LinkedList<LoadListener> mLoaderQueue;
@@ -57,13 +52,15 @@ public class SslErrorHandler extends Handler {
private Bundle mSslPrefTable;
// Message id for handling the response
- private final int HANDLE_RESPONSE = 100;
+ private static final int HANDLE_RESPONSE = 100;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case HANDLE_RESPONSE:
- handleSslErrorResponse(msg.arg1 == 1);
+ LoadListener loader = (LoadListener) msg.obj;
+ handleSslErrorResponse(loader, loader.sslError(),
+ msg.arg1 == 1);
fastProcessQueuedSslErrors();
break;
}
@@ -72,9 +69,7 @@ public class SslErrorHandler extends Handler {
/**
* Creates a new error handler with an empty loader queue.
*/
- /* package */ SslErrorHandler(Network network) {
- mNetwork = network;
-
+ /* package */ SslErrorHandler() {
mLoaderQueue = new LinkedList<LoadListener>();
mSslPrefTable = new Bundle();
}
@@ -83,7 +78,7 @@ public class SslErrorHandler extends Handler {
* Saves this handler's state into a map.
* @return True iff succeeds.
*/
- /* package */ boolean saveState(Bundle outState) {
+ /* package */ synchronized boolean saveState(Bundle outState) {
boolean success = (outState != null);
if (success) {
// TODO?
@@ -97,7 +92,7 @@ public class SslErrorHandler extends Handler {
* Restores this handler's state from a map.
* @return True iff succeeds.
*/
- /* package */ boolean restoreState(Bundle inState) {
+ /* package */ synchronized boolean restoreState(Bundle inState) {
boolean success = (inState != null);
if (success) {
success = inState.containsKey("ssl-error-handler");
@@ -120,7 +115,7 @@ public class SslErrorHandler extends Handler {
* Handles SSL error(s) on the way up to the user.
*/
/* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
"url=" + loader.url());
}
@@ -134,6 +129,28 @@ public class SslErrorHandler extends Handler {
}
/**
+ * Check the preference table for a ssl error that has already been shown
+ * to the user.
+ */
+ /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
+ SslError error) {
+ final String host = loader.host();
+ final int primary = error.getPrimaryError();
+
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Assert.assertTrue(host != null && primary != 0);
+ }
+
+ if (mSslPrefTable.containsKey(host)) {
+ if (primary <= mSslPrefTable.getInt(host)) {
+ handleSslErrorResponse(loader, error, true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Processes queued SSL-error confirmation requests in
* a tight loop while there is no need to ask the user.
*/
@@ -151,28 +168,24 @@ public class SslErrorHandler extends Handler {
if (loader != null) {
// if this loader has been cancelled
if (loader.cancelled()) {
- // go to the following loader in the queue
+ // go to the following loader in the queue. Make sure this
+ // loader has been removed from the queue.
+ mLoaderQueue.remove(loader);
return true;
}
SslError error = loader.sslError();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertNotNull(error);
}
- int primary = error.getPrimaryError();
- String host = loader.host();
-
- if (WebView.DEBUG) {
- Assert.assertTrue(host != null && primary != 0);
- }
-
- if (mSslPrefTable.containsKey(host)) {
- if (primary <= mSslPrefTable.getInt(host)) {
- handleSslErrorResponse(true);
- return true;
- }
+ // checkSslPrefTable will handle the ssl error response if the
+ // answer is available. It does not remove the loader from the
+ // queue.
+ if (checkSslPrefTable(loader, error)) {
+ mLoaderQueue.remove(loader);
+ return true;
}
// if we do not have information on record, ask
@@ -189,7 +202,7 @@ public class SslErrorHandler extends Handler {
* Proceed with the SSL certificate.
*/
public void proceed() {
- sendMessage(obtainMessage(HANDLE_RESPONSE, 1, 0));
+ sendMessage(obtainMessage(HANDLE_RESPONSE, 1, 0, mLoaderQueue.poll()));
}
/**
@@ -197,19 +210,20 @@ public class SslErrorHandler extends Handler {
* the error.
*/
public void cancel() {
- sendMessage(obtainMessage(HANDLE_RESPONSE, 0, 0));
+ sendMessage(obtainMessage(HANDLE_RESPONSE, 0, 0, mLoaderQueue.poll()));
}
/**
* Handles SSL error(s) on the way down from the user.
*/
- /* package */ synchronized void handleSslErrorResponse(boolean proceed) {
- LoadListener loader = mLoaderQueue.poll();
- if (WebView.DEBUG) {
+ /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
+ SslError error, boolean proceed) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertNotNull(loader);
+ Assert.assertNotNull(error);
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
+ " proceed: " + proceed
+ " url:" + loader.url());
@@ -218,16 +232,16 @@ public class SslErrorHandler extends Handler {
if (!loader.cancelled()) {
if (proceed) {
// update the user's SSL error preference table
- int primary = loader.sslError().getPrimaryError();
+ int primary = error.getPrimaryError();
String host = loader.host();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertTrue(host != null && primary != 0);
}
boolean hasKey = mSslPrefTable.containsKey(host);
if (!hasKey ||
primary > mSslPrefTable.getInt(host)) {
- mSslPrefTable.putInt(host, new Integer(primary));
+ mSslPrefTable.putInt(host, primary);
}
}
loader.handleSslErrorResponse(proceed);
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 705157c..623ff29 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -102,7 +102,7 @@ abstract class StreamLoader extends Handler {
// to pass data to the loader
mData = new byte[8192];
sendHeaders();
- while (!sendData());
+ while (!sendData() && !mHandler.cancelled());
closeStreamAndSendEndData();
mHandler.loadSynchronousMessages();
}
@@ -113,9 +113,13 @@ abstract class StreamLoader extends Handler {
* @see android.os.Handler#handleMessage(android.os.Message)
*/
public void handleMessage(Message msg) {
- if (WebView.DEBUG && mHandler.isSynchronous()) {
+ if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
throw new AssertionError();
}
+ if (mHandler.cancelled()) {
+ closeStreamAndSendEndData();
+ return;
+ }
switch(msg.what) {
case MSG_STATUS:
if (setupStreamAndSendStatus()) {
@@ -153,7 +157,6 @@ abstract class StreamLoader extends Handler {
if (mContentLength > 0) {
headers.setContentLength(mContentLength);
}
- headers.setCacheControl(NO_STORE);
buildHeaders(headers);
mHandler.headers(headers);
}
diff --git a/core/java/android/webkit/TextDialog.java b/core/java/android/webkit/TextDialog.java
deleted file mode 100644
index 99de56d..0000000
--- a/core/java/android/webkit/TextDialog.java
+++ /dev/null
@@ -1,593 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
-import android.text.Editable;
-import android.text.InputFilter;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.method.MovementMethod;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AbsoluteLayout.LayoutParams;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * TextDialog is a specialized version of EditText used by WebView
- * to overlay html textfields (and textareas) to use our standard
- * text editing.
- */
-/* package */ class TextDialog extends AutoCompleteTextView {
-
- private WebView mWebView;
- private boolean mSingle;
- private int mWidthSpec;
- private int mHeightSpec;
- private int mNodePointer;
- // FIXME: This is a hack for blocking unmatched key ups, in particular
- // on the enter key. The method for blocking unmatched key ups prevents
- // the shift key from working properly.
- private boolean mGotEnterDown;
- // mScrollToAccommodateCursor being set to false prevents us from scrolling
- // the cursor on screen when using the trackball to select a textfield.
- private boolean mScrollToAccommodateCursor;
- private int mMaxLength;
- // Keep track of the text before the change so we know whether we actually
- // need to send down the DOM events.
- private String mPreChange;
- // Array to store the final character added in onTextChanged, so that its
- // KeyEvents may be determined.
- private char[] mCharacter = new char[1];
- // This is used to reset the length filter when on a textfield
- // with no max length.
- // FIXME: This can be replaced with TextView.NO_FILTERS if that
- // is made public/protected.
- private static final InputFilter[] NO_FILTERS = new InputFilter[0];
-
- /**
- * Create a new TextDialog.
- * @param context The Context for this TextDialog.
- * @param webView The WebView that created this.
- */
- /* package */ TextDialog(Context context, WebView webView) {
- super(context);
- mWebView = webView;
- ShapeDrawable background = new ShapeDrawable(new RectShape());
- Paint shapePaint = background.getPaint();
- shapePaint.setStyle(Paint.Style.STROKE);
- ColorDrawable color = new ColorDrawable(Color.WHITE);
- Drawable[] array = new Drawable[2];
- array[0] = color;
- array[1] = background;
- LayerDrawable layers = new LayerDrawable(array);
- // Hide WebCore's text behind this and allow the WebView
- // to draw its own focusring.
- setBackgroundDrawable(layers);
- // Align the text better with the text behind it, so moving
- // off of the textfield will not appear to move the text.
- setPadding(3, 2, 0, 0);
- mMaxLength = -1;
- // Turn on subpixel text, and turn off kerning, so it better matches
- // the text in webkit.
- TextPaint paint = getPaint();
- int flags = paint.getFlags() | Paint.SUBPIXEL_TEXT_FLAG |
- Paint.ANTI_ALIAS_FLAG & ~Paint.DEV_KERN_TEXT_FLAG;
- paint.setFlags(flags);
- // Set the text color to black, regardless of the theme. This ensures
- // that other applications that use embedded WebViews will properly
- // display the text in textfields.
- setTextColor(Color.BLACK);
- setImeOptions(EditorInfo.IME_ACTION_NONE);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.isSystem()) {
- return super.dispatchKeyEvent(event);
- }
- // Treat ACTION_DOWN and ACTION MULTIPLE the same
- boolean down = event.getAction() != KeyEvent.ACTION_UP;
- int keyCode = event.getKeyCode();
- Spannable text = (Spannable) getText();
- int oldLength = text.length();
- // Normally the delete key's dom events are sent via onTextChanged.
- // However, if the length is zero, the text did not change, so we
- // go ahead and pass the key down immediately.
- if (KeyEvent.KEYCODE_DEL == keyCode && 0 == oldLength) {
- sendDomEvent(event);
- return true;
- }
-
- if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode)) {
- if (isPopupShowing()) {
- return super.dispatchKeyEvent(event);
- }
- if (!down) {
- // Hide the keyboard, since the user has just submitted this
- // form. The submission happens thanks to the two calls
- // to sendDomEvent.
- InputMethodManager.getInstance(mContext)
- .hideSoftInputFromWindow(getWindowToken(), 0);
- sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
- sendDomEvent(event);
- }
- return super.dispatchKeyEvent(event);
- } else if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
- // Note that this handles center key and trackball.
- if (isPopupShowing()) {
- return super.dispatchKeyEvent(event);
- }
- // Center key should be passed to a potential onClick
- if (!down) {
- mWebView.shortPressOnTextField();
- }
- // Pass to super to handle longpress.
- return super.dispatchKeyEvent(event);
- }
-
- // Ensure there is a layout so arrow keys are handled properly.
- if (getLayout() == null) {
- measure(mWidthSpec, mHeightSpec);
- }
- int oldStart = Selection.getSelectionStart(text);
- int oldEnd = Selection.getSelectionEnd(text);
-
- boolean maxedOut = mMaxLength != -1 && oldLength == mMaxLength;
- // If we are at max length, and there is a selection rather than a
- // cursor, we need to store the text to compare later, since the key
- // may have changed the string.
- String oldText;
- if (maxedOut && oldEnd != oldStart) {
- oldText = text.toString();
- } else {
- oldText = "";
- }
- if (super.dispatchKeyEvent(event)) {
- // If the TextDialog handled the key it was either an alphanumeric
- // key, a delete, or a movement within the text. All of those are
- // ok to pass to javascript.
-
- // UNLESS there is a max length determined by the html. In that
- // case, if the string was already at the max length, an
- // alphanumeric key will be erased by the LengthFilter,
- // so do not pass down to javascript, and instead
- // return true. If it is an arrow key or a delete key, we can go
- // ahead and pass it down.
- boolean isArrowKey;
- switch(keyCode) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- isArrowKey = true;
- break;
- case KeyEvent.KEYCODE_ENTER:
- // For multi-line text boxes, newlines will
- // trigger onTextChanged for key down (which will send both
- // key up and key down) but not key up.
- mGotEnterDown = true;
- default:
- isArrowKey = false;
- break;
- }
- if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) {
- if (oldEnd == oldStart) {
- // Return true so the key gets dropped.
- mScrollToAccommodateCursor = true;
- return true;
- } else if (!oldText.equals(getText().toString())) {
- // FIXME: This makes the text work properly, but it
- // does not pass down the key event, so it may not
- // work for a textfield that has the type of
- // behavior of GoogleSuggest. That said, it is
- // unlikely that a site would combine the two in
- // one textfield.
- Spannable span = (Spannable) getText();
- int newStart = Selection.getSelectionStart(span);
- int newEnd = Selection.getSelectionEnd(span);
- mWebView.replaceTextfieldText(0, oldLength, span.toString(),
- newStart, newEnd);
- mScrollToAccommodateCursor = true;
- return true;
- }
- }
- if (isArrowKey) {
- // Arrow key does not change the text, but we still want to send
- // the DOM events.
- sendDomEvent(event);
- }
- mScrollToAccommodateCursor = true;
- return true;
- }
- // FIXME: TextViews return false for up and down key events even though
- // they change the selection. Since we don't want the get out of sync
- // with WebCore's notion of the current selection, reset the selection
- // to what it was before the key event.
- Selection.setSelection(text, oldStart, oldEnd);
- // Ignore the key up event for newlines. This prevents
- // multiple newlines in the native textarea.
- if (mGotEnterDown && !down) {
- return true;
- }
- // if it is a navigation key, pass it to WebView
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT
- || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
- || keyCode == KeyEvent.KEYCODE_DPAD_UP
- || keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- // WebView check the trackballtime in onKeyDown to avoid calling
- // native from both trackball and key handling. As this is called
- // from TextDialog, we always want WebView to check with native.
- // Reset trackballtime to ensure it.
- mWebView.resetTrackballTime();
- return down ? mWebView.onKeyDown(keyCode, event) : mWebView
- .onKeyUp(keyCode, event);
- }
- return false;
- }
-
- /**
- * Create a fake touch up event at (x,y) with respect to this TextDialog.
- * This is used by WebView to act as though a touch event which happened
- * before we placed the TextDialog actually hit it, so that it can place
- * the cursor accordingly.
- */
- /* package */ void fakeTouchEvent(float x, float y) {
- // We need to ensure that there is a Layout, since the Layout is used
- // in determining where to place the cursor.
- if (getLayout() == null) {
- measure(mWidthSpec, mHeightSpec);
- }
- // Create a fake touch up, which is used to place the cursor.
- MotionEvent ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
- x, y, 0);
- onTouchEvent(ev);
- ev.recycle();
- }
-
- /**
- * Determine whether this TextDialog currently represents the node
- * represented by ptr.
- * @param ptr Pointer to a node to compare to.
- * @return boolean Whether this TextDialog already represents the node
- * pointed to by ptr.
- */
- /* package */ boolean isSameTextField(int ptr) {
- return ptr == mNodePointer;
- }
-
- @Override
- public boolean onPreDraw() {
- if (getLayout() == null) {
- measure(mWidthSpec, mHeightSpec);
- }
- return super.onPreDraw();
- }
-
- @Override
- protected void onTextChanged(CharSequence s,int start,int before,int count){
- super.onTextChanged(s, start, before, count);
- String postChange = s.toString();
- // Prevent calls to setText from invoking onTextChanged (since this will
- // mean we are on a different textfield). Also prevent the change when
- // going from a textfield with a string of text to one with a smaller
- // limit on text length from registering the onTextChanged event.
- if (mPreChange == null || mPreChange.equals(postChange) ||
- (mMaxLength > -1 && mPreChange.length() > mMaxLength &&
- mPreChange.substring(0, mMaxLength).equals(postChange))) {
- return;
- }
- mPreChange = postChange;
- // This was simply a delete or a cut, so just delete the
- // selection.
- if (before > 0 && 0 == count) {
- mWebView.deleteSelection(start, start + before);
- // For this and all changes to the text, update our cache
- updateCachedTextfield();
- return;
- }
- // Find the last character being replaced. If it can be represented by
- // events, we will pass them to native (after replacing the beginning
- // of the changed text), so we can see javascript events.
- // Otherwise, replace the text being changed (including the last
- // character) in the textfield.
- TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
- KeyCharacterMap kmap =
- KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
- KeyEvent[] events = kmap.getEvents(mCharacter);
- boolean cannotUseKeyEvents = null == events;
- int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
- if (count > 1 || cannotUseKeyEvents) {
- String replace = s.subSequence(start,
- start + count - charactersFromKeyEvents).toString();
- mWebView.replaceTextfieldText(start, start + before, replace,
- start + count - charactersFromKeyEvents,
- start + count - charactersFromKeyEvents);
- } else {
- // This corrects the selection which may have been affected by the
- // trackball or auto-correct.
- mWebView.setSelection(start, start + before);
- }
- updateCachedTextfield();
- if (cannotUseKeyEvents) {
- return;
- }
- int length = events.length;
- for (int i = 0; i < length; i++) {
- // We never send modifier keys to native code so don't send them
- // here either.
- if (!KeyEvent.isModifierKey(events[i].getKeyCode())) {
- sendDomEvent(events[i]);
- }
- }
- }
-
- @Override
- public boolean onTrackballEvent(MotionEvent event) {
- if (isPopupShowing()) {
- return super.onTrackballEvent(event);
- }
- if (event.getAction() != MotionEvent.ACTION_MOVE) {
- return false;
- }
- Spannable text = (Spannable) getText();
- MovementMethod move = getMovementMethod();
- if (move != null && getLayout() != null &&
- move.onTrackballEvent(this, text, event)) {
- // Need to pass down the selection, which has changed.
- // FIXME: This should work, but does not, so we set the selection
- // in onTextChanged.
- //int start = Selection.getSelectionStart(text);
- //int end = Selection.getSelectionEnd(text);
- //mWebView.setSelection(start, end);
- return true;
- }
- // If the user is in a textfield, and the movement method is not
- // handling the trackball events, it means they are at the end of the
- // field and continuing to move the trackball. In this case, we should
- // not scroll the cursor on screen bc the user may be attempting to
- // scroll the page, possibly in the opposite direction of the cursor.
- mScrollToAccommodateCursor = false;
- return false;
- }
-
- /**
- * Remove this TextDialog from its host WebView, and return
- * focus to the host.
- */
- /* package */ void remove() {
- // hide the soft keyboard when the edit text is out of focus
- InputMethodManager.getInstance(mContext).hideSoftInputFromWindow(
- getWindowToken(), 0);
- mWebView.removeView(this);
- mWebView.requestFocus();
- mScrollToAccommodateCursor = false;
- }
-
- /* package */ void enableScrollOnScreen(boolean enable) {
- mScrollToAccommodateCursor = enable;
- }
-
- /* package */ void bringIntoView() {
- if (getLayout() != null) {
- bringPointIntoView(Selection.getSelectionEnd(getText()));
- }
- }
-
- @Override
- public boolean requestRectangleOnScreen(Rect rectangle) {
- if (mScrollToAccommodateCursor) {
- return super.requestRectangleOnScreen(rectangle);
- }
- return false;
- }
-
- /**
- * Send the DOM events for the specified event.
- * @param event KeyEvent to be translated into a DOM event.
- */
- private void sendDomEvent(KeyEvent event) {
- mWebView.passToJavaScript(getText().toString(), event);
- }
-
- /**
- * Always use this instead of setAdapter, as this has features specific to
- * the TextDialog.
- */
- public void setAdapterCustom(AutoCompleteAdapter adapter) {
- if (adapter != null) {
- setInputType(EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- adapter.setTextView(this);
- }
- super.setAdapter(adapter);
- }
-
- /**
- * This is a special version of ArrayAdapter which changes its text size
- * to match the text size of its host TextView.
- */
- public static class AutoCompleteAdapter extends ArrayAdapter<String> {
- private TextView mTextView;
-
- public AutoCompleteAdapter(Context context, ArrayList<String> entries) {
- super(context, com.android.internal.R.layout
- .search_dropdown_item_1line, entries);
- }
-
- /**
- * {@inheritDoc}
- */
- public View getView(int position, View convertView, ViewGroup parent) {
- TextView tv =
- (TextView) super.getView(position, convertView, parent);
- if (tv != null && mTextView != null) {
- tv.setTextSize(mTextView.getTextSize());
- }
- return tv;
- }
-
- /**
- * Set the TextView so we can match its text size.
- */
- private void setTextView(TextView tv) {
- mTextView = tv;
- }
- }
-
- /**
- * Determine whether to use the system-wide password disguising method,
- * or to use none.
- * @param inPassword True if the textfield is a password field.
- */
- /* package */ void setInPassword(boolean inPassword) {
- if (inPassword) {
- setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
- TYPE_TEXT_VARIATION_PASSWORD);
- }
- }
-
- /* package */ void setMaxLength(int maxLength) {
- mMaxLength = maxLength;
- if (-1 == maxLength) {
- setFilters(NO_FILTERS);
- } else {
- setFilters(new InputFilter[] {
- new InputFilter.LengthFilter(maxLength) });
- }
- }
-
- /**
- * Set the pointer for this node so it can be determined which node this
- * TextDialog represents.
- * @param ptr Integer representing the pointer to the node which this
- * TextDialog represents.
- */
- /* package */ void setNodePointer(int ptr) {
- mNodePointer = ptr;
- }
-
- /**
- * Determine the position and size of TextDialog, and add it to the
- * WebView's view heirarchy. All parameters are presumed to be in
- * view coordinates. Also requests Focus and sets the cursor to not
- * request to be in view.
- * @param x x-position of the textfield.
- * @param y y-position of the textfield.
- * @param width width of the textfield.
- * @param height height of the textfield.
- */
- /* package */ void setRect(int x, int y, int width, int height) {
- LayoutParams lp = (LayoutParams) getLayoutParams();
- if (null == lp) {
- lp = new LayoutParams(width, height, x, y);
- } else {
- lp.x = x;
- lp.y = y;
- lp.width = width;
- lp.height = height;
- }
- if (getParent() == null) {
- mWebView.addView(this, lp);
- } else {
- setLayoutParams(lp);
- }
- // Set up a measure spec so a layout can always be recreated.
- mWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
- mHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
- requestFocus();
- }
-
- /**
- * Set whether this is a single-line textfield or a multi-line textarea.
- * Textfields scroll horizontally, and do not handle the enter key.
- * Textareas behave oppositely.
- * Do NOT call this after calling setInPassword(true). This will result in
- * removing the password input type.
- */
- public void setSingleLine(boolean single) {
- int inputType = EditorInfo.TYPE_CLASS_TEXT
- | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
- if (!single) {
- inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
- | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
- | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
- }
- mSingle = single;
- setHorizontallyScrolling(single);
- setInputType(inputType);
- }
-
- /**
- * Set the text for this TextDialog, and set the selection to (start, end)
- * @param text Text to go into this TextDialog.
- * @param start Beginning of the selection.
- * @param end End of the selection.
- */
- /* package */ void setText(CharSequence text, int start, int end) {
- mPreChange = text.toString();
- setText(text);
- Spannable span = (Spannable) getText();
- int length = span.length();
- if (end > length) {
- end = length;
- }
- if (start < 0) {
- start = 0;
- } else if (start > length) {
- start = length;
- }
- Selection.setSelection(span, start, end);
- }
-
- /**
- * Set the text to the new string, but use the old selection, making sure
- * to keep it within the new string.
- * @param text The new text to place in the textfield.
- */
- /* package */ void setTextAndKeepSelection(String text) {
- mPreChange = text.toString();
- Editable edit = (Editable) getText();
- edit.replace(0, edit.length(), text);
- updateCachedTextfield();
- }
-
- /**
- * Update the cache to reflect the current text.
- */
- /* package */ void updateCachedTextfield() {
- mWebView.updateCachedTextfield(getText().toString());
- }
-}
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index 9889fe9..232ed36 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -61,7 +61,7 @@ public final class URLUtil {
webAddress = new WebAddress(inUrl);
} catch (ParseException ex) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.URL_UTIL) {
Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
}
return retVal;
@@ -126,6 +126,32 @@ public final class URLUtil {
return retData;
}
+ /**
+ * @return True iff the url is correctly URL encoded
+ */
+ static boolean verifyURLEncoding(String url) {
+ int count = url.length();
+ if (count == 0) {
+ return false;
+ }
+
+ int index = url.indexOf('%');
+ while (index >= 0 && index < count) {
+ if (index < count - 2) {
+ try {
+ parseHex((byte) url.charAt(++index));
+ parseHex((byte) url.charAt(++index));
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ index = url.indexOf('%', index + 1);
+ }
+ return true;
+ }
+
private static int parseHex(byte b) {
if (b >= '0' && b <= '9') return (b - '0');
if (b >= 'A' && b <= 'F') return (b - 'A' + 10);
@@ -146,6 +172,7 @@ public final class URLUtil {
* requests from a file url.
* @deprecated Cookieless proxy is no longer supported.
*/
+ @Deprecated
public static boolean isCookielessProxyUrl(String url) {
return (null != url) && url.startsWith(PROXY_BASE);
}
diff --git a/core/java/android/webkit/UrlInterceptHandler.java b/core/java/android/webkit/UrlInterceptHandler.java
index 9216413..78bab04 100644
--- a/core/java/android/webkit/UrlInterceptHandler.java
+++ b/core/java/android/webkit/UrlInterceptHandler.java
@@ -20,6 +20,11 @@ import android.webkit.CacheManager.CacheResult;
import android.webkit.PluginData;
import java.util.Map;
+/**
+ * @deprecated This interface was inteded to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+@Deprecated
public interface UrlInterceptHandler {
/**
@@ -30,8 +35,8 @@ public interface UrlInterceptHandler {
* @param url URL string.
* @param headers The headers associated with the request. May be null.
* @return The CacheResult containing the surrogate response.
- * @Deprecated Use PluginData getPluginData(String url,
- * Map<String, String> headers); instead
+ *
+ * @deprecated Do not use, this interface is deprecated.
*/
@Deprecated
public CacheResult service(String url, Map<String, String> headers);
@@ -44,6 +49,9 @@ public interface UrlInterceptHandler {
* @param url URL string.
* @param headers The headers associated with the request. May be null.
* @return The PluginData containing the surrogate response.
+ *
+ * @deprecated Do not use, this interface is deprecated.
*/
+ @Deprecated
public PluginData getPluginData(String url, Map<String, String> headers);
}
diff --git a/core/java/android/webkit/UrlInterceptRegistry.java b/core/java/android/webkit/UrlInterceptRegistry.java
index 6051f29..eca5acd 100644
--- a/core/java/android/webkit/UrlInterceptRegistry.java
+++ b/core/java/android/webkit/UrlInterceptRegistry.java
@@ -24,6 +24,11 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
+/**
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
+ */
+@Deprecated
public final class UrlInterceptRegistry {
private final static String LOGTAG = "intercept";
@@ -42,7 +47,11 @@ public final class UrlInterceptRegistry {
* set the flag to control whether url intercept is enabled or disabled
*
* @param disabled true to disable the cache
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public static synchronized void setUrlInterceptDisabled(boolean disabled) {
mDisabled = disabled;
}
@@ -51,7 +60,11 @@ public final class UrlInterceptRegistry {
* get the state of the url intercept, enabled or disabled
*
* @return return if it is disabled
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public static synchronized boolean urlInterceptDisabled() {
return mDisabled;
}
@@ -62,7 +75,11 @@ public final class UrlInterceptRegistry {
*
* @param handler The new UrlInterceptHandler object
* @return true if the handler was not previously registered.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public static synchronized boolean registerHandler(
UrlInterceptHandler handler) {
if (!getHandlers().contains(handler)) {
@@ -78,7 +95,11 @@ public final class UrlInterceptRegistry {
*
* @param handler A previously registered UrlInterceptHandler.
* @return true if the handler was found and removed from the list.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public static synchronized boolean unregisterHandler(
UrlInterceptHandler handler) {
return getHandlers().remove(handler);
@@ -89,8 +110,9 @@ public final class UrlInterceptRegistry {
* UrlInterceptHandler interested, or null if none are.
*
* @return A CacheResult containing surrogate content.
- * @Deprecated Use PluginData getPluginData( String url,
- * Map<String, String> headers) instead.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
@Deprecated
public static synchronized CacheResult getSurrogate(
@@ -115,7 +137,11 @@ public final class UrlInterceptRegistry {
* intercepts are disabled.
*
* @return A PluginData instance containing surrogate content.
+ *
+ * @deprecated This class was intended to be used by Gears. Since Gears was
+ * deprecated, so is this class.
*/
+ @Deprecated
public static synchronized PluginData getPluginData(
String url, Map<String, String> headers) {
if (urlInterceptDisabled()) {
diff --git a/core/java/android/webkit/ViewManager.java b/core/java/android/webkit/ViewManager.java
new file mode 100644
index 0000000..6a838c3
--- /dev/null
+++ b/core/java/android/webkit/ViewManager.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.view.View;
+import android.widget.AbsoluteLayout;
+
+import java.util.ArrayList;
+
+class ViewManager {
+ private final WebView mWebView;
+ private final ArrayList<ChildView> mChildren = new ArrayList<ChildView>();
+ private boolean mHidden;
+
+ class ChildView {
+ int x;
+ int y;
+ int width;
+ int height;
+ View mView; // generic view to show
+
+ ChildView() {
+ }
+
+ void setBounds(int x, int y, int width, int height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ void attachView(int x, int y, int width, int height) {
+ if (mView == null) {
+ return;
+ }
+ setBounds(x, y, width, height);
+ final AbsoluteLayout.LayoutParams lp =
+ new AbsoluteLayout.LayoutParams(ctvD(width), ctvD(height),
+ ctvX(x), ctvY(y));
+ mWebView.mPrivateHandler.post(new Runnable() {
+ public void run() {
+ // This method may be called multiple times. If the view is
+ // already attached, just set the new LayoutParams,
+ // otherwise attach the view and add it to the list of
+ // children.
+ if (mView.getParent() != null) {
+ mView.setLayoutParams(lp);
+ } else {
+ attachViewOnUIThread(lp);
+ }
+ }
+ });
+ }
+
+ void attachViewOnUIThread(AbsoluteLayout.LayoutParams lp) {
+ mWebView.addView(mView, lp);
+ mChildren.add(this);
+ }
+
+ void removeView() {
+ if (mView == null) {
+ return;
+ }
+ mWebView.mPrivateHandler.post(new Runnable() {
+ public void run() {
+ removeViewOnUIThread();
+ }
+ });
+ }
+
+ void removeViewOnUIThread() {
+ mWebView.removeView(mView);
+ mChildren.remove(this);
+ }
+ }
+
+ ViewManager(WebView w) {
+ mWebView = w;
+ }
+
+ ChildView createView() {
+ return new ChildView();
+ }
+
+ /**
+ * Shorthand for calling mWebView.contentToViewDimension. Used when
+ * obtaining a view dimension from a content dimension, whether it be in x
+ * or y.
+ */
+ private int ctvD(int val) {
+ return mWebView.contentToViewDimension(val);
+ }
+
+ /**
+ * Shorthand for calling mWebView.contentToViewX. Used when obtaining a
+ * view x coordinate from a content x coordinate.
+ */
+ private int ctvX(int val) {
+ return mWebView.contentToViewX(val);
+ }
+
+ /**
+ * Shorthand for calling mWebView.contentToViewY. Used when obtaining a
+ * view y coordinate from a content y coordinate.
+ */
+ private int ctvY(int val) {
+ return mWebView.contentToViewY(val);
+ }
+
+ void scaleAll() {
+ for (ChildView v : mChildren) {
+ View view = v.mView;
+ AbsoluteLayout.LayoutParams lp =
+ (AbsoluteLayout.LayoutParams) view.getLayoutParams();
+ lp.width = ctvD(v.width);
+ lp.height = ctvD(v.height);
+ lp.x = ctvX(v.x);
+ lp.y = ctvY(v.y);
+ view.setLayoutParams(lp);
+ }
+ }
+
+ void hideAll() {
+ if (mHidden) {
+ return;
+ }
+ for (ChildView v : mChildren) {
+ v.mView.setVisibility(View.GONE);
+ }
+ mHidden = true;
+ }
+
+ void showAll() {
+ if (!mHidden) {
+ return;
+ }
+ for (ChildView v : mChildren) {
+ v.mView.setVisibility(View.VISIBLE);
+ }
+ mHidden = false;
+ }
+}
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index ffd6a11..62a5531 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -137,7 +137,7 @@ public class WebBackForwardList implements Cloneable, Serializable {
// when removing the first item, we can assert that the index is 0.
// This lets us change the current index without having to query the
// native BackForwardList.
- if (WebView.DEBUG && (index != 0)) {
+ if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) {
throw new AssertionError();
}
final WebHistoryItem h = mArray.remove(index);
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 9d9763c..6421fe7 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -18,6 +18,7 @@ package android.webkit;
import android.graphics.Bitmap;
import android.os.Message;
+import android.view.View;
public class WebChromeClient {
@@ -44,6 +45,47 @@ public class WebChromeClient {
public void onReceivedIcon(WebView view, Bitmap icon) {}
/**
+ * Notify the host application of the url for an apple-touch-icon.
+ * @param view The WebView that initiated the callback.
+ * @param url The icon url.
+ * @hide pending council approval
+ */
+ public void onReceivedTouchIconUrl(WebView view, String url) {}
+
+ /**
+ * A callback interface used by the host application to notify
+ * the current page that its custom view has been dismissed.
+ *
+ * @hide pending council approval
+ */
+ public interface CustomViewCallback {
+ /**
+ * Invoked when the host application dismisses the
+ * custom view.
+ */
+ public void onCustomViewHidden();
+ }
+
+ /**
+ * Notify the host application that the current page would
+ * like to show a custom View.
+ * @param view is the View object to be shown.
+ * @param callback is the callback to be invoked if and when the view
+ * is dismissed.
+ *
+ * @hide pending council approval
+ */
+ public void onShowCustomView(View view, CustomViewCallback callback) {};
+
+ /**
+ * Notify the host application that the current page would
+ * like to hide its custom view.
+ *
+ * @hide pending council approval
+ */
+ public void onHideCustomView() {}
+
+ /**
* Request the host application to create a new Webview. The host
* application should handle placement of the new WebView in the view
* system. The default behavior returns null.
@@ -158,6 +200,55 @@ public class WebChromeClient {
return false;
}
+ /**
+ * Tell the client that the database quota for the origin has been exceeded.
+ * @param url The URL that triggered the notification
+ * @param databaseIdentifier The identifier of the database that caused the
+ * quota overflow.
+ * @param currentQuota The current quota for the origin.
+ * @param estimatedSize The estimated size of the database.
+ * @param totalUsedQuota is the sum of all origins' quota.
+ * @param quotaUpdater A callback to inform the WebCore thread that a new
+ * quota is available. This callback must always be executed at some
+ * point to ensure that the sleeping WebCore thread is woken up.
+ */
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier,
+ long currentQuota, long estimatedSize, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ // This default implementation passes the current quota back to WebCore.
+ // WebCore will interpret this that new quota was declined.
+ quotaUpdater.updateQuota(currentQuota);
+ }
+
+ /**
+ * Tell the client that the Application Cache has exceeded its max size.
+ * @param spaceNeeded is the amount of disk space that would be needed
+ * in order for the last appcache operation to succeed.
+ * @param totalUsedQuota is the sum of all origins' quota.
+ * @param quotaUpdater A callback to inform the WebCore thread that a new
+ * app cache size is available. This callback must always be executed at
+ * some point to ensure that the sleeping WebCore thread is woken up.
+ * @hide pending API council approval.
+ */
+ public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ quotaUpdater.updateQuota(0);
+ }
+
+ /**
+ * Instructs the client to show a prompt to ask the user to set the
+ * Geolocation permission state for the specified origin.
+ * @hide pending API council approval.
+ */
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {}
+
+ /**
+ * Instructs the client to hide the Geolocation permissions prompt.
+ * @hide pending API council approval.
+ */
+ public void onGeolocationPermissionsHidePrompt() {}
+
/**
* Tell the client that a JavaScript execution timeout has occured. And the
* client may decide whether or not to interrupt the execution. If the
@@ -172,4 +263,14 @@ public class WebChromeClient {
public boolean onJsTimeout() {
return true;
}
+
+ /**
+ * Add a JavaScript error message to the console. Clients should override
+ * this to process the log message as they see fit.
+ * @param message The error message to report.
+ * @param lineNumber The line number of the error.
+ * @param sourceID The name of the source file that caused the error.
+ * @hide pending API council.
+ */
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {}
}
diff --git a/core/java/android/webkit/WebHistoryItem.java b/core/java/android/webkit/WebHistoryItem.java
index fd26b98..abd8237 100644
--- a/core/java/android/webkit/WebHistoryItem.java
+++ b/core/java/android/webkit/WebHistoryItem.java
@@ -39,6 +39,8 @@ public class WebHistoryItem implements Cloneable {
private Bitmap mFavicon;
// The pre-flattened data used for saving the state.
private byte[] mFlattenedData;
+ // The apple-touch-icon url for use when adding the site to the home screen
+ private String mTouchIconUrl;
/**
* Basic constructor that assigns a unique id to the item. Called by JNI
@@ -127,6 +129,14 @@ public class WebHistoryItem implements Cloneable {
}
/**
+ * Return the touch icon url.
+ * @hide
+ */
+ public String getTouchIconUrl() {
+ return mTouchIconUrl;
+ }
+
+ /**
* Set the favicon.
* @param icon A Bitmap containing the favicon for this history item.
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
@@ -137,6 +147,14 @@ public class WebHistoryItem implements Cloneable {
}
/**
+ * Set the touch icon url.
+ * @hide
+ */
+ /*package*/ void setTouchIconUrl(String url) {
+ mTouchIconUrl = url;
+ }
+
+ /**
* Get the pre-flattened data.
* Note: The VM ensures 32-bit atomic read/write operations so we don't have
* to synchronize this method.
diff --git a/core/java/android/webkit/WebIconDatabase.java b/core/java/android/webkit/WebIconDatabase.java
index d284f5e..6cc6bb4 100644
--- a/core/java/android/webkit/WebIconDatabase.java
+++ b/core/java/android/webkit/WebIconDatabase.java
@@ -37,7 +37,7 @@ public final class WebIconDatabase {
private final EventHandler mEventHandler = new EventHandler();
// Class to handle messages before WebCore is ready
- private class EventHandler extends Handler {
+ private static class EventHandler extends Handler {
// Message ids
static final int OPEN = 0;
static final int CLOSE = 1;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index a038b55..196bbd7 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -22,9 +22,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.provider.Checkin;
-
import java.lang.SecurityException;
-
import java.util.Locale;
/**
@@ -130,9 +128,13 @@ public class WebSettings {
private boolean mSyncPending = false;
// Custom handler that queues messages until the WebCore thread is active.
private final EventHandler mEventHandler;
+
// Private settings so we don't have to go into native code to
// retrieve the values. After setXXX, postSync() needs to be called.
- // XXX: The default values need to match those in WebSettings.cpp
+ //
+ // The default values need to match those in WebSettings.cpp
+ // If the defaults change, please also update the JavaDocs so developers
+ // know what they are.
private LayoutAlgorithm mLayoutAlgorithm = LayoutAlgorithm.NARROW_COLUMNS;
private Context mContext;
private TextSize mTextSize = TextSize.NORMAL;
@@ -146,7 +148,6 @@ public class WebSettings {
private String mUserAgent;
private boolean mUseDefaultUserAgent;
private String mAcceptLanguage;
- private String mPluginsPath = "";
private int mMinimumFontSize = 8;
private int mMinimumLogicalFontSize = 8;
private int mDefaultFontSize = 16;
@@ -161,6 +162,17 @@ public class WebSettings {
private boolean mUseWideViewport = false;
private boolean mSupportMultipleWindows = false;
private boolean mShrinksStandaloneImagesToFit = false;
+ // HTML5 API flags
+ private boolean mAppCacheEnabled = false;
+ private boolean mDatabaseEnabled = false;
+ private boolean mDomStorageEnabled = false;
+ private boolean mWorkersEnabled = false; // only affects V8.
+ private boolean mGeolocationEnabled = true;
+ // HTML5 configuration parameters
+ private long mAppCacheMaxSize = Long.MAX_VALUE;
+ private String mAppCachePath = "";
+ private String mDatabasePath = "";
+ private String mGeolocationDatabasePath = "";
// Don't need to synchronize the get/set methods as they
// are basic types, also none of these values are used in
// native WebCore code.
@@ -175,9 +187,11 @@ public class WebSettings {
private boolean mSupportZoom = true;
private boolean mBuiltInZoomControls = false;
private boolean mAllowFileAccess = true;
+ private boolean mLoadWithOverviewMode = false;
- // The Gears permissions manager. Only in Donut.
- static GearsPermissionsManager sGearsPermissionsManager;
+ // Manages interaction of the system setting 'Location & security - Share
+ // with Google' and the browser.
+ static GoogleLocationSettingManager sGoogleLocationSettingManager;
// Class to handle messages before WebCore is ready.
private class EventHandler {
@@ -199,7 +213,6 @@ public class WebSettings {
switch (msg.what) {
case SYNC:
synchronized (WebSettings.this) {
- checkGearsPermissions();
if (mBrowserFrame.mNativeFrame != 0) {
nativeSync(mBrowserFrame.mNativeFrame);
}
@@ -247,13 +260,13 @@ public class WebSettings {
// User agent strings.
private static final String DESKTOP_USERAGENT =
- "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en)"
- + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
- + " Safari/525.20.1";
+ "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
+ + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
+ + " Safari/530.17";
private static final String IPHONE_USERAGENT =
- "Mozilla/5.0 (iPhone; U; CPU iPhone 2_1 like Mac OS X; en)"
- + " AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2"
- + " Mobile/5F136 Safari/525.20.1";
+ "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
+ + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
+ + " Mobile/7A341 Safari/528.16";
private static Locale sLocale;
private static Object sLockForLocaleSettings;
@@ -347,11 +360,13 @@ public class WebSettings {
// default to "en"
buffer.append("en");
}
-
- final String model = Build.MODEL;
- if (model.length() > 0) {
- buffer.append("; ");
- buffer.append(model);
+ // add the model for the release build
+ if ("REL".equals(Build.VERSION.CODENAME)) {
+ final String model = Build.MODEL;
+ if (model.length() > 0) {
+ buffer.append("; ");
+ buffer.append(model);
+ }
}
final String id = Build.ID;
if (id.length() > 0) {
@@ -421,6 +436,22 @@ public class WebSettings {
}
/**
+ * Set whether the WebView loads a page with overview mode.
+ * @hide Pending API council approval
+ */
+ public void setLoadWithOverviewMode(boolean overview) {
+ mLoadWithOverviewMode = overview;
+ }
+
+ /**
+ * Returns true if this WebView loads page with overview mode
+ * @hide Pending API council approval
+ */
+ public boolean getLoadWithOverviewMode() {
+ return mLoadWithOverviewMode;
+ }
+
+ /**
* Store whether the WebView is saving form data.
*/
public void setSaveFormData(boolean save) {
@@ -511,24 +542,21 @@ public class WebSettings {
}
/**
- * Tell the WebView to use the double tree rendering algorithm.
- * @param use True if the WebView is to use double tree rendering, false
- * otherwise.
+ * @deprecated This setting controlled a rendering optimization
+ * that is no longer present. Setting it now has no effect.
*/
+ @Deprecated
public synchronized void setUseDoubleTree(boolean use) {
- if (mUseDoubleTree != use) {
- mUseDoubleTree = use;
- postSync();
- }
+ return;
}
/**
- * Return true if the WebView is using the double tree rendering algorithm.
- * @return True if the WebView is using the double tree rendering
- * algorithm.
+ * @deprecated This setting controlled a rendering optimization
+ * that is no longer present. Setting it now has no effect.
*/
+ @Deprecated
public synchronized boolean getUseDoubleTree() {
- return mUseDoubleTree;
+ return false;
}
/**
@@ -633,7 +661,7 @@ public class WebSettings {
}
/**
- * Return the current layout algorithm.
+ * Return the current layout algorithm. The default is NARROW_COLUMNS.
* @return LayoutAlgorithm enum value describing the layout algorithm
* being used.
* @see WebSettings.LayoutAlgorithm
@@ -654,7 +682,7 @@ public class WebSettings {
}
/**
- * Get the standard font family name.
+ * Get the standard font family name. The default is "sans-serif".
* @return The standard font family name as a string.
*/
public synchronized String getStandardFontFamily() {
@@ -673,7 +701,7 @@ public class WebSettings {
}
/**
- * Get the fixed font family name.
+ * Get the fixed font family name. The default is "monospace".
* @return The fixed font family name as a string.
*/
public synchronized String getFixedFontFamily() {
@@ -700,7 +728,7 @@ public class WebSettings {
}
/**
- * Set the serif font family name.
+ * Set the serif font family name. The default is "sans-serif".
* @param font A font family name.
*/
public synchronized void setSerifFontFamily(String font) {
@@ -711,7 +739,7 @@ public class WebSettings {
}
/**
- * Get the serif font family name.
+ * Get the serif font family name. The default is "serif".
* @return The serif font family name as a string.
*/
public synchronized String getSerifFontFamily() {
@@ -730,7 +758,7 @@ public class WebSettings {
}
/**
- * Get the cursive font family name.
+ * Get the cursive font family name. The default is "cursive".
* @return The cursive font family name as a string.
*/
public synchronized String getCursiveFontFamily() {
@@ -749,7 +777,7 @@ public class WebSettings {
}
/**
- * Get the fantasy font family name.
+ * Get the fantasy font family name. The default is "fantasy".
* @return The fantasy font family name as a string.
*/
public synchronized String getFantasyFontFamily() {
@@ -770,7 +798,7 @@ public class WebSettings {
}
/**
- * Get the minimum font size.
+ * Get the minimum font size. The default is 8.
* @return A non-negative integer between 1 and 72.
*/
public synchronized int getMinimumFontSize() {
@@ -791,7 +819,7 @@ public class WebSettings {
}
/**
- * Get the minimum logical font size.
+ * Get the minimum logical font size. The default is 8.
* @return A non-negative integer between 1 and 72.
*/
public synchronized int getMinimumLogicalFontSize() {
@@ -812,7 +840,7 @@ public class WebSettings {
}
/**
- * Get the default font size.
+ * Get the default font size. The default is 16.
* @return A non-negative integer between 1 and 72.
*/
public synchronized int getDefaultFontSize() {
@@ -833,7 +861,7 @@ public class WebSettings {
}
/**
- * Get the default fixed font size.
+ * Get the default fixed font size. The default is 16.
* @return A non-negative integer between 1 and 72.
*/
public synchronized int getDefaultFixedFontSize() {
@@ -853,6 +881,7 @@ public class WebSettings {
/**
* Return true if the WebView will load image resources automatically.
+ * The default is true.
* @return True if the WebView loads images automatically.
*/
public synchronized boolean getLoadsImagesAutomatically() {
@@ -872,16 +901,16 @@ public class WebSettings {
}
/**
- * Return true if the WebView will block network image.
+ * Return true if the WebView will block network image. The default is false.
* @return True if the WebView blocks network image.
*/
public synchronized boolean getBlockNetworkImage() {
return mBlockNetworkImage;
}
-
+
/**
* @hide
- * Tell the WebView to block all network load requests.
+ * Tell the WebView to block all network load requests.
* @param flag True if the WebView should block all network loads
*/
public synchronized void setBlockNetworkLoads(boolean flag) {
@@ -894,13 +923,14 @@ public class WebSettings {
/**
* @hide
* Return true if the WebView will block all network loads.
+ * The default is false.
* @return True if the WebView blocks all network loads.
*/
public synchronized boolean getBlockNetworkLoads() {
return mBlockNetworkLoads;
}
-
-
+
+
private void verifyNetworkAccess() {
if (!mBlockNetworkLoads) {
if (mContext.checkPermission("android.permission.INTERNET",
@@ -936,19 +966,158 @@ public class WebSettings {
}
/**
- * Set a custom path to plugins used by the WebView. The client
- * must ensure it exists before this call.
- * @param pluginsPath String path to the directory containing plugins.
+ * TODO: need to add @Deprecated
*/
public synchronized void setPluginsPath(String pluginsPath) {
- if (pluginsPath != null && !pluginsPath.equals(mPluginsPath)) {
- mPluginsPath = pluginsPath;
+ }
+
+ /**
+ * Set the path to where database storage API databases should be saved.
+ * This will update WebCore when the Sync runs in the C++ side.
+ * @param databasePath String path to the directory where databases should
+ * be saved. May be the empty string but should never be null.
+ */
+ public synchronized void setDatabasePath(String databasePath) {
+ if (databasePath != null && !databasePath.equals(mDatabasePath)) {
+ mDatabasePath = databasePath;
+ postSync();
+ }
+ }
+
+ /**
+ * Set the path where the Geolocation permissions database should be saved.
+ * This will update WebCore when the Sync runs in the C++ side.
+ * @param databasePath String path to the directory where the Geolocation
+ * permissions database should be saved. May be the empty string but
+ * should never be null.
+ * @hide pending api council approval
+ */
+ public synchronized void setGeolocationDatabasePath(String databasePath) {
+ if (databasePath != null && !databasePath.equals(mDatabasePath)) {
+ mGeolocationDatabasePath = databasePath;
+ postSync();
+ }
+ }
+
+ /**
+ * Tell the WebView to enable Application Caches API.
+ * @param flag True if the WebView should enable Application Caches.
+ * @hide pending api council approval
+ */
+ public synchronized void setAppCacheEnabled(boolean flag) {
+ if (mAppCacheEnabled != flag) {
+ mAppCacheEnabled = flag;
+ postSync();
+ }
+ }
+
+ /**
+ * Set a custom path to the Application Caches files. The client
+ * must ensure it exists before this call.
+ * @param appCachePath String path to the directory containing Application
+ * Caches files. The appCache path can be the empty string but should not
+ * be null. Passing null for this parameter will result in a no-op.
+ * @hide pending api council approval
+ */
+ public synchronized void setAppCachePath(String appCachePath) {
+ if (appCachePath != null && !appCachePath.equals(mAppCachePath)) {
+ mAppCachePath = appCachePath;
+ postSync();
+ }
+ }
+
+ /**
+ * Set the maximum size for the Application Caches content.
+ * @param appCacheMaxSize the maximum size in bytes.
+ *
+ * @hide pending api council approval
+ */
+ public synchronized void setAppCacheMaxSize(long appCacheMaxSize) {
+ if (appCacheMaxSize != mAppCacheMaxSize) {
+ mAppCacheMaxSize = appCacheMaxSize;
+ postSync();
+ }
+ }
+
+ /**
+ * Set whether the database storage API is enabled.
+ * @param flag boolean True if the WebView should use the database storage
+ * API.
+ */
+ public synchronized void setDatabaseEnabled(boolean flag) {
+ if (mDatabaseEnabled != flag) {
+ mDatabaseEnabled = flag;
+ postSync();
+ }
+ }
+
+ /**
+ * Set whether the DOM storage API is enabled.
+ * @param flag boolean True if the WebView should use the DOM storage
+ * API.
+ * @hide pending API council.
+ */
+ public synchronized void setDomStorageEnabled(boolean flag) {
+ if (mDomStorageEnabled != flag) {
+ mDomStorageEnabled = flag;
+ postSync();
+ }
+ }
+
+ /**
+ * Returns true if the DOM Storage API's are enabled.
+ * @return True if the DOM Storage API's are enabled.
+ * @hide pending API council.
+ */
+ public synchronized boolean getDomStorageEnabled() {
+ return mDomStorageEnabled;
+ }
+
+ /**
+ * Return the path to where database storage API databases are saved for
+ * the current WebView.
+ * @return the String path to the database storage API databases.
+ */
+ public synchronized String getDatabasePath() {
+ return mDatabasePath;
+ }
+
+ /**
+ * Returns true if database storage API is enabled.
+ * @return True if the database storage API is enabled.
+ */
+ public synchronized boolean getDatabaseEnabled() {
+ return mDatabaseEnabled;
+ }
+
+ /**
+ * Tell the WebView to enable WebWorkers API.
+ * @param flag True if the WebView should enable WebWorkers.
+ * Note that this flag only affects V8. JSC does not have
+ * an equivalent setting.
+ * @hide pending api council approval
+ */
+ public synchronized void setWorkersEnabled(boolean flag) {
+ if (mWorkersEnabled != flag) {
+ mWorkersEnabled = flag;
postSync();
}
}
/**
- * Return true if javascript is enabled.
+ * Sets whether Geolocation is enabled.
+ * @param flag Whether Geolocation should be enabled.
+ * @hide pending api council approval
+ */
+ public synchronized void setGeolocationEnabled(boolean flag) {
+ if (mGeolocationEnabled != flag) {
+ mGeolocationEnabled = flag;
+ postSync();
+ }
+ }
+
+ /**
+ * Return true if javascript is enabled. <b>Note: The default is false.</b>
* @return True if javascript is enabled.
*/
public synchronized boolean getJavaScriptEnabled() {
@@ -964,11 +1133,10 @@ public class WebSettings {
}
/**
- * Return the current path used for plugins in the WebView.
- * @return The string path to the WebView plugins.
+ * TODO: need to add @Deprecated
*/
public synchronized String getPluginsPath() {
- return mPluginsPath;
+ return "";
}
/**
@@ -985,7 +1153,8 @@ public class WebSettings {
}
/**
- * Return true if javascript can open windows automatically.
+ * Return true if javascript can open windows automatically. The default
+ * is false.
* @return True if javascript can open windows automatically during
* window.open().
*/
@@ -1005,7 +1174,7 @@ public class WebSettings {
}
/**
- * Get the default text encoding name.
+ * Get the default text encoding name. The default is "Latin-1".
* @return The default text encoding name as a string.
*/
public synchronized String getDefaultTextEncodingName() {
@@ -1094,8 +1263,8 @@ public class WebSettings {
/**
* Set the priority of the Render thread. Unlike the other settings, this
- * one only needs to be called once per process.
- *
+ * one only needs to be called once per process. The default is NORMAL.
+ *
* @param priority RenderPriority, can be normal, high or low.
*/
public synchronized void setRenderPriority(RenderPriority priority) {
@@ -1149,10 +1318,11 @@ public class WebSettings {
/*package*/
synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
mBrowserFrame = frame;
- if (WebView.DEBUG) {
+ if (DebugFlags.WEB_SETTINGS) {
junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
}
- checkGearsPermissions();
+ sGoogleLocationSettingManager = new GoogleLocationSettingManager(mContext);
+ sGoogleLocationSettingManager.start();
nativeSync(frame.mNativeFrame);
mSyncPending = false;
mEventHandler.createHandler();
@@ -1168,23 +1338,6 @@ public class WebSettings {
return size;
}
- private void checkGearsPermissions() {
- // Did we already check the permissions at startup?
- if (sGearsPermissionsManager != null) {
- return;
- }
- // Is the pluginsPath sane?
- String pluginsPath = getPluginsPath();
- if (pluginsPath == null || pluginsPath.length() == 0) {
- // We don't yet have a meaningful plugin path, so
- // we can't do anything about the Gears permissions.
- return;
- }
- sGearsPermissionsManager =
- new GearsPermissionsManager(mContext, pluginsPath);
- sGearsPermissionsManager.doCheckAndStartObserver();
- }
-
/* Post a SYNC message to handle syncing the native settings. */
private synchronized void postSync() {
// Only post if a sync is not pending
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
new file mode 100644
index 0000000..ae560fb
--- /dev/null
+++ b/core/java/android/webkit/WebStorage.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Functionality for manipulating the webstorage databases.
+ */
+public final class WebStorage {
+
+ /**
+ * Encapsulates a callback function to be executed when a new quota is made
+ * available. We primarily want this to allow us to call back the sleeping
+ * WebCore thread from outside the WebViewCore class (as the native call
+ * is private). It is imperative that this the setDatabaseQuota method is
+ * executed once a decision to either allow or deny new quota is made,
+ * otherwise the WebCore thread will remain asleep.
+ */
+ public interface QuotaUpdater {
+ public void updateQuota(long newQuota);
+ };
+
+ // Log tag
+ private static final String TAG = "webstorage";
+
+ // Global instance of a WebStorage
+ private static WebStorage sWebStorage;
+
+ // We keep the origins, quotas and usages as member values
+ // that we protect via a lock and update in syncValues().
+ // This is needed to transfer this data across threads.
+ private static Lock mLock = new ReentrantLock();
+ private static Condition mUpdateCondition = mLock.newCondition();
+ private static boolean mUpdateAvailable;
+
+ // Message ids
+ static final int UPDATE = 0;
+ static final int SET_QUOTA_ORIGIN = 1;
+ static final int DELETE_ORIGIN = 2;
+ static final int DELETE_ALL = 3;
+
+ private Set <String> mOrigins;
+ private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
+ private HashMap <String, Long> mUsages = new HashMap<String, Long>();
+
+ private Handler mHandler = null;
+
+ private static class Origin {
+ String mOrigin = null;
+ long mQuota = 0;
+
+ public Origin(String origin, long quota) {
+ mOrigin = origin;
+ mQuota = quota;
+ }
+
+ public Origin(String origin) {
+ mOrigin = origin;
+ }
+
+ public String getOrigin() {
+ return mOrigin;
+ }
+
+ public long getQuota() {
+ return mQuota;
+ }
+ }
+
+ /**
+ * @hide
+ * Message handler
+ */
+ public void createHandler() {
+ if (mHandler == null) {
+ mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SET_QUOTA_ORIGIN: {
+ Origin website = (Origin) msg.obj;
+ nativeSetQuotaForOrigin(website.getOrigin(),
+ website.getQuota());
+ } break;
+
+ case DELETE_ORIGIN: {
+ Origin website = (Origin) msg.obj;
+ nativeDeleteOrigin(website.getOrigin());
+ } break;
+
+ case DELETE_ALL:
+ nativeDeleteAllData();
+ break;
+
+ case UPDATE:
+ syncValues();
+ break;
+ }
+ }
+ };
+ }
+ }
+
+ /**
+ * @hide
+ * Returns a list of origins having a database
+ */
+ public Set getOrigins() {
+ Set ret = null;
+ mLock.lock();
+ try {
+ mUpdateAvailable = false;
+ update();
+ while (!mUpdateAvailable) {
+ mUpdateCondition.await();
+ }
+ ret = mOrigins;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Exception while waiting on the updated origins", e);
+ } finally {
+ mLock.unlock();
+ }
+ return ret;
+ }
+
+ /**
+ * @hide
+ * Returns the use for a given origin
+ */
+ public long getUsageForOrigin(String origin) {
+ long ret = 0;
+ if (origin == null) {
+ return ret;
+ }
+ mLock.lock();
+ try {
+ mUpdateAvailable = false;
+ update();
+ while (!mUpdateAvailable) {
+ mUpdateCondition.await();
+ }
+ Long usage = mUsages.get(origin);
+ if (usage != null) {
+ ret = usage.longValue();
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Exception while waiting on the updated origins", e);
+ } finally {
+ mLock.unlock();
+ }
+ return ret;
+ }
+
+ /**
+ * @hide
+ * Returns the quota for a given origin
+ */
+ public long getQuotaForOrigin(String origin) {
+ long ret = 0;
+ if (origin == null) {
+ return ret;
+ }
+ mLock.lock();
+ try {
+ mUpdateAvailable = false;
+ update();
+ while (!mUpdateAvailable) {
+ mUpdateCondition.await();
+ }
+ Long quota = mQuotas.get(origin);
+ if (quota != null) {
+ ret = quota.longValue();
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Exception while waiting on the updated origins", e);
+ } finally {
+ mLock.unlock();
+ }
+ return ret;
+ }
+
+ /**
+ * @hide
+ * Set the quota for a given origin
+ */
+ public void setQuotaForOrigin(String origin, long quota) {
+ if (origin != null) {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ nativeSetQuotaForOrigin(origin, quota);
+ } else {
+ postMessage(Message.obtain(null, SET_QUOTA_ORIGIN,
+ new Origin(origin, quota)));
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Delete a given origin
+ */
+ public void deleteOrigin(String origin) {
+ if (origin != null) {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ nativeDeleteOrigin(origin);
+ } else {
+ postMessage(Message.obtain(null, DELETE_ORIGIN,
+ new Origin(origin)));
+ }
+ }
+ }
+
+ /**
+ * @hide
+ * Delete all databases
+ */
+ public void deleteAllData() {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ nativeDeleteAllData();
+ } else {
+ postMessage(Message.obtain(null, DELETE_ALL));
+ }
+ }
+
+ /**
+ * Utility function to send a message to our handler
+ */
+ private void postMessage(Message msg) {
+ if (mHandler != null) {
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ /**
+ * @hide
+ * Get the global instance of WebStorage.
+ * @return A single instance of WebStorage.
+ */
+ public static WebStorage getInstance() {
+ if (sWebStorage == null) {
+ sWebStorage = new WebStorage();
+ }
+ return sWebStorage;
+ }
+
+ /**
+ * @hide
+ * Post a Sync request
+ */
+ public void update() {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ syncValues();
+ } else {
+ postMessage(Message.obtain(null, UPDATE));
+ }
+ }
+
+ /**
+ * Run on the webcore thread
+ * set the local values with the current ones
+ */
+ private void syncValues() {
+ mLock.lock();
+ Set tmp = nativeGetOrigins();
+ mOrigins = new HashSet<String>();
+ mQuotas.clear();
+ mUsages.clear();
+ Iterator<String> iter = tmp.iterator();
+ while (iter.hasNext()) {
+ String origin = iter.next();
+ mOrigins.add(origin);
+ mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
+ mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
+ }
+ mUpdateAvailable = true;
+ mUpdateCondition.signal();
+ mLock.unlock();
+ }
+
+ // Native functions
+ private static native Set nativeGetOrigins();
+ private static native long nativeGetUsageForOrigin(String origin);
+ private static native long nativeGetQuotaForOrigin(String origin);
+ private static native void nativeSetQuotaForOrigin(String origin, long quota);
+ private static native void nativeDeleteOrigin(String origin);
+ private static native void nativeDeleteAllData();
+}
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index ded17ed..d3ec603 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -47,7 +47,7 @@ abstract class WebSyncManager implements Runnable {
@Override
public void handleMessage(Message msg) {
if (msg.what == SYNC_MESSAGE) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager sync ***");
}
syncFromRamToFlash();
@@ -94,7 +94,7 @@ abstract class WebSyncManager implements Runnable {
* sync() forces sync manager to sync now
*/
public void sync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager sync ***");
}
if (mHandler == null) {
@@ -109,7 +109,7 @@ abstract class WebSyncManager implements Runnable {
* resetSync() resets sync manager's timer
*/
public void resetSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager resetSync ***");
}
if (mHandler == null) {
@@ -124,7 +124,7 @@ abstract class WebSyncManager implements Runnable {
* startSync() requests sync manager to start sync
*/
public void startSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager startSync ***, Ref count:" +
mStartSyncRefCount);
}
@@ -142,7 +142,7 @@ abstract class WebSyncManager implements Runnable {
* the queue to break the sync loop
*/
public void stopSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager stopSync ***, Ref count:" +
mStartSyncRefCount);
}
diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java
new file mode 100644
index 0000000..95b3a12
--- /dev/null
+++ b/core/java/android/webkit/WebTextView.java
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.method.MovementMethod;
+import android.text.method.Touch;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputConnection;
+import android.widget.AbsoluteLayout.LayoutParams;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * WebTextView is a specialized version of EditText used by WebView
+ * to overlay html textfields (and textareas) to use our standard
+ * text editing.
+ */
+/* package */ class WebTextView extends AutoCompleteTextView {
+
+ static final String LOGTAG = "webtextview";
+
+ private WebView mWebView;
+ private boolean mSingle;
+ private int mWidthSpec;
+ private int mHeightSpec;
+ private int mNodePointer;
+ // FIXME: This is a hack for blocking unmatched key ups, in particular
+ // on the enter key. The method for blocking unmatched key ups prevents
+ // the shift key from working properly.
+ private boolean mGotEnterDown;
+ private int mMaxLength;
+ // Keep track of the text before the change so we know whether we actually
+ // need to send down the DOM events.
+ private String mPreChange;
+ private Drawable mBackground;
+ // Variables for keeping track of the touch down, to send to the WebView
+ // when a drag starts
+ private float mDragStartX;
+ private float mDragStartY;
+ private long mDragStartTime;
+ private boolean mDragSent;
+ // True if the most recent drag event has caused either the TextView to
+ // scroll or the web page to scroll. Gets reset after a touch down.
+ private boolean mScrolled;
+ // Gets set to true when the the IME jumps to the next textfield. When this
+ // happens, the next time the user hits a key it is okay for the focus
+ // pointer to not match the WebTextView's node pointer
+ private boolean mOkayForFocusNotToMatch;
+ // Whether or not a selection change was generated from webkit. If it was,
+ // we do not need to pass the selection back to webkit.
+ private boolean mFromWebKit;
+ private boolean mGotTouchDown;
+ // Array to store the final character added in onTextChanged, so that its
+ // KeyEvents may be determined.
+ private char[] mCharacter = new char[1];
+ // This is used to reset the length filter when on a textfield
+ // with no max length.
+ // FIXME: This can be replaced with TextView.NO_FILTERS if that
+ // is made public/protected.
+ private static final InputFilter[] NO_FILTERS = new InputFilter[0];
+
+ /**
+ * Create a new WebTextView.
+ * @param context The Context for this WebTextView.
+ * @param webView The WebView that created this.
+ */
+ /* package */ WebTextView(Context context, WebView webView) {
+ super(context);
+ mWebView = webView;
+ mMaxLength = -1;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.isSystem()) {
+ return super.dispatchKeyEvent(event);
+ }
+ // Treat ACTION_DOWN and ACTION MULTIPLE the same
+ boolean down = event.getAction() != KeyEvent.ACTION_UP;
+ int keyCode = event.getKeyCode();
+
+ boolean isArrowKey = false;
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (!mWebView.nativeCursorMatchesFocus()) {
+ return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+ .onKeyUp(keyCode, event);
+
+ }
+ isArrowKey = true;
+ break;
+ }
+ if (!isArrowKey && !mOkayForFocusNotToMatch
+ && mWebView.nativeFocusNodePointer() != mNodePointer) {
+ mWebView.nativeClearCursor();
+ // Do not call remove() here, which hides the soft keyboard. If
+ // the soft keyboard is being displayed, the user will still want
+ // it there.
+ mWebView.removeView(this);
+ mWebView.requestFocus();
+ return mWebView.dispatchKeyEvent(event);
+ }
+ // After a jump to next textfield and the first key press, the cursor
+ // and focus will once again match, so reset this value.
+ mOkayForFocusNotToMatch = false;
+
+ Spannable text = (Spannable) getText();
+ int oldLength = text.length();
+ // Normally the delete key's dom events are sent via onTextChanged.
+ // However, if the length is zero, the text did not change, so we
+ // go ahead and pass the key down immediately.
+ if (KeyEvent.KEYCODE_DEL == keyCode && 0 == oldLength) {
+ sendDomEvent(event);
+ return true;
+ }
+
+ if ((mSingle && KeyEvent.KEYCODE_ENTER == keyCode)) {
+ if (isPopupShowing()) {
+ return super.dispatchKeyEvent(event);
+ }
+ if (!down) {
+ // Hide the keyboard, since the user has just submitted this
+ // form. The submission happens thanks to the two calls
+ // to sendDomEvent.
+ InputMethodManager.getInstance(mContext)
+ .hideSoftInputFromWindow(getWindowToken(), 0);
+ sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ sendDomEvent(event);
+ }
+ return super.dispatchKeyEvent(event);
+ } else if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
+ // Note that this handles center key and trackball.
+ if (isPopupShowing()) {
+ return super.dispatchKeyEvent(event);
+ }
+ if (!mWebView.nativeCursorMatchesFocus()) {
+ return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+ .onKeyUp(keyCode, event);
+ }
+ // Center key should be passed to a potential onClick
+ if (!down) {
+ mWebView.shortPressOnTextField();
+ }
+ // Pass to super to handle longpress.
+ return super.dispatchKeyEvent(event);
+ }
+
+ // Ensure there is a layout so arrow keys are handled properly.
+ if (getLayout() == null) {
+ measure(mWidthSpec, mHeightSpec);
+ }
+ int oldStart = Selection.getSelectionStart(text);
+ int oldEnd = Selection.getSelectionEnd(text);
+
+ boolean maxedOut = mMaxLength != -1 && oldLength == mMaxLength;
+ // If we are at max length, and there is a selection rather than a
+ // cursor, we need to store the text to compare later, since the key
+ // may have changed the string.
+ String oldText;
+ if (maxedOut && oldEnd != oldStart) {
+ oldText = text.toString();
+ } else {
+ oldText = "";
+ }
+ if (super.dispatchKeyEvent(event)) {
+ // If the WebTextView handled the key it was either an alphanumeric
+ // key, a delete, or a movement within the text. All of those are
+ // ok to pass to javascript.
+
+ // UNLESS there is a max length determined by the html. In that
+ // case, if the string was already at the max length, an
+ // alphanumeric key will be erased by the LengthFilter,
+ // so do not pass down to javascript, and instead
+ // return true. If it is an arrow key or a delete key, we can go
+ // ahead and pass it down.
+ if (KeyEvent.KEYCODE_ENTER == keyCode) {
+ // For multi-line text boxes, newlines will
+ // trigger onTextChanged for key down (which will send both
+ // key up and key down) but not key up.
+ mGotEnterDown = true;
+ }
+ if (maxedOut && !isArrowKey && keyCode != KeyEvent.KEYCODE_DEL) {
+ if (oldEnd == oldStart) {
+ // Return true so the key gets dropped.
+ return true;
+ } else if (!oldText.equals(getText().toString())) {
+ // FIXME: This makes the text work properly, but it
+ // does not pass down the key event, so it may not
+ // work for a textfield that has the type of
+ // behavior of GoogleSuggest. That said, it is
+ // unlikely that a site would combine the two in
+ // one textfield.
+ Spannable span = (Spannable) getText();
+ int newStart = Selection.getSelectionStart(span);
+ int newEnd = Selection.getSelectionEnd(span);
+ mWebView.replaceTextfieldText(0, oldLength, span.toString(),
+ newStart, newEnd);
+ return true;
+ }
+ }
+ /* FIXME:
+ * In theory, we would like to send the events for the arrow keys.
+ * However, the TextView can arbitrarily change the selection (i.e.
+ * long press followed by using the trackball). Therefore, we keep
+ * in sync with the TextView via onSelectionChanged. If we also
+ * send the DOM event, we lose the correct selection.
+ if (isArrowKey) {
+ // Arrow key does not change the text, but we still want to send
+ // the DOM events.
+ sendDomEvent(event);
+ }
+ */
+ return true;
+ }
+ // Ignore the key up event for newlines. This prevents
+ // multiple newlines in the native textarea.
+ if (mGotEnterDown && !down) {
+ return true;
+ }
+ // if it is a navigation key, pass it to WebView
+ if (isArrowKey) {
+ // WebView check the trackballtime in onKeyDown to avoid calling
+ // native from both trackball and key handling. As this is called
+ // from WebTextView, we always want WebView to check with native.
+ // Reset trackballtime to ensure it.
+ mWebView.resetTrackballTime();
+ return down ? mWebView.onKeyDown(keyCode, event) : mWebView
+ .onKeyUp(keyCode, event);
+ }
+ return false;
+ }
+
+ /**
+ * Create a fake touch up event at (x,y) with respect to this WebTextView.
+ * This is used by WebView to act as though a touch event which happened
+ * before we placed the WebTextView actually hit it, so that it can place
+ * the cursor accordingly.
+ */
+ /* package */ void fakeTouchEvent(float x, float y) {
+ // We need to ensure that there is a Layout, since the Layout is used
+ // in determining where to place the cursor.
+ if (getLayout() == null) {
+ measure(mWidthSpec, mHeightSpec);
+ }
+ // Create a fake touch up, which is used to place the cursor.
+ MotionEvent ev = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP,
+ x, y, 0);
+ onTouchEvent(ev);
+ ev.recycle();
+ }
+
+ /**
+ * Determine whether this WebTextView currently represents the node
+ * represented by ptr.
+ * @param ptr Pointer to a node to compare to.
+ * @return boolean Whether this WebTextView already represents the node
+ * pointed to by ptr.
+ */
+ /* package */ boolean isSameTextField(int ptr) {
+ return ptr == mNodePointer;
+ }
+
+ @Override public InputConnection onCreateInputConnection(
+ EditorInfo outAttrs) {
+ InputConnection connection = super.onCreateInputConnection(outAttrs);
+ if (mWebView != null) {
+ // Use the name of the textfield + the url. Use backslash as an
+ // arbitrary separator.
+ outAttrs.fieldName = mWebView.nativeFocusCandidateName() + "\\"
+ + mWebView.getUrl();
+ }
+ return connection;
+ }
+
+ @Override
+ public void onEditorAction(int actionCode) {
+ switch (actionCode) {
+ case EditorInfo.IME_ACTION_NEXT:
+ mWebView.nativeMoveCursorToNextTextInput();
+ // Preemptively rebuild the WebTextView, so that the action will
+ // be set properly.
+ mWebView.rebuildWebTextView();
+ // Since the cursor will no longer be in the same place as the
+ // focus, set the focus controller back to inactive
+ mWebView.setFocusControllerInactive();
+ mOkayForFocusNotToMatch = true;
+ break;
+ case EditorInfo.IME_ACTION_DONE:
+ super.onEditorAction(actionCode);
+ break;
+ case EditorInfo.IME_ACTION_GO:
+ // Send an enter and hide the soft keyboard
+ InputMethodManager.getInstance(mContext)
+ .hideSoftInputFromWindow(getWindowToken(), 0);
+ sendDomEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_ENTER));
+ sendDomEvent(new KeyEvent(KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_ENTER));
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged(int selStart, int selEnd) {
+ if (!mFromWebKit && mWebView != null) {
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "onSelectionChanged selStart=" + selStart
+ + " selEnd=" + selEnd);
+ }
+ mWebView.setSelection(selStart, selEnd);
+ }
+ }
+
+ @Override
+ protected void onTextChanged(CharSequence s,int start,int before,int count){
+ super.onTextChanged(s, start, before, count);
+ String postChange = s.toString();
+ // Prevent calls to setText from invoking onTextChanged (since this will
+ // mean we are on a different textfield). Also prevent the change when
+ // going from a textfield with a string of text to one with a smaller
+ // limit on text length from registering the onTextChanged event.
+ if (mPreChange == null || mPreChange.equals(postChange) ||
+ (mMaxLength > -1 && mPreChange.length() > mMaxLength &&
+ mPreChange.substring(0, mMaxLength).equals(postChange))) {
+ return;
+ }
+ mPreChange = postChange;
+ // This was simply a delete or a cut, so just delete the selection.
+ if (before > 0 && 0 == count) {
+ mWebView.deleteSelection(start, start + before);
+ // For this and all changes to the text, update our cache
+ updateCachedTextfield();
+ return;
+ }
+ // Find the last character being replaced. If it can be represented by
+ // events, we will pass them to native (after replacing the beginning
+ // of the changed text), so we can see javascript events.
+ // Otherwise, replace the text being changed (including the last
+ // character) in the textfield.
+ TextUtils.getChars(s, start + count - 1, start + count, mCharacter, 0);
+ KeyCharacterMap kmap =
+ KeyCharacterMap.load(KeyCharacterMap.BUILT_IN_KEYBOARD);
+ KeyEvent[] events = kmap.getEvents(mCharacter);
+ boolean cannotUseKeyEvents = null == events;
+ int charactersFromKeyEvents = cannotUseKeyEvents ? 0 : 1;
+ if (count > 1 || cannotUseKeyEvents) {
+ String replace = s.subSequence(start,
+ start + count - charactersFromKeyEvents).toString();
+ mWebView.replaceTextfieldText(start, start + before, replace,
+ start + count - charactersFromKeyEvents,
+ start + count - charactersFromKeyEvents);
+ } else {
+ // This corrects the selection which may have been affected by the
+ // trackball or auto-correct.
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "onTextChanged start=" + start
+ + " start + before=" + (start + before));
+ }
+ mWebView.setSelection(start, start + before);
+ }
+ if (!cannotUseKeyEvents) {
+ int length = events.length;
+ for (int i = 0; i < length; i++) {
+ // We never send modifier keys to native code so don't send them
+ // here either.
+ if (!KeyEvent.isModifierKey(events[i].getKeyCode())) {
+ sendDomEvent(events[i]);
+ }
+ }
+ }
+ updateCachedTextfield();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ super.onTouchEvent(event);
+ // This event may be the start of a drag, so store it to pass to the
+ // WebView if it is.
+ mDragStartX = event.getX();
+ mDragStartY = event.getY();
+ mDragStartTime = event.getEventTime();
+ mDragSent = false;
+ mScrolled = false;
+ mGotTouchDown = true;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ Spannable buffer = getText();
+ int initialScrollX = Touch.getInitialScrollX(this, buffer);
+ int initialScrollY = Touch.getInitialScrollY(this, buffer);
+ super.onTouchEvent(event);
+ if (mScrollX != initialScrollX
+ || mScrollY != initialScrollY) {
+ if (mWebView != null) {
+ mWebView.scrollFocusedTextInput(mScrollX, mScrollY);
+ }
+ mScrolled = true;
+ return true;
+ }
+ if (mWebView != null) {
+ // Only want to set the initial state once.
+ if (!mDragSent) {
+ mWebView.initiateTextFieldDrag(mDragStartX, mDragStartY,
+ mDragStartTime);
+ mDragSent = true;
+ }
+ boolean scrolled = mWebView.textFieldDrag(event);
+ if (scrolled) {
+ mScrolled = true;
+ cancelLongPress();
+ return true;
+ }
+ }
+ return false;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (!mScrolled) {
+ // If the page scrolled, or the TextView scrolled, we do not
+ // want to change the selection
+ cancelLongPress();
+ if (mGotTouchDown && mWebView != null) {
+ mWebView.touchUpOnTextField(event);
+ }
+ }
+ // Necessary for the WebView to reset its state
+ if (mWebView != null && mDragSent) {
+ mWebView.onTouchEvent(event);
+ }
+ mGotTouchDown = false;
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ if (isPopupShowing()) {
+ return super.onTrackballEvent(event);
+ }
+ if (event.getAction() != MotionEvent.ACTION_MOVE) {
+ return false;
+ }
+ // If the Cursor is not on the text input, webview should handle the
+ // trackball
+ if (!mWebView.nativeCursorMatchesFocus()) {
+ return mWebView.onTrackballEvent(event);
+ }
+ Spannable text = (Spannable) getText();
+ MovementMethod move = getMovementMethod();
+ if (move != null && getLayout() != null &&
+ move.onTrackballEvent(this, text, event)) {
+ // Selection is changed in onSelectionChanged
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove this WebTextView from its host WebView, and return
+ * focus to the host.
+ */
+ /* package */ void remove() {
+ // hide the soft keyboard when the edit text is out of focus
+ InputMethodManager.getInstance(mContext).hideSoftInputFromWindow(
+ getWindowToken(), 0);
+ mWebView.removeView(this);
+ mWebView.requestFocus();
+ }
+
+ /* package */ void bringIntoView() {
+ if (getLayout() != null) {
+ bringPointIntoView(Selection.getSelectionEnd(getText()));
+ }
+ }
+
+ /**
+ * Send the DOM events for the specified event.
+ * @param event KeyEvent to be translated into a DOM event.
+ */
+ private void sendDomEvent(KeyEvent event) {
+ mWebView.passToJavaScript(getText().toString(), event);
+ }
+
+ /**
+ * Always use this instead of setAdapter, as this has features specific to
+ * the WebTextView.
+ */
+ public void setAdapterCustom(AutoCompleteAdapter adapter) {
+ if (adapter != null) {
+ setInputType(EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+ adapter.setTextView(this);
+ }
+ super.setAdapter(adapter);
+ }
+
+ /**
+ * This is a special version of ArrayAdapter which changes its text size
+ * to match the text size of its host TextView.
+ */
+ public static class AutoCompleteAdapter extends ArrayAdapter<String> {
+ private TextView mTextView;
+
+ public AutoCompleteAdapter(Context context, ArrayList<String> entries) {
+ super(context, com.android.internal.R.layout
+ .search_dropdown_item_1line, entries);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public View getView(int position, View convertView, ViewGroup parent) {
+ TextView tv =
+ (TextView) super.getView(position, convertView, parent);
+ if (tv != null && mTextView != null) {
+ tv.setTextSize(mTextView.getTextSize());
+ }
+ return tv;
+ }
+
+ /**
+ * Set the TextView so we can match its text size.
+ */
+ private void setTextView(TextView tv) {
+ mTextView = tv;
+ }
+ }
+
+ /**
+ * Determine whether to use the system-wide password disguising method,
+ * or to use none.
+ * @param inPassword True if the textfield is a password field.
+ */
+ /* package */ void setInPassword(boolean inPassword) {
+ if (inPassword) {
+ setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.
+ TYPE_TEXT_VARIATION_PASSWORD);
+ createBackground();
+ }
+ // For password fields, draw the WebTextView. For others, just show
+ // webkit's drawing.
+ setWillNotDraw(!inPassword);
+ setBackgroundDrawable(inPassword ? mBackground : null);
+ // For non-password fields, avoid the invals from TextView's blinking
+ // cursor
+ setCursorVisible(inPassword);
+ }
+
+ /**
+ * Private class used for the background of a password textfield.
+ */
+ private static class OutlineDrawable extends Drawable {
+ public void draw(Canvas canvas) {
+ Rect bounds = getBounds();
+ Paint paint = new Paint();
+ paint.setAntiAlias(true);
+ // Draw the background.
+ paint.setColor(Color.WHITE);
+ canvas.drawRect(bounds, paint);
+ // Draw the outline.
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setColor(Color.BLACK);
+ canvas.drawRect(bounds, paint);
+ }
+ // Always want it to be opaque.
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+ // These are needed because they are abstract in Drawable.
+ public void setAlpha(int alpha) { }
+ public void setColorFilter(ColorFilter cf) { }
+ }
+
+ /**
+ * Create a background for the WebTextView and set up the paint for drawing
+ * the text. This way, we can see the password transformation of the
+ * system, which (optionally) shows the actual text before changing to dots.
+ * The background is necessary to hide the webkit-drawn text beneath.
+ */
+ private void createBackground() {
+ if (mBackground != null) {
+ return;
+ }
+ mBackground = new OutlineDrawable();
+
+ setGravity(Gravity.CENTER_VERTICAL);
+ // Turn on subpixel text, and turn off kerning, so it better matches
+ // the text in webkit.
+ TextPaint paint = getPaint();
+ int flags = paint.getFlags() | Paint.SUBPIXEL_TEXT_FLAG |
+ Paint.ANTI_ALIAS_FLAG & ~Paint.DEV_KERN_TEXT_FLAG;
+ paint.setFlags(flags);
+ // Set the text color to black, regardless of the theme. This ensures
+ // that other applications that use embedded WebViews will properly
+ // display the text in password textfields.
+ setTextColor(Color.BLACK);
+ }
+
+ /* package */ void setMaxLength(int maxLength) {
+ mMaxLength = maxLength;
+ if (-1 == maxLength) {
+ setFilters(NO_FILTERS);
+ } else {
+ setFilters(new InputFilter[] {
+ new InputFilter.LengthFilter(maxLength) });
+ }
+ }
+
+ /**
+ * Set the pointer for this node so it can be determined which node this
+ * WebTextView represents.
+ * @param ptr Integer representing the pointer to the node which this
+ * WebTextView represents.
+ */
+ /* package */ void setNodePointer(int ptr) {
+ mNodePointer = ptr;
+ }
+
+ /**
+ * Determine the position and size of WebTextView, and add it to the
+ * WebView's view heirarchy. All parameters are presumed to be in
+ * view coordinates. Also requests Focus and sets the cursor to not
+ * request to be in view.
+ * @param x x-position of the textfield.
+ * @param y y-position of the textfield.
+ * @param width width of the textfield.
+ * @param height height of the textfield.
+ */
+ /* package */ void setRect(int x, int y, int width, int height) {
+ LayoutParams lp = (LayoutParams) getLayoutParams();
+ if (null == lp) {
+ lp = new LayoutParams(width, height, x, y);
+ } else {
+ lp.x = x;
+ lp.y = y;
+ lp.width = width;
+ lp.height = height;
+ }
+ if (getParent() == null) {
+ mWebView.addView(this, lp);
+ } else {
+ setLayoutParams(lp);
+ }
+ // Set up a measure spec so a layout can always be recreated.
+ mWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ mHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ requestFocus();
+ }
+
+ /**
+ * Set the selection, and disable our onSelectionChanged action.
+ */
+ /* package */ void setSelectionFromWebKit(int start, int end) {
+ if (start < 0 || end < 0) return;
+ Spannable text = (Spannable) getText();
+ int length = text.length();
+ if (start > length || end > length) return;
+ mFromWebKit = true;
+ Selection.setSelection(text, start, end);
+ mFromWebKit = false;
+ }
+
+ /**
+ * Set whether this is a single-line textfield or a multi-line textarea.
+ * Textfields scroll horizontally, and do not handle the enter key.
+ * Textareas behave oppositely.
+ * Do NOT call this after calling setInPassword(true). This will result in
+ * removing the password input type.
+ */
+ public void setSingleLine(boolean single) {
+ int inputType = EditorInfo.TYPE_CLASS_TEXT
+ | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
+ if (single) {
+ int action = mWebView.nativeTextFieldAction();
+ switch (action) {
+ // Keep in sync with CachedRoot::ImeAction
+ case 0: // NEXT
+ setImeOptions(EditorInfo.IME_ACTION_NEXT);
+ break;
+ case 1: // GO
+ setImeOptions(EditorInfo.IME_ACTION_GO);
+ break;
+ case -1: // FAILURE
+ case 2: // DONE
+ setImeOptions(EditorInfo.IME_ACTION_DONE);
+ break;
+ }
+ } else {
+ inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE
+ | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES
+ | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT;
+ setImeOptions(EditorInfo.IME_ACTION_NONE);
+ }
+ mSingle = single;
+ setHorizontallyScrolling(single);
+ setInputType(inputType);
+ }
+
+ /**
+ * Set the text for this WebTextView, and set the selection to (start, end)
+ * @param text Text to go into this WebTextView.
+ * @param start Beginning of the selection.
+ * @param end End of the selection.
+ */
+ /* package */ void setText(CharSequence text, int start, int end) {
+ mPreChange = text.toString();
+ setText(text);
+ Spannable span = (Spannable) getText();
+ int length = span.length();
+ if (end > length) {
+ end = length;
+ }
+ if (start < 0) {
+ start = 0;
+ } else if (start > length) {
+ start = length;
+ }
+ if (DebugFlags.WEB_TEXT_VIEW) {
+ Log.v(LOGTAG, "setText start=" + start
+ + " end=" + end);
+ }
+ Selection.setSelection(span, start, end);
+ }
+
+ /**
+ * Set the text to the new string, but use the old selection, making sure
+ * to keep it within the new string.
+ * @param text The new text to place in the textfield.
+ */
+ /* package */ void setTextAndKeepSelection(String text) {
+ mPreChange = text.toString();
+ Editable edit = (Editable) getText();
+ edit.replace(0, edit.length(), text);
+ updateCachedTextfield();
+ }
+
+ /**
+ * Update the cache to reflect the current text.
+ */
+ /* package */ void updateCachedTextfield() {
+ mWebView.updateCachedTextfield(getText().toString());
+ }
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6936435..8446475 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -31,6 +31,7 @@ import android.graphics.Picture;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.drawable.Drawable;
import android.net.http.SslCertificate;
import android.net.Uri;
import android.os.Bundle;
@@ -45,6 +46,7 @@ import android.text.Spannable;
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
+import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -57,8 +59,9 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.animation.AlphaAnimation;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
-import android.webkit.TextDialog.AutoCompleteAdapter;
+import android.webkit.WebTextView.AutoCompleteAdapter;
import android.webkit.WebViewCore.EventHub;
import android.widget.AbsoluteLayout;
import android.widget.Adapter;
@@ -67,6 +70,7 @@ import android.widget.ArrayAdapter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.ScrollBarDrawable;
import android.widget.Scroller;
import android.widget.Toast;
import android.widget.ZoomButtonsController;
@@ -80,11 +84,11 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
- * <p>A View that displays web pages. This class is the basis upon which you
+ * <p>A View that displays web pages. This class is the basis upon which you
* can roll your own web browser or simply display some online content within your Activity.
* It uses the WebKit rendering engine to display
* web pages and includes methods to navigate forward and backward
@@ -93,12 +97,109 @@ import java.util.List;
* {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
* (introduced in API version 3).
* <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the <var>INTERNET</var> permissions to your
+ * in a WebView, you must add the <var>INTERNET</var> permissions to your
* Android Manifest file:</p>
* <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
+ *
* <p>This must be a child of the <code>&lt;manifest></code> element.</p>
+ *
+ * <h3>Basic usage</h3>
+ *
+ * <p>By default, a WebView provides no browser-like widgets, does not
+ * enable JavaScript and errors will be ignored. If your goal is only
+ * to display some HTML as a part of your UI, this is probably fine;
+ * the user won't need to interact with the web page beyond reading
+ * it, and the web page won't need to interact with the user. If you
+ * actually want a fully blown web browser, then you probably want to
+ * invoke the Browser application with your URL rather than show it
+ * with a WebView. See {@link android.content.Intent} for more information.</p>
+ *
+ * <pre class="prettyprint">
+ * WebView webview = new WebView(this);
+ * setContentView(webview);
+ *
+ * // Simplest usage: note that an exception will NOT be thrown
+ * // if there is an error loading this page (see below).
+ * webview.loadUrl("http://slashdot.org/");
+ *
+ * // Of course you can also load from any string:
+ * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
+ * webview.loadData(summary, "text/html", "utf-8");
+ * // ... although note that there are restrictions on what this HTML can do.
+ * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
+ * </pre>
+ *
+ * <p>A WebView has several customization points where you can add your
+ * own behavior. These are:</p>
+ *
+ * <ul>
+ * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
+ * This class is called when something that might impact a
+ * browser UI happens, for instance, progress updates and
+ * JavaScript alerts are sent here.
+ * </li>
+ * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
+ * It will be called when things happen that impact the
+ * rendering of the content, eg, errors or form submissions. You
+ * can also intercept URL loading here.</li>
+ * <li>Via the {@link android.webkit.WebSettings} class, which contains
+ * miscellaneous configuration. </li>
+ * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
+ * This lets you bind Java objects into the WebView so they can be
+ * controlled from the web pages JavaScript.</li>
+ * </ul>
+ *
+ * <p>Here's a more complicated example, showing error handling,
+ * settings, and progress notification:</p>
+ *
+ * <pre class="prettyprint">
+ * // Let's display the progress in the activity title bar, like the
+ * // browser app does.
+ * getWindow().requestFeature(Window.FEATURE_PROGRESS);
+ *
+ * webview.getSettings().setJavaScriptEnabled(true);
+ *
+ * final Activity activity = this;
+ * webview.setWebChromeClient(new WebChromeClient() {
+ * public void onProgressChanged(WebView view, int progress) {
+ * // Activities and WebViews measure progress with different scales.
+ * // The progress meter will automatically disappear when we reach 100%
+ * activity.setProgress(progress * 1000);
+ * }
+ * });
+ * webview.setWebViewClient(new WebViewClient() {
+ * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+ * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
+ * }
+ * });
+ *
+ * webview.loadUrl("http://slashdot.org/");
+ * </pre>
+ *
+ * <h3>Cookie and window management</h3>
+ *
+ * <p>For obvious security reasons, your application has its own
+ * cache, cookie store etc - it does not share the Browser
+ * applications data. Cookies are managed on a separate thread, so
+ * operations like index building don't block the UI
+ * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
+ * if you want to use cookies in your application.
+ * </p>
+ *
+ * <p>By default, requests by the HTML to open new windows are
+ * ignored. This is true whether they be opened by JavaScript or by
+ * the target attribute on a link. You can customize your
+ * WebChromeClient to provide your own behaviour for opening multiple windows,
+ * and render them in whatever manner you want.</p>
+ *
+ * <p>Standard behavior for an Activity is to be destroyed and
+ * recreated when the devices orientation is changed. This will cause
+ * the WebView to reload the current page. If you don't want that, you
+ * can set your Activity to handle the orientation and keyboardHidden
+ * changes, and then just leave the WebView alone. It'll automatically
+ * re-orient itself as appropriate.</p>
*/
-public class WebView extends AbsoluteLayout
+public class WebView extends AbsoluteLayout
implements ViewTreeObserver.OnGlobalFocusChangeListener,
ViewGroup.OnHierarchyChangeListener {
@@ -108,62 +209,61 @@ public class WebView extends AbsoluteLayout
// true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
private boolean mAutoRedraw;
- // keep debugging parameters near the top of the file
static final String LOGTAG = "webview";
- static final boolean DEBUG = false;
- static final boolean LOGV_ENABLED = DEBUG;
- private class ExtendedZoomControls extends FrameLayout {
+ private static class ExtendedZoomControls extends FrameLayout {
public ExtendedZoomControls(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
- mZoomControls = (ZoomControls) findViewById(com.android.internal.R.id.zoomControls);
+ mPlusMinusZoomControls = (ZoomControls) findViewById(
+ com.android.internal.R.id.zoomControls);
mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify);
}
-
+
public void show(boolean showZoom, boolean canZoomOut) {
- mZoomControls.setVisibility(showZoom ? View.VISIBLE : View.GONE);
+ mPlusMinusZoomControls.setVisibility(
+ showZoom ? View.VISIBLE : View.GONE);
mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE);
fade(View.VISIBLE, 0.0f, 1.0f);
}
-
+
public void hide() {
fade(View.GONE, 1.0f, 0.0f);
}
-
+
private void fade(int visibility, float startAlpha, float endAlpha) {
AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
anim.setDuration(500);
startAnimation(anim);
setVisibility(visibility);
}
-
+
public void setIsZoomMagnifyEnabled(boolean isEnabled) {
mZoomMagnify.setEnabled(isEnabled);
}
-
+
public boolean hasFocus() {
- return mZoomControls.hasFocus() || mZoomMagnify.hasFocus();
+ return mPlusMinusZoomControls.hasFocus() || mZoomMagnify.hasFocus();
}
-
+
public void setOnZoomInClickListener(OnClickListener listener) {
- mZoomControls.setOnZoomInClickListener(listener);
+ mPlusMinusZoomControls.setOnZoomInClickListener(listener);
}
-
+
public void setOnZoomOutClickListener(OnClickListener listener) {
- mZoomControls.setOnZoomOutClickListener(listener);
+ mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
}
-
+
public void setOnZoomMagnifyClickListener(OnClickListener listener) {
mZoomMagnify.setOnClickListener(listener);
}
- ZoomControls mZoomControls;
- ImageView mZoomMagnify;
+ ZoomControls mPlusMinusZoomControls;
+ ImageView mZoomMagnify;
}
-
+
/**
* Transportation object for returning WebView across thread boundaries.
*/
@@ -203,13 +303,13 @@ public class WebView extends AbsoluteLayout
private WebViewCore mWebViewCore;
// Handler for dispatching UI messages.
/* package */ final Handler mPrivateHandler = new PrivateHandler();
- private TextDialog mTextEntry;
+ private WebTextView mWebTextView;
// Used to ignore changes to webkit text that arrives to the UI side after
// more key events.
private int mTextGeneration;
- // The list of loaded plugins.
- private static PluginList sPluginList;
+ // Used by WebViewCore to create child views.
+ /* package */ final ViewManager mViewManager;
/**
* Position of the last touch event.
@@ -229,15 +329,30 @@ public class WebView extends AbsoluteLayout
/**
* The minimum elapsed time before sending another ACTION_MOVE event to
- * WebViewCore
+ * WebViewCore. This really should be tuned for each type of the devices.
+ * For example in Google Map api test case, it takes Dream device at least
+ * 150ms to do a full cycle in the WebViewCore by processing a touch event,
+ * triggering the layout and drawing the picture. While the same process
+ * takes 60+ms on the current high speed device. If we make
+ * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
+ * to WebViewCore queue and the real layout and draw events will be pushed
+ * to further, which slows down the refresh rate. Choose 50 to favor the
+ * current high speed devices. For Dream like devices, 100 is a better
+ * choice. Maybe make this in the buildspec later.
*/
- private static final int TOUCH_SENT_INTERVAL = 100;
+ private static final int TOUCH_SENT_INTERVAL = 50;
/**
* Helper class to get velocity for fling
*/
VelocityTracker mVelocityTracker;
private int mMaximumFling;
+ private float mLastVelocity;
+ private float mLastVelX;
+ private float mLastVelY;
+
+ // use this flag to control whether enabling the new double tap zoom
+ static final boolean ENABLE_DOUBLETAP_ZOOM = true;
/**
* Touch mode
@@ -258,6 +373,7 @@ public class WebView extends AbsoluteLayout
private static final int SCROLL_ZOOM_OUT = 11;
private static final int LAST_SCROLL_ZOOM = 11;
// end of touch mode values specific to scale+scroll
+ private static final int TOUCH_DOUBLE_TAP_MODE = 12;
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
@@ -267,18 +383,23 @@ public class WebView extends AbsoluteLayout
// take control of touch events unless it says no for touch down event.
private boolean mPreventDrag;
- // If updateTextEntry gets called while we are out of focus, use this
- // variable to remember to do it next time we gain focus.
- private boolean mNeedsUpdateTextEntry = false;
-
- // Whether or not to draw the focus ring.
- private boolean mDrawFocusRing = true;
+ // To keep track of whether the current drag was initiated by a WebTextView,
+ // so that we know not to hide the cursor
+ boolean mDragFromTextInput;
+
+ // Whether or not to draw the cursor ring.
+ private boolean mDrawCursorRing = true;
+
+ // true if onPause has been called (and not onResume)
+ private boolean mIsPaused;
/**
* Customizable constant
*/
// pre-computed square of ViewConfiguration.getScaledTouchSlop()
private int mTouchSlopSquare;
+ // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
+ private int mDoubleTapSlopSquare;
// pre-computed density adjusted navigation slop
private int mNavSlop;
// This should be ViewConfiguration.getTapTimeout()
@@ -292,7 +413,7 @@ public class WebView extends AbsoluteLayout
// needed to avoid flinging after a pause of no movement
private static final int MIN_FLING_TIME = 250;
// The time that the Zoom Controls are visible before fading away
- private static final long ZOOM_CONTROLS_TIMEOUT =
+ private static final long ZOOM_CONTROLS_TIMEOUT =
ViewConfiguration.getZoomControlsTimeout();
// The amount of content to overlap between two screens when going through
// pages with the space bar, in pixels.
@@ -313,7 +434,7 @@ public class WebView extends AbsoluteLayout
private int mContentWidth; // cache of value from WebViewCore
private int mContentHeight; // cache of value from WebViewCore
- // Need to have the separate control for horizontal and vertical scrollbar
+ // Need to have the separate control for horizontal and vertical scrollbar
// style than the View's single scrollbar style
private boolean mOverlayHorizontalScrollbar = true;
private boolean mOverlayVerticalScrollbar = false;
@@ -328,51 +449,49 @@ public class WebView extends AbsoluteLayout
private boolean mWrapContent;
- // true if we should call webcore to draw the content, false means we have
- // requested something but it isn't ready to draw yet.
- private WebViewCore.FocusData mFocusData;
/**
* Private message ids
*/
- private static final int REMEMBER_PASSWORD = 1;
- private static final int NEVER_REMEMBER_PASSWORD = 2;
- private static final int SWITCH_TO_SHORTPRESS = 3;
- private static final int SWITCH_TO_LONGPRESS = 4;
- private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
- private static final int SWITCH_TO_ENTER = 7;
- private static final int RESUME_WEBCORE_UPDATE = 8;
+ private static final int REMEMBER_PASSWORD = 1;
+ private static final int NEVER_REMEMBER_PASSWORD = 2;
+ private static final int SWITCH_TO_SHORTPRESS = 3;
+ private static final int SWITCH_TO_LONGPRESS = 4;
+ private static final int RELEASE_SINGLE_TAP = 5;
+ private static final int REQUEST_FORM_DATA = 6;
+ private static final int SWITCH_TO_CLICK = 7;
+ private static final int RESUME_WEBCORE_UPDATE = 8;
//! arg1=x, arg2=y
- static final int SCROLL_TO_MSG_ID = 10;
- static final int SCROLL_BY_MSG_ID = 11;
+ static final int SCROLL_TO_MSG_ID = 10;
+ static final int SCROLL_BY_MSG_ID = 11;
//! arg1=x, arg2=y
- static final int SPAWN_SCROLL_TO_MSG_ID = 12;
+ static final int SPAWN_SCROLL_TO_MSG_ID = 12;
//! arg1=x, arg2=y
- static final int SYNC_SCROLL_TO_MSG_ID = 13;
- static final int NEW_PICTURE_MSG_ID = 14;
- static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
- static final int WEBCORE_INITIALIZED_MSG_ID = 16;
- static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
- static final int DID_FIRST_LAYOUT_MSG_ID = 18;
- static final int RECOMPUTE_FOCUS_MSG_ID = 19;
- static final int NOTIFY_FOCUS_SET_MSG_ID = 20;
- static final int MARK_NODE_INVALID_ID = 21;
- static final int UPDATE_CLIPBOARD = 22;
- static final int LONG_PRESS_ENTER = 23;
- static final int PREVENT_TOUCH_ID = 24;
- static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
+ static final int SYNC_SCROLL_TO_MSG_ID = 13;
+ static final int NEW_PICTURE_MSG_ID = 14;
+ static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
+ static final int WEBCORE_INITIALIZED_MSG_ID = 16;
+ static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
+ static final int MOVE_OUT_OF_PLUGIN = 19;
+ static final int CLEAR_TEXT_ENTRY = 20;
+ static final int UPDATE_TEXT_SELECTION_MSG_ID = 21;
+ static final int UPDATE_CLIPBOARD = 22;
+ static final int LONG_PRESS_CENTER = 23;
+ static final int PREVENT_TOUCH_ID = 24;
+ static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
// obj=Rect in doc coordinates
- static final int INVAL_RECT_MSG_ID = 26;
-
+ static final int INVAL_RECT_MSG_ID = 26;
+ static final int REQUEST_KEYBOARD = 27;
+
static final String[] HandlerDebugString = {
- "REMEMBER_PASSWORD", // = 1;
- "NEVER_REMEMBER_PASSWORD", // = 2;
- "SWITCH_TO_SHORTPRESS", // = 3;
- "SWITCH_TO_LONGPRESS", // = 4;
- "5",
- "UPDATE_TEXT_ENTRY_ADAPTER", // = 6;
- "SWITCH_TO_ENTER", // = 7;
- "RESUME_WEBCORE_UPDATE", // = 8;
+ "REMEMBER_PASSWORD", // = 1;
+ "NEVER_REMEMBER_PASSWORD", // = 2;
+ "SWITCH_TO_SHORTPRESS", // = 3;
+ "SWITCH_TO_LONGPRESS", // = 4;
+ "RELEASE_SINGLE_TAP", // = 5;
+ "REQUEST_FORM_DATA", // = 6;
+ "SWITCH_TO_CLICK", // = 7;
+ "RESUME_WEBCORE_UPDATE", // = 8;
"9",
"SCROLL_TO_MSG_ID", // = 10;
"SCROLL_BY_MSG_ID", // = 11;
@@ -382,31 +501,40 @@ public class WebView extends AbsoluteLayout
"UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
"WEBCORE_INITIALIZED_MSG_ID", // = 16;
"UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
- "DID_FIRST_LAYOUT_MSG_ID", // = 18;
- "RECOMPUTE_FOCUS_MSG_ID", // = 19;
- "NOTIFY_FOCUS_SET_MSG_ID", // = 20;
- "MARK_NODE_INVALID_ID", // = 21;
+ "18", // = 18;
+ "MOVE_OUT_OF_PLUGIN", // = 19;
+ "CLEAR_TEXT_ENTRY", // = 20;
+ "UPDATE_TEXT_SELECTION_MSG_ID", // = 21;
"UPDATE_CLIPBOARD", // = 22;
- "LONG_PRESS_ENTER", // = 23;
+ "LONG_PRESS_CENTER", // = 23;
"PREVENT_TOUCH_ID", // = 24;
"WEBCORE_NEED_TOUCH_EVENTS", // = 25;
- "INVAL_RECT_MSG_ID" // = 26;
+ "INVAL_RECT_MSG_ID", // = 26;
+ "REQUEST_KEYBOARD" // = 27;
};
- // width which view is considered to be fully zoomed out
- static final int ZOOM_OUT_WIDTH = 1008;
-
// default scale limit. Depending on the display density
private static float DEFAULT_MAX_ZOOM_SCALE;
private static float DEFAULT_MIN_ZOOM_SCALE;
// scale limit, which can be set through viewport meta tag in the web page
private float mMaxZoomScale;
private float mMinZoomScale;
- private boolean mMinZoomScaleFixed = false;
+ private boolean mMinZoomScaleFixed = true;
// initial scale in percent. 0 means using default.
private int mInitialScale = 0;
+ // while in the zoom overview mode, the page's width is fully fit to the
+ // current window. The page is alive, in another words, you can click to
+ // follow the links. Double tap will toggle between zoom overview mode and
+ // the last zoom scale.
+ boolean mInZoomOverview = false;
+
+ // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
+ // engadget always have wider mContentWidth no matter what viewport size is.
+ int mZoomOverviewWidth = WebViewCore.DEFAULT_VIEWPORT_WIDTH;
+ float mLastScale;
+
// default scale. Depending on the display density.
static int DEFAULT_SCALE_PERCENT;
private float mDefaultScale;
@@ -421,6 +549,8 @@ public class WebView extends AbsoluteLayout
private float mZoomScale;
private float mInvInitialZoomScale;
private float mInvFinalZoomScale;
+ private int mInitialScrollX;
+ private int mInitialScrollY;
private long mZoomStart;
private static final int ZOOM_ANIMATION_LENGTH = 500;
@@ -433,7 +563,7 @@ public class WebView extends AbsoluteLayout
private static final int SNAP_X_LOCK = 4;
private static final int SNAP_Y_LOCK = 5;
private boolean mSnapPositive;
-
+
// Used to match key downs and key ups
private boolean mGotKeyDown;
@@ -456,7 +586,7 @@ public class WebView extends AbsoluteLayout
* URI scheme for map address
*/
public static final String SCHEME_GEO = "geo:0,0?q=";
-
+
private int mBackgroundColor = Color.WHITE;
// Used to notify listeners of a new picture.
@@ -473,7 +603,8 @@ public class WebView extends AbsoluteLayout
public void onNewPicture(WebView view, Picture picture);
}
- public class HitTestResult {
+ // FIXME: Want to make this public, but need to change the API file.
+ public /*static*/ class HitTestResult {
/**
* Default HitTestResult, where the target is unknown
*/
@@ -543,8 +674,7 @@ public class WebView extends AbsoluteLayout
private ExtendedZoomControls mZoomControls;
private Runnable mZoomControlRunnable;
- private ZoomButtonsController mZoomButtonsController;
- private ImageView mZoomOverviewButton;
+ private ZoomButtonsController mZoomButtonsController;
// These keep track of the center point of the zoom. They are used to
// determine the point around which we should zoom.
@@ -567,11 +697,11 @@ public class WebView extends AbsoluteLayout
} else {
zoomOut();
}
-
+
updateZoomButtonsEnabled();
}
};
-
+
/**
* Construct a new WebView with a Context object.
* @param context A Context object used to access application assets.
@@ -596,24 +726,33 @@ public class WebView extends AbsoluteLayout
* @param defStyle The default style resource ID.
*/
public WebView(Context context, AttributeSet attrs, int defStyle) {
+ this(context, attrs, defStyle, null);
+ }
+
+ /**
+ * Construct a new WebView with layout parameters, a default style and a set
+ * of custom Javscript interfaces to be added to the WebView at initialization
+ * time. This guraratees that these interfaces will be available when the JS
+ * context is initialized.
+ * @param context A Context object used to access application assets.
+ * @param attrs An AttributeSet passed to our parent.
+ * @param defStyle The default style resource ID.
+ * @param javascriptInterfaces is a Map of intareface names, as keys, and
+ * object implementing those interfaces, as values.
+ * @hide pending API council approval.
+ */
+ protected WebView(Context context, AttributeSet attrs, int defStyle,
+ Map<String, Object> javascriptInterfaces) {
super(context, attrs, defStyle);
init();
mCallbackProxy = new CallbackProxy(context, this);
- mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
+ mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
mDatabase = WebViewDatabase.getInstance(context);
- mFocusData = new WebViewCore.FocusData();
- mFocusData.mFrame = 0;
- mFocusData.mNode = 0;
- mFocusData.mX = 0;
- mFocusData.mY = 0;
mScroller = new Scroller(context);
- initZoomController(context);
- }
+ mViewManager = new ViewManager(this);
- private void initZoomController(Context context) {
- // Create the buttons controller
mZoomButtonsController = new ZoomButtonsController(this);
mZoomButtonsController.setOnZoomListener(mZoomListener);
// ZoomButtonsController positions the buttons at the bottom, but in
@@ -626,30 +765,11 @@ public class WebView extends AbsoluteLayout
params;
frameParams.gravity = Gravity.RIGHT;
}
-
- // Create the accessory buttons
- LayoutInflater inflater =
- (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- ViewGroup container = mZoomButtonsController.getContainer();
- inflater.inflate(com.android.internal.R.layout.zoom_browser_accessory_buttons, container);
- mZoomOverviewButton =
- (ImageView) container.findViewById(com.android.internal.R.id.zoom_page_overview);
- mZoomOverviewButton.setOnClickListener(
- new View.OnClickListener() {
- public void onClick(View v) {
- mZoomButtonsController.setVisible(false);
- zoomScrollOut();
- if (mLogEvent) {
- Checkin.updateStats(mContext.getContentResolver(),
- Checkin.Stats.Tag.BROWSER_ZOOM_OVERVIEW, 1, 0.0);
- }
- }
- });
}
private void updateZoomButtonsEnabled() {
boolean canZoomIn = mActualScale < mMaxZoomScale;
- boolean canZoomOut = mActualScale > mMinZoomScale;
+ boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
if (!canZoomIn && !canZoomOut) {
// Hide the zoom in and out buttons, as well as the fit to page
// button, if the page cannot zoom
@@ -663,8 +783,6 @@ public class WebView extends AbsoluteLayout
mZoomButtonsController.setZoomInEnabled(canZoomIn);
mZoomButtonsController.setZoomOutEnabled(canZoomOut);
}
- mZoomOverviewButton.setVisibility(canZoomScrollOut() ? View.VISIBLE:
- View.GONE);
}
private void init() {
@@ -675,9 +793,11 @@ public class WebView extends AbsoluteLayout
setLongClickable(true);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
- final int slop = configuration.getScaledTouchSlop();
+ int slop = configuration.getScaledTouchSlop();
mTouchSlopSquare = slop * slop;
mMinLockSnapReverseDistance = slop;
+ slop = configuration.getScaledDoubleTapSlop();
+ mDoubleTapSlopSquare = slop * slop;
final float density = getContext().getResources().getDisplayMetrics().density;
// use one line height, 16 based on our current default font, for how
// far we allow a touch be away from the edge of a link
@@ -821,15 +941,30 @@ public class WebView extends AbsoluteLayout
}
/*
+ * returns the height of the titlebarview (if any). Does not care about
+ * scrolling
+ */
+ private int getTitleHeight() {
+ return mTitleBar != null ? mTitleBar.getHeight() : 0;
+ }
+
+ /*
+ * Return the amount of the titlebarview (if any) that is visible
+ */
+ private int getVisibleTitleHeight() {
+ return Math.max(getTitleHeight() - mScrollY, 0);
+ }
+
+ /*
* Return the height of the view where the content of WebView should render
- * to.
+ * to. Note that this excludes mTitleBar, if there is one.
*/
private int getViewHeight() {
- if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) {
- return getHeight();
- } else {
- return getHeight() - getHorizontalScrollbarHeight();
+ int height = getHeight();
+ if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
+ height -= getHorizontalScrollbarHeight();
}
+ return height - getVisibleTitleHeight();
}
/**
@@ -899,7 +1034,7 @@ public class WebView extends AbsoluteLayout
clearTextEntry();
if (mWebViewCore != null) {
// Set the handlers to null before destroying WebViewCore so no
- // more messages will be posted.
+ // more messages will be posted.
mCallbackProxy.setWebViewClient(null);
mCallbackProxy.setWebChromeClient(null);
// Tell WebViewCore to destroy itself
@@ -930,12 +1065,23 @@ public class WebView extends AbsoluteLayout
/**
* If platform notifications are enabled, this should be called
- * from onPause() or onStop().
+ * from the Activity's onPause() or onStop().
*/
public static void disablePlatformNotifications() {
Network.disablePlatformNotifications();
}
-
+
+ /**
+ * Sets JavaScript engine flags.
+ *
+ * @param flags JS engine flags in a String
+ *
+ * @hide pending API solidification
+ */
+ public void setJsFlags(String flags) {
+ mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
+ }
+
/**
* Inform WebView of the network state. This is used to set
* the javascript property window.navigator.isOnline and
@@ -948,7 +1094,7 @@ public class WebView extends AbsoluteLayout
}
/**
- * Save the state of this WebView used in
+ * Save the state of this WebView used in
* {@link android.app.Activity#onSaveInstanceState}. Please note that this
* method no longer stores the display data for this WebView. The previous
* behavior could potentially leak files if {@link #restoreState} was never
@@ -1026,6 +1172,9 @@ public class WebView extends AbsoluteLayout
b.putInt("scrollX", mScrollX);
b.putInt("scrollY", mScrollY);
b.putFloat("scale", mActualScale);
+ if (mInZoomOverview) {
+ b.putFloat("lastScale", mLastScale);
+ }
return true;
}
return false;
@@ -1070,6 +1219,13 @@ public class WebView extends AbsoluteLayout
// onSizeChanged() is called, the rest will be set
// correctly
mActualScale = scale;
+ float lastScale = b.getFloat("lastScale", -1.0f);
+ if (lastScale > 0) {
+ mInZoomOverview = true;
+ mLastScale = lastScale;
+ } else {
+ mInZoomOverview = false;
+ }
invalidate();
return true;
}
@@ -1079,10 +1235,10 @@ public class WebView extends AbsoluteLayout
/**
* Restore the state of this WebView from the given map used in
- * {@link android.app.Activity#onRestoreInstanceState}. This method should
- * be called to restore the state of the WebView before using the object. If
- * it is called after the WebView has had a chance to build state (load
- * pages, create a back/forward list, etc.) there may be undesirable
+ * {@link android.app.Activity#onRestoreInstanceState}. This method should
+ * be called to restore the state of the WebView before using the object. If
+ * it is called after the WebView has had a chance to build state (load
+ * pages, create a back/forward list, etc.) there may be undesirable
* side-effects. Please note that this method no longer restores the
* display data for this WebView. See {@link #savePicture} and {@link
* #restorePicture} for saving and restoring the display data.
@@ -1143,6 +1299,9 @@ public class WebView extends AbsoluteLayout
* @param url The url of the resource to load.
*/
public void loadUrl(String url) {
+ if (url == null) {
+ return;
+ }
switchOutDrawHistory();
mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
clearTextEntry();
@@ -1152,18 +1311,16 @@ public class WebView extends AbsoluteLayout
* Load the url with postData using "POST" method into the WebView. If url
* is not a network url, it will be loaded with {link
* {@link #loadUrl(String)} instead.
- *
+ *
* @param url The url of the resource to load.
* @param postData The data will be passed to "POST" request.
- *
- * @hide pending API solidification
*/
public void postUrl(String url, byte[] postData) {
if (URLUtil.isNetworkUrl(url)) {
switchOutDrawHistory();
- HashMap arg = new HashMap();
- arg.put("url", url);
- arg.put("data", postData);
+ WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
+ arg.mUrl = url;
+ arg.mPostData = postData;
mWebViewCore.sendMessage(EventHub.POST_URL, arg);
clearTextEntry();
} else {
@@ -1197,7 +1354,7 @@ public class WebView extends AbsoluteLayout
* able to access asset files. If the baseUrl is anything other than
* http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
* sub resources.
- *
+ *
* @param baseUrl Url to resolve relative paths with, if null defaults to
* "about:blank"
* @param data A String of data in the given encoding.
@@ -1208,18 +1365,18 @@ public class WebView extends AbsoluteLayout
*/
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding, String failUrl) {
-
+
if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
loadData(data, mimeType, encoding);
return;
}
switchOutDrawHistory();
- HashMap arg = new HashMap();
- arg.put("baseUrl", baseUrl);
- arg.put("data", data);
- arg.put("mimeType", mimeType);
- arg.put("encoding", encoding);
- arg.put("failUrl", failUrl);
+ WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
+ arg.mBaseUrl = baseUrl;
+ arg.mData = data;
+ arg.mMimeType = mimeType;
+ arg.mEncoding = encoding;
+ arg.mFailUrl = failUrl;
mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
clearTextEntry();
}
@@ -1329,7 +1486,7 @@ public class WebView extends AbsoluteLayout
ignoreSnapshot ? 1 : 0);
}
}
-
+
private boolean extendScroll(int y) {
int finalY = mScroller.getFinalY();
int newY = pinLocY(finalY + y);
@@ -1338,7 +1495,7 @@ public class WebView extends AbsoluteLayout
mScroller.extendDuration(computeDuration(0, y));
return true;
}
-
+
/**
* Scroll the contents of the view up by half the view size
* @param top true to jump to the top of the page
@@ -1348,7 +1505,7 @@ public class WebView extends AbsoluteLayout
if (mNativeClass == 0) {
return false;
}
- nativeClearFocus(-1, -1);
+ nativeClearCursor(); // start next trackball movement from page edge
if (top) {
// go to the top of the document
return pinScrollTo(mScrollX, 0, true, 0);
@@ -1362,10 +1519,10 @@ public class WebView extends AbsoluteLayout
y = -h / 2;
}
mUserScroll = true;
- return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
+ return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
: extendScroll(y);
}
-
+
/**
* Scroll the contents of the view down by half the page size
* @param bottom true to jump to bottom of page
@@ -1375,7 +1532,7 @@ public class WebView extends AbsoluteLayout
if (mNativeClass == 0) {
return false;
}
- nativeClearFocus(-1, -1);
+ nativeClearCursor(); // start next trackball movement from page edge
if (bottom) {
return pinScrollTo(mScrollX, mContentHeight, true, 0);
}
@@ -1388,7 +1545,7 @@ public class WebView extends AbsoluteLayout
y = h / 2;
}
mUserScroll = true;
- return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
+ return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
: extendScroll(y);
}
@@ -1401,7 +1558,7 @@ public class WebView extends AbsoluteLayout
mContentHeight = 0;
mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
}
-
+
/**
* Return a new picture that captures the current display of the webview.
* This is a copy of the display, and will be unaffected if the webview
@@ -1412,7 +1569,7 @@ public class WebView extends AbsoluteLayout
* bounds of the view.
*/
public Picture capturePicture() {
- if (null == mWebViewCore) return null; // check for out of memory tab
+ if (null == mWebViewCore) return null; // check for out of memory tab
return mWebViewCore.copyContentPicture();
}
@@ -1420,17 +1577,17 @@ public class WebView extends AbsoluteLayout
* Return true if the browser is displaying a TextView for text input.
*/
private boolean inEditingMode() {
- return mTextEntry != null && mTextEntry.getParent() != null
- && mTextEntry.hasFocus();
+ return mWebTextView != null && mWebTextView.getParent() != null
+ && mWebTextView.hasFocus();
}
private void clearTextEntry() {
if (inEditingMode()) {
- mTextEntry.remove();
+ mWebTextView.remove();
}
}
- /**
+ /**
* Return the current scale of the WebView
* @return The current scale.
*/
@@ -1471,7 +1628,7 @@ public class WebView extends AbsoluteLayout
}
/**
- * Return a HitTestResult based on the current focus node. If a HTML::a tag
+ * Return a HitTestResult based on the current cursor node. If a HTML::a tag
* is found and the anchor has a non-javascript url, the HitTestResult type
* is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
* anchor does not have a url or if it is a javascript url, the type will
@@ -1494,26 +1651,26 @@ public class WebView extends AbsoluteLayout
}
HitTestResult result = new HitTestResult();
-
- if (nativeUpdateFocusNode()) {
- FocusNode node = mFocusNode;
- if (node.mIsTextField || node.mIsTextArea) {
+ if (nativeHasCursorNode()) {
+ if (nativeCursorIsTextInput()) {
result.setType(HitTestResult.EDIT_TEXT_TYPE);
- } else if (node.mText != null) {
- String text = node.mText;
- if (text.startsWith(SCHEME_TEL)) {
- result.setType(HitTestResult.PHONE_TYPE);
- result.setExtra(text.substring(SCHEME_TEL.length()));
- } else if (text.startsWith(SCHEME_MAILTO)) {
- result.setType(HitTestResult.EMAIL_TYPE);
- result.setExtra(text.substring(SCHEME_MAILTO.length()));
- } else if (text.startsWith(SCHEME_GEO)) {
- result.setType(HitTestResult.GEO_TYPE);
- result.setExtra(URLDecoder.decode(text
- .substring(SCHEME_GEO.length())));
- } else if (node.mIsAnchor) {
- result.setType(HitTestResult.SRC_ANCHOR_TYPE);
- result.setExtra(text);
+ } else {
+ String text = nativeCursorText();
+ if (text != null) {
+ if (text.startsWith(SCHEME_TEL)) {
+ result.setType(HitTestResult.PHONE_TYPE);
+ result.setExtra(text.substring(SCHEME_TEL.length()));
+ } else if (text.startsWith(SCHEME_MAILTO)) {
+ result.setType(HitTestResult.EMAIL_TYPE);
+ result.setExtra(text.substring(SCHEME_MAILTO.length()));
+ } else if (text.startsWith(SCHEME_GEO)) {
+ result.setType(HitTestResult.GEO_TYPE);
+ result.setExtra(URLDecoder.decode(text
+ .substring(SCHEME_GEO.length())));
+ } else if (nativeCursorIsAnchor()) {
+ result.setType(HitTestResult.SRC_ANCHOR_TYPE);
+ result.setExtra(text);
+ }
}
}
}
@@ -1521,12 +1678,12 @@ public class WebView extends AbsoluteLayout
if (type == HitTestResult.UNKNOWN_TYPE
|| type == HitTestResult.SRC_ANCHOR_TYPE) {
// Now check to see if it is an image.
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
String text = nativeImageURI(contentX, contentY);
if (text != null) {
- result.setType(type == HitTestResult.UNKNOWN_TYPE ?
- HitTestResult.IMAGE_TYPE :
+ result.setType(type == HitTestResult.UNKNOWN_TYPE ?
+ HitTestResult.IMAGE_TYPE :
HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
result.setExtra(text);
}
@@ -1538,37 +1695,35 @@ public class WebView extends AbsoluteLayout
* Request the href of an anchor element due to getFocusNodePath returning
* "href." If hrefMsg is null, this method returns immediately and does not
* dispatch hrefMsg to its target.
- *
+ *
* @param hrefMsg This message will be dispatched with the result of the
* request as the data member with "url" as key. The result can
* be null.
*/
+ // FIXME: API change required to change the name of this function. We now
+ // look at the cursor node, and not the focus node. Also, what is
+ // getFocusNodePath?
public void requestFocusNodeHref(Message hrefMsg) {
if (hrefMsg == null || mNativeClass == 0) {
return;
}
- if (nativeUpdateFocusNode()) {
- FocusNode node = mFocusNode;
- if (node.mIsAnchor) {
- // NOTE: We may already have the url of the anchor stored in
- // node.mText but it may be out of date or the caller may want
- // to know about javascript urls.
- mWebViewCore.sendMessage(EventHub.REQUEST_FOCUS_HREF,
- node.mFramePointer, node.mNodePointer, hrefMsg);
- }
+ if (nativeCursorIsAnchor()) {
+ mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
+ nativeCursorFramePointer(), nativeCursorNodePointer(),
+ hrefMsg);
}
}
-
+
/**
* Request the url of the image last touched by the user. msg will be sent
* to its target with a String representing the url as its object.
- *
+ *
* @param msg This message will be dispatched with the result of the request
* as the data member with "url" as key. The result can be null.
*/
public void requestImageRef(Message msg) {
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
String ref = nativeImageURI(contentX, contentY);
Bundle data = msg.getData();
data.putString("url", ref);
@@ -1599,34 +1754,111 @@ public class WebView extends AbsoluteLayout
// Expects y in view coordinates
private int pinLocY(int y) {
- return pinLoc(y, getViewHeight(), computeVerticalScrollRange());
+ int titleH = getTitleHeight();
+ // if the titlebar is still visible, just pin against 0
+ if (y <= titleH) {
+ return Math.max(y, 0);
+ }
+ // convert to 0-based coordinate (subtract the title height)
+ // pin(), and then add the title height back in
+ return pinLoc(y - titleH, getViewHeight(),
+ computeVerticalScrollRange()) + titleH;
}
- /*package*/ int viewToContent(int x) {
+ /**
+ * A title bar which is embedded in this WebView, and scrolls along with it
+ * vertically, but not horizontally.
+ */
+ private View mTitleBar;
+
+ /**
+ * Add or remove a title bar to be embedded into the WebView, and scroll
+ * along with it vertically, while remaining in view horizontally. Pass
+ * null to remove the title bar from the WebView, and return to drawing
+ * the WebView normally without translating to account for the title bar.
+ * @hide
+ */
+ public void setEmbeddedTitleBar(View v) {
+ if (mTitleBar == v) return;
+ if (mTitleBar != null) {
+ removeView(mTitleBar);
+ }
+ if (null != v) {
+ addView(v, new AbsoluteLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
+ }
+ mTitleBar = v;
+ }
+
+ /**
+ * Given an x coordinate in view space, convert it to content space. Also
+ * may be used for absolute heights (such as for the WebTextView's
+ * textSize, which is unaffected by the height of the title bar).
+ */
+ /*package*/ int viewToContentX(int x) {
return Math.round(x * mInvActualScale);
}
- private int contentToView(int x) {
- return Math.round(x * mActualScale);
+ /**
+ * Given a y coordinate in view space, convert it to content space.
+ * Takes into account the height of the title bar if there is one
+ * embedded into the WebView.
+ */
+ /*package*/ int viewToContentY(int y) {
+ return viewToContentX(y - getTitleHeight());
+ }
+
+ /**
+ * Given a distance in content space, convert it to view space. Note: this
+ * does not reflect translation, just scaling, so this should not be called
+ * with coordinates, but should be called for dimensions like width or
+ * height.
+ */
+ /*package*/ int contentToViewDimension(int d) {
+ return Math.round(d * mActualScale);
+ }
+
+ /**
+ * Given an x coordinate in content space, convert it to view
+ * space. Also used for absolute heights.
+ */
+ /*package*/ int contentToViewX(int x) {
+ return contentToViewDimension(x);
+ }
+
+ /**
+ * Given a y coordinate in content space, convert it to view
+ * space. Takes into account the height of the title bar.
+ */
+ /*package*/ int contentToViewY(int y) {
+ return contentToViewDimension(y) + getTitleHeight();
}
// Called by JNI to invalidate the View, given rectangle coordinates in
// content space
private void viewInvalidate(int l, int t, int r, int b) {
- invalidate(contentToView(l), contentToView(t), contentToView(r),
- contentToView(b));
+ invalidate(contentToViewX(l), contentToViewY(t), contentToViewX(r),
+ contentToViewY(b));
}
// Called by JNI to invalidate the View after a delay, given rectangle
// coordinates in content space
private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
- postInvalidateDelayed(delay, contentToView(l), contentToView(t),
- contentToView(r), contentToView(b));
+ postInvalidateDelayed(delay, contentToViewX(l), contentToViewY(t),
+ contentToViewX(r), contentToViewY(b));
}
private Rect contentToView(Rect x) {
- return new Rect(contentToView(x.left), contentToView(x.top)
- , contentToView(x.right), contentToView(x.bottom));
+ return new Rect(contentToViewX(x.left), contentToViewY(x.top)
+ , contentToViewX(x.right), contentToViewY(x.bottom));
+ }
+
+ // stop the scroll animation, and don't let a subsequent fling add
+ // to the existing velocity
+ private void abortAnimation() {
+ mScroller.abortAnimation();
+ mLastVelocity = 0;
}
/* call from webcoreview.draw(), so we're still executing in the UI thread
@@ -1637,7 +1869,7 @@ public class WebView extends AbsoluteLayout
if ((w | h) == 0) {
return;
}
-
+
// don't abort a scroll animation if we didn't change anything
if (mContentWidth != w || mContentHeight != h) {
// record new dimensions
@@ -1653,7 +1885,7 @@ public class WebView extends AbsoluteLayout
mScrollY = pinLocY(mScrollY);
// android.util.Log.d("skia", "recordNewContentSize -
// abortAnimation");
- mScroller.abortAnimation(); // just in case
+ abortAnimation(); // just in case
if (oldX != mScrollX || oldY != mScrollY) {
sendOurVisibleRect();
}
@@ -1697,7 +1929,10 @@ public class WebView extends AbsoluteLayout
mActualScale = scale;
mInvActualScale = 1 / scale;
- // as we don't have animation for scaling, don't do animation
+ // Scale all the child views
+ mViewManager.scaleAll();
+
+ // as we don't have animation for scaling, don't do animation
// for scrolling, as it causes weird intermediate state
// pinScrollTo(Math.round(sx), Math.round(sy));
mScrollX = pinLocX(Math.round(sx));
@@ -1718,18 +1953,21 @@ public class WebView extends AbsoluteLayout
private Rect sendOurVisibleRect() {
Rect rect = new Rect();
calcOurContentVisibleRect(rect);
- if (mFindIsUp) {
- rect.bottom -= viewToContent(FIND_HEIGHT);
- }
// Rect.equals() checks for null input.
if (!rect.equals(mLastVisibleRectSent)) {
+ Point pos = new Point(rect.left, rect.top);
mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
- rect.left, rect.top);
+ nativeMoveGeneration(), 0, pos);
mLastVisibleRectSent = rect;
}
Rect globalRect = new Rect();
if (getGlobalVisibleRect(globalRect)
&& !globalRect.equals(mLastGlobalRect)) {
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
+ + globalRect.top + ",r=" + globalRect.right + ",b="
+ + globalRect.bottom);
+ }
// TODO: the global offset is only used by windowRect()
// in ChromeClientAndroid ; other clients such as touch
// and mouse events could return view + screen relative points.
@@ -1744,15 +1982,26 @@ public class WebView extends AbsoluteLayout
Point p = new Point();
getGlobalVisibleRect(r, p);
r.offset(-p.x, -p.y);
+ if (mFindIsUp) {
+ r.bottom -= FIND_HEIGHT;
+ }
}
// Sets r to be our visible rectangle in content coordinates
private void calcOurContentVisibleRect(Rect r) {
calcOurVisibleRect(r);
- r.left = viewToContent(r.left);
- r.top = viewToContent(r.top);
- r.right = viewToContent(r.right);
- r.bottom = viewToContent(r.bottom);
+ r.left = viewToContentX(r.left);
+ r.top = viewToContentY(r.top);
+ r.right = viewToContentX(r.right);
+ r.bottom = viewToContentY(r.bottom);
+ }
+
+ static class ViewSizeData {
+ int mWidth;
+ int mHeight;
+ int mTextWrapWidth;
+ float mScale;
+ boolean mIgnoreHeight;
}
/**
@@ -1762,7 +2011,8 @@ public class WebView extends AbsoluteLayout
* @return true if new values were sent
*/
private boolean sendViewSizeZoom() {
- int newWidth = Math.round(getViewWidth() * mInvActualScale);
+ int viewWidth = getViewWidth();
+ int newWidth = Math.round(viewWidth * mInvActualScale);
int newHeight = Math.round(getViewHeight() * mInvActualScale);
/*
* Because the native side may have already done a layout before the
@@ -1777,8 +2027,17 @@ public class WebView extends AbsoluteLayout
}
// Avoid sending another message if the dimensions have not changed.
if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
- mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED,
- newWidth, newHeight, new Float(mActualScale));
+ ViewSizeData data = new ViewSizeData();
+ data.mWidth = newWidth;
+ data.mHeight = newHeight;
+ // while in zoom overview mode, the text are wrapped to the screen
+ // width matching mLastScale. So that we don't trigger re-flow while
+ // toggling between overview mode and normal mode.
+ data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
+ / mLastScale) : newWidth;
+ data.mScale = mActualScale;
+ data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
+ mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
mLastWidthSent = newWidth;
mLastHeightSent = newHeight;
return true;
@@ -1790,8 +2049,12 @@ public class WebView extends AbsoluteLayout
protected int computeHorizontalScrollRange() {
if (mDrawHistory) {
return mHistoryWidth;
+ } else if (mLastWidthSent == mContentWidth) {
+ // special case to avoid rounding error. Otherwise we may get a
+ // faked scrollbar sometimes.
+ return getViewWidth();
} else {
- return contentToView(mContentWidth);
+ return contentToViewDimension(mContentWidth);
}
}
@@ -1803,7 +2066,14 @@ public class WebView extends AbsoluteLayout
if (mDrawHistory) {
return mHistoryHeight;
} else {
- int height = contentToView(mContentHeight);
+ int height;
+ // special case to avoid rounding error. Otherwise we may get a
+ // faked scrollbar sometimes.
+ if (mLastHeightSent == mContentHeight) {
+ height = getViewHeight();
+ } else {
+ height = contentToViewDimension(mContentHeight);
+ }
if (mFindIsUp) {
height += FIND_HEIGHT;
}
@@ -1811,6 +2081,25 @@ public class WebView extends AbsoluteLayout
}
}
+ @Override
+ protected int computeVerticalScrollOffset() {
+ return Math.max(mScrollY - getTitleHeight(), 0);
+ }
+
+ @Override
+ protected int computeVerticalScrollExtent() {
+ return getViewHeight();
+ }
+
+ /** @hide */
+ @Override
+ protected void onDrawVerticalScrollBar(Canvas canvas,
+ Drawable scrollBar,
+ int l, int t, int r, int b) {
+ scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
+ scrollBar.draw(canvas);
+ }
+
/**
* Get the url for the current page. This is not always the same as the url
* passed to WebViewClient.onPageStarted because although the load for
@@ -1821,10 +2110,10 @@ public class WebView extends AbsoluteLayout
WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
return h != null ? h.getUrl() : null;
}
-
+
/**
- * Get the original url for the current page. This is not always the same
- * as the url passed to WebViewClient.onPageStarted because although the
+ * Get the original url for the current page. This is not always the same
+ * as the url passed to WebViewClient.onPageStarted because although the
* load for that url has begun, the current page may not have changed.
* Also, there may have been redirects resulting in a different url to that
* originally requested.
@@ -1856,13 +2145,22 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Get the touch icon url for the apple-touch-icon <link> element.
+ * @hide
+ */
+ public String getTouchIconUrl() {
+ WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
+ return h != null ? h.getTouchIconUrl() : null;
+ }
+
+ /**
* Get the progress for the current page.
* @return The progress for the current page between 0 and 100.
*/
public int getProgress() {
return mCallbackProxy.getProgress();
}
-
+
/**
* @return the height of the HTML content.
*/
@@ -1871,30 +2169,77 @@ public class WebView extends AbsoluteLayout
}
/**
- * Pause all layout, parsing, and javascript timers. This can be useful if
- * the WebView is not visible or the application has been paused.
+ * Pause all layout, parsing, and javascript timers for all webviews. This
+ * is a global requests, not restricted to just this webview. This can be
+ * useful if the application has been paused.
*/
public void pauseTimers() {
mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
}
/**
- * Resume all layout, parsing, and javascript timers. This will resume
- * dispatching all timers.
+ * Resume all layout, parsing, and javascript timers for all webviews.
+ * This will resume dispatching all timers.
*/
public void resumeTimers() {
mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
}
/**
- * Clear the resource cache. This will cause resources to be re-downloaded
- * if accessed again.
- * <p>
- * Note: this really needs to be a static method as it clears cache for all
- * WebView. But we need mWebViewCore to send message to WebCore thread, so
- * we can't make this static.
+ * Call this to pause any extra processing associated with this view and
+ * its associated DOM/plugins/javascript/etc. For example, if the view is
+ * taken offscreen, this could be called to reduce unnecessary CPU and/or
+ * network traffic. When the view is again "active", call onResume().
+ *
+ * Note that this differs from pauseTimers(), which affects all views/DOMs
+ * @hide
+ */
+ public void onPause() {
+ if (!mIsPaused) {
+ mIsPaused = true;
+ mWebViewCore.sendMessage(EventHub.ON_PAUSE);
+ }
+ }
+
+ /**
+ * Call this to balanace a previous call to onPause()
+ * @hide
+ */
+ public void onResume() {
+ if (mIsPaused) {
+ mIsPaused = false;
+ mWebViewCore.sendMessage(EventHub.ON_RESUME);
+ }
+ }
+
+ /**
+ * Returns true if the view is paused, meaning onPause() was called. Calling
+ * onResume() sets the paused state back to false.
+ * @hide
+ */
+ public boolean isPaused() {
+ return mIsPaused;
+ }
+
+ /**
+ * Call this to inform the view that memory is low so that it can
+ * free any available memory.
+ * @hide
+ */
+ public void freeMemory() {
+ mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
+ }
+
+ /**
+ * Clear the resource cache. Note that the cache is per-application, so
+ * this will clear the cache for all WebViews used.
+ *
+ * @param includeDiskFiles If false, only the RAM cache is cleared.
*/
public void clearCache(boolean includeDiskFiles) {
+ // Note: this really needs to be a static method as it clears cache for all
+ // WebView. But we need mWebViewCore to send message to WebCore thread, so
+ // we can't make this static.
mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
includeDiskFiles ? 1 : 0, 0);
}
@@ -1906,7 +2251,7 @@ public class WebView extends AbsoluteLayout
public void clearFormData() {
if (inEditingMode()) {
AutoCompleteAdapter adapter = null;
- mTextEntry.setAdapterCustom(adapter);
+ mWebTextView.setAdapterCustom(adapter);
}
}
@@ -1940,7 +2285,7 @@ public class WebView extends AbsoluteLayout
/*
* Highlight and scroll to the next occurance of String in findAll.
- * Wraps the page infinitely, and scrolls. Must be called after
+ * Wraps the page infinitely, and scrolls. Must be called after
* calling findAll.
*
* @param forward Direction to search.
@@ -1966,11 +2311,8 @@ public class WebView extends AbsoluteLayout
// or not we draw the highlights for matches.
private boolean mFindIsUp;
- private native int nativeFindAll(String findLower, String findUpper);
- private native void nativeFindNext(boolean forward);
-
/**
- * Return the first substring consisting of the address of a physical
+ * Return the first substring consisting of the address of a physical
* location. Currently, only addresses in the United States are detected,
* and consist of:
* - a house number
@@ -1983,14 +2325,40 @@ public class WebView extends AbsoluteLayout
* All names must be correctly capitalized, and the zip code, if present,
* must be valid for the state. The street type must be a standard USPS
* spelling or abbreviation. The state or territory must also be spelled
- * or abbreviated using USPS standards. The house number may not exceed
+ * or abbreviated using USPS standards. The house number may not exceed
* five digits.
* @param addr The string to search for addresses.
*
* @return the address, or if no address is found, return null.
*/
public static String findAddress(String addr) {
- return WebViewCore.nativeFindAddress(addr);
+ return findAddress(addr, false);
+ }
+
+ /**
+ * @hide
+ * Return the first substring consisting of the address of a physical
+ * location. Currently, only addresses in the United States are detected,
+ * and consist of:
+ * - a house number
+ * - a street name
+ * - a street type (Road, Circle, etc), either spelled out or abbreviated
+ * - a city name
+ * - a state or territory, either spelled out or two-letter abbr.
+ * - an optional 5 digit or 9 digit zip code.
+ *
+ * Names are optionally capitalized, and the zip code, if present,
+ * must be valid for the state. The street type must be a standard USPS
+ * spelling or abbreviation. The state or territory must also be spelled
+ * or abbreviated using USPS standards. The house number may not exceed
+ * five digits.
+ * @param addr The string to search for addresses.
+ * @param caseInsensitive addr Set to true to make search ignore case.
+ *
+ * @return the address, or if no address is found, return null.
+ */
+ public static String findAddress(String addr, boolean caseInsensitive) {
+ return WebViewCore.nativeFindAddress(addr, caseInsensitive);
}
/*
@@ -2067,7 +2435,7 @@ public class WebView extends AbsoluteLayout
animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
invalidate();
} else {
- mScroller.abortAnimation(); // just in case
+ abortAnimation(); // just in case
scrollTo(x, y);
}
return true;
@@ -2075,16 +2443,16 @@ public class WebView extends AbsoluteLayout
// Scale from content to view coordinates, and pin.
// Also called by jni webview.cpp
- private void setContentScrollBy(int cx, int cy, boolean animate) {
+ private boolean setContentScrollBy(int cx, int cy, boolean animate) {
if (mDrawHistory) {
// disallow WebView to change the scroll position as History Picture
// is used in the view system.
// TODO: as we switchOutDrawHistory when trackball or navigation
// keys are hit, this should be safe. Right?
- return;
+ return false;
}
- cx = contentToView(cx);
- cy = contentToView(cy);
+ cx = contentToViewDimension(cx);
+ cy = contentToViewDimension(cy);
if (mHeightCanMeasure) {
// move our visible rect according to scroll request
if (cy != 0) {
@@ -2098,11 +2466,9 @@ public class WebView extends AbsoluteLayout
// FIXME: Why do we only scroll horizontally if there is no
// vertical scroll?
// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
- if (cy == 0 && cx != 0) {
- pinScrollBy(cx, 0, animate, 0);
- }
+ return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
} else {
- pinScrollBy(cx, cy, animate, 0);
+ return pinScrollBy(cx, cy, animate, 0);
}
}
@@ -2118,8 +2484,8 @@ public class WebView extends AbsoluteLayout
// saved scroll position, it is ok to skip this.
return false;
}
- int vx = contentToView(cx);
- int vy = contentToView(cy);
+ int vx = contentToViewX(cx);
+ int vy = contentToViewY(cy);
// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
// vx + " " + vy + "]");
pinScrollTo(vx, vy, false, 0);
@@ -2137,8 +2503,8 @@ public class WebView extends AbsoluteLayout
// is used in the view system.
return;
}
- int vx = contentToView(cx);
- int vy = contentToView(cy);
+ int vx = contentToViewX(cx);
+ int vy = contentToViewY(cy);
pinScrollTo(vx, vy, true, 0);
}
@@ -2155,12 +2521,12 @@ public class WebView extends AbsoluteLayout
}
if (mHeightCanMeasure) {
- if (getMeasuredHeight() != contentToView(mContentHeight)
+ if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
&& updateLayout) {
requestLayout();
}
} else if (mWidthCanMeasure) {
- if (getMeasuredWidth() != contentToView(mContentWidth)
+ if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
&& updateLayout) {
requestLayout();
}
@@ -2201,6 +2567,16 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Gets the chrome handler.
+ * @return the current WebChromeClient instance.
+ *
+ * @hide API council approval.
+ */
+ public WebChromeClient getWebChromeClient() {
+ return mCallbackProxy.getWebChromeClient();
+ }
+
+ /**
* Set the Picture listener. This is an interface used to receive
* notifications of a new Picture.
* @param listener An implementation of WebView.PictureListener.
@@ -2245,10 +2621,9 @@ public class WebView extends AbsoluteLayout
* @param interfaceName The name to used to expose the class in Javascript
*/
public void addJavascriptInterface(Object obj, String interfaceName) {
- // Use Hashmap rather than Bundle as Bundles can't cope with Objects
- HashMap arg = new HashMap();
- arg.put("object", obj);
- arg.put("interfaceName", interfaceName);
+ WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+ arg.mObject = obj;
+ arg.mInterfaceName = interfaceName;
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
}
@@ -2265,26 +2640,19 @@ public class WebView extends AbsoluteLayout
/**
* Return the list of currently loaded plugins.
* @return The list of currently loaded plugins.
+ *
+ * @deprecated This was used for Gears, which has been deprecated.
*/
+ @Deprecated
public static synchronized PluginList getPluginList() {
- if (sPluginList == null) {
- sPluginList = new PluginList();
- }
- return sPluginList;
+ return null;
}
/**
- * Signal the WebCore thread to refresh its list of plugins. Use
- * this if the directory contents of one of the plugin directories
- * has been modified and needs its changes reflecting. May cause
- * plugin load and/or unload.
- * @param reloadOpenPages Set to true to reload all open pages.
+ * @deprecated This was used for Gears, which has been deprecated.
*/
- public void refreshPlugins(boolean reloadOpenPages) {
- if (mWebViewCore != null) {
- mWebViewCore.sendMessage(EventHub.REFRESH_PLUGINS, reloadOpenPages);
- }
- }
+ @Deprecated
+ public void refreshPlugins(boolean reloadOpenPages) { }
//-------------------------------------------------------------------------
// Override View methods
@@ -2292,44 +2660,62 @@ public class WebView extends AbsoluteLayout
@Override
protected void finalize() throws Throwable {
- destroy();
+ try {
+ destroy();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (child == mTitleBar) {
+ // When drawing the title bar, move it horizontally to always show
+ // at the top of the WebView.
+ mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
+ }
+ return super.drawChild(canvas, child, drawingTime);
}
-
+
@Override
protected void onDraw(Canvas canvas) {
+ int saveCount = canvas.getSaveCount();
+ if (mTitleBar != null) {
+ canvas.save();
+ canvas.translate(0, (int) mTitleBar.getHeight());
+ }
// if mNativeClass is 0, the WebView has been destroyed. Do nothing.
if (mNativeClass == 0) {
return;
}
if (mWebViewCore.mEndScaleZoom) {
mWebViewCore.mEndScaleZoom = false;
- if (mTouchMode >= FIRST_SCROLL_ZOOM
+ if (mTouchMode >= FIRST_SCROLL_ZOOM
&& mTouchMode <= LAST_SCROLL_ZOOM) {
setHorizontalScrollBarEnabled(true);
setVerticalScrollBarEnabled(true);
mTouchMode = TOUCH_DONE_MODE;
}
}
- int sc = canvas.save();
+ canvas.save();
if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
scrollZoomDraw(canvas);
} else {
- nativeRecomputeFocus();
// Update the buttons in the picture, so when we draw the picture
// to the screen, they are in the correct state.
// Tell the native side if user is a) touching the screen,
// b) pressing the trackball down, or c) pressing the enter key
- // If the focus is a button, we need to draw it in the pressed
+ // If the cursor is on a button, we need to draw it in the pressed
// state.
// If mNativeClass is 0, we should not reach here, so we do not
// need to check it again.
nativeRecordButtons(hasFocus() && hasWindowFocus(),
mTouchMode == TOUCH_SHORTPRESS_START_MODE
- || mTrackballDown || mGotEnterDown, false);
- drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);
+ || mTrackballDown || mGotCenterDown, false);
+ drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
}
- canvas.restoreToCount(sc);
-
+ canvas.restoreToCount(saveCount);
+
if (AUTO_REDRAW_HACK && mAutoRedraw) {
invalidate();
}
@@ -2345,15 +2731,32 @@ public class WebView extends AbsoluteLayout
@Override
public boolean performLongClick() {
+ if (mNativeClass != 0 && nativeCursorIsTextInput()) {
+ // Send the click so that the textfield is in focus
+ // FIXME: When we start respecting changes to the native textfield's
+ // selection, need to make sure that this does not change it.
+ mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
+ nativeCursorNodePointer());
+ rebuildWebTextView();
+ }
if (inEditingMode()) {
- return mTextEntry.performLongClick();
+ return mWebTextView.performLongClick();
} else {
return super.performLongClick();
}
}
- private void drawCoreAndFocusRing(Canvas canvas, int color,
- boolean drawFocus) {
+ /**
+ * Need to adjust the WebTextView after a change in zoom, since mActualScale
+ * has changed. This is especially important for password fields, which are
+ * drawn by the WebTextView, since it conveys more information than what
+ * webkit draws. Thus we need to reposition it to show in the correct
+ * place.
+ */
+ private boolean mNeedToAdjustWebTextView;
+
+ private void drawCoreAndCursorRing(Canvas canvas, int color,
+ boolean drawCursorRing) {
if (mDrawHistory) {
canvas.scale(mActualScale, mActualScale);
canvas.drawPicture(mHistoryPicture);
@@ -2361,41 +2764,58 @@ public class WebView extends AbsoluteLayout
}
boolean animateZoom = mZoomScale != 0;
- boolean animateScroll = !mScroller.isFinished()
+ boolean animateScroll = !mScroller.isFinished()
|| mVelocityTracker != null;
if (animateZoom) {
float zoomScale;
int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
if (interval < ZOOM_ANIMATION_LENGTH) {
float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
- zoomScale = 1.0f / (mInvInitialZoomScale
+ zoomScale = 1.0f / (mInvInitialZoomScale
+ (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
invalidate();
} else {
zoomScale = mZoomScale;
// set mZoomScale to be 0 as we have done animation
mZoomScale = 0;
+ if (mNeedToAdjustWebTextView) {
+ mNeedToAdjustWebTextView = false;
+ mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ contentToViewDimension(
+ nativeFocusCandidateTextSize()));
+ Rect bounds = nativeFocusCandidateNodeBounds();
+ Rect vBox = contentToView(bounds);
+ mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+ vBox.height());
+ // If it is a password field, start drawing the
+ // WebTextView once again.
+ if (nativeFocusCandidateIsPassword()) {
+ mWebTextView.setInPassword(true);
+ }
+ }
}
- float scale = (mActualScale - zoomScale) * mInvActualScale;
- float tx = scale * (mZoomCenterX + mScrollX);
- float ty = scale * (mZoomCenterY + mScrollY);
-
- // this block pins the translate to "legal" bounds. This makes the
- // animation a bit non-obvious, but it means we won't pop when the
- // "real" zoom takes effect
- if (true) {
- // canvas.translate(mScrollX, mScrollY);
- tx -= mScrollX;
- ty -= mScrollY;
- tx = -pinLoc(-Math.round(tx), getViewWidth(), Math
- .round(mContentWidth * zoomScale));
- ty = -pinLoc(-Math.round(ty), getViewHeight(), Math
- .round(mContentHeight * zoomScale));
- tx += mScrollX;
- ty += mScrollY;
- }
+ float scale = zoomScale * mInvInitialZoomScale;
+ int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
+ - mZoomCenterX);
+ tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
+ * zoomScale)) + mScrollX;
+ int ty = Math.round(scale * (mInitialScrollY + mZoomCenterY)
+ - mZoomCenterY);
+ ty = -pinLoc(ty, getViewHeight(), Math.round(mContentHeight
+ * zoomScale)) + mScrollY;
canvas.translate(tx, ty);
canvas.scale(zoomScale, zoomScale);
+ if (inEditingMode() && !mNeedToAdjustWebTextView
+ && mZoomScale != 0) {
+ // The WebTextView is up. Keep track of this so we can adjust
+ // its size and placement when we finish zooming
+ mNeedToAdjustWebTextView = true;
+ // If it is in password mode, turn it off so it does not draw
+ // misplaced.
+ if (nativeFocusCandidateIsPassword()) {
+ mWebTextView.setInPassword(false);
+ }
+ }
} else {
canvas.scale(mActualScale, mActualScale);
}
@@ -2408,10 +2828,10 @@ public class WebView extends AbsoluteLayout
if (mTouchSelection) {
nativeDrawSelectionRegion(canvas);
} else {
- nativeDrawSelection(canvas, mSelectX, mSelectY,
+ nativeDrawSelection(canvas, mSelectX, mSelectY,
mExtendSelection);
}
- } else if (drawFocus) {
+ } else if (drawCursorRing) {
if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
mTouchMode = TOUCH_SHORTPRESS_MODE;
HitTestResult hitTest = getHitTestResult();
@@ -2422,7 +2842,7 @@ public class WebView extends AbsoluteLayout
LONG_PRESS_TIMEOUT);
}
}
- nativeDrawFocusRing(canvas);
+ nativeDrawCursorRing(canvas);
}
// When the FindDialog is up, only draw the matches if we are not in
// the process of scrolling them into view.
@@ -2431,14 +2851,12 @@ public class WebView extends AbsoluteLayout
}
}
- private native void nativeDrawMatches(Canvas canvas);
-
private float scrollZoomGridScale(float invScale) {
- float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
+ float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
/ (float) SCROLL_ZOOM_GRID;
return 1.0f / griddedInvScale;
}
-
+
private float scrollZoomX(float scale) {
int width = getViewWidth();
float maxScrollZoomX = mContentWidth * scale - width;
@@ -2454,7 +2872,7 @@ public class WebView extends AbsoluteLayout
return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
: maxScrollZoomY / 2);
}
-
+
private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
final float ADORNMENT_LEN = 16.0f;
float width = frame.width();
@@ -2475,13 +2893,13 @@ public class WebView extends AbsoluteLayout
path.offset(frame.left, frame.top);
canvas.drawPath(path, paint);
}
-
- // Returns frame surrounding magified portion of screen while
+
+ // Returns frame surrounding magified portion of screen while
// scroll-zoom is enabled. The frame is also used to center the
// zoom-in zoom-out points at the start and end of the animation.
private Rect scrollZoomFrame(int width, int height, float halfScale) {
Rect scrollFrame = new Rect();
- scrollFrame.set(mZoomScrollX, mZoomScrollY,
+ scrollFrame.set(mZoomScrollX, mZoomScrollY,
mZoomScrollX + width, mZoomScrollY + height);
if (mContentWidth * mZoomScrollLimit < width) {
float scale = zoomFrameScaleX(width, halfScale, 1.0f);
@@ -2497,37 +2915,37 @@ public class WebView extends AbsoluteLayout
}
return scrollFrame;
}
-
+
private float zoomFrameScaleX(int width, float halfScale, float noScale) {
// mContentWidth > width > mContentWidth * mZoomScrollLimit
if (mContentWidth <= width) {
return halfScale;
}
- float part = (width - mContentWidth * mZoomScrollLimit)
+ float part = (width - mContentWidth * mZoomScrollLimit)
/ (width * (1 - mZoomScrollLimit));
return halfScale * part + noScale * (1.0f - part);
}
-
+
private float zoomFrameScaleY(int height, float halfScale, float noScale) {
if (mContentHeight <= height) {
return halfScale;
}
- float part = (height - mContentHeight * mZoomScrollLimit)
+ float part = (height - mContentHeight * mZoomScrollLimit)
/ (height * (1 - mZoomScrollLimit));
return halfScale * part + noScale * (1.0f - part);
}
-
+
private float scrollZoomMagScale(float invScale) {
return (invScale * 2 + mInvActualScale) / 3;
}
-
+
private void scrollZoomDraw(Canvas canvas) {
- float invScale = mZoomScrollInvLimit;
+ float invScale = mZoomScrollInvLimit;
int elapsed = 0;
if (mTouchMode != SCROLL_ZOOM_OUT) {
- elapsed = (int) Math.min(System.currentTimeMillis()
+ elapsed = (int) Math.min(System.currentTimeMillis()
- mZoomScrollStart, SCROLL_ZOOM_DURATION);
- float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
+ float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
* elapsed / SCROLL_ZOOM_DURATION;
if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
invScale = mInvActualScale + transitionScale;
@@ -2545,21 +2963,23 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
setHorizontalScrollBarEnabled(true);
setVerticalScrollBarEnabled(true);
- updateTextEntry();
- scrollTo((int) (scrollFrame.centerX() * mActualScale)
- - (width >> 1), (int) (scrollFrame.centerY()
+ rebuildWebTextView();
+ scrollTo((int) (scrollFrame.centerX() * mActualScale)
+ - (width >> 1), (int) (scrollFrame.centerY()
* mActualScale) - (height >> 1));
mTouchMode = TOUCH_DONE_MODE;
+ // Show all the child views once we are done.
+ mViewManager.showAll();
} else {
mTouchMode = SCROLL_ZOOM_OUT;
}
}
float newX = scrollZoomX(scale);
float newY = scrollZoomY(scale);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
+ ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
- + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
+ + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
+ scale);
}
canvas.translate(newX, newY);
@@ -2603,8 +3023,8 @@ public class WebView extends AbsoluteLayout
}
canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
, mZoomScrollY + height * halfY);
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
+ width + ", " + height + ") half=(" + halfX + ", "
+ halfY + ")");
}
@@ -2632,14 +3052,17 @@ public class WebView extends AbsoluteLayout
, Math.max(0, (int) ((x - left) / scale)));
mZoomScrollY = Math.min(mContentHeight - height
, Math.max(0, (int) ((y - top) / scale)));
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
+ ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
+ mZoomScrollY + ")" + " x=" + x + " y=" + y);
}
}
- private boolean canZoomScrollOut() {
+ /**
+ * @hide
+ */
+ public boolean canZoomScrollOut() {
if (mContentWidth == 0 || mContentHeight == 0) {
return false;
}
@@ -2649,7 +3072,7 @@ public class WebView extends AbsoluteLayout
float y = (float) height / (float) mContentHeight;
mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "canZoomScrollOut"
+ " mInvActualScale=" + mInvActualScale
+ " mZoomScrollLimit=" + mZoomScrollLimit
@@ -2664,7 +3087,7 @@ public class WebView extends AbsoluteLayout
return mContentWidth >= width * limit
|| mContentHeight >= height * limit;
}
-
+
private void startZoomScrollOut() {
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
@@ -2686,34 +3109,39 @@ public class WebView extends AbsoluteLayout
mLastTouchX = halfW;
int halfH = height >> 1;
mLastTouchY = halfH;
- mScroller.abortAnimation();
+ abortAnimation();
mZoomScrollStart = System.currentTimeMillis();
Rect zoomFrame = scrollZoomFrame(width, height
, scrollZoomMagScale(mZoomScrollInvLimit));
- mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
+ mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
- (zoomFrame.width() >> 1));
- mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
+ mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
- (zoomFrame.height() >> 1));
scrollTo(0, 0); // triggers inval, starts animation
clearTextEntry();
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
+ mZoomScrollX + ", " + mZoomScrollY +")");
}
}
-
- private void zoomScrollOut() {
+
+ /**
+ * @hide
+ */
+ public void zoomScrollOut() {
if (canZoomScrollOut() == false) {
mTouchMode = TOUCH_DONE_MODE;
return;
}
+ // Hide the child views while in this mode.
+ mViewManager.hideAll();
startZoomScrollOut();
mTouchMode = SCROLL_ZOOM_ANIMATION_OUT;
invalidate();
}
private void moveZoomScrollWindow(float x, float y) {
- if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
+ if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
&& Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
return;
}
@@ -2725,12 +3153,12 @@ public class WebView extends AbsoluteLayout
int height = getViewHeight();
int maxZoomX = mContentWidth - width;
if (maxZoomX > 0) {
- int maxScreenX = width - (int) Math.ceil(width
+ int maxScreenX = width - (int) Math.ceil(width
* mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "moveZoomScrollWindow-X"
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "moveZoomScrollWindow-X"
+ " maxScreenX=" + maxScreenX + " width=" + width
- + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
+ + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
}
x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
@@ -2738,12 +3166,12 @@ public class WebView extends AbsoluteLayout
}
int maxZoomY = mContentHeight - height;
if (maxZoomY > 0) {
- int maxScreenY = height - (int) Math.ceil(height
+ int maxScreenY = height - (int) Math.ceil(height
* mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "moveZoomScrollWindow-Y"
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "moveZoomScrollWindow-Y"
+ " maxScreenY=" + maxScreenY + " height=" + height
- + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
+ + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
}
y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
@@ -2752,12 +3180,12 @@ public class WebView extends AbsoluteLayout
if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
invalidate();
}
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "moveZoomScrollWindow"
- + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
- + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
- + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
- + " last=("+mLastScrollX+", "+mLastScrollY+")"
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "moveZoomScrollWindow"
+ + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
+ + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
+ + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
+ + " last=("+mLastScrollX+", "+mLastScrollY+")"
+ " x=" + x + " y=" + y);
}
}
@@ -2802,7 +3230,7 @@ public class WebView extends AbsoluteLayout
// Should only be called in UI thread
void switchOutDrawHistory() {
if (null == mWebViewCore) return; // CallbackProxy may trigger this
- if (mDrawHistory) {
+ if (mDrawHistory && mWebViewCore.pictureReady()) {
mDrawHistory = false;
invalidate();
int oldScrollX = mScrollX;
@@ -2818,72 +3246,29 @@ public class WebView extends AbsoluteLayout
}
}
- /**
- * Class representing the node which is focused.
- */
- private class FocusNode {
- public FocusNode() {
- mBounds = new Rect();
- }
- // Only to be called by JNI
- private void setAll(boolean isTextField, boolean isTextArea, boolean
- isPassword, boolean isAnchor, boolean isRtlText, int maxLength,
- int textSize, int boundsX, int boundsY, int boundsRight, int
- boundsBottom, int nodePointer, int framePointer, String text,
- String name, int rootTextGeneration) {
- mIsTextField = isTextField;
- mIsTextArea = isTextArea;
- mIsPassword = isPassword;
- mIsAnchor = isAnchor;
- mIsRtlText = isRtlText;
-
- mMaxLength = maxLength;
- mTextSize = textSize;
-
- mBounds.set(boundsX, boundsY, boundsRight, boundsBottom);
-
-
- mNodePointer = nodePointer;
- mFramePointer = framePointer;
- mText = text;
- mName = name;
- mRootTextGeneration = rootTextGeneration;
- }
- public boolean mIsTextField;
- public boolean mIsTextArea;
- public boolean mIsPassword;
- public boolean mIsAnchor;
- public boolean mIsRtlText;
-
- public int mSelectionStart;
- public int mSelectionEnd;
- public int mMaxLength;
- public int mTextSize;
-
- public Rect mBounds;
-
- public int mNodePointer;
- public int mFramePointer;
- public String mText;
- public String mName;
- public int mRootTextGeneration;
- }
-
- // Warning: ONLY use mFocusNode AFTER calling nativeUpdateFocusNode(),
- // and ONLY if it returns true;
- private FocusNode mFocusNode = new FocusNode();
-
+ WebViewCore.CursorData cursorData() {
+ WebViewCore.CursorData result = new WebViewCore.CursorData();
+ result.mMoveGeneration = nativeMoveGeneration();
+ result.mFrame = nativeCursorFramePointer();
+ Point position = nativeCursorPosition();
+ result.mX = position.x;
+ result.mY = position.y;
+ return result;
+ }
+
/**
* Delete text from start to end in the focused textfield. If there is no
- * focus, or if start == end, silently fail. If start and end are out of
+ * focus, or if start == end, silently fail. If start and end are out of
* order, swap them.
* @param start Beginning of selection to delete.
* @param end End of selection to delete.
*/
/* package */ void deleteSelection(int start, int end) {
mTextGeneration++;
- mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
- new WebViewCore.FocusData(mFocusData));
+ WebViewCore.TextSelectionData data
+ = new WebViewCore.TextSelectionData(start, end);
+ mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
+ data);
}
/**
@@ -2893,119 +3278,139 @@ public class WebView extends AbsoluteLayout
* @param end End of selection.
*/
/* package */ void setSelection(int start, int end) {
- mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end,
- new WebViewCore.FocusData(mFocusData));
+ mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
}
// Called by JNI when a touch event puts a textfield into focus.
- private void displaySoftKeyboard() {
+ private void displaySoftKeyboard(boolean isTextView) {
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mTextEntry, 0);
- mTextEntry.enableScrollOnScreen(true);
- // Now we need to fake a touch event to place the cursor where the
- // user touched.
- AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
- mTextEntry.getLayoutParams();
- if (lp != null) {
- // Take the last touch and adjust for the location of the
- // TextDialog.
- float x = mLastTouchX + (float) (mScrollX - lp.x);
- float y = mLastTouchY + (float) (mScrollY - lp.y);
- mTextEntry.fakeTouchEvent(x, y);
- }
- }
-
- private void updateTextEntry() {
- if (mTextEntry == null) {
- mTextEntry = new TextDialog(mContext, WebView.this);
- // Initialize our generation number.
- mTextGeneration = 0;
+
+ if (isTextView) {
+ imm.showSoftInput(mWebTextView, 0);
+ // Now we need to fake a touch event to place the cursor where the
+ // user touched.
+ AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
+ mWebTextView.getLayoutParams();
+ if (lp != null) {
+ // Take the last touch and adjust for the location of the
+ // WebTextView.
+ float x = mLastTouchX + (float) (mScrollX - lp.x);
+ float y = mLastTouchY + (float) (mScrollY - lp.y);
+ mWebTextView.fakeTouchEvent(x, y);
+ }
+ if (mInZoomOverview) {
+ // if in zoom overview mode, call doDoubleTap() to bring it back
+ // to normal mode so that user can enter text.
+ doDoubleTap();
+ }
+ }
+ else { // used by plugins
+ imm.showSoftInput(this, 0);
}
- // If we do not have focus, do nothing until we gain focus.
- if (!hasFocus() && !mTextEntry.hasFocus()
- || (mTouchMode >= FIRST_SCROLL_ZOOM
+ }
+
+ // Called by WebKit to instruct the UI to hide the keyboard
+ private void hideSoftKeyboard() {
+ InputMethodManager imm = (InputMethodManager)
+ getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
+ }
+
+ /*
+ * This method checks the current focus and cursor and potentially rebuilds
+ * mWebTextView to have the appropriate properties, such as password,
+ * multiline, and what text it contains. It also removes it if necessary.
+ */
+ /* package */ void rebuildWebTextView() {
+ // If the WebView does not have focus, do nothing until it gains focus.
+ if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())
+ || (mTouchMode >= FIRST_SCROLL_ZOOM
&& mTouchMode <= LAST_SCROLL_ZOOM)) {
- mNeedsUpdateTextEntry = true;
return;
}
boolean alreadyThere = inEditingMode();
- if (0 == mNativeClass || !nativeUpdateFocusNode()) {
+ // inEditingMode can only return true if mWebTextView is non-null,
+ // so we can safely call remove() if (alreadyThere)
+ if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
if (alreadyThere) {
- mTextEntry.remove();
+ mWebTextView.remove();
}
return;
}
- FocusNode node = mFocusNode;
- if (!node.mIsTextField && !node.mIsTextArea) {
- if (alreadyThere) {
- mTextEntry.remove();
- }
- return;
+ // At this point, we know we have found an input field, so go ahead
+ // and create the WebTextView if necessary.
+ if (mWebTextView == null) {
+ mWebTextView = new WebTextView(mContext, WebView.this);
+ // Initialize our generation number.
+ mTextGeneration = 0;
}
- mTextEntry.setTextSize(contentToView(node.mTextSize));
- Rect visibleRect = sendOurVisibleRect();
+ mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ contentToViewDimension(nativeFocusCandidateTextSize()));
+ Rect visibleRect = new Rect();
+ calcOurContentVisibleRect(visibleRect);
// Note that sendOurVisibleRect calls viewToContent, so the coordinates
// should be in content coordinates.
- if (!Rect.intersects(node.mBounds, visibleRect)) {
- // Node is not on screen, so do not bother.
- return;
+ Rect bounds = nativeFocusCandidateNodeBounds();
+ if (!Rect.intersects(bounds, visibleRect)) {
+ mWebTextView.bringIntoView();
}
- int x = node.mBounds.left;
- int y = node.mBounds.top;
- int width = node.mBounds.width();
- int height = node.mBounds.height();
- if (alreadyThere && mTextEntry.isSameTextField(node.mNodePointer)) {
+ String text = nativeFocusCandidateText();
+ int nodePointer = nativeFocusCandidatePointer();
+ if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
// It is possible that we have the same textfield, but it has moved,
// i.e. In the case of opening/closing the screen.
// In that case, we need to set the dimensions, but not the other
// aspects.
// We also need to restore the selection, which gets wrecked by
// calling setTextEntryRect.
- Spannable spannable = (Spannable) mTextEntry.getText();
+ Spannable spannable = (Spannable) mWebTextView.getText();
int start = Selection.getSelectionStart(spannable);
int end = Selection.getSelectionEnd(spannable);
- setTextEntryRect(x, y, width, height);
// If the text has been changed by webkit, update it. However, if
// there has been more UI text input, ignore it. We will receive
// another update when that text is recognized.
- if (node.mText != null && !node.mText.equals(spannable.toString())
- && node.mRootTextGeneration == mTextGeneration) {
- mTextEntry.setTextAndKeepSelection(node.mText);
+ if (text != null && !text.equals(spannable.toString())
+ && nativeTextGeneration() == mTextGeneration) {
+ mWebTextView.setTextAndKeepSelection(text);
} else {
Selection.setSelection(spannable, start, end);
}
} else {
- String text = node.mText;
- setTextEntryRect(x, y, width, height);
- mTextEntry.setGravity(node.mIsRtlText ? Gravity.RIGHT :
- Gravity.NO_GRAVITY);
+ Rect vBox = contentToView(bounds);
+ mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
+ vBox.height());
+ mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
+ Gravity.RIGHT : Gravity.NO_GRAVITY);
// this needs to be called before update adapter thread starts to
- // ensure the mTextEntry has the same node pointer
- mTextEntry.setNodePointer(node.mNodePointer);
+ // ensure the mWebTextView has the same node pointer
+ mWebTextView.setNodePointer(nodePointer);
int maxLength = -1;
- if (node.mIsTextField) {
- maxLength = node.mMaxLength;
+ boolean isTextField = nativeFocusCandidateIsTextField();
+ if (isTextField) {
+ maxLength = nativeFocusCandidateMaxLength();
+ String name = nativeFocusCandidateName();
if (mWebViewCore.getSettings().getSaveFormData()
- && node.mName != null) {
- HashMap data = new HashMap();
- data.put("text", node.mText);
+ && name != null) {
Message update = mPrivateHandler.obtainMessage(
- UPDATE_TEXT_ENTRY_ADAPTER, node.mNodePointer, 0,
- data);
- UpdateTextEntryAdapter updater = new UpdateTextEntryAdapter(
- node.mName, getUrl(), update);
+ REQUEST_FORM_DATA, nodePointer);
+ RequestFormData updater = new RequestFormData(name,
+ getUrl(), update);
Thread t = new Thread(updater);
t.start();
}
}
- mTextEntry.setMaxLength(maxLength);
+ mWebTextView.setMaxLength(maxLength);
AutoCompleteAdapter adapter = null;
- mTextEntry.setAdapterCustom(adapter);
- mTextEntry.setSingleLine(node.mIsTextField);
- mTextEntry.setInPassword(node.mIsPassword);
+ mWebTextView.setAdapterCustom(adapter);
+ mWebTextView.setSingleLine(isTextField);
+ mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
if (null == text) {
- mTextEntry.setText("", 0, 0);
+ mWebTextView.setText("", 0, 0);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView null == text");
+ }
} else {
// Change to true to enable the old style behavior, where
// entering a textfield/textarea always set the selection to the
@@ -3016,24 +3421,35 @@ public class WebView extends AbsoluteLayout
// textarea. Testing out a new behavior, where textfields set
// selection at the end, and textareas at the beginning.
if (false) {
- mTextEntry.setText(text, 0, text.length());
- } else if (node.mIsTextField) {
+ mWebTextView.setText(text, 0, text.length());
+ } else if (isTextField) {
int length = text.length();
- mTextEntry.setText(text, length, length);
+ mWebTextView.setText(text, length, length);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView length=" + length);
+ }
} else {
- mTextEntry.setText(text, 0, 0);
+ mWebTextView.setText(text, 0, 0);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "rebuildWebTextView !isTextField");
+ }
}
}
- mTextEntry.requestFocus();
+ mWebTextView.requestFocus();
}
}
- private class UpdateTextEntryAdapter implements Runnable {
+ /*
+ * This class requests an Adapter for the WebTextView which shows past
+ * entries stored in the database. It is a Runnable so that it can be done
+ * in its own thread, without slowing down the UI.
+ */
+ private class RequestFormData implements Runnable {
private String mName;
private String mUrl;
private Message mUpdateMessage;
- public UpdateTextEntryAdapter(String name, String url, Message msg) {
+ public RequestFormData(String name, String url, Message msg) {
mName = name;
mUrl = url;
mUpdateMessage = msg;
@@ -3044,29 +3460,21 @@ public class WebView extends AbsoluteLayout
if (pastEntries.size() > 0) {
AutoCompleteAdapter adapter = new
AutoCompleteAdapter(mContext, pastEntries);
- ((HashMap) mUpdateMessage.obj).put("adapter", adapter);
+ mUpdateMessage.obj = adapter;
mUpdateMessage.sendToTarget();
}
}
}
- private void setTextEntryRect(int x, int y, int width, int height) {
- x = contentToView(x);
- y = contentToView(y);
- width = contentToView(width);
- height = contentToView(height);
- mTextEntry.setRect(x, y, width, height);
- }
-
- // This is used to determine long press with the enter key, or
- // a center key. Does not affect long press with the trackball/touch.
- private boolean mGotEnterDown = false;
+ // This is used to determine long press with the center key. Does not
+ // affect long press with the trackball/touch.
+ private boolean mGotCenterDown = false;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
- + ", " + event);
+ + ", " + event + ", unicode=" + event.getUnicodeChar());
}
if (mNativeClass == 0) {
@@ -3092,29 +3500,27 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
- && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
+ if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
+ && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
mExtendSelection = false;
mShiftIsPressed = true;
- if (nativeUpdateFocusNode()) {
- FocusNode node = mFocusNode;
- mSelectX = contentToView(node.mBounds.left);
- mSelectY = contentToView(node.mBounds.top);
+ if (nativeHasCursorNode()) {
+ Rect rect = nativeCursorNodeBounds();
+ mSelectX = contentToViewX(rect.left);
+ mSelectY = contentToViewY(rect.top);
} else {
mSelectX = mScrollX + (int) mLastTouchX;
mSelectY = mScrollY + (int) mLastTouchY;
}
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
- nativeClearFocus(contentX, contentY);
+ nativeHideCursor();
}
if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
&& keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
// always handle the navigation keys in the UI thread
switchOutDrawHistory();
- if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
+ if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
playSoundEffect(keyCodeToSoundsEffect(keyCode));
return true;
}
@@ -3122,13 +3528,12 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
- || keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
switchOutDrawHistory();
if (event.getRepeatCount() == 0) {
- mGotEnterDown = true;
+ mGotCenterDown = true;
mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
+ .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
// Already checked mNativeClass, so we do not need to check it
// again.
nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
@@ -3138,6 +3543,15 @@ public class WebView extends AbsoluteLayout
return false;
}
+ if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
+ && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
+ // turn off copy select if a shift-key combo is pressed
+ mExtendSelection = mShiftIsPressed = false;
+ if (mTouchMode == TOUCH_SELECT_MODE) {
+ mTouchMode = TOUCH_INIT_MODE;
+ }
+ }
+
if (getSettings().getNavDump()) {
switch (keyCode) {
case KeyEvent.KEYCODE_4:
@@ -3166,8 +3580,30 @@ public class WebView extends AbsoluteLayout
}
}
+ if (nativeCursorIsPlugin()) {
+ nativeUpdatePluginReceivesEvents();
+ invalidate();
+ } else if (nativeCursorIsTextInput()) {
+ // This message will put the node in focus, for the DOM's notion
+ // of focus, and make the focuscontroller active
+ mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
+ nativeCursorNodePointer());
+ // This will bring up the WebTextView and put it in focus, for
+ // our view system's notion of focus
+ rebuildWebTextView();
+ // Now we need to pass the event to it
+ return mWebTextView.onKeyDown(keyCode, event);
+ } else if (nativeHasFocusNode()) {
+ // In this case, the cursor is not on a text input, but the focus
+ // might be. Check it, and if so, hand over to the WebTextView.
+ rebuildWebTextView();
+ if (inEditingMode()) {
+ return mWebTextView.onKeyDown(keyCode, event);
+ }
+ }
+
// TODO: should we pass all the keys to DOM or check the meta tag
- if (nativeFocusNodeWantsKeyEvents() || true) {
+ if (nativeCursorWantsKeyEvents() || true) {
// pass the key to DOM
mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
// return true as DOM handles the key
@@ -3180,20 +3616,19 @@ public class WebView extends AbsoluteLayout
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
- + ", " + event);
+ + ", " + event + ", unicode=" + event.getUnicodeChar());
}
if (mNativeClass == 0) {
return false;
}
- // special CALL handling when focus node's href is "tel:XXX"
- if (keyCode == KeyEvent.KEYCODE_CALL && nativeUpdateFocusNode()) {
- FocusNode node = mFocusNode;
- String text = node.mText;
- if (!node.mIsTextField && !node.mIsTextArea && text != null
+ // special CALL handling when cursor node's href is "tel:XXX"
+ if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
+ String text = nativeCursorText();
+ if (!nativeCursorIsTextInput() && text != null
&& text.startsWith(SCHEME_TEL)) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
getContext().startActivity(intent);
@@ -3220,7 +3655,7 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
+ if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
if (commitCopy()) {
return true;
@@ -3234,55 +3669,30 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
- || keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// remove the long press message first
- mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
- mGotEnterDown = false;
+ mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+ mGotCenterDown = false;
- if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
- if (mShiftIsPressed) {
- return false;
- }
- if (getSettings().supportZoom()) {
- if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
- zoomScrollOut();
- } else {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
- }
- mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(SWITCH_TO_ENTER), TAP_TIMEOUT);
- mTouchMode = TOUCH_DOUBLECLICK_MODE;
- }
- return true;
- }
+ if (mShiftIsPressed) {
+ return false;
}
-
- Rect visibleRect = sendOurVisibleRect();
- // Note that sendOurVisibleRect calls viewToContent, so the
- // coordinates should be in content coordinates.
- if (nativeUpdateFocusNode()) {
- if (Rect.intersects(mFocusNode.mBounds, visibleRect)) {
- nativeSetFollowedLink(true);
- mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
- EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
- new WebViewCore.FocusData(mFocusData));
- playSoundEffect(SoundEffectConstants.CLICK);
- if (!mCallbackProxy.uiOverrideUrlLoading(mFocusNode.mText)) {
- // use CLICK instead of KEY_DOWN/KEY_UP so that we can
- // trigger mouse click events
- mWebViewCore.sendMessage(EventHub.CLICK);
- }
+ if (getSettings().supportZoom()
+ && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
+ zoomScrollOut();
+ } else {
+ mPrivateHandler.sendMessageDelayed(mPrivateHandler
+ .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
}
- return true;
+ mTouchMode = TOUCH_DOUBLECLICK_MODE;
}
- // Bubble up the key event as WebView doesn't handle it
- return false;
+ return true;
}
// TODO: should we pass all the keys to DOM or check the meta tag
- if (nativeFocusNodeWantsKeyEvents() || true) {
+ if (nativeCursorWantsKeyEvents() || true) {
// pass the key to DOM
mWebViewCore.sendMessage(EventHub.KEY_UP, event);
// return true as DOM handles the key
@@ -3292,16 +3702,14 @@ public class WebView extends AbsoluteLayout
// Bubble up the key event as WebView doesn't handle it
return false;
}
-
+
/**
* @hide
*/
public void emulateShiftHeld() {
mExtendSelection = false;
mShiftIsPressed = true;
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
- nativeClearFocus(contentX, contentY);
+ nativeHideCursor();
}
private boolean commitCopy() {
@@ -3349,16 +3757,13 @@ public class WebView extends AbsoluteLayout
// Clean up the zoom controller
mZoomButtonsController.setVisible(false);
}
-
+
// Implementation for OnHierarchyChangeListener
public void onChildViewAdded(View parent, View child) {}
-
+
public void onChildViewRemoved(View p, View child) {
if (child == this) {
- if (inEditingMode()) {
- clearTextEntry();
- mNeedsUpdateTextEntry = true;
- }
+ clearTextEntry();
}
}
@@ -3371,26 +3776,25 @@ public class WebView extends AbsoluteLayout
public void onGlobalFocusChanged(View oldFocus, View newFocus) {
}
- // To avoid drawing the focus ring, and remove the TextView when our window
+ // To avoid drawing the cursor ring, and remove the TextView when our window
// loses focus.
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
if (hasWindowFocus) {
if (hasFocus()) {
// If our window regained focus, and we have focus, then begin
- // drawing the focus ring, and restore the TextView if
- // necessary.
- mDrawFocusRing = true;
- if (mNeedsUpdateTextEntry) {
- updateTextEntry();
- }
+ // drawing the cursor ring
+ mDrawCursorRing = true;
if (mNativeClass != 0) {
nativeRecordButtons(true, false, true);
+ if (inEditingMode()) {
+ mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
+ }
}
} else {
// If our window gained focus, but we do not have it, do not
- // draw the focus ring.
- mDrawFocusRing = false;
+ // draw the cursor ring.
+ mDrawCursorRing = false;
// We do not call nativeRecordButtons here because we assume
// that when we lost focus, or window focus, it got called with
// false for the first parameter
@@ -3399,39 +3803,49 @@ public class WebView extends AbsoluteLayout
if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
/*
* The zoom controls come in their own window, so our window
- * loses focus. Our policy is to not draw the focus ring if
+ * loses focus. Our policy is to not draw the cursor ring if
* our window is not focused, but this is an exception since
* the user can still navigate the web page with the zoom
* controls showing.
*/
- // If our window has lost focus, stop drawing the focus ring
- mDrawFocusRing = false;
+ // If our window has lost focus, stop drawing the cursor ring
+ mDrawCursorRing = false;
}
mGotKeyDown = false;
mShiftIsPressed = false;
if (mNativeClass != 0) {
nativeRecordButtons(false, false, true);
}
+ setFocusControllerInactive();
}
invalidate();
super.onWindowFocusChanged(hasWindowFocus);
}
+ /*
+ * Pass a message to WebCore Thread, telling the WebCore::Page's
+ * FocusController to be "inactive" so that it will
+ * not draw the blinking cursor. It gets set to "active" to draw the cursor
+ * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
+ */
+ /* package */ void setFocusControllerInactive() {
+ // Do not need to also check whether mWebViewCore is null, because
+ // mNativeClass is only set if mWebViewCore is non null
+ if (mNativeClass == 0) return;
+ mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
+ }
+
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
}
if (focused) {
// When we regain focus, if we have window focus, resume drawing
- // the focus ring, and add the TextView if necessary.
+ // the cursor ring
if (hasWindowFocus()) {
- mDrawFocusRing = true;
- if (mNeedsUpdateTextEntry) {
- updateTextEntry();
- mNeedsUpdateTextEntry = false;
- }
+ mDrawCursorRing = true;
if (mNativeClass != 0) {
nativeRecordButtons(true, false, true);
}
@@ -3442,12 +3856,13 @@ public class WebView extends AbsoluteLayout
}
} else {
// When we lost focus, unless focus went to the TextView (which is
- // true if we are in editing mode), stop drawing the focus ring.
+ // true if we are in editing mode), stop drawing the cursor ring.
if (!inEditingMode()) {
- mDrawFocusRing = false;
+ mDrawCursorRing = false;
if (mNativeClass != 0) {
nativeRecordButtons(false, false, true);
}
+ setFocusControllerInactive();
}
mGotKeyDown = false;
}
@@ -3459,13 +3874,16 @@ public class WebView extends AbsoluteLayout
protected void onSizeChanged(int w, int h, int ow, int oh) {
super.onSizeChanged(w, h, ow, oh);
// Center zooming to the center of the screen.
- mZoomCenterX = getViewWidth() * .5f;
- mZoomCenterY = getViewHeight() * .5f;
+ if (mZoomScale == 0) { // unless we're already zooming
+ mZoomCenterX = getViewWidth() * .5f;
+ mZoomCenterY = getViewHeight() * .5f;
+ }
// update mMinZoomScale if the minimum zoom scale is not fixed
if (!mMinZoomScaleFixed) {
mMinZoomScale = (float) getViewWidth()
- / Math.max(ZOOM_OUT_WIDTH, mContentWidth);
+ / (mDrawHistory ? mHistoryPicture.getWidth()
+ : mZoomOverviewWidth);
}
// we always force, in case our height changed, in which case we still
@@ -3476,10 +3894,11 @@ public class WebView extends AbsoluteLayout
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
+
sendOurVisibleRect();
}
-
-
+
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
boolean dispatch = true;
@@ -3523,7 +3942,7 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
+ mTouchMode);
}
@@ -3548,12 +3967,12 @@ public class WebView extends AbsoluteLayout
if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
&& mTouchMode != SCROLL_ZOOM_ANIMATION_IN
&& mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
- && (action != MotionEvent.ACTION_MOVE ||
+ && (action != MotionEvent.ACTION_MOVE ||
eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
ted.mAction = action;
- ted.mX = viewToContent((int) x + mScrollX);
- ted.mY = viewToContent((int) y + mScrollY);
+ ted.mX = viewToContentX((int) x + mScrollX);
+ ted.mY = viewToContentY((int) y + mScrollY);
mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
mLastSentTouchTime = eventTime;
}
@@ -3572,6 +3991,9 @@ public class WebView extends AbsoluteLayout
mLastScrollY = mZoomScrollY;
// If two taps are close, ignore the first tap
} else if (!mScroller.isFinished()) {
+ // stop the current scroll animation, but if this is
+ // the start of a fling, allow it to add to the current
+ // fling's velocity
mScroller.abortAnimation();
mTouchMode = TOUCH_DRAG_START_MODE;
mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
@@ -3579,22 +4001,34 @@ public class WebView extends AbsoluteLayout
mSelectX = mScrollX + (int) x;
mSelectY = mScrollY + (int) y;
mTouchMode = TOUCH_SELECT_MODE;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
}
- nativeMoveSelection(viewToContent(mSelectX)
- , viewToContent(mSelectY), false);
+ nativeMoveSelection(viewToContentX(mSelectX),
+ viewToContentY(mSelectY), false);
mTouchSelection = mExtendSelection = true;
+ } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
+ mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
+ if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
+ mTouchMode = TOUCH_DOUBLE_TAP_MODE;
+ } else {
+ // commit the short press action for the previous tap
+ doShortPress();
+ // continue, mTouchMode should be still TOUCH_INIT_MODE
+ }
} else {
mTouchMode = TOUCH_INIT_MODE;
mPreventDrag = mForwardTouchEvents;
+ mWebViewCore.sendMessage(
+ EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
(eventTime - mLastTouchUpTime), eventTime);
}
}
// Trigger the link
- if (mTouchMode == TOUCH_INIT_MODE) {
+ if (mTouchMode == TOUCH_INIT_MODE
+ || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
mPrivateHandler.sendMessageDelayed(mPrivateHandler
.obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
}
@@ -3607,7 +4041,7 @@ public class WebView extends AbsoluteLayout
break;
}
case MotionEvent.ACTION_MOVE: {
- if (mTouchMode == TOUCH_DONE_MODE
+ if (mTouchMode == TOUCH_DONE_MODE
|| mTouchMode == SCROLL_ZOOM_ANIMATION_IN
|| mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
// no dragging during scroll zoom animation
@@ -3624,11 +4058,11 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_SELECT_MODE) {
mSelectX = mScrollX + (int) x;
mSelectY = mScrollY + (int) y;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
}
- nativeMoveSelection(viewToContent(mSelectX)
- , viewToContent(mSelectY), true);
+ nativeMoveSelection(viewToContentX(mSelectX),
+ viewToContentY(mSelectY), true);
invalidate();
break;
}
@@ -3640,7 +4074,8 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_SHORTPRESS_MODE
|| mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
- } else if (mTouchMode == TOUCH_INIT_MODE) {
+ } else if (mTouchMode == TOUCH_INIT_MODE
+ || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
}
@@ -3657,22 +4092,14 @@ public class WebView extends AbsoluteLayout
mTouchMode = TOUCH_DRAG_MODE;
WebViewCore.pauseUpdate(mWebViewCore);
- int contentX = viewToContent((int) x + mScrollX);
- int contentY = viewToContent((int) y + mScrollY);
- if (inEditingMode()) {
- mTextEntry.updateCachedTextfield();
- }
- nativeClearFocus(contentX, contentY);
- // remove the zoom anchor if there is any
- if (mZoomScale != 0) {
- mWebViewCore
- .sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
+ if (!mDragFromTextInput) {
+ nativeHideCursor();
}
WebSettings settings = getSettings();
if (settings.supportZoom()
&& settings.getBuiltInZoomControls()
&& !mZoomButtonsController.isVisible()
- && (canZoomScrollOut() ||
+ && (canZoomScrollOut() ||
mMinZoomScale < mMaxZoomScale)) {
mZoomButtonsController.setVisible(true);
}
@@ -3698,7 +4125,7 @@ public class WebView extends AbsoluteLayout
}
// reverse direction means lock in the snap mode
if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
- ((mSnapPositive &&
+ ((mSnapPositive &&
deltaX < -mMinLockSnapReverseDistance)
|| (!mSnapPositive &&
deltaX > mMinLockSnapReverseDistance))) {
@@ -3712,9 +4139,9 @@ public class WebView extends AbsoluteLayout
}
// reverse direction means lock in the snap mode
if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
- ((mSnapPositive &&
+ ((mSnapPositive &&
deltaY < -mMinLockSnapReverseDistance)
- || (!mSnapPositive &&
+ || (!mSnapPositive &&
deltaY > mMinLockSnapReverseDistance))) {
mSnapScrollMode = SNAP_Y_LOCK;
}
@@ -3762,7 +4189,23 @@ public class WebView extends AbsoluteLayout
case MotionEvent.ACTION_UP: {
mLastTouchUpTime = eventTime;
switch (mTouchMode) {
+ case TOUCH_DOUBLE_TAP_MODE: // double tap
+ mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+ mTouchMode = TOUCH_DONE_MODE;
+ doDoubleTap();
+ break;
case TOUCH_INIT_MODE: // tap
+ if (ENABLE_DOUBLETAP_ZOOM) {
+ mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+ if (!mPreventDrag) {
+ mPrivateHandler.sendMessageDelayed(
+ mPrivateHandler.obtainMessage(
+ RELEASE_SINGLE_TAP),
+ ViewConfiguration.getDoubleTapTimeout());
+ }
+ break;
+ }
+ // fall through
case TOUCH_SHORTPRESS_START_MODE:
case TOUCH_SHORTPRESS_MODE:
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
@@ -3779,7 +4222,7 @@ public class WebView extends AbsoluteLayout
// no action during scroll animation
break;
case SCROLL_ZOOM_OUT:
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
+ " eventTime - mLastTouchTime="
+ (eventTime - mLastTouchTime));
@@ -3804,6 +4247,7 @@ public class WebView extends AbsoluteLayout
doFling();
break;
}
+ mLastVelocity = 0;
WebViewCore.resumeUpdate(mWebViewCore);
break;
case TOUCH_DRAG_START_MODE:
@@ -3837,18 +4281,13 @@ public class WebView extends AbsoluteLayout
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
mTouchMode = TOUCH_DONE_MODE;
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
- if (inEditingMode()) {
- mTextEntry.updateCachedTextfield();
- }
- nativeClearFocus(contentX, contentY);
+ nativeHideCursor();
break;
}
}
return true;
}
-
+
private long mTrackballFirstTime = 0;
private long mTrackballLastTime = 0;
private float mTrackballRemainsX = 0.0f;
@@ -3870,14 +4309,14 @@ public class WebView extends AbsoluteLayout
private boolean mShiftIsPressed = false;
private boolean mTrackballDown = false;
private long mTrackballUpTime = 0;
- private long mLastFocusTime = 0;
- private Rect mLastFocusBounds;
+ private long mLastCursorTime = 0;
+ private Rect mLastCursorBounds;
// Set by default; BrowserActivity clears to interpret trackball data
- // directly for movement. Currently, the framework only passes
+ // directly for movement. Currently, the framework only passes
// arrow key events, not trackball events, from one child to the next
private boolean mMapTrackballToArrowKeys = true;
-
+
public void setMapTrackballToArrowKeys(boolean setMap) {
mMapTrackballToArrowKeys = setMap;
}
@@ -3895,26 +4334,27 @@ public class WebView extends AbsoluteLayout
return true;
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
+ mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
mTrackballDown = true;
- if (mNativeClass != 0) {
- nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
+ if (mNativeClass == 0) {
+ return false;
}
- if (time - mLastFocusTime <= TRACKBALL_TIMEOUT
- && !mLastFocusBounds.equals(nativeGetFocusRingBounds())) {
- nativeSelectBestAt(mLastFocusBounds);
+ nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
+ if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
+ && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
+ nativeSelectBestAt(mLastCursorBounds);
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
- + " time=" + time
- + " mLastFocusTime=" + mLastFocusTime);
+ + " time=" + time
+ + " mLastCursorTime=" + mLastCursorTime);
}
if (isInTouchMode()) requestFocusFromTouch();
return false; // let common code in onKeyDown at it
- }
+ }
if (ev.getAction() == MotionEvent.ACTION_UP) {
- // LONG_PRESS_ENTER is set in common onKeyDown
- mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
+ // LONG_PRESS_CENTER is set in common onKeyDown
+ mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
mTrackballDown = false;
mTrackballUpTime = time;
if (mShiftIsPressed) {
@@ -3924,42 +4364,42 @@ public class WebView extends AbsoluteLayout
mExtendSelection = true;
}
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
- + " time=" + time
+ + " time=" + time
);
}
return false; // let common code in onKeyUp at it
}
if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent gmail quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
return false;
}
- // no move if we're still waiting on SWITCH_TO_ENTER timeout
+ // no move if we're still waiting on SWITCH_TO_CLICK timeout
if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
return true;
}
if (mTrackballDown) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent down quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
return true; // discard move if trackball is down
}
if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
return true;
}
// TODO: alternatively we can do panning as touch does
switchOutDrawHistory();
if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "onTrackballEvent time="
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "onTrackballEvent time="
+ time + " last=" + mTrackballLastTime);
}
mTrackballFirstTime = time;
mTrackballXMove = mTrackballYMove = 0;
}
mTrackballLastTime = time;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
}
mTrackballRemainsX += ev.getX();
@@ -3967,7 +4407,7 @@ public class WebView extends AbsoluteLayout
doTrackball(time);
return true;
}
-
+
void moveSelection(float xRate, float yRate) {
if (mNativeClass == 0)
return;
@@ -3981,8 +4421,8 @@ public class WebView extends AbsoluteLayout
, mSelectX));
mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
, mSelectY));
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "moveSelection"
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "moveSelection"
+ " mSelectX=" + mSelectX
+ " mSelectY=" + mSelectY
+ " mScrollX=" + mScrollX
@@ -3991,13 +4431,13 @@ public class WebView extends AbsoluteLayout
+ " yRate=" + yRate
);
}
- nativeMoveSelection(viewToContent(mSelectX)
- , viewToContent(mSelectY), mExtendSelection);
+ nativeMoveSelection(viewToContentX(mSelectX),
+ viewToContentY(mSelectY), mExtendSelection);
int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
- : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
+ : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
: 0;
int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
- : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
+ : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
: 0;
pinScrollBy(scrollX, scrollY, true, 0);
Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
@@ -4054,7 +4494,7 @@ public class WebView extends AbsoluteLayout
if (elapsed == 0) {
elapsed = TRACKBALL_TIMEOUT;
}
- float xRate = mTrackballRemainsX * 1000 / elapsed;
+ float xRate = mTrackballRemainsX * 1000 / elapsed;
float yRate = mTrackballRemainsY * 1000 / elapsed;
if (mShiftIsPressed) {
moveSelection(xRate, yRate);
@@ -4064,7 +4504,7 @@ public class WebView extends AbsoluteLayout
float ax = Math.abs(xRate);
float ay = Math.abs(yRate);
float maxA = Math.max(ax, ay);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
+ " xRate=" + xRate
+ " yRate=" + yRate
@@ -4081,9 +4521,9 @@ public class WebView extends AbsoluteLayout
int maxWH = Math.max(width, height);
mZoomScrollX += scaleTrackballX(xRate, maxWH);
mZoomScrollY += scaleTrackballY(yRate, maxWH);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
- + " mZoomScrollX=" + mZoomScrollX
+ + " mZoomScrollX=" + mZoomScrollX
+ " mZoomScrollY=" + mZoomScrollY);
}
mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
@@ -4101,18 +4541,18 @@ public class WebView extends AbsoluteLayout
int oldScrollX = mScrollX;
int oldScrollY = mScrollY;
if (count > 0) {
- int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
- KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
+ int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
+ KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
KeyEvent.KEYCODE_DPAD_RIGHT;
count = Math.min(count, TRACKBALL_MOVE_COUNT);
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
+ " count=" + count
+ " mTrackballRemainsX=" + mTrackballRemainsX
+ " mTrackballRemainsY=" + mTrackballRemainsY);
}
- if (navHandledKey(selectKeyCode, count, false, time)) {
+ if (navHandledKey(selectKeyCode, count, false, time, false)) {
playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
}
mTrackballRemainsX = mTrackballRemainsY = 0;
@@ -4120,12 +4560,12 @@ public class WebView extends AbsoluteLayout
if (count >= TRACKBALL_SCROLL_COUNT) {
int xMove = scaleTrackballX(xRate, width);
int yMove = scaleTrackballY(yRate, height);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball pinScrollBy"
+ " count=" + count
+ " xMove=" + xMove + " yMove=" + yMove
- + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
- + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
+ + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
+ + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
);
}
if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
@@ -4138,24 +4578,29 @@ public class WebView extends AbsoluteLayout
pinScrollBy(xMove, yMove, true, 0);
}
mUserScroll = true;
- }
- mWebViewCore.sendMessage(EventHub.UNBLOCK_FOCUS);
+ }
+ }
+
+ private int computeMaxScrollY() {
+ int maxContentH = contentToViewDimension(mContentHeight)
+ + getTitleHeight();
+ return Math.max(maxContentH - getHeight(), 0);
}
public void flingScroll(int vx, int vy) {
int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
- int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
-
+ int maxY = computeMaxScrollY();
+
mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
invalidate();
}
-
+
private void doFling() {
if (mVelocityTracker == null) {
return;
}
int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
- int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
+ int maxY = computeMaxScrollY();
mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
int vx = (int) mVelocityTracker.getXVelocity();
@@ -4168,12 +4613,40 @@ public class WebView extends AbsoluteLayout
vx = 0;
}
}
-
+
if (true /* EMG release: make our fling more like Maps' */) {
// maps cuts their velocity in half
vx = vx * 3 / 4;
vy = vy * 3 / 4;
}
+ if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
+ WebViewCore.resumeUpdate(mWebViewCore);
+ return;
+ }
+ float currentVelocity = mScroller.getCurrVelocity();
+ if (mLastVelocity > 0 && currentVelocity > 0) {
+ float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
+ - Math.atan2(vy, vx)));
+ final float circle = (float) (Math.PI) * 2.0f;
+ if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
+ vx += currentVelocity * mLastVelX / mLastVelocity;
+ vy += currentVelocity * mLastVelY / mLastVelocity;
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
+ }
+ } else if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "doFling missed " + deltaR / circle);
+ }
+ } else if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "doFling start last=" + mLastVelocity
+ + " current=" + currentVelocity
+ + " vx=" + vx + " vy=" + vy
+ + " maxX=" + maxX + " maxY=" + maxY
+ + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
+ }
+ mLastVelX = vx;
+ mLastVelY = vy;
+ mLastVelocity = (float) Math.hypot(vx, vy);
mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
// TODO: duration is calculated based on velocity, if the range is
@@ -4187,6 +4660,8 @@ public class WebView extends AbsoluteLayout
private boolean zoomWithPreview(float scale) {
float oldScale = mActualScale;
+ mInitialScrollX = mScrollX;
+ mInitialScrollY = mScrollY;
// snap to DEFAULT_SCALE if it is close
if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
@@ -4201,6 +4676,9 @@ public class WebView extends AbsoluteLayout
mInvInitialZoomScale = 1.0f / oldScale;
mInvFinalZoomScale = 1.0f / mActualScale;
mZoomScale = mActualScale;
+ if (!mInZoomOverview) {
+ mLastScale = scale;
+ }
invalidate();
return true;
} else {
@@ -4229,7 +4707,7 @@ public class WebView extends AbsoluteLayout
}
if (mZoomControls == null) {
mZoomControls = createZoomControls();
-
+
/*
* need to be set to VISIBLE first so that getMeasuredHeight() in
* {@link #onSizeChanged()} can return the measured value for proper
@@ -4238,7 +4716,7 @@ public class WebView extends AbsoluteLayout
mZoomControls.setVisibility(View.VISIBLE);
mZoomControlRunnable = new Runnable() {
public void run() {
-
+
/* Don't dismiss the controls if the user has
* focus on them. Wait and check again later.
*/
@@ -4290,7 +4768,7 @@ public class WebView extends AbsoluteLayout
/**
* Gets the {@link ZoomButtonsController} which can be used to add
* additional buttons to the zoom controls window.
- *
+ *
* @return The instance of {@link ZoomButtonsController} used by this class,
* or null if it is unavailable.
* @hide
@@ -4306,7 +4784,18 @@ public class WebView extends AbsoluteLayout
public boolean zoomIn() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
- return zoomWithPreview(mActualScale * 1.25f);
+ // Center zooming to the center of the screen.
+ if (mInZoomOverview) {
+ // if in overview mode, bring it back to normal mode
+ mLastTouchX = getViewWidth() * .5f;
+ mLastTouchY = getViewHeight() * .5f;
+ doDoubleTap();
+ return true;
+ } else {
+ mZoomCenterX = getViewWidth() * .5f;
+ mZoomCenterY = getViewHeight() * .5f;
+ return zoomWithPreview(mActualScale * 1.25f);
+ }
}
/**
@@ -4316,7 +4805,18 @@ public class WebView extends AbsoluteLayout
public boolean zoomOut() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
- return zoomWithPreview(mActualScale * 0.8f);
+ float scale = mActualScale * 0.8f;
+ if (scale < (mMinZoomScale + 0.1f) && WebView.ENABLE_DOUBLETAP_ZOOM
+ && mWebViewCore.getSettings().getUseWideViewPort()) {
+ // when zoom out to min scale, switch to overview mode
+ doDoubleTap();
+ return true;
+ } else {
+ // Center zooming to the center of the screen.
+ mZoomCenterX = getViewWidth() * .5f;
+ mZoomCenterY = getViewHeight() * .5f;
+ return zoomWithPreview(scale);
+ }
}
private void updateSelection() {
@@ -4324,23 +4824,81 @@ public class WebView extends AbsoluteLayout
return;
}
// mLastTouchX and mLastTouchY are the point in the current viewport
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
contentX + mNavSlop, contentY + mNavSlop);
- // If we were already focused on a textfield, update its cache.
- if (inEditingMode()) {
- mTextEntry.updateCachedTextfield();
- }
nativeSelectBestAt(rect);
}
+ /**
+ * Scroll the focused text field/area to match the WebTextView
+ * @param x New x position of the WebTextView in view coordinates
+ * @param y New y position of the WebTextView in view coordinates
+ */
+ /*package*/ void scrollFocusedTextInput(int x, int y) {
+ if (!inEditingMode() || mWebViewCore == null) {
+ return;
+ }
+ mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, viewToContentX(x),
+ viewToContentY(y));
+ }
+
+ /**
+ * Set our starting point and time for a drag from the WebTextView.
+ */
+ /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
+ if (!inEditingMode()) {
+ return;
+ }
+ mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
+ mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
+ mLastTouchTime = eventTime;
+ if (!mScroller.isFinished()) {
+ abortAnimation();
+ mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
+ }
+ mSnapScrollMode = SNAP_NONE;
+ mVelocityTracker = VelocityTracker.obtain();
+ mTouchMode = TOUCH_DRAG_START_MODE;
+ }
+
+ /**
+ * Given a motion event from the WebTextView, set its location to our
+ * coordinates, and handle the event.
+ */
+ /*package*/ boolean textFieldDrag(MotionEvent event) {
+ if (!inEditingMode()) {
+ return false;
+ }
+ mDragFromTextInput = true;
+ event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
+ (float) (mWebTextView.getTop() - mScrollY));
+ boolean result = onTouchEvent(event);
+ mDragFromTextInput = false;
+ return result;
+ }
+
+ /**
+ * Do a touch up from a WebTextView. This will be handled by webkit to
+ * change the selection.
+ * @param event MotionEvent in the WebTextView's coordinates.
+ */
+ /*package*/ void touchUpOnTextField(MotionEvent event) {
+ if (!inEditingMode()) {
+ return;
+ }
+ int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
+ int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
+ nativeTextInputMotionUp(x, y);
+ }
+
/*package*/ void shortPressOnTextField() {
if (inEditingMode()) {
- View v = mTextEntry;
- int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
- int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
- nativeMotionUp(x, y, mNavSlop, true);
+ View v = mWebTextView;
+ int x = viewToContentX((v.getLeft() + v.getRight()) >> 1);
+ int y = viewToContentY((v.getTop() + v.getBottom()) >> 1);
+ nativeTextInputMotionUp(x, y);
}
}
@@ -4350,31 +4908,77 @@ public class WebView extends AbsoluteLayout
}
switchOutDrawHistory();
// mLastTouchX and mLastTouchY are the point in the current viewport
- int contentX = viewToContent((int) mLastTouchX + mScrollX);
- int contentY = viewToContent((int) mLastTouchY + mScrollY);
- if (nativeMotionUp(contentX, contentY, mNavSlop, true)) {
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
+ if (nativeMotionUp(contentX, contentY, mNavSlop)) {
if (mLogEvent) {
Checkin.updateStats(mContext.getContentResolver(),
Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
}
}
- if (nativeUpdateFocusNode() && !mFocusNode.mIsTextField
- && !mFocusNode.mIsTextArea) {
+ if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
playSoundEffect(SoundEffectConstants.CLICK);
}
}
+ private void doDoubleTap() {
+ if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
+ return;
+ }
+ mZoomCenterX = mLastTouchX;
+ mZoomCenterY = mLastTouchY;
+ mInZoomOverview = !mInZoomOverview;
+ // remove the zoom control after double tap
+ if (getSettings().getBuiltInZoomControls()) {
+ if (mZoomButtonsController.isVisible()) {
+ mZoomButtonsController.setVisible(false);
+ }
+ } else {
+ if (mZoomControlRunnable != null) {
+ mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+ }
+ if (mZoomControls != null) {
+ mZoomControls.hide();
+ }
+ }
+ if (mInZoomOverview) {
+ zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth);
+ } else {
+ // mLastTouchX and mLastTouchY are the point in the current viewport
+ int contentX = viewToContentX((int) mLastTouchX + mScrollX);
+ int contentY = viewToContentY((int) mLastTouchY + mScrollY);
+ int left = nativeGetBlockLeftEdge(contentX, contentY, mActualScale);
+ if (left != NO_LEFTEDGE) {
+ // add a 5pt padding to the left edge. Re-calculate the zoom
+ // center so that the new scroll x will be on the left edge.
+ mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
+ * mActualScale / (mLastScale - mActualScale);
+ }
+ zoomWithPreview(mLastScale);
+ }
+ }
+
// Called by JNI to handle a touch on a node representing an email address,
// address, or phone number
private void overrideLoading(String url) {
mCallbackProxy.uiOverrideUrlLoading(url);
}
+ // called by JNI
+ private void sendPluginState(int state) {
+ WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
+ psd.mFrame = nativeCursorFramePointer();
+ psd.mNode = nativeCursorNodePointer();
+ psd.mState = state;
+ mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
+ }
+
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
boolean result = false;
if (inEditingMode()) {
- result = mTextEntry.requestFocus(direction, previouslyFocusedRect);
+ result = mWebTextView.requestFocus(direction,
+ previouslyFocusedRect);
} else {
result = super.requestFocus(direction, previouslyFocusedRect);
if (mWebViewCore.getSettings().getNeedInitialFocus()) {
@@ -4398,8 +5002,8 @@ public class WebView extends AbsoluteLayout
default:
return result;
}
- if (mNativeClass != 0 && !nativeUpdateFocusNode()) {
- navHandledKey(fakeKeyDirection, 1, true, 0);
+ if (mNativeClass != 0 && !nativeHasCursorNode()) {
+ navHandledKey(fakeKeyDirection, 1, true, 0, true);
}
}
}
@@ -4419,8 +5023,8 @@ public class WebView extends AbsoluteLayout
int measuredWidth = widthSize;
// Grab the content size from WebViewCore.
- int contentHeight = mContentHeight;
- int contentWidth = mContentWidth;
+ int contentHeight = Math.round(mContentHeight * mActualScale);
+ int contentWidth = Math.round(mContentWidth * mActualScale);
// Log.d(LOGTAG, "------- measure " + heightMode);
@@ -4467,14 +5071,19 @@ public class WebView extends AbsoluteLayout
int scrollYDelta = 0;
- if (rect.bottom > screenBottom && rect.top > screenTop) {
- if (rect.height() > height) {
- scrollYDelta += (rect.top - screenTop);
+ if (rect.bottom > screenBottom) {
+ int oneThirdOfScreenHeight = height / 3;
+ if (rect.height() > 2 * oneThirdOfScreenHeight) {
+ // If the rectangle is too tall to fit in the bottom two thirds
+ // of the screen, place it at the top.
+ scrollYDelta = rect.top - screenTop;
} else {
- scrollYDelta += (rect.bottom - screenBottom);
+ // If the rectangle will still fit on screen, we want its
+ // top to be in the top third of the screen.
+ scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
}
} else if (rect.top < screenTop) {
- scrollYDelta -= (screenTop - rect.top);
+ scrollYDelta = rect.top - screenTop;
}
int width = getWidth() - getVerticalScrollbarWidth();
@@ -4499,33 +5108,35 @@ public class WebView extends AbsoluteLayout
return false;
}
-
+
/* package */ void replaceTextfieldText(int oldStart, int oldEnd,
String replace, int newStart, int newEnd) {
- HashMap arg = new HashMap();
- arg.put("focusData", new WebViewCore.FocusData(mFocusData));
- arg.put("replace", replace);
- arg.put("start", new Integer(newStart));
- arg.put("end", new Integer(newEnd));
+ WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
+ arg.mReplace = replace;
+ arg.mNewStart = newStart;
+ arg.mNewEnd = newEnd;
mTextGeneration++;
+ arg.mTextGeneration = mTextGeneration;
mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
}
/* package */ void passToJavaScript(String currentText, KeyEvent event) {
- HashMap arg = new HashMap();
- arg.put("focusData", new WebViewCore.FocusData(mFocusData));
- arg.put("event", event);
- arg.put("currentText", currentText);
+ if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
+ mWebViewCore.sendMessage(EventHub.CLICK);
+ }
+ WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
+ arg.mEvent = event;
+ arg.mCurrentText = currentText;
// Increase our text generation number, and pass it to webcore thread
mTextGeneration++;
mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
// WebKit's document state is not saved until about to leave the page.
- // To make sure the host application, like Browser, has the up to date
- // document state when it goes to background, we force to save the
+ // To make sure the host application, like Browser, has the up to date
+ // document state when it goes to background, we force to save the
// document state.
mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
- new WebViewCore.FocusData(mFocusData), 1000);
+ cursorData(), 1000);
}
/* package */ WebViewCore getWebViewCore() {
@@ -4543,9 +5154,9 @@ public class WebView extends AbsoluteLayout
class PrivateHandler extends Handler {
@Override
public void handleMessage(Message msg) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
- > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
+ > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
}
switch (msg.what) {
@@ -4567,6 +5178,8 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_INIT_MODE) {
mTouchMode = TOUCH_SHORTPRESS_START_MODE;
updateSelection();
+ } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+ mTouchMode = TOUCH_DONE_MODE;
}
break;
}
@@ -4574,23 +5187,49 @@ public class WebView extends AbsoluteLayout
if (!mPreventDrag) {
mTouchMode = TOUCH_DONE_MODE;
performLongClick();
- updateTextEntry();
+ rebuildWebTextView();
+ }
+ break;
+ }
+ case RELEASE_SINGLE_TAP: {
+ if (!mPreventDrag) {
+ mTouchMode = TOUCH_DONE_MODE;
+ doShortPress();
}
break;
}
- case SWITCH_TO_ENTER:
- if (LOGV_ENABLED) Log.v(LOGTAG, "SWITCH_TO_ENTER");
+ case SWITCH_TO_CLICK:
+ // The user clicked with the trackball, and did not click a
+ // second time, so perform the action of a trackball single
+ // click
mTouchMode = TOUCH_DONE_MODE;
- onKeyUp(KeyEvent.KEYCODE_ENTER
- , new KeyEvent(KeyEvent.ACTION_UP
- , KeyEvent.KEYCODE_ENTER));
+ Rect visibleRect = sendOurVisibleRect();
+ // Note that sendOurVisibleRect calls viewToContent, so the
+ // coordinates should be in content coordinates.
+ if (!nativeCursorIntersects(visibleRect)) {
+ break;
+ }
+ nativeSetFollowedLink(true);
+ nativeUpdatePluginReceivesEvents();
+ WebViewCore.CursorData data = cursorData();
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
+ playSoundEffect(SoundEffectConstants.CLICK);
+ boolean isTextInput = nativeCursorIsTextInput();
+ if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
+ nativeCursorText())) {
+ mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
+ nativeCursorNodePointer());
+ }
+ if (isTextInput) {
+ rebuildWebTextView();
+ }
break;
case SCROLL_BY_MSG_ID:
setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
break;
case SYNC_SCROLL_TO_MSG_ID:
if (mUserScroll) {
- // if user has scrolled explicitly, don't sync the
+ // if user has scrolled explicitly, don't sync the
// scroll position any more
mUserScroll = false;
break;
@@ -4599,7 +5238,7 @@ public class WebView extends AbsoluteLayout
case SCROLL_TO_MSG_ID:
if (setContentScrollTo(msg.arg1, msg.arg2)) {
// if we can't scroll to the exact position due to pin,
- // send a message to WebCore to re-scroll when we get a
+ // send a message to WebCore to re-scroll when we get a
// new picture
mUserScroll = false;
mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
@@ -4609,24 +5248,57 @@ public class WebView extends AbsoluteLayout
case SPAWN_SCROLL_TO_MSG_ID:
spawnContentScrollTo(msg.arg1, msg.arg2);
break;
- case NEW_PICTURE_MSG_ID:
+ case NEW_PICTURE_MSG_ID: {
+ WebSettings settings = mWebViewCore.getSettings();
// called for new content
- final WebViewCore.DrawData draw =
+ final int viewWidth = getViewWidth();
+ final WebViewCore.DrawData draw =
(WebViewCore.DrawData) msg.obj;
final Point viewSize = draw.mViewPoint;
- if (mZoomScale > 0) {
- // use the same logic in sendViewSizeZoom() to make sure
- // the mZoomScale has matched the viewSize so that we
- // can clear mZoomScale
- if (Math.round(getViewWidth() / mZoomScale) == viewSize.x) {
- mZoomScale = 0;
- mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
- 0, 0);
+ boolean useWideViewport = settings.getUseWideViewPort();
+ WebViewCore.RestoreState restoreState = draw.mRestoreState;
+ if (restoreState != null) {
+ mInZoomOverview = false;
+ mLastScale = restoreState.mTextWrapScale;
+ if (restoreState.mMinScale == 0) {
+ if (restoreState.mMobileSite) {
+ if (draw.mMinPrefWidth > draw.mViewPoint.x) {
+ mMinZoomScale = (float) viewWidth
+ / draw.mMinPrefWidth;
+ mMinZoomScaleFixed = false;
+ } else {
+ mMinZoomScale = mDefaultScale;
+ mMinZoomScaleFixed = true;
+ }
+ } else {
+ mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
+ mMinZoomScaleFixed = false;
+ }
+ } else {
+ mMinZoomScale = restoreState.mMinScale;
+ mMinZoomScaleFixed = true;
}
- }
- if (!mMinZoomScaleFixed) {
- mMinZoomScale = (float) getViewWidth()
- / Math.max(ZOOM_OUT_WIDTH, draw.mWidthHeight.x);
+ if (restoreState.mMaxScale == 0) {
+ mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
+ } else {
+ mMaxZoomScale = restoreState.mMaxScale;
+ }
+ setNewZoomScale(mLastScale, false);
+ setContentScrollTo(restoreState.mScrollX,
+ restoreState.mScrollY);
+ if (ENABLE_DOUBLETAP_ZOOM && useWideViewport
+ && settings.getLoadWithOverviewMode()) {
+ if (restoreState.mViewScale == 0
+ || (restoreState.mMobileSite
+ && mMinZoomScale < mDefaultScale)) {
+ mInZoomOverview = true;
+ }
+ }
+ // As we are on a new page, remove the WebTextView. This
+ // is necessary for page loads driven by webkit, and in
+ // particular when the user was on a password field, so
+ // the WebTextView was visible.
+ clearTextEntry();
}
// We update the layout (i.e. request a layout from the
// view system) if the last view size that we sent to
@@ -4634,9 +5306,9 @@ public class WebView extends AbsoluteLayout
// received in the fixed dimension.
final boolean updateLayout = viewSize.x == mLastWidthSent
&& viewSize.y == mLastHeightSent;
- recordNewContentSize(draw.mWidthHeight.x,
+ recordNewContentSize(draw.mWidthHeight.x,
draw.mWidthHeight.y, updateLayout);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
@@ -4645,114 +5317,76 @@ public class WebView extends AbsoluteLayout
if (mPictureListener != null) {
mPictureListener.onNewPicture(WebView.this, capturePicture());
}
+ if (useWideViewport) {
+ mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
+ draw.mViewPoint.x);
+ }
+ if (!mMinZoomScaleFixed) {
+ mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
+ }
+ if (!mDrawHistory && mInZoomOverview) {
+ // fit the content width to the current view. Ignore
+ // the rounding error case.
+ if (Math.abs((viewWidth * mInvActualScale)
+ - mZoomOverviewWidth) > 1) {
+ setNewZoomScale((float) viewWidth
+ / mZoomOverviewWidth, false);
+ }
+ }
break;
+ }
case WEBCORE_INITIALIZED_MSG_ID:
// nativeCreate sets mNativeClass to a non-zero value
nativeCreate(msg.arg1);
break;
case UPDATE_TEXTFIELD_TEXT_MSG_ID:
// Make sure that the textfield is currently focused
- // and representing the same node as the pointer.
- if (inEditingMode() &&
- mTextEntry.isSameTextField(msg.arg1)) {
+ // and representing the same node as the pointer.
+ if (inEditingMode() &&
+ mWebTextView.isSameTextField(msg.arg1)) {
if (msg.getData().getBoolean("password")) {
- Spannable text = (Spannable) mTextEntry.getText();
+ Spannable text = (Spannable) mWebTextView.getText();
int start = Selection.getSelectionStart(text);
int end = Selection.getSelectionEnd(text);
- mTextEntry.setInPassword(true);
+ mWebTextView.setInPassword(true);
// Restore the selection, which may have been
// ruined by setInPassword.
- Spannable pword = (Spannable) mTextEntry.getText();
+ Spannable pword =
+ (Spannable) mWebTextView.getText();
Selection.setSelection(pword, start, end);
// If the text entry has created more events, ignore
// this one.
} else if (msg.arg2 == mTextGeneration) {
- mTextEntry.setTextAndKeepSelection(
+ mWebTextView.setTextAndKeepSelection(
(String) msg.obj);
}
}
break;
- case DID_FIRST_LAYOUT_MSG_ID:
- if (mNativeClass == 0) {
- break;
- }
-// Do not reset the focus or clear the text; the user may have already
-// navigated or entered text at this point. The focus should have gotten
-// reset, if need be, when the focus cache was built. Similarly, the text
-// view should already be torn down and rebuilt if needed.
-// nativeResetFocus();
-// clearTextEntry();
- HashMap scaleLimit = (HashMap) msg.obj;
- int minScale = (Integer) scaleLimit.get("minScale");
- if (minScale == 0) {
- mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
- mMinZoomScaleFixed = false;
- } else {
- mMinZoomScale = (float) (minScale / 100.0);
- mMinZoomScaleFixed = true;
- }
- int maxScale = (Integer) scaleLimit.get("maxScale");
- if (maxScale == 0) {
- mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
- } else {
- mMaxZoomScale = (float) (maxScale / 100.0);
- }
- // If history Picture is drawn, don't update zoomWidth
- if (mDrawHistory) {
- break;
- }
- int width = getViewWidth();
- if (width == 0) {
- break;
- }
- int initialScale = msg.arg1;
- int viewportWidth = msg.arg2;
- // start a new page with DEFAULT_SCALE zoom scale.
- float scale = mDefaultScale;
- if (mInitialScale > 0) {
- scale = mInitialScale / 100.0f;
- } else {
- if (mWebViewCore.getSettings().getUseWideViewPort()) {
- // force viewSizeChanged by setting mLastWidthSent
- // to 0
- mLastWidthSent = 0;
- }
- if (initialScale == 0) {
- // if viewportWidth is defined and it is smaller
- // than the view width, zoom in to fill the view
- if (viewportWidth > 0 && viewportWidth < width) {
- scale = (float) width / viewportWidth;
- }
- } else {
- scale = initialScale / 100.0f;
- }
+ case UPDATE_TEXT_SELECTION_MSG_ID:
+ if (inEditingMode()
+ && mWebTextView.isSameTextField(msg.arg1)
+ && msg.arg2 == mTextGeneration) {
+ WebViewCore.TextSelectionData tData
+ = (WebViewCore.TextSelectionData) msg.obj;
+ mWebTextView.setSelectionFromWebKit(tData.mStart,
+ tData.mEnd);
}
- setNewZoomScale(scale, false);
- break;
- case MARK_NODE_INVALID_ID:
- nativeMarkNodeInvalid(msg.arg1);
break;
- case NOTIFY_FOCUS_SET_MSG_ID:
- if (mNativeClass != 0) {
- nativeNotifyFocusSet(inEditingMode());
+ case MOVE_OUT_OF_PLUGIN:
+ if (nativePluginEatsNavKey()) {
+ navHandledKey(msg.arg1, 1, false, 0, true);
}
break;
case UPDATE_TEXT_ENTRY_MSG_ID:
- // this is sent after finishing resize in WebViewCore. Make
+ // this is sent after finishing resize in WebViewCore. Make
// sure the text edit box is still on the screen.
- boolean alreadyThere = inEditingMode();
- if (alreadyThere && nativeUpdateFocusNode()) {
- FocusNode node = mFocusNode;
- if (node.mIsTextField || node.mIsTextArea) {
- mTextEntry.bringIntoView();
- }
+ if (inEditingMode() && nativeCursorIsTextInput()) {
+ mWebTextView.bringIntoView();
+ rebuildWebTextView();
}
- updateTextEntry();
break;
- case RECOMPUTE_FOCUS_MSG_ID:
- if (mNativeClass != 0) {
- nativeRecomputeFocus();
- }
+ case CLEAR_TEXT_ENTRY:
+ clearTextEntry();
break;
case INVAL_RECT_MSG_ID: {
Rect r = (Rect)msg.obj;
@@ -4765,17 +5399,15 @@ public class WebView extends AbsoluteLayout
}
break;
}
- case UPDATE_TEXT_ENTRY_ADAPTER:
- HashMap data = (HashMap) msg.obj;
- if (mTextEntry.isSameTextField(msg.arg1)) {
- AutoCompleteAdapter adapter =
- (AutoCompleteAdapter) data.get("adapter");
- mTextEntry.setAdapterCustom(adapter);
+ case REQUEST_FORM_DATA:
+ AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
+ if (mWebTextView.isSameTextField(msg.arg1)) {
+ mWebTextView.setAdapterCustom(adapter);
}
break;
case UPDATE_CLIPBOARD:
String str = (String) msg.obj;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
}
try {
@@ -4790,12 +5422,12 @@ public class WebView extends AbsoluteLayout
WebViewCore.resumeUpdate(mWebViewCore);
break;
- case LONG_PRESS_ENTER:
+ case LONG_PRESS_CENTER:
// as this is shared by keydown and trackballdown, reset all
// the states
- mGotEnterDown = false;
+ mGotCenterDown = false;
mTrackballDown = false;
- // LONG_PRESS_ENTER is sent as a delayed message. If we
+ // LONG_PRESS_CENTER is sent as a delayed message. If we
// switch to windows overview, the WebView will be
// temporarily removed from the view system. In that case,
// do nothing.
@@ -4817,6 +5449,14 @@ public class WebView extends AbsoluteLayout
}
break;
+ case REQUEST_KEYBOARD:
+ if (msg.arg1 == 0) {
+ hideSoftKeyboard();
+ } else {
+ displaySoftKeyboard(false);
+ }
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -4826,16 +5466,12 @@ public class WebView extends AbsoluteLayout
// Class used to use a dropdown for a <select> element
private class InvokeListBox implements Runnable {
- // Strings for the labels in the listbox.
- private String[] mArray;
- // Array representing whether each item is enabled.
- private boolean[] mEnableArray;
// Whether the listbox allows multiple selection.
private boolean mMultiple;
// Passed in to a list with multiple selection to tell
// which items are selected.
private int[] mSelectedArray;
- // Passed in to a list with single selection to tell
+ // Passed in to a list with single selection to tell
// where the initial selection is.
private int mSelection;
@@ -4854,14 +5490,14 @@ public class WebView extends AbsoluteLayout
}
/**
- * Subclass ArrayAdapter so we can disable OptionGroupLabels,
+ * Subclass ArrayAdapter so we can disable OptionGroupLabels,
* and allow filtering.
*/
private class MyArrayListAdapter extends ArrayAdapter<Container> {
public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
- super(context,
+ super(context,
multiple ? com.android.internal.R.layout.select_dialog_multichoice :
- com.android.internal.R.layout.select_dialog_singlechoice,
+ com.android.internal.R.layout.select_dialog_singlechoice,
objects);
}
@@ -4919,7 +5555,7 @@ public class WebView extends AbsoluteLayout
}
}
- private InvokeListBox(String[] array, boolean[] enabled, int
+ private InvokeListBox(String[] array, boolean[] enabled, int
selection) {
mSelection = selection;
mMultiple = false;
@@ -4982,31 +5618,36 @@ public class WebView extends AbsoluteLayout
public void run() {
final ListView listView = (ListView) LayoutInflater.from(mContext)
.inflate(com.android.internal.R.layout.select_dialog, null);
- final MyArrayListAdapter adapter = new
+ final MyArrayListAdapter adapter = new
MyArrayListAdapter(mContext, mContainers, mMultiple);
AlertDialog.Builder b = new AlertDialog.Builder(mContext)
.setView(listView).setCancelable(true)
.setInverseBackgroundForced(true);
-
+
if (mMultiple) {
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mWebViewCore.sendMessage(
- EventHub.LISTBOX_CHOICES,
+ EventHub.LISTBOX_CHOICES,
adapter.getCount(), 0,
listView.getCheckedItemPositions());
}});
- b.setNegativeButton(android.R.string.cancel, null);
+ b.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mWebViewCore.sendMessage(
+ EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
+ }});
}
final AlertDialog dialog = b.create();
listView.setAdapter(adapter);
listView.setFocusableInTouchMode(true);
// There is a bug (1250103) where the checks in a ListView with
// multiple items selected are associated with the positions, not
- // the ids, so the items do not properly retain their checks when
+ // the ids, so the items do not properly retain their checks when
// filtered. Do not allow filtering on multiple lists until
// that bug is fixed.
-
+
listView.setTextFilterEnabled(!mMultiple);
if (mMultiple) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
@@ -5069,48 +5710,39 @@ public class WebView extends AbsoluteLayout
}
// called by JNI
- private void sendFinalFocus(int frame, int node, int x, int y) {
- WebViewCore.FocusData focusData = new WebViewCore.FocusData();
- focusData.mFrame = frame;
- focusData.mNode = node;
- focusData.mX = x;
- focusData.mY = y;
- mWebViewCore.sendMessage(EventHub.SET_FINAL_FOCUS,
- EventHub.NO_FOCUS_CHANGE_BLOCK, 0, focusData);
+ private void sendMoveMouse(int frame, int node, int x, int y) {
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
+ new WebViewCore.CursorData(frame, node, x, y));
}
- // called by JNI
- private void setFocusData(int moveGeneration, int buildGeneration,
- int frame, int node, int x, int y, boolean ignoreNullFocus) {
- mFocusData.mMoveGeneration = moveGeneration;
- mFocusData.mBuildGeneration = buildGeneration;
- mFocusData.mFrame = frame;
- mFocusData.mNode = node;
- mFocusData.mX = x;
- mFocusData.mY = y;
- mFocusData.mIgnoreNullFocus = ignoreNullFocus;
- }
-
- // called by JNI
- private void sendKitFocus() {
- WebViewCore.FocusData focusData = new WebViewCore.FocusData(mFocusData);
- mWebViewCore.sendMessage(EventHub.SET_KIT_FOCUS, focusData);
+ /*
+ * Send a mouse move event to the webcore thread.
+ *
+ * @param removeFocus Pass true if the "mouse" cursor is now over a node
+ * which wants key events, but it is not the focus. This
+ * will make the visual appear as though nothing is in
+ * focus. Remove the WebTextView, if present, and stop
+ * drawing the blinking caret.
+ * called by JNI
+ */
+ private void sendMoveMouseIfLatest(boolean removeFocus) {
+ if (removeFocus) {
+ clearTextEntry();
+ setFocusControllerInactive();
+ }
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
+ cursorData());
}
// called by JNI
- private void sendMotionUp(int touchGeneration, int buildGeneration,
- int frame, int node, int x, int y, int size, boolean isClick,
- boolean retry) {
+ private void sendMotionUp(int touchGeneration,
+ int frame, int node, int x, int y) {
WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
touchUpData.mMoveGeneration = touchGeneration;
- touchUpData.mBuildGeneration = buildGeneration;
- touchUpData.mSize = size;
- touchUpData.mIsClick = isClick;
- touchUpData.mRetry = retry;
- mFocusData.mFrame = touchUpData.mFrame = frame;
- mFocusData.mNode = touchUpData.mNode = node;
- mFocusData.mX = touchUpData.mX = x;
- mFocusData.mY = touchUpData.mY = y;
+ touchUpData.mFrame = frame;
+ touchUpData.mNode = node;
+ touchUpData.mX = x;
+ touchUpData.mY = y;
mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
}
@@ -5125,7 +5757,7 @@ public class WebView extends AbsoluteLayout
width = visRect.width() / 2;
}
// FIXME the divisor should be retrieved from somewhere
- return viewToContent(width);
+ return viewToContentX(width);
}
private int getScaledMaxYScroll() {
@@ -5140,7 +5772,7 @@ public class WebView extends AbsoluteLayout
// FIXME the divisor should be retrieved from somewhere
// the closest thing today is hard-coded into ScrollView.java
// (from ScrollView.java, line 363) int maxJump = height/2;
- return viewToContent(height);
+ return viewToContentY(height);
}
/**
@@ -5149,56 +5781,72 @@ public class WebView extends AbsoluteLayout
private void viewInvalidate() {
invalidate();
}
-
+
// return true if the key was handled
- private boolean navHandledKey(int keyCode, int count, boolean noScroll
- , long time) {
+ private boolean navHandledKey(int keyCode, int count, boolean noScroll,
+ long time, boolean ignorePlugin) {
if (mNativeClass == 0) {
return false;
}
- mLastFocusTime = time;
- mLastFocusBounds = nativeGetFocusRingBounds();
- boolean keyHandled = nativeMoveFocus(keyCode, count, noScroll) == false;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "navHandledKey mLastFocusBounds=" + mLastFocusBounds
- + " mLastFocusTime=" + mLastFocusTime
+ if (ignorePlugin == false && nativePluginEatsNavKey()) {
+ KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
+ , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
+ | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
+ | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
+ , 0, 0, 0);
+ mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
+ mWebViewCore.sendMessage(EventHub.KEY_UP, event);
+ return true;
+ }
+ mLastCursorTime = time;
+ mLastCursorBounds = nativeGetCursorRingBounds();
+ boolean keyHandled
+ = nativeMoveCursor(keyCode, count, noScroll) == false;
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
+ + " mLastCursorTime=" + mLastCursorTime
+ " handled=" + keyHandled);
}
if (keyHandled == false || mHeightCanMeasure == false) {
return keyHandled;
}
- Rect contentFocus = nativeGetFocusRingBounds();
- if (contentFocus.isEmpty()) return keyHandled;
- Rect viewFocus = contentToView(contentFocus);
+ Rect contentCursorRingBounds = nativeGetCursorRingBounds();
+ if (contentCursorRingBounds.isEmpty()) return keyHandled;
+ Rect viewCursorRingBounds = contentToView(contentCursorRingBounds);
Rect visRect = new Rect();
calcOurVisibleRect(visRect);
Rect outset = new Rect(visRect);
int maxXScroll = visRect.width() / 2;
int maxYScroll = visRect.height() / 2;
outset.inset(-maxXScroll, -maxYScroll);
- if (Rect.intersects(outset, viewFocus) == false) {
+ if (Rect.intersects(outset, viewCursorRingBounds) == false) {
return keyHandled;
}
// FIXME: Necessary because ScrollView/ListView do not scroll left/right
- int maxH = Math.min(viewFocus.right - visRect.right, maxXScroll);
+ int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
+ maxXScroll);
if (maxH > 0) {
pinScrollBy(maxH, 0, true, 0);
} else {
- maxH = Math.max(viewFocus.left - visRect.left, -maxXScroll);
+ maxH = Math.max(viewCursorRingBounds.left - visRect.left,
+ -maxXScroll);
if (maxH < 0) {
pinScrollBy(maxH, 0, true, 0);
}
}
- if (mLastFocusBounds.isEmpty()) return keyHandled;
- if (mLastFocusBounds.equals(contentFocus)) return keyHandled;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "navHandledKey contentFocus=" + contentFocus);
+ if (mLastCursorBounds.isEmpty()) return keyHandled;
+ if (mLastCursorBounds.equals(contentCursorRingBounds)) {
+ return keyHandled;
+ }
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
+ + contentCursorRingBounds);
}
- requestRectangleOnScreen(viewFocus);
+ requestRectangleOnScreen(viewCursorRingBounds);
mUserScroll = true;
return keyHandled;
}
-
+
/**
* Set the background color. It's white by default. Pass
* zero to make the view transparent.
@@ -5213,7 +5861,7 @@ public class WebView extends AbsoluteLayout
nativeDebugDump();
mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
}
-
+
/**
* Update our cache with updatedText.
* @param updatedText The new text to put in our cache.
@@ -5223,52 +5871,85 @@ public class WebView extends AbsoluteLayout
// we recognize that it is up to date.
nativeUpdateCachedTextfield(updatedText, mTextGeneration);
}
-
- // Never call this version except by updateCachedTextfield(String) -
- // we always want to pass in our generation number.
- private native void nativeUpdateCachedTextfield(String updatedText,
- int generation);
- private native void nativeClearFocus(int x, int y);
+
+ /* package */ native void nativeClearCursor();
private native void nativeCreate(int ptr);
+ private native int nativeCursorFramePointer();
+ private native Rect nativeCursorNodeBounds();
+ /* package */ native int nativeCursorNodePointer();
+ /* package */ native boolean nativeCursorMatchesFocus();
+ private native boolean nativeCursorIntersects(Rect visibleRect);
+ private native boolean nativeCursorIsAnchor();
+ private native boolean nativeCursorIsPlugin();
+ private native boolean nativeCursorIsTextInput();
+ private native Point nativeCursorPosition();
+ private native String nativeCursorText();
+ /**
+ * Returns true if the native cursor node says it wants to handle key events
+ * (ala plugins). This can only be called if mNativeClass is non-zero!
+ */
+ private native boolean nativeCursorWantsKeyEvents();
private native void nativeDebugDump();
private native void nativeDestroy();
- private native void nativeDrawFocusRing(Canvas content);
+ private native void nativeDrawCursorRing(Canvas content);
+ private native void nativeDrawMatches(Canvas canvas);
private native void nativeDrawSelection(Canvas content
, int x, int y, boolean extendSelection);
private native void nativeDrawSelectionRegion(Canvas content);
- private native boolean nativeUpdateFocusNode();
- private native Rect nativeGetFocusRingBounds();
- private native Rect nativeGetNavBounds();
+ private native void nativeDumpDisplayTree(String urlOrNull);
+ private native int nativeFindAll(String findLower, String findUpper);
+ private native void nativeFindNext(boolean forward);
+ private native boolean nativeFocusCandidateIsPassword();
+ private native boolean nativeFocusCandidateIsRtlText();
+ private native boolean nativeFocusCandidateIsTextField();
+ private native boolean nativeFocusCandidateIsTextInput();
+ private native int nativeFocusCandidateMaxLength();
+ /* package */ native String nativeFocusCandidateName();
+ private native Rect nativeFocusCandidateNodeBounds();
+ /* package */ native int nativeFocusCandidatePointer();
+ private native String nativeFocusCandidateText();
+ private native int nativeFocusCandidateTextSize();
+ /* package */ native int nativeFocusNodePointer();
+ private native Rect nativeGetCursorRingBounds();
+ private native Region nativeGetSelection();
+ private native boolean nativeHasCursorNode();
+ private native boolean nativeHasFocusNode();
+ private native void nativeHideCursor();
+ private native String nativeImageURI(int x, int y);
private native void nativeInstrumentReport();
- private native void nativeMarkNodeInvalid(int node);
+ /* package */ native void nativeMoveCursorToNextTextInput();
// return true if the page has been scrolled
- private native boolean nativeMotionUp(int x, int y, int slop, boolean isClick);
+ private native boolean nativeMotionUp(int x, int y, int slop);
// returns false if it handled the key
- private native boolean nativeMoveFocus(int keyCode, int count,
+ private native boolean nativeMoveCursor(int keyCode, int count,
boolean noScroll);
- private native void nativeNotifyFocusSet(boolean inEditingMode);
- private native void nativeRecomputeFocus();
+ private native int nativeMoveGeneration();
+ private native void nativeMoveSelection(int x, int y,
+ boolean extendSelection);
+ private native boolean nativePluginEatsNavKey();
// Like many other of our native methods, you must make sure that
// mNativeClass is not null before calling this method.
private native void nativeRecordButtons(boolean focused,
boolean pressed, boolean invalidate);
- private native void nativeResetFocus();
- private native void nativeResetNavClipBounds();
private native void nativeSelectBestAt(Rect rect);
private native void nativeSetFindIsDown();
private native void nativeSetFollowedLink(boolean followed);
private native void nativeSetHeightCanMeasure(boolean measure);
- private native void nativeSetNavBounds(Rect rect);
- private native void nativeSetNavClipBounds(Rect rect);
- private native String nativeImageURI(int x, int y);
+ // Returns a value corresponding to CachedFrame::ImeAction
+ /* package */ native int nativeTextFieldAction();
/**
- * Returns true if the native focus nodes says it wants to handle key events
- * (ala plugins). This can only be called if mNativeClass is non-zero!
+ * Perform a click on a currently focused text input. Since it is already
+ * focused, there is no need to go through the nativeMotionUp code, which
+ * may change the Cursor.
*/
- private native boolean nativeFocusNodeWantsKeyEvents();
- private native void nativeMoveSelection(int x, int y
- , boolean extendSelection);
- private native Region nativeGetSelection();
-
- private native void nativeDumpDisplayTree(String urlOrNull);
+ private native void nativeTextInputMotionUp(int x, int y);
+ private native int nativeTextGeneration();
+ // Never call this version except by updateCachedTextfield(String) -
+ // we always want to pass in our generation number.
+ private native void nativeUpdateCachedTextfield(String updatedText,
+ int generation);
+ private native void nativeUpdatePluginReceivesEvents();
+ // return NO_LEFTEDGE means failure.
+ private static final int NO_LEFTEDGE = -1;
+ private native int nativeGetBlockLeftEdge(int x, int y, float scale);
}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index a185779..30dea74 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -92,14 +92,46 @@ public class WebViewClient {
cancelMsg.sendToTarget();
}
+ // These ints must match up to the hidden values in EventHandler.
+ /** Generic error */
+ public static final int ERROR_UNKNOWN = -1;
+ /** Server or proxy hostname lookup failed */
+ public static final int ERROR_HOST_LOOKUP = -2;
+ /** Unsupported authentication scheme (not basic or digest) */
+ public static final int ERROR_UNSUPPORTED_AUTH_SCHEME = -3;
+ /** User authentication failed on server */
+ public static final int ERROR_AUTHENTICATION = -4;
+ /** User authentication failed on proxy */
+ public static final int ERROR_PROXY_AUTHENTICATION = -5;
+ /** Failed to connect to the server */
+ public static final int ERROR_CONNECT = -6;
+ /** Failed to read or write to the server */
+ public static final int ERROR_IO = -7;
+ /** Connection timed out */
+ public static final int ERROR_TIMEOUT = -8;
+ /** Too many redirects */
+ public static final int ERROR_REDIRECT_LOOP = -9;
+ /** Unsupported URI scheme */
+ public static final int ERROR_UNSUPPORTED_SCHEME = -10;
+ /** Failed to perform SSL handshake */
+ public static final int ERROR_FAILED_SSL_HANDSHAKE = -11;
+ /** Malformed URL */
+ public static final int ERROR_BAD_URL = -12;
+ /** Generic file error */
+ public static final int ERROR_FILE = -13;
+ /** File not found */
+ public static final int ERROR_FILE_NOT_FOUND = -14;
+ /** Too many requests during this load */
+ public static final int ERROR_TOO_MANY_REQUESTS = -15;
+
/**
- * Report an error to an activity. These errors come up from WebCore, and
- * are network errors.
- *
+ * Report an error to the host application. These errors are unrecoverable
+ * (i.e. the main resource is unavailable). The errorCode parameter
+ * corresponds to one of the ERROR_* constants.
* @param view The WebView that is initiating the callback.
- * @param errorCode The HTTP error code.
- * @param description A String description.
- * @param failingUrl The url that failed.
+ * @param errorCode The error code corresponding to an ERROR_* value.
+ * @param description A String describing the error.
+ * @param failingUrl The url that failed to load.
*/
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index a5fa41e..26d9343 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -32,17 +32,19 @@ import android.os.Process;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import junit.framework.Assert;
final class WebViewCore {
private static final String LOGTAG = "webcore";
- static final boolean DEBUG = false;
- static final boolean LOGV_ENABLED = DEBUG;
static {
// Load libwebcore during static initialization. This happens in the
@@ -67,7 +69,8 @@ final class WebViewCore {
private int mNativeClass;
// The BrowserFrame is an interface to the native Frame component.
private BrowserFrame mBrowserFrame;
-
+ // Custom JS interfaces to add during the initialization.
+ private Map<String, Object> mJavascriptInterfaces;
/*
* range is from 200 to 10,000. 0 is a special value means device-width. -1
* means undefined.
@@ -96,22 +99,29 @@ final class WebViewCore {
private int mViewportMaximumScale = 0;
private boolean mViewportUserScalable = true;
-
- private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT;
+
+ private int mRestoredScale = 0;
+ private int mRestoredScreenWidthScale = 0;
private int mRestoredX = 0;
private int mRestoredY = 0;
private int mWebkitScrollX = 0;
private int mWebkitScrollY = 0;
+ // If the site doesn't use viewport meta tag to specify the viewport, use
+ // DEFAULT_VIEWPORT_WIDTH as default viewport width
+ static final int DEFAULT_VIEWPORT_WIDTH = 800;
+
// The thread name used to identify the WebCore thread and for use in
// debugging other classes that require operation within the WebCore thread.
/* package */ static final String THREAD_NAME = "WebViewCoreThread";
- public WebViewCore(Context context, WebView w, CallbackProxy proxy) {
+ public WebViewCore(Context context, WebView w, CallbackProxy proxy,
+ Map<String, Object> javascriptInterfaces) {
// No need to assign this in the WebCore thread.
mCallbackProxy = proxy;
mWebView = w;
+ mJavascriptInterfaces = javascriptInterfaces;
// This context object is used to initialize the WebViewCore during
// subwindow creation.
mContext = context;
@@ -143,6 +153,8 @@ final class WebViewCore {
// The WebIconDatabase needs to be initialized within the UI thread so
// just request the instance here.
WebIconDatabase.getInstance();
+ // Create the WebStorage singleton
+ WebStorage.getInstance();
// Send a message to initialize the WebViewCore.
Message init = sWebCoreHandler.obtainMessage(
WebCoreThread.INITIALIZE, this);
@@ -157,11 +169,16 @@ final class WebViewCore {
* in turn creates a C level FrameView and attaches it to the frame.
*/
mBrowserFrame = new BrowserFrame(mContext, this, mCallbackProxy,
- mSettings);
+ mSettings, mJavascriptInterfaces);
+ mJavascriptInterfaces = null;
// Sync the native settings and also create the WebCore thread handler.
mSettings.syncSettingsAndCreateHandler(mBrowserFrame);
// Create the handler and transfer messages for the IconDatabase
WebIconDatabase.getInstance().createHandler();
+ // Create the handler for WebStorage
+ WebStorage.getInstance().createHandler();
+ // Create the handler for GeolocationPermissions.
+ GeolocationPermissions.getInstance().createHandler();
// The transferMessages call will transfer all pending messages to the
// WebCore thread handler.
mEventHub.transferMessages();
@@ -225,6 +242,16 @@ final class WebViewCore {
}
/**
+ * Add an error message to the client's console.
+ * @param message The message to add
+ * @param lineNumber the line on which the error occurred
+ * @param sourceID the filename of the source that caused the error.
+ */
+ protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+ }
+
+ /**
* Invoke a javascript alert.
* @param message The message displayed in the alert.
*/
@@ -233,6 +260,71 @@ final class WebViewCore {
}
/**
+ * Notify the browser that the origin has exceeded it's database quota.
+ * @param url The URL that caused the overflow.
+ * @param databaseIdentifier The identifier of the database.
+ * @param currentQuota The current quota for the origin.
+ * @param estimatedSize The estimated size of the database.
+ */
+ protected void exceededDatabaseQuota(String url,
+ String databaseIdentifier,
+ long currentQuota,
+ long estimatedSize) {
+ // Inform the callback proxy of the quota overflow. Send an object
+ // that encapsulates a call to the nativeSetDatabaseQuota method to
+ // awaken the sleeping webcore thread when a decision from the
+ // client to allow or deny quota is available.
+ mCallbackProxy.onExceededDatabaseQuota(url, databaseIdentifier,
+ currentQuota, estimatedSize, getUsedQuota(),
+ new WebStorage.QuotaUpdater() {
+ public void updateQuota(long quota) {
+ nativeSetNewStorageLimit(quota);
+ }
+ });
+ }
+
+ /**
+ * Notify the browser that the appcache has exceeded its max size.
+ * @param spaceNeeded is the amount of disk space that would be needed
+ * in order for the last appcache operation to succeed.
+ */
+ protected void reachedMaxAppCacheSize(long spaceNeeded) {
+ mCallbackProxy.onReachedMaxAppCacheSize(spaceNeeded, getUsedQuota(),
+ new WebStorage.QuotaUpdater() {
+ public void updateQuota(long quota) {
+ nativeSetNewStorageLimit(quota);
+ }
+ });
+ }
+
+ /**
+ * Shows a prompt to ask the user to set the Geolocation permission state
+ * for the given origin.
+ * @param origin The origin for which Geolocation permissions are
+ * requested.
+ */
+ protected void geolocationPermissionsShowPrompt(String origin) {
+ mCallbackProxy.onGeolocationPermissionsShowPrompt(origin,
+ new GeolocationPermissions.Callback() {
+ public void invoke(String origin, boolean allow, boolean remember) {
+ GeolocationPermissionsData data = new GeolocationPermissionsData();
+ data.mOrigin = origin;
+ data.mAllow = allow;
+ data.mRemember = remember;
+ // Marshall to WebCore thread.
+ sendMessage(EventHub.GEOLOCATION_PERMISSIONS_PROVIDE, data);
+ }
+ });
+ }
+
+ /**
+ * Hides the Geolocation permissions prompt.
+ */
+ protected void geolocationPermissionsHidePrompt() {
+ mCallbackProxy.onGeolocationPermissionsHidePrompt();
+ }
+
+ /**
* Invoke a javascript confirm dialog.
* @param message The message displayed in the dialog.
* @return True if the user confirmed or false if the user cancelled.
@@ -277,31 +369,36 @@ final class WebViewCore {
// JNI methods
//-------------------------------------------------------------------------
- static native String nativeFindAddress(String addr);
+ static native String nativeFindAddress(String addr, boolean caseInsensitive);
/**
* Empty the picture set.
*/
private native void nativeClearContent();
-
+
/**
* Create a flat picture from the set of pictures.
*/
private native void nativeCopyContentToPicture(Picture picture);
-
+
/**
* Draw the picture set with a background color. Returns true
- * if some individual picture took too long to draw and can be
+ * if some individual picture took too long to draw and can be
* split into parts. Called from the UI thread.
*/
private native boolean nativeDrawContent(Canvas canvas, int color);
-
+
+ /**
+ * check to see if picture is blank and in progress
+ */
+ private native boolean nativePictureReady();
+
/**
* Redraw a portion of the picture set. The Point wh returns the
* width and height of the overall picture.
*/
private native boolean nativeRecordContent(Region invalRegion, Point wh);
-
+
/**
* Splits slow parts of the picture set. Called from the webkit
* thread after nativeDrawContent returns true.
@@ -309,9 +406,10 @@ final class WebViewCore {
private native void nativeSplitContent();
private native boolean nativeKey(int keyCode, int unichar,
- int repeatCount, boolean isShift, boolean isAlt, boolean isDown);
+ int repeatCount, boolean isShift, boolean isAlt, boolean isSym,
+ boolean isDown);
- private native boolean nativeClick();
+ private native void nativeClick(int framePtr, int nodePtr);
private native void nativeSendListBoxChoices(boolean[] choices, int size);
@@ -326,63 +424,58 @@ final class WebViewCore {
should this be called nativeSetViewPortSize?
*/
private native void nativeSetSize(int width, int height, int screenWidth,
- float scale, int realScreenWidth, int screenHeight);
+ float scale, int realScreenWidth, int screenHeight,
+ boolean ignoreHeight);
private native int nativeGetContentMinPrefWidth();
-
+
// Start: functions that deal with text editing
- private native void nativeReplaceTextfieldText(int frame, int node, int x,
- int y, int oldStart, int oldEnd, String replace, int newStart,
- int newEnd);
+ private native void nativeReplaceTextfieldText(
+ int oldStart, int oldEnd, String replace, int newStart, int newEnd,
+ int textGeneration);
- private native void passToJs(int frame, int node, int x, int y, int gen,
+ private native void passToJs(int gen,
String currentText, int keyCode, int keyValue, boolean down,
boolean cap, boolean fn, boolean sym);
+ private native void nativeSetFocusControllerActive(boolean active);
+
private native void nativeSaveDocumentState(int frame);
- private native void nativeSetFinalFocus(int framePtr, int nodePtr, int x,
- int y, boolean block);
+ private native void nativeMoveMouse(int framePtr, int x, int y);
- private native void nativeSetKitFocus(int moveGeneration,
- int buildGeneration, int framePtr, int nodePtr, int x, int y,
- boolean ignoreNullFocus);
+ private native void nativeMoveMouseIfLatest(int moveGeneration,
+ int framePtr, int x, int y);
private native String nativeRetrieveHref(int framePtr, int nodePtr);
-
- private native void nativeTouchUp(int touchGeneration,
- int buildGeneration, int framePtr, int nodePtr, int x, int y,
- int size, boolean isClick, boolean retry);
+
+ private native void nativeTouchUp(int touchGeneration,
+ int framePtr, int nodePtr, int x, int y);
private native boolean nativeHandleTouchEvent(int action, int x, int y);
- private native void nativeUnblockFocus();
-
private native void nativeUpdateFrameCache();
-
- private native void nativeSetSnapAnchor(int x, int y);
-
- private native void nativeSnapToAnchor();
-
+
private native void nativeSetBackgroundColor(int color);
-
+
private native void nativeDumpDomTree(boolean useFile);
private native void nativeDumpRenderTree(boolean useFile);
private native void nativeDumpNavTree();
- private native void nativeRefreshPlugins(boolean reloadOpenPages);
-
+ private native void nativeSetJsFlags(String flags);
+
/**
* Delete text from start to end in the focused textfield. If there is no
- * focus, or if start == end, silently fail. If start and end are out of
+ * focus, or if start == end, silently fail. If start and end are out of
* order, swap them.
* @param start Beginning of selection to delete.
* @param end End of selection to delete.
+ * @param textGeneration Text generation number when delete was pressed.
*/
- private native void nativeDeleteSelection(int frame, int node, int x, int y,
- int start, int end);
+ private native void nativeDeleteSelection(int start, int end,
+ int textGeneration);
/**
* Set the selection to (start, end) in the focused textfield. If start and
@@ -390,15 +483,34 @@ final class WebViewCore {
* @param start Beginning of selection.
* @param end End of selection.
*/
- private native void nativeSetSelection(int frame, int node, int x, int y,
- int start, int end);
+ private native void nativeSetSelection(int start, int end);
private native String nativeGetSelection(Region sel);
-
+
// Register a scheme to be treated as local scheme so that it can access
// local asset files for resources
private native void nativeRegisterURLSchemeAsLocal(String scheme);
+ /*
+ * Inform webcore that the user has decided whether to allow or deny new
+ * quota for the current origin or more space for the app cache, and that
+ * the main thread should wake up now.
+ * @param limit Is the new quota for an origin or new app cache max size.
+ */
+ private native void nativeSetNewStorageLimit(long limit);
+
+ private native void nativeUpdatePluginState(int framePtr, int nodePtr, int state);
+
+ /**
+ * Provide WebCore with a Geolocation permission state for the specified
+ * origin.
+ * @param origin The origin for which Geolocation permissions are provided.
+ * @param allow Whether Geolocation permissions are allowed.
+ * @param remember Whether this decision should be remembered beyond the
+ * life of the current page.
+ */
+ private native void nativeGeolocationPermissionsProvide(String origin, boolean allow, boolean remember);
+
// EventHub for processing messages
private final EventHub mEventHub;
// WebCore thread handler
@@ -447,7 +559,7 @@ final class WebViewCore {
CacheManager.endCacheTransaction();
CacheManager.startCacheTransaction();
sendMessageDelayed(
- obtainMessage(CACHE_TICKER),
+ obtainMessage(CACHE_TICKER),
CACHE_TICKER_INTERVAL);
}
break;
@@ -472,36 +584,64 @@ final class WebViewCore {
}
}
- static class FocusData {
- FocusData() {}
- FocusData(FocusData d) {
- mMoveGeneration = d.mMoveGeneration;
- mBuildGeneration = d.mBuildGeneration;
- mFrame = d.mFrame;
- mNode = d.mNode;
- mX = d.mX;
- mY = d.mY;
- mIgnoreNullFocus = d.mIgnoreNullFocus;
+ static class BaseUrlData {
+ String mBaseUrl;
+ String mData;
+ String mMimeType;
+ String mEncoding;
+ String mFailUrl;
+ }
+
+ static class CursorData {
+ CursorData() {}
+ CursorData(int frame, int node, int x, int y) {
+ mFrame = frame;
+ mX = x;
+ mY = y;
}
int mMoveGeneration;
- int mBuildGeneration;
int mFrame;
- int mNode;
int mX;
int mY;
- boolean mIgnoreNullFocus;
+ }
+
+ static class JSInterfaceData {
+ Object mObject;
+ String mInterfaceName;
+ }
+
+ static class JSKeyData {
+ String mCurrentText;
+ KeyEvent mEvent;
+ }
+
+ static class PostUrlData {
+ String mUrl;
+ byte[] mPostData;
+ }
+
+ static class ReplaceTextData {
+ String mReplace;
+ int mNewStart;
+ int mNewEnd;
+ int mTextGeneration;
+ }
+
+ static class TextSelectionData {
+ public TextSelectionData(int start, int end) {
+ mStart = start;
+ mEnd = end;
+ }
+ int mStart;
+ int mEnd;
}
static class TouchUpData {
int mMoveGeneration;
- int mBuildGeneration;
int mFrame;
int mNode;
int mX;
int mY;
- int mSize;
- boolean mIsClick;
- boolean mRetry;
}
static class TouchEventData {
@@ -510,7 +650,21 @@ final class WebViewCore {
int mY;
}
+ static class PluginStateData {
+ int mFrame;
+ int mNode;
+ int mState;
+ }
+
+ static class GeolocationPermissionsData {
+ String mOrigin;
+ boolean mAllow;
+ boolean mRemember;
+ }
+
static final String[] HandlerDebugString = {
+ "UPDATE_FRAME_CACHE_IF_LOADING", // = 98
+ "SCROLL_TEXT_INPUT", // = 99
"LOAD_URL", // = 100;
"STOP_LOADING", // = 101;
"RELOAD", // = 102;
@@ -532,33 +686,37 @@ final class WebViewCore {
"CLICK", // = 118;
"SET_NETWORK_STATE", // = 119;
"DOC_HAS_IMAGES", // = 120;
- "SET_SNAP_ANCHOR", // = 121;
+ "121", // = 121;
"DELETE_SELECTION", // = 122;
"LISTBOX_CHOICES", // = 123;
"SINGLE_LISTBOX_CHOICE", // = 124;
- "125",
+ "MESSAGE_RELAY", // = 125;
"SET_BACKGROUND_COLOR", // = 126;
- "UNBLOCK_FOCUS", // = 127;
+ "PLUGIN_STATE", // = 127;
"SAVE_DOCUMENT_STATE", // = 128;
"GET_SELECTION", // = 129;
"WEBKIT_DRAW", // = 130;
"SYNC_SCROLL", // = 131;
- "REFRESH_PLUGINS", // = 132;
- // this will replace REFRESH_PLUGINS in the next release
- "POST_URL", // = 142;
+ "POST_URL", // = 132;
"SPLIT_PICTURE_SET", // = 133;
"CLEAR_CONTENT", // = 134;
- "SET_FINAL_FOCUS", // = 135;
- "SET_KIT_FOCUS", // = 136;
- "REQUEST_FOCUS_HREF", // = 137;
+ "SET_MOVE_MOUSE", // = 135;
+ "SET_MOVE_MOUSE_IF_LATEST", // = 136;
+ "REQUEST_CURSOR_HREF", // = 137;
"ADD_JS_INTERFACE", // = 138;
"LOAD_DATA", // = 139;
"TOUCH_UP", // = 140;
"TOUCH_EVENT", // = 141;
+ "SET_ACTIVE", // = 142;
+ "ON_PAUSE", // = 143
+ "ON_RESUME", // = 144
+ "FREE_MEMORY", // = 145
};
class EventHub {
// Message Ids
+ static final int UPDATE_FRAME_CACHE_IF_LOADING = 98;
+ static final int SCROLL_TEXT_INPUT = 99;
static final int LOAD_URL = 100;
static final int STOP_LOADING = 101;
static final int RELOAD = 102;
@@ -580,26 +738,24 @@ final class WebViewCore {
static final int CLICK = 118;
static final int SET_NETWORK_STATE = 119;
static final int DOC_HAS_IMAGES = 120;
- static final int SET_SNAP_ANCHOR = 121;
static final int DELETE_SELECTION = 122;
static final int LISTBOX_CHOICES = 123;
static final int SINGLE_LISTBOX_CHOICE = 124;
+ static final int MESSAGE_RELAY = 125;
static final int SET_BACKGROUND_COLOR = 126;
- static final int UNBLOCK_FOCUS = 127;
+ static final int PLUGIN_STATE = 127; // plugin notifications
static final int SAVE_DOCUMENT_STATE = 128;
static final int GET_SELECTION = 129;
static final int WEBKIT_DRAW = 130;
static final int SYNC_SCROLL = 131;
- static final int REFRESH_PLUGINS = 132;
- // this will replace REFRESH_PLUGINS in the next release
- static final int POST_URL = 142;
+ static final int POST_URL = 132;
static final int SPLIT_PICTURE_SET = 133;
static final int CLEAR_CONTENT = 134;
-
+
// UI nav messages
- static final int SET_FINAL_FOCUS = 135;
- static final int SET_KIT_FOCUS = 136;
- static final int REQUEST_FOCUS_HREF = 137;
+ static final int SET_MOVE_MOUSE = 135;
+ static final int SET_MOVE_MOUSE_IF_LATEST = 136;
+ static final int REQUEST_CURSOR_HREF = 137;
static final int ADD_JS_INTERFACE = 138;
static final int LOAD_DATA = 139;
@@ -608,6 +764,17 @@ final class WebViewCore {
// message used to pass UI touch events to WebCore
static final int TOUCH_EVENT = 141;
+ // Used to tell the focus controller not to draw the blinking cursor,
+ // based on whether the WebView has focus and whether the WebView's
+ // cursor matches the webpage's focus.
+ static final int SET_ACTIVE = 142;
+
+ // lifecycle activities for just this DOM (unlike pauseTimers, which
+ // is global)
+ static final int ON_PAUSE = 143;
+ static final int ON_RESUME = 144;
+ static final int FREE_MEMORY = 145;
+
// Network-based messaging
static final int CLEAR_SSL_PREF_TABLE = 150;
@@ -620,12 +787,12 @@ final class WebViewCore {
static final int DUMP_RENDERTREE = 171;
static final int DUMP_NAVTREE = 172;
+ static final int SET_JS_FLAGS = 173;
+ // Geolocation
+ static final int GEOLOCATION_PERMISSIONS_PROVIDE = 180;
+
// private message ids
private static final int DESTROY = 200;
-
- // flag values passed to message SET_FINAL_FOCUS
- static final int NO_FOCUS_CHANGE_BLOCK = 0;
- static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
// Private handler for WebCore messages.
private Handler mHandler;
@@ -654,10 +821,14 @@ final class WebViewCore {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
- > TOUCH_EVENT ? Integer.toString(msg.what)
- : HandlerDebugString[msg.what - LOAD_URL]);
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, (msg.what < UPDATE_FRAME_CACHE_IF_LOADING
+ || msg.what
+ > FREE_MEMORY ? Integer.toString(msg.what)
+ : HandlerDebugString[msg.what
+ - UPDATE_FRAME_CACHE_IF_LOADING])
+ + " arg1=" + msg.arg1 + " arg2=" + msg.arg2
+ + " obj=" + msg.obj);
}
switch (msg.what) {
case WEBKIT_DRAW:
@@ -672,20 +843,26 @@ final class WebViewCore {
mNativeClass = 0;
break;
+ case UPDATE_FRAME_CACHE_IF_LOADING:
+ nativeUpdateFrameCacheIfLoading();
+ break;
+
+ case SCROLL_TEXT_INPUT:
+ nativeScrollFocusedTextInput(msg.arg1, msg.arg2);
+ break;
+
case LOAD_URL:
loadUrl((String) msg.obj);
break;
case POST_URL: {
- HashMap param = (HashMap) msg.obj;
- String url = (String) param.get("url");
- byte[] data = (byte[]) param.get("data");
- mBrowserFrame.postUrl(url, data);
+ PostUrlData param = (PostUrlData) msg.obj;
+ mBrowserFrame.postUrl(param.mUrl, param.mPostData);
break;
}
case LOAD_DATA:
- HashMap loadParams = (HashMap) msg.obj;
- String baseUrl = (String) loadParams.get("baseUrl");
+ BaseUrlData loadParams = (BaseUrlData) msg.obj;
+ String baseUrl = loadParams.mBaseUrl;
if (baseUrl != null) {
int i = baseUrl.indexOf(':');
if (i > 0) {
@@ -698,7 +875,7 @@ final class WebViewCore {
* we automatically add the scheme of the
* baseUrl for local access as long as it is
* not http(s)/ftp(s)/about/javascript
- */
+ */
String scheme = baseUrl.substring(0, i);
if (!scheme.startsWith("http") &&
!scheme.startsWith("ftp") &&
@@ -709,16 +886,16 @@ final class WebViewCore {
}
}
mBrowserFrame.loadData(baseUrl,
- (String) loadParams.get("data"),
- (String) loadParams.get("mimeType"),
- (String) loadParams.get("encoding"),
- (String) loadParams.get("failUrl"));
+ loadParams.mData,
+ loadParams.mMimeType,
+ loadParams.mEncoding,
+ loadParams.mFailUrl);
break;
case STOP_LOADING:
- // If the WebCore has committed the load, but not
- // finished the first layout yet, we need to set
- // first layout done to trigger the interpreted side sync
+ // If the WebCore has committed the load, but not
+ // finished the first layout yet, we need to set
+ // first layout done to trigger the interpreted side sync
// up with native side
if (mBrowserFrame.committed()
&& !mBrowserFrame.firstLayoutDone()) {
@@ -741,20 +918,24 @@ final class WebViewCore {
break;
case CLICK:
- nativeClick();
+ nativeClick(msg.arg1, msg.arg2);
break;
- case VIEW_SIZE_CHANGED:
- viewSizeChanged(msg.arg1, msg.arg2,
- ((Float) msg.obj).floatValue());
+ case VIEW_SIZE_CHANGED: {
+ WebView.ViewSizeData data =
+ (WebView.ViewSizeData) msg.obj;
+ viewSizeChanged(data.mWidth, data.mHeight,
+ data.mTextWrapWidth, data.mScale,
+ data.mIgnoreHeight);
break;
-
+ }
case SET_SCROLL_OFFSET:
// note: these are in document coordinates
// (inv-zoom)
- nativeSetScrollOffset(msg.arg1, msg.arg2);
+ Point pt = (Point) msg.obj;
+ nativeSetScrollOffset(msg.arg1, pt.x, pt.y);
break;
-
+
case SET_GLOBAL_BOUNDS:
Rect r = (Rect) msg.obj;
nativeSetGlobalBounds(r.left, r.top, r.width(),
@@ -765,7 +946,7 @@ final class WebViewCore {
// If it is a standard load and the load is not
// committed yet, we interpret BACK as RELOAD
if (!mBrowserFrame.committed() && msg.arg1 == -1 &&
- (mBrowserFrame.loadType() ==
+ (mBrowserFrame.loadType() ==
BrowserFrame.FRAME_LOADTYPE_STANDARD)) {
mBrowserFrame.reload(true);
} else {
@@ -802,6 +983,24 @@ final class WebViewCore {
}
break;
+ case ON_PAUSE:
+ nativePause();
+ break;
+
+ case ON_RESUME:
+ nativeResume();
+ break;
+
+ case FREE_MEMORY:
+ clearCache(false);
+ nativeFreeMemory();
+ break;
+
+ case PLUGIN_STATE:
+ PluginStateData psd = (PluginStateData) msg.obj;
+ nativeUpdatePluginState(psd.mFrame, psd.mNode, psd.mState);
+ break;
+
case SET_NETWORK_STATE:
if (BrowserFrame.sJavaBridge == null) {
throw new IllegalStateException("No WebView " +
@@ -812,10 +1011,7 @@ final class WebViewCore {
break;
case CLEAR_CACHE:
- mBrowserFrame.clearCache();
- if (msg.arg1 == 1) {
- CacheManager.removeAllCacheFiles();
- }
+ clearCache(msg.arg1 == 1);
break;
case CLEAR_HISTORY:
@@ -823,29 +1019,21 @@ final class WebViewCore {
close(mBrowserFrame.mNativeFrame);
break;
- case REPLACE_TEXT:
- HashMap jMap = (HashMap) msg.obj;
- FocusData fData = (FocusData) jMap.get("focusData");
- String replace = (String) jMap.get("replace");
- int newStart =
- ((Integer) jMap.get("start")).intValue();
- int newEnd =
- ((Integer) jMap.get("end")).intValue();
- nativeReplaceTextfieldText(fData.mFrame,
- fData.mNode, fData.mX, fData.mY, msg.arg1,
- msg.arg2, replace, newStart, newEnd);
+ case REPLACE_TEXT:
+ ReplaceTextData rep = (ReplaceTextData) msg.obj;
+ nativeReplaceTextfieldText(msg.arg1, msg.arg2,
+ rep.mReplace, rep.mNewStart, rep.mNewEnd,
+ rep.mTextGeneration);
break;
case PASS_TO_JS: {
- HashMap jsMap = (HashMap) msg.obj;
- FocusData fDat = (FocusData) jsMap.get("focusData");
- KeyEvent evt = (KeyEvent) jsMap.get("event");
+ JSKeyData jsData = (JSKeyData) msg.obj;
+ KeyEvent evt = jsData.mEvent;
int keyCode = evt.getKeyCode();
int keyValue = evt.getUnicodeChar();
int generation = msg.arg1;
- passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
- generation,
- (String) jsMap.get("currentText"),
+ passToJs(generation,
+ jsData.mCurrentText,
keyCode,
keyValue,
evt.isDown(),
@@ -855,8 +1043,8 @@ final class WebViewCore {
}
case SAVE_DOCUMENT_STATE: {
- FocusData fDat = (FocusData) msg.obj;
- nativeSaveDocumentState(fDat.mFrame);
+ CursorData cDat = (CursorData) msg.obj;
+ nativeSaveDocumentState(cDat.mFrame);
break;
}
@@ -868,11 +1056,8 @@ final class WebViewCore {
case TOUCH_UP:
TouchUpData touchUpData = (TouchUpData) msg.obj;
nativeTouchUp(touchUpData.mMoveGeneration,
- touchUpData.mBuildGeneration,
touchUpData.mFrame, touchUpData.mNode,
- touchUpData.mX, touchUpData.mY,
- touchUpData.mSize, touchUpData.mIsClick,
- touchUpData.mRetry);
+ touchUpData.mX, touchUpData.mY);
break;
case TOUCH_EVENT: {
@@ -885,13 +1070,14 @@ final class WebViewCore {
break;
}
+ case SET_ACTIVE:
+ nativeSetFocusControllerActive(msg.arg1 == 1);
+ break;
+
case ADD_JS_INTERFACE:
- HashMap map = (HashMap) msg.obj;
- Object obj = map.get("object");
- String interfaceName = (String)
- map.get("interfaceName");
- mBrowserFrame.addJavascriptInterface(obj,
- interfaceName);
+ JSInterfaceData jsData = (JSInterfaceData) msg.obj;
+ mBrowserFrame.addJavascriptInterface(jsData.mObject,
+ jsData.mInterfaceName);
break;
case REQUEST_EXT_REPRESENTATION:
@@ -903,35 +1089,27 @@ final class WebViewCore {
mBrowserFrame.documentAsText((Message) msg.obj);
break;
- case SET_FINAL_FOCUS:
- FocusData finalData = (FocusData) msg.obj;
- nativeSetFinalFocus(finalData.mFrame,
- finalData.mNode, finalData.mX,
- finalData.mY, msg.arg1
- != EventHub.NO_FOCUS_CHANGE_BLOCK);
+ case SET_MOVE_MOUSE:
+ CursorData cursorData = (CursorData) msg.obj;
+ nativeMoveMouse(cursorData.mFrame,
+ cursorData.mX, cursorData.mY);
break;
- case UNBLOCK_FOCUS:
- nativeUnblockFocus();
+ case SET_MOVE_MOUSE_IF_LATEST:
+ CursorData cData = (CursorData) msg.obj;
+ nativeMoveMouseIfLatest(cData.mMoveGeneration,
+ cData.mFrame,
+ cData.mX, cData.mY);
break;
- case SET_KIT_FOCUS:
- FocusData focusData = (FocusData) msg.obj;
- nativeSetKitFocus(focusData.mMoveGeneration,
- focusData.mBuildGeneration,
- focusData.mFrame, focusData.mNode,
- focusData.mX, focusData.mY,
- focusData.mIgnoreNullFocus);
- break;
-
- case REQUEST_FOCUS_HREF: {
+ case REQUEST_CURSOR_HREF: {
Message hrefMsg = (Message) msg.obj;
String res = nativeRetrieveHref(msg.arg1, msg.arg2);
hrefMsg.getData().putString("url", res);
hrefMsg.sendToTarget();
break;
}
-
+
case UPDATE_CACHE_AND_TEXT_ENTRY:
nativeUpdateFrameCache();
// FIXME: this should provide a minimal rectangle
@@ -948,24 +1126,17 @@ final class WebViewCore {
imageResult.sendToTarget();
break;
- case SET_SNAP_ANCHOR:
- nativeSetSnapAnchor(msg.arg1, msg.arg2);
- break;
-
case DELETE_SELECTION:
- FocusData delData = (FocusData) msg.obj;
- nativeDeleteSelection(delData.mFrame,
- delData.mNode, delData.mX,
- delData.mY, msg.arg1, msg.arg2);
+ TextSelectionData deleteSelectionData
+ = (TextSelectionData) msg.obj;
+ nativeDeleteSelection(deleteSelectionData.mStart,
+ deleteSelectionData.mEnd, msg.arg1);
break;
case SET_SELECTION:
- FocusData selData = (FocusData) msg.obj;
- nativeSetSelection(selData.mFrame,
- selData.mNode, selData.mX,
- selData.mY, msg.arg1, msg.arg2);
+ nativeSetSelection(msg.arg1, msg.arg2);
break;
-
+
case LISTBOX_CHOICES:
SparseBooleanArray choices = (SparseBooleanArray)
msg.obj;
@@ -974,18 +1145,18 @@ final class WebViewCore {
for (int c = 0; c < choicesSize; c++) {
choicesArray[c] = choices.get(c);
}
- nativeSendListBoxChoices(choicesArray,
+ nativeSendListBoxChoices(choicesArray,
choicesSize);
break;
case SINGLE_LISTBOX_CHOICE:
nativeSendListBoxChoice(msg.arg1);
break;
-
+
case SET_BACKGROUND_COLOR:
nativeSetBackgroundColor(msg.arg1);
break;
-
+
case GET_SELECTION:
String str = nativeGetSelection((Region) msg.obj);
Message.obtain(mWebView.mPrivateHandler
@@ -1005,26 +1176,39 @@ final class WebViewCore {
nativeDumpNavTree();
break;
+ case SET_JS_FLAGS:
+ nativeSetJsFlags((String)msg.obj);
+ break;
+
+ case GEOLOCATION_PERMISSIONS_PROVIDE:
+ GeolocationPermissionsData data =
+ (GeolocationPermissionsData) msg.obj;
+ nativeGeolocationPermissionsProvide(data.mOrigin,
+ data.mAllow, data.mRemember);
+ break;
+
case SYNC_SCROLL:
mWebkitScrollX = msg.arg1;
mWebkitScrollY = msg.arg2;
break;
- case REFRESH_PLUGINS:
- nativeRefreshPlugins(msg.arg1 != 0);
- break;
-
case SPLIT_PICTURE_SET:
nativeSplitContent();
mSplitPictureIsScheduled = false;
break;
-
+
case CLEAR_CONTENT:
// Clear the view so that onDraw() will draw nothing
// but white background
// (See public method WebView.clearView)
nativeClearContent();
break;
+
+ case MESSAGE_RELAY:
+ if (msg.obj instanceof Message) {
+ ((Message) msg.obj).sendToTarget();
+ }
+ break;
}
}
};
@@ -1066,6 +1250,18 @@ final class WebViewCore {
}
}
+ private synchronized boolean hasMessages(int what) {
+ if (mBlockMessages) {
+ return false;
+ }
+ if (mMessages != null) {
+ Log.w(LOGTAG, "hasMessages() is not supported in this case.");
+ return false;
+ } else {
+ return mHandler.hasMessages(what);
+ }
+ }
+
private synchronized void sendMessageDelayed(Message msg, long delay) {
if (mBlockMessages) {
return;
@@ -1114,7 +1310,7 @@ final class WebViewCore {
//-------------------------------------------------------------------------
void stopLoading() {
- if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
if (mBrowserFrame != null) {
mBrowserFrame.stopLoading();
}
@@ -1177,9 +1373,22 @@ final class WebViewCore {
// We don't want anyone to post a message between removing pending
// messages and sending the destroy message.
synchronized (mEventHub) {
+ // RESUME_TIMERS and PAUSE_TIMERS are per process base. They need to
+ // be preserved even the WebView is destroyed.
+ // Note: we should not have more than one RESUME_TIMERS/PAUSE_TIMERS
+ boolean hasResume = mEventHub.hasMessages(EventHub.RESUME_TIMERS);
+ boolean hasPause = mEventHub.hasMessages(EventHub.PAUSE_TIMERS);
mEventHub.removeMessages();
mEventHub.sendMessageAtFrontOfQueue(
Message.obtain(null, EventHub.DESTROY));
+ if (hasPause) {
+ mEventHub.sendMessageAtFrontOfQueue(
+ Message.obtain(null, EventHub.PAUSE_TIMERS));
+ }
+ if (hasResume) {
+ mEventHub.sendMessageAtFrontOfQueue(
+ Message.obtain(null, EventHub.RESUME_TIMERS));
+ }
mEventHub.blockMessages();
mWebView = null;
}
@@ -1189,20 +1398,42 @@ final class WebViewCore {
// WebViewCore private methods
//-------------------------------------------------------------------------
+ private void clearCache(boolean includeDiskFiles) {
+ mBrowserFrame.clearCache();
+ if (includeDiskFiles) {
+ CacheManager.removeAllCacheFiles();
+ }
+ }
+
private void loadUrl(String url) {
- if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
mBrowserFrame.loadUrl(url);
}
private void key(KeyEvent evt, boolean isDown) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
- if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
+ int keyCode = evt.getKeyCode();
+ if (!nativeKey(keyCode, evt.getUnicodeChar(),
evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
- isDown)) {
+ evt.isSymPressed(),
+ isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
+ if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
+ && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, "key: arrow unused by plugin: " + keyCode);
+ }
+ if (mWebView != null && evt.isDown()) {
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.MOVE_OUT_OF_PLUGIN, keyCode).sendToTarget();
+ }
+ return;
+ }
// bubble up the event handling
+ // but do not bubble up the ENTER key, which would open the search
+ // bar without any text.
mCallbackProxy.onUnhandledKeyEvent(evt);
}
}
@@ -1210,21 +1441,25 @@ final class WebViewCore {
// These values are used to avoid requesting a layout based on old values
private int mCurrentViewWidth = 0;
private int mCurrentViewHeight = 0;
+ private float mCurrentViewScale = 1.0f;
// notify webkit that our virtual view size changed size (after inv-zoom)
- private void viewSizeChanged(int w, int h, float scale) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
+ private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
+ boolean ignoreHeight) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
+ + "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
+ }
if (w == 0) {
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
}
- if (mSettings.getUseWideViewPort()
- && (w < mViewportWidth || mViewportWidth == -1)) {
- int width = mViewportWidth;
+ int width = w;
+ if (mSettings.getUseWideViewPort()) {
if (mViewportWidth == -1) {
- if (mSettings.getLayoutAlgorithm() ==
+ if (mSettings.getLayoutAlgorithm() ==
WebSettings.LayoutAlgorithm.NORMAL) {
- width = WebView.ZOOM_OUT_WIDTH;
+ width = DEFAULT_VIEWPORT_WIDTH;
} else {
/*
* if a page's minimum preferred width is wider than the
@@ -1238,22 +1473,24 @@ final class WebViewCore {
* In the worse case, the native width will be adjusted when
* next zoom or screen orientation change happens.
*/
- width = Math.max(w, nativeGetContentMinPrefWidth());
+ width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
+ nativeGetContentMinPrefWidth()));
}
+ } else {
+ width = Math.max(w, mViewportWidth);
}
- nativeSetSize(width, Math.round((float) width * h / w), w, scale,
- w, h);
- } else {
- nativeSetSize(w, h, w, scale, w, h);
}
+ nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
+ textwrapWidth, scale, w, h, ignoreHeight);
// Remember the current width and height
boolean needInvalidate = (mCurrentViewWidth == 0);
mCurrentViewWidth = w;
mCurrentViewHeight = h;
+ mCurrentViewScale = scale;
if (needInvalidate) {
// ensure {@link #webkitDraw} is called as we were blocking in
// {@link #contentDraw} when mCurrentViewWidth is 0
- if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
contentDraw();
}
mEventHub.sendMessage(Message.obtain(null,
@@ -1267,9 +1504,24 @@ final class WebViewCore {
}
}
+ // Utility method for exceededDatabaseQuota and reachedMaxAppCacheSize
+ // callbacks. Computes the sum of database quota for all origins.
+ private long getUsedQuota() {
+ WebStorage webStorage = WebStorage.getInstance();
+ Set<String> origins = webStorage.getOrigins();
+ if (origins == null) {
+ return 0;
+ }
+ long usedQuota = 0;
+ for (String origin : origins) {
+ usedQuota += webStorage.getQuotaForOrigin(origin);
+ }
+ return usedQuota;
+ }
+
// Used to avoid posting more than one draw message.
private boolean mDrawIsScheduled;
-
+
// Used to avoid posting more than one split picture message.
private boolean mSplitPictureIsScheduled;
@@ -1278,31 +1530,59 @@ final class WebViewCore {
// Used to end scale+scroll mode, accessed by both threads
boolean mEndScaleZoom = false;
-
- public class DrawData {
- public DrawData() {
+
+ // mRestoreState is set in didFirstLayout(), and reset in the next
+ // webkitDraw after passing it to the UI thread.
+ private RestoreState mRestoreState = null;
+
+ static class RestoreState {
+ float mMinScale;
+ float mMaxScale;
+ float mViewScale;
+ float mTextWrapScale;
+ int mScrollX;
+ int mScrollY;
+ boolean mMobileSite;
+ }
+
+ static class DrawData {
+ DrawData() {
mInvalRegion = new Region();
mWidthHeight = new Point();
}
- public Region mInvalRegion;
- public Point mViewPoint;
- public Point mWidthHeight;
+ Region mInvalRegion;
+ Point mViewPoint;
+ Point mWidthHeight;
+ int mMinPrefWidth;
+ RestoreState mRestoreState; // only non-null if it is for the first
+ // picture set after the first layout
}
-
+
private void webkitDraw() {
mDrawIsScheduled = false;
DrawData draw = new DrawData();
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
- if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
+ if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
== false) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
return;
}
if (mWebView != null) {
// Send the native view size that was used during the most recent
// layout.
draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+ if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
+ draw.mMinPrefWidth = Math.max(
+ mViewportWidth == -1 ? DEFAULT_VIEWPORT_WIDTH
+ : (mViewportWidth == 0 ? mCurrentViewWidth
+ : mViewportWidth),
+ nativeGetContentMinPrefWidth());
+ }
+ if (mRestoreState != null) {
+ draw.mRestoreState = mRestoreState;
+ mRestoreState = null;
+ }
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
Message.obtain(mWebView.mPrivateHandler,
WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
@@ -1312,9 +1592,6 @@ final class WebViewCore {
mWebkitScrollY).sendToTarget();
mWebkitScrollX = mWebkitScrollY = 0;
}
- // nativeSnapToAnchor() needs to be called after NEW_PICTURE_MSG_ID
- // is sent, so that scroll will be based on the new content size.
- nativeSnapToAnchor();
}
}
@@ -1350,6 +1627,10 @@ final class WebViewCore {
}
}
+ /* package */ boolean pictureReady() {
+ return nativePictureReady();
+ }
+
/*package*/ Picture copyContentPicture() {
Picture result = new Picture();
nativeCopyContentToPicture(result);
@@ -1363,9 +1644,9 @@ final class WebViewCore {
sWebCoreHandler.sendMessageAtFrontOfQueue(sWebCoreHandler
.obtainMessage(WebCoreThread.REDUCE_PRIORITY));
// Note: there is one possible failure mode. If pauseUpdate() is called
- // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
- // of the queue and about to be executed. mDrawIsScheduled may be set to
- // false in webkitDraw(). So update won't be blocked. But at least the
+ // from UI thread while in webcore thread WEBKIT_DRAW is just pulled out
+ // of the queue and about to be executed. mDrawIsScheduled may be set to
+ // false in webkitDraw(). So update won't be blocked. But at least the
// webcore thread priority is still lowered.
if (core != null) {
synchronized (core) {
@@ -1385,7 +1666,7 @@ final class WebViewCore {
synchronized (core) {
core.mDrawIsScheduled = false;
core.mDrawIsPaused = false;
- if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
core.contentDraw();
}
}
@@ -1434,7 +1715,7 @@ final class WebViewCore {
mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
}
}
-
+
// called by JNI
private void contentScrollBy(int dx, int dy, boolean animate) {
if (!mBrowserFrame.firstLayoutDone()) {
@@ -1442,9 +1723,14 @@ final class WebViewCore {
return;
}
if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.SCROLL_BY_MSG_ID, dx, dy,
- new Boolean(animate)).sendToTarget();
+ Message msg = Message.obtain(mWebView.mPrivateHandler,
+ WebView.SCROLL_BY_MSG_ID, dx, dy, new Boolean(animate));
+ if (mDrawIsScheduled) {
+ mEventHub.sendMessage(Message.obtain(null,
+ EventHub.MESSAGE_RELAY, msg));
+ } else {
+ msg.sendToTarget();
+ }
}
}
@@ -1461,8 +1747,14 @@ final class WebViewCore {
return;
}
if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.SCROLL_TO_MSG_ID, x, y).sendToTarget();
+ Message msg = Message.obtain(mWebView.mPrivateHandler,
+ WebView.SCROLL_TO_MSG_ID, x, y);
+ if (mDrawIsScheduled) {
+ mEventHub.sendMessage(Message.obtain(null,
+ EventHub.MESSAGE_RELAY, msg));
+ } else {
+ msg.sendToTarget();
+ }
}
}
@@ -1479,24 +1771,14 @@ final class WebViewCore {
return;
}
if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.SPAWN_SCROLL_TO_MSG_ID, x, y).sendToTarget();
- }
- }
-
- // called by JNI
- private void sendMarkNodeInvalid(int node) {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.MARK_NODE_INVALID_ID, node, 0).sendToTarget();
- }
- }
-
- // called by JNI
- private void sendNotifyFocusSet() {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.NOTIFY_FOCUS_SET_MSG_ID).sendToTarget();
+ Message msg = Message.obtain(mWebView.mPrivateHandler,
+ WebView.SPAWN_SCROLL_TO_MSG_ID, x, y);
+ if (mDrawIsScheduled) {
+ mEventHub.sendMessage(Message.obtain(null,
+ EventHub.MESSAGE_RELAY, msg));
+ } else {
+ msg.sendToTarget();
+ }
}
}
@@ -1511,14 +1793,6 @@ final class WebViewCore {
contentDraw();
}
- // called by JNI
- private void sendRecomputeFocus() {
- if (mWebView != null) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.RECOMPUTE_FOCUS_MSG_ID).sendToTarget();
- }
- }
-
/* Called by JNI. The coordinates are in doc coordinates, so they need to
be scaled before they can be used by the view system, which happens
in WebView since it (and its thread) know the current scale factor.
@@ -1536,27 +1810,36 @@ final class WebViewCore {
}
private native void setViewportSettingsFromNative();
-
+
// called by JNI
- private void didFirstLayout() {
- // Trick to ensure that the Picture has the exact height for the content
- // by forcing to layout with 0 height after the page is ready, which is
- // indicated by didFirstLayout. This is essential to get rid of the
- // white space in the GMail which uses WebView for message view.
- if (mWebView != null && mWebView.mHeightCanMeasure) {
- mWebView.mLastHeightSent = 0;
- // Send a negative scale to indicate that WebCore should reuse the
- // current scale
- mEventHub.sendMessage(Message.obtain(null,
- EventHub.VIEW_SIZE_CHANGED, mWebView.mLastWidthSent,
- mWebView.mLastHeightSent, -1.0f));
+ private void didFirstLayout(boolean standardLoad) {
+ if (DebugFlags.WEB_VIEW_CORE) {
+ Log.v(LOGTAG, "didFirstLayout standardLoad =" + standardLoad);
}
mBrowserFrame.didFirstLayout();
- // reset the scroll position as it is a new page now
- mWebkitScrollX = mWebkitScrollY = 0;
+ if (mWebView == null) return;
+
+ setupViewport(standardLoad || mRestoredScale > 0);
+
+ // reset the scroll position, the restored offset and scales
+ mWebkitScrollX = mWebkitScrollY = mRestoredX = mRestoredY
+ = mRestoredScale = mRestoredScreenWidthScale = 0;
+ }
+
+ // called by JNI
+ private void updateViewport() {
+ // if updateViewport is called before first layout, wait until first
+ // layout to update the viewport. In the rare case, this is called after
+ // first layout, force an update as we have just parsed the viewport
+ // meta tag.
+ if (mBrowserFrame.firstLayoutDone()) {
+ setupViewport(true);
+ }
+ }
+ private void setupViewport(boolean updateRestoreState) {
// set the viewport settings from WebKit
setViewportSettingsFromNative();
@@ -1579,9 +1862,6 @@ final class WebViewCore {
if (mViewportInitialScale == 0) {
mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
}
- if (mViewportMinimumScale == 0) {
- mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT;
- }
}
if (mViewportUserScalable == false) {
mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT;
@@ -1607,38 +1887,83 @@ final class WebViewCore {
mViewportWidth = 0;
}
- // now notify webview
- if (mWebView != null) {
- HashMap scaleLimit = new HashMap();
- scaleLimit.put("minScale", mViewportMinimumScale);
- scaleLimit.put("maxScale", mViewportMaximumScale);
+ // if mViewportWidth is 0, it means device-width, always update.
+ if (mViewportWidth != 0 && !updateRestoreState) return;
- if (mRestoredScale > 0) {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
- scaleLimit).sendToTarget();
- mRestoredScale = 0;
+ // now notify webview
+ int webViewWidth = Math.round(mCurrentViewWidth * mCurrentViewScale);
+ mRestoreState = new RestoreState();
+ mRestoreState.mMinScale = mViewportMinimumScale / 100.0f;
+ mRestoreState.mMaxScale = mViewportMaximumScale / 100.0f;
+ mRestoreState.mScrollX = mRestoredX;
+ mRestoreState.mScrollY = mRestoredY;
+ mRestoreState.mMobileSite = (0 == mViewportWidth);
+ if (mRestoredScale > 0) {
+ if (mRestoredScreenWidthScale > 0) {
+ mRestoreState.mTextWrapScale =
+ mRestoredScreenWidthScale / 100.0f;
+ // 0 will trigger WebView to turn on zoom overview mode
+ mRestoreState.mViewScale = 0;
} else {
- Message.obtain(mWebView.mPrivateHandler,
- WebView.DID_FIRST_LAYOUT_MSG_ID, mViewportInitialScale,
- mViewportWidth, scaleLimit).sendToTarget();
+ mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
+ mRestoredScale / 100.0f;
}
+ } else {
+ if (mViewportInitialScale > 0) {
+ mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
+ mViewportInitialScale / 100.0f;
+ } else if (mViewportWidth > 0 && mViewportWidth < webViewWidth) {
+ mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
+ (float) webViewWidth / mViewportWidth;
+ } else {
+ mRestoreState.mTextWrapScale =
+ WebView.DEFAULT_SCALE_PERCENT / 100.0f;
+ // 0 will trigger WebView to turn on zoom overview mode
+ mRestoreState.mViewScale = 0;
+ }
+ }
- // if no restored offset, move the new page to (0, 0)
- Message.obtain(mWebView.mPrivateHandler, WebView.SCROLL_TO_MSG_ID,
- mRestoredX, mRestoredY).sendToTarget();
- mRestoredX = mRestoredY = 0;
-
- // force an early draw for quick feedback after the first layout
- if (mCurrentViewWidth != 0) {
- synchronized (this) {
- if (mDrawIsScheduled) {
- mEventHub.removeMessages(EventHub.WEBKIT_DRAW);
- }
- mDrawIsScheduled = true;
- mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
- EventHub.WEBKIT_DRAW));
- }
+ if (mWebView.mHeightCanMeasure) {
+ // Trick to ensure that the Picture has the exact height for the
+ // content by forcing to layout with 0 height after the page is
+ // ready, which is indicated by didFirstLayout. This is essential to
+ // get rid of the white space in the GMail which uses WebView for
+ // message view.
+ mWebView.mLastHeightSent = 0;
+ // Send a negative scale to indicate that WebCore should reuse
+ // the current scale
+ WebView.ViewSizeData data = new WebView.ViewSizeData();
+ data.mWidth = mWebView.mLastWidthSent;
+ data.mHeight = 0;
+ // if mHeightCanMeasure is true, getUseWideViewPort() can't be
+ // true. It is safe to use mWidth for mTextWrapWidth.
+ data.mTextWrapWidth = data.mWidth;
+ data.mScale = -1.0f;
+ data.mIgnoreHeight = false;
+ mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
+ EventHub.VIEW_SIZE_CHANGED, data));
+ } else if (mSettings.getUseWideViewPort()) {
+ if (mCurrentViewWidth == 0) {
+ // Trick to ensure VIEW_SIZE_CHANGED will be sent from WebView
+ // to WebViewCore
+ mWebView.mLastWidthSent = 0;
+ } else {
+ WebView.ViewSizeData data = new WebView.ViewSizeData();
+ // mViewScale as 0 means it is in zoom overview mode. So we don't
+ // know the exact scale. If mRestoredScale is non-zero, use it;
+ // otherwise just use mTextWrapScale as the initial scale.
+ data.mScale = mRestoreState.mViewScale == 0
+ ? (mRestoredScale > 0 ? mRestoredScale
+ : mRestoreState.mTextWrapScale)
+ : mRestoreState.mViewScale;
+ data.mWidth = Math.round(webViewWidth / data.mScale);
+ data.mHeight = mCurrentViewHeight * data.mWidth
+ / mCurrentViewWidth;
+ data.mTextWrapWidth = Math.round(webViewWidth
+ / mRestoreState.mTextWrapScale);
+ data.mIgnoreHeight = false;
+ mEventHub.sendMessageAtFrontOfQueue(Message.obtain(null,
+ EventHub.VIEW_SIZE_CHANGED, data));
}
}
}
@@ -1651,6 +1976,17 @@ final class WebViewCore {
}
// called by JNI
+ private void restoreScreenWidthScale(int scale) {
+ if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
+ return;
+ }
+
+ if (mBrowserFrame.firstLayoutDone() == false) {
+ mRestoredScreenWidthScale = scale;
+ }
+ }
+
+ // called by JNI
private void needTouchEvents(boolean need) {
if (mWebView != null) {
Message.obtain(mWebView.mPrivateHandler,
@@ -1664,15 +2000,39 @@ final class WebViewCore {
String text, int textGeneration) {
if (mWebView != null) {
Message msg = Message.obtain(mWebView.mPrivateHandler,
- WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
+ WebView.UPDATE_TEXTFIELD_TEXT_MSG_ID, ptr,
textGeneration, text);
msg.getData().putBoolean("password", changeToPassword);
msg.sendToTarget();
}
}
+ // called by JNI
+ private void updateTextSelection(int pointer, int start, int end,
+ int textGeneration) {
+ if (mWebView != null) {
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.UPDATE_TEXT_SELECTION_MSG_ID, pointer, textGeneration,
+ new TextSelectionData(start, end)).sendToTarget();
+ }
+ }
+
+ // called by JNI
+ private void clearTextEntry() {
+ if (mWebView == null) return;
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.CLEAR_TEXT_ENTRY).sendToTarget();
+ }
+
+ private native void nativeUpdateFrameCacheIfLoading();
+
+ /**
+ * Scroll the focused textfield to (x, y) in document space
+ */
+ private native void nativeScrollFocusedTextInput(int x, int y);
+
// these must be in document space (i.e. not scaled/zoomed).
- private native void nativeSetScrollOffset(int dx, int dy);
+ private native void nativeSetScrollOffset(int gen, int dx, int dy);
private native void nativeSetGlobalBounds(int x, int y, int w, int h);
@@ -1690,6 +2050,97 @@ final class WebViewCore {
if (mWebView != null) {
mWebView.requestListBox(array, enabledArray, selection);
}
-
+
}
+
+ // called by JNI
+ private void requestKeyboard(boolean showKeyboard) {
+ if (mWebView != null) {
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.REQUEST_KEYBOARD, showKeyboard ? 1 : 0, 0)
+ .sendToTarget();
+ }
+ }
+
+ // This class looks like a SurfaceView to native code. In java, we can
+ // assume the passed in SurfaceView is this class so we can talk to the
+ // ViewManager through the ChildView.
+ private class SurfaceViewProxy extends SurfaceView
+ implements SurfaceHolder.Callback {
+ private final ViewManager.ChildView mChildView;
+ private int mPointer;
+ private final boolean mIsFixedSize;
+ SurfaceViewProxy(Context context, ViewManager.ChildView childView,
+ int pointer, int pixelFormat, boolean isFixedSize) {
+ super(context);
+ setWillNotDraw(false); // this prevents the black box artifact
+ getHolder().addCallback(this);
+ getHolder().setFormat(pixelFormat);
+ mChildView = childView;
+ mChildView.mView = this;
+ mPointer = pointer;
+ mIsFixedSize = isFixedSize;
+ }
+ void destroy() {
+ mPointer = 0;
+ mChildView.removeView();
+ }
+ void attach(int x, int y, int width, int height) {
+ mChildView.attachView(x, y, width, height);
+
+ if (mIsFixedSize) {
+ getHolder().setFixedSize(width, height);
+ }
+ }
+
+ // SurfaceHolder.Callback methods
+ public void surfaceCreated(SurfaceHolder holder) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 0, 0, 0, 0);
+ }
+ }
+ public void surfaceChanged(SurfaceHolder holder, int format, int width,
+ int height) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 1, format, width, height);
+ }
+ }
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ if (mPointer != 0) {
+ nativeSurfaceChanged(mPointer, 2, 0, 0, 0);
+ }
+ }
+ }
+
+ // PluginWidget functions for mainting SurfaceViews for the Surface drawing
+ // model.
+ private SurfaceView createSurface(int nativePointer, int pixelFormat,
+ boolean isFixedSize) {
+ if (mWebView == null) {
+ return null;
+ }
+ return new SurfaceViewProxy(mContext, mWebView.mViewManager.createView(),
+ nativePointer, pixelFormat, isFixedSize);
+ }
+
+ private void destroySurface(SurfaceView surface) {
+ SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
+ proxy.destroy();
+ }
+
+ private void attachSurface(SurfaceView surface, int x, int y,
+ int width, int height) {
+ SurfaceViewProxy proxy = (SurfaceViewProxy) surface;
+ proxy.attach(x, y, width, height);
+ }
+
+ // Callback for the SurfaceHolder.Callback. Called for all the surface
+ // callbacks. The state parameter is one of Created(0), Changed(1),
+ // Destroyed(2).
+ private native void nativeSurfaceChanged(int pointer, int state, int format,
+ int width, int height);
+
+ private native void nativePause();
+ private native void nativeResume();
+ private native void nativeFreeMemory();
}
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 1004e30..4e76254 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -48,7 +48,9 @@ public class WebViewDatabase {
// 6 -> 7 Change cache localPath from int to String
// 7 -> 8 Move cache to its own db
// 8 -> 9 Store both scheme and host when storing passwords
- private static final int CACHE_DATABASE_VERSION = 1;
+ private static final int CACHE_DATABASE_VERSION = 3;
+ // 1 -> 2 Add expires String
+ // 2 -> 3 Add content-disposition
private static WebViewDatabase mInstance = null;
@@ -107,6 +109,8 @@ public class WebViewDatabase {
private static final String CACHE_EXPIRES_COL = "expires";
+ private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
+
private static final String CACHE_MIMETYPE_COL = "mimetype";
private static final String CACHE_ENCODING_COL = "encoding";
@@ -117,6 +121,8 @@ public class WebViewDatabase {
private static final String CACHE_CONTENTLENGTH_COL = "contentlength";
+ private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition";
+
// column id strings for "password" table
private static final String PASSWORD_HOST_COL = "host";
@@ -150,11 +156,13 @@ public class WebViewDatabase {
private static int mCacheLastModifyColIndex;
private static int mCacheETagColIndex;
private static int mCacheExpiresColIndex;
+ private static int mCacheExpiresStringColIndex;
private static int mCacheMimeTypeColIndex;
private static int mCacheEncodingColIndex;
private static int mCacheHttpStatusColIndex;
private static int mCacheLocationColIndex;
private static int mCacheContentLengthColIndex;
+ private static int mCacheContentDispositionColIndex;
private static int mCacheTransactionRefcount;
@@ -220,6 +228,8 @@ public class WebViewDatabase {
.getColumnIndex(CACHE_ETAG_COL);
mCacheExpiresColIndex = mCacheInserter
.getColumnIndex(CACHE_EXPIRES_COL);
+ mCacheExpiresStringColIndex = mCacheInserter
+ .getColumnIndex(CACHE_EXPIRES_STRING_COL);
mCacheMimeTypeColIndex = mCacheInserter
.getColumnIndex(CACHE_MIMETYPE_COL);
mCacheEncodingColIndex = mCacheInserter
@@ -230,6 +240,8 @@ public class WebViewDatabase {
.getColumnIndex(CACHE_LOCATION_COL);
mCacheContentLengthColIndex = mCacheInserter
.getColumnIndex(CACHE_CONTENTLENGTH_COL);
+ mCacheContentDispositionColIndex = mCacheInserter
+ .getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
}
}
@@ -320,11 +332,12 @@ public class WebViewDatabase {
+ " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
+ CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
+ " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
+ + CACHE_EXPIRES_STRING_COL + " TEXT, "
+ CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
+ " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
+ CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
- + " INTEGER, " + " UNIQUE (" + CACHE_URL_COL
- + ") ON CONFLICT REPLACE);");
+ + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, "
+ + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);");
mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache ("
+ CACHE_URL_COL + ")");
}
@@ -537,8 +550,8 @@ public class WebViewDatabase {
}
Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
- + "mimetype, encoding, httpstatus, location, contentlength "
- + "FROM cache WHERE url = ?",
+ + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
+ + "contentdisposition FROM cache WHERE url = ?",
new String[] { url });
try {
@@ -548,11 +561,13 @@ public class WebViewDatabase {
ret.lastModified = cursor.getString(1);
ret.etag = cursor.getString(2);
ret.expires = cursor.getLong(3);
- ret.mimeType = cursor.getString(4);
- ret.encoding = cursor.getString(5);
- ret.httpStatusCode = cursor.getInt(6);
- ret.location = cursor.getString(7);
- ret.contentLength = cursor.getLong(8);
+ ret.expiresString = cursor.getString(4);
+ ret.mimeType = cursor.getString(5);
+ ret.encoding = cursor.getString(6);
+ ret.httpStatusCode = cursor.getInt(7);
+ ret.location = cursor.getString(8);
+ ret.contentLength = cursor.getLong(9);
+ ret.contentdisposition = cursor.getString(10);
return ret;
}
} finally {
@@ -591,11 +606,14 @@ public class WebViewDatabase {
mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
mCacheInserter.bind(mCacheETagColIndex, c.etag);
mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
+ mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
mCacheInserter.bind(mCacheLocationColIndex, c.location);
mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
+ mCacheInserter.bind(mCacheContentDispositionColIndex,
+ c.contentdisposition);
mCacheInserter.execute();
}
diff --git a/core/java/android/webkit/gears/AndroidGpsLocationProvider.java b/core/java/android/webkit/gears/AndroidGpsLocationProvider.java
deleted file mode 100644
index 3646042..0000000
--- a/core/java/android/webkit/gears/AndroidGpsLocationProvider.java
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.Context;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.util.Log;
-import android.webkit.WebView;
-
-/**
- * GPS provider implementation for Android.
- */
-public final class AndroidGpsLocationProvider implements LocationListener {
- /**
- * Logging tag
- */
- private static final String TAG = "Gears-J-GpsProvider";
- /**
- * Our location manager instance.
- */
- private LocationManager locationManager;
- /**
- * The native object ID.
- */
- private long nativeObject;
-
- public AndroidGpsLocationProvider(WebView webview, long object) {
- nativeObject = object;
- locationManager = (LocationManager) webview.getContext().getSystemService(
- Context.LOCATION_SERVICE);
- if (locationManager == null) {
- Log.e(TAG,
- "AndroidGpsLocationProvider: could not get location manager.");
- throw new NullPointerException(
- "AndroidGpsLocationProvider: locationManager is null.");
- }
- // Register for location updates.
- try {
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
- this);
- } catch (IllegalArgumentException ex) {
- Log.e(TAG,
- "AndroidLocationGpsProvider: could not register for updates: " + ex);
- throw ex;
- } catch (SecurityException ex) {
- Log.e(TAG,
- "AndroidGpsLocationProvider: not allowed to register for update: "
- + ex);
- throw ex;
- }
- }
-
- /**
- * Called when the provider is no longer needed.
- */
- public void shutdown() {
- locationManager.removeUpdates(this);
- Log.i(TAG, "GPS provider closed.");
- }
-
- /**
- * Called when the location has changed.
- * @param location The new location, as a Location object.
- */
- public void onLocationChanged(Location location) {
- Log.i(TAG, "Location changed: " + location);
- nativeLocationChanged(location, nativeObject);
- }
-
- /**
- * Called when the provider status changes.
- *
- * @param provider the name of the location provider associated with this
- * update.
- * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
- * provider is out of service, and this is not expected to change in the
- * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
- * the provider is temporarily unavailable but is expected to be available
- * shortly; and {@link LocationProvider#AVAILABLE} if the
- * provider is currently available.
- * @param extras an optional Bundle which will contain provider specific
- * status variables (such as number of satellites).
- */
- public void onStatusChanged(String provider, int status, Bundle extras) {
- Log.i(TAG, "Provider " + provider + " status changed to " + status);
- if (status == LocationProvider.OUT_OF_SERVICE ||
- status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
- nativeProviderError(false, nativeObject);
- }
- }
-
- /**
- * Called when the provider is enabled.
- *
- * @param provider the name of the location provider that is now enabled.
- */
- public void onProviderEnabled(String provider) {
- Log.i(TAG, "Provider " + provider + " enabled.");
- // No need to notify the native side. It's enough to start sending
- // valid position fixes again.
- }
-
- /**
- * Called when the provider is disabled.
- *
- * @param provider the name of the location provider that is now disabled.
- */
- public void onProviderDisabled(String provider) {
- Log.i(TAG, "Provider " + provider + " disabled.");
- nativeProviderError(true, nativeObject);
- }
-
- /**
- * The native method called when a new location is available.
- * @param location is the new Location instance to pass to the native side.
- * @param nativeObject is a pointer to the corresponding
- * AndroidGpsLocationProvider C++ instance.
- */
- private native void nativeLocationChanged(Location location, long object);
-
- /**
- * The native method called when there is a GPS provder error.
- * @param isDisabled is true when the error signifies the fact that the GPS
- * HW is disabled. For other errors, this param is always false.
- * @param nativeObject is a pointer to the corresponding
- * AndroidGpsLocationProvider C++ instance.
- */
- private native void nativeProviderError(boolean isDisabled, long object);
-}
diff --git a/core/java/android/webkit/gears/AndroidRadioDataProvider.java b/core/java/android/webkit/gears/AndroidRadioDataProvider.java
deleted file mode 100644
index 1384042..0000000
--- a/core/java/android/webkit/gears/AndroidRadioDataProvider.java
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.Context;
-import android.telephony.CellLocation;
-import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-import android.telephony.gsm.GsmCellLocation;
-import android.telephony.PhoneStateListener;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-import android.webkit.WebView;
-
-/**
- * Radio data provider implementation for Android.
- */
-public final class AndroidRadioDataProvider extends PhoneStateListener {
-
- /** Logging tag */
- private static final String TAG = "Gears-J-RadioProvider";
-
- /** Network types */
- private static final int RADIO_TYPE_UNKNOWN = 0;
- private static final int RADIO_TYPE_GSM = 1;
- private static final int RADIO_TYPE_WCDMA = 2;
- private static final int RADIO_TYPE_CDMA = 3;
- private static final int RADIO_TYPE_EVDO = 4;
- private static final int RADIO_TYPE_1xRTT = 5;
-
- /** Simple container for radio data */
- public static final class RadioData {
- public int cellId = -1;
- public int locationAreaCode = -1;
- // TODO: use new SignalStrength instead of asu
- public int signalStrength = -1;
- public int mobileCountryCode = -1;
- public int mobileNetworkCode = -1;
- public int homeMobileCountryCode = -1;
- public int homeMobileNetworkCode = -1;
- public int radioType = RADIO_TYPE_UNKNOWN;
- public String carrierName;
-
- /**
- * Constructs radioData object from the given telephony data.
- * @param telephonyManager contains the TelephonyManager instance.
- * @param cellLocation contains information about the current GSM cell.
- * @param signalStrength is the strength of the network signal.
- * @param serviceState contains information about the network service.
- * @return a new RadioData object populated with the currently
- * available network information or null if there isn't
- * enough information.
- */
- public static RadioData getInstance(TelephonyManager telephonyManager,
- CellLocation cellLocation, int signalStrength,
- ServiceState serviceState) {
-
- if (!(cellLocation instanceof GsmCellLocation)) {
- // This also covers the case when cellLocation is null.
- // When that happens, we do not bother creating a
- // RadioData instance.
- return null;
- }
-
- RadioData radioData = new RadioData();
- GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
-
- // Extract the cell id, LAC, and signal strength.
- radioData.cellId = gsmCellLocation.getCid();
- radioData.locationAreaCode = gsmCellLocation.getLac();
- radioData.signalStrength = signalStrength;
-
- // Extract the home MCC and home MNC.
- String operator = telephonyManager.getSimOperator();
- radioData.setMobileCodes(operator, true);
-
- if (serviceState != null) {
- // Extract the carrier name.
- radioData.carrierName = serviceState.getOperatorAlphaLong();
-
- // Extract the MCC and MNC.
- operator = serviceState.getOperatorNumeric();
- radioData.setMobileCodes(operator, false);
- }
-
- // Finally get the radio type.
- //TODO We have to edit the parameter for getNetworkType regarding CDMA
- int type = telephonyManager.getNetworkType();
- if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
- radioData.radioType = RADIO_TYPE_WCDMA;
- } else if (type == TelephonyManager.NETWORK_TYPE_GPRS
- || type == TelephonyManager.NETWORK_TYPE_EDGE) {
- radioData.radioType = RADIO_TYPE_GSM;
- } else if (type == TelephonyManager.NETWORK_TYPE_CDMA) {
- radioData.radioType = RADIO_TYPE_CDMA;
- } else if (type == TelephonyManager.NETWORK_TYPE_EVDO_0) {
- radioData.radioType = RADIO_TYPE_EVDO;
- } else if (type == TelephonyManager.NETWORK_TYPE_EVDO_A) {
- radioData.radioType = RADIO_TYPE_EVDO;
- } else if (type == TelephonyManager.NETWORK_TYPE_1xRTT) {
- radioData.radioType = RADIO_TYPE_1xRTT;
- }
-
- // Print out what we got.
- Log.i(TAG, "Got the following data:");
- Log.i(TAG, "CellId: " + radioData.cellId);
- Log.i(TAG, "LAC: " + radioData.locationAreaCode);
- Log.i(TAG, "MNC: " + radioData.mobileNetworkCode);
- Log.i(TAG, "MCC: " + radioData.mobileCountryCode);
- Log.i(TAG, "home MNC: " + radioData.homeMobileNetworkCode);
- Log.i(TAG, "home MCC: " + radioData.homeMobileCountryCode);
- Log.i(TAG, "Signal strength: " + radioData.signalStrength);
- Log.i(TAG, "Carrier: " + radioData.carrierName);
- Log.i(TAG, "Network type: " + radioData.radioType);
-
- return radioData;
- }
-
- private RadioData() {}
-
- /**
- * Parses a string containing a mobile country code and a mobile
- * network code and sets the corresponding member variables.
- * @param codes is the string to parse.
- * @param homeValues flags whether the codes are for the home operator.
- */
- private void setMobileCodes(String codes, boolean homeValues) {
- if (codes != null) {
- try {
- // The operator numeric format is 3 digit country code plus 2 or
- // 3 digit network code.
- int mcc = Integer.parseInt(codes.substring(0, 3));
- int mnc = Integer.parseInt(codes.substring(3));
- if (homeValues) {
- homeMobileCountryCode = mcc;
- homeMobileNetworkCode = mnc;
- } else {
- mobileCountryCode = mcc;
- mobileNetworkCode = mnc;
- }
- } catch (IndexOutOfBoundsException ex) {
- Log.e(
- TAG,
- "AndroidRadioDataProvider: Invalid operator numeric data: " + ex);
- } catch (NumberFormatException ex) {
- Log.e(
- TAG,
- "AndroidRadioDataProvider: Operator numeric format error: " + ex);
- }
- }
- }
- };
-
- /** The native object ID */
- private long nativeObject;
-
- /** The last known cellLocation */
- private CellLocation cellLocation = null;
-
- /** The last known signal strength */
- // TODO: use new SignalStrength instead of asu
- private int signalStrength = -1;
-
- /** The last known serviceState */
- private ServiceState serviceState = null;
-
- /**
- * Our TelephonyManager instance.
- */
- private TelephonyManager telephonyManager;
-
- /**
- * Public constructor. Uses the webview to get the Context object.
- */
- public AndroidRadioDataProvider(WebView webview, long object) {
- super();
- nativeObject = object;
- telephonyManager = (TelephonyManager) webview.getContext().getSystemService(
- Context.TELEPHONY_SERVICE);
- if (telephonyManager == null) {
- Log.e(TAG,
- "AndroidRadioDataProvider: could not get tepephony manager.");
- throw new NullPointerException(
- "AndroidRadioDataProvider: telephonyManager is null.");
- }
-
- // Register for cell id, signal strength and service state changed
- // notifications.
- telephonyManager.listen(this, PhoneStateListener.LISTEN_CELL_LOCATION
- | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
- | PhoneStateListener.LISTEN_SERVICE_STATE);
- }
-
- /**
- * Should be called when the provider is no longer needed.
- */
- public void shutdown() {
- telephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
- Log.i(TAG, "AndroidRadioDataProvider shutdown.");
- }
-
- @Override
- public void onServiceStateChanged(ServiceState state) {
- serviceState = state;
- notifyListeners();
- }
-
- @Override
- public void onSignalStrengthsChanged(SignalStrength ss) {
- int gsmSignalStrength = ss.getGsmSignalStrength();
- signalStrength = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
- notifyListeners();
- }
-
- @Override
- public void onCellLocationChanged(CellLocation location) {
- cellLocation = location;
- notifyListeners();
- }
-
- private void notifyListeners() {
- RadioData radioData = RadioData.getInstance(telephonyManager, cellLocation,
- signalStrength, serviceState);
- if (radioData != null) {
- onUpdateAvailable(radioData, nativeObject);
- }
- }
-
- /**
- * The native method called when new radio data is available.
- * @param radioData is the RadioData instance to pass to the native side.
- * @param nativeObject is a pointer to the corresponding
- * AndroidRadioDataProvider C++ instance.
- */
- private static native void onUpdateAvailable(
- RadioData radioData, long nativeObject);
-}
diff --git a/core/java/android/webkit/gears/AndroidWifiDataProvider.java b/core/java/android/webkit/gears/AndroidWifiDataProvider.java
deleted file mode 100644
index d2850b0..0000000
--- a/core/java/android/webkit/gears/AndroidWifiDataProvider.java
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2008, Google Inc.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.webkit.WebView;
-import java.util.List;
-
-/**
- * WiFi data provider implementation for Android.
- * {@hide}
- */
-public final class AndroidWifiDataProvider extends BroadcastReceiver {
- /**
- * Logging tag
- */
- private static final String TAG = "Gears-J-WifiProvider";
- /**
- * Flag for guarding Log.v() calls.
- * Set to true to enable extra debug logging.
- */
- private static final boolean LOGV_ENABLED = false;
- /**
- * Our Wifi manager instance.
- */
- private WifiManager mWifiManager;
- /**
- * The native object ID.
- */
- private long mNativeObject;
- /**
- * The Context instance.
- */
- private Context mContext;
-
- /**
- * Constructs a instance of this class and registers for wifi scan
- * updates. Note that this constructor must be called on a Looper
- * thread. Suitable threads can be created on the native side using
- * the AndroidLooperThread C++ class.
- */
- public AndroidWifiDataProvider(WebView webview, long object) {
- mNativeObject = object;
- mContext = webview.getContext();
- mWifiManager =
- (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
- if (mWifiManager == null) {
- Log.e(TAG,
- "AndroidWifiDataProvider: could not get location manager.");
- throw new NullPointerException(
- "AndroidWifiDataProvider: locationManager is null.");
- }
-
- // Create a Handler that identifies the message loop associated
- // with the current thread. Note that it is not necessary to
- // override handleMessage() at all since the Intent
- // ReceiverDispatcher (see the ActivityThread class) only uses
- // this handler to post a Runnable to this thread's loop.
- Handler handler = new Handler(Looper.myLooper());
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
- mContext.registerReceiver(this, filter, null, handler);
-
- // Get the last scan results and pass them to the native side.
- // We can't just invoke the callback here, so we queue a message
- // to this thread's loop.
- handler.post(new Runnable() {
- public void run() {
- onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
- }
- });
- }
-
- /**
- * Called when the provider is no longer needed.
- */
- public void shutdown() {
- mContext.unregisterReceiver(this);
- if (LOGV_ENABLED) {
- Log.v(TAG, "Wifi provider closed.");
- }
- }
-
- /**
- * This method is called when the AndroidWifiDataProvider is receiving an
- * Intent broadcast.
- * @param context The Context in which the receiver is running.
- * @param intent The Intent being received.
- */
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(
- mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
- if (LOGV_ENABLED) {
- Log.v(TAG, "Wifi scan resulst available");
- }
- onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
- }
- }
-
- /**
- * The native method called when new wifi data is available.
- * @param scanResults is a list of ScanResults to pass to the native side.
- * @param nativeObject is a pointer to the corresponding
- * AndroidWifiDataProvider C++ instance.
- */
- private static native void onUpdateAvailable(
- List<ScanResult> scanResults, long nativeObject);
-}
diff --git a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java b/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
deleted file mode 100644
index 74d27ed..0000000
--- a/core/java/android/webkit/gears/ApacheHttpRequestAndroid.java
+++ /dev/null
@@ -1,1134 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.net.http.Headers;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.webkit.CacheManager;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.CookieManager;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.IOException;
-import java.lang.StringBuilder;
-import java.util.Date;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.Iterator;
-
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.client.params.HttpClientParams;
-import org.apache.http.params.HttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpProtocolParams;
-import org.apache.http.HttpResponse;
-import org.apache.http.entity.AbstractHttpEntity;
-import org.apache.http.client.*;
-import org.apache.http.client.methods.*;
-import org.apache.http.impl.client.AbstractHttpClient;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
-import org.apache.http.conn.ssl.StrictHostnameVerifier;
-import org.apache.http.impl.cookie.DateUtils;
-import org.apache.http.util.CharArrayBuffer;
-
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Performs the underlying HTTP/HTTPS GET, POST, HEAD, PUT, DELETE requests.
- * <p> These are performed synchronously (blocking). The caller should
- * ensure that it is in a background thread if asynchronous behavior
- * is required. All data is pushed, so there is no need for JNI native
- * callbacks.
- * <p> This uses Apache's HttpClient framework to perform most
- * of the underlying network activity. The Android brower's cache,
- * android.webkit.CacheManager, is also used when caching is enabled,
- * and updated with new data. The android.webkit.CookieManager is also
- * queried and updated as necessary.
- * <p> The public interface is designed to be called by native code
- * through JNI, and to simplify coding none of the public methods will
- * surface a checked exception. Unchecked exceptions may still be
- * raised but only if the system is in an ill state, such as out of
- * memory.
- * <p> TODO: This isn't plumbed into LocalServer yet. Mutually
- * dependent on LocalServer - will attach the two together once both
- * are submitted.
- */
-public final class ApacheHttpRequestAndroid {
- /** Debug logging tag. */
- private static final String LOG_TAG = "Gears-J";
- /** Flag for guarding Log.v() calls. */
- private static final boolean LOGV_ENABLED = false;
- /** HTTP response header line endings are CR-LF style. */
- private static final String HTTP_LINE_ENDING = "\r\n";
- /** Safe MIME type to use whenever it isn't specified. */
- private static final String DEFAULT_MIME_TYPE = "text/plain";
- /** Case-sensitive header keys */
- public static final String KEY_CONTENT_LENGTH = "Content-Length";
- public static final String KEY_EXPIRES = "Expires";
- public static final String KEY_LAST_MODIFIED = "Last-Modified";
- public static final String KEY_ETAG = "ETag";
- public static final String KEY_LOCATION = "Location";
- public static final String KEY_CONTENT_TYPE = "Content-Type";
- /** Number of bytes to send and receive on the HTTP connection in
- * one go. */
- private static final int BUFFER_SIZE = 4096;
-
- /** The first element of the String[] value in a headers map is the
- * unmodified (case-sensitive) key. */
- public static final int HEADERS_MAP_INDEX_KEY = 0;
- /** The second element of the String[] value in a headers map is the
- * associated value. */
- public static final int HEADERS_MAP_INDEX_VALUE = 1;
-
- /** Request headers, as key -> value map. */
- // TODO: replace this design by a simpler one (the C++ side has to
- // be modified too), where we do not store both the original header
- // and the lowercase one.
- private Map<String, String[]> mRequestHeaders =
- new HashMap<String, String[]>();
- /** Response headers, as a lowercase key -> value map. */
- private Map<String, String[]> mResponseHeaders =
- new HashMap<String, String[]>();
- /** The URL used for createCacheResult() */
- private String mCacheResultUrl;
- /** CacheResult being saved into, if inserting a new cache entry. */
- private CacheResult mCacheResult;
- /** Initialized by initChildThread(). Used to target abort(). */
- private Thread mBridgeThread;
-
- /** Our HttpClient */
- private AbstractHttpClient mClient;
- /** The HttpMethod associated with this request */
- private HttpRequestBase mMethod;
- /** The complete response line e.g "HTTP/1.0 200 OK" */
- private String mResponseLine;
- /** HTTP body stream, setup after connection. */
- private InputStream mBodyInputStream;
-
- /** HTTP Response Entity */
- private HttpResponse mResponse;
-
- /** Post Entity, used to stream the request to the server */
- private StreamEntity mPostEntity = null;
- /** Content lenght, mandatory when using POST */
- private long mContentLength;
-
- /** The request executes in a parallel thread */
- private Thread mHttpThread = null;
- /** protect mHttpThread, if interrupt() is called concurrently */
- private Lock mHttpThreadLock = new ReentrantLock();
- /** Flag set to true when the request thread is joined */
- private boolean mConnectionFinished = false;
- /** Flag set to true by interrupt() and/or connection errors */
- private boolean mConnectionFailed = false;
- /** Lock protecting the access to mConnectionFailed */
- private Lock mConnectionFailedLock = new ReentrantLock();
-
- /** Lock on the loop in StreamEntity */
- private Lock mStreamingReadyLock = new ReentrantLock();
- /** Condition variable used to signal the loop is ready... */
- private Condition mStreamingReady = mStreamingReadyLock.newCondition();
-
- /** Used to pass around the block of data POSTed */
- private Buffer mBuffer = new Buffer();
- /** Used to signal that the block of data has been written */
- private SignalConsumed mSignal = new SignalConsumed();
-
- // inner classes
-
- /**
- * Implements the http request
- */
- class Connection implements Runnable {
- public void run() {
- boolean problem = false;
- try {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "REQUEST : " + mMethod.getRequestLine());
- }
- mResponse = mClient.execute(mMethod);
- if (mResponse != null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "response (status line): "
- + mResponse.getStatusLine());
- }
- mResponseLine = "" + mResponse.getStatusLine();
- } else {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "problem, response == null");
- }
- problem = true;
- }
- } catch (IOException e) {
- Log.e(LOG_TAG, "Connection IO exception ", e);
- problem = true;
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Connection runtime exception ", e);
- problem = true;
- }
-
- if (!problem) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Request complete ("
- + mMethod.getRequestLine() + ")");
- }
- } else {
- mConnectionFailedLock.lock();
- mConnectionFailed = true;
- mConnectionFailedLock.unlock();
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Request FAILED ("
- + mMethod.getRequestLine() + ")");
- }
- // We abort the execution in order to shutdown and release
- // the underlying connection
- mMethod.abort();
- if (mPostEntity != null) {
- // If there is a post entity, we need to wake it up from
- // a potential deadlock
- mPostEntity.signalOutputStream();
- }
- }
- }
- }
-
- /**
- * simple buffer class implementing a producer/consumer model
- */
- class Buffer {
- private DataPacket mPacket;
- private boolean mEmpty = true;
- public synchronized void put(DataPacket packet) {
- while (!mEmpty) {
- try {
- wait();
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "InterruptedException while putting " +
- "a DataPacket in the Buffer: " + e);
- }
- }
- }
- mPacket = packet;
- mEmpty = false;
- notify();
- }
- public synchronized DataPacket get() {
- while (mEmpty) {
- try {
- wait();
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "InterruptedException while getting " +
- "a DataPacket in the Buffer: " + e);
- }
- }
- }
- mEmpty = true;
- notify();
- return mPacket;
- }
- }
-
- /**
- * utility class used to block until the packet is signaled as being
- * consumed
- */
- class SignalConsumed {
- private boolean mConsumed = false;
- public synchronized void waitUntilPacketConsumed() {
- while (!mConsumed) {
- try {
- wait();
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "InterruptedException while waiting " +
- "until a DataPacket is consumed: " + e);
- }
- }
- }
- mConsumed = false;
- notify();
- }
- public synchronized void packetConsumed() {
- while (mConsumed) {
- try {
- wait();
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "InterruptedException while indicating "
- + "that the DataPacket has been consumed: " + e);
- }
- }
- }
- mConsumed = true;
- notify();
- }
- }
-
- /**
- * Utility class encapsulating a packet of data
- */
- class DataPacket {
- private byte[] mContent;
- private int mLength;
- public DataPacket(byte[] content, int length) {
- mContent = content;
- mLength = length;
- }
- public byte[] getBytes() {
- return mContent;
- }
- public int getLength() {
- return mLength;
- }
- }
-
- /**
- * HttpEntity class to write the bytes received by the C++ thread
- * on the connection outputstream, in a streaming way.
- * This entity is executed in the request thread.
- * The writeTo() method is automatically called by the
- * HttpPost execution; upon reception, we loop while receiving
- * the data packets from the main thread, until completion
- * or error. When done, we flush the outputstream.
- * The main thread (sendPostData()) also blocks until the
- * outputstream is made available (or an error happens)
- */
- class StreamEntity implements HttpEntity {
- private OutputStream mOutputStream;
-
- // HttpEntity interface methods
-
- public boolean isRepeatable() {
- return false;
- }
-
- public boolean isChunked() {
- return false;
- }
-
- public long getContentLength() {
- return mContentLength;
- }
-
- public Header getContentType() {
- return null;
- }
-
- public Header getContentEncoding() {
- return null;
- }
-
- public InputStream getContent() throws IOException {
- return null;
- }
-
- public void writeTo(final OutputStream out) throws IOException {
- // We signal that the outputstream is available
- mStreamingReadyLock.lock();
- mOutputStream = out;
- mStreamingReady.signal();
- mStreamingReadyLock.unlock();
-
- // We then loop waiting on messages to process.
- boolean finished = false;
- while (!finished) {
- DataPacket packet = mBuffer.get();
- if (packet == null) {
- finished = true;
- } else {
- write(packet);
- }
- mSignal.packetConsumed();
- mConnectionFailedLock.lock();
- if (mConnectionFailed) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "stopping loop on error");
- }
- finished = true;
- }
- mConnectionFailedLock.unlock();
- }
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "flushing the outputstream...");
- }
- mOutputStream.flush();
- }
-
- public boolean isStreaming() {
- return true;
- }
-
- public void consumeContent() throws IOException {
- // Nothing to release
- }
-
- // local methods
-
- private void write(DataPacket packet) {
- try {
- if (mOutputStream == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "NO OUTPUT STREAM !!!");
- }
- return;
- }
- mOutputStream.write(packet.getBytes(), 0, packet.getLength());
- mOutputStream.flush();
- } catch (IOException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "exc: " + e);
- }
- mConnectionFailedLock.lock();
- mConnectionFailed = true;
- mConnectionFailedLock.unlock();
- }
- }
-
- public boolean isReady() {
- mStreamingReadyLock.lock();
- try {
- if (mOutputStream == null) {
- mStreamingReady.await();
- }
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "InterruptedException in "
- + "StreamEntity::isReady() : ", e);
- }
- } finally {
- mStreamingReadyLock.unlock();
- }
- if (mOutputStream == null) {
- return false;
- }
- return true;
- }
-
- public void signalOutputStream() {
- mStreamingReadyLock.lock();
- mStreamingReady.signal();
- mStreamingReadyLock.unlock();
- }
- }
-
- /**
- * Initialize mBridgeThread using the TLS value of
- * Thread.currentThread(). Called on start up of the native child
- * thread.
- */
- public synchronized void initChildThread() {
- mBridgeThread = Thread.currentThread();
- }
-
- public void setContentLength(long length) {
- mContentLength = length;
- }
-
- /**
- * Analagous to the native-side HttpRequest::open() function. This
- * initializes an underlying HttpClient method, but does
- * not go to the wire. On success, this enables a call to send() to
- * initiate the transaction.
- *
- * @param method The HTTP method, e.g GET or POST.
- * @param url The URL to open.
- * @return True on success with a complete HTTP response.
- * False on failure.
- */
- public synchronized boolean open(String method, String url) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "open " + method + " " + url);
- }
- // Create the client
- if (mConnectionFailed) {
- // interrupt() could have been called even before open()
- return false;
- }
- mClient = new DefaultHttpClient();
- mClient.setHttpRequestRetryHandler(
- new DefaultHttpRequestRetryHandler(0, false));
- mBodyInputStream = null;
- mResponseLine = null;
- mResponseHeaders = null;
- mPostEntity = null;
- mHttpThread = null;
- mConnectionFailed = false;
- mConnectionFinished = false;
-
- // Create the method. We support everything that
- // Apache HttpClient supports, apart from TRACE.
- if ("GET".equalsIgnoreCase(method)) {
- mMethod = new HttpGet(url);
- } else if ("POST".equalsIgnoreCase(method)) {
- mMethod = new HttpPost(url);
- mPostEntity = new StreamEntity();
- ((HttpPost)mMethod).setEntity(mPostEntity);
- } else if ("HEAD".equalsIgnoreCase(method)) {
- mMethod = new HttpHead(url);
- } else if ("PUT".equalsIgnoreCase(method)) {
- mMethod = new HttpPut(url);
- } else if ("DELETE".equalsIgnoreCase(method)) {
- mMethod = new HttpDelete(url);
- } else {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Method " + method + " not supported");
- }
- return false;
- }
- HttpParams params = mClient.getParams();
- // We handle the redirections C++-side
- HttpClientParams.setRedirecting(params, false);
- HttpProtocolParams.setUseExpectContinue(params, false);
- return true;
- }
-
- /**
- * We use this to start the connection thread (doing the method execute).
- * We usually always return true here, as the connection will run its
- * course in the thread.
- * We only return false if interrupted beforehand -- if a connection
- * problem happens, we will thus fail in either sendPostData() or
- * parseHeaders().
- */
- public synchronized boolean connectToRemote() {
- boolean ret = false;
- applyRequestHeaders();
- mConnectionFailedLock.lock();
- if (!mConnectionFailed) {
- mHttpThread = new Thread(new Connection());
- mHttpThread.start();
- }
- ret = mConnectionFailed;
- mConnectionFailedLock.unlock();
- return !ret;
- }
-
- /**
- * Get the complete response line of the HTTP request. Only valid on
- * completion of the transaction.
- * @return The complete HTTP response line, e.g "HTTP/1.0 200 OK".
- */
- public synchronized String getResponseLine() {
- return mResponseLine;
- }
-
- /**
- * Wait for the request thread completion
- * (unless already finished)
- */
- private void waitUntilConnectionFinished() {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "waitUntilConnectionFinished("
- + mConnectionFinished + ")");
- }
- if (!mConnectionFinished) {
- if (mHttpThread != null) {
- try {
- mHttpThread.join();
- mConnectionFinished = true;
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "http thread joined");
- }
- } catch (InterruptedException e) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "interrupted: " + e);
- }
- }
- } else {
- Log.e(LOG_TAG, ">>> Trying to join on mHttpThread " +
- "when it does not exist!");
- }
- }
- }
-
- // Headers handling
-
- /**
- * Receive all headers from the server and populate
- * mResponseHeaders.
- * @return True if headers are successfully received, False on
- * connection error.
- */
- public synchronized boolean parseHeaders() {
- mConnectionFailedLock.lock();
- if (mConnectionFailed) {
- mConnectionFailedLock.unlock();
- return false;
- }
- mConnectionFailedLock.unlock();
- waitUntilConnectionFinished();
- mResponseHeaders = new HashMap<String, String[]>();
- if (mResponse == null)
- return false;
-
- Header[] headers = mResponse.getAllHeaders();
- for (int i = 0; i < headers.length; i++) {
- Header header = headers[i];
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "header " + header.getName()
- + " -> " + header.getValue());
- }
- setResponseHeader(header.getName(), header.getValue());
- }
-
- return true;
- }
-
- /**
- * Set a header to send with the HTTP request. Will not take effect
- * on a transaction already in progress. The key is associated
- * case-insensitive, but stored case-sensitive.
- * @param name The name of the header, e.g "Set-Cookie".
- * @param value The value for this header, e.g "text/html".
- */
- public synchronized void setRequestHeader(String name, String value) {
- String[] mapValue = { name, value };
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "setRequestHeader: " + name + " => " + value);
- }
- if (name.equalsIgnoreCase(KEY_CONTENT_LENGTH)) {
- setContentLength(Long.parseLong(value));
- } else {
- mRequestHeaders.put(name.toLowerCase(), mapValue);
- }
- }
-
- /**
- * Returns the value associated with the given request header.
- * @param name The name of the request header, non-null, case-insensitive.
- * @return The value associated with the request header, or null if
- * not set, or error.
- */
- public synchronized String getRequestHeader(String name) {
- String[] value = mRequestHeaders.get(name.toLowerCase());
- if (value != null) {
- return value[HEADERS_MAP_INDEX_VALUE];
- } else {
- return null;
- }
- }
-
- private void applyRequestHeaders() {
- if (mMethod == null)
- return;
- Iterator<String[]> it = mRequestHeaders.values().iterator();
- while (it.hasNext()) {
- // Set the key case-sensitive.
- String[] entry = it.next();
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "apply header " + entry[HEADERS_MAP_INDEX_KEY] +
- " => " + entry[HEADERS_MAP_INDEX_VALUE]);
- }
- mMethod.setHeader(entry[HEADERS_MAP_INDEX_KEY],
- entry[HEADERS_MAP_INDEX_VALUE]);
- }
- }
-
- /**
- * Returns the value associated with the given response header.
- * @param name The name of the response header, non-null, case-insensitive.
- * @return The value associated with the response header, or null if
- * not set or error.
- */
- public synchronized String getResponseHeader(String name) {
- if (mResponseHeaders != null) {
- String[] value = mResponseHeaders.get(name.toLowerCase());
- if (value != null) {
- return value[HEADERS_MAP_INDEX_VALUE];
- } else {
- return null;
- }
- } else {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "getResponseHeader() called but "
- + "response not received");
- }
- return null;
- }
- }
-
- /**
- * Return all response headers, separated by CR-LF line endings, and
- * ending with a trailing blank line. This mimics the format of the
- * raw response header up to but not including the body.
- * @return A string containing the entire response header.
- */
- public synchronized String getAllResponseHeaders() {
- if (mResponseHeaders == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "getAllResponseHeaders() called but "
- + "response not received");
- }
- return null;
- }
- StringBuilder result = new StringBuilder();
- Iterator<String[]> it = mResponseHeaders.values().iterator();
- while (it.hasNext()) {
- String[] entry = it.next();
- // Output the "key: value" lines.
- result.append(entry[HEADERS_MAP_INDEX_KEY]);
- result.append(": ");
- result.append(entry[HEADERS_MAP_INDEX_VALUE]);
- result.append(HTTP_LINE_ENDING);
- }
- result.append(HTTP_LINE_ENDING);
- return result.toString();
- }
-
-
- /**
- * Set a response header and associated value. The key is associated
- * case-insensitively, but stored case-sensitively.
- * @param name Case sensitive request header key.
- * @param value The associated value.
- */
- private void setResponseHeader(String name, String value) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Set response header " + name + ": " + value);
- }
- String mapValue[] = { name, value };
- mResponseHeaders.put(name.toLowerCase(), mapValue);
- }
-
- // Cookie handling
-
- /**
- * Get the cookie for the given URL.
- * @param url The fully qualified URL.
- * @return A string containing the cookie for the URL if it exists,
- * or null if not.
- */
- public static String getCookieForUrl(String url) {
- // Get the cookie for this URL, set as a header
- return CookieManager.getInstance().getCookie(url);
- }
-
- /**
- * Set the cookie for the given URL.
- * @param url The fully qualified URL.
- * @param cookie The new cookie value.
- * @return A string containing the cookie for the URL if it exists,
- * or null if not.
- */
- public static void setCookieForUrl(String url, String cookie) {
- // Get the cookie for this URL, set as a header
- CookieManager.getInstance().setCookie(url, cookie);
- }
-
- // Cache handling
-
- /**
- * Perform a request using LocalServer if possible. Initializes
- * class members so that receive() will obtain data from the stream
- * provided by the response.
- * @param url The fully qualified URL to try in LocalServer.
- * @return True if the url was found and is now setup to receive.
- * False if not found, with no side-effect.
- */
- public synchronized boolean useLocalServerResult(String url) {
- UrlInterceptHandlerGears handler =
- UrlInterceptHandlerGears.getInstance();
- if (handler == null) {
- return false;
- }
- UrlInterceptHandlerGears.ServiceResponse serviceResponse =
- handler.getServiceResponse(url, mRequestHeaders);
- if (serviceResponse == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "No response in LocalServer");
- }
- return false;
- }
- // LocalServer will handle this URL. Initialize stream and
- // response.
- mBodyInputStream = serviceResponse.getInputStream();
- mResponseLine = serviceResponse.getStatusLine();
- mResponseHeaders = serviceResponse.getResponseHeaders();
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Got response from LocalServer: " + mResponseLine);
- }
- return true;
- }
-
- /**
- * Perform a request using the cache result if present. Initializes
- * class members so that receive() will obtain data from the cache.
- * @param url The fully qualified URL to try in the cache.
- * @return True is the url was found and is now setup to receive
- * from cache. False if not found, with no side-effect.
- */
- public synchronized boolean useCacheResult(String url) {
- // Try the browser's cache. CacheManager wants a Map<String, String>.
- Map<String, String> cacheRequestHeaders = new HashMap<String, String>();
- Iterator<Map.Entry<String, String[]>> it =
- mRequestHeaders.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, String[]> entry = it.next();
- cacheRequestHeaders.put(
- entry.getKey(),
- entry.getValue()[HEADERS_MAP_INDEX_VALUE]);
- }
- CacheResult mCacheResult =
- CacheManager.getCacheFile(url, cacheRequestHeaders);
- if (mCacheResult == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "No CacheResult for " + url);
- }
- return false;
- }
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Got CacheResult from browser cache");
- }
- // Check for expiry. -1 is "never", otherwise milliseconds since 1970.
- // Can be compared to System.currentTimeMillis().
- long expires = mCacheResult.getExpires();
- if (expires >= 0 && System.currentTimeMillis() >= expires) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "CacheResult expired "
- + (System.currentTimeMillis() - expires)
- + " milliseconds ago");
- }
- // Cache hit has expired. Do not return it.
- return false;
- }
- // Setup the mBodyInputStream to come from the cache.
- mBodyInputStream = mCacheResult.getInputStream();
- if (mBodyInputStream == null) {
- // Cache result may have gone away.
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "No mBodyInputStream for CacheResult " + url);
- }
- return false;
- }
- // Cache hit. Parse headers.
- synthesizeHeadersFromCacheResult(mCacheResult);
- return true;
- }
-
- /**
- * Take the limited set of headers in a CacheResult and synthesize
- * response headers.
- * @param cacheResult A CacheResult to populate mResponseHeaders with.
- */
- private void synthesizeHeadersFromCacheResult(CacheResult cacheResult) {
- int statusCode = cacheResult.getHttpStatusCode();
- // The status message is informal, so we can greatly simplify it.
- String statusMessage;
- if (statusCode >= 200 && statusCode < 300) {
- statusMessage = "OK";
- } else if (statusCode >= 300 && statusCode < 400) {
- statusMessage = "MOVED";
- } else {
- statusMessage = "UNAVAILABLE";
- }
- // Synthesize the response line.
- mResponseLine = "HTTP/1.1 " + statusCode + " " + statusMessage;
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Synthesized " + mResponseLine);
- }
- // Synthesize the returned headers from cache.
- mResponseHeaders = new HashMap<String, String[]>();
- String contentLength = Long.toString(cacheResult.getContentLength());
- setResponseHeader(KEY_CONTENT_LENGTH, contentLength);
- long expires = cacheResult.getExpires();
- if (expires >= 0) {
- // "Expires" header is valid and finite. Milliseconds since 1970
- // epoch, formatted as RFC-1123.
- String expiresString = DateUtils.formatDate(new Date(expires));
- setResponseHeader(KEY_EXPIRES, expiresString);
- }
- String lastModified = cacheResult.getLastModified();
- if (lastModified != null) {
- // Last modification time of the page. Passed end-to-end, but
- // not used by us.
- setResponseHeader(KEY_LAST_MODIFIED, lastModified);
- }
- String eTag = cacheResult.getETag();
- if (eTag != null) {
- // Entity tag. A kind of GUID to identify identical resources.
- setResponseHeader(KEY_ETAG, eTag);
- }
- String location = cacheResult.getLocation();
- if (location != null) {
- // If valid, refers to the location of a redirect.
- setResponseHeader(KEY_LOCATION, location);
- }
- String mimeType = cacheResult.getMimeType();
- if (mimeType == null) {
- // Use a safe default MIME type when none is
- // specified. "text/plain" is safe to render in the browser
- // window (even if large) and won't be intepreted as anything
- // that would cause execution.
- mimeType = DEFAULT_MIME_TYPE;
- }
- String encoding = cacheResult.getEncoding();
- // Encoding may not be specified. No default.
- String contentType = mimeType;
- if (encoding != null) {
- if (encoding.length() > 0) {
- contentType += "; charset=" + encoding;
- }
- }
- setResponseHeader(KEY_CONTENT_TYPE, contentType);
- }
-
- /**
- * Create a CacheResult for this URL. This enables the repsonse body
- * to be sent in calls to appendCacheResult().
- * @param url The fully qualified URL to add to the cache.
- * @param responseCode The response code returned for the request, e.g 200.
- * @param mimeType The MIME type of the body, e.g "text/plain".
- * @param encoding The encoding, e.g "utf-8". Use "" for unknown.
- */
- public synchronized boolean createCacheResult(
- String url, int responseCode, String mimeType, String encoding) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Making cache entry for " + url);
- }
- // Take the headers and parse them into a format needed by
- // CacheManager.
- Headers cacheHeaders = new Headers();
- Iterator<Map.Entry<String, String[]>> it =
- mResponseHeaders.entrySet().iterator();
- while (it.hasNext()) {
- Map.Entry<String, String[]> entry = it.next();
- // Headers.parseHeader() expects lowercase keys.
- String keyValue = entry.getKey() + ": "
- + entry.getValue()[HEADERS_MAP_INDEX_VALUE];
- CharArrayBuffer buffer = new CharArrayBuffer(keyValue.length());
- buffer.append(keyValue);
- // Parse it into the header container.
- cacheHeaders.parseHeader(buffer);
- }
- mCacheResult = CacheManager.createCacheFile(
- url, responseCode, cacheHeaders, mimeType, true);
- if (mCacheResult != null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Saving into cache");
- }
- mCacheResult.setEncoding(encoding);
- mCacheResultUrl = url;
- return true;
- } else {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Couldn't create mCacheResult");
- }
- return false;
- }
- }
-
- /**
- * Add data from the response body to the CacheResult created with
- * createCacheResult().
- * @param data A byte array of the next sequential bytes in the
- * response body.
- * @param bytes The number of bytes to write from the start of
- * the array.
- * @return True if all bytes successfully written, false on failure.
- */
- public synchronized boolean appendCacheResult(byte[] data, int bytes) {
- if (mCacheResult == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "appendCacheResult() called without a "
- + "CacheResult initialized");
- }
- return false;
- }
- try {
- mCacheResult.getOutputStream().write(data, 0, bytes);
- } catch (IOException ex) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Got IOException writing cache data: " + ex);
- }
- return false;
- }
- return true;
- }
-
- /**
- * Save the completed CacheResult into the CacheManager. This must
- * have been created first with createCacheResult().
- * @return Returns true if the entry has been successfully saved.
- */
- public synchronized boolean saveCacheResult() {
- if (mCacheResult == null || mCacheResultUrl == null) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Tried to save cache result but "
- + "createCacheResult not called");
- }
- return false;
- }
-
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Saving cache result");
- }
- CacheManager.saveCacheFile(mCacheResultUrl, mCacheResult);
- mCacheResult = null;
- mCacheResultUrl = null;
- return true;
- }
-
- /**
- * Called by the main thread to interrupt the child thread.
- * We do not set mConnectionFailed here as we still need the
- * ability to receive a null packet for sendPostData().
- */
- public synchronized void abort() {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "ABORT CALLED");
- }
- if (mMethod != null) {
- mMethod.abort();
- }
- }
-
- /**
- * Interrupt a blocking IO operation and wait for the
- * thread to complete.
- */
- public synchronized void interrupt() {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "INTERRUPT CALLED");
- }
- mConnectionFailedLock.lock();
- mConnectionFailed = true;
- mConnectionFailedLock.unlock();
- if (mMethod != null) {
- mMethod.abort();
- }
- if (mHttpThread != null) {
- waitUntilConnectionFinished();
- }
- }
-
- /**
- * Receive the next sequential bytes of the response body after
- * successful connection. This will receive up to the size of the
- * provided byte array. If there is no body, this will return 0
- * bytes on the first call after connection.
- * @param buf A pre-allocated byte array to receive data into.
- * @return The number of bytes from the start of the array which
- * have been filled, 0 on EOF, or negative on error.
- */
- public synchronized int receive(byte[] buf) {
- if (mBodyInputStream == null) {
- // If this is the first call, setup the InputStream. This may
- // fail if there were headers, but no body returned by the
- // server.
- try {
- if (mResponse != null) {
- HttpEntity entity = mResponse.getEntity();
- mBodyInputStream = entity.getContent();
- }
- } catch (IOException inputException) {
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Failed to connect InputStream: "
- + inputException);
- }
- // Not unexpected. For example, 404 response return headers,
- // and sometimes a body with a detailed error.
- }
- if (mBodyInputStream == null) {
- // No error stream either. Treat as a 0 byte response.
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "No InputStream");
- }
- return 0; // EOF.
- }
- }
- int ret;
- try {
- int got = mBodyInputStream.read(buf);
- if (got > 0) {
- // Got some bytes, not EOF.
- ret = got;
- } else {
- // EOF.
- mBodyInputStream.close();
- ret = 0;
- }
- } catch (IOException e) {
- // An abort() interrupts us by calling close() on our stream.
- if (LOGV_ENABLED) {
- Log.i(LOG_TAG, "Got IOException in mBodyInputStream.read(): ", e);
- }
- ret = -1;
- }
- return ret;
- }
-
- /**
- * For POST method requests, send a stream of data provided by the
- * native side in repeated callbacks.
- * We put the data in mBuffer, and wait until it is consumed
- * by the StreamEntity in the request thread.
- * @param data A byte array containing the data to sent, or null
- * if indicating EOF.
- * @param bytes The number of bytes from the start of the array to
- * send, or 0 if indicating EOF.
- * @return True if all bytes were successfully sent, false on error.
- */
- public boolean sendPostData(byte[] data, int bytes) {
- mConnectionFailedLock.lock();
- if (mConnectionFailed) {
- mConnectionFailedLock.unlock();
- return false;
- }
- mConnectionFailedLock.unlock();
- if (mPostEntity == null) return false;
-
- // We block until the outputstream is available
- // (or in case of connection error)
- if (!mPostEntity.isReady()) return false;
-
- if (data == null && bytes == 0) {
- mBuffer.put(null);
- } else {
- mBuffer.put(new DataPacket(data, bytes));
- }
- mSignal.waitUntilPacketConsumed();
-
- mConnectionFailedLock.lock();
- if (mConnectionFailed) {
- Log.e(LOG_TAG, "failure");
- mConnectionFailedLock.unlock();
- return false;
- }
- mConnectionFailedLock.unlock();
- return true;
- }
-
-}
diff --git a/core/java/android/webkit/gears/DesktopAndroid.java b/core/java/android/webkit/gears/DesktopAndroid.java
deleted file mode 100644
index a7a144b..0000000
--- a/core/java/android/webkit/gears/DesktopAndroid.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2008 The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.Context;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.provider.Browser;
-import android.util.Log;
-import android.webkit.WebView;
-
-/**
- * Utility class to create a shortcut on Android
- */
-public class DesktopAndroid {
-
- private static final String TAG = "Gears-J-Desktop";
- private static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
- private static final String ACTION_INSTALL_SHORTCUT =
- "com.android.launcher.action.INSTALL_SHORTCUT";
-
- // Android now enforces a 64x64 limit for the icon
- private static int MAX_WIDTH = 64;
- private static int MAX_HEIGHT = 64;
-
- /**
- * Small utility function returning a Bitmap object.
- *
- * @param path the icon path
- */
- private static Bitmap getBitmap(String path) {
- return BitmapFactory.decodeFile(path);
- }
-
- /**
- * Create a shortcut for a webpage.
- *
- * <p>To set a shortcut on Android, we use the ACTION_INSTALL_SHORTCUT
- * from the default Home application. We only have to create an Intent
- * containing extra parameters specifying the shortcut.
- * <p>Note: the shortcut mechanism is not system wide and depends on the
- * Home application; if phone carriers decide to rewrite a Home application
- * that does not accept this Intent, no shortcut will be added.
- *
- * @param webview the webview we are called from
- * @param title the shortcut's title
- * @param url the shortcut's url
- * @param imagePath the local path of the shortcut's icon
- */
- public static void setShortcut(WebView webview, String title,
- String url, String imagePath) {
- Context context = webview.getContext();
-
- Intent viewWebPage = new Intent(Intent.ACTION_VIEW);
- viewWebPage.setData(Uri.parse(url));
- long urlHash = url.hashCode();
- long uniqueId = (urlHash << 32) | viewWebPage.hashCode();
- viewWebPage.putExtra(Browser.EXTRA_APPLICATION_ID,
- Long.toString(uniqueId));
-
- Intent intent = new Intent(ACTION_INSTALL_SHORTCUT);
- intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, viewWebPage);
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
-
- // We disallow the creation of duplicate shortcuts (i.e. same
- // url, same title, but different screen position).
- intent.putExtra(EXTRA_SHORTCUT_DUPLICATE, false);
-
- Bitmap bmp = getBitmap(imagePath);
- if (bmp != null) {
- if ((bmp.getWidth() > MAX_WIDTH) ||
- (bmp.getHeight() > MAX_HEIGHT)) {
- Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp,
- MAX_WIDTH, MAX_HEIGHT, true);
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, scaledBitmap);
- } else {
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bmp);
- }
- } else {
- // This should not happen as we just downloaded the icon
- Log.e(TAG, "icon file <" + imagePath + "> not found");
- }
-
- context.sendBroadcast(intent);
- }
-
-}
diff --git a/core/java/android/webkit/gears/NativeDialog.java b/core/java/android/webkit/gears/NativeDialog.java
deleted file mode 100644
index 9e2b375..0000000
--- a/core/java/android/webkit/gears/NativeDialog.java
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2008 The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.ActivityNotFoundException;
-import android.content.Context;
-import android.content.Intent;
-import android.util.Log;
-
-import java.io.File;
-import java.lang.InterruptedException;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * Utility class to call a modal native dialog on Android
- * The dialog itself is an Activity defined in the Browser.
- * @hide
- */
-public class NativeDialog {
-
- private static final String TAG = "Gears-J-NativeDialog";
-
- private final String DIALOG_PACKAGE = "com.android.browser";
- private final String DIALOG_CLASS = DIALOG_PACKAGE + ".GearsNativeDialog";
-
- private static Lock mLock = new ReentrantLock();
- private static Condition mDialogFinished = mLock.newCondition();
- private static String mResults = null;
-
- private static boolean mAsynchronousDialog;
-
- /**
- * Utility function to build the intent calling the
- * dialog activity
- */
- private Intent createIntent(String type, String arguments) {
- Intent intent = new Intent();
- intent.setClassName(DIALOG_PACKAGE, DIALOG_CLASS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra("dialogArguments", arguments);
- intent.putExtra("dialogType", type);
- return intent;
- }
-
- /**
- * Opens a native dialog synchronously and waits for its completion.
- *
- * The dialog is an activity (GearsNativeDialog) provided by the Browser
- * that we call via startActivity(). Contrary to a normal activity though,
- * we need to block until it returns. To do so, we define a static lock
- * object in this class, which GearsNativeDialog can unlock once done
- */
- public String showDialog(Context context, String file,
- String arguments) {
-
- try {
- mAsynchronousDialog = false;
- mLock.lock();
- File path = new File(file);
- String fileName = path.getName();
- String type = fileName.substring(0, fileName.indexOf(".html"));
- Intent intent = createIntent(type, arguments);
-
- mResults = null;
- context.startActivity(intent);
- mDialogFinished.await();
- } catch (InterruptedException e) {
- Log.e(TAG, "exception e: " + e);
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "exception e: " + e);
- } finally {
- mLock.unlock();
- }
-
- return mResults;
- }
-
- /**
- * Opens a native dialog asynchronously
- *
- * The dialog is an activity (GearsNativeDialog) provided by the
- * Browser.
- */
- public void showAsyncDialog(Context context, String type,
- String arguments) {
- mAsynchronousDialog = true;
- Intent intent = createIntent(type, arguments);
- context.startActivity(intent);
- }
-
- /**
- * Static method that GearsNativeDialog calls to unlock us
- */
- public static void signalFinishedDialog() {
- if (!mAsynchronousDialog) {
- mLock.lock();
- mDialogFinished.signal();
- mLock.unlock();
- } else {
- // we call the native callback
- closeAsynchronousDialog(mResults);
- }
- }
-
- /**
- * Static method that GearsNativeDialog calls to set the
- * dialog's result
- */
- public static void closeDialog(String res) {
- mResults = res;
- }
-
- /**
- * Native callback method
- */
- private native static void closeAsynchronousDialog(String res);
-}
diff --git a/core/java/android/webkit/gears/PluginSettings.java b/core/java/android/webkit/gears/PluginSettings.java
deleted file mode 100644
index 2d0cc13..0000000
--- a/core/java/android/webkit/gears/PluginSettings.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2008 The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.content.Context;
-import android.util.Log;
-import android.webkit.Plugin;
-import android.webkit.Plugin.PreferencesClickHandler;
-
-/**
- * Simple bridge class intercepting the click in the
- * browser plugin list and calling the Gears settings
- * dialog.
- */
-public class PluginSettings {
-
- private static final String TAG = "Gears-J-PluginSettings";
- private Context mContext;
-
- public PluginSettings(Plugin plugin) {
- plugin.setClickHandler(new ClickHandler());
- }
-
- /**
- * We do not call the dialog synchronously here as the main
- * message loop would be blocked, so we call it via a secondary thread.
- */
- private class ClickHandler implements PreferencesClickHandler {
- public void handleClickEvent(Context context) {
- mContext = context.getApplicationContext();
- Thread startDialog = new Thread(new StartDialog(context));
- startDialog.start();
- }
- }
-
- /**
- * Simple wrapper class to call the gears native method in
- * a separate thread (the native code will then instanciate a NativeDialog
- * object which will start the GearsNativeDialog activity defined in
- * the Browser).
- */
- private class StartDialog implements Runnable {
- Context mContext;
-
- public StartDialog(Context context) {
- mContext = context;
- }
-
- public void run() {
- runSettingsDialog(mContext);
- }
- }
-
- private static native void runSettingsDialog(Context c);
-
-}
diff --git a/core/java/android/webkit/gears/UrlInterceptHandlerGears.java b/core/java/android/webkit/gears/UrlInterceptHandlerGears.java
deleted file mode 100644
index 43104bf..0000000
--- a/core/java/android/webkit/gears/UrlInterceptHandlerGears.java
+++ /dev/null
@@ -1,417 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.util.Log;
-import android.webkit.CacheManager.CacheResult;
-import android.webkit.Plugin;
-import android.webkit.PluginData;
-import android.webkit.UrlInterceptRegistry;
-import android.webkit.UrlInterceptHandler;
-import android.webkit.WebView;
-
-import org.apache.http.util.CharArrayBuffer;
-
-import java.io.*;
-import java.util.*;
-
-/**
- * Services requests to handle URLs coming from the browser or
- * HttpRequestAndroid. This registers itself with the
- * UrlInterceptRegister in Android so we get a chance to service all
- * URLs passing through the browser before anything else.
- */
-public class UrlInterceptHandlerGears implements UrlInterceptHandler {
- /** Singleton instance. */
- private static UrlInterceptHandlerGears instance;
- /** Debug logging tag. */
- private static final String LOG_TAG = "Gears-J";
- /** Buffer size for reading/writing streams. */
- private static final int BUFFER_SIZE = 4096;
- /** Enable/disable all logging in this class. */
- private static boolean logEnabled = false;
- /** The unmodified (case-sensitive) key in the headers map is the
- * same index as used by HttpRequestAndroid. */
- public static final int HEADERS_MAP_INDEX_KEY =
- ApacheHttpRequestAndroid.HEADERS_MAP_INDEX_KEY;
- /** The associated value in the headers map is the same index as
- * used by HttpRequestAndroid. */
- public static final int HEADERS_MAP_INDEX_VALUE =
- ApacheHttpRequestAndroid.HEADERS_MAP_INDEX_VALUE;
-
- /**
- * Object passed to the native side, containing information about
- * the URL to service.
- */
- public static class ServiceRequest {
- // The URL being requested.
- private String url;
- // Request headers. Map of lowercase key to [ unmodified key, value ].
- private Map<String, String[]> requestHeaders;
-
- /**
- * Initialize members on construction.
- * @param url The URL being requested.
- * @param requestHeaders Headers associated with the request,
- * or null if none.
- * Map of lowercase key to [ unmodified key, value ].
- */
- public ServiceRequest(String url, Map<String, String[]> requestHeaders) {
- this.url = url;
- this.requestHeaders = requestHeaders;
- }
-
- /**
- * Returns the URL being requested.
- * @return The URL being requested.
- */
- public String getUrl() {
- return url;
- }
-
- /**
- * Get the value associated with a request header key, if any.
- * @param header The key to find, case insensitive.
- * @return The value associated with this header, or null if not found.
- */
- public String getRequestHeader(String header) {
- if (requestHeaders != null) {
- String[] value = requestHeaders.get(header.toLowerCase());
- if (value != null) {
- return value[HEADERS_MAP_INDEX_VALUE];
- } else {
- return null;
- }
- } else {
- return null;
- }
- }
- }
-
- /**
- * Object returned by the native side, containing information needed
- * to pass the entire response back to the browser or
- * HttpRequestAndroid. Works from either an in-memory array or a
- * file on disk.
- */
- public class ServiceResponse {
- // The response status code, e.g 200.
- private int statusCode;
- // The full status line, e.g "HTTP/1.1 200 OK".
- private String statusLine;
- // All headers associated with the response. Map of lowercase key
- // to [ unmodified key, value ].
- private Map<String, String[]> responseHeaders =
- new HashMap<String, String[]>();
- // The MIME type, e.g "text/html".
- private String mimeType;
- // The encoding, e.g "utf-8", or null if none.
- private String encoding;
- // The stream which contains the body when read().
- private InputStream inputStream;
- // The length of the content body.
- private long contentLength;
-
- /**
- * Initialize members using an in-memory array to return the body.
- * @param statusCode The response status code, e.g 200.
- * @param statusLine The full status line, e.g "HTTP/1.1 200 OK".
- * @param mimeType The MIME type, e.g "text/html".
- * @param encoding Encoding, e.g "utf-8" or null if none.
- * @param body The response body as a byte array, non-empty.
- */
- void setResultArray(
- int statusCode,
- String statusLine,
- String mimeType,
- String encoding,
- byte[] body) {
- this.statusCode = statusCode;
- this.statusLine = statusLine;
- this.mimeType = mimeType;
- this.encoding = encoding;
- // Setup a stream to read out of the byte array.
- this.contentLength = body.length;
- this.inputStream = new ByteArrayInputStream(body);
- }
-
- /**
- * Initialize members using a file on disk to return the body.
- * @param statusCode The response status code, e.g 200.
- * @param statusLine The full status line, e.g "HTTP/1.1 200 OK".
- * @param mimeType The MIME type, e.g "text/html".
- * @param encoding Encoding, e.g "utf-8" or null if none.
- * @param path Full path to the file containing the body.
- * @return True if the file is successfully setup to stream,
- * false on error such as file not found.
- */
- boolean setResultFile(
- int statusCode,
- String statusLine,
- String mimeType,
- String encoding,
- String path) {
- this.statusCode = statusCode;
- this.statusLine = statusLine;
- this.mimeType = mimeType;
- this.encoding = encoding;
- try {
- // Setup a stream to read out of a file on disk.
- File file = new File(path);
- this.contentLength = file.length();
- this.inputStream = new FileInputStream(file);
- return true;
- } catch (java.io.FileNotFoundException ex) {
- log("File not found: " + path);
- return false;
- }
- }
-
- /**
- * Set a response header, adding its settings to the header members.
- * @param key The case sensitive key for the response header,
- * e.g "Set-Cookie".
- * @param value The value associated with this key, e.g "cookie1234".
- */
- public void setResponseHeader(String key, String value) {
- // The map value contains the unmodified key (not lowercase).
- String[] mapValue = { key, value };
- responseHeaders.put(key.toLowerCase(), mapValue);
- }
-
- /**
- * Return the "Content-Type" header possibly supplied by a
- * previous setResponseHeader().
- * @return The "Content-Type" value, or null if not present.
- */
- public String getContentType() {
- // The map keys are lowercase.
- String[] value = responseHeaders.get("content-type");
- if (value != null) {
- return value[HEADERS_MAP_INDEX_VALUE];
- } else {
- return null;
- }
- }
-
- /**
- * Returns the HTTP status code for the response, supplied in
- * setResultArray() or setResultFile().
- * @return The HTTP statue code, e.g 200.
- */
- public int getStatusCode() {
- return statusCode;
- }
-
- /**
- * Returns the full HTTP status line for the response, supplied in
- * setResultArray() or setResultFile().
- * @return The HTTP statue line, e.g "HTTP/1.1 200 OK".
- */
- public String getStatusLine() {
- return statusLine;
- }
-
- /**
- * Get all response headers supplied in calls in
- * setResponseHeader().
- * @return A Map<String, String[]> containing all headers.
- */
- public Map<String, String[]> getResponseHeaders() {
- return responseHeaders;
- }
-
- /**
- * Returns the MIME type for the response, supplied in
- * setResultArray() or setResultFile().
- * @return The MIME type, e.g "text/html".
- */
- public String getMimeType() {
- return mimeType;
- }
-
- /**
- * Returns the encoding for the response, supplied in
- * setResultArray() or setResultFile(), or null if none.
- * @return The encoding, e.g "utf-8", or null if none.
- */
- public String getEncoding() {
- return encoding;
- }
-
- /**
- * Returns the InputStream setup by setResultArray() or
- * setResultFile() to allow reading data either from memory or
- * disk.
- * @return The InputStream containing the response body.
- */
- public InputStream getInputStream() {
- return inputStream;
- }
-
- /**
- * @return The length of the response body.
- */
- public long getContentLength() {
- return contentLength;
- }
- }
-
- /**
- * Construct and initialize the singleton instance.
- */
- public UrlInterceptHandlerGears() {
- if (instance != null) {
- Log.e(LOG_TAG, "UrlInterceptHandlerGears singleton already constructed");
- throw new RuntimeException();
- }
- instance = this;
- }
-
- /**
- * Turn on/off logging in this class.
- * @param on Logging enable state.
- */
- public static void enableLogging(boolean on) {
- logEnabled = on;
- }
-
- /**
- * Get the singleton instance.
- * @return The singleton instance.
- */
- public static UrlInterceptHandlerGears getInstance() {
- return instance;
- }
-
- /**
- * Register the singleton instance with the browser's interception
- * mechanism.
- */
- public synchronized void register() {
- UrlInterceptRegistry.registerHandler(this);
- }
-
- /**
- * Unregister the singleton instance from the browser's interception
- * mechanism.
- */
- public synchronized void unregister() {
- UrlInterceptRegistry.unregisterHandler(this);
- }
-
- /**
- * Given an URL, returns the CacheResult which contains the
- * surrogate response for the request, or null if the handler is
- * not interested.
- *
- * @param url URL string.
- * @param headers The headers associated with the request. May be null.
- * @return The CacheResult containing the surrogate response.
- * @Deprecated Use PluginData getPluginData(String url,
- * Map<String, String> headers); instead
- */
- @Deprecated
- public CacheResult service(String url, Map<String, String> headers) {
- throw new UnsupportedOperationException("unimplemented");
- }
-
- /**
- * Given an URL, returns a PluginData instance which contains the
- * response for the request. This implements the UrlInterceptHandler
- * interface.
- *
- * @param url The fully qualified URL being requested.
- * @param requestHeaders The request headers for this URL.
- * @return a PluginData object.
- */
- public PluginData getPluginData(String url, Map<String, String> requestHeaders) {
- // Thankfully the browser does call us with case-sensitive
- // headers. We just need to map it case-insensitive.
- Map<String, String[]> lowercaseRequestHeaders =
- new HashMap<String, String[]>();
- Iterator<Map.Entry<String, String>> requestHeadersIt =
- requestHeaders.entrySet().iterator();
- while (requestHeadersIt.hasNext()) {
- Map.Entry<String, String> entry = requestHeadersIt.next();
- String key = entry.getKey();
- String mapValue[] = { key, entry.getValue() };
- lowercaseRequestHeaders.put(key.toLowerCase(), mapValue);
- }
- ServiceResponse response = getServiceResponse(url, lowercaseRequestHeaders);
- if (response == null) {
- // No result for this URL.
- return null;
- }
- return new PluginData(response.getInputStream(),
- response.getContentLength(),
- response.getResponseHeaders(),
- response.getStatusCode());
- }
-
- /**
- * Given an URL, returns a CacheResult and headers which contain the
- * response for the request.
- *
- * @param url The fully qualified URL being requested.
- * @param requestHeaders The request headers for this URL.
- * @return If a response can be crafted, a ServiceResponse is
- * created which contains all response headers and an InputStream
- * attached to the body. If there is no response, null is returned.
- */
- public ServiceResponse getServiceResponse(String url,
- Map<String, String[]> requestHeaders) {
- if (!url.startsWith("http://") && !url.startsWith("https://")) {
- // Don't know how to service non-HTTP URLs
- return null;
- }
- // Call the native handler to craft a response for this URL.
- return nativeService(new ServiceRequest(url, requestHeaders));
- }
-
- /**
- * Convenience debug function. Calls the Android logging
- * mechanism. logEnabled is not a constant, so if the string
- * evaluation is potentially expensive, the caller also needs to
- * check it.
- * @param str String to log to the Android console.
- */
- private void log(String str) {
- if (logEnabled) {
- Log.i(LOG_TAG, str);
- }
- }
-
- /**
- * Native method which handles the bulk of the request in LocalServer.
- * @param request A ServiceRequest object containing information about
- * the request.
- * @return If serviced, a ServiceResponse object containing all the
- * information to provide a response for the URL, or null
- * if no response available for this URL.
- */
- private native static ServiceResponse nativeService(ServiceRequest request);
-}
diff --git a/core/java/android/webkit/gears/VersionExtractor.java b/core/java/android/webkit/gears/VersionExtractor.java
deleted file mode 100644
index 172dacb..0000000
--- a/core/java/android/webkit/gears/VersionExtractor.java
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.util.Log;
-import java.io.IOException;
-import java.io.StringReader;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.FactoryConfigurationError;
-import javax.xml.parsers.ParserConfigurationException;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.w3c.dom.Document;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * A class that can extract the Gears version and upgrade URL from an
- * xml document.
- */
-public final class VersionExtractor {
-
- /**
- * Logging tag
- */
- private static final String TAG = "Gears-J-VersionExtractor";
- /**
- * XML element names.
- */
- private static final String VERSION = "em:version";
- private static final String URL = "em:updateLink";
-
- /**
- * Parses the input xml string and invokes the native
- * setVersionAndUrl method.
- * @param xml is the XML string to parse.
- * @return true if the extraction is successful and false otherwise.
- */
- public static boolean extract(String xml, long nativeObject) {
- try {
- // Build the builders.
- DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
- factory.setNamespaceAware(false);
- DocumentBuilder builder = factory.newDocumentBuilder();
-
- // Create the document.
- Document doc = builder.parse(new InputSource(new StringReader(xml)));
-
- // Look for the version and url elements and get their text
- // contents.
- String version = extractText(doc, VERSION);
- String url = extractText(doc, URL);
-
- // If we have both, let the native side know.
- if (version != null && url != null) {
- setVersionAndUrl(version, url, nativeObject);
- return true;
- }
-
- return false;
-
- } catch (FactoryConfigurationError ex) {
- Log.e(TAG, "Could not create the DocumentBuilderFactory " + ex);
- } catch (ParserConfigurationException ex) {
- Log.e(TAG, "Could not create the DocumentBuilder " + ex);
- } catch (SAXException ex) {
- Log.e(TAG, "Could not parse the xml " + ex);
- } catch (IOException ex) {
- Log.e(TAG, "Could not read the xml " + ex);
- }
-
- return false;
- }
-
- /**
- * Extracts the text content of the first element with the given name.
- * @param doc is the Document where the element is searched for.
- * @param elementName is name of the element to searched for.
- * @return the text content of the element or null if no such
- * element is found.
- */
- private static String extractText(Document doc, String elementName) {
- String text = null;
- NodeList node_list = doc.getElementsByTagName(elementName);
-
- if (node_list.getLength() > 0) {
- // We are only interested in the first node. Normally there
- // should not be more than one anyway.
- Node node = node_list.item(0);
-
- // Iterate through the text children.
- NodeList child_list = node.getChildNodes();
-
- try {
- for (int i = 0; i < child_list.getLength(); ++i) {
- Node child = child_list.item(i);
- if (child.getNodeType() == Node.TEXT_NODE) {
- if (text == null) {
- text = new String();
- }
- text += child.getNodeValue();
- }
- }
- } catch (DOMException ex) {
- Log.e(TAG, "getNodeValue() failed " + ex);
- }
- }
-
- if (text != null) {
- text = text.trim();
- }
-
- return text;
- }
-
- /**
- * Native method used to send the version and url back to the C++
- * side.
- */
- private static native void setVersionAndUrl(
- String version, String url, long nativeObject);
-}
diff --git a/core/java/android/webkit/gears/ZipInflater.java b/core/java/android/webkit/gears/ZipInflater.java
deleted file mode 100644
index f6b6be5..0000000
--- a/core/java/android/webkit/gears/ZipInflater.java
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2008, The Android Open Source Project
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// 1. Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// 3. Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
-// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-package android.webkit.gears;
-
-import android.os.StatFs;
-import android.util.Log;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.zip.CRC32;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import java.util.zip.ZipInputStream;
-
-
-/**
- * A class that can inflate a zip archive.
- */
-public final class ZipInflater {
- /**
- * Logging tag
- */
- private static final String TAG = "Gears-J-ZipInflater";
-
- /**
- * The size of the buffer used to read from the archive.
- */
- private static final int BUFFER_SIZE_BYTES = 32 * 1024; // 32 KB.
- /**
- * The path navigation component (i.e. "../").
- */
- private static final String PATH_NAVIGATION_COMPONENT = ".." + File.separator;
- /**
- * The root of the data partition.
- */
- private static final String DATA_PARTITION_ROOT = "/data";
-
- /**
- * We need two be able to store two versions of gears in parallel:
- * - the zipped version
- * - the unzipped version, which will be loaded next time the browser is started.
- * We are conservative and do not attempt to unpack unless there enough free
- * space on the device to store 4 times the new Gears size.
- */
- private static final long SIZE_MULTIPLIER = 4;
-
- /**
- * Unzips the archive with the given name.
- * @param filename is the name of the zip to inflate.
- * @param path is the path where the zip should be unpacked. It must contain
- * a trailing separator, or the extraction will fail.
- * @return true if the extraction is successful and false otherwise.
- */
- public static boolean inflate(String filename, String path) {
- Log.i(TAG, "Extracting " + filename + " to " + path);
-
- // Check that the path ends with a separator.
- if (!path.endsWith(File.separator)) {
- Log.e(TAG, "Path missing trailing separator: " + path);
- return false;
- }
-
- boolean result = false;
-
- // Use a ZipFile to get an enumeration of the entries and
- // calculate the overall uncompressed size of the archive. Also
- // check for existing files or directories that have the same
- // name as the entries in the archive. Also check for invalid
- // entry names (e.g names that attempt to navigate to the
- // parent directory).
- ZipInputStream zipStream = null;
- long uncompressedSize = 0;
- try {
- ZipFile zipFile = new ZipFile(filename);
- try {
- Enumeration entries = zipFile.entries();
- while (entries.hasMoreElements()) {
- ZipEntry entry = (ZipEntry) entries.nextElement();
- uncompressedSize += entry.getSize();
- // Check against entry names that may attempt to navigate
- // out of the destination directory.
- if (entry.getName().indexOf(PATH_NAVIGATION_COMPONENT) >= 0) {
- throw new IOException("Illegal entry name: " + entry.getName());
- }
-
- // Check against entries with the same name as pre-existing files or
- // directories.
- File file = new File(path + entry.getName());
- if (file.exists()) {
- // A file or directory with the same name already exist.
- // This must not happen, so we treat this as an error.
- throw new IOException(
- "A file or directory with the same name already exists.");
- }
- }
- } finally {
- zipFile.close();
- }
-
- Log.i(TAG, "Determined uncompressed size: " + uncompressedSize);
- // Check we have enough space to unpack this archive.
- if (freeSpace() <= uncompressedSize * SIZE_MULTIPLIER) {
- throw new IOException("Not enough space to unpack this archive.");
- }
-
- zipStream = new ZipInputStream(
- new BufferedInputStream(new FileInputStream(filename)));
- ZipEntry entry;
- int counter;
- byte buffer[] = new byte[BUFFER_SIZE_BYTES];
-
- // Iterate through the entries and write each of them to a file.
- while ((entry = zipStream.getNextEntry()) != null) {
- File file = new File(path + entry.getName());
- if (entry.isDirectory()) {
- // If the entry denotes a directory, we need to create a
- // directory with the same name.
- file.mkdirs();
- } else {
- CRC32 checksum = new CRC32();
- BufferedOutputStream output = new BufferedOutputStream(
- new FileOutputStream(file),
- BUFFER_SIZE_BYTES);
- try {
- // Read the entry and write it to the file.
- while ((counter = zipStream.read(buffer, 0, BUFFER_SIZE_BYTES)) !=
- -1) {
- output.write(buffer, 0, counter);
- checksum.update(buffer, 0, counter);
- }
- output.flush();
- } finally {
- output.close();
- }
-
- if (checksum.getValue() != entry.getCrc()) {
- throw new IOException(
- "Integrity check failed for: " + entry.getName());
- }
- }
- zipStream.closeEntry();
- }
-
- result = true;
-
- } catch (FileNotFoundException ex) {
- Log.e(TAG, "The zip file could not be found. " + ex);
- } catch (IOException ex) {
- Log.e(TAG, "Could not read or write an entry. " + ex);
- } catch(IllegalArgumentException ex) {
- Log.e(TAG, "Could not create the BufferedOutputStream. " + ex);
- } finally {
- if (zipStream != null) {
- try {
- zipStream.close();
- } catch (IOException ex) {
- // Ignored.
- }
- }
- // Discard any exceptions and return the result to the native side.
- return result;
- }
- }
-
- private static final long freeSpace() {
- StatFs data_partition = new StatFs(DATA_PARTITION_ROOT);
- long freeSpace = data_partition.getAvailableBlocks() *
- data_partition.getBlockSize();
- Log.i(TAG, "Free space on the data partition: " + freeSpace);
- return freeSpace;
- }
-}
diff --git a/core/java/android/webkit/gears/package.html b/core/java/android/webkit/gears/package.html
deleted file mode 100644
index db6f78b..0000000
--- a/core/java/android/webkit/gears/package.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<body>
-{@hide}
-</body> \ No newline at end of file
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index eea97dc..2f292d5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -546,6 +546,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private void initAbsListView() {
// Setting focusable in touch mode will set the focusable property to true
+ setClickable(true);
setFocusableInTouchMode(true);
setWillNotDraw(false);
setAlwaysDrawnWithCacheEnabled(false);
@@ -1433,6 +1434,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
* this is a long press.
*/
void keyPressed() {
+ if (!isEnabled() || !isClickable()) {
+ return;
+ }
+
Drawable selector = mSelector;
Rect selectorRect = mSelectorRect;
if (selector != null && (isFocused() || touchModeDrawsInPressedState())
@@ -1450,8 +1455,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
Drawable d = selector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
if (longClickable) {
- ((TransitionDrawable) d).startTransition(ViewConfiguration
- .getLongPressTimeout());
+ ((TransitionDrawable) d).startTransition(
+ ViewConfiguration.getLongPressTimeout());
} else {
((TransitionDrawable) d).resetTransition();
}
@@ -1732,18 +1737,29 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
@Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ @Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_ENTER:
- if (isPressed() && mSelectedPosition >= 0 && mAdapter != null &&
+ if (!isEnabled()) {
+ return true;
+ }
+ if (isClickable() && isPressed() &&
+ mSelectedPosition >= 0 && mAdapter != null &&
mSelectedPosition < mAdapter.getCount()) {
+
final View view = getChildAt(mSelectedPosition - mFirstPosition);
performItemClick(view, mSelectedPosition, mSelectedRowId);
setPressed(false);
if (view != null) view.setPressed(false);
return true;
}
+ break;
}
return super.onKeyUp(keyCode, event);
}
@@ -1892,6 +1908,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
@Override
public boolean onTouchEvent(MotionEvent ev) {
+ if (!isEnabled()) {
+ // A disabled view that is clickable still consumes the touch
+ // events, it just doesn't respond to them.
+ return isClickable() || isLongClickable();
+ }
+
if (mFastScroller != null) {
boolean intercepted = mFastScroller.onTouchEvent(ev);
if (intercepted) {
@@ -1974,7 +1996,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (y != mLastY) {
deltaY -= mMotionCorrection;
int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
- trackMotionScroll(deltaY, incrementalDeltaY);
+ // No need to do all this work if we're not going to move anyway
+ if (incrementalDeltaY != 0) {
+ trackMotionScroll(deltaY, incrementalDeltaY);
+ }
// Check to see if we have bumped into the scroll limit
View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
@@ -2041,7 +2066,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (mSelector != null) {
Drawable d = mSelector.getCurrent();
if (d != null && d instanceof TransitionDrawable) {
- ((TransitionDrawable)d).resetTransition();
+ ((TransitionDrawable) d).resetTransition();
}
}
postDelayed(new Runnable() {
@@ -2065,15 +2090,27 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
mTouchMode = TOUCH_MODE_REST;
break;
case TOUCH_MODE_SCROLL:
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int initialVelocity = (int) velocityTracker.getYVelocity();
- if (Math.abs(initialVelocity) > mMinimumVelocity && (getChildCount() > 0)) {
- if (mFlingRunnable == null) {
- mFlingRunnable = new FlingRunnable();
+ final int childCount = getChildCount();
+ if (childCount > 0) {
+ if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
+ mFirstPosition + childCount < mItemCount &&
+ getChildAt(childCount - 1).getBottom() <=
+ getHeight() - mListPadding.bottom) {
+ mTouchMode = TOUCH_MODE_REST;
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+ } else {
+ final VelocityTracker velocityTracker = mVelocityTracker;
+ velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+ final int initialVelocity = (int) velocityTracker.getYVelocity();
+
+ if (Math.abs(initialVelocity) > mMinimumVelocity) {
+ if (mFlingRunnable == null) {
+ mFlingRunnable = new FlingRunnable();
+ }
+ reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+ mFlingRunnable.start(-initialVelocity);
+ }
}
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
- mFlingRunnable.start(-initialVelocity);
} else {
mTouchMode = TOUCH_MODE_REST;
reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 04cb8a0..2a0e5e5 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -64,10 +64,10 @@ public abstract class AbsSeekBar extends ProgressBar {
TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.SeekBar, defStyle, 0);
Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
- setThumb(thumb);
+ setThumb(thumb); // will guess mThumbOffset if thumb != null...
+ // ...but allow layout to override this
int thumbOffset =
- a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, 0);
- setThumbOffset(thumbOffset);
+ a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
a.recycle();
a = context.obtainStyledAttributes(attrs,
@@ -77,13 +77,21 @@ public abstract class AbsSeekBar extends ProgressBar {
}
/**
- * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar
+ * Sets the thumb that will be drawn at the end of the progress meter within the SeekBar.
+ * <p>
+ * If the thumb is a valid drawable (i.e. not null), half its width will be
+ * used as the new thumb offset (@see #setThumbOffset(int)).
*
* @param thumb Drawable representing the thumb
*/
public void setThumb(Drawable thumb) {
if (thumb != null) {
thumb.setCallback(this);
+
+ // Assuming the thumb drawable is symmetric, set the thumb offset
+ // such that the thumb will hang halfway off either edge of the
+ // progress bar.
+ mThumbOffset = (int)thumb.getIntrinsicWidth() / 2;
}
mThumb = thumb;
invalidate();
@@ -320,11 +328,6 @@ public abstract class AbsSeekBar extends ProgressBar {
final int max = getMax();
progress += scale * max;
- if (progress < 0) {
- progress = 0;
- } else if (progress > max) {
- progress = max;
- }
setProgress((int) progress, true);
}
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 7d2fcbc..fe6d91a 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -163,7 +163,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup {
/**
* View to show if there are no items to show.
*/
- View mEmptyView;
+ private View mEmptyView;
/**
* The number of items in the current adapter.
diff --git a/core/java/android/widget/AlphabetIndexer.java b/core/java/android/widget/AlphabetIndexer.java
index f50676a..59b2c2a 100644
--- a/core/java/android/widget/AlphabetIndexer.java
+++ b/core/java/android/widget/AlphabetIndexer.java
@@ -28,7 +28,7 @@ import android.util.SparseIntArray;
* invalidates the cache if changes occur in the cursor.
* <p/>
* Your adapter is responsible for updating the cursor by calling {@link #setCursor} if the
- * cursor changes. {@link #getPositionForSection} method does the binary search for the starting
+ * cursor changes. {@link #getPositionForSection} method does the binary search for the starting
* index of a given section (alphabet).
*/
public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
@@ -37,33 +37,33 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
* Cursor that is used by the adapter of the list view.
*/
protected Cursor mDataCursor;
-
+
/**
* The index of the cursor column that this list is sorted on.
*/
protected int mColumnIndex;
-
+
/**
* The string of characters that make up the indexing sections.
*/
protected CharSequence mAlphabet;
-
+
/**
* Cached length of the alphabet array.
*/
private int mAlphabetLength;
-
+
/**
* This contains a cache of the computed indices so far. It will get reset whenever
* the dataset changes or the cursor changes.
*/
private SparseIntArray mAlphaMap;
-
+
/**
* Use a collator to compare strings in a localized manner.
*/
private java.text.Collator mCollator;
-
+
/**
* The section array converted from the alphabet string.
*/
@@ -72,9 +72,9 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
/**
* Constructs the indexer.
* @param cursor the cursor containing the data set
- * @param sortedColumnIndex the column number in the cursor that is sorted
+ * @param sortedColumnIndex the column number in the cursor that is sorted
* alphabetically
- * @param alphabet string containing the alphabet, with space as the first character.
+ * @param alphabet string containing the alphabet, with space as the first character.
* For example, use the string " ABCDEFGHIJKLMNOPQRSTUVWXYZ" for English indexing.
* The characters must be uppercase and be sorted in ascii/unicode order. Basically
* characters in the alphabet will show up as preview letters.
@@ -104,7 +104,7 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
public Object[] getSections() {
return mAlphabetArray;
}
-
+
/**
* Sets a new cursor as the data set and resets the cache of indices.
* @param cursor the new cursor to use as the data set
@@ -124,9 +124,16 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
* Default implementation compares the first character of word with letter.
*/
protected int compare(String word, String letter) {
- return mCollator.compare(word.substring(0, 1), letter);
+ final String firstLetter;
+ if (word.length() == 0) {
+ firstLetter = " ";
+ } else {
+ firstLetter = word.substring(0, 1);
+ }
+
+ return mCollator.compare(firstLetter, letter);
}
-
+
/**
* Performs a binary search or cache lookup to find the first row that
* matches a given section's starting letter.
@@ -143,7 +150,7 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
if (cursor == null || mAlphabet == null) {
return 0;
}
-
+
// Check bounds
if (sectionIndex <= 0) {
return 0;
@@ -164,7 +171,7 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
int key = letter;
// Check map
if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) {
- // Is it approximate? Using negative value to indicate that it's
+ // Is it approximate? Using negative value to indicate that it's
// an approximation and positive value when it is the accurate
// position.
if (pos < 0) {
@@ -204,7 +211,7 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
}
int diff = compare(curName, targetLetter);
if (diff != 0) {
- // Commenting out approximation code because it doesn't work for certain
+ // TODO: Commenting out approximation code because it doesn't work for certain
// lists with custom comparators
// Enter approximation in hash if a better solution doesn't exist
// String startingLetter = Character.toString(getFirstLetter(curName));
@@ -259,9 +266,9 @@ public class AlphabetIndexer extends DataSetObserver implements SectionIndexer {
return i;
}
}
- return 0; // Don't recognize the letter - falls under zero'th section
+ return 0; // Don't recognize the letter - falls under zero'th section
}
-
+
/*
* @hide
*/
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 02d77d1..d821a7d 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -194,7 +194,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
setFocusable(true);
addTextChangedListener(new MyWatcher());
-
+
mPassThroughClickListener = new PassThroughClickListener();
super.setOnClickListener(mPassThroughClickListener);
}
@@ -321,8 +321,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* @return the background drawable
*
* @attr ref android.R.styleable#PopupWindow_popupBackground
- *
- * @hide Pending API council approval
*/
public Drawable getDropDownBackground() {
return mPopup.getBackground();
@@ -334,8 +332,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* @param d the drawable to set as the background
*
* @attr ref android.R.styleable#PopupWindow_popupBackground
- *
- * @hide Pending API council approval
*/
public void setDropDownBackgroundDrawable(Drawable d) {
mPopup.setBackgroundDrawable(d);
@@ -347,47 +343,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* @param id the id of the drawable to set as the background
*
* @attr ref android.R.styleable#PopupWindow_popupBackground
- *
- * @hide Pending API council approval
*/
public void setDropDownBackgroundResource(int id) {
mPopup.setBackgroundDrawable(getResources().getDrawable(id));
}
-
- /**
- * <p>Sets the animation style of the auto-complete drop-down list.</p>
- *
- * <p>If the drop-down is showing, calling this method will take effect only
- * the next time the drop-down is shown.</p>
- *
- * @param animationStyle animation style to use when the drop-down appears
- * and disappears. Set to -1 for the default animation, 0 for no
- * animation, or a resource identifier for an explicit animation.
- *
- * @hide Pending API council approval
- */
- public void setDropDownAnimationStyle(int animationStyle) {
- mPopup.setAnimationStyle(animationStyle);
- }
-
- /**
- * <p>Returns the animation style that is used when the drop-down list appears and disappears
- * </p>
- *
- * @return the animation style that is used when the drop-down list appears and disappears
- *
- * @hide Pending API council approval
- */
- public int getDropDownAnimationStyle() {
- return mPopup.getAnimationStyle();
- }
/**
* <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
*
* @param offset the vertical offset
- *
- * @hide Pending API council approval
*/
public void setDropDownVerticalOffset(int offset) {
mDropDownVerticalOffset = offset;
@@ -397,8 +361,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
*
* @return the vertical offset
- *
- * @hide Pending API council approval
*/
public int getDropDownVerticalOffset() {
return mDropDownVerticalOffset;
@@ -408,8 +370,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
*
* @param offset the horizontal offset
- *
- * @hide Pending API council approval
*/
public void setDropDownHorizontalOffset(int offset) {
mDropDownHorizontalOffset = offset;
@@ -419,13 +379,39 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
* <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
*
* @return the horizontal offset
- *
- * @hide Pending API council approval
*/
public int getDropDownHorizontalOffset() {
return mDropDownHorizontalOffset;
}
+ /**
+ * <p>Sets the animation style of the auto-complete drop-down list.</p>
+ *
+ * <p>If the drop-down is showing, calling this method will take effect only
+ * the next time the drop-down is shown.</p>
+ *
+ * @param animationStyle animation style to use when the drop-down appears
+ * and disappears. Set to -1 for the default animation, 0 for no
+ * animation, or a resource identifier for an explicit animation.
+ *
+ * @hide Pending API council approval
+ */
+ public void setDropDownAnimationStyle(int animationStyle) {
+ mPopup.setAnimationStyle(animationStyle);
+ }
+
+ /**
+ * <p>Returns the animation style that is used when the drop-down list appears and disappears
+ * </p>
+ *
+ * @return the animation style that is used when the drop-down list appears and disappears
+ *
+ * @hide Pending API council approval
+ */
+ public int getDropDownAnimationStyle() {
+ return mPopup.getAnimationStyle();
+ }
+
/**
* @return Whether the drop-down is visible as long as there is {@link #enoughToFilter()}
*
@@ -1595,7 +1581,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
*/
CharSequence fixText(CharSequence invalidText);
}
-
+
/**
* Allows us a private hook into the on click event without preventing users from setting
* their own click listener.
@@ -1611,5 +1597,5 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
if (mWrapped != null) mWrapped.onClick(v);
}
}
-
+
}
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 5360621..6abb2ae 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -33,6 +33,7 @@ import android.view.ContextMenu;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ExpandableListConnector.PositionMetadata;
/**
@@ -916,7 +917,14 @@ public class ExpandableListView extends ListView {
@Override
ContextMenuInfo createContextMenuInfo(View view, int flatListPosition, long id) {
- PositionMetadata pm = mConnector.getUnflattenedPos(flatListPosition);
+ // Adjust for and handle for header views
+ final int adjustedPosition = flatListPosition - getHeaderViewsCount();
+ if (adjustedPosition < 0) {
+ // Return normal info for header view context menus
+ return new AdapterContextMenuInfo(view, flatListPosition, id);
+ }
+
+ PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
ExpandableListPosition pos = pm.position;
pm.recycle();
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 2da777a..67c0def 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -55,7 +55,7 @@ class FastScroller {
private int mThumbY;
private RectF mOverlayPos;
- private int mOverlaySize = 104;
+ private int mOverlaySize;
private AbsListView mList;
private boolean mScrollCompleted;
@@ -119,10 +119,10 @@ class FastScroller {
private void useThumbDrawable(Context context, Drawable drawable) {
mThumbDrawable = drawable;
- mThumbW = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 64, context.getResources().getDisplayMetrics());
- mThumbH = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 52, context.getResources().getDisplayMetrics());
+ mThumbW = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.fastscroll_thumb_width);
+ mThumbH = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.fastscroll_thumb_height);
mChangedBounds = true;
}
@@ -138,7 +138,9 @@ class FastScroller {
mScrollCompleted = true;
getSectionsFromIndexer();
-
+
+ mOverlaySize = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.fastscroll_overlay_size);
mOverlayPos = new RectF();
mScrollFade = new ScrollFade();
mPaint = new Paint();
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index a9822f8..6cc794b 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -59,7 +59,7 @@ public class LinearLayout extends ViewGroup {
* Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
* with whether the children of this layout are baseline aligned.
*/
- private int mBaselineAlignedChildIndex = 0;
+ private int mBaselineAlignedChildIndex = -1;
/**
* The additional offset to the child's baseline.
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index f8a6f89..6316864 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1328,19 +1328,23 @@ public class ListView extends AbsListView {
// Make sure we are 1) Too low, and 2) Either there are more rows below the
// last row or the last row is scrolled off the bottom of the drawable area
- if (topOffset > 0 && (lastPosition < mItemCount - 1 || lastBottom > end)) {
- if (lastPosition == mItemCount - 1 ) {
- // Don't pull the bottom too far up
- topOffset = Math.min(topOffset, lastBottom - end);
- }
- // Move everything up
- offsetChildrenTopAndBottom(-topOffset);
- if (lastPosition < mItemCount - 1) {
- // Fill the gap that was opened below the last position with more rows, if
- // possible
- fillDown(lastPosition + 1, lastChild.getBottom() + mDividerHeight);
- // Close up the remaining gap
- adjustViewsUpOrDown();
+ if (topOffset > 0) {
+ if (lastPosition < mItemCount - 1 || lastBottom > end) {
+ if (lastPosition == mItemCount - 1) {
+ // Don't pull the bottom too far up
+ topOffset = Math.min(topOffset, lastBottom - end);
+ }
+ // Move everything up
+ offsetChildrenTopAndBottom(-topOffset);
+ if (lastPosition < mItemCount - 1) {
+ // Fill the gap that was opened below the last position with more rows, if
+ // possible
+ fillDown(lastPosition + 1, lastChild.getBottom() + mDividerHeight);
+ // Close up the remaining gap
+ adjustViewsUpOrDown();
+ }
+ } else if (lastPosition == mItemCount - 1) {
+ adjustViewsUpOrDown();
}
}
}
@@ -1428,7 +1432,8 @@ public class ListView extends AbsListView {
throw new IllegalStateException("The content of the adapter has changed but "
+ "ListView did not receive a notification. Make sure the content of "
+ "your adapter is not modified from a background thread, but only "
- + "from the UI thread.");
+ + "from the UI thread. [in ListView(" + getId() + ", " + getClass()
+ + ") with Adapter(" + mAdapter.getClass() + ")]");
}
setSelectedPositionInt(mNextSelectedPosition);
@@ -3264,12 +3269,13 @@ public class ListView extends AbsListView {
if (mChoiceMode == CHOICE_MODE_MULTIPLE) {
mCheckStates.put(position, value);
} else {
- // Clear the old value: if something was selected and value == false
- // then it is unselected
- mCheckStates.clear();
- // If value == true, select the appropriate position
+ // Clear all values if we're checking something, or unchecking the currently
+ // selected item
+ if (value || isItemChecked(position)) {
+ mCheckStates.clear();
+ }
// this may end up selecting the value we just cleared but this way
- // we don't have to first to a get(position)
+ // we ensure length of mCheckStates is 1, a fact getCheckedItemPosition relies on
if (value) {
mCheckStates.put(position, true);
}
@@ -3285,11 +3291,12 @@ public class ListView extends AbsListView {
/**
* Returns the checked state of the specified position. The result is only
- * valid if the choice mode has not been set to {@link #CHOICE_MODE_SINGLE}
+ * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
* or {@link #CHOICE_MODE_MULTIPLE}.
*
* @param position The item whose checked state to return
- * @return The item's checked state
+ * @return The item's checked state or <code>false</code> if choice mode
+ * is invalid
*
* @see #setChoiceMode(int)
*/
@@ -3303,7 +3310,7 @@ public class ListView extends AbsListView {
/**
* Returns the currently checked item. The result is only valid if the choice
- * mode has not been set to {@link #CHOICE_MODE_SINGLE}.
+ * mode has been set to {@link #CHOICE_MODE_SINGLE}.
*
* @return The position of the currently checked item or
* {@link #INVALID_POSITION} if nothing is selected
@@ -3320,10 +3327,12 @@ public class ListView extends AbsListView {
/**
* Returns the set of checked items in the list. The result is only valid if
- * the choice mode has not been set to {@link #CHOICE_MODE_SINGLE}.
+ * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
*
* @return A SparseBooleanArray which will return true for each call to
- * get(int position) where position is a position in the list.
+ * get(int position) where position is a position in the list,
+ * or <code>null</code> if the choice mode is set to
+ * {@link #CHOICE_MODE_NONE}.
*/
public SparseBooleanArray getCheckedItemPositions() {
if (mChoiceMode != CHOICE_MODE_NONE) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 0c9d980..446a992 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -250,6 +250,29 @@ public class MediaController extends FrameLayout {
}
/**
+ * Disable pause or seek buttons if the stream cannot be paused or seeked.
+ * This requires the control interface to be a MediaPlayerControlExt
+ */
+ private void disableUnsupportedButtons() {
+ try {
+ if (mPauseButton != null && !mPlayer.canPause()) {
+ mPauseButton.setEnabled(false);
+ }
+ if (mRewButton != null && !mPlayer.canSeekBackward()) {
+ mRewButton.setEnabled(false);
+ }
+ if (mFfwdButton != null && !mPlayer.canSeekForward()) {
+ mFfwdButton.setEnabled(false);
+ }
+ } catch (IncompatibleClassChangeError ex) {
+ // We were given an old version of the interface, that doesn't have
+ // the canPause/canSeekXYZ methods. This is OK, it just means we
+ // assume the media can be paused and seeked, and so we don't disable
+ // the buttons.
+ }
+ }
+
+ /**
* Show the controller on screen. It will go away
* automatically after 'timeout' milliseconds of inactivity.
* @param timeout The timeout in milliseconds. Use 0 to show
@@ -259,6 +282,10 @@ public class MediaController extends FrameLayout {
if (!mShowing && mAnchor != null) {
setProgress();
+ if (mPauseButton != null) {
+ mPauseButton.requestFocus();
+ }
+ disableUnsupportedButtons();
int [] anchorpos = new int[2];
mAnchor.getLocationOnScreen(anchorpos);
@@ -392,6 +419,9 @@ public class MediaController extends FrameLayout {
keyCode == KeyEvent.KEYCODE_SPACE)) {
doPauseResume();
show(sDefaultTimeout);
+ if (mPauseButton != null) {
+ mPauseButton.requestFocus();
+ }
return true;
} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {
if (mPlayer.isPlaying()) {
@@ -421,17 +451,13 @@ public class MediaController extends FrameLayout {
};
private void updatePausePlay() {
- if (mRoot == null)
- return;
-
- ImageButton button = (ImageButton) mRoot.findViewById(com.android.internal.R.id.pause);
- if (button == null)
+ if (mRoot == null || mPauseButton == null)
return;
if (mPlayer.isPlaying()) {
- button.setImageResource(com.android.internal.R.drawable.ic_media_pause);
+ mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_pause);
} else {
- button.setImageResource(com.android.internal.R.drawable.ic_media_play);
+ mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_play);
}
}
@@ -516,7 +542,7 @@ public class MediaController extends FrameLayout {
if (mProgress != null) {
mProgress.setEnabled(enabled);
}
-
+ disableUnsupportedButtons();
super.setEnabled(enabled);
}
@@ -579,5 +605,8 @@ public class MediaController extends FrameLayout {
void seekTo(int pos);
boolean isPlaying();
int getBufferPercentage();
- };
+ boolean canPause();
+ boolean canSeekBackward();
+ boolean canSeekForward();
+ }
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 90fbb77..548dee9 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -817,6 +817,7 @@ public class PopupWindow {
* @param p the layout parameters of the popup's content view
*/
private void invokePopup(WindowManager.LayoutParams p) {
+ p.packageName = mContext.getPackageName();
mWindowManager.addView(mPopupView, p);
}
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index b179a13..2f28d9f 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -651,6 +651,7 @@ public class ProgressBar extends View {
if (mProgress > max) {
mProgress = max;
+ refreshProgress(R.id.progress, mProgress, false);
}
}
}
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index c9ace0a..11dab02 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -17,6 +17,7 @@
package android.widget;
import android.content.Context;
+import android.hardware.SensorManager;
import android.view.ViewConfiguration;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
@@ -79,9 +80,9 @@ public class Scroller {
mFinished = true;
mInterpolator = interpolator;
float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
- mDeceleration = 9.8f // g (m/s^2)
- * 39.37f // inch/meter
- * ppi // pixels per inch
+ mDeceleration = SensorManager.GRAVITY_EARTH // g (m/s^2)
+ * 39.37f // inch/meter
+ * ppi // pixels per inch
* ViewConfiguration.getScrollFriction();
}
@@ -132,6 +133,17 @@ public class Scroller {
}
/**
+ * @hide
+ * Returns the current velocity.
+ *
+ * @return The original velocity less the deceleration. Result may be
+ * negative.
+ */
+ public float getCurrVelocity() {
+ return mVelocity - mDeceleration * timePassed() / 2000.0f;
+ }
+
+ /**
* Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
@@ -347,7 +359,11 @@ public class Scroller {
}
/**
- *
+ * Stops the animation. Contrary to {@link #forceFinished(boolean)},
+ * aborting the animating cause the scroller to move to the final x and y
+ * position
+ *
+ * @see #forceFinished(boolean)
*/
public void abortAnimation() {
mCurrX = mFinalX;
@@ -356,10 +372,12 @@ public class Scroller {
}
/**
- * Extend the scroll animation. This allows a running animation to
- * scroll further and longer, when used with setFinalX() or setFinalY().
+ * Extend the scroll animation. This allows a running animation to scroll
+ * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
*
* @param extend Additional time to scroll in milliseconds.
+ * @see #setFinalX(int)
+ * @see #setFinalY(int)
*/
public void extendDuration(int extend) {
int passed = timePassed();
@@ -367,18 +385,37 @@ public class Scroller {
mDurationReciprocal = 1.0f / (float)mDuration;
mFinished = false;
}
-
+
+ /**
+ * Returns the time elapsed since the beginning of the scrolling.
+ *
+ * @return The elapsed time in milliseconds.
+ */
public int timePassed() {
return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
-
+
+ /**
+ * Sets the final position (X) for this scroller.
+ *
+ * @param newX The new X offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalY(int)
+ */
public void setFinalX(int newX) {
mFinalX = newX;
mDeltaX = mFinalX - mStartX;
mFinished = false;
}
- public void setFinalY(int newY) {
+ /**
+ * Sets the final position (Y) for this scroller.
+ *
+ * @param newY The new Y offset as an absolute distance from the origin.
+ * @see #extendDuration(int)
+ * @see #setFinalX(int)
+ */
+ public void setFinalY(int newY) {
mFinalY = newY;
mDeltaY = mFinalY - mStartY;
mFinished = false;
diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java
index c456f56..a1c65f0 100644
--- a/core/java/android/widget/SimpleCursorTreeAdapter.java
+++ b/core/java/android/widget/SimpleCursorTreeAdapter.java
@@ -26,9 +26,16 @@ import android.view.View;
* defined in an XML file. You can specify which columns you want, which views
* you want to display the columns, and the XML file that defines the appearance
* of these views. Separate XML files for child and groups are possible.
- * TextViews bind the values to their text property (see
- * {@link TextView#setText(CharSequence)}). ImageViews bind the values to their
- * image's Uri property (see {@link ImageView#setImageURI(android.net.Uri)}).
+ *
+ * Binding occurs in two phases. First, if a
+ * {@link android.widget.SimpleCursorTreeAdapter.ViewBinder} is available,
+ * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
+ * is invoked. If the returned value is true, binding has occurred. If the
+ * returned value is false and the view to bind is a TextView,
+ * {@link #setViewText(TextView, String)} is invoked. If the returned value
+ * is false and the view to bind is an ImageView,
+ * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
+ * binding can be found, an {@link IllegalStateException} is thrown.
*/
public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter {
/** The indices of columns that contain data to display for a group. */
@@ -48,6 +55,11 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter
private int[] mChildTo;
/**
+ * View binder, if supplied
+ */
+ private ViewBinder mViewBinder;
+
+ /**
* Constructor.
*
* @param context The context where the {@link ExpandableListView}
@@ -193,21 +205,53 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter
initFromColumns(childCursor, childFromNames, mChildFrom);
}
+ /**
+ * Returns the {@link ViewBinder} used to bind data to views.
+ *
+ * @return a ViewBinder or null if the binder does not exist
+ *
+ * @see #setViewBinder(android.widget.SimpleCursorTreeAdapter.ViewBinder)
+ */
+ public ViewBinder getViewBinder() {
+ return mViewBinder;
+ }
+
+ /**
+ * Sets the binder used to bind data to views.
+ *
+ * @param viewBinder the binder used to bind data to views, can be null to
+ * remove the existing binder
+ *
+ * @see #getViewBinder()
+ */
+ public void setViewBinder(ViewBinder viewBinder) {
+ mViewBinder = viewBinder;
+ }
+
private void bindView(View view, Context context, Cursor cursor, int[] from, int[] to) {
+ final ViewBinder binder = mViewBinder;
+
for (int i = 0; i < to.length; i++) {
View v = view.findViewById(to[i]);
if (v != null) {
- String text = cursor.getString(from[i]);
- if (text == null) {
- text = "";
+ boolean bound = false;
+ if (binder != null) {
+ bound = binder.setViewValue(v, cursor, from[i]);
}
- if (v instanceof TextView) {
- ((TextView) v).setText(text);
- } else if (v instanceof ImageView) {
- setViewImage((ImageView) v, text);
- } else {
- throw new IllegalStateException("SimpleCursorAdapter can bind values only to" +
- " TextView and ImageView!");
+
+ if (!bound) {
+ String text = cursor.getString(from[i]);
+ if (text == null) {
+ text = "";
+ }
+ if (v instanceof TextView) {
+ setViewText((TextView) v, text);
+ } else if (v instanceof ImageView) {
+ setViewImage((ImageView) v, text);
+ } else {
+ throw new IllegalStateException("SimpleCursorTreeAdapter can bind values" +
+ " only to TextView and ImageView!");
+ }
}
}
}
@@ -238,4 +282,48 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter
v.setImageURI(Uri.parse(value));
}
}
+
+ /**
+ * Called by bindView() to set the text for a TextView but only if
+ * there is no existing ViewBinder or if the existing ViewBinder cannot
+ * handle binding to an TextView.
+ *
+ * Intended to be overridden by Adapters that need to filter strings
+ * retrieved from the database.
+ *
+ * @param v TextView to receive text
+ * @param text the text to be set for the TextView
+ */
+ public void setViewText(TextView v, String text) {
+ v.setText(text);
+ }
+
+ /**
+ * This class can be used by external clients of SimpleCursorTreeAdapter
+ * to bind values from the Cursor to views.
+ *
+ * You should use this class to bind values from the Cursor to views
+ * that are not directly supported by SimpleCursorTreeAdapter or to
+ * change the way binding occurs for views supported by
+ * SimpleCursorTreeAdapter.
+ *
+ * @see SimpleCursorTreeAdapter#setViewImage(ImageView, String)
+ * @see SimpleCursorTreeAdapter#setViewText(TextView, String)
+ */
+ public static interface ViewBinder {
+ /**
+ * Binds the Cursor column defined by the specified index to the specified view.
+ *
+ * When binding is handled by this ViewBinder, this method must return true.
+ * If this method returns false, SimpleCursorTreeAdapter will attempts to handle
+ * the binding on its own.
+ *
+ * @param view the view to bind the data to
+ * @param cursor the cursor to get the data from
+ * @param columnIndex the column at which the data can be found in the cursor
+ *
+ * @return true if the data was bound to the view, false otherwise
+ */
+ boolean setViewValue(View view, Cursor cursor, int columnIndex);
+ }
}
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 103d44d..ee3b91e 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -68,7 +68,7 @@ public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchMode
initTabHost();
}
- private final void initTabHost() {
+ private void initTabHost() {
setFocusableInTouchMode(true);
setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
@@ -101,8 +101,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
throw new RuntimeException(
"Your TabHost must have a TabWidget whose id attribute is 'android.R.id.tabs'");
}
-
- // KeyListener to attach to all tabs. Detects non-navigation keys
+
+ // KeyListener to attach to all tabs. Detects non-navigation keys
// and relays them to the tab content.
mTabKeyListener = new OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -114,14 +114,14 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_ENTER:
return false;
-
+
}
mTabContent.requestFocus(View.FOCUS_FORWARD);
return mTabContent.dispatchKeyEvent(event);
}
-
+
};
-
+
mTabWidget.setTabSelectionListener(new TabWidget.OnTabSelectionChanged() {
public void onTabSelectionChanged(int tabIndex, boolean clicked) {
setCurrentTab(tabIndex);
@@ -134,7 +134,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
mTabContent = (FrameLayout) findViewById(com.android.internal.R.id.tabcontent);
if (mTabContent == null) {
throw new RuntimeException(
- "Your TabHost must have a FrameLayout whose id attribute is 'android.R.id.tabcontent'");
+ "Your TabHost must have a FrameLayout whose id attribute is "
+ + "'android.R.id.tabcontent'");
}
}
@@ -176,7 +177,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
if (!isInTouchMode) {
// leaving touch mode.. if nothing has focus, let's give it to
// the indicator of the current tab
- if (!mCurrentView.hasFocus() || mCurrentView.isFocused()) {
+ if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
}
}
@@ -283,7 +284,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
return true;
}
- return handled;
+ return handled;
}
@@ -312,7 +313,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
// Call the tab widget's focusCurrentTab(), instead of just
// selecting the tab.
mTabWidget.focusCurrentTab(mCurrentTab);
-
+
// tab content
mCurrentView = spec.mContentStrategy.getContentView();
@@ -367,7 +368,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
public interface TabContentFactory {
/**
* Callback to make the tab contents
- *
+ *
* @param tag
* Which tab was selected.
* @return The view to display the contents of the selected tab.
@@ -501,6 +502,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
View tabIndicator = inflater.inflate(R.layout.tab_indicator,
mTabWidget, // tab widget is the parent
false); // no inflate params
+ // TODO: Move this to xml when bug 2068024 is resolved.
+ tabIndicator.getBackground().setDither(true);
final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
tv.setText(mLabel);
@@ -528,6 +531,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
View tabIndicator = inflater.inflate(R.layout.tab_indicator,
mTabWidget, // tab widget is the parent
false); // no inflate params
+ // TODO: Move this to xml when bug 2068024 is resolved.
+ tabIndicator.getBackground().setDither(true);
final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
tv.setText(mLabel);
@@ -637,7 +642,7 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
}
}
mLaunchedView = wd;
-
+
// XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
// focus if none of their children have it. They need focus to be able to
// display menu items.
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index 47f5c6c..889f37f 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -30,7 +30,7 @@ import android.view.View.OnFocusChangeListener;
/**
- *
+ *
* Displays a list of tab labels representing each page in the parent's tab
* collection. The container object for this widget is
* {@link android.widget.TabHost TabHost}. When the user selects a tab, this
@@ -64,21 +64,36 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
super(context, attrs);
initTabWidget();
- TypedArray a =
+ TypedArray a =
context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.TabWidget,
defStyle, 0);
a.recycle();
}
-
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mStripMoved = true;
super.onSizeChanged(w, h, oldw, oldh);
}
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ // Always draw the selected tab last, so that drop shadows are drawn
+ // in the correct z-order.
+ if (i == childCount - 1) {
+ return mSelectedTab;
+ } else if (i >= mSelectedTab) {
+ return i + 1;
+ } else {
+ return i;
+ }
+ }
+
private void initTabWidget() {
setOrientation(LinearLayout.HORIZONTAL);
+ mGroupFlags |= FLAG_USE_CHILD_DRAWING_ORDER;
+
mBottomLeftStrip = mContext.getResources().getDrawable(
com.android.internal.R.drawable.tab_bottom_left);
mBottomRightStrip = mContext.getResources().getDrawable(
@@ -156,7 +171,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
}
super.childDrawableStateChanged(child);
}
-
+
@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
@@ -169,17 +184,17 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
}
View selectedChild = getChildTabViewAt(mSelectedTab);
-
+
mBottomLeftStrip.setState(selectedChild.getDrawableState());
mBottomRightStrip.setState(selectedChild.getDrawableState());
-
+
if (mStripMoved) {
Rect selBounds = new Rect(); // Bounds of the selected tab indicator
selBounds.left = selectedChild.getLeft();
selBounds.right = selectedChild.getRight();
final int myHeight = getHeight();
mBottomLeftStrip.setBounds(
- Math.min(0, selBounds.left
+ Math.min(0, selBounds.left
- mBottomLeftStrip.getIntrinsicWidth()),
myHeight - mBottomLeftStrip.getIntrinsicHeight(),
selBounds.left,
@@ -187,12 +202,12 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
mBottomRightStrip.setBounds(
selBounds.right,
myHeight - mBottomRightStrip.getIntrinsicHeight(),
- Math.max(getWidth(),
+ Math.max(getWidth(),
selBounds.right + mBottomRightStrip.getIntrinsicWidth()),
myHeight);
mStripMoved = false;
}
-
+
mBottomLeftStrip.draw(canvas);
mBottomRightStrip.draw(canvas);
}
@@ -202,26 +217,26 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
* This method is used to bring a tab to the front of the Widget,
* and is used to post to the rest of the UI that a different tab
* has been brought to the foreground.
- *
- * Note, this is separate from the traditional "focus" that is
- * employed from the view logic.
- *
- * For instance, if we have a list in a tabbed view, a user may be
- * navigating up and down the list, moving the UI focus (orange
- * highlighting) through the list items. The cursor movement does
- * not effect the "selected" tab though, because what is being
- * scrolled through is all on the same tab. The selected tab only
- * changes when we navigate between tabs (moving from the list view
+ *
+ * Note, this is separate from the traditional "focus" that is
+ * employed from the view logic.
+ *
+ * For instance, if we have a list in a tabbed view, a user may be
+ * navigating up and down the list, moving the UI focus (orange
+ * highlighting) through the list items. The cursor movement does
+ * not effect the "selected" tab though, because what is being
+ * scrolled through is all on the same tab. The selected tab only
+ * changes when we navigate between tabs (moving from the list view
* to the next tabbed view, in this example).
- *
+ *
* To move both the focus AND the selected tab at once, please use
- * {@link #setCurrentTab}. Normally, the view logic takes care of
- * adjusting the focus, so unless you're circumventing the UI,
+ * {@link #setCurrentTab}. Normally, the view logic takes care of
+ * adjusting the focus, so unless you're circumventing the UI,
* you'll probably just focus your interest here.
- *
+ *
* @param index The tab that you want to indicate as the selected
* tab (tab brought to the front of the widget)
- *
+ *
* @see #focusCurrentTab
*/
public void setCurrentTab(int index) {
@@ -234,19 +249,19 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
getChildTabViewAt(mSelectedTab).setSelected(true);
mStripMoved = true;
}
-
+
/**
* Sets the current tab and focuses the UI on it.
- * This method makes sure that the focused tab matches the selected
- * tab, normally at {@link #setCurrentTab}. Normally this would not
- * be an issue if we go through the UI, since the UI is responsible
- * for calling TabWidget.onFocusChanged(), but in the case where we
- * are selecting the tab programmatically, we'll need to make sure
+ * This method makes sure that the focused tab matches the selected
+ * tab, normally at {@link #setCurrentTab}. Normally this would not
+ * be an issue if we go through the UI, since the UI is responsible
+ * for calling TabWidget.onFocusChanged(), but in the case where we
+ * are selecting the tab programmatically, we'll need to make sure
* focus keeps up.
- *
- * @param index The tab that you want focused (highlighted in orange)
+ *
+ * @param index The tab that you want focused (highlighted in orange)
* and selected (tab brought to the front of the widget)
- *
+ *
* @see #setCurrentTab
*/
public void focusCurrentTab(int index) {
@@ -254,18 +269,18 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
// set the tab
setCurrentTab(index);
-
+
// change the focus if applicable.
if (oldTab != index) {
getChildTabViewAt(index).requestFocus();
}
}
-
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
int count = getTabCount();
-
+
for (int i = 0; i < count; i++) {
View child = getChildTabViewAt(i);
child.setEnabled(enabled);
@@ -318,7 +333,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
getChildTabViewAt(mSelectedTab).requestFocus();
return;
}
-
+
if (hasFocus) {
int i = 0;
int numTabs = getTabCount();
@@ -354,7 +369,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener {
/**
* Informs the TabHost which tab was selected. It also indicates
* if the tab was clicked/pressed or just focused into.
- *
+ *
* @param tabIndex index of the tab that was selected
* @param clicked whether the selection changed due to a touch/click
* or due to focus entering the tab through navigation. Pass true
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 260799e..a611d5a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -327,6 +327,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mText = "";
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.density = getResources().getDisplayMetrics().density;
// If we get the paint from the skin, we should set it to left, since
// the layout always wants it to be left.
// mTextPaint.setTextAlign(Paint.Align.LEFT);
@@ -5769,7 +5770,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* Causes words in the text that are longer than the view is wide
* to be ellipsized instead of broken in the middle. You may also
* want to {@link #setSingleLine} or {@link #setHorizontallyScrolling}
- * to constrain the text toa single line. Use <code>null</code>
+ * to constrain the text to a single line. Use <code>null</code>
* to turn off ellipsizing.
*
* @attr ref android.R.styleable#TextView_ellipsize
@@ -6916,6 +6917,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ boolean hasLetter = false;
+ for (int i = start; i < end; i++) {
+ if (Character.isLetter(mTransformed.charAt(i))) {
+ hasLetter = true;
+ break;
+ }
+ }
+ if (!hasLetter) {
+ return null;
+ }
+
if (start == end) {
return null;
}
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 20dd8a6..549f984 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -23,6 +23,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.media.AudioManager;
import android.media.MediaPlayer;
+import android.media.Metadata;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
@@ -34,7 +35,7 @@ import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
-import android.widget.MediaController.MediaPlayerControl;
+import android.widget.MediaController.*;
import java.io.IOException;
@@ -51,11 +52,26 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
private Uri mUri;
private int mDuration;
+ // all possible internal states
+ private static final int STATE_ERROR = -1;
+ private static final int STATE_IDLE = 0;
+ private static final int STATE_PREPARING = 1;
+ private static final int STATE_PREPARED = 2;
+ private static final int STATE_PLAYING = 3;
+ private static final int STATE_PAUSED = 4;
+ private static final int STATE_PLAYBACK_COMPLETED = 5;
+
+ // mCurrentState is a VideoView object's current state.
+ // mTargetState is the state that a method caller intends to reach.
+ // For instance, regardless the VideoView object's current state,
+ // calling pause() intends to bring the object to a target state
+ // of STATE_PAUSED.
+ private int mCurrentState = STATE_IDLE;
+ private int mTargetState = STATE_IDLE;
+
// All the stuff we need for playing and showing a video
private SurfaceHolder mSurfaceHolder = null;
private MediaPlayer mMediaPlayer = null;
- private boolean mIsPrepared;
- private boolean mIsPlaybackCompleted;
private int mVideoWidth;
private int mVideoHeight;
private int mSurfaceWidth;
@@ -65,8 +81,10 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
private MediaPlayer.OnPreparedListener mOnPreparedListener;
private int mCurrentBufferPercentage;
private OnErrorListener mOnErrorListener;
- private boolean mStartWhenPrepared;
- private int mSeekWhenPrepared;
+ private int mSeekWhenPrepared; // recording the seek position while preparing
+ private boolean mCanPause;
+ private boolean mCanSeekBack;
+ private boolean mCanSeekForward;
public VideoView(Context context) {
super(context);
@@ -80,7 +98,6 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
public VideoView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
-
initVideoView();
}
@@ -143,6 +160,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
}
public void setVideoPath(String path) {
@@ -151,7 +170,6 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
public void setVideoURI(Uri uri) {
mUri = uri;
- mStartWhenPrepared = false;
mSeekWhenPrepared = 0;
openVideo();
requestLayout();
@@ -163,6 +181,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
+ mCurrentState = STATE_IDLE;
+ mTargetState = STATE_IDLE;
}
}
@@ -176,18 +196,14 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
Intent i = new Intent("com.android.music.musicservicecommand");
i.putExtra("command", "pause");
mContext.sendBroadcast(i);
-
- if (mMediaPlayer != null) {
- mMediaPlayer.reset();
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
+
+ // we shouldn't clear the target state, because somebody might have
+ // called start() previously
+ release(false);
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(mPreparedListener);
mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);
- mIsPrepared = false;
- Log.v(TAG, "reset duration to -1 in openVideo");
mDuration = -1;
mMediaPlayer.setOnCompletionListener(mCompletionListener);
mMediaPlayer.setOnErrorListener(mErrorListener);
@@ -198,12 +214,19 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setScreenOnWhilePlaying(true);
mMediaPlayer.prepareAsync();
+ // we don't set the target state here either, but preserve the
+ // target state that was there before.
+ mCurrentState = STATE_PREPARING;
attachMediaController();
} catch (IOException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
return;
} catch (IllegalArgumentException ex) {
Log.w(TAG, "Unable to open content: " + mUri, ex);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
return;
}
}
@@ -222,7 +245,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
View anchorView = this.getParent() instanceof View ?
(View)this.getParent() : this;
mMediaController.setAnchorView(anchorView);
- mMediaController.setEnabled(mIsPrepared);
+ mMediaController.setEnabled(isInPlaybackState());
}
}
@@ -239,8 +262,23 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
- // briefly show the mediacontroller
- mIsPrepared = true;
+ mCurrentState = STATE_PREPARED;
+
+ // Get the capabilities of the player for this stream
+ Metadata data = mp.getMetadata(MediaPlayer.METADATA_ALL,
+ MediaPlayer.BYPASS_METADATA_FILTER);
+
+ if (data != null) {
+ mCanPause = !data.has(Metadata.PAUSE_AVAILABLE)
+ || data.getBoolean(Metadata.PAUSE_AVAILABLE);
+ mCanSeekBack = !data.has(Metadata.SEEK_BACKWARD_AVAILABLE)
+ || data.getBoolean(Metadata.SEEK_BACKWARD_AVAILABLE);
+ mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE)
+ || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE);
+ } else {
+ mCanPause = mCanSeekForward = mCanSeekForward = true;
+ }
+
if (mOnPreparedListener != null) {
mOnPreparedListener.onPrepared(mMediaPlayer);
}
@@ -249,6 +287,11 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
mVideoWidth = mp.getVideoWidth();
mVideoHeight = mp.getVideoHeight();
+
+ int seekToPosition = mSeekWhenPrepared; // mSeekWhenPrepared may be changed after seekTo() call
+ if (seekToPosition != 0) {
+ seekTo(seekToPosition);
+ }
if (mVideoWidth != 0 && mVideoHeight != 0) {
//Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);
getHolder().setFixedSize(mVideoWidth, mVideoHeight);
@@ -256,18 +299,13 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
// We didn't actually change the size (it was already at the size
// we need), so we won't get a "surface changed" callback, so
// start the video here instead of in the callback.
- if (mSeekWhenPrepared != 0) {
- mMediaPlayer.seekTo(mSeekWhenPrepared);
- mSeekWhenPrepared = 0;
- }
- if (mStartWhenPrepared) {
+ if (mTargetState == STATE_PLAYING) {
start();
- mStartWhenPrepared = false;
if (mMediaController != null) {
mMediaController.show();
}
} else if (!isPlaying() &&
- (mSeekWhenPrepared != 0 || getCurrentPosition() > 0)) {
+ (seekToPosition != 0 || getCurrentPosition() > 0)) {
if (mMediaController != null) {
// Show the media controls when we're paused into a video and make 'em stick.
mMediaController.show(0);
@@ -277,13 +315,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
} else {
// We don't know the video size yet, but should start anyway.
// The video size might be reported to us later.
- if (mSeekWhenPrepared != 0) {
- mMediaPlayer.seekTo(mSeekWhenPrepared);
- mSeekWhenPrepared = 0;
- }
- if (mStartWhenPrepared) {
+ if (mTargetState == STATE_PLAYING) {
start();
- mStartWhenPrepared = false;
}
}
}
@@ -292,7 +325,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
private MediaPlayer.OnCompletionListener mCompletionListener =
new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
- mIsPlaybackCompleted = true;
+ mCurrentState = STATE_PLAYBACK_COMPLETED;
+ mTargetState = STATE_PLAYBACK_COMPLETED;
if (mMediaController != null) {
mMediaController.hide();
}
@@ -306,6 +340,8 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {
Log.d(TAG, "Error: " + framework_err + "," + impl_err);
+ mCurrentState = STATE_ERROR;
+ mTargetState = STATE_ERROR;
if (mMediaController != null) {
mMediaController.hide();
}
@@ -402,14 +438,13 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
{
mSurfaceWidth = w;
mSurfaceHeight = h;
- if (mMediaPlayer != null && mIsPrepared && mVideoWidth == w && mVideoHeight == h) {
+ boolean isValidState = (mTargetState == STATE_PLAYING);
+ boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);
+ if (mMediaPlayer != null && isValidState && hasValidSize) {
if (mSeekWhenPrepared != 0) {
- mMediaPlayer.seekTo(mSeekWhenPrepared);
- mSeekWhenPrepared = 0;
+ seekTo(mSeekWhenPrepared);
}
- if (!mIsPlaybackCompleted) {
- start();
- }
+ start();
if (mMediaController != null) {
mMediaController.show();
}
@@ -427,17 +462,28 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
// after we return from this we can't use the surface any more
mSurfaceHolder = null;
if (mMediaController != null) mMediaController.hide();
- if (mMediaPlayer != null) {
- mMediaPlayer.reset();
- mMediaPlayer.release();
- mMediaPlayer = null;
- }
+ release(true);
}
};
+ /*
+ * release the media player in any state
+ */
+ private void release(boolean cleartargetstate) {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.reset();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ mCurrentState = STATE_IDLE;
+ if (cleartargetstate) {
+ mTargetState = STATE_IDLE;
+ }
+ }
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
- if (mIsPrepared && mMediaPlayer != null && mMediaController != null) {
+ if (isInPlaybackState() && mMediaController != null) {
toggleMediaControlsVisiblity();
}
return false;
@@ -445,7 +491,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
@Override
public boolean onTrackballEvent(MotionEvent ev) {
- if (mIsPrepared && mMediaPlayer != null && mMediaController != null) {
+ if (isInPlaybackState() && mMediaController != null) {
toggleMediaControlsVisiblity();
}
return false;
@@ -454,15 +500,13 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
- if (mIsPrepared &&
- keyCode != KeyEvent.KEYCODE_BACK &&
- keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
- keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
- keyCode != KeyEvent.KEYCODE_MENU &&
- keyCode != KeyEvent.KEYCODE_CALL &&
- keyCode != KeyEvent.KEYCODE_ENDCALL &&
- mMediaPlayer != null &&
- mMediaController != null) {
+ boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_UP &&
+ keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&
+ keyCode != KeyEvent.KEYCODE_MENU &&
+ keyCode != KeyEvent.KEYCODE_CALL &&
+ keyCode != KeyEvent.KEYCODE_ENDCALL;
+ if (isInPlaybackState() && isKeyCodeSupported && mMediaController != null) {
if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||
keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
if (mMediaPlayer.isPlaying()) {
@@ -494,26 +538,26 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
public void start() {
- mIsPlaybackCompleted = false;
- if (mMediaPlayer != null && mIsPrepared) {
- mMediaPlayer.start();
- mStartWhenPrepared = false;
- } else {
- mStartWhenPrepared = true;
+ if (isInPlaybackState()) {
+ mMediaPlayer.start();
+ mCurrentState = STATE_PLAYING;
}
+ mTargetState = STATE_PLAYING;
}
public void pause() {
- if (mMediaPlayer != null && mIsPrepared) {
+ if (isInPlaybackState()) {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
+ mCurrentState = STATE_PAUSED;
}
}
- mStartWhenPrepared = false;
+ mTargetState = STATE_PAUSED;
}
+ // cache duration as mDuration for faster access
public int getDuration() {
- if (mMediaPlayer != null && mIsPrepared) {
+ if (isInPlaybackState()) {
if (mDuration > 0) {
return mDuration;
}
@@ -525,25 +569,23 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
public int getCurrentPosition() {
- if (mMediaPlayer != null && mIsPrepared) {
+ if (isInPlaybackState()) {
return mMediaPlayer.getCurrentPosition();
}
return 0;
}
public void seekTo(int msec) {
- if (mMediaPlayer != null && mIsPrepared) {
+ if (isInPlaybackState()) {
mMediaPlayer.seekTo(msec);
+ mSeekWhenPrepared = 0;
} else {
mSeekWhenPrepared = msec;
}
}
public boolean isPlaying() {
- if (mMediaPlayer != null && mIsPrepared) {
- return mMediaPlayer.isPlaying();
- }
- return false;
+ return isInPlaybackState() && mMediaPlayer.isPlaying();
}
public int getBufferPercentage() {
@@ -552,4 +594,23 @@ public class VideoView extends SurfaceView implements MediaPlayerControl {
}
return 0;
}
+
+ private boolean isInPlaybackState() {
+ return (mMediaPlayer != null &&
+ mCurrentState != STATE_ERROR &&
+ mCurrentState != STATE_IDLE &&
+ mCurrentState != STATE_PREPARING);
+ }
+
+ public boolean canPause() {
+ return mCanPause;
+ }
+
+ public boolean canSeekBackward() {
+ return mCanSeekBack;
+ }
+
+ public boolean canSeekForward() {
+ return mCanSeekForward;
+ }
}
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1697df2..9ed4dd8 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -17,17 +17,40 @@
package com.android.internal.app;
import android.content.Intent;
+import android.content.pm.ResolveInfo;
import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
public class ChooserActivity extends ResolverActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
- Intent target = (Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ if (!(targetParcelable instanceof Intent)) {
+ Log.w("ChooseActivity", "Target is not an intent: " + targetParcelable);
+ finish();
+ return;
+ }
+ Intent target = (Intent)targetParcelable;
CharSequence title = intent.getCharSequenceExtra(Intent.EXTRA_TITLE);
if (title == null) {
title = getResources().getText(com.android.internal.R.string.chooseActivity);
}
- super.onCreate(savedInstanceState, target, title, false);
+ Parcelable[] pa = intent.getParcelableArrayExtra(Intent.EXTRA_INITIAL_INTENTS);
+ Intent[] initialIntents = null;
+ if (pa != null) {
+ initialIntents = new Intent[pa.length];
+ for (int i=0; i<pa.length; i++) {
+ if (!(pa[i] instanceof Intent)) {
+ Log.w("ChooseActivity", "Initial intent #" + i
+ + " not an Intent: " + pa[i]);
+ finish();
+ return;
+ }
+ initialIntents[i] = (Intent)pa[i];
+ }
+ }
+ super.onCreate(savedInstanceState, target, title, initialIntents, false);
}
}
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
new file mode 100755
index 0000000..98fb236
--- /dev/null
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 Google Inc.
+ *
+ * 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.internal.app;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IMountService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.widget.Toast;
+import android.util.Log;
+import android.location.LocationManager;
+import com.android.internal.location.GpsLocationProvider;
+import com.android.internal.location.GpsNetInitiatedHandler;
+
+/**
+ * This activity is shown to the user for him/her to accept or deny network-initiated
+ * requests. It uses the alert dialog style. It will be launched from a notification.
+ */
+public class NetInitiatedActivity extends AlertActivity implements DialogInterface.OnClickListener {
+
+ private static final String TAG = "NetInitiatedActivity";
+
+ private static final boolean DEBUG = true;
+ private static final boolean VERBOSE = false;
+
+ private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+ private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON2;
+
+ // Dialog button text
+ public static final String BUTTON_TEXT_ACCEPT = "Accept";
+ public static final String BUTTON_TEXT_DENY = "Deny";
+
+ // Received ID from intent, -1 when no notification is in progress
+ private int notificationId = -1;
+
+ /** Used to detect when NI request is received */
+ private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.d(TAG, "NetInitiatedReceiver onReceive: " + intent.getAction());
+ if (intent.getAction() == GpsNetInitiatedHandler.ACTION_NI_VERIFY) {
+ handleNIVerify(intent);
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Set up the "dialog"
+ final Intent intent = getIntent();
+ final AlertController.AlertParams p = mAlertParams;
+ p.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+ p.mTitle = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_TITLE);
+ p.mMessage = intent.getStringExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_MESSAGE);
+ p.mPositiveButtonText = BUTTON_TEXT_ACCEPT;
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = BUTTON_TEXT_DENY;
+ p.mNegativeButtonListener = this;
+
+ notificationId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
+ if (DEBUG) Log.d(TAG, "onCreate, notifId: " + notificationId);
+
+ setupAlert();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (DEBUG) Log.d(TAG, "onResume");
+ registerReceiver(mNetInitiatedReceiver, new IntentFilter(GpsNetInitiatedHandler.ACTION_NI_VERIFY));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (DEBUG) Log.d(TAG, "onPause");
+ unregisterReceiver(mNetInitiatedReceiver);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == POSITIVE_BUTTON) {
+ sendUserResponse(GpsNetInitiatedHandler.GPS_NI_RESPONSE_ACCEPT);
+ }
+ if (which == NEGATIVE_BUTTON) {
+ sendUserResponse(GpsNetInitiatedHandler.GPS_NI_RESPONSE_DENY);
+ }
+
+ // No matter what, finish the activity
+ finish();
+ notificationId = -1;
+ }
+
+ // Respond to NI Handler under GpsLocationProvider, 1 = accept, 2 = deny
+ private void sendUserResponse(int response) {
+ if (DEBUG) Log.d(TAG, "sendUserResponse, response: " + response);
+ LocationManager locationManager = (LocationManager)
+ this.getSystemService(Context.LOCATION_SERVICE);
+ locationManager.sendNiResponse(notificationId, response);
+ }
+
+ private void handleNIVerify(Intent intent) {
+ int notifId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
+ notificationId = notifId;
+
+ if (DEBUG) Log.d(TAG, "handleNIVerify action: " + intent.getAction());
+ }
+
+ private void showNIError() {
+ Toast.makeText(this, "NI error" /* com.android.internal.R.string.usb_storage_error_message */,
+ Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index e907a04..7466cc4 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -23,8 +23,10 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.LabeledIntent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.PatternMatcher;
@@ -61,11 +63,11 @@ public class ResolverActivity extends AlertActivity implements
protected void onCreate(Bundle savedInstanceState) {
onCreate(savedInstanceState, new Intent(getIntent()),
getResources().getText(com.android.internal.R.string.whichApplication),
- true);
+ null, true);
}
protected void onCreate(Bundle savedInstanceState, Intent intent,
- CharSequence title, boolean alwaysUseOption) {
+ CharSequence title, Intent[] initialIntents, boolean alwaysUseOption) {
super.onCreate(savedInstanceState);
mPm = getPackageManager();
intent.setComponent(null);
@@ -86,7 +88,7 @@ public class ResolverActivity extends AlertActivity implements
com.android.internal.R.id.clearDefaultHint);
mClearDefaultHint.setVisibility(View.GONE);
}
- mAdapter = new ResolveListAdapter(this, intent);
+ mAdapter = new ResolveListAdapter(this, intent, initialIntents);
if (mAdapter.getCount() > 1) {
ap.mAdapter = mAdapter;
} else if (mAdapter.getCount() == 1) {
@@ -185,12 +187,16 @@ public class ResolverActivity extends AlertActivity implements
private final class DisplayResolveInfo {
ResolveInfo ri;
CharSequence displayLabel;
+ Drawable displayIcon;
CharSequence extendedInfo;
+ Intent origIntent;
- DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel, CharSequence pInfo) {
+ DisplayResolveInfo(ResolveInfo pri, CharSequence pLabel,
+ CharSequence pInfo, Intent pOrigIntent) {
ri = pri;
displayLabel = pLabel;
extendedInfo = pInfo;
+ origIntent = pOrigIntent;
}
}
@@ -200,7 +206,8 @@ public class ResolverActivity extends AlertActivity implements
private List<DisplayResolveInfo> mList;
- public ResolveListAdapter(Context context, Intent intent) {
+ public ResolveListAdapter(Context context, Intent intent,
+ Intent[] initialIntents) {
mIntent = new Intent(intent);
mIntent.setComponent(null);
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -234,9 +241,39 @@ public class ResolverActivity extends AlertActivity implements
new ResolveInfo.DisplayNameComparator(mPm);
Collections.sort(rList, rComparator);
}
+
+ mList = new ArrayList<DisplayResolveInfo>();
+
+ // First put the initial items at the top.
+ if (initialIntents != null) {
+ for (int i=0; i<initialIntents.length; i++) {
+ Intent ii = initialIntents[i];
+ if (ii == null) {
+ continue;
+ }
+ ActivityInfo ai = ii.resolveActivityInfo(
+ getPackageManager(), 0);
+ if (ai == null) {
+ Log.w("ResolverActivity", "No activity found for "
+ + ii);
+ continue;
+ }
+ ResolveInfo ri = new ResolveInfo();
+ ri.activityInfo = ai;
+ if (ii instanceof LabeledIntent) {
+ LabeledIntent li = (LabeledIntent)ii;
+ ri.resolvePackageName = li.getSourcePackage();
+ ri.labelRes = li.getLabelResource();
+ ri.nonLocalizedLabel = li.getNonLocalizedLabel();
+ ri.icon = li.getIconResource();
+ }
+ mList.add(new DisplayResolveInfo(ri,
+ ri.loadLabel(getPackageManager()), null, ii));
+ }
+ }
+
// Check for applications with same name and use application name or
// package name if necessary
- mList = new ArrayList<DisplayResolveInfo>();
r0 = rList.get(0);
int start = 0;
CharSequence r0Label = r0.loadLabel(mPm);
@@ -268,7 +305,7 @@ public class ResolverActivity extends AlertActivity implements
int num = end - start+1;
if (num == 1) {
// No duplicate labels. Use label for entry at start
- mList.add(new DisplayResolveInfo(ro, roLabel, null));
+ mList.add(new DisplayResolveInfo(ro, roLabel, null, null));
} else {
boolean usePkg = false;
CharSequence startApp = ro.activityInfo.applicationInfo.loadLabel(mPm);
@@ -298,11 +335,11 @@ public class ResolverActivity extends AlertActivity implements
if (usePkg) {
// Use application name for all entries from start to end-1
mList.add(new DisplayResolveInfo(add, roLabel,
- add.activityInfo.packageName));
+ add.activityInfo.packageName, null));
} else {
// Use package name for all entries from start to end-1
mList.add(new DisplayResolveInfo(add, roLabel,
- add.activityInfo.applicationInfo.loadLabel(mPm)));
+ add.activityInfo.applicationInfo.loadLabel(mPm), null));
}
}
}
@@ -321,10 +358,13 @@ public class ResolverActivity extends AlertActivity implements
return null;
}
- Intent intent = new Intent(mIntent);
+ DisplayResolveInfo dri = mList.get(position);
+
+ Intent intent = new Intent(dri.origIntent != null
+ ? dri.origIntent : mIntent);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
|Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
- ActivityInfo ai = mList.get(position).ri.activityInfo;
+ ActivityInfo ai = dri.ri.activityInfo;
intent.setComponent(new ComponentName(
ai.applicationInfo.packageName, ai.name));
return intent;
@@ -365,7 +405,10 @@ public class ResolverActivity extends AlertActivity implements
} else {
text2.setVisibility(View.GONE);
}
- icon.setImageDrawable(info.ri.loadIcon(mPm));
+ if (info.displayIcon == null) {
+ info.displayIcon = info.ri.loadIcon(mPm);
+ }
+ icon.setImageDrawable(info.displayIcon);
}
}
diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java
index 77d6e20..9e1f325 100644
--- a/core/java/com/android/internal/app/ShutdownThread.java
+++ b/core/java/com/android/internal/app/ShutdownThread.java
@@ -21,8 +21,8 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.ProgressDialog;
import android.app.AlertDialog;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.IBluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.IBluetooth;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -179,13 +179,13 @@ public final class ShutdownThread extends Thread {
final ITelephony phone =
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
- final IBluetoothDevice bluetooth =
- IBluetoothDevice.Stub.asInterface(ServiceManager.checkService(
+ final IBluetooth bluetooth =
+ IBluetooth.Stub.asInterface(ServiceManager.checkService(
Context.BLUETOOTH_SERVICE));
try {
bluetoothOff = bluetooth == null ||
- bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF;
+ bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
if (!bluetoothOff) {
Log.w(TAG, "Disabling Bluetooth...");
bluetooth.disable(false); // disable but don't persist new state
@@ -213,7 +213,7 @@ public final class ShutdownThread extends Thread {
if (!bluetoothOff) {
try {
bluetoothOff =
- bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF;
+ bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
} catch (RemoteException ex) {
Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
bluetoothOff = true;
diff --git a/core/java/com/android/internal/backup/SystemBackupAgent.java b/core/java/com/android/internal/backup/SystemBackupAgent.java
deleted file mode 100644
index 6b396d7..0000000
--- a/core/java/com/android/internal/backup/SystemBackupAgent.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.backup;
-
-import android.backup.AbsoluteFileBackupHelper;
-import android.backup.BackupHelperAgent;
-
-/**
- * Backup agent for various system-managed data
- */
-public class SystemBackupAgent extends BackupHelperAgent {
- // the set of files that we back up whole, as absolute paths
- String[] mFiles = {
- /* WallpaperService.WALLPAPER_FILE */
- "/data/data/com.android.settings/files/wallpaper",
- };
-
- public void onCreate() {
- addHelper("system_files", new AbsoluteFileBackupHelper(this, mFiles));
- }
-}
diff --git a/core/java/com/android/internal/content/SyncStateContentProviderHelper.java b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
new file mode 100644
index 0000000..cd6a9a1
--- /dev/null
+++ b/core/java/com/android/internal/content/SyncStateContentProviderHelper.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.content;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.accounts.Account;
+import android.content.ContentValues;
+import android.provider.SyncStateContract;
+
+/**
+ * Extends the schema of a ContentProvider to include the _sync_state table
+ * and implements query/insert/update/delete to access that table using the
+ * authority "syncstate". This can be used to store the sync state for a
+ * set of accounts.
+ *
+ * @hide
+ */
+public class SyncStateContentProviderHelper {
+ private static final String SELECT_BY_ACCOUNT =
+ SyncStateContract.Columns.ACCOUNT_NAME + "=? AND "
+ + SyncStateContract.Columns.ACCOUNT_TYPE + "=?";
+
+ private static final String SYNC_STATE_TABLE = "_sync_state";
+ private static final String SYNC_STATE_META_TABLE = "_sync_state_metadata";
+ private static final String SYNC_STATE_META_VERSION_COLUMN = "version";
+
+ private static long DB_VERSION = 1;
+
+ private static final String[] ACCOUNT_PROJECTION =
+ new String[]{SyncStateContract.Columns.ACCOUNT_NAME,
+ SyncStateContract.Columns.ACCOUNT_TYPE};
+
+ public static final String PATH = "syncstate";
+
+ public void createDatabase(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_TABLE);
+ db.execSQL("CREATE TABLE " + SYNC_STATE_TABLE + " ("
+ + SyncStateContract.Columns._ID + " INTEGER PRIMARY KEY,"
+ + SyncStateContract.Columns.ACCOUNT_NAME + " TEXT NOT NULL,"
+ + SyncStateContract.Columns.ACCOUNT_TYPE + " TEXT NOT NULL,"
+ + SyncStateContract.Columns.DATA + " TEXT,"
+ + "UNIQUE(" + SyncStateContract.Columns.ACCOUNT_NAME + ", "
+ + SyncStateContract.Columns.ACCOUNT_TYPE + "));");
+
+ db.execSQL("DROP TABLE IF EXISTS " + SYNC_STATE_META_TABLE);
+ db.execSQL("CREATE TABLE " + SYNC_STATE_META_TABLE + " ("
+ + SYNC_STATE_META_VERSION_COLUMN + " INTEGER);");
+ ContentValues values = new ContentValues();
+ values.put(SYNC_STATE_META_VERSION_COLUMN, DB_VERSION);
+ db.insert(SYNC_STATE_META_TABLE, SYNC_STATE_META_VERSION_COLUMN, values);
+ }
+
+ public void onDatabaseOpened(SQLiteDatabase db) {
+ long version = DatabaseUtils.longForQuery(db,
+ "SELECT " + SYNC_STATE_META_VERSION_COLUMN + " FROM " + SYNC_STATE_META_TABLE,
+ null);
+ if (version != DB_VERSION) {
+ createDatabase(db);
+ }
+ }
+
+ public Cursor query(SQLiteDatabase db, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder) {
+ return db.query(SYNC_STATE_TABLE, projection, selection, selectionArgs,
+ null, null, sortOrder);
+ }
+
+ public long insert(SQLiteDatabase db, ContentValues values) {
+ return db.replace(SYNC_STATE_TABLE, SyncStateContract.Columns.ACCOUNT_NAME, values);
+ }
+
+ public int delete(SQLiteDatabase db, String userWhere, String[] whereArgs) {
+ return db.delete(SYNC_STATE_TABLE, userWhere, whereArgs);
+ }
+
+ public int update(SQLiteDatabase db, ContentValues values,
+ String selection, String[] selectionArgs) {
+ return db.update(SYNC_STATE_TABLE, values, selection, selectionArgs);
+ }
+
+ public void update(SQLiteDatabase db, long rowId, Object data) {
+ db.execSQL("UPDATE " + SYNC_STATE_TABLE
+ + " SET " + SyncStateContract.Columns.DATA + "=?"
+ + " WHERE " + SyncStateContract.Columns._ID + "=" + rowId,
+ new Object[]{data});
+ }
+
+ public void onAccountsChanged(SQLiteDatabase db, Account[] accounts) {
+ Cursor c = db.query(SYNC_STATE_TABLE, ACCOUNT_PROJECTION, null, null, null, null, null);
+ try {
+ while (c.moveToNext()) {
+ final String accountName = c.getString(0);
+ final String accountType = c.getString(1);
+ Account account = new Account(accountName, accountType);
+ if (!ArrayUtils.contains(accounts, account)) {
+ db.delete(SYNC_STATE_TABLE, SELECT_BY_ACCOUNT,
+ new String[]{accountName, accountType});
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a449e5f..2da72df 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -978,10 +978,13 @@ public final class BatteryStatsImpl extends BatteryStats {
} else if (mBtHeadset != null) {
return getCurrentBluetoothPingCount() - mBluetoothPingStart;
}
- return -1;
+ return 0;
}
public void setBtHeadset(BluetoothHeadset headset) {
+ if (headset != null && mBtHeadset == null && isOnBattery() && mBluetoothPingStart == -1) {
+ mBluetoothPingStart = getCurrentBluetoothPingCount();
+ }
mBtHeadset = headset;
}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index 932555d..5825024 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -93,6 +93,18 @@ public class HandlerCaller {
mH.sendMessage(msg);
}
+ public boolean hasMessages(int what) {
+ return mH.hasMessages(what);
+ }
+
+ public void removeMessages(int what) {
+ mH.removeMessages(what);
+ }
+
+ public void removeMessages(int what, Object obj) {
+ mH.removeMessages(what, obj);
+ }
+
public void sendMessage(Message msg) {
mH.sendMessage(msg);
}
@@ -132,6 +144,14 @@ public class HandlerCaller {
return mH.obtainMessage(what, arg1, arg2, arg3);
}
+ public Message obtainMessageIIOO(int what, int arg1, int arg2,
+ Object arg3, Object arg4) {
+ SomeArgs args = obtainArgs();
+ args.arg1 = arg3;
+ args.arg2 = arg4;
+ return mH.obtainMessage(what, arg1, arg2, args);
+ }
+
public Message obtainMessageIOO(int what, int arg1, Object arg2, Object arg3) {
SomeArgs args = obtainArgs();
args.arg1 = arg2;
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 8486272..4e6f9ca 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -83,7 +83,7 @@ public class RuntimeInit {
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
int hasQwerty = getQwertyKeyboard();
-
+
if (Config.LOGV) Log.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
if (hasQwerty == 1) {
System.setProperty("qwerty", "1");
@@ -133,13 +133,13 @@ public class RuntimeInit {
* @param className Fully-qualified class name
* @param argv Argument vector for main()
*/
- private static void invokeStaticMain(String className, String[] argv)
+ private static void invokeStaticMain(String className, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
-
+
// We want to be fairly aggressive about heap utilization, to avoid
// holding on to a lot of memory that isn't needed.
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
-
+
Class<?> cl;
try {
@@ -178,7 +178,7 @@ public class RuntimeInit {
public static final void main(String[] argv) {
commonInit();
-
+
/*
* Now that we're running in interpreted code, call back into native code
* to run the system.
@@ -187,7 +187,7 @@ public class RuntimeInit {
if (Config.LOGV) Log.d(TAG, "Leaving RuntimeInit!");
}
-
+
public static final native void finishInit();
/**
@@ -236,7 +236,7 @@ public class RuntimeInit {
}
// Remaining arguments are passed to the start class's static main
-
+
String startClass = argv[curArg++];
String[] startArgs = new String[argv.length - curArg];
@@ -245,28 +245,28 @@ public class RuntimeInit {
}
public static final native void zygoteInitNative();
-
+
/**
* Returns 1 if the computer is on. If the computer isn't on, the value returned by this method is undefined.
*/
public static final native int isComputerOn();
/**
- * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined.
+ * Turns the computer on if the computer is off. If the computer is on, the behavior of this method is undefined.
*/
public static final native void turnComputerOn();
/**
- *
+ *
* @return 1 if the device has a qwerty keyboard
*/
public static native int getQwertyKeyboard();
-
+
/**
* Report a fatal error in the current process. If this is a user-process,
* a dialog may be displayed informing the user of the error. This
* function does not return; it forces the current process to exit.
- *
+ *
* @param tag to use when logging the error
* @param t exception that was generated by the error
*/
@@ -405,7 +405,7 @@ public class RuntimeInit {
/**
* Replay an encoded CrashData record back into a useable CrashData record. This can be
* helpful for providing debugging output after a process error.
- *
+ *
* @param crashDataBytes The byte array containing the encoded crash record
* @return new CrashData record, or null if could not create one.
*/
diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
new file mode 100644
index 0000000..44bcd16
--- /dev/null
+++ b/core/java/com/android/internal/os/SamplingProfilerIntegration.java
@@ -0,0 +1,144 @@
+package com.android.internal.os;
+
+import dalvik.system.SamplingProfiler;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.FileNotFoundException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import android.util.Log;
+import android.os.*;
+import android.net.Uri;
+
+/**
+ * Integrates the framework with Dalvik's sampling profiler.
+ */
+public class SamplingProfilerIntegration {
+
+ private static final String TAG = "SamplingProfilerIntegration";
+
+ private static final boolean enabled;
+ private static final Executor snapshotWriter;
+ static {
+ enabled = "1".equals(SystemProperties.get("persist.sampling_profiler"));
+ if (enabled) {
+ snapshotWriter = Executors.newSingleThreadExecutor();
+ Log.i(TAG, "Profiler is enabled.");
+ } else {
+ snapshotWriter = null;
+ Log.i(TAG, "Profiler is disabled.");
+ }
+ }
+
+ /**
+ * Is profiling enabled?
+ */
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Starts the profiler if profiling is enabled.
+ */
+ public static void start() {
+ if (!enabled) return;
+ SamplingProfiler.getInstance().start(10);
+ }
+
+ /** Whether or not we've created the snapshots dir. */
+ static boolean dirMade = false;
+
+ /** Whether or not a snapshot is being persisted. */
+ static volatile boolean pending;
+
+ /**
+ * Writes a snapshot to the SD card if profiling is enabled.
+ */
+ public static void writeSnapshot(final String name) {
+ if (!enabled) return;
+
+ if (!pending) {
+ pending = true;
+ snapshotWriter.execute(new Runnable() {
+ public void run() {
+ String dir = "/sdcard/snapshots";
+ if (!dirMade) {
+ makeDirectory(dir);
+ dirMade = true;
+ }
+ try {
+ writeSnapshot(dir, name);
+ } finally {
+ pending = false;
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Writes the zygote's snapshot to internal storage if profiling is enabled.
+ */
+ public static void writeZygoteSnapshot() {
+ if (!enabled) return;
+
+ String dir = "/data/zygote/snapshots";
+ makeDirectory(dir);
+ writeSnapshot(dir, "zygote");
+ }
+
+ private static void writeSnapshot(String dir, String name) {
+ byte[] snapshot = SamplingProfiler.getInstance().snapshot();
+ if (snapshot == null) {
+ return;
+ }
+
+ /*
+ * We use the current time as a unique ID. We can't use a counter
+ * because processes restart. This could result in some overlap if
+ * we capture two snapshots in rapid succession.
+ */
+ long start = System.currentTimeMillis();
+ String path = dir + "/" + name.replace(':', '.') + "-"
+ + System.currentTimeMillis() + ".snapshot";
+ try {
+ // Try to open the file a few times. The SD card may not be mounted.
+ FileOutputStream out;
+ int count = 0;
+ while (true) {
+ try {
+ out = new FileOutputStream(path);
+ break;
+ } catch (FileNotFoundException e) {
+ if (++count > 3) {
+ Log.e(TAG, "Could not open " + path + ".");
+ return;
+ }
+
+ // Sleep for a bit and then try again.
+ try {
+ Thread.sleep(2500);
+ } catch (InterruptedException e1) { /* ignore */ }
+ }
+ }
+
+ try {
+ out.write(snapshot);
+ } finally {
+ out.close();
+ }
+ long elapsed = System.currentTimeMillis() - start;
+ Log.i(TAG, "Wrote snapshot for " + name
+ + " in " + elapsed + "ms.");
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing snapshot.", e);
+ }
+ }
+
+ private static void makeDirectory(String dir) {
+ new File(dir).mkdirs();
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 94149e1..404c513 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,7 +19,6 @@ package com.android.internal.os;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket;
import android.os.Debug;
@@ -31,6 +30,7 @@ import android.util.Log;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
+import dalvik.system.SamplingProfiler;
import java.io.BufferedReader;
import java.io.FileDescriptor;
@@ -73,7 +73,7 @@ public class ZygoteInit {
* never gets destroyed.
*/
private static Resources mResources;
-
+
/**
* The number of times that the main Zygote loop
* should run before calling gc() again.
@@ -93,7 +93,26 @@ public class ZygoteInit {
/** Controls whether we should preload resources during zygote init. */
private static final boolean PRELOAD_RESOURCES = true;
-
+
+ /**
+ * List of methods we "warm up" in the register map cache. These were
+ * chosen because they appeared on the stack in GCs in multiple
+ * applications.
+ *
+ * This is in a VM-ready format, to minimize string processing. If a
+ * class is not already loaded, or a method is not found, the entry
+ * will be skipped.
+ *
+ * This doesn't really merit a separately-generated input file at this
+ * time. The list is fairly short, and the consequences of failure
+ * are minor.
+ */
+ private static final String[] REGISTER_MAP_METHODS = {
+ // (currently not doing any)
+ //"Landroid/app/Activity;.setContentView:(I)V",
+ };
+
+
/**
* Invokes a static "main(argv[]) method on class "className".
* Converts various failing exceptions into RuntimeExceptions, with
@@ -173,7 +192,7 @@ public class ZygoteInit {
* RuntimeException on failure.
*/
private static ZygoteConnection acceptCommandPeer() {
- try {
+ try {
return new ZygoteConnection(sServerSocket.accept());
} catch (IOException ex) {
throw new RuntimeException(
@@ -232,7 +251,7 @@ public class ZygoteInit {
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
-
+
InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
PRELOADED_CLASSES);
if (is == null) {
@@ -240,7 +259,7 @@ public class ZygoteInit {
} else {
Log.i(TAG, "Preloading classes...");
long startTime = SystemClock.uptimeMillis();
-
+
// Drop root perms while running static initializers.
setEffectiveGroup(UNPRIVILEGED_GID);
setEffectiveUser(UNPRIVILEGED_UID);
@@ -256,7 +275,7 @@ public class ZygoteInit {
Debug.startAllocCounting();
try {
- BufferedReader br
+ BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
@@ -328,6 +347,45 @@ public class ZygoteInit {
}
/**
+ * Pre-caches register maps for methods that are commonly used.
+ */
+ private static void cacheRegisterMaps() {
+ String failed = null;
+ int failure;
+ long startTime = System.nanoTime();
+
+ failure = 0;
+
+ for (int i = 0; i < REGISTER_MAP_METHODS.length; i++) {
+ String str = REGISTER_MAP_METHODS[i];
+
+ if (!Debug.cacheRegisterMap(str)) {
+ if (failed == null)
+ failed = str;
+ failure++;
+ }
+ }
+
+ long delta = System.nanoTime() - startTime;
+
+ if (failure == REGISTER_MAP_METHODS.length) {
+ if (REGISTER_MAP_METHODS.length > 0) {
+ Log.i(TAG,
+ "Register map caching failed (precise GC not enabled?)");
+ }
+ return;
+ }
+
+ Log.i(TAG, "Register map cache: found " +
+ (REGISTER_MAP_METHODS.length - failure) + " of " +
+ REGISTER_MAP_METHODS.length + " methods in " +
+ (delta / 1000000L) + "ms");
+ if (failure > 0) {
+ Log.i(TAG, " First failure: " + failed);
+ }
+ }
+
+ /**
* Load in commonly used resources, so they can be shared across
* processes.
*
@@ -336,7 +394,7 @@ public class ZygoteInit {
*/
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
-
+
Debug.startAllocCounting();
try {
runtime.gcSoftReferences();
@@ -469,7 +527,7 @@ public class ZygoteInit {
/**
* Prepare the arguments and fork for the system server process.
*/
- private static boolean startSystemServer()
+ private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
/* Hardcoded command line to start the system server */
String args[] = {
@@ -503,8 +561,8 @@ public class ZygoteInit {
parsedArgs.gids, debugFlags, null);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
- }
-
+ }
+
/* For child process */
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
@@ -515,14 +573,25 @@ public class ZygoteInit {
public static void main(String argv[]) {
try {
+ // Start profiling the zygote initialization.
+ SamplingProfilerIntegration.start();
+
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preloadClasses();
+ //cacheRegisterMaps();
preloadResources();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
+ if (SamplingProfilerIntegration.isEnabled()) {
+ SamplingProfiler sp = SamplingProfiler.getInstance();
+ sp.pause();
+ SamplingProfilerIntegration.writeZygoteSnapshot();
+ sp.shutDown();
+ }
+
// Do an initial gc to clean up after startup
gc();
diff --git a/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
new file mode 100644
index 0000000..5357469
--- /dev/null
+++ b/core/java/com/android/internal/service/wallpaper/ImageWallpaper.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.service.wallpaper;
+
+import android.app.WallpaperManager;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.service.wallpaper.WallpaperService;
+import android.view.MotionEvent;
+import android.view.SurfaceHolder;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+
+/**
+ * Default built-in wallpaper that simply shows a static image.
+ */
+public class ImageWallpaper extends WallpaperService {
+ WallpaperManager mWallpaperManager;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
+ }
+
+ public Engine onCreateEngine() {
+ return new DrawableEngine();
+ }
+
+ class DrawableEngine extends Engine {
+ private final Object mLock = new Object();
+ private final Rect mBounds = new Rect();
+ private WallpaperObserver mReceiver;
+ Drawable mBackground;
+ float mXOffset;
+ float mYOffset;
+
+ class WallpaperObserver extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ updateWallpaper();
+ drawFrame();
+ }
+ }
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ super.onCreate(surfaceHolder);
+ IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
+ mReceiver = new WallpaperObserver();
+ registerReceiver(mReceiver, filter);
+ updateWallpaper();
+ surfaceHolder.setSizeFromLayout();
+ //setTouchEventsEnabled(true);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ }
+
+ @Override
+ public void onVisibilityChanged(boolean visible) {
+ drawFrame();
+ }
+
+ @Override
+ public void onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+ }
+
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset,
+ int xPixels, int yPixels) {
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+ drawFrame();
+ }
+
+ @Override
+ public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ super.onSurfaceChanged(holder, format, width, height);
+ drawFrame();
+ }
+
+ @Override
+ public void onSurfaceCreated(SurfaceHolder holder) {
+ super.onSurfaceCreated(holder);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ super.onSurfaceDestroyed(holder);
+ }
+
+ void drawFrame() {
+ SurfaceHolder sh = getSurfaceHolder();
+ Canvas c = sh.lockCanvas();
+ if (c != null) {
+ final Rect frame = sh.getSurfaceFrame();
+ synchronized (mLock) {
+ final Drawable background = mBackground;
+ final int dw = frame.width();
+ final int dh = frame.height();
+ final int bw = mBackground.getIntrinsicWidth();
+ final int bh = mBackground.getIntrinsicHeight();
+ final int availw = bw-dw;
+ final int availh = bh-dh;
+ int xPixels = availw > 0
+ ? -(int)(availw*mXOffset+.5f) : -(availw/2);
+ int yPixels = availh > 0
+ ? -(int)(availh*mYOffset+.5f) : -(availh/2);
+ c.translate(xPixels, yPixels);
+ c.drawColor(0xff000000);
+ background.draw(c);
+ }
+ sh.unlockCanvasAndPost(c);
+ }
+ }
+
+ void updateWallpaper() {
+ synchronized (mLock) {
+ mBackground = mWallpaperManager.getDrawable();
+ mBounds.left = mBounds.top = 0;
+ mBounds.right = mBackground.getIntrinsicWidth();
+ mBounds.bottom = mBackground.getIntrinsicHeight();
+ mBackground.setBounds(mBounds);
+ }
+ }
+ }
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
new file mode 100644
index 0000000..f4f6297
--- /dev/null
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -0,0 +1,95 @@
+package com.android.internal.view;
+
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class BaseIWindow extends IWindow.Stub {
+ private IWindowSession mSession;
+
+ public void setSession(IWindowSession session) {
+ mSession = session;
+ }
+
+ public void resized(int w, int h, Rect coveredInsets,
+ Rect visibleInsets, boolean reportDraw) {
+ if (reportDraw) {
+ try {
+ mSession.finishDrawing(this);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void dispatchKey(KeyEvent event) {
+ try {
+ mSession.finishKey(this);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ public boolean onDispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
+ event.recycle();
+ return false;
+ }
+
+ public void dispatchPointer(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
+ try {
+ if (event == null) {
+ event = mSession.getPendingPointerMove(this);
+ onDispatchPointer(event, eventTime, false);
+ } else if (callWhenDone) {
+ if (!onDispatchPointer(event, eventTime, true)) {
+ mSession.finishKey(this);
+ }
+ } else {
+ onDispatchPointer(event, eventTime, false);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ public boolean onDispatchTrackball(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
+ event.recycle();
+ return false;
+ }
+
+ public void dispatchTrackball(MotionEvent event, long eventTime,
+ boolean callWhenDone) {
+ try {
+ if (event == null) {
+ event = mSession.getPendingTrackballMove(this);
+ onDispatchTrackball(event, eventTime, false);
+ } else if (callWhenDone) {
+ if (!onDispatchTrackball(event, eventTime, true)) {
+ mSession.finishKey(this);
+ }
+ } else {
+ onDispatchTrackball(event, eventTime, false);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ public void dispatchAppVisibility(boolean visible) {
+ }
+
+ public void dispatchGetNewSurface() {
+ }
+
+ public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+ }
+
+ public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+ }
+
+ public void dispatchWallpaperOffsets(float x, float y) {
+ }
+}
diff --git a/core/java/com/android/internal/view/BaseSurfaceHolder.java b/core/java/com/android/internal/view/BaseSurfaceHolder.java
new file mode 100644
index 0000000..2823689
--- /dev/null
+++ b/core/java/com/android/internal/view/BaseSurfaceHolder.java
@@ -0,0 +1,174 @@
+package com.android.internal.view;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.util.ArrayList;
+import java.util.concurrent.locks.ReentrantLock;
+
+public abstract class BaseSurfaceHolder implements SurfaceHolder {
+ private static final String TAG = "BaseSurfaceHolder";
+ static final boolean DEBUG = false;
+
+ public final ArrayList<SurfaceHolder.Callback> mCallbacks
+ = new ArrayList<SurfaceHolder.Callback>();
+
+ public final ReentrantLock mSurfaceLock = new ReentrantLock();
+ public final Surface mSurface = new Surface();
+
+ int mRequestedWidth = -1;
+ int mRequestedHeight = -1;
+ int mRequestedFormat = PixelFormat.OPAQUE;
+ int mRequestedType = -1;
+
+ long mLastLockTime = 0;
+
+ int mType = -1;
+ final Rect mSurfaceFrame = new Rect();
+
+ public abstract void onUpdateSurface();
+ public abstract void onRelayoutContainer();
+ public abstract boolean onAllowLockCanvas();
+
+ public int getRequestedWidth() {
+ return mRequestedWidth;
+ }
+
+ public int getRequestedHeight() {
+ return mRequestedHeight;
+ }
+
+ public int getRequestedFormat() {
+ return mRequestedFormat;
+ }
+
+ public int getRequestedType() {
+ return mRequestedType;
+ }
+
+ public void addCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ // This is a linear search, but in practice we'll
+ // have only a couple callbacks, so it doesn't matter.
+ if (mCallbacks.contains(callback) == false) {
+ mCallbacks.add(callback);
+ }
+ }
+ }
+
+ public void removeCallback(Callback callback) {
+ synchronized (mCallbacks) {
+ mCallbacks.remove(callback);
+ }
+ }
+
+ public void setFixedSize(int width, int height) {
+ if (mRequestedWidth != width || mRequestedHeight != height) {
+ mRequestedWidth = width;
+ mRequestedHeight = height;
+ onRelayoutContainer();
+ }
+ }
+
+ public void setSizeFromLayout() {
+ if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+ mRequestedWidth = mRequestedHeight = -1;
+ onRelayoutContainer();
+ }
+ }
+
+ public void setFormat(int format) {
+ if (mRequestedFormat != format) {
+ mRequestedFormat = format;
+ onUpdateSurface();
+ }
+ }
+
+ public void setType(int type) {
+ switch (type) {
+ case SURFACE_TYPE_HARDWARE:
+ case SURFACE_TYPE_GPU:
+ // these are deprecated, treat as "NORMAL"
+ type = SURFACE_TYPE_NORMAL;
+ break;
+ }
+ switch (type) {
+ case SURFACE_TYPE_NORMAL:
+ case SURFACE_TYPE_PUSH_BUFFERS:
+ if (mRequestedType != type) {
+ mRequestedType = type;
+ onUpdateSurface();
+ }
+ break;
+ }
+ }
+
+ public Canvas lockCanvas() {
+ return internalLockCanvas(null);
+ }
+
+ public Canvas lockCanvas(Rect dirty) {
+ return internalLockCanvas(dirty);
+ }
+
+ private final Canvas internalLockCanvas(Rect dirty) {
+ if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
+ throw new BadSurfaceTypeException(
+ "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
+ }
+ mSurfaceLock.lock();
+
+ if (DEBUG) Log.i(TAG, "Locking canvas..,");
+
+ Canvas c = null;
+ if (onAllowLockCanvas()) {
+ Rect frame = dirty != null ? dirty : mSurfaceFrame;
+ try {
+ c = mSurface.lockCanvas(frame);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception locking surface", e);
+ }
+ }
+
+ if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
+ if (c != null) {
+ mLastLockTime = SystemClock.uptimeMillis();
+ return c;
+ }
+
+ // If the Surface is not ready to be drawn, then return null,
+ // but throttle calls to this function so it isn't called more
+ // than every 100ms.
+ long now = SystemClock.uptimeMillis();
+ long nextTime = mLastLockTime + 100;
+ if (nextTime > now) {
+ try {
+ Thread.sleep(nextTime-now);
+ } catch (InterruptedException e) {
+ }
+ now = SystemClock.uptimeMillis();
+ }
+ mLastLockTime = now;
+ mSurfaceLock.unlock();
+
+ return null;
+ }
+
+ public void unlockCanvasAndPost(Canvas canvas) {
+ mSurface.unlockCanvasAndPost(canvas);
+ mSurfaceLock.unlock();
+ }
+
+ public Surface getSurface() {
+ return mSurface;
+ }
+
+ public Rect getSurfaceFrame() {
+ return mSurfaceFrame;
+ }
+};
diff --git a/core/java/com/android/internal/widget/ContactHeaderWidget.java b/core/java/com/android/internal/widget/ContactHeaderWidget.java
new file mode 100644
index 0000000..fe01866
--- /dev/null
+++ b/core/java/com/android/internal/widget/ContactHeaderWidget.java
@@ -0,0 +1,493 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import android.Manifest;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.Presence;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.SocialContract.Activities;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+/**
+ * Header used across system for displaying a title bar with contact info. You
+ * can bind specific values on the header, or use helper methods like
+ * {@link #bindFromContactId(long)} to populate asynchronously.
+ * <p>
+ * The parent must request the {@link Manifest.permission#READ_CONTACTS}
+ * permission to access contact data.
+ */
+public class ContactHeaderWidget extends FrameLayout implements View.OnClickListener,
+ View.OnLongClickListener {
+
+ private static final String TAG = "ContactHeaderWidget";
+
+ private TextView mDisplayNameView;
+ private TextView mPhoneticNameView;
+ private CheckBox mStarredView;
+ private ImageView mPhotoView;
+ private ImageView mPresenceView;
+ private TextView mStatusView;
+ private int mNoPhotoResource;
+ private QueryHandler mQueryHandler;
+
+ protected long mContactId;
+ protected Uri mContactUri;
+ protected Uri mStatusUri;
+
+ protected String[] mExcludeMimes = null;
+
+ protected ContentResolver mContentResolver;
+
+ /**
+ * Interface for callbacks invoked when the user interacts with a header.
+ */
+ public interface ContactHeaderListener {
+ public void onPhotoLongClick(View view);
+ public void onDisplayNameLongClick(View view);
+ }
+
+ private ContactHeaderListener mListener;
+
+ //Projection used for the summary info in the header.
+ protected static final String[] HEADER_PROJECTION = new String[] {
+ Contacts.DISPLAY_NAME,
+ Contacts.STARRED,
+ Contacts.PHOTO_ID,
+ Contacts.PRESENCE_STATUS,
+ };
+ protected static final int HEADER_DISPLAY_NAME_COLUMN_INDEX = 0;
+ //TODO: We need to figure out how we're going to get the phonetic name.
+ //static final int HEADER_PHONETIC_NAME_COLUMN_INDEX
+ protected static final int HEADER_STARRED_COLUMN_INDEX = 1;
+ protected static final int HEADER_PHOTO_ID_COLUMN_INDEX = 2;
+ protected static final int HEADER_PRESENCE_STATUS_COLUMN_INDEX = 3;
+
+ //Projection used for finding the most recent social status.
+ protected static final String[] SOCIAL_PROJECTION = new String[] {
+ Activities.TITLE,
+ Activities.PUBLISHED,
+ };
+ protected static final int SOCIAL_TITLE_COLUMN_INDEX = 0;
+ protected static final int SOCIAL_PUBLISHED_COLUMN_INDEX = 1;
+
+ //Projection used for looking up contact id from phone number
+ protected static final String[] PHONE_LOOKUP_PROJECTION = new String[] {
+ PhoneLookup._ID,
+ };
+ protected static final int PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+
+ //Projection used for looking up contact id from email address
+ protected static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
+ RawContacts.CONTACT_ID,
+ };
+ protected static final int EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX = 0;
+
+
+ private static final int TOKEN_CONTACT_INFO = 0;
+ private static final int TOKEN_SOCIAL = 1;
+
+ public ContactHeaderWidget(Context context) {
+ this(context, null);
+ }
+
+ public ContactHeaderWidget(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ContactHeaderWidget(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mContentResolver = mContext.getContentResolver();
+
+ LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.contact_header, this);
+
+ mDisplayNameView = (TextView) findViewById(R.id.name);
+ mDisplayNameView.setOnLongClickListener(this);
+
+ mPhoneticNameView = (TextView) findViewById(R.id.phonetic_name);
+
+ mStarredView = (CheckBox)findViewById(R.id.star);
+ mStarredView.setOnClickListener(this);
+
+ mPhotoView = (ImageView)findViewById(R.id.photo);
+ mPhotoView.setOnClickListener(this);
+ mPhotoView.setOnLongClickListener(this);
+
+ mPresenceView = (ImageView) findViewById(R.id.presence);
+
+ mStatusView = (TextView)findViewById(R.id.status);
+
+ // Set the photo with a random "no contact" image
+ long now = SystemClock.elapsedRealtime();
+ int num = (int) now & 0xf;
+ if (num < 9) {
+ // Leaning in from right, common
+ mNoPhotoResource = R.drawable.ic_contact_picture;
+ } else if (num < 14) {
+ // Leaning in from left uncommon
+ mNoPhotoResource = R.drawable.ic_contact_picture_2;
+ } else {
+ // Coming in from the top, rare
+ mNoPhotoResource = R.drawable.ic_contact_picture_3;
+ }
+
+ mQueryHandler = new QueryHandler(mContentResolver);
+ }
+
+ /**
+ * Set the given {@link ContactHeaderListener} to handle header events.
+ */
+ public void setContactHeaderListener(ContactHeaderListener listener) {
+ mListener = listener;
+ }
+
+ /** {@inheritDoc} */
+ public boolean onLongClick(View v) {
+ switch (v.getId()) {
+ case R.id.photo:
+ performPhotoLongClick();
+ return true;
+ case R.id.name:
+ performDisplayNameLongClick();
+ return true;
+ }
+ return false;
+ }
+
+ private void performPhotoLongClick() {
+ if (mListener != null) {
+ mListener.onPhotoLongClick(mPhotoView);
+ }
+ }
+
+ private void performDisplayNameLongClick() {
+ if (mListener != null) {
+ mListener.onDisplayNameLongClick(mDisplayNameView);
+ }
+ }
+
+ private class QueryHandler extends AsyncQueryHandler {
+
+ public QueryHandler(ContentResolver cr) {
+ super(cr);
+ }
+
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ try{
+ if (token == TOKEN_CONTACT_INFO) {
+ bindContactInfo(cursor);
+ invalidate();
+ } else if (token == TOKEN_SOCIAL) {
+ bindSocial(cursor);
+ invalidate();
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+ }
+
+ /**
+ * Turn on/off showing of the star element.
+ */
+ public void showStar(boolean showStar) {
+ mStarredView.setVisibility(showStar ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Manually set the starred state of this header widget. This doesn't change
+ * the underlying {@link Contacts} value, only the UI state.
+ */
+ public void setStared(boolean starred) {
+ mStarredView.setChecked(starred);
+ }
+
+ /**
+ * Manually set the presence.
+ */
+ public void setPresence(int presence) {
+ mPresenceView.setImageResource(Presence.getPresenceIconResourceId(presence));
+ }
+
+ /**
+ * Manually set the contact uri
+ */
+ public void setContactUri(Uri uri) {
+ mContactUri = uri;
+ }
+
+ /**
+ * Manually set the photo to display in the header. This doesn't change the
+ * underlying {@link Contacts}, only the UI state.
+ */
+ public void setPhoto(Bitmap bitmap) {
+ mPhotoView.setImageBitmap(bitmap);
+ }
+
+ /**
+ * Manually set the display name and phonetic name to show in the header.
+ * This doesn't change the underlying {@link Contacts}, only the UI state.
+ */
+ public void setDisplayName(CharSequence displayName, CharSequence phoneticName) {
+ mDisplayNameView.setText(displayName);
+ if (mPhoneticNameView != null) {
+ mPhoneticNameView.setText(phoneticName);
+ }
+ }
+
+ /**
+ * Manually set the social snippet text to display in the header.
+ */
+ public void setSocialSnippet(CharSequence snippet) {
+ mStatusView.setText(snippet);
+ }
+
+ /**
+ * Set a list of specific MIME-types to exclude and not display. For
+ * example, this can be used to hide the {@link Contacts#CONTENT_ITEM_TYPE}
+ * profile icon.
+ */
+ public void setExcludeMimes(String[] excludeMimes) {
+ mExcludeMimes = excludeMimes;
+ }
+
+ /**
+ * Convenience method for binding all available data from an existing
+ * contact.
+ *
+ * @param contactId the contact id of the contact whose info should be displayed.
+ */
+ public void bindFromContactId(long contactId) {
+ mContactId = contactId;
+ mContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, mContactId);
+
+ bindContactUri(mContactUri);
+ bindSocialUri(ContentUris.withAppendedId(Activities.CONTENT_CONTACT_STATUS_URI, mContactId));
+ }
+
+ /**
+ * Convenience method for binding {@link Contacts} header details from a
+ * {@link Contacts#CONTENT_URI} reference.
+ */
+ public void bindContactUri(Uri contactUri) {
+ mQueryHandler.startQuery(TOKEN_CONTACT_INFO, null, contactUri, HEADER_PROJECTION,
+ null, null, null);
+ }
+
+ /**
+ * Convenience method for binding {@link Activities} header details from a
+ * {@link Activities#CONTENT_CONTACT_STATUS_URI}.
+ */
+ public void bindSocialUri(Uri contactSocial) {
+ mStatusUri = contactSocial;
+ mQueryHandler.startQuery(TOKEN_SOCIAL, null, mStatusUri, SOCIAL_PROJECTION, null, null,
+ null);
+ }
+
+ /**
+ * Convenience method for binding all available data from an existing
+ * contact.
+ *
+ * @param emailAddress The email address used to do a reverse lookup in
+ * the contacts database. If more than one contact contains this email
+ * address, one of them will be chosen to bind to.
+ */
+ public void bindFromEmail(String emailAddress) {
+ Cursor c = null;
+ try {
+ c = mContentResolver.query(Uri.withAppendedPath(Email.CONTENT_FILTER_EMAIL_URI, Uri
+ .encode(emailAddress)), EMAIL_LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.moveToFirst()) {
+ long contactId = c.getLong(EMAIL_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+ bindFromContactId(contactId);
+ } else {
+ setDisplayName(emailAddress, null);
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Convenience method for binding all available data from an existing
+ * contact.
+ *
+ * @param number The phone number used to do a reverse lookup in
+ * the contacts database. If more than one contact contains this phone
+ * number, one of them will be chosen to bind to.
+ */
+ public void bindFromPhoneNumber(String number) {
+ Cursor c = null;
+ try {
+ c = mContentResolver.query(
+ Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number)),
+ PHONE_LOOKUP_PROJECTION, null, null, null);
+ if (c != null && c.moveToFirst()) {
+ long contactId = c.getLong(PHONE_LOOKUP_CONTACT_ID_COLUMN_INDEX);
+ bindFromContactId(contactId);
+ } else {
+ setDisplayName(number, null);
+ }
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
+ /**
+ * Bind the contact details provided by the given {@link Cursor}.
+ */
+ protected void bindContactInfo(Cursor c) {
+ if (c == null || !c.moveToFirst()) return;
+
+ // TODO: Bring back phonetic name
+ final String displayName = c.getString(HEADER_DISPLAY_NAME_COLUMN_INDEX);
+ final String phoneticName = null;
+ this.setDisplayName(displayName, null);
+
+ final boolean starred = c.getInt(HEADER_STARRED_COLUMN_INDEX) != 0;
+ mStarredView.setChecked(starred);
+
+ //Set the photo
+ Bitmap photoBitmap = loadContactPhoto(c.getLong(HEADER_PHOTO_ID_COLUMN_INDEX), null);
+ if (photoBitmap == null) {
+ photoBitmap = loadPlaceholderPhoto(null);
+ }
+ mPhotoView.setImageBitmap(photoBitmap);
+
+ //Set the presence status
+ int presence = c.getInt(HEADER_PRESENCE_STATUS_COLUMN_INDEX);
+ mPresenceView.setImageResource(Presence.getPresenceIconResourceId(presence));
+ }
+
+ /**
+ * Bind the social data provided by the given {@link Cursor}.
+ */
+ protected void bindSocial(Cursor c) {
+ if (c == null || !c.moveToFirst()) return;
+ final String status = c.getString(SOCIAL_TITLE_COLUMN_INDEX);
+ this.setSocialSnippet(status);
+ }
+
+ public void onClick(View view) {
+ // Make sure there is a contact
+ if (mContactUri == null) {
+ return;
+ }
+
+ switch (view.getId()) {
+ case R.id.star: {
+ // Toggle "starred" state
+ final ContentValues values = new ContentValues(1);
+ values.put(Contacts.STARRED, mStarredView.isChecked());
+ mContentResolver.update(mContactUri, values, null, null);
+ break;
+ }
+ case R.id.photo: {
+ // Photo launches contact detail action
+ final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, mContactUri);
+ final Rect target = getTargetRect(view);
+ intent.putExtra(Intents.EXTRA_TARGET_RECT, target);
+ intent.putExtra(Intents.EXTRA_MODE, Intents.MODE_SMALL);
+ if (mExcludeMimes != null) {
+ // Exclude specific MIME-types when requested
+ intent.putExtra(Intents.EXTRA_EXCLUDE_MIMES, mExcludeMimes);
+ }
+ mContext.startActivity(intent);
+ break;
+ }
+ }
+ }
+
+ private Rect getTargetRect(View anchor) {
+ final int[] location = new int[2];
+ anchor.getLocationOnScreen(location);
+
+ final Rect rect = new Rect();
+ rect.left = location[0];
+ rect.top = location[1];
+ rect.right = rect.left + anchor.getWidth();
+ rect.bottom = rect.top + anchor.getHeight();
+ return rect;
+ }
+
+ private Bitmap loadContactPhoto(long photoId, BitmapFactory.Options options) {
+ Cursor photoCursor = null;
+ Bitmap photoBm = null;
+
+ try {
+ photoCursor = mContentResolver.query(
+ ContentUris.withAppendedId(Data.CONTENT_URI, photoId),
+ new String[] { Photo.PHOTO },
+ null, null, null);
+
+ if (photoCursor != null && photoCursor.moveToFirst() && !photoCursor.isNull(0)) {
+ byte[] photoData = photoCursor.getBlob(0);
+ photoBm = BitmapFactory.decodeByteArray(photoData, 0,
+ photoData.length, options);
+ }
+ } finally {
+ if (photoCursor != null) {
+ photoCursor.close();
+ }
+ }
+
+ return photoBm;
+ }
+
+ private Bitmap loadPlaceholderPhoto(BitmapFactory.Options options) {
+ if (mNoPhotoResource == 0) {
+ return null;
+ }
+ return BitmapFactory.decodeResource(mContext.getResources(),
+ mNoPhotoResource, options);
+ }
+}
diff --git a/core/java/com/android/internal/widget/DialogTitle.java b/core/java/com/android/internal/widget/DialogTitle.java
index 2eef0b6..125d2c5 100644
--- a/core/java/com/android/internal/widget/DialogTitle.java
+++ b/core/java/com/android/internal/widget/DialogTitle.java
@@ -58,14 +58,15 @@ public class DialogTitle extends TextView {
android.R.style.TextAppearance_Medium,
android.R.styleable.TextAppearance);
final int textSize = a.getDimensionPixelSize(
- android.R.styleable.TextAppearance_textSize, 20);
+ android.R.styleable.TextAppearance_textSize,
+ (int) (20 * getResources().getDisplayMetrics().density));
- setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
+ // textSize is already expressed in pixels
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
setMaxLines(2);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
}
-
-} \ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f0b311c..58056bd 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -316,20 +316,14 @@ public class LockPatternUtils {
/**
* Set the state of whether the device is permanently locked, meaning the user
- * must authenticate via other means. If false, that means the user has gone
- * out of permanent lock, so the existing (forgotten) lock pattern needs to
- * be cleared.
+ * must authenticate via other means.
+ *
* @param locked Whether the user is permanently locked out until they verify their
* credentials. Occurs after {@link #FAILED_ATTEMPTS_BEFORE_RESET} failed
* attempts.
*/
public void setPermanentlyLocked(boolean locked) {
setBoolean(LOCKOUT_PERMANENT_KEY, locked);
-
- if (!locked) {
- setLockPatternEnabled(false);
- saveLockPattern(null);
- }
}
/**
diff --git a/core/java/com/android/internal/widget/NumberPicker.java b/core/java/com/android/internal/widget/NumberPicker.java
index 0424ced..ae08eca 100644
--- a/core/java/com/android/internal/widget/NumberPicker.java
+++ b/core/java/com/android/internal/widget/NumberPicker.java
@@ -36,7 +36,7 @@ import com.android.internal.R;
public class NumberPicker extends LinearLayout implements OnClickListener,
OnFocusChangeListener, OnLongClickListener {
-
+
public interface OnChangedListener {
void onChanged(NumberPicker picker, int oldVal, int newVal);
}
@@ -51,7 +51,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
* most efficient way to do this; it avoids creating temporary objects
* on every call to format().
*/
- public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER =
+ public static final NumberPicker.Formatter TWO_DIGIT_FORMATTER =
new NumberPicker.Formatter() {
final StringBuilder mBuilder = new StringBuilder();
final java.util.Formatter mFmt = new java.util.Formatter(mBuilder);
@@ -63,7 +63,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
return mFmt.toString();
}
};
-
+
private final Handler mHandler;
private final Runnable mRunnable = new Runnable() {
public void run() {
@@ -81,21 +81,21 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
private final InputFilter mNumberInputFilter;
private String[] mDisplayedValues;
- private int mStart;
- private int mEnd;
- private int mCurrent;
- private int mPrevious;
+ protected int mStart;
+ protected int mEnd;
+ protected int mCurrent;
+ protected int mPrevious;
private OnChangedListener mListener;
private Formatter mFormatter;
private long mSpeed = 300;
-
+
private boolean mIncrement;
private boolean mDecrement;
-
+
public NumberPicker(Context context) {
this(context, null);
}
-
+
public NumberPicker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@@ -117,7 +117,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
mDecrementButton.setOnClickListener(this);
mDecrementButton.setOnLongClickListener(this);
mDecrementButton.setNumberPicker(this);
-
+
mText = (EditText) findViewById(R.id.timepicker_input);
mText.setOnFocusChangeListener(this);
mText.setFilters(new InputFilter[] {inputFilter});
@@ -127,7 +127,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
setEnabled(false);
}
}
-
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -135,19 +135,19 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
mDecrementButton.setEnabled(enabled);
mText.setEnabled(enabled);
}
-
+
public void setOnChangeListener(OnChangedListener listener) {
mListener = listener;
}
-
+
public void setFormatter(Formatter formatter) {
mFormatter = formatter;
}
-
+
/**
* Set the range of numbers allowed for the number picker. The current
* value will be automatically set to the start.
- *
+ *
* @param start the start of the range (inclusive)
* @param end the end of the range (inclusive)
*/
@@ -157,12 +157,12 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
mCurrent = start;
updateView();
}
-
+
/**
* Set the range of numbers allowed for the number picker. The current
* value will be automatically set to the start. Also provide a mapping
* for values used to display to the user.
- *
+ *
* @param start the start of the range (inclusive)
* @param end the end of the range (inclusive)
* @param displayedValues the values displayed to the user.
@@ -174,7 +174,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
mCurrent = start;
updateView();
}
-
+
public void setCurrent(int current) {
mCurrent = current;
updateView();
@@ -187,7 +187,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
public void setSpeed(long speed) {
mSpeed = speed;
}
-
+
public void onClick(View v) {
validateInput(mText);
if (!mText.hasFocus()) mText.requestFocus();
@@ -199,15 +199,15 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
changeCurrent(mCurrent - 1);
}
}
-
+
private String formatNumber(int value) {
return (mFormatter != null)
? mFormatter.toString(value)
: String.valueOf(value);
}
-
- private void changeCurrent(int current) {
-
+
+ protected void changeCurrent(int current) {
+
// Wrap around the values if we go past the start or end
if (current > mEnd) {
current = mStart;
@@ -219,15 +219,15 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
notifyChange();
updateView();
}
-
- private void notifyChange() {
+
+ protected void notifyChange() {
if (mListener != null) {
mListener.onChanged(this, mPrevious, mCurrent);
}
}
- private void updateView() {
-
+ protected void updateView() {
+
/* If we don't have displayed values then use the
* current number else find the correct value in the
* displayed values for the current number.
@@ -239,7 +239,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
mText.setSelection(mText.getText().length());
}
-
+
private void validateCurrentView(CharSequence str) {
int val = getSelectedPos(str.toString());
if ((val >= mStart) && (val <= mEnd)) {
@@ -253,7 +253,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
public void onFocusChange(View v, boolean hasFocus) {
-
+
/* When focus is lost check that the text field
* has valid values.
*/
@@ -280,12 +280,12 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
* to inform us when the long click has ended.
*/
public boolean onLongClick(View v) {
-
+
/* The text view may still have focus so clear it's focus which will
* trigger the on focus changed and any typed values to be pulled.
*/
mText.clearFocus();
-
+
if (R.id.increment == v.getId()) {
mIncrement = true;
mHandler.post(mRunnable);
@@ -295,22 +295,22 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
}
return true;
}
-
+
public void cancelIncrement() {
mIncrement = false;
}
-
+
public void cancelDecrement() {
mDecrement = false;
}
-
+
private static final char[] DIGIT_CHARACTERS = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
-
+
private NumberPickerButton mIncrementButton;
private NumberPickerButton mDecrementButton;
-
+
private class NumberPickerInputFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
@@ -331,7 +331,7 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
return "";
}
}
-
+
private class NumberRangeKeyListener extends NumberKeyListener {
// XXX This doesn't allow for range limits when controlled by a
@@ -339,12 +339,12 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
public int getInputType() {
return InputType.TYPE_CLASS_NUMBER;
}
-
+
@Override
protected char[] getAcceptedChars() {
return DIGIT_CHARACTERS;
}
-
+
@Override
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
@@ -381,21 +381,21 @@ public class NumberPicker extends LinearLayout implements OnClickListener,
return Integer.parseInt(str);
} else {
for (int i = 0; i < mDisplayedValues.length; i++) {
-
+
/* Don't force the user to type in jan when ja will do */
str = str.toLowerCase();
if (mDisplayedValues[i].toLowerCase().startsWith(str)) {
return mStart + i;
}
}
-
+
/* The user might have typed in a number into the month field i.e.
* 10 instead of OCT so support that too.
*/
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
-
+
/* Ignore as if it's not a number we don't care */
}
}
diff --git a/core/java/com/google/android/gdata/client/QueryParamsImpl.java b/core/java/com/google/android/gdata/client/QueryParamsImpl.java
index e27b36f..fbe0d22 100644
--- a/core/java/com/google/android/gdata/client/QueryParamsImpl.java
+++ b/core/java/com/google/android/gdata/client/QueryParamsImpl.java
@@ -60,6 +60,8 @@ public class QueryParamsImpl extends QueryParams {
sb.append('?');
}
for (String param : params) {
+ String value = mParams.get(param);
+ if (value == null) continue;
if (first) {
first = false;
} else {
@@ -67,7 +69,7 @@ public class QueryParamsImpl extends QueryParams {
}
sb.append(param);
sb.append('=');
- String value = mParams.get(param);
+
String encodedValue = null;
try {
diff --git a/core/java/com/google/android/gdata2/client/AndroidGDataClient.java b/core/java/com/google/android/gdata2/client/AndroidGDataClient.java
new file mode 100644
index 0000000..6ba791d
--- /dev/null
+++ b/core/java/com/google/android/gdata2/client/AndroidGDataClient.java
@@ -0,0 +1,590 @@
+// Copyright 2007 The Android Open Source Project
+
+package com.google.android.gdata2.client;
+
+import com.google.android.net.GoogleHttpClient;
+import com.google.wireless.gdata2.client.GDataClient;
+import com.google.wireless.gdata2.client.HttpException;
+import com.google.wireless.gdata2.client.QueryParams;
+import com.google.wireless.gdata2.data.StringUtils;
+import com.google.wireless.gdata2.parser.ParseException;
+import com.google.wireless.gdata2.serializer.GDataSerializer;
+import com.google.android.gdata2.client.QueryParamsImpl;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.entity.InputStreamEntity;
+import org.apache.http.entity.AbstractHttpEntity;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.http.AndroidHttpClient;
+import android.text.TextUtils;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.BufferedInputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+
+/**
+ * Implementation of a GDataClient using GoogleHttpClient to make HTTP
+ * requests. Always issues GETs and POSTs, using the X-HTTP-Method-Override
+ * header when a PUT or DELETE is desired, to avoid issues with firewalls, etc.,
+ * that do not allow methods other than GET or POST.
+ */
+public class AndroidGDataClient implements GDataClient {
+
+ private static final String TAG = "GDataClient";
+ private static final boolean DEBUG = false;
+ private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+
+ private static final String X_HTTP_METHOD_OVERRIDE =
+ "X-HTTP-Method-Override";
+
+ private static final String DEFAULT_USER_AGENT_APP_VERSION = "Android-GData/1.2";
+
+ private static final int MAX_REDIRECTS = 10;
+ private static String DEFAULT_GDATA_VERSION = "2.0";
+
+
+ private String mGDataVersion;
+ private final GoogleHttpClient mHttpClient;
+ private ContentResolver mResolver;
+
+ /**
+ * Interface for creating HTTP requests. Used by
+ * {@link AndroidGDataClient#createAndExecuteMethod}, since HttpUriRequest does not allow for
+ * changing the URI after creation, e.g., when you want to follow a redirect.
+ */
+ private interface HttpRequestCreator {
+ HttpUriRequest createRequest(URI uri);
+ }
+
+ private static class GetRequestCreator implements HttpRequestCreator {
+ public GetRequestCreator() {
+ }
+
+ public HttpUriRequest createRequest(URI uri) {
+ HttpGet get = new HttpGet(uri);
+ return get;
+ }
+ }
+
+ private static class PostRequestCreator implements HttpRequestCreator {
+ private final String mMethodOverride;
+ private final HttpEntity mEntity;
+ public PostRequestCreator(String methodOverride, HttpEntity entity) {
+ mMethodOverride = methodOverride;
+ mEntity = entity;
+ }
+
+ public HttpUriRequest createRequest(URI uri) {
+ HttpPost post = new HttpPost(uri);
+ if (mMethodOverride != null) {
+ post.addHeader(X_HTTP_METHOD_OVERRIDE, mMethodOverride);
+ }
+ post.setEntity(mEntity);
+ return post;
+ }
+ }
+
+ // MAJOR TODO: make this work across redirects (if we can reset the InputStream).
+ // OR, read the bits into a local buffer (yuck, the media could be large).
+ private static class MediaPutRequestCreator implements HttpRequestCreator {
+ private final InputStream mMediaInputStream;
+ private final String mContentType;
+ public MediaPutRequestCreator(InputStream mediaInputStream, String contentType) {
+ mMediaInputStream = mediaInputStream;
+ mContentType = contentType;
+ }
+
+ public HttpUriRequest createRequest(URI uri) {
+ HttpPost post = new HttpPost(uri);
+ post.addHeader(X_HTTP_METHOD_OVERRIDE, "PUT");
+ // mMediaInputStream.reset();
+ InputStreamEntity entity = new InputStreamEntity(mMediaInputStream,
+ -1 /* read until EOF */);
+ entity.setContentType(mContentType);
+ post.setEntity(entity);
+ return post;
+ }
+ }
+
+
+ /**
+ * Creates a new AndroidGDataClient.
+ *
+ * @param context The ContentResolver to get URL rewriting rules from
+ * through the Android proxy server, using null to indicate not using proxy.
+ * The context will also be used by GoogleHttpClient for configuration of
+ * SSL session persistence.
+ */
+ public AndroidGDataClient(Context context) {
+ this(context, DEFAULT_USER_AGENT_APP_VERSION);
+ }
+
+ /**
+ * Creates a new AndroidGDataClient.
+ *
+ * @param context The ContentResolver to get URL rewriting rules from
+ * through the Android proxy server, using null to indicate not using proxy.
+ * The context will also be used by GoogleHttpClient for configuration of
+ * SSL session persistence.
+ * @param appAndVersion The application name and version to be used as the basis of the
+ * User-Agent. e.g., Android-GData/1.5.0.
+ */
+ public AndroidGDataClient(Context context, String appAndVersion) {
+ this(context, appAndVersion, DEFAULT_GDATA_VERSION);
+ }
+
+ /**
+ * Creates a new AndroidGDataClient.
+ *
+ * @param context The ContentResolver to get URL rewriting rules from
+ * through the Android proxy server, using null to indicate not using proxy.
+ * The context will also be used by GoogleHttpClient for configuration of
+ * SSL session persistence.
+ * @param appAndVersion The application name and version to be used as the basis of the
+ * User-Agent. e.g., Android-GData/1.5.0.
+ * @param gdataVersion The gdata service version that should be
+ * used, e.g. "2.0"
+ *
+ */
+ public AndroidGDataClient(Context context, String appAndVersion, String gdataVersion) {
+ mHttpClient = new GoogleHttpClient(context, appAndVersion,
+ true /* gzip capable */);
+ mHttpClient.enableCurlLogging(TAG, Log.VERBOSE);
+ mResolver = context.getContentResolver();
+ mGDataVersion = gdataVersion;
+ }
+
+
+ public void close() {
+ mHttpClient.close();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see GDataClient#encodeUri(java.lang.String)
+ */
+ public String encodeUri(String uri) {
+ String encodedUri;
+ try {
+ encodedUri = URLEncoder.encode(uri, "UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ // should not happen.
+ Log.e("JakartaGDataClient",
+ "UTF-8 not supported -- should not happen. "
+ + "Using default encoding.", uee);
+ encodedUri = URLEncoder.encode(uri);
+ }
+ return encodedUri;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.google.wireless.gdata.client.GDataClient#createQueryParams()
+ */
+ public QueryParams createQueryParams() {
+ return new QueryParamsImpl();
+ }
+
+ // follows redirects
+ private InputStream createAndExecuteMethod(HttpRequestCreator creator,
+ String uriString,
+ String authToken,
+ String eTag,
+ String protocolVersion)
+ throws HttpException, IOException {
+
+ HttpResponse response = null;
+ int status = 500;
+ int redirectsLeft = MAX_REDIRECTS;
+
+ URI uri;
+ try {
+ uri = new URI(uriString);
+ } catch (URISyntaxException use) {
+ Log.w(TAG, "Unable to parse " + uriString + " as URI.", use);
+ throw new IOException("Unable to parse " + uriString + " as URI: "
+ + use.getMessage());
+ }
+
+ // we follow redirects ourselves, since we want to follow redirects even on POSTs, which
+ // the HTTP library does not do. following redirects ourselves also allows us to log
+ // the redirects using our own logging.
+ while (redirectsLeft > 0) {
+
+ HttpUriRequest request = creator.createRequest(uri);
+
+ AndroidHttpClient.modifyRequestToAcceptGzipResponse(request);
+ // only add the auth token if not null (to allow for GData feeds that do not require
+ // authentication.)
+ if (!TextUtils.isEmpty(authToken)) {
+ request.addHeader("Authorization", "GoogleLogin auth=" + authToken);
+ }
+
+ // while by default we have a 2.0 in this variable, it is possible to construct
+ // a client that has an empty version field, to work with 1.0 services.
+ if (!TextUtils.isEmpty(mGDataVersion)) {
+ request.addHeader("GDataVersion", mGDataVersion);
+ }
+
+ // if we have a passed down eTag value, we need to add several headers
+ if (!TextUtils.isEmpty(eTag)) {
+ String method = request.getMethod();
+ Header overrideMethodHeader = request.getFirstHeader(X_HTTP_METHOD_OVERRIDE);
+ if (overrideMethodHeader != null) {
+ method = overrideMethodHeader.getValue();
+ }
+ if ("GET".equals(method)) {
+ // add the none match header, if the resource is not changed
+ // this request will result in a 304 now.
+ request.addHeader("If-None-Match", eTag);
+ } else if ("DELETE".equals(method)
+ || "PUT".equals(method)) {
+ // now we send an if-match, but only if the passed in eTag is a strong eTag
+ // as this only makes sense for a strong eTag
+ if (!eTag.startsWith("W/")) {
+ request.addHeader("If-Match", eTag);
+ }
+ }
+ }
+
+ if (LOCAL_LOGV) {
+ for (Header h : request.getAllHeaders()) {
+ Log.v(TAG, h.getName() + ": " + h.getValue());
+ }
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Executing " + request.getRequestLine().toString());
+ }
+
+ response = null;
+
+ try {
+ response = mHttpClient.execute(request);
+ } catch (IOException ioe) {
+ Log.w(TAG, "Unable to execute HTTP request." + ioe);
+ throw ioe;
+ }
+
+ StatusLine statusLine = response.getStatusLine();
+ if (statusLine == null) {
+ Log.w(TAG, "StatusLine is null.");
+ throw new NullPointerException("StatusLine is null -- should not happen.");
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, response.getStatusLine().toString());
+ for (Header h : response.getAllHeaders()) {
+ Log.d(TAG, h.getName() + ": " + h.getValue());
+ }
+ }
+ status = statusLine.getStatusCode();
+
+ HttpEntity entity = response.getEntity();
+
+ if ((status >= 200) && (status < 300) && entity != null) {
+ InputStream in = AndroidHttpClient.getUngzippedContent(entity);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ in = logInputStreamContents(in);
+ }
+ return in;
+ }
+
+ // TODO: handle 301, 307?
+ // TODO: let the http client handle the redirects, if we can be sure we'll never get a
+ // redirect on POST.
+ if (status == 302) {
+ // consume the content, so the connection can be closed.
+ entity.consumeContent();
+ Header location = response.getFirstHeader("Location");
+ if (location == null) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Redirect requested but no Location "
+ + "specified.");
+ }
+ break;
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Following redirect to " + location.getValue());
+ }
+ try {
+ uri = new URI(location.getValue());
+ } catch (URISyntaxException use) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Unable to parse " + location.getValue() + " as URI.", use);
+ throw new IOException("Unable to parse " + location.getValue()
+ + " as URI.");
+ }
+ break;
+ }
+ --redirectsLeft;
+ } else {
+ break;
+ }
+ }
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Received " + status + " status code.");
+ }
+ String errorMessage = null;
+ HttpEntity entity = response.getEntity();
+ try {
+ if (response != null && entity != null) {
+ InputStream in = AndroidHttpClient.getUngzippedContent(entity);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buf = new byte[8192];
+ int bytesRead = -1;
+ while ((bytesRead = in.read(buf)) != -1) {
+ baos.write(buf, 0, bytesRead);
+ }
+ // TODO: use appropriate encoding, picked up from Content-Type.
+ errorMessage = new String(baos.toByteArray());
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, errorMessage);
+ }
+ }
+ } finally {
+ if (entity != null) {
+ entity.consumeContent();
+ }
+ }
+ String exceptionMessage = "Received " + status + " status code";
+ if (errorMessage != null) {
+ exceptionMessage += (": " + errorMessage);
+ }
+ throw new HttpException(exceptionMessage, status, null /* InputStream */);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see GDataClient#getFeedAsStream(java.lang.String, java.lang.String)
+ */
+ public InputStream getFeedAsStream(String feedUrl,
+ String authToken,
+ String eTag,
+ String protocolVersion)
+ throws HttpException, IOException {
+
+ InputStream in = createAndExecuteMethod(new GetRequestCreator(), feedUrl, authToken, eTag, protocolVersion);
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to access feed.");
+ }
+
+ /**
+ * Log the contents of the input stream.
+ * The original input stream is consumed, so the caller must use the
+ * BufferedInputStream that is returned.
+ * @param in InputStream
+ * @return replacement input stream for caller to use
+ * @throws IOException
+ */
+ private InputStream logInputStreamContents(InputStream in) throws IOException {
+ if (in == null) {
+ return in;
+ }
+ // bufferSize is the (arbitrary) maximum amount to log.
+ // The original InputStream is wrapped in a
+ // BufferedInputStream with a 16K buffer. This lets
+ // us read up to 16K, write it to the log, and then
+ // reset the stream so the the original client can
+ // then read the data. The BufferedInputStream
+ // provides the mark and reset support, even when
+ // the original InputStream does not.
+ final int bufferSize = 16384;
+ BufferedInputStream bin = new BufferedInputStream(in, bufferSize);
+ bin.mark(bufferSize);
+ int wanted = bufferSize;
+ int totalReceived = 0;
+ byte buf[] = new byte[wanted];
+ while (wanted > 0) {
+ int got = bin.read(buf, totalReceived, wanted);
+ if (got <= 0) break; // EOF
+ wanted -= got;
+ totalReceived += got;
+ }
+ Log.d(TAG, new String(buf, 0, totalReceived, "UTF-8"));
+ bin.reset();
+ return bin;
+ }
+
+ public InputStream getMediaEntryAsStream(String mediaEntryUrl, String authToken, String eTag, String protocolVersion)
+ throws HttpException, IOException {
+
+ InputStream in = createAndExecuteMethod(new GetRequestCreator(), mediaEntryUrl, authToken, eTag, protocolVersion);
+
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to access media entry.");
+ }
+
+ /* (non-Javadoc)
+ * @see GDataClient#createEntry
+ */
+ public InputStream createEntry(String feedUrl,
+ String authToken,
+ String protocolVersion,
+ GDataSerializer entry)
+ throws HttpException, IOException {
+
+ HttpEntity entity = createEntityForEntry(entry, GDataSerializer.FORMAT_CREATE);
+ InputStream in = createAndExecuteMethod(
+ new PostRequestCreator(null /* override */, entity),
+ feedUrl,
+ authToken,
+ null,
+ protocolVersion);
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to create entry.");
+ }
+
+ /* (non-Javadoc)
+ * @see GDataClient#updateEntry
+ */
+ public InputStream updateEntry(String editUri,
+ String authToken,
+ String eTag,
+ String protocolVersion,
+ GDataSerializer entry)
+ throws HttpException, IOException {
+ HttpEntity entity = createEntityForEntry(entry, GDataSerializer.FORMAT_UPDATE);
+ final String method = entry.getSupportsPartial() ? "PATCH" : "PUT";
+ InputStream in = createAndExecuteMethod(
+ new PostRequestCreator(method, entity),
+ editUri,
+ authToken,
+ eTag,
+ protocolVersion);
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to update entry.");
+ }
+
+ /* (non-Javadoc)
+ * @see GDataClient#deleteEntry
+ */
+ public void deleteEntry(String editUri, String authToken, String eTag)
+ throws HttpException, IOException {
+ if (StringUtils.isEmpty(editUri)) {
+ throw new IllegalArgumentException(
+ "you must specify an non-empty edit url");
+ }
+ InputStream in =
+ createAndExecuteMethod(
+ new PostRequestCreator("DELETE", null /* entity */),
+ editUri,
+ authToken,
+ eTag,
+ null /* protocolVersion, not required for a delete */);
+ if (in == null) {
+ throw new IOException("Unable to delete entry.");
+ }
+ try {
+ in.close();
+ } catch (IOException ioe) {
+ // ignore
+ }
+ }
+
+ public InputStream updateMediaEntry(String editUri, String authToken, String eTag,
+ String protocolVersion, InputStream mediaEntryInputStream, String contentType)
+ throws HttpException, IOException {
+ InputStream in = createAndExecuteMethod(
+ new MediaPutRequestCreator(mediaEntryInputStream, contentType),
+ editUri,
+ authToken,
+ eTag,
+ protocolVersion);
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to write media entry.");
+ }
+
+ private HttpEntity createEntityForEntry(GDataSerializer entry, int format) throws IOException {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ entry.serialize(baos, format);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Unable to serialize entry.", ioe);
+ throw ioe;
+ } catch (ParseException pe) {
+ Log.e(TAG, "Unable to serialize entry.", pe);
+ throw new IOException("Unable to serialize entry: " + pe.getMessage());
+ }
+
+ byte[] entryBytes = baos.toByteArray();
+
+ if (entryBytes != null && Log.isLoggable(TAG, Log.DEBUG)) {
+ try {
+ Log.d(TAG, "Serialized entry: " + new String(entryBytes, "UTF-8"));
+ } catch (UnsupportedEncodingException uee) {
+ // should not happen
+ throw new IllegalStateException("UTF-8 should be supported!",
+ uee);
+ }
+ }
+
+ AbstractHttpEntity entity = AndroidHttpClient.getCompressedEntity(entryBytes, mResolver);
+ entity.setContentType(entry.getContentType());
+ return entity;
+ }
+
+ /**
+ * Connects to a GData server (specified by the batchUrl) and submits a
+ * batch for processing. The response from the server is returned as an
+ * {@link InputStream}. The caller is responsible for calling
+ * {@link InputStream#close()} on the returned {@link InputStream}.
+ *
+ * @param batchUrl The batch url to which the batch is submitted.
+ * @param authToken the authentication token that should be used when
+ * submitting the batch.
+ * @param protocolVersion The version of the protocol that
+ * should be used for this request.
+ * @param batch The batch of entries to submit.
+ * @throws IOException Thrown if an io error occurs while communicating with
+ * the service.
+ * @throws HttpException if the service returns an error response.
+ */
+ public InputStream submitBatch(String batchUrl,
+ String authToken,
+ String protocolVersion,
+ GDataSerializer batch)
+ throws HttpException, IOException
+ {
+ HttpEntity entity = createEntityForEntry(batch, GDataSerializer.FORMAT_BATCH);
+ InputStream in = createAndExecuteMethod(
+ new PostRequestCreator("POST", entity),
+ batchUrl,
+ authToken,
+ null,
+ protocolVersion);
+ if (in != null) {
+ return in;
+ }
+ throw new IOException("Unable to process batch request.");
+ }
+}
+
diff --git a/core/java/com/google/android/gdata2/client/AndroidXmlParserFactory.java b/core/java/com/google/android/gdata2/client/AndroidXmlParserFactory.java
new file mode 100644
index 0000000..f097706
--- /dev/null
+++ b/core/java/com/google/android/gdata2/client/AndroidXmlParserFactory.java
@@ -0,0 +1,31 @@
+package com.google.android.gdata2.client;
+
+import com.google.wireless.gdata2.parser.xml.XmlParserFactory;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import android.util.Xml;
+
+/**
+ * XmlParserFactory for the Android platform.
+ */
+public class AndroidXmlParserFactory implements XmlParserFactory {
+
+ /*
+ * (non-javadoc)
+ * @see XmlParserFactory#createParser
+ */
+ public XmlPullParser createParser() throws XmlPullParserException {
+ return Xml.newPullParser();
+ }
+
+ /*
+ * (non-javadoc)
+ * @see XmlParserFactory#createSerializer
+ */
+ public XmlSerializer createSerializer() throws XmlPullParserException {
+ return Xml.newSerializer();
+ }
+}
diff --git a/core/java/com/google/android/gdata2/client/QueryParamsImpl.java b/core/java/com/google/android/gdata2/client/QueryParamsImpl.java
new file mode 100644
index 0000000..a26f4ce
--- /dev/null
+++ b/core/java/com/google/android/gdata2/client/QueryParamsImpl.java
@@ -0,0 +1,99 @@
+package com.google.android.gdata2.client;
+import com.google.wireless.gdata2.client.QueryParams;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Simple implementation of the QueryParams interface.
+ */
+// TODO: deal with categories
+public class QueryParamsImpl extends QueryParams {
+
+ private final Map<String,String> mParams = new HashMap<String,String>();
+
+ /**
+ * Creates a new empty QueryParamsImpl.
+ */
+ public QueryParamsImpl() {
+ }
+
+ @Override
+ public void clear() {
+ setEntryId(null);
+ mParams.clear();
+ }
+
+ @Override
+ public String generateQueryUrl(String feedUrl) {
+
+ if (TextUtils.isEmpty(getEntryId()) &&
+ mParams.isEmpty()) {
+ // nothing to do
+ return feedUrl;
+ }
+
+ // handle entry IDs
+ if (!TextUtils.isEmpty(getEntryId())) {
+ if (!mParams.isEmpty()) {
+ throw new IllegalStateException("Cannot set both an entry ID "
+ + "and other query paramters.");
+ }
+ return feedUrl + '/' + getEntryId();
+ }
+
+ // otherwise, append the querystring params.
+ StringBuilder sb = new StringBuilder();
+ sb.append(feedUrl);
+ Set<String> params = mParams.keySet();
+ boolean first = true;
+ if (feedUrl.contains("?")) {
+ first = false;
+ } else {
+ sb.append('?');
+ }
+ for (String param : params) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append('&');
+ }
+ sb.append(param);
+ sb.append('=');
+ String value = mParams.get(param);
+ String encodedValue = null;
+
+ try {
+ encodedValue = URLEncoder.encode(value, "UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ // should not happen.
+ Log.w("QueryParamsImpl",
+ "UTF-8 not supported -- should not happen. "
+ + "Using default encoding.", uee);
+ encodedValue = URLEncoder.encode(value);
+ }
+ sb.append(encodedValue);
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String getParamValue(String param) {
+ if (!(mParams.containsKey(param))) {
+ return null;
+ }
+ return mParams.get(param);
+ }
+
+ @Override
+ public void setParamValue(String param, String value) {
+ mParams.put(param, value);
+ }
+
+}
diff --git a/core/java/com/google/android/mms/pdu/EncodedStringValue.java b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
index 7696c5e..a27962d 100644
--- a/core/java/com/google/android/mms/pdu/EncodedStringValue.java
+++ b/core/java/com/google/android/mms/pdu/EncodedStringValue.java
@@ -269,4 +269,16 @@ public class EncodedStringValue implements Cloneable {
return new EncodedStringValue(value.mCharacterSet, value.mData);
}
+
+ public static EncodedStringValue[] encodeStrings(String[] array) {
+ int count = array.length;
+ if (count > 0) {
+ EncodedStringValue[] encodedArray = new EncodedStringValue[count];
+ for (int i = 0; i < count; i++) {
+ encodedArray[i] = new EncodedStringValue(array[i]);
+ }
+ return encodedArray;
+ }
+ return null;
+ }
}
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index d2a41f1..df91ea6 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -31,6 +31,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
+import android.provider.Telephony;
import android.provider.Telephony.Mms;
import android.provider.Telephony.MmsSms;
import android.provider.Telephony.Threads;
@@ -54,6 +55,8 @@ import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
+import com.google.android.mms.pdu.EncodedStringValue;
+
/**
* This class is the high-level manager of PDU storage.
*/
@@ -159,6 +162,7 @@ public class PduPersister {
Part.CONTENT_TYPE,
Part.FILENAME,
Part.NAME,
+ Part.TEXT
};
private static final int PART_COLUMN_ID = 0;
@@ -169,6 +173,7 @@ public class PduPersister {
private static final int PART_COLUMN_CONTENT_TYPE = 5;
private static final int PART_COLUMN_FILENAME = 6;
private static final int PART_COLUMN_NAME = 7;
+ private static final int PART_COLUMN_TEXT = 8;
private static final HashMap<Uri, Integer> MESSAGE_BOX_MAP;
// These map are used for convenience in persist() and load().
@@ -414,26 +419,36 @@ public class PduPersister {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
- try {
- is = mContentResolver.openInputStream(partURI);
-
- byte[] buffer = new byte[256];
- int len = is.read(buffer);
- while (len >= 0) {
- baos.write(buffer, 0, len);
- len = is.read(buffer);
- }
- } catch (IOException e) {
- Log.e(TAG, "Failed to load part data", e);
- c.close();
- throw new MmsException(e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close stream", e);
- } // Ignore
+ // Store simple string values directly in the database instead of an
+ // external file. This makes the text searchable and retrieval slightly
+ // faster.
+ if ("text/plain".equals(type) || "application/smil".equals(type)) {
+ String text = c.getString(PART_COLUMN_TEXT);
+ byte [] blob = new EncodedStringValue(text).getTextString();
+ baos.write(blob, 0, blob.length);
+ } else {
+
+ try {
+ is = mContentResolver.openInputStream(partURI);
+
+ byte[] buffer = new byte[256];
+ int len = is.read(buffer);
+ while (len >= 0) {
+ baos.write(buffer, 0, len);
+ len = is.read(buffer);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to load part data", e);
+ c.close();
+ throw new MmsException(e);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close stream", e);
+ } // Ignore
+ }
}
}
part.setData(baos.toByteArray());
@@ -719,29 +734,37 @@ public class PduPersister {
InputStream is = null;
try {
- os = mContentResolver.openOutputStream(uri);
byte[] data = part.getData();
- if (data == null) {
- Uri dataUri = part.getDataUri();
- if ((dataUri == null) || (dataUri == uri)) {
- Log.w(TAG, "Can't find data for this part.");
- return;
- }
- is = mContentResolver.openInputStream(dataUri);
-
- if (LOCAL_LOGV) {
- Log.v(TAG, "Saving data to: " + uri);
- }
-
- byte[] buffer = new byte[256];
- for (int len = 0; (len = is.read(buffer)) != -1; ) {
- os.write(buffer, 0, len);
+ if ("text/plain".equals(contentType) || "application/smil".equals(contentType)) {
+ ContentValues cv = new ContentValues();
+ cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString());
+ if (mContentResolver.update(uri, cv, null, null) != 1) {
+ throw new MmsException("unable to update " + uri.toString());
}
} else {
- if (LOCAL_LOGV) {
- Log.v(TAG, "Saving data to: " + uri);
+ os = mContentResolver.openOutputStream(uri);
+ if (data == null) {
+ Uri dataUri = part.getDataUri();
+ if ((dataUri == null) || (dataUri == uri)) {
+ Log.w(TAG, "Can't find data for this part.");
+ return;
+ }
+ is = mContentResolver.openInputStream(dataUri);
+
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+
+ byte[] buffer = new byte[256];
+ for (int len = 0; (len = is.read(buffer)) != -1; ) {
+ os.write(buffer, 0, len);
+ }
+ } else {
+ if (LOCAL_LOGV) {
+ Log.v(TAG, "Saving data to: " + uri);
+ }
+ os.write(data);
}
- os.write(data);
}
} catch (FileNotFoundException e) {
Log.e(TAG, "Failed to open Input/Output stream.", e);
@@ -998,6 +1021,7 @@ public class PduPersister {
+ "content://mms/drafts, content://mms/outbox, "
+ "content://mms/temp.");
}
+ PDU_CACHE_INSTANCE.purge(uri);
PduHeaders header = pdu.getPduHeaders();
PduBody body = null;
diff --git a/core/java/com/google/android/net/GoogleHttpClient.java b/core/java/com/google/android/net/GoogleHttpClient.java
index 922f5be..8a1298f 100644
--- a/core/java/com/google/android/net/GoogleHttpClient.java
+++ b/core/java/com/google/android/net/GoogleHttpClient.java
@@ -58,8 +58,8 @@ import java.net.URISyntaxException;
* and otherwise tweak HTTP requests.
*/
public class GoogleHttpClient implements HttpClient {
-
private static final String TAG = "GoogleHttpClient";
+ private static final boolean LOCAL_LOGV = Config.LOGV || false;
/** Exception thrown when a request is blocked by the URL rules. */
public static class BlockedRequestException extends IOException {
@@ -289,9 +289,7 @@ public class GoogleHttpClient implements HttpClient {
wrapper.setURI(uri);
request = wrapper;
- if (Config.LOGV) {
- Log.v(TAG, "Rule " + rule.mName + ": " + original + " -> " + rewritten);
- }
+ if (LOCAL_LOGV) Log.v(TAG, "Rule " + rule.mName + ": " + original + " -> " + rewritten);
return executeWithoutRewriting(request, context);
}
diff --git a/core/java/com/google/android/net/UrlRules.java b/core/java/com/google/android/net/UrlRules.java
index c269d1b..54d139d 100644
--- a/core/java/com/google/android/net/UrlRules.java
+++ b/core/java/com/google/android/net/UrlRules.java
@@ -20,6 +20,7 @@ import android.content.ContentResolver;
import android.database.Cursor;
import android.provider.Checkin;
import android.provider.Settings;
+import android.util.Config;
import android.util.Log;
import java.util.ArrayList;
@@ -53,6 +54,9 @@ import java.util.regex.Pattern;
* </pre>
*/
public class UrlRules {
+ public static final String TAG = "UrlRules";
+ public static final boolean LOCAL_LOGV = Config.LOGV || false;
+
/** Thrown when the rewrite rules can't be parsed. */
public static class RuleFormatException extends Exception {
public RuleFormatException(String msg) { super(msg); }
@@ -192,10 +196,11 @@ public class UrlRules {
Settings.Gservices.PROVISIONING_DIGEST);
if (sCachedDigest != null && sCachedDigest.equals(digest)) {
// The digest is the same, so the rules are the same.
+ if (LOCAL_LOGV) Log.v(TAG, "Using cached rules for digest: " + digest);
return sCachedRules;
}
- // Get all the Gservices settings with names starting with "url:".
+ if (LOCAL_LOGV) Log.v(TAG, "Scanning for Gservices \"url:*\" rules");
Cursor cursor = resolver.query(Settings.Gservices.CONTENT_URI,
new String[] {
Settings.Gservices.NAME,
@@ -210,16 +215,18 @@ public class UrlRules {
String name = cursor.getString(0).substring(4); // "url:X"
String value = cursor.getString(1);
if (value == null || value.length() == 0) continue;
+ if (LOCAL_LOGV) Log.v(TAG, " Rule " + name + ": " + value);
rules.add(new Rule(name, value));
} catch (RuleFormatException e) {
// Oops, Gservices has an invalid rule! Skip it.
- Log.e("UrlRules", "Invalid rule from Gservices", e);
+ Log.e(TAG, "Invalid rule from Gservices", e);
Checkin.logEvent(resolver,
Checkin.Events.Tag.GSERVICES_ERROR, e.toString());
}
}
sCachedRules = new UrlRules(rules.toArray(new Rule[rules.size()]));
sCachedDigest = digest;
+ if (LOCAL_LOGV) Log.v(TAG, "New rules stored for digest: " + digest);
} finally {
cursor.close();
}
diff --git a/core/jni/.android_server_BluetoothEventLoop.cpp.swp b/core/jni/.android_server_BluetoothEventLoop.cpp.swp
new file mode 100644
index 0000000..d36e403
--- /dev/null
+++ b/core/jni/.android_server_BluetoothEventLoop.cpp.swp
Binary files differ
diff --git a/core/jni/ActivityManager.cpp b/core/jni/ActivityManager.cpp
index 9017827..8950dfb 100644
--- a/core/jni/ActivityManager.cpp
+++ b/core/jni/ActivityManager.cpp
@@ -16,9 +16,9 @@
#include <unistd.h>
#include <android_runtime/ActivityManager.h>
-#include <utils/IBinder.h>
-#include <utils/IServiceManager.h>
-#include <utils/Parcel.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
#include <utils/String8.h>
namespace android {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 888cb11..015268b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -11,10 +11,16 @@ else
LOCAL_CFLAGS += -DPACKED=""
endif
+ifeq ($(WITH_JIT),true)
+ LOCAL_CFLAGS += -DWITH_JIT
+endif
+
ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),)
LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX)
endif
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
LOCAL_SRC_FILES:= \
ActivityManager.cpp \
AndroidRuntime.cpp \
@@ -39,7 +45,6 @@ LOCAL_SRC_FILES:= \
android_text_AndroidCharacter.cpp \
android_text_KeyCharacterMap.cpp \
android_os_Debug.cpp \
- android_os_Exec.cpp \
android_os_FileUtils.cpp \
android_os_MemoryFile.cpp \
android_os_ParcelFileDescriptor.cpp \
@@ -104,13 +109,12 @@ LOCAL_SRC_FILES:= \
android_util_FileObserver.cpp \
android/opengl/poly_clip.cpp.arm \
android/opengl/util.cpp.arm \
- android_bluetooth_Database.cpp \
android_bluetooth_HeadsetBase.cpp \
android_bluetooth_common.cpp \
android_bluetooth_BluetoothAudioGateway.cpp \
- android_bluetooth_RfcommSocket.cpp \
+ android_bluetooth_BluetoothSocket.cpp \
android_bluetooth_ScoSocket.cpp \
- android_server_BluetoothDeviceService.cpp \
+ android_server_BluetoothService.cpp \
android_server_BluetoothEventLoop.cpp \
android_server_BluetoothA2dpService.cpp \
android_message_digest_sha1.cpp \
@@ -150,11 +154,11 @@ LOCAL_SHARED_LIBRARIES := \
libnativehelper \
libcutils \
libutils \
+ libbinder \
libnetutils \
libui \
libskiagl \
- libsgl \
- libcorecg \
+ libskia \
libsqlite \
libdvm \
libEGL \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index c815301..f61e247 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -19,12 +19,12 @@
//#define LOG_NDEBUG 0
#include <android_runtime/AndroidRuntime.h>
-#include <utils/IBinder.h>
-#include <utils/IServiceManager.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <utils/misc.h>
-#include <utils/Parcel.h>
-#include <utils/string_array.h>
+#include <binder/Parcel.h>
+#include <utils/StringArray.h>
#include <utils/threads.h>
#include <cutils/properties.h>
@@ -130,7 +130,6 @@ extern int register_android_os_Power(JNIEnv *env);
extern int register_android_os_StatFs(JNIEnv *env);
extern int register_android_os_SystemProperties(JNIEnv *env);
extern int register_android_os_Hardware(JNIEnv* env);
-extern int register_android_os_Exec(JNIEnv *env);
extern int register_android_os_SystemClock(JNIEnv* env);
extern int register_android_os_FileObserver(JNIEnv *env);
extern int register_android_os_FileUtils(JNIEnv *env);
@@ -143,12 +142,11 @@ extern int register_android_security_Md5MessageDigest(JNIEnv *env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_KeyCharacterMap(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
-extern int register_android_bluetooth_Database(JNIEnv* env);
extern int register_android_bluetooth_HeadsetBase(JNIEnv* env);
extern int register_android_bluetooth_BluetoothAudioGateway(JNIEnv* env);
-extern int register_android_bluetooth_RfcommSocket(JNIEnv *env);
+extern int register_android_bluetooth_BluetoothSocket(JNIEnv *env);
extern int register_android_bluetooth_ScoSocket(JNIEnv *env);
-extern int register_android_server_BluetoothDeviceService(JNIEnv* env);
+extern int register_android_server_BluetoothService(JNIEnv* env);
extern int register_android_server_BluetoothEventLoop(JNIEnv *env);
extern int register_android_server_BluetoothA2dpService(JNIEnv* env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
@@ -509,11 +507,17 @@ static void readLocale(char* language, char* region)
//LOGD("language=%s region=%s\n", language, region);
}
-void AndroidRuntime::start(const char* className, const bool startSystemServer)
+/*
+ * Start the Dalvik Virtual Machine.
+ *
+ * Various arguments, most determined by system properties, are passed in.
+ * The "mOptions" vector is updated.
+ *
+ * Returns 0 on success.
+ */
+int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
- LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
-
- JNIEnv* env;
+ int result = -1;
JavaVMInitArgs initArgs;
JavaVMOption opt;
char propBuf[PROPERTY_VALUE_MAX];
@@ -521,25 +525,20 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
char dexoptFlagsBuf[PROPERTY_VALUE_MAX];
char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];
char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
+ char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
char* stackTraceFile = NULL;
- char* slashClassName = NULL;
- char* cp;
bool checkJni = false;
+ bool checkDexSum = false;
bool logStdio = false;
- enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault;
+ enum {
+ kEMDefault,
+ kEMIntPortable,
+ kEMIntFast,
+#if defined(WITH_JIT)
+ kEMJitCompiler,
+#endif
+ } executionMode = kEMDefault;
- blockSigpipe();
-
- /*
- * 'startSystemServer == true' means runtime is obslete and not run from
- * init.rc anymore, so we print out the boot start event here.
- */
- if (startSystemServer) {
- /* track our progress through the boot sequence */
- const int LOG_BOOT_PROGRESS_START = 3000;
- LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
- ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
- }
property_get("dalvik.vm.checkjni", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
@@ -557,10 +556,19 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
executionMode = kEMIntPortable;
} else if (strcmp(propBuf, "int:fast") == 0) {
executionMode = kEMIntFast;
+#if defined(WITH_JIT)
+ } else if (strcmp(propBuf, "int:jit") == 0) {
+ executionMode = kEMJitCompiler;
+#endif
}
property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, "");
+ property_get("dalvik.vm.check-dex-sum", propBuf, "");
+ if (strcmp(propBuf, "true") == 0) {
+ checkDexSum = true;
+ }
+
property_get("log.redirect-stdio", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
logStdio = true;
@@ -572,19 +580,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
strcpy(jniOptsBuf, "-Xjniopts:");
property_get("dalvik.vm.jniopts", jniOptsBuf+10, "");
- const char* rootDir = getenv("ANDROID_ROOT");
- if (rootDir == NULL) {
- rootDir = "/system";
- if (!hasDir("/system")) {
- LOG_FATAL("No root directory specified, and /android does not exist.");
- return;
- }
- setenv("ANDROID_ROOT", rootDir, 1);
- }
-
- const char* kernelHack = getenv("LD_ASSUME_KERNEL");
- //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
-
/* route exit() to our handler */
opt.extraInfo = (void*) runtime_exit;
opt.optionString = "exit";
@@ -603,16 +598,10 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
mOptions.add(opt);
//options[curOpt++].optionString = "-verbose:class";
-#ifdef CUSTOM_RUNTIME_HEAP_MAX
-#define __make_max_heap_opt(val) #val
-#define _make_max_heap_opt(val) "-Xmx" __make_max_heap_opt(val)
- opt.optionString = _make_max_heap_opt(CUSTOM_RUNTIME_HEAP_MAX);
-#undef __make_max_heap_opt
-#undef _make_max_heap_opt
-#else
- /* limit memory use to 16MB */
- opt.optionString = "-Xmx16m";
-#endif
+ strcpy(heapsizeOptsBuf, "-Xmx");
+ property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
+ //LOGI("Heap size: %s", heapsizeOptsBuf);
+ opt.optionString = heapsizeOptsBuf;
mOptions.add(opt);
/*
@@ -658,6 +647,10 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
if (opc != NULL) {
opt.optionString = "-Xgenregmap";
mOptions.add(opt);
+
+ /* turn on precise GC while we're at it */
+ opt.optionString = "-Xgc:precise";
+ mOptions.add(opt);
}
}
@@ -697,13 +690,87 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
//opt.optionString = "-verbose:jni";
//mOptions.add(opt);
}
+
+#if defined(WITH_JIT)
+ /* Minimal profile threshold to trigger JIT compilation */
+ char jitThresholdBuf[sizeof("-Xthreshold:") + PROPERTY_VALUE_MAX];
+ property_get("dalvik.vm.jit.threshold", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ strcpy(jitThresholdBuf, "-Xthreshold:");
+ strcat(jitThresholdBuf, propBuf);
+ opt.optionString = jitThresholdBuf;
+ mOptions.add(opt);
+ }
+
+ /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */
+ char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX];
+ property_get("dalvik.vm.jit.op", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ strcpy(jitOpBuf, "-Xjitop:");
+ strcat(jitOpBuf, propBuf);
+ opt.optionString = jitOpBuf;
+ mOptions.add(opt);
+ }
+
+ /*
+ * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only
+ * for non-selected opcodes.
+ */
+ property_get("dalvik.vm.jit.includeop", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ opt.optionString = "-Xincludeselectedop";
+ mOptions.add(opt);
+ }
+
+ /* Force interpreter-only mode for selected methods */
+ char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX];
+ property_get("dalvik.vm.jit.method", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ strcpy(jitMethodBuf, "-Xjitmethod:");
+ strcat(jitMethodBuf, propBuf);
+ opt.optionString = jitMethodBuf;
+ mOptions.add(opt);
+ }
+
+ /*
+ * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only
+ * for non-selected methods.
+ */
+ property_get("dalvik.vm.jit.includemethod", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ opt.optionString = "-Xincludeselectedmethod";
+ mOptions.add(opt);
+ }
+
+ /*
+ * Enable profile collection on JIT'ed code.
+ */
+ property_get("dalvik.vm.jit.profile", propBuf, "");
+ if (strlen(propBuf) > 0) {
+ opt.optionString = "-Xjitprofile";
+ mOptions.add(opt);
+ }
+#endif
+
if (executionMode == kEMIntPortable) {
opt.optionString = "-Xint:portable";
mOptions.add(opt);
} else if (executionMode == kEMIntFast) {
opt.optionString = "-Xint:fast";
mOptions.add(opt);
+#if defined(WITH_JIT)
+ } else if (executionMode == kEMJitCompiler) {
+ opt.optionString = "-Xint:jit";
+ mOptions.add(opt);
+#endif
}
+
+ if (checkDexSum) {
+ /* perform additional DEX checksum tests */
+ opt.optionString = "-Xcheckdexsum";
+ mOptions.add(opt);
+ }
+
if (logStdio) {
/* convert stdout/stderr to log messages */
opt.optionString = "-Xlog-stdio";
@@ -771,11 +838,61 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
- if (JNI_CreateJavaVM(&mJavaVM, &env, &initArgs) < 0) {
+ if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
LOGE("JNI_CreateJavaVM failed\n");
goto bail;
}
+ result = 0;
+
+bail:
+ free(stackTraceFile);
+ return result;
+}
+
+/*
+ * Start the Android runtime. This involves starting the virtual machine
+ * and calling the "static void main(String[] args)" method in the class
+ * named by "className".
+ */
+void AndroidRuntime::start(const char* className, const bool startSystemServer)
+{
+ LOGD("\n>>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<\n");
+
+ char* slashClassName = NULL;
+ char* cp;
+ JNIEnv* env;
+
+ blockSigpipe();
+
+ /*
+ * 'startSystemServer == true' means runtime is obslete and not run from
+ * init.rc anymore, so we print out the boot start event here.
+ */
+ if (startSystemServer) {
+ /* track our progress through the boot sequence */
+ const int LOG_BOOT_PROGRESS_START = 3000;
+ LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
+ ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
+ }
+
+ const char* rootDir = getenv("ANDROID_ROOT");
+ if (rootDir == NULL) {
+ rootDir = "/system";
+ if (!hasDir("/system")) {
+ LOG_FATAL("No root directory specified, and /android does not exist.");
+ goto bail;
+ }
+ setenv("ANDROID_ROOT", rootDir, 1);
+ }
+
+ //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
+ //LOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
+
+ /* start the virtual machine */
+ if (startVm(&mJavaVM, &env) != 0)
+ goto bail;
+
/*
* Register android functions.
*/
@@ -845,7 +962,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
bail:
free(slashClassName);
- free(stackTraceFile);
}
void AndroidRuntime::start()
@@ -1095,7 +1211,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_database_SQLiteQuery),
REG_JNI(register_android_database_SQLiteStatement),
REG_JNI(register_android_os_Debug),
- REG_JNI(register_android_os_Exec),
REG_JNI(register_android_os_FileObserver),
REG_JNI(register_android_os_FileUtils),
REG_JNI(register_android_os_ParcelFileDescriptor),
@@ -1117,12 +1232,11 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_ToneGenerator),
REG_JNI(register_android_opengl_classes),
- REG_JNI(register_android_bluetooth_Database),
REG_JNI(register_android_bluetooth_HeadsetBase),
REG_JNI(register_android_bluetooth_BluetoothAudioGateway),
- REG_JNI(register_android_bluetooth_RfcommSocket),
+ REG_JNI(register_android_bluetooth_BluetoothSocket),
REG_JNI(register_android_bluetooth_ScoSocket),
- REG_JNI(register_android_server_BluetoothDeviceService),
+ REG_JNI(register_android_server_BluetoothService),
REG_JNI(register_android_server_BluetoothEventLoop),
REG_JNI(register_android_server_BluetoothA2dpService),
REG_JNI(register_android_message_digest_sha1),
diff --git a/core/jni/CursorWindow.cpp b/core/jni/CursorWindow.cpp
index fb891c9..7864189 100644
--- a/core/jni/CursorWindow.cpp
+++ b/core/jni/CursorWindow.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "CursorWindow"
#include <utils/Log.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
#include <assert.h>
#include <string.h>
diff --git a/core/jni/CursorWindow.h b/core/jni/CursorWindow.h
index 0fb074f..e98b009 100644
--- a/core/jni/CursorWindow.h
+++ b/core/jni/CursorWindow.h
@@ -21,7 +21,7 @@
#include <stddef.h>
#include <stdint.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
#include <utils/RefBase.h>
#include <jni.h>
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 3fb07a7..002d3db 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -5,7 +5,7 @@
#include "SkDither.h"
#include "SkUnPreMultiply.h"
-#include "Parcel.h"
+#include <binder/Parcel.h>
#include "android_util_Binder.h"
#include "android_nio_utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 1c2e055..dc72008 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -23,6 +23,7 @@
#include "SkGLCanvas.h"
#include "SkGraphics.h"
#include "SkImageRef_GlobalPool.h"
+#include "SkPorterDuff.h"
#include "SkShader.h"
#include "SkTemplates.h"
@@ -324,7 +325,7 @@ public:
static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas,
jint color, SkPorterDuff::Mode mode) {
- canvas->drawColor(color, mode);
+ canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
}
static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas,
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index b6ec4a2..ebfb209 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -21,6 +21,7 @@
#include "SkColorFilter.h"
#include "SkColorMatrixFilter.h"
+#include "SkPorterDuff.h"
namespace android {
@@ -32,8 +33,9 @@ public:
}
static SkColorFilter* CreatePorterDuffFilter(JNIEnv* env, jobject,
- jint srcColor, SkPorterDuff::Mode porterDuffMode) {
- return SkColorFilter::CreatePorterDuffFilter(srcColor, porterDuffMode);
+ jint srcColor, SkPorterDuff::Mode mode) {
+ return SkColorFilter::CreateModeFilter(srcColor,
+ SkPorterDuff::ToXfermodeMode(mode));
}
static SkColorFilter* CreateLightingFilter(JNIEnv* env, jobject,
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d1fe83e..6b7f045 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -48,6 +48,13 @@ static JMetricsID gFontMetrics_fieldID;
static jclass gFontMetricsInt_class;
static JMetricsID gFontMetricsInt_fieldID;
+static void defaultSettingsForAndroid(SkPaint* paint) {
+ // looks best we decided
+ paint->setHinting(SkPaint::kSlight_Hinting);
+ // utf16 is required for java
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+}
+
class SkPaintGlue {
public:
@@ -57,8 +64,7 @@ public:
static SkPaint* init(JNIEnv* env, jobject clazz) {
SkPaint* obj = new SkPaint();
- // utf16 is required for java
- obj->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ defaultSettingsForAndroid(obj);
return obj;
}
@@ -69,8 +75,7 @@ public:
static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) {
obj->reset();
- // utf16 is required for java
- obj->setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ defaultSettingsForAndroid(obj);
}
static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) {
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index effb1c8..11c608c 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -75,9 +75,8 @@ public:
return result;
}
- static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, SkPath::BoundsType btype) {
- SkRect bounds_;
- obj->computeBounds(&bounds_, btype);
+ static void computeBounds(JNIEnv* env, jobject clazz, SkPath* obj, jobject bounds, int boundstype) {
+ const SkRect& bounds_ = obj->getBounds();
GraphicsJNI::rect_to_jrectf(bounds_, env, bounds);
}
diff --git a/core/jni/android/graphics/Region.cpp b/core/jni/android/graphics/Region.cpp
index 1dc0314..723cd37 100644
--- a/core/jni/android/graphics/Region.cpp
+++ b/core/jni/android/graphics/Region.cpp
@@ -134,7 +134,7 @@ static void Region_scale(JNIEnv* env, jobject region, jfloat scale, jobject dst)
////////////////////////////////////////////////////////////////////////////////////////////////////////////
-#include "Parcel.h"
+#include <binder/Parcel.h>
#include "android_util_Binder.h"
static SkRegion* Region_createFromParcel(JNIEnv* env, jobject clazz, jobject parcel)
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 21dde63..238ece1 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -141,6 +141,25 @@ static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath)
return SkTypeface::CreateFromFile(str.c_str());
}
+#define MIN_GAMMA (0.1f)
+#define MAX_GAMMA (10.0f)
+static float pinGamma(float gamma) {
+ if (gamma < MIN_GAMMA) {
+ gamma = MIN_GAMMA;
+ } else if (gamma > MAX_GAMMA) {
+ gamma = MAX_GAMMA;
+ }
+ return gamma;
+}
+
+extern void skia_set_text_gamma(float, float);
+
+static void Typeface_setGammaForText(JNIEnv* env, jobject, jfloat blackGamma,
+ jfloat whiteGamma) {
+ // Comment this out for release builds. This is only used during development
+ skia_set_text_gamma(pinGamma(blackGamma), pinGamma(whiteGamma));
+}
+
///////////////////////////////////////////////////////////////////////////////
static JNINativeMethod gTypefaceMethods[] = {
@@ -151,7 +170,8 @@ static JNINativeMethod gTypefaceMethods[] = {
{ "nativeCreateFromAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;)I",
(void*)Typeface_createFromAsset },
{ "nativeCreateFromFile", "(Ljava/lang/String;)I",
- (void*)Typeface_createFromFile }
+ (void*)Typeface_createFromFile },
+ { "setGammaForText", "(FF)V", (void*)Typeface_setGammaForText },
};
int register_android_graphics_Typeface(JNIEnv* env);
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index 41044db..4041346 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -1,16 +1,16 @@
/**
** Copyright 2007, The Android Open Source Project
**
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
**
- ** http://www.apache.org/licenses/LICENSE-2.0
+ ** http://www.apache.org/licenses/LICENSE-2.0
**
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -53,18 +53,18 @@ public:
MallocHelper() {
mData = 0;
}
-
+
~MallocHelper() {
if (mData != 0) {
free(mData);
}
}
-
+
void* alloc(size_t size) {
mData = malloc(size);
return mData;
}
-
+
private:
void* mData;
};
@@ -88,17 +88,17 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength,
int result = POLY_CLIP_OUT;
float* pTransformed = 0;
int transformedIndexCount = 0;
-
+
if ( indexCount < 3 ) {
return POLY_CLIP_OUT;
}
-
+
// Find out how many vertices we need to transform
// We transform every vertex between the min and max indices, inclusive.
// This is OK for the data sets we expect to use with this function, but
// for other loads it might be better to use a more sophisticated vertex
// cache of some sort.
-
+
int minIndex = 65536;
int maxIndex = -1;
for(int i = 0; i < indexCount; i++) {
@@ -110,18 +110,18 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength,
maxIndex = index;
}
}
-
+
if ( maxIndex * 3 > positionsLength) {
return -1;
}
-
+
transformedIndexCount = maxIndex - minIndex + 1;
pTransformed = (float*) mallocHelper.alloc(transformedIndexCount * 4 * sizeof(float));
-
+
if (pTransformed == 0 ) {
return -2;
}
-
+
// Transform the vertices
{
const float* pSrc = pPositions + 3 * minIndex;
@@ -130,9 +130,9 @@ int visibilityTest(float* pWS, float* pPositions, int positionsLength,
mx4transform(pSrc[0], pSrc[1], pSrc[2], 1.0f, pWS, pDst);
}
}
-
+
// Clip the triangles
-
+
Poly poly;
float* pDest = & poly.vert[0].sx;
for (int i = 0; i < indexCount; i += 3) {
@@ -166,14 +166,14 @@ public:
mEnv->ReleasePrimitiveArrayCritical(mRef, mBase, mReleaseParam);
}
}
-
+
// We seperate the bounds check from the initialization because we want to
// be able to bounds-check multiple arrays, and we can't throw an exception
// after we've called GetPrimitiveArrayCritical.
-
+
// Return true if the bounds check succeeded
// Else instruct the runtime to throw an exception
-
+
bool check() {
if ( ! mRef) {
mEnv->ThrowNew(gIAEClass, "array == null");
@@ -190,9 +190,9 @@ public:
}
return true;
}
-
+
// Bind the array.
-
+
void bind() {
mBase = (T*) mEnv->GetPrimitiveArrayCritical(mRef, (jboolean *) 0);
mData = mBase + mOffset;
@@ -201,10 +201,10 @@ public:
void commitChanges() {
mReleaseParam = 0;
}
-
+
T* mData;
int mLength;
-
+
private:
T* mBase;
JNIEnv* mEnv;
@@ -226,29 +226,29 @@ inline float distance2(float x, float y, float z) {
inline float distance(float x, float y, float z) {
return sqrtf(distance2(x, y, z));
}
-
+
static
void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
jfloatArray positions_ref, jint positionsOffset, jint positionsCount,
jfloatArray sphere_ref, jint sphereOffset) {
FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
FloatArrayHelper sphere(env, sphere_ref, sphereOffset, 4);
-
+
bool checkOK = positions.check() && sphere.check();
if (! checkOK) {
return;
}
-
+
positions.bind();
sphere.bind();
-
+
if ( positionsCount < 1 ) {
env->ThrowNew(gIAEClass, "positionsCount < 1");
return;
}
-
+
const float* pSrc = positions.mData;
-
+
// find bounding box
float x0 = *pSrc++;
float x1 = x0;
@@ -256,7 +256,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
float y1 = y0;
float z0 = *pSrc++;
float z1 = z0;
-
+
for(int i = 1; i < positionsCount; i++) {
{
float x = *pSrc++;
@@ -286,7 +286,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
}
}
}
-
+
// Because we know our input meshes fit pretty well into bounding boxes,
// just take the diagonal of the box as defining our sphere.
float* pSphere = sphere.mData;
@@ -297,7 +297,7 @@ void util_computeBoundingSphere(JNIEnv *env, jclass clazz,
*pSphere++ = y0 + dy * 0.5f;
*pSphere++ = z0 + dz * 0.5f;
*pSphere++ = distance(dx, dy, dz) * 0.5f;
-
+
sphere.commitChanges();
}
@@ -344,7 +344,7 @@ static void computeFrustum(const float* m, float* f) {
normalizePlane(f);
f+= 4;
- // left
+ // left
f[0] = m3 + m[0];
f[1] = m7 + m[4];
f[2] = m11 + m[8];
@@ -396,20 +396,20 @@ int util_frustumCullSpheres(JNIEnv *env, jclass clazz,
FloatArrayHelper mvp(env, mvp_ref, mvpOffset, 16);
FloatArrayHelper spheres(env, spheres_ref, spheresOffset, spheresCount * 4);
IntArrayHelper results(env, results_ref, resultsOffset, resultsCapacity);
-
+
bool initializedOK = mvp.check() && spheres.check() && results.check();
if (! initializedOK) {
return -1;
}
-
+
mvp.bind();
spheres.bind();
results.bind();
-
+
computeFrustum(mvp.mData, frustum);
-
+
// Cull the spheres
-
+
pSphere = spheres.mData;
pResults = results.mData;
outputCount = 0;
@@ -436,27 +436,27 @@ int util_visibilityTest(JNIEnv *env, jclass clazz,
jfloatArray ws_ref, jint wsOffset,
jfloatArray positions_ref, jint positionsOffset,
jcharArray indices_ref, jint indicesOffset, jint indexCount) {
-
+
FloatArrayHelper ws(env, ws_ref, wsOffset, 16);
FloatArrayHelper positions(env, positions_ref, positionsOffset, 0);
UnsignedShortArrayHelper indices(env, indices_ref, indicesOffset, 0);
-
+
bool checkOK = ws.check() && positions.check() && indices.check();
if (! checkOK) {
// Return value will be ignored, because an exception has been thrown.
return -1;
}
-
+
if (indices.mLength < indexCount) {
env->ThrowNew(gIAEClass, "length < offset + indexCount");
// Return value will be ignored, because an exception has been thrown.
return -1;
}
-
+
ws.bind();
positions.bind();
indices.bind();
-
+
return visibilityTest(ws.mData,
positions.mData, positions.mLength,
indices.mData, indexCount);
@@ -496,19 +496,19 @@ void util_multiplyMM(JNIEnv *env, jclass clazz,
FloatArrayHelper resultMat(env, result_ref, resultOffset, 16);
FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 16);
-
+
bool checkOK = resultMat.check() && lhs.check() && rhs.check();
-
+
if ( !checkOK ) {
return;
}
-
+
resultMat.bind();
lhs.bind();
rhs.bind();
-
+
multiplyMM(resultMat.mData, lhs.mData, rhs.mData);
-
+
resultMat.commitChanges();
}
@@ -527,19 +527,19 @@ void util_multiplyMV(JNIEnv *env, jclass clazz,
FloatArrayHelper resultV(env, result_ref, resultOffset, 4);
FloatArrayHelper lhs(env, lhs_ref, lhsOffset, 16);
FloatArrayHelper rhs(env, rhs_ref, rhsOffset, 4);
-
+
bool checkOK = resultV.check() && lhs.check() && rhs.check();
-
+
if ( !checkOK ) {
return;
}
-
+
resultV.bind();
lhs.bind();
rhs.bind();
-
+
multiplyMV(resultV.mData, lhs.mData, rhs.mData);
-
+
resultV.commitChanges();
}
@@ -550,7 +550,7 @@ static jfieldID nativeBitmapID = 0;
void nativeUtilsClassInit(JNIEnv *env, jclass clazz)
{
jclass bitmapClass = env->FindClass("android/graphics/Bitmap");
- nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
+ nativeBitmapID = env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
}
static int checkFormat(SkBitmap::Config config, int format, int type)
@@ -653,7 +653,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz,
}
int err = checkFormat(config, internalformat, type);
if (err)
- return err;
+ return err;
bitmap.lockPixels();
const int w = bitmap.width();
const int h = bitmap.height();
@@ -665,14 +665,15 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz,
}
const size_t size = bitmap.getSize();
const size_t palette_size = 256*sizeof(SkPMColor);
- void* const data = malloc(size + palette_size);
+ const size_t imageSize = size + palette_size;
+ void* const data = malloc(imageSize);
if (data) {
void* const pixels = (char*)data + palette_size;
SkColorTable* ctable = bitmap.getColorTable();
memcpy(data, ctable->lockColors(), ctable->count() * sizeof(SkPMColor));
memcpy(pixels, p, size);
ctable->unlockColors(false);
- glCompressedTexImage2D(target, level, internalformat, w, h, border, 0, data);
+ glCompressedTexImage2D(target, level, internalformat, w, h, border, imageSize, data);
free(data);
} else {
err = -1;
@@ -700,7 +701,7 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz,
}
int err = checkFormat(config, format, type);
if (err)
- return err;
+ return err;
bitmap.lockPixels();
const int w = bitmap.width();
const int h = bitmap.height();
@@ -723,22 +724,22 @@ lookupClasses(JNIEnv* env) {
}
static JNINativeMethod gMatrixMethods[] = {
- { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
- { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
+ { "multiplyMM", "([FI[FI[FI)V", (void*)util_multiplyMM },
+ { "multiplyMV", "([FI[FI[FI)V", (void*)util_multiplyMV },
};
static JNINativeMethod gVisiblityMethods[] = {
- { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
+ { "computeBoundingSphere", "([FII[FI)V", (void*)util_computeBoundingSphere },
{ "frustumCullSpheres", "([FI[FII[III)I", (void*)util_frustumCullSpheres },
- { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
+ { "visibilityTest", "([FI[FI[CII)I", (void*)util_visibilityTest },
};
static JNINativeMethod gUtilsMethods[] = {
{"nativeClassInit", "()V", (void*)nativeUtilsClassInit },
- { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
- { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
- { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
- { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
+ { "native_getInternalFormat", "(Landroid/graphics/Bitmap;)I", (void*) util_getInternalFormat },
+ { "native_getType", "(Landroid/graphics/Bitmap;)I", (void*) util_getType },
+ { "native_texImage2D", "(IIILandroid/graphics/Bitmap;II)I", (void*)util_texImage2D },
+ { "native_texSubImage2D", "(IIIILandroid/graphics/Bitmap;II)I", (void*)util_texSubImage2D },
};
typedef struct _ClassRegistrationInfo {
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
new file mode 100644
index 0000000..51cf0cb
--- /dev/null
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -0,0 +1,534 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BluetoothSocket.cpp"
+
+#include "android_bluetooth_common.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "JNIHelp.h"
+#include "utils/Log.h"
+#include "cutils/abort_socket.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_BLUETOOTH
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sco.h>
+#endif
+
+#define TYPE_AS_STR(t) \
+ ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
+
+namespace android {
+
+static jfieldID field_mAuth; /* read-only */
+static jfieldID field_mEncrypt; /* read-only */
+static jfieldID field_mType; /* read-only */
+static jfieldID field_mAddress; /* read-only */
+static jfieldID field_mPort; /* read-only */
+static jfieldID field_mSocketData;
+static jmethodID method_BluetoothSocket_ctor;
+static jclass class_BluetoothSocket;
+
+/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
+static const int TYPE_RFCOMM = 1;
+static const int TYPE_SCO = 2;
+static const int TYPE_L2CAP = 3; // TODO: Test l2cap code paths
+
+static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
+
+static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
+ struct asocket *s =
+ (struct asocket *) env->GetIntField(obj, field_mSocketData);
+ if (!s)
+ jniThrowException(env, "java/io/IOException", "null socketData");
+ return s;
+}
+
+static void initSocketFromFdNative(JNIEnv *env, jobject obj, jint fd) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ struct asocket *s = asocket_init(fd);
+
+ if (!s) {
+ LOGV("asocket_init() failed, throwing");
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ env->SetIntField(obj, field_mSocketData, (jint)s);
+
+ return;
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static void initSocketNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int fd;
+ int lm = 0;
+ int sndbuf;
+ jboolean auth;
+ jboolean encrypt;
+ jint type;
+
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ break;
+ case TYPE_SCO:
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ break;
+ case TYPE_L2CAP:
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
+
+ if (fd < 0) {
+ LOGV("socket() failed, throwing");
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ auth = env->GetBooleanField(obj, field_mAuth);
+ encrypt = env->GetBooleanField(obj, field_mEncrypt);
+
+ /* kernel does not yet support LM for SCO */
+ switch (type) {
+ case TYPE_RFCOMM:
+ lm |= auth ? RFCOMM_LM_AUTH : 0;
+ lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+ break;
+ case TYPE_L2CAP:
+ lm |= auth ? L2CAP_LM_AUTH : 0;
+ lm |= encrypt? L2CAP_LM_ENCRYPT : 0;
+ break;
+ }
+
+ if (lm) {
+ if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
+ LOGV("setsockopt(RFCOMM_LM) failed, throwing");
+ jniThrowIOException(env, errno);
+ return;
+ }
+ }
+
+ if (type == TYPE_RFCOMM) {
+ sndbuf = RFCOMM_SO_SNDBUF;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
+ LOGV("setsockopt(SO_SNDBUF) failed, throwing");
+ jniThrowIOException(env, errno);
+ return;
+ }
+ }
+
+ LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
+
+ initSocketFromFdNative(env, obj, fd);
+ return;
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static void connectNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int ret;
+ jint type;
+ const char *c_address;
+ jstring address;
+ bdaddr_t bdaddress;
+ socklen_t addr_sz;
+ struct sockaddr *addr;
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return;
+
+ type = env->GetIntField(obj, field_mType);
+
+ /* parse address into bdaddress */
+ address = (jstring) env->GetObjectField(obj, field_mAddress);
+ c_address = env->GetStringUTFChars(address, NULL);
+ if (get_bdaddr(c_address, &bdaddress)) {
+ env->ReleaseStringUTFChars(address, c_address);
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ env->ReleaseStringUTFChars(address, c_address);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+
+ memset(addr, 0, addr_sz);
+ addr_rc.rc_family = AF_BLUETOOTH;
+ addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+
+ memset(addr, 0, addr_sz);
+ addr_sco.sco_family = AF_BLUETOOTH;
+ memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+
+ memset(addr, 0, addr_sz);
+ addr_l2.l2_family = AF_BLUETOOTH;
+ addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
+
+ ret = asocket_connect(s, addr, addr_sz, -1);
+ LOGV("...connect(%d, %s) = %d (errno %d)",
+ s->fd, TYPE_AS_STR(type), ret, errno);
+
+ if (ret)
+ jniThrowIOException(env, errno);
+
+ return;
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static void bindListenNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ jint type;
+ socklen_t addr_sz;
+ struct sockaddr *addr;
+ bdaddr_t bdaddr = *BDADDR_ANY;
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return;
+
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+
+ memset(addr, 0, addr_sz);
+ addr_rc.rc_family = AF_BLUETOOTH;
+ addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+
+ memset(addr, 0, addr_sz);
+ addr_sco.sco_family = AF_BLUETOOTH;
+ memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+
+ memset(addr, 0, addr_sz);
+ addr_l2.l2_family = AF_BLUETOOTH;
+ addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
+
+ if (bind(s->fd, addr, addr_sz)) {
+ LOGV("...bind(%d) gave errno %d", s->fd, errno);
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ if (listen(s->fd, 1)) {
+ LOGV("...listen(%d) gave errno %d", s->fd, errno);
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ LOGV("...bindListenNative(%d) success", s->fd);
+
+ return;
+
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int fd;
+ jint type;
+ struct sockaddr *addr;
+ socklen_t addr_sz;
+ jstring addr_jstr;
+ char addr_cstr[BTADDR_SIZE];
+ bdaddr_t *bdaddr;
+ jboolean auth;
+ jboolean encrypt;
+
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return NULL;
+
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+ bdaddr = &addr_rc.rc_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+ bdaddr = &addr_sco.sco_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+ bdaddr = &addr_l2.l2_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return NULL;
+ }
+
+ fd = asocket_accept(s, addr, &addr_sz, timeout);
+
+ LOGV("...accept(%d, %s) = %d (errno %d)",
+ s->fd, TYPE_AS_STR(type), fd, errno);
+
+ if (fd < 0) {
+ jniThrowIOException(env, errno);
+ return NULL;
+ }
+
+ /* Connected - return new BluetoothSocket */
+ auth = env->GetBooleanField(obj, field_mAuth);
+ encrypt = env->GetBooleanField(obj, field_mEncrypt);
+
+ get_bdaddr_as_string(bdaddr, addr_cstr);
+
+ addr_jstr = env->NewStringUTF(addr_cstr);
+ return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
+ type, fd, auth, encrypt, addr_jstr, -1);
+
+#endif
+ jniThrowIOException(env, ENOSYS);
+ return NULL;
+}
+
+static jint availableNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int available;
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return -1;
+
+ if (ioctl(s->fd, FIONREAD, &available) < 0) {
+ jniThrowIOException(env, errno);
+ return -1;
+ }
+
+ return available;
+
+#endif
+ jniThrowIOException(env, ENOSYS);
+ return -1;
+}
+
+/** jb must not be null. offset and offset+length must be within array */
+static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+ jint length) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int ret;
+ jbyte *b;
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return -1;
+
+ b = env->GetByteArrayElements(jb, NULL);
+ if (b == NULL) {
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ ret = asocket_read(s, &b[offset], length, -1);
+ if (ret < 0) {
+ jniThrowIOException(env, errno);
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+ return -1;
+ }
+
+ env->ReleaseByteArrayElements(jb, b, 0);
+ return (jint)ret;
+
+#endif
+ jniThrowIOException(env, ENOSYS);
+ return -1;
+}
+
+/** jb must not be null. offset and offset+length must be within array */
+static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+ jint length) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+
+ int ret;
+ jbyte *b;
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return -1;
+
+ b = env->GetByteArrayElements(jb, NULL);
+ if (b == NULL) {
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ ret = asocket_write(s, &b[offset], length, -1);
+ if (ret < 0) {
+ jniThrowIOException(env, errno);
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+ return -1;
+ }
+
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT); // no need to commit
+ return (jint)ret;
+
+#endif
+ jniThrowIOException(env, ENOSYS);
+ return -1;
+}
+
+static void abortNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ struct asocket *s = get_socketData(env, obj);
+
+ if (!s)
+ return;
+
+ asocket_abort(s);
+
+ LOGV("...asocket_abort(%d) complete", s->fd);
+ return;
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static void destroyNative(JNIEnv *env, jobject obj) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ struct asocket *s = get_socketData(env, obj);
+ int fd = s->fd;
+
+ if (!s)
+ return;
+
+ asocket_destroy(s);
+
+ LOGV("...asocket_destroy(%d) complete", fd);
+ return;
+#endif
+ jniThrowIOException(env, ENOSYS);
+}
+
+static JNINativeMethod sMethods[] = {
+ {"initSocketNative", "()V", (void*) initSocketNative},
+ {"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
+ {"connectNative", "()V", (void *) connectNative},
+ {"bindListenNative", "()V", (void *) bindListenNative},
+ {"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
+ {"availableNative", "()I", (void *) availableNative},
+ {"readNative", "([BII)I", (void *) readNative},
+ {"writeNative", "([BII)I", (void *) writeNative},
+ {"abortNative", "()V", (void *) abortNative},
+ {"destroyNative", "()V", (void *) destroyNative},
+};
+
+int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/bluetooth/BluetoothSocket");
+ if (clazz == NULL)
+ return -1;
+ class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
+ field_mType = env->GetFieldID(clazz, "mType", "I");
+ field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
+ field_mPort = env->GetFieldID(clazz, "mPort", "I");
+ field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
+ field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
+ field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
+ method_BluetoothSocket_ctor = env->GetMethodID(clazz, "<init>", "(IIZZLjava/lang/String;I)V");
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
+
diff --git a/core/jni/android_bluetooth_Database.cpp b/core/jni/android_bluetooth_Database.cpp
deleted file mode 100644
index 73b8efd..0000000
--- a/core/jni/android_bluetooth_Database.cpp
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Database"
-#define LOG_TAG "bluetooth_Database.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-
-#ifdef HAVE_BLUETOOTH
-#include <dbus/dbus.h>
-#endif
-
-namespace android {
-
-#ifdef HAVE_BLUETOOTH
-static DBusConnection* conn = NULL; // Singleton thread-safe connection
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- conn = NULL;
-#endif
-}
-
-static void initializeNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-
-#ifdef HAVE_BLUETOOTH
- if (conn == NULL) {
- DBusError err;
- dbus_error_init(&err);
- dbus_threads_init_default();
- conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
- if (dbus_error_is_set(&err)) {
- LOGE("Could not get onto the system bus!");
- dbus_error_free(&err);
- }
- dbus_connection_set_exit_on_disconnect(conn, FALSE);
- }
-#endif
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-}
-
-static jint addServiceRecordNative(JNIEnv *env, jobject object,
- jbyteArray record) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- if (conn != NULL) {
- jbyte* c_record = env->GetByteArrayElements(record, NULL);
- DBusMessage *reply = dbus_func_args(env,
- conn,
- BLUEZ_DBUS_BASE_PATH,
- DBUS_CLASS_NAME,
- "AddServiceRecord",
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &c_record,
- env->GetArrayLength(record),
- DBUS_TYPE_INVALID);
- env->ReleaseByteArrayElements(record, c_record, JNI_ABORT);
- return reply ? dbus_returns_uint32(env, reply) : -1;
- }
-#endif
- return -1;
-}
-
-static jint addServiceRecordFromXmlNative(JNIEnv *env, jobject object,
- jstring record) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- if (conn != NULL) {
- const char *c_record = env->GetStringUTFChars(record, NULL);
- DBusMessage *reply = dbus_func_args(env,
- conn,
- BLUEZ_DBUS_BASE_PATH,
- DBUS_CLASS_NAME,
- "AddServiceRecordFromXML",
- DBUS_TYPE_STRING, &c_record,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(record, c_record);
- return reply ? dbus_returns_uint32(env, reply) : -1;
- }
-#endif
- return -1;
-}
-
-static void updateServiceRecordNative(JNIEnv *env, jobject object,
- jint handle,
- jbyteArray record) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- if (conn != NULL) {
- jbyte* c_record = env->GetByteArrayElements(record, NULL);
- DBusMessage *reply = dbus_func_args(env,
- conn,
- BLUEZ_DBUS_BASE_PATH,
- DBUS_CLASS_NAME,
- "UpdateServiceRecord",
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &c_record,
- env->GetArrayLength(record),
- DBUS_TYPE_INVALID);
- env->ReleaseByteArrayElements(record, c_record, JNI_ABORT);
- }
-#endif
-}
-
-static void updateServiceRecordFromXmlNative(JNIEnv *env, jobject object,
- jint handle,
- jstring record) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- if (conn != NULL) {
- const char *c_record = env->GetStringUTFChars(record, NULL);
- DBusMessage *reply = dbus_func_args(env,
- conn,
- BLUEZ_DBUS_BASE_PATH,
- DBUS_CLASS_NAME,
- "UpdateServiceRecordFromXML",
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_STRING, &c_record,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(record, c_record);
- }
-#endif
-}
-
-/* private static native void removeServiceRecordNative(int handle); */
-static void removeServiceRecordNative(JNIEnv *env, jobject object,
- jint handle) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- if (conn != NULL) {
- DBusMessage *reply = dbus_func_args(env,
- conn,
- BLUEZ_DBUS_BASE_PATH,
- DBUS_CLASS_NAME,
- "RemoveServiceRecord",
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID);
- }
-#endif
-}
-
-
-static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"classInitNative", "()V", (void*)classInitNative},
- {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
- {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
- {"addServiceRecordNative", "([B)I", (void*)addServiceRecordNative},
- {"addServiceRecordFromXmlNative", "(Ljava/lang/String;)I", (void*)addServiceRecordFromXmlNative},
- {"updateServiceRecordNative", "(I[B)V", (void*)updateServiceRecordNative},
- {"updateServiceRecordFromXmlNative", "(ILjava/lang/String;)V", (void*)updateServiceRecordFromXmlNative},
- {"removeServiceRecordNative", "(I)V", (void*)removeServiceRecordNative},
-};
-
-int register_android_bluetooth_Database(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/bluetooth/Database", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_bluetooth_RfcommSocket.cpp b/core/jni/android_bluetooth_RfcommSocket.cpp
deleted file mode 100644
index 3ed35d9..0000000
--- a/core/jni/android_bluetooth_RfcommSocket.cpp
+++ /dev/null
@@ -1,621 +0,0 @@
-/*
-** Copyright 2006, 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.
-*/
-
-#define LOG_TAG "bluetooth_RfcommSocket.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/poll.h>
-
-#ifdef HAVE_BLUETOOTH
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-#endif
-
-namespace android {
-
-#ifdef HAVE_BLUETOOTH
-static jfieldID field_mNativeData;
-static jfieldID field_mTimeoutRemainingMs;
-static jfieldID field_mAcceptTimeoutRemainingMs;
-static jfieldID field_mAddress;
-static jfieldID field_mPort;
-
-typedef struct {
- jstring address;
- const char *c_address;
- int rfcomm_channel;
- int last_read_err;
- int rfcomm_sock;
- // < 0 -- in progress,
- // 0 -- not connected
- // > 0 connected
- // 1 input is open
- // 2 output is open
- // 3 both input and output are open
- int rfcomm_connected;
- int rfcomm_sock_flags;
-} native_data_t;
-
-static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
- return (native_data_t *)(env->GetIntField(object, field_mNativeData));
-}
-
-static inline void init_socket_info(
- JNIEnv *env, jobject object,
- native_data_t *nat,
- jstring address,
- jint rfcomm_channel) {
- nat->address = (jstring)env->NewGlobalRef(address);
- nat->c_address = env->GetStringUTFChars(nat->address, NULL);
- nat->rfcomm_channel = (int)rfcomm_channel;
-}
-
-static inline void cleanup_socket_info(JNIEnv *env, native_data_t *nat) {
- if (nat->c_address != NULL) {
- env->ReleaseStringUTFChars(nat->address, nat->c_address);
- env->DeleteGlobalRef(nat->address);
- nat->c_address = NULL;
- }
-}
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- field_mNativeData = get_field(env, clazz, "mNativeData", "I");
- field_mTimeoutRemainingMs = get_field(env, clazz, "mTimeoutRemainingMs", "I");
- field_mAcceptTimeoutRemainingMs = get_field(env, clazz, "mAcceptTimeoutRemainingMs", "I");
- field_mAddress = get_field(env, clazz, "mAddress", "Ljava/lang/String;");
- field_mPort = get_field(env, clazz, "mPort", "I");
-#endif
-}
-
-static void initializeNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
- native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
- if (nat == NULL) {
- LOGE("%s: out of memory!", __FUNCTION__);
- return;
- }
-
- env->SetIntField(object, field_mNativeData, (jint)nat);
- nat->rfcomm_sock = -1;
- nat->rfcomm_connected = 0;
-#endif
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- free(nat);
- }
-#endif
-}
-
-static jobject createNative(JNIEnv *env, jobject obj) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- int lm;
- native_data_t *nat = get_native_data(env, obj);
- nat->rfcomm_sock = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
-
- if (nat->rfcomm_sock < 0) {
- LOGE("%s: Could not create RFCOMM socket: %s\n", __FUNCTION__,
- strerror(errno));
- return NULL;
- }
-
- lm = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
-
- if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm,
- sizeof(lm)) < 0) {
- LOGE("%s: Can't set RFCOMM link mode", __FUNCTION__);
- close(nat->rfcomm_sock);
- return NULL;
- }
-
- return jniCreateFileDescriptor(env, nat->rfcomm_sock);
-#else
- return NULL;
-#endif
-}
-
-static void destroyNative(JNIEnv *env, jobject obj) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, obj);
- cleanup_socket_info(env, nat);
- if (nat->rfcomm_sock >= 0) {
- close(nat->rfcomm_sock);
- nat->rfcomm_sock = -1;
- }
-#endif
-}
-
-
-static jboolean connectNative(JNIEnv *env, jobject obj,
- jstring address, jint port) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, obj);
-
- if (nat->rfcomm_sock >= 0) {
- if (nat->rfcomm_connected) {
- LOGI("RFCOMM socket: %s.",
- (nat->rfcomm_connected > 0) ? "already connected" : "connection is in progress");
- return JNI_TRUE;
- }
-
- init_socket_info(env, obj, nat, address, port);
-
- struct sockaddr_rc addr;
- memset(&addr, 0, sizeof(struct sockaddr_rc));
- get_bdaddr(nat->c_address, &addr.rc_bdaddr);
- addr.rc_channel = nat->rfcomm_channel;
- addr.rc_family = AF_BLUETOOTH;
- nat->rfcomm_connected = 0;
-
- while (nat->rfcomm_connected == 0) {
- if (connect(nat->rfcomm_sock, (struct sockaddr *)&addr,
- sizeof(addr)) < 0) {
- if (errno == EINTR) continue;
- LOGE("connect error: %s (%d)\n", strerror(errno), errno);
- break;
- } else {
- nat->rfcomm_connected = 3; // input and output
- }
- }
- } else {
- LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
- }
-
- if (nat->rfcomm_connected > 0) {
- env->SetIntField(obj, field_mPort, port);
- return JNI_TRUE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean connectAsyncNative(JNIEnv *env, jobject obj,
- jstring address, jint port) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, obj);
-
- if (nat->rfcomm_sock < 0) {
- LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
- return JNI_FALSE;
- }
-
- if (nat->rfcomm_connected) {
- LOGI("RFCOMM socket: %s.",
- (nat->rfcomm_connected > 0) ?
- "already connected" : "connection is in progress");
- return JNI_TRUE;
- }
-
- init_socket_info(env, obj, nat, address, port);
-
- struct sockaddr_rc addr;
- memset(&addr, 0, sizeof(struct sockaddr_rc));
- get_bdaddr(nat->c_address, &addr.rc_bdaddr);
- addr.rc_channel = nat->rfcomm_channel;
- addr.rc_family = AF_BLUETOOTH;
-
- nat->rfcomm_sock_flags = fcntl(nat->rfcomm_sock, F_GETFL, 0);
- if (fcntl(nat->rfcomm_sock,
- F_SETFL, nat->rfcomm_sock_flags | O_NONBLOCK) >= 0) {
- int rc;
- nat->rfcomm_connected = 0;
- errno = 0;
- rc = connect(nat->rfcomm_sock,
- (struct sockaddr *)&addr,
- sizeof(addr));
-
- if (rc >= 0) {
- nat->rfcomm_connected = 3;
- LOGI("RFCOMM async connect immediately successful");
- env->SetIntField(obj, field_mPort, port);
- return JNI_TRUE;
- }
- else if (rc < 0) {
- if (errno == EINPROGRESS || errno == EAGAIN)
- {
- LOGI("RFCOMM async connect is in progress (%s)",
- strerror(errno));
- nat->rfcomm_connected = -1;
- env->SetIntField(obj, field_mPort, port);
- return JNI_TRUE;
- }
- else
- {
- LOGE("RFCOMM async connect error (%d): %s (%d)",
- nat->rfcomm_sock, strerror(errno), errno);
- return JNI_FALSE;
- }
- }
- } // fcntl(nat->rfcomm_sock ...)
-#endif
- return JNI_FALSE;
-}
-
-static jboolean interruptAsyncConnectNative(JNIEnv *env, jobject obj) {
- //WRITEME
- return JNI_TRUE;
-}
-
-static jint waitForAsyncConnectNative(JNIEnv *env, jobject obj,
- jint timeout_ms) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- struct sockaddr_rc addr;
- native_data_t *nat = get_native_data(env, obj);
-
- env->SetIntField(obj, field_mTimeoutRemainingMs, timeout_ms);
-
- if (nat->rfcomm_sock < 0) {
- LOGE("%s: socket(RFCOMM) error: socket not created", __FUNCTION__);
- return -1;
- }
-
- if (nat->rfcomm_connected > 0) {
- LOGI("%s: RFCOMM is already connected!", __FUNCTION__);
- return 1;
- }
-
- /* Do an asynchronous select() */
- int n;
- fd_set rset, wset;
- struct timeval to;
-
- FD_ZERO(&rset);
- FD_ZERO(&wset);
- FD_SET(nat->rfcomm_sock, &rset);
- FD_SET(nat->rfcomm_sock, &wset);
- if (timeout_ms >= 0) {
- to.tv_sec = timeout_ms / 1000;
- to.tv_usec = 1000 * (timeout_ms % 1000);
- }
- n = select(nat->rfcomm_sock + 1,
- &rset,
- &wset,
- NULL,
- (timeout_ms < 0 ? NULL : &to));
-
- if (timeout_ms > 0) {
- jint remaining = to.tv_sec*1000 + to.tv_usec/1000;
- LOGI("Remaining time %ldms", (long)remaining);
- env->SetIntField(obj, field_mTimeoutRemainingMs,
- remaining);
- }
-
- if (n <= 0) {
- if (n < 0) {
- LOGE("select() on RFCOMM socket: %s (%d)",
- strerror(errno),
- errno);
- return -1;
- }
- return 0;
- }
- /* n must be equal to 1 and either rset or wset must have the
- file descriptor set. */
- LOGI("select() returned %d.", n);
- if (FD_ISSET(nat->rfcomm_sock, &rset) ||
- FD_ISSET(nat->rfcomm_sock, &wset)) {
- /* A trial async read() will tell us if everything is OK. */
- char ch;
- errno = 0;
- int nr = read(nat->rfcomm_sock, &ch, 1);
- /* It should be that nr != 1 because we just opened a socket
- and we haven't sent anything over it for the other side to
- respond... but one can't be paranoid enough.
- */
- if (nr >= 0 || errno != EAGAIN) {
- LOGE("RFCOMM async connect() error: %s (%d), nr = %d\n",
- strerror(errno),
- errno,
- nr);
- /* Clear the rfcomm_connected flag to cause this function
- to re-create the socket and re-attempt the connect()
- the next time it is called.
- */
- nat->rfcomm_connected = 0;
- /* Restore the blocking properties of the socket. */
- fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
- return -1;
- }
- /* Restore the blocking properties of the socket. */
- fcntl(nat->rfcomm_sock, F_SETFL, nat->rfcomm_sock_flags);
- LOGI("Successful RFCOMM socket connect.");
- nat->rfcomm_connected = 3; // input and output
- return 1;
- }
-#endif
- return -1;
-}
-
-static jboolean shutdownNative(JNIEnv *env, jobject obj,
- jboolean shutdownInput) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- /* NOTE: If you change the bcode to modify nat, make sure you
- add synchronize(this) to the method calling this native
- method.
- */
- native_data_t *nat = get_native_data(env, obj);
- if (nat->rfcomm_sock < 0) {
- LOGE("socket(RFCOMM) error: socket not created");
- return JNI_FALSE;
- }
- int rc = shutdown(nat->rfcomm_sock,
- shutdownInput ? SHUT_RD : SHUT_WR);
- if (!rc) {
- nat->rfcomm_connected &=
- shutdownInput ? ~1 : ~2;
- return JNI_TRUE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jint isConnectedNative(JNIEnv *env, jobject obj) {
- LOGI(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- const native_data_t *nat = get_native_data(env, obj);
- return nat->rfcomm_connected;
-#endif
- return 0;
-}
-
-//@@@@@@@@@ bind to device???
-static jboolean bindNative(JNIEnv *env, jobject obj, jstring device,
- jint port) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-
- /* NOTE: If you change the code to modify nat, make sure you
- add synchronize(this) to the method calling this native
- method.
- */
- const native_data_t *nat = get_native_data(env, obj);
- if (nat->rfcomm_sock < 0) {
- LOGE("socket(RFCOMM) error: socket not created");
- return JNI_FALSE;
- }
-
- struct sockaddr_rc laddr;
- int lm;
-
- lm = 0;
-/*
- lm |= RFCOMM_LM_MASTER;
- lm |= RFCOMM_LM_AUTH;
- lm |= RFCOMM_LM_ENCRYPT;
- lm |= RFCOMM_LM_SECURE;
-*/
-
- if (lm && setsockopt(nat->rfcomm_sock, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm)) < 0) {
- LOGE("Can't set RFCOMM link mode");
- return JNI_FALSE;
- }
-
- laddr.rc_family = AF_BLUETOOTH;
- bacpy(&laddr.rc_bdaddr, BDADDR_ANY);
- laddr.rc_channel = port;
-
- if (bind(nat->rfcomm_sock, (struct sockaddr *)&laddr, sizeof(laddr)) < 0) {
- LOGE("Can't bind RFCOMM socket");
- return JNI_FALSE;
- }
-
- env->SetIntField(obj, field_mPort, port);
-
- return JNI_TRUE;
-#endif
- return JNI_FALSE;
-}
-
-static jboolean listenNative(JNIEnv *env, jobject obj, jint backlog) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- /* NOTE: If you change the code to modify nat, make sure you
- add synchronize(this) to the method calling this native
- method.
- */
- const native_data_t *nat = get_native_data(env, obj);
- if (nat->rfcomm_sock < 0) {
- LOGE("socket(RFCOMM) error: socket not created");
- return JNI_FALSE;
- }
- return listen(nat->rfcomm_sock, backlog) < 0 ? JNI_FALSE : JNI_TRUE;
-#else
- return JNI_FALSE;
-#endif
-}
-
-static int set_nb(int sk, bool nb) {
- int flags = fcntl(sk, F_GETFL);
- if (flags < 0) {
- LOGE("Can't get socket flags with fcntl(): %s (%d)",
- strerror(errno), errno);
- close(sk);
- return -1;
- }
- flags &= ~O_NONBLOCK;
- if (nb) flags |= O_NONBLOCK;
- int status = fcntl(sk, F_SETFL, flags);
- if (status < 0) {
- LOGE("Can't set socket to nonblocking mode with fcntl(): %s (%d)",
- strerror(errno), errno);
- close(sk);
- return -1;
- }
- return 0;
-}
-
-// Note: the code should check at a higher level to see whether
-// listen() has been called.
-#ifdef HAVE_BLUETOOTH
-static int do_accept(JNIEnv* env, jobject object, int sock,
- jobject newsock,
- jfieldID out_address,
- bool must_succeed) {
-
- if (must_succeed && set_nb(sock, true) < 0)
- return -1;
-
- struct sockaddr_rc raddr;
- int alen = sizeof(raddr);
- int nsk = accept(sock, (struct sockaddr *) &raddr, &alen);
- if (nsk < 0) {
- LOGE("Error on accept from socket fd %d: %s (%d).",
- sock,
- strerror(errno),
- errno);
- if (must_succeed) set_nb(sock, false);
- return -1;
- }
-
- char addr[BTADDR_SIZE];
- get_bdaddr_as_string(&raddr.rc_bdaddr, addr);
- env->SetObjectField(newsock, out_address, env->NewStringUTF(addr));
-
- LOGI("Successful accept() on AG socket %d: new socket %d, address %s, RFCOMM channel %d",
- sock,
- nsk,
- addr,
- raddr.rc_channel);
- if (must_succeed) set_nb(sock, false);
- return nsk;
-}
-#endif /*HAVE_BLUETOOTH*/
-
-static jobject acceptNative(JNIEnv *env, jobject obj,
- jobject newsock, jint timeoutMs) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, obj);
- if (nat->rfcomm_sock < 0) {
- LOGE("socket(RFCOMM) error: socket not created");
- return JNI_FALSE;
- }
-
- if (newsock == NULL) {
- LOGE("%s: newsock = NULL\n", __FUNCTION__);
- return JNI_FALSE;
- }
-
- int nsk = -1;
- if (timeoutMs < 0) {
- /* block until accept() succeeds */
- nsk = do_accept(env, obj, nat->rfcomm_sock,
- newsock, field_mAddress, false);
- if (nsk < 0) {
- return NULL;
- }
- }
- else {
- /* wait with a timeout */
-
- struct pollfd fds;
- fds.fd = nat->rfcomm_sock;
- fds.events = POLLIN | POLLPRI | POLLOUT | POLLERR;
-
- env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, 0);
- int n = poll(&fds, 1, timeoutMs);
- if (n <= 0) {
- if (n < 0) {
- LOGE("listening poll() on RFCOMM socket: %s (%d)",
- strerror(errno),
- errno);
- env->SetIntField(obj, field_mAcceptTimeoutRemainingMs, timeoutMs);
- }
- else {
- LOGI("listening poll() on RFCOMM socket timed out");
- }
- return NULL;
- }
-
- LOGI("listening poll() on RFCOMM socket returned %d", n);
- if (fds.fd == nat->rfcomm_sock) {
- if (fds.revents & (POLLIN | POLLPRI | POLLOUT)) {
- LOGI("Accepting connection.\n");
- nsk = do_accept(env, obj, nat->rfcomm_sock,
- newsock, field_mAddress, true);
- if (nsk < 0) {
- return NULL;
- }
- }
- }
- }
-
- LOGI("Connection accepted, new socket fd = %d.", nsk);
- native_data_t *newnat = get_native_data(env, newsock);
- newnat->rfcomm_sock = nsk;
- newnat->rfcomm_connected = 3;
- return jniCreateFileDescriptor(env, nsk);
-#else
- return NULL;
-#endif
-}
-
-static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"classInitNative", "()V", (void*)classInitNative},
- {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
- {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
-
- {"createNative", "()Ljava/io/FileDescriptor;", (void *)createNative},
- {"destroyNative", "()V", (void *)destroyNative},
- {"connectNative", "(Ljava/lang/String;I)Z", (void *)connectNative},
- {"connectAsyncNative", "(Ljava/lang/String;I)Z", (void *)connectAsyncNative},
- {"interruptAsyncConnectNative", "()Z", (void *)interruptAsyncConnectNative},
- {"waitForAsyncConnectNative", "(I)I", (void *)waitForAsyncConnectNative},
- {"shutdownNative", "(Z)Z", (void *)shutdownNative},
- {"isConnectedNative", "()I", (void *)isConnectedNative},
-
- {"bindNative", "(Ljava/lang/String;I)Z", (void*)bindNative},
- {"listenNative", "(I)Z", (void*)listenNative},
- {"acceptNative", "(Landroid/bluetooth/RfcommSocket;I)Ljava/io/FileDescriptor;", (void*)acceptNative},
-};
-
-int register_android_bluetooth_RfcommSocket(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/bluetooth/RfcommSocket", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index 0b8a604..343fa53 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -36,6 +36,43 @@
namespace android {
#ifdef HAVE_BLUETOOTH
+
+static Properties remote_device_properties[] = {
+ {"Address", DBUS_TYPE_STRING},
+ {"Name", DBUS_TYPE_STRING},
+ {"Icon", DBUS_TYPE_STRING},
+ {"Class", DBUS_TYPE_UINT32},
+ {"UUIDs", DBUS_TYPE_ARRAY},
+ {"Paired", DBUS_TYPE_BOOLEAN},
+ {"Connected", DBUS_TYPE_BOOLEAN},
+ {"Trusted", DBUS_TYPE_BOOLEAN},
+ {"Alias", DBUS_TYPE_STRING},
+ {"Nodes", DBUS_TYPE_ARRAY},
+ {"Adapter", DBUS_TYPE_OBJECT_PATH},
+ {"LegacyPairing", DBUS_TYPE_BOOLEAN},
+ {"RSSI", DBUS_TYPE_INT16},
+ {"TX", DBUS_TYPE_UINT32}
+};
+
+static Properties adapter_properties[] = {
+ {"Address", DBUS_TYPE_STRING},
+ {"Name", DBUS_TYPE_STRING},
+ {"Class", DBUS_TYPE_UINT32},
+ {"Powered", DBUS_TYPE_BOOLEAN},
+ {"Discoverable", DBUS_TYPE_BOOLEAN},
+ {"DiscoverableTimeout", DBUS_TYPE_UINT32},
+ {"Pairable", DBUS_TYPE_BOOLEAN},
+ {"PairableTimeout", DBUS_TYPE_UINT32},
+ {"Discovering", DBUS_TYPE_BOOLEAN},
+ {"Devices", DBUS_TYPE_ARRAY},
+};
+
+typedef union {
+ char *str_val;
+ int int_val;
+ char **array_val;
+} property_value;
+
jfieldID get_field(JNIEnv *env, jclass clazz, const char *member,
const char *mtype) {
jfieldID field = env->GetFieldID(clazz, member, mtype);
@@ -332,6 +369,44 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) {
return ret;
}
+static void set_object_array_element(JNIEnv *env, jobjectArray strArray,
+ const char *value, int index) {
+ jstring obj;
+ obj = env->NewStringUTF(value);
+ env->SetObjectArrayElement(strArray, index, obj);
+ env->DeleteLocalRef(obj);
+}
+
+jobjectArray dbus_returns_array_of_object_path(JNIEnv *env,
+ DBusMessage *reply) {
+
+ DBusError err;
+ char **list;
+ int i, len;
+ jobjectArray strArray = NULL;
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args (reply,
+ &err,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
+ &list, &len,
+ DBUS_TYPE_INVALID)) {
+ jclass stringClass;
+ jstring classNameStr;
+
+ stringClass = env->FindClass("java/lang/String");
+ strArray = env->NewObjectArray(len, stringClass, NULL);
+
+ for (i = 0; i < len; i++)
+ set_object_array_element(env, strArray, list[i], i);
+ } else {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+ }
+
+ dbus_message_unref(reply);
+ return strArray;
+}
+
jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
DBusError err;
@@ -353,11 +428,8 @@ jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
stringClass = env->FindClass("java/lang/String");
strArray = env->NewObjectArray(len, stringClass, NULL);
- for (i = 0; i < len; i++) {
- //LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]);
- env->SetObjectArrayElement(strArray, i,
- env->NewStringUTF(list[i]));
- }
+ for (i = 0; i < len; i++)
+ set_object_array_element(env, strArray, list[i], i);
} else {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
}
@@ -390,17 +462,263 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) {
return byteArray;
}
-void get_bdaddr(const char *str, bdaddr_t *ba) {
+void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter value_iter;
+ char var_type[2] = { type, '\0'};
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter);
+ dbus_message_iter_append_basic(&value_iter, type, val);
+ dbus_message_iter_close_container(iter, &value_iter);
+}
+
+int get_property(DBusMessageIter iter, Properties *properties,
+ int max_num_properties, int *prop_index, property_value *value, int *len) {
+ DBusMessageIter prop_val, array_val_iter;
+ char *property = NULL;
+ uint32_t array_type;
+ char *str_val;
+ int i, j, type, int_val;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return -1;
+ dbus_message_iter_get_basic(&iter, &property);
+ if (!dbus_message_iter_next(&iter))
+ return -1;
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return -1;
+ for (i = 0; i < max_num_properties; i++) {
+ if (!strncmp(property, properties[i].name, strlen(property)))
+ break;
+ }
+ *prop_index = i;
+ if (i == max_num_properties)
+ return -1;
+
+ dbus_message_iter_recurse(&iter, &prop_val);
+ type = properties[*prop_index].type;
+ if (dbus_message_iter_get_arg_type(&prop_val) != type) {
+ LOGE("Property type mismatch in get_property: %d, expected:%d, index:%d",
+ dbus_message_iter_get_arg_type(&prop_val), type, *prop_index);
+ return -1;
+ }
+
+ switch(type) {
+ case DBUS_TYPE_STRING:
+ case DBUS_TYPE_OBJECT_PATH:
+ dbus_message_iter_get_basic(&prop_val, &value->str_val);
+ *len = 1;
+ break;
+ case DBUS_TYPE_UINT32:
+ case DBUS_TYPE_INT16:
+ case DBUS_TYPE_BOOLEAN:
+ dbus_message_iter_get_basic(&prop_val, &int_val);
+ value->int_val = int_val;
+ *len = 1;
+ break;
+ case DBUS_TYPE_ARRAY:
+ dbus_message_iter_recurse(&prop_val, &array_val_iter);
+ array_type = dbus_message_iter_get_arg_type(&array_val_iter);
+ *len = 0;
+ value->array_val = NULL;
+ if (array_type == DBUS_TYPE_OBJECT_PATH ||
+ array_type == DBUS_TYPE_STRING){
+ j = 0;
+ do {
+ j ++;
+ } while(dbus_message_iter_next(&array_val_iter));
+ dbus_message_iter_recurse(&prop_val, &array_val_iter);
+ // Allocate an array of char *
+ *len = j;
+ char **tmp = (char **)malloc(sizeof(char *) * *len);
+ if (!tmp)
+ return -1;
+ j = 0;
+ do {
+ dbus_message_iter_get_basic(&array_val_iter, &tmp[j]);
+ j ++;
+ } while(dbus_message_iter_next(&array_val_iter));
+ value->array_val = tmp;
+ }
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+void create_prop_array(JNIEnv *env, jobjectArray strArray, Properties *property,
+ property_value *value, int len, int *array_index ) {
+ char **prop_val = NULL;
+ char buf[32] = {'\0'}, buf1[32] = {'\0'};
+ int i;
+
+ char *name = property->name;
+ int prop_type = property->type;
+
+ set_object_array_element(env, strArray, name, *array_index);
+ *array_index += 1;
+
+ if (prop_type == DBUS_TYPE_UINT32 || prop_type == DBUS_TYPE_INT16) {
+ sprintf(buf, "%d", value->int_val);
+ set_object_array_element(env, strArray, buf, *array_index);
+ *array_index += 1;
+ } else if (prop_type == DBUS_TYPE_BOOLEAN) {
+ sprintf(buf, "%s", value->int_val ? "true" : "false");
+
+ set_object_array_element(env, strArray, buf, *array_index);
+ *array_index += 1;
+ } else if (prop_type == DBUS_TYPE_ARRAY) {
+ // Write the length first
+ sprintf(buf1, "%d", len);
+ set_object_array_element(env, strArray, buf1, *array_index);
+ *array_index += 1;
+
+ prop_val = value->array_val;
+ for (i = 0; i < len; i++) {
+ set_object_array_element(env, strArray, prop_val[i], *array_index);
+ *array_index += 1;
+ }
+ } else {
+ set_object_array_element(env, strArray, (const char *) value->str_val, *array_index);
+ *array_index += 1;
+ }
+}
+
+jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
+ const int max_num_properties) {
+ DBusMessageIter dict_entry, dict;
+ jobjectArray strArray = NULL;
+ property_value value;
+ int i, size = 0,array_index = 0;
+ int len = 0, prop_type = DBUS_TYPE_INVALID, prop_index = -1, type;
+ struct {
+ property_value value;
+ int len;
+ bool used;
+ } values[max_num_properties];
+ int t, j;
+
+ jclass stringClass = env->FindClass("java/lang/String");
+ DBusError err;
+ dbus_error_init(&err);
+
+ for (i = 0; i < max_num_properties; i++) {
+ values[i].used = false;
+ }
+
+ if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ goto failure;
+ dbus_message_iter_recurse(iter, &dict);
+ do {
+ len = 0;
+ if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY)
+ goto failure;
+ dbus_message_iter_recurse(&dict, &dict_entry);
+
+ if (!get_property(dict_entry, properties, max_num_properties, &prop_index,
+ &value, &len)) {
+ size += 2;
+ if (properties[prop_index].type == DBUS_TYPE_ARRAY)
+ size += len;
+ values[prop_index].value = value;
+ values[prop_index].len = len;
+ values[prop_index].used = true;
+ } else {
+ goto failure;
+ }
+ } while(dbus_message_iter_next(&dict));
+
+ strArray = env->NewObjectArray(size, stringClass, NULL);
+
+ for (i = 0; i < max_num_properties; i++) {
+ if (values[i].used) {
+ create_prop_array(env, strArray, &properties[i], &values[i].value, values[i].len,
+ &array_index);
+
+ if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used
+ && values[i].value.array_val != NULL)
+ free(values[i].value.array_val);
+ }
+
+ }
+ return strArray;
+
+failure:
+ if (dbus_error_is_set(&err))
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ for (i = 0; i < max_num_properties; i++)
+ if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used == true
+ && values[i].value.array_val != NULL)
+ free(values[i].value.array_val);
+ return NULL;
+}
+
+jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
+ Properties *properties, int max_num_properties) {
+ DBusMessageIter iter;
+ DBusError err;
+ jobjectArray strArray = NULL;
+ jclass stringClass= env->FindClass("java/lang/String");
+ int len = 0, prop_index = -1;
+ int array_index = 0, size = 0;
+ property_value value;
+
+ dbus_error_init(&err);
+ if (!dbus_message_iter_init(msg, &iter))
+ goto failure;
+
+ if (!get_property(iter, properties, max_num_properties,
+ &prop_index, &value, &len)) {
+ size += 2;
+ if (properties[prop_index].type == DBUS_TYPE_ARRAY)
+ size += len;
+ strArray = env->NewObjectArray(size, stringClass, NULL);
+
+ create_prop_array(env, strArray, &properties[prop_index],
+ &value, len, &array_index);
+
+ if (properties[prop_index].type == DBUS_TYPE_ARRAY && value.array_val != NULL)
+ free(value.array_val);
+
+ return strArray;
+ }
+failure:
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ return NULL;
+}
+
+jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
+ return parse_property_change(env, msg, (Properties *) &adapter_properties,
+ sizeof(adapter_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) {
+ return parse_property_change(env, msg, (Properties *) &remote_device_properties,
+ sizeof(remote_device_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
+ return parse_properties(env, iter, (Properties *) &adapter_properties,
+ sizeof(adapter_properties) / sizeof(Properties));
+}
+
+jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) {
+ return parse_properties(env, iter, (Properties *) &remote_device_properties,
+ sizeof(remote_device_properties) / sizeof(Properties));
+}
+
+int get_bdaddr(const char *str, bdaddr_t *ba) {
char *d = ((char *)ba) + 5, *endp;
int i;
for(i = 0; i < 6; i++) {
*d-- = strtol(str, &endp, 16);
if (*endp != ':' && i != 5) {
memset(ba, 0, sizeof(bdaddr_t));
- return;
+ return -1;
}
str = endp + 1;
}
+ return 0;
}
void get_bdaddr_as_string(const bdaddr_t *ba, char *str) {
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index 69092dd..ef9b66b 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -68,6 +68,7 @@ jfieldID get_field(JNIEnv *env,
struct event_loop_native_data_t {
DBusConnection *conn;
+ const char *adapter;
/* protects the thread */
pthread_mutex_t thread_mutex;
@@ -89,6 +90,12 @@ struct event_loop_native_data_t {
jobject me;
};
+struct _Properties {
+ char name[32];
+ int type;
+};
+typedef struct _Properties Properties;
+
dbus_bool_t dbus_func_args_async(JNIEnv *env,
DBusConnection *conn,
int timeout_ms,
@@ -142,9 +149,19 @@ jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply);
jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply);
jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply);
jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply);
+jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply);
jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply);
-void get_bdaddr(const char *str, bdaddr_t *ba);
+jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
+ const int max_num_properties);
+jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
+ Properties *properties, int max_num_properties);
+jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter);
+jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter);
+jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
+jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
+void append_variant(DBusMessageIter *iter, int type, void *val);
+int get_bdaddr(const char *str, bdaddr_t *ba);
void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
bool debug_no_encrypt();
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index f19fbbf..91449bc 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -229,7 +229,7 @@ static jboolean isBlob_native(JNIEnv* env, jobject object, jint row, jint column
{
int32_t err;
CursorWindow * window = GET_WINDOW(env, object);
-LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window);
+LOG_WINDOW("Checking if column is a blob or null for %d,%d from %p", row, column, window);
field_slot_t field;
err = window->read_field_slot(row, column, &field);
@@ -241,6 +241,54 @@ LOG_WINDOW("Checking if column is a blob for %d,%d from %p", row, column, window
return field.type == FIELD_TYPE_BLOB || field.type == FIELD_TYPE_NULL;
}
+static jboolean isString_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a string or null for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_STRING || field.type == FIELD_TYPE_NULL;
+}
+
+static jboolean isInteger_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is an integer for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_INTEGER;
+}
+
+static jboolean isFloat_native(JNIEnv* env, jobject object, jint row, jint column)
+{
+ int32_t err;
+ CursorWindow * window = GET_WINDOW(env, object);
+LOG_WINDOW("Checking if column is a float for %d,%d from %p", row, column, window);
+
+ field_slot_t field;
+ err = window->read_field_slot(row, column, &field);
+ if (err != 0) {
+ throwExceptionWithRowCol(env, row, column);
+ return NULL;
+ }
+
+ return field.type == FIELD_TYPE_FLOAT;
+}
+
static jstring getString_native(JNIEnv* env, jobject object, jint row, jint column)
{
int32_t err;
@@ -326,11 +374,11 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
jniThrowException(env, "java/lang/IllegalStateException", "Unable to get field slot");
return NULL;
}
-
+
jcharArray buffer = (jcharArray)env->GetObjectField(buf, gBufferField);
if (buffer == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "buf should not be null");
- return NULL;
+ return NULL;
}
jchar* dst = env->GetCharArrayElements(buffer, NULL);
uint8_t type = field.type;
@@ -338,7 +386,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
jcharArray newArray = NULL;
if (type == FIELD_TYPE_STRING) {
uint32_t size = field.data.buffer.size;
- if (size > 0) {
+ if (size > 0) {
#if WINDOW_STORAGE_UTF8
// Pass size - 1 since the UTF8 is null terminated and we don't want a null terminator on the UTF16 string
String16 utf16((char const *)window->offsetToPtr(field.data.buffer.offset), size - 1);
@@ -346,7 +394,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
if (strSize > bufferSize || dst == NULL) {
newArray = env->NewCharArray(strSize);
env->SetCharArrayRegion(newArray, 0, strSize, (jchar const *)utf16.string());
- } else {
+ } else {
memcpy(dst, (jchar const *)utf16.string(), strSize * 2);
}
sizeCopied = strSize;
@@ -359,7 +407,7 @@ LOG_WINDOW("Copying string for %d,%d from %p", row, column, window);
memcpy(dst, (jchar const *)window->offsetToPtr(field.data.buffer.offset), size);
}
#endif
- }
+ }
} else if (type == FIELD_TYPE_INTEGER) {
int64_t value;
if (window->getLong(row, column, &value)) {
@@ -628,6 +676,9 @@ static JNINativeMethod sMethods[] =
{"putDouble_native", "(DII)Z", (void *)putDouble_native},
{"freeLastRow_native", "()V", (void *)freeLastRow},
{"putNull_native", "(II)Z", (void *)putNull_native},
+ {"isString_native", "(II)Z", (void *)isString_native},
+ {"isFloat_native", "(II)Z", (void *)isFloat_native},
+ {"isInteger_native", "(II)Z", (void *)isInteger_native},
};
int register_android_database_CursorWindow(JNIEnv * env)
@@ -646,7 +697,7 @@ int register_android_database_CursorWindow(JNIEnv * env)
LOGE("Error locating fields");
return -1;
}
-
+
clazz = env->FindClass("android/database/CharArrayBuffer");
if (clazz == NULL) {
LOGE("Can't find android/database/CharArrayBuffer");
diff --git a/core/jni/android_database_SQLiteDatabase.cpp b/core/jni/android_database_SQLiteDatabase.cpp
index 66858f9..020aff4 100644
--- a/core/jni/android_database_SQLiteDatabase.cpp
+++ b/core/jni/android_database_SQLiteDatabase.cpp
@@ -28,7 +28,10 @@
#include <sqlite3.h>
#include <sqlite3_android.h>
#include <string.h>
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <ctype.h>
#include <stdio.h>
diff --git a/core/jni/android_emoji_EmojiFactory.cpp b/core/jni/android_emoji_EmojiFactory.cpp
index 7d6e24f..4c213a3 100644
--- a/core/jni/android_emoji_EmojiFactory.cpp
+++ b/core/jni/android_emoji_EmojiFactory.cpp
@@ -197,8 +197,11 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
static void android_emoji_EmojiFactory_destructor(
JNIEnv* env, jobject obj, jint nativeEmojiFactory) {
+ /*
+ // Must not delete this object!!
EmojiFactory *factory = reinterpret_cast<EmojiFactory *>(nativeEmojiFactory);
delete factory;
+ */
}
static jint android_emoji_EmojiFactory_getAndroidPuaFromVendorSpecificSjis(
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 9053468..8a312d9 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -25,7 +25,7 @@
#include <ui/Surface.h>
#include <ui/Camera.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
using namespace android;
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index bf0bd65..4aed277 100755
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -16,33 +16,49 @@
#define LOG_TAG "GpsLocationProvider"
+//#define LOG_NDDEBUG 0
+
#include "JNIHelp.h"
#include "jni.h"
#include "hardware_legacy/gps.h"
+#include "hardware_legacy/gps_ni.h"
#include "utils/Log.h"
#include "utils/misc.h"
#include <string.h>
#include <pthread.h>
-
static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER;
static jmethodID method_reportLocation;
static jmethodID method_reportStatus;
static jmethodID method_reportSvStatus;
static jmethodID method_reportAGpsStatus;
+static jmethodID method_reportNmea;
static jmethodID method_xtraDownloadRequest;
+static jmethodID method_reportNiNotification;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
static const AGpsInterface* sAGpsInterface = NULL;
+static const GpsNiInterface* sGpsNiInterface = NULL;
// data written to by GPS callbacks
static GpsLocation sGpsLocation;
static GpsStatus sGpsStatus;
static GpsSvStatus sGpsSvStatus;
static AGpsStatus sAGpsStatus;
+static GpsNiNotification sGpsNiNotification;
+
+// buffer for NMEA data
+#define NMEA_SENTENCE_LENGTH 100
+#define NMEA_SENTENCE_COUNT 40
+struct NmeaSentence {
+ GpsUtcTime timestamp;
+ char nmea[NMEA_SENTENCE_LENGTH];
+};
+static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_LENGTH];
+static int mNmeaSentenceCount = 0;
// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event
// and android_location_GpsLocationProvider_read_status
@@ -50,6 +66,8 @@ static GpsLocation sGpsLocationCopy;
static GpsStatus sGpsStatusCopy;
static GpsSvStatus sGpsSvStatusCopy;
static AGpsStatus sAGpsStatusCopy;
+static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_LENGTH];
+static GpsNiNotification sGpsNiNotificationCopy;
enum CallbackType {
kLocation = 1,
@@ -58,6 +76,8 @@ enum CallbackType {
kAGpsStatus = 8,
kXtraDownloadRequest = 16,
kDisableRequest = 32,
+ kNmeaAvailable = 64,
+ kNiNotification = 128,
};
static int sPendingCallbacks;
@@ -96,6 +116,30 @@ static void sv_status_callback(GpsSvStatus* sv_status)
pthread_mutex_unlock(&sEventMutex);
}
+static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
+{
+ pthread_mutex_lock(&sEventMutex);
+
+ if (length >= NMEA_SENTENCE_LENGTH) {
+ LOGE("NMEA data too long in nmea_callback (length = %d)\n", length);
+ length = NMEA_SENTENCE_LENGTH - 1;
+ }
+ if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) {
+ LOGE("NMEA data overflowed buffer\n");
+ pthread_mutex_unlock(&sEventMutex);
+ return;
+ }
+
+ sPendingCallbacks |= kNmeaAvailable;
+ sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp;
+ memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length);
+ sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0;
+ mNmeaSentenceCount++;
+
+ pthread_cond_signal(&sEventCond);
+ pthread_mutex_unlock(&sEventMutex);
+}
+
static void agps_status_callback(AGpsStatus* agps_status)
{
pthread_mutex_lock(&sEventMutex);
@@ -111,6 +155,7 @@ GpsCallbacks sGpsCallbacks = {
location_callback,
status_callback,
sv_status_callback,
+ nmea_callback
};
static void
@@ -122,6 +167,20 @@ download_request_callback()
pthread_mutex_unlock(&sEventMutex);
}
+static void
+gps_ni_notify_callback(GpsNiNotification *notification)
+{
+ LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id);
+
+ pthread_mutex_lock(&sEventMutex);
+
+ sPendingCallbacks |= kNiNotification;
+ memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification));
+
+ pthread_cond_signal(&sEventCond);
+ pthread_mutex_unlock(&sEventMutex);
+}
+
GpsXtraCallbacks sGpsXtraCallbacks = {
download_request_callback,
};
@@ -130,12 +189,18 @@ AGpsCallbacks sAGpsCallbacks = {
agps_status_callback,
};
+GpsNiCallbacks sGpsNiCallbacks = {
+ gps_ni_notify_callback,
+};
+
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V");
+ method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
+ method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
}
static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
@@ -155,6 +220,12 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
if (sAGpsInterface)
sAGpsInterface->init(&sAGpsCallbacks);
+
+ if (!sGpsNiInterface)
+ sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ if (sGpsNiInterface)
+ sGpsNiInterface->init(&sGpsNiCallbacks);
+
return true;
}
@@ -171,7 +242,7 @@ static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject ob
sGpsInterface->cleanup();
}
-static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode,
+static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode,
jboolean singleFix, jint fixFrequency)
{
int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency));
@@ -196,45 +267,78 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
{
pthread_mutex_lock(&sEventMutex);
pthread_cond_wait(&sEventCond, &sEventMutex);
-
+
// copy and clear the callback flags
int pendingCallbacks = sPendingCallbacks;
sPendingCallbacks = 0;
+ int nmeaSentenceCount = mNmeaSentenceCount;
+ mNmeaSentenceCount = 0;
// copy everything and unlock the mutex before calling into Java code to avoid the possibility
// of timeouts in the GPS engine.
- memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
- memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
- memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
- memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
+ if (pendingCallbacks & kLocation)
+ memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
+ if (pendingCallbacks & kStatus)
+ memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
+ if (pendingCallbacks & kSvStatus)
+ memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
+ if (pendingCallbacks & kAGpsStatus)
+ memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
+ if (pendingCallbacks & kNmeaAvailable)
+ memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0]));
+ if (pendingCallbacks & kNiNotification)
+ memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy));
pthread_mutex_unlock(&sEventMutex);
- if (pendingCallbacks & kLocation) {
+ if (pendingCallbacks & kLocation) {
env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags,
(jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude,
- (jdouble)sGpsLocationCopy.altitude,
- (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,
+ (jdouble)sGpsLocationCopy.altitude,
+ (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,
(jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp);
}
if (pendingCallbacks & kStatus) {
env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status);
- }
+ }
if (pendingCallbacks & kSvStatus) {
env->CallVoidMethod(obj, method_reportSvStatus);
}
if (pendingCallbacks & kAGpsStatus) {
env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status);
}
- if (pendingCallbacks & kXtraDownloadRequest) {
+ if (pendingCallbacks & kNmeaAvailable) {
+ for (int i = 0; i < nmeaSentenceCount; i++) {
+ env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp);
+ }
+ }
+ if (pendingCallbacks & kXtraDownloadRequest) {
env->CallVoidMethod(obj, method_xtraDownloadRequest);
}
if (pendingCallbacks & kDisableRequest) {
// don't need to do anything - we are just poking so wait_for_event will return.
}
+ if (pendingCallbacks & kNiNotification) {
+ LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback.");
+ jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id);
+ jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text);
+ jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras);
+ env->CallVoidMethod(obj, method_reportNiNotification,
+ sGpsNiNotificationCopy.notification_id,
+ sGpsNiNotificationCopy.ni_type,
+ sGpsNiNotificationCopy.notify_flags,
+ sGpsNiNotificationCopy.timeout,
+ sGpsNiNotificationCopy.default_response,
+ reqId,
+ text,
+ sGpsNiNotificationCopy.requestor_id_encoding,
+ sGpsNiNotificationCopy.text_encoding,
+ extras
+ );
+ }
}
-static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
- jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
+static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
+ jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
jintArray maskArray)
{
// this should only be called from within a call to reportStatus, so we don't need to lock here
@@ -264,6 +368,21 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job
return num_svs;
}
+static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size)
+{
+ // this should only be called from within a call to reportNmea, so we don't need to lock here
+
+ jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0);
+
+ int length = strlen(sNmeaBufferCopy[index].nmea);
+ if (length > buffer_size)
+ length = buffer_size;
+ memcpy(nmea, sNmeaBufferCopy[index].nmea, length);
+
+ env->ReleaseByteArrayElements(nmeaArray, nmea, 0);
+ return length;
+}
+
static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time,
jlong timeReference, jint uncertainty)
{
@@ -291,7 +410,7 @@ static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env,
return (sGpsXtraInterface != NULL);
}
-static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
+static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
jbyteArray data, jint length)
{
jbyte* bytes = env->GetByteArrayElements(data, 0);
@@ -348,6 +467,16 @@ static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jo
}
}
+static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj,
+ jint notifId, jint response)
+{
+ if (!sGpsNiInterface)
+ sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE);
+ if (sGpsNiInterface) {
+ sGpsNiInterface->respond(notifId, response);
+ }
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -360,6 +489,7 @@ static JNINativeMethod sMethods[] = {
{"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data},
{"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event},
{"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status},
+ {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
{"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
{"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
{"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
@@ -368,6 +498,7 @@ static JNINativeMethod sMethods[] = {
{"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
{"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
{"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server},
+ {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response},
};
int register_android_location_GpsLocationProvider(JNIEnv* env)
diff --git a/core/jni/android_media_AudioRecord.cpp b/core/jni/android_media_AudioRecord.cpp
index 44a9e8c..0be996d 100644
--- a/core/jni/android_media_AudioRecord.cpp
+++ b/core/jni/android_media_AudioRecord.cpp
@@ -28,8 +28,8 @@
#include "android_runtime/AndroidRuntime.h"
#include "utils/Log.h"
-#include "media/AudioSystem.h"
#include "media/AudioRecord.h"
+#include "media/mediarecorder.h"
// ----------------------------------------------------------------------------
@@ -62,7 +62,7 @@ struct audiorecord_callback_cookie {
#define AUDIORECORD_ERROR_BAD_VALUE -2
#define AUDIORECORD_ERROR_INVALID_OPERATION -3
#define AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT -16
-#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT -17
+#define AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK -17
#define AUDIORECORD_ERROR_SETUP_INVALIDFORMAT -18
#define AUDIORECORD_ERROR_SETUP_INVALIDSOURCE -19
#define AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED -20
@@ -122,17 +122,18 @@ static void recorderCallback(int event, void* user, void *info) {
// ----------------------------------------------------------------------------
static int
android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint source, jint sampleRateInHertz, jint nbChannels,
+ jint source, jint sampleRateInHertz, jint channels,
jint audioFormat, jint buffSizeInBytes)
{
//LOGV(">> Entering android_media_AudioRecord_setup");
- //LOGV("sampleRate=%d, audioFormat=%d, nbChannels=%d, buffSizeInBytes=%d",
- // sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes);
+ //LOGV("sampleRate=%d, audioFormat=%d, channels=%x, buffSizeInBytes=%d",
+ // sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
- if ((nbChannels == 0) || (nbChannels > 2)) {
+ if (!AudioSystem::isInputChannel(channels)) {
LOGE("Error creating AudioRecord: channel count is not 1 or 2.");
- return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT;
+ return AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK;
}
+ uint32_t nbChannels = AudioSystem::popCount(channels);
// compare the format against the Java constants
if ((audioFormat != javaAudioRecordFields.PCM16)
@@ -152,12 +153,7 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
int frameSize = nbChannels * bytesPerSample;
size_t frameCount = buffSizeInBytes / frameSize;
- // convert and check input source value
- // input_source values defined in AudioRecord.h are equal to
- // JAVA MediaRecord.AudioSource values minus 1.
- AudioRecord::input_source arSource = (AudioRecord::input_source)(source - 1);
- if (arSource < AudioRecord::DEFAULT_INPUT ||
- arSource >= AudioRecord::NUM_INPUT_SOURCES) {
+ if (source >= AUDIO_SOURCE_LIST_END) {
LOGE("Error creating AudioRecord: unknown source.");
return AUDIORECORD_ERROR_SETUP_INVALIDSOURCE;
}
@@ -184,10 +180,10 @@ android_media_AudioRecord_setup(JNIEnv *env, jobject thiz, jobject weak_this,
// we use a weak reference so the AudioRecord object can be garbage collected.
lpCallbackData->audioRecord_ref = env->NewGlobalRef(weak_this);
- lpRecorder->set(arSource,
+ lpRecorder->set(source,
sampleRateInHertz,
format, // word length, PCM
- nbChannels,
+ channels,
frameCount,
0, // flags
recorderCallback,// callback_t
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 692610e..3d8d296 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -50,25 +50,6 @@ static int check_AudioSystem_Command(status_t status)
}
static int
-android_media_AudioSystem_setVolume(JNIEnv *env, jobject clazz, jint type, jint volume)
-{
- LOGV("setVolume(%d)", int(volume));
-
- return check_AudioSystem_Command(AudioSystem::setStreamVolume(type, AudioSystem::linearToLog(volume)));
-}
-
-static int
-android_media_AudioSystem_getVolume(JNIEnv *env, jobject clazz, jint type)
-{
- float v;
- int v_int = -1;
- if (AudioSystem::getStreamVolume(int(type), &v) == NO_ERROR) {
- v_int = AudioSystem::logToLinear(v);
- }
- return v_int;
-}
-
-static int
android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on)
{
return check_AudioSystem_Command(AudioSystem::muteMicrophone(on));
@@ -82,34 +63,6 @@ android_media_AudioSystem_isMicrophoneMuted(JNIEnv *env, jobject thiz)
return state;
}
-static int
-android_media_AudioSystem_setRouting(JNIEnv *env, jobject clazz, jint mode, jint routes, jint mask)
-{
- return check_AudioSystem_Command(AudioSystem::setRouting(mode, uint32_t(routes), uint32_t(mask)));
-}
-
-static jint
-android_media_AudioSystem_getRouting(JNIEnv *env, jobject clazz, jint mode)
-{
- uint32_t routes = -1;
- AudioSystem::getRouting(mode, &routes);
- return jint(routes);
-}
-
-static int
-android_media_AudioSystem_setMode(JNIEnv *env, jobject clazz, jint mode)
-{
- return check_AudioSystem_Command(AudioSystem::setMode(mode));
-}
-
-static jint
-android_media_AudioSystem_getMode(JNIEnv *env, jobject clazz)
-{
- int mode = AudioSystem::MODE_INVALID;
- AudioSystem::getMode(&mode);
- return jint(mode);
-}
-
static jboolean
android_media_AudioSystem_isMusicActive(JNIEnv *env, jobject thiz)
{
@@ -118,16 +71,29 @@ android_media_AudioSystem_isMusicActive(JNIEnv *env, jobject thiz)
return state;
}
-// Temporary interface, do not use
-// TODO: Replace with a more generic key:value get/set mechanism
-static void
-android_media_AudioSystem_setParameter(JNIEnv *env, jobject thiz, jstring key, jstring value)
+static int
+android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyValuePairs)
{
- const char *c_key = env->GetStringUTFChars(key, NULL);
- const char *c_value = env->GetStringUTFChars(value, NULL);
- AudioSystem::setParameter(c_key, c_value);
- env->ReleaseStringUTFChars(key, c_key);
- env->ReleaseStringUTFChars(value, c_value);
+ const jchar* c_keyValuePairs = env->GetStringCritical(keyValuePairs, 0);
+ String8 c_keyValuePairs8;
+ if (keyValuePairs) {
+ c_keyValuePairs8 = String8(c_keyValuePairs, env->GetStringLength(keyValuePairs));
+ env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs);
+ }
+ int status = check_AudioSystem_Command(AudioSystem::setParameters(0, c_keyValuePairs8));
+ return status;
+}
+
+static jstring
+android_media_AudioSystem_getParameters(JNIEnv *env, jobject thiz, jstring keys)
+{
+ const jchar* c_keys = env->GetStringCritical(keys, 0);
+ String8 c_keys8;
+ if (keys) {
+ c_keys8 = String8(c_keys, env->GetStringLength(keys));
+ env->ReleaseStringCritical(keys, c_keys);
+ }
+ return env->NewStringUTF(AudioSystem::getParameters(0, c_keys8).string());
}
void android_media_AudioSystem_error_callback(status_t err)
@@ -152,19 +118,93 @@ void android_media_AudioSystem_error_callback(status_t err)
env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz, "errorCallbackFromNative","(I)V"), error);
}
+static int
+android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address)
+{
+ const char *c_address = env->GetStringUTFChars(device_address, NULL);
+ int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <AudioSystem::audio_devices>(device),
+ static_cast <AudioSystem::device_connection_state>(state),
+ c_address));
+ env->ReleaseStringUTFChars(device_address, c_address);
+ return status;
+}
+
+static int
+android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address)
+{
+ const char *c_address = env->GetStringUTFChars(device_address, NULL);
+ int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <AudioSystem::audio_devices>(device),
+ c_address));
+ env->ReleaseStringUTFChars(device_address, c_address);
+ return state;
+}
+
+static int
+android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
+{
+ return check_AudioSystem_Command(AudioSystem::setPhoneState(state));
+}
+
+static int
+android_media_AudioSystem_setRingerMode(JNIEnv *env, jobject thiz, jint mode, jint mask)
+{
+ return check_AudioSystem_Command(AudioSystem::setRingerMode(mode, mask));
+}
+
+static int
+android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config)
+{
+ return check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <AudioSystem::force_use>(usage),
+ static_cast <AudioSystem::forced_config>(config)));
+}
+
+static int
+android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage)
+{
+ return static_cast <int>(AudioSystem::getForceUse(static_cast <AudioSystem::force_use>(usage)));
+}
+
+static int
+android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax)
+{
+ return check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <AudioSystem::stream_type>(stream),
+ indexMin,
+ indexMax));
+}
+
+static int
+android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream, jint index)
+{
+ return check_AudioSystem_Command(AudioSystem::setStreamVolumeIndex(static_cast <AudioSystem::stream_type>(stream), index));
+}
+
+static int
+android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jobject thiz, jint stream)
+{
+ int index;
+ if (AudioSystem::getStreamVolumeIndex(static_cast <AudioSystem::stream_type>(stream), &index) != NO_ERROR) {
+ index = -1;
+ }
+ return index;
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"setVolume", "(II)I", (void *)android_media_AudioSystem_setVolume},
- {"getVolume", "(I)I", (void *)android_media_AudioSystem_getVolume},
- {"setParameter", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)android_media_AudioSystem_setParameter},
+ {"setParameters", "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
+ {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
{"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone},
{"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted},
- {"setRouting", "(III)I", (void *)android_media_AudioSystem_setRouting},
- {"getRouting", "(I)I", (void *)android_media_AudioSystem_getRouting},
- {"setMode", "(I)I", (void *)android_media_AudioSystem_setMode},
- {"getMode", "()I", (void *)android_media_AudioSystem_getMode},
{"isMusicActive", "()Z", (void *)android_media_AudioSystem_isMusicActive},
+ {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
+ {"getDeviceConnectionState", "(ILjava/lang/String;)I", (void *)android_media_AudioSystem_getDeviceConnectionState},
+ {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+ {"setRingerMode", "(II)I", (void *)android_media_AudioSystem_setRingerMode},
+ {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
+ {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
+ {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
+ {"setStreamVolumeIndex","(II)I", (void *)android_media_AudioSystem_setStreamVolumeIndex},
+ {"getStreamVolumeIndex","(I)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}
};
const char* const kClassPathName = "android/media/AudioSystem";
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index fd92fbe..65c0435 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -30,8 +30,8 @@
#include "media/AudioSystem.h"
#include "media/AudioTrack.h"
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
// ----------------------------------------------------------------------------
@@ -53,10 +53,11 @@ struct fields_t {
int STREAM_MUSIC; //... stream type constants
int STREAM_ALARM; //... stream type constants
int STREAM_NOTIFICATION; //... stream type constants
- int STREAM_BLUETOOTH_SCO; //... stream type constants
+ int STREAM_BLUETOOTH_SCO; //... stream type constants
+ int STREAM_DTMF; //... stream type constants
int MODE_STREAM; //... memory mode
int MODE_STATIC; //... memory mode
- jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
+ jfieldID nativeTrackInJavaObj; // stores in Java the native AudioTrack object
jfieldID jniData; // stores in Java additional resources used by the native AudioTrack
};
static fields_t javaAudioTrackFields;
@@ -103,7 +104,7 @@ class AudioTrackJniStorage {
#define AUDIOTRACK_ERROR_BAD_VALUE -2
#define AUDIOTRACK_ERROR_INVALID_OPERATION -3
#define AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM -16
-#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT -17
+#define AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK -17
#define AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT -18
#define AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE -19
#define AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED -20
@@ -164,11 +165,11 @@ static void audioCallback(int event, void* user, void *info) {
// ----------------------------------------------------------------------------
static int
android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
- jint streamType, jint sampleRateInHertz, jint nbChannels,
+ jint streamType, jint sampleRateInHertz, jint channels,
jint audioFormat, jint buffSizeInBytes, jint memoryMode)
{
- LOGV("sampleRate=%d, audioFormat(from Java)=%d, nbChannels=%d, buffSize=%d",
- sampleRateInHertz, audioFormat, nbChannels, buffSizeInBytes);
+ LOGV("sampleRate=%d, audioFormat(from Java)=%d, channels=%x, buffSize=%d",
+ sampleRateInHertz, audioFormat, channels, buffSizeInBytes);
int afSampleRate;
int afFrameCount;
@@ -181,10 +182,11 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
return AUDIOTRACK_ERROR_SETUP_AUDIOSYSTEM;
}
- if ((nbChannels == 0) || (nbChannels > 2)) {
- LOGE("Error creating AudioTrack: channel count is not 1 or 2.");
- return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELCOUNT;
+ if (!AudioSystem::isOutputChannel(channels)) {
+ LOGE("Error creating AudioTrack: invalid channel mask.");
+ return AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
}
+ int nbChannels = AudioSystem::popCount(channels);
// check the stream type
AudioSystem::stream_type atStreamType;
@@ -202,6 +204,8 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
atStreamType = AudioSystem::NOTIFICATION;
} else if (streamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
atStreamType = AudioSystem::BLUETOOTH_SCO;
+ } else if (streamType == javaAudioTrackFields.STREAM_DTMF) {
+ atStreamType = AudioSystem::DTMF;
} else {
LOGE("Error creating AudioTrack: unknown stream type.");
return AUDIOTRACK_ERROR_SETUP_INVALIDSTREAMTYPE;
@@ -231,15 +235,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
int bytesPerSample = audioFormat == javaAudioTrackFields.PCM16 ? 2 : 1;
int format = audioFormat == javaAudioTrackFields.PCM16 ?
AudioSystem::PCM_16_BIT : AudioSystem::PCM_8_BIT;
- int frameCount;
- if (buffSizeInBytes == -1) {
- // compute the frame count based on the system's output frame count
- // and the native sample rate
- frameCount = (sampleRateInHertz*afFrameCount)/afSampleRate;
- } else {
- // compute the frame count based on the specified buffer size
- frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
- }
+ int frameCount = buffSizeInBytes / (nbChannels * bytesPerSample);
AudioTrackJniStorage* lpJniStorage = new AudioTrackJniStorage();
@@ -271,7 +267,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
atStreamType,// stream type
sampleRateInHertz,
format,// word length, PCM
- nbChannels,
+ channels,
frameCount,
0,// flags
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
@@ -291,7 +287,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
atStreamType,// stream type
sampleRateInHertz,
format,// word length, PCM
- nbChannels,
+ channels,
frameCount,
0,// flags
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
@@ -734,6 +730,8 @@ static jint android_media_AudioTrack_get_output_sample_rate(JNIEnv *env, jobjec
nativeStreamType = AudioSystem::NOTIFICATION;
} else if (javaStreamType == javaAudioTrackFields.STREAM_BLUETOOTH_SCO) {
nativeStreamType = AudioSystem::BLUETOOTH_SCO;
+ } else if (javaStreamType == javaAudioTrackFields.STREAM_DTMF) {
+ nativeStreamType = AudioSystem::DTMF;
} else {
nativeStreamType = AudioSystem::DEFAULT;
}
@@ -829,6 +827,7 @@ static JNINativeMethod gMethods[] = {
#define JAVA_CONST_STREAM_ALARM_NAME "STREAM_ALARM"
#define JAVA_CONST_STREAM_NOTIFICATION_NAME "STREAM_NOTIFICATION"
#define JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME "STREAM_BLUETOOTH_SCO"
+#define JAVA_CONST_STREAM_DTMF_NAME "STREAM_DTMF"
#define JAVA_CONST_MODE_STREAM_NAME "MODE_STREAM"
#define JAVA_CONST_MODE_STATIC_NAME "MODE_STATIC"
#define JAVA_NATIVETRACKINJAVAOBJ_FIELD_NAME "mNativeTrackInJavaObj"
@@ -950,8 +949,10 @@ int register_android_media_AudioTrack(JNIEnv *env)
JAVA_CONST_STREAM_NOTIFICATION_NAME, &(javaAudioTrackFields.STREAM_NOTIFICATION))
|| !android_media_getIntConstantFromClass(env, audioManagerClass,
JAVA_AUDIOMANAGER_CLASS_NAME,
- JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME,
- &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))) {
+ JAVA_CONST_STREAM_BLUETOOTH_SCO_NAME, &(javaAudioTrackFields.STREAM_BLUETOOTH_SCO))
+ || !android_media_getIntConstantFromClass(env, audioManagerClass,
+ JAVA_AUDIOMANAGER_CLASS_NAME,
+ JAVA_CONST_STREAM_DTMF_NAME, &(javaAudioTrackFields.STREAM_DTMF))) {
// error log performed in android_media_getIntConstantFromClass()
return -1;
}
diff --git a/core/jni/android_media_ToneGenerator.cpp b/core/jni/android_media_ToneGenerator.cpp
index a4388de..07bb1ab 100644
--- a/core/jni/android_media_ToneGenerator.cpp
+++ b/core/jni/android_media_ToneGenerator.cpp
@@ -38,7 +38,7 @@ struct fields_t {
};
static fields_t fields;
-static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType) {
+static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz, jint toneType, jint durationMs) {
LOGV("android_media_ToneGenerator_startTone: %x\n", (int)thiz);
ToneGenerator *lpToneGen = (ToneGenerator *)env->GetIntField(thiz,
@@ -48,7 +48,7 @@ static jboolean android_media_ToneGenerator_startTone(JNIEnv *env, jobject thiz,
return false;
}
- return lpToneGen->startTone(toneType);
+ return lpToneGen->startTone(toneType, durationMs);
}
static void android_media_ToneGenerator_stopTone(JNIEnv *env, jobject thiz) {
@@ -120,7 +120,7 @@ static void android_media_ToneGenerator_native_finalize(JNIEnv *env,
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- { "startTone", "(I)Z", (void *)android_media_ToneGenerator_startTone },
+ { "startTone", "(II)Z", (void *)android_media_ToneGenerator_startTone },
{ "stopTone", "()V", (void *)android_media_ToneGenerator_stopTone },
{ "release", "()V", (void *)android_media_ToneGenerator_release },
{ "native_setup", "(II)V", (void *)android_media_ToneGenerator_native_setup },
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 8383deb..feb0dad 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -23,6 +23,7 @@
#include <arpa/inet.h>
extern "C" {
+int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
int ifc_add_host_route(const char *ifname, uint32_t addr);
int ifc_remove_host_routes(const char *ifname);
@@ -66,6 +67,16 @@ static struct fieldIds {
jfieldID leaseDuration;
} dhcpInfoFieldIds;
+static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
+{
+ int result;
+
+ const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+ result = ::ifc_enable(nameStr);
+ env->ReleaseStringUTFChars(ifname, nameStr);
+ return (jint)result;
+}
+
static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
@@ -209,6 +220,7 @@ static jboolean android_net_utils_configureInterface(JNIEnv* env,
static JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
+ { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
{ "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute },
{ "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes },
diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp
index ae744a8..38f3fda 100644
--- a/core/jni/android_net_wifi_Wifi.cpp
+++ b/core/jni/android_net_wifi_Wifi.cpp
@@ -311,18 +311,26 @@ static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz)
return result;
}
-static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+static jint android_net_wifi_getRssiHelper(const char *cmd)
{
char reply[256];
int rssi = -200;
- if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) {
+ if (doCommand(cmd, reply, sizeof(reply)) != 0) {
return (jint)-1;
}
+
// reply comes back in the form "<SSID> rssi XX" where XX is the
// number we're interested in. if we're associating, it returns "OK".
// beware - <SSID> can contain spaces.
if (strcmp(reply, "OK") != 0) {
+ // beware of trailing spaces
+ char* end = reply + strlen(reply);
+ while (end > reply && end[-1] == ' ') {
+ end--;
+ }
+ *end = 0;
+
char* lastSpace = strrchr(reply, ' ');
// lastSpace should be preceded by "rssi" and followed by the value
if (lastSpace && !strncmp(lastSpace - 4, "rssi", 4)) {
@@ -332,6 +340,16 @@ static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
return (jint)rssi;
}
+static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
+{
+ return android_net_wifi_getRssiHelper("DRIVER RSSI");
+}
+
+static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz)
+{
+ return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
+}
+
static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
{
char reply[256];
@@ -527,6 +545,8 @@ static JNINativeMethod gWifiMethods[] = {
{ "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
(void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
{ "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
+ { "getRssiApproxCommand", "()I",
+ (void*) android_net_wifi_getRssiApproxCommand},
{ "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
{ "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
{ "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index b4c60f1..3ee404a 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -200,12 +200,13 @@ static void load_maps(int pid, stats_t* stats)
fclose(fp);
}
-static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
+static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
+ jint pid, jobject object)
{
stats_t stats;
memset(&stats, 0, sizeof(stats_t));
- load_maps(getpid(), &stats);
+ load_maps(pid, &stats);
env->SetIntField(object, dalvikPss_field, stats.dalvikPss);
env->SetIntField(object, dalvikPrivateDirty_field, stats.dalvikPrivateDirty);
@@ -220,6 +221,11 @@ static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject o
env->SetIntField(object, otherSharedDirty_field, stats.otherSharedDirty);
}
+static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
+{
+ android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
+}
+
static jint read_binder_stat(const char* stat)
{
FILE* fp = fopen(BINDER_STATS, "r");
@@ -281,6 +287,8 @@ static JNINativeMethod gMethods[] = {
(void*) android_os_Debug_getNativeHeapFreeSize },
{ "getMemoryInfo", "(Landroid/os/Debug$MemoryInfo;)V",
(void*) android_os_Debug_getDirtyPages },
+ { "getMemoryInfo", "(ILandroid/os/Debug$MemoryInfo;)V",
+ (void*) android_os_Debug_getDirtyPagesPid },
{ "getBinderSentTransactions", "()I",
(void*) android_os_Debug_getBinderSentTransactions },
{ "getBinderReceivedTransactions", "()I",
diff --git a/core/jni/android_os_Exec.cpp b/core/jni/android_os_Exec.cpp
deleted file mode 100644
index ca5e695..0000000
--- a/core/jni/android_os_Exec.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "Exec"
-
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-#include "android_runtime/AndroidRuntime.h"
-
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <termios.h>
-
-namespace android
-{
-
-static jclass class_fileDescriptor;
-static jfieldID field_fileDescriptor_descriptor;
-static jmethodID method_fileDescriptor_init;
-
-
-static int create_subprocess(const char *cmd, const char *arg0, const char *arg1,
- int* pProcessId)
-{
- char *devname;
- int ptm;
- pid_t pid;
-
- ptm = open("/dev/ptmx", O_RDWR); // | O_NOCTTY);
- if(ptm < 0){
- LOGE("[ cannot open /dev/ptmx - %s ]\n",strerror(errno));
- return -1;
- }
- fcntl(ptm, F_SETFD, FD_CLOEXEC);
-
- if(grantpt(ptm) || unlockpt(ptm) ||
- ((devname = (char*) ptsname(ptm)) == 0)){
- LOGE("[ trouble with /dev/ptmx - %s ]\n", strerror(errno));
- return -1;
- }
-
- pid = fork();
- if(pid < 0) {
- LOGE("- fork failed: %s -\n", strerror(errno));
- return -1;
- }
-
- if(pid == 0){
- int pts;
-
- setsid();
-
- pts = open(devname, O_RDWR);
- if(pts < 0) exit(-1);
-
- dup2(pts, 0);
- dup2(pts, 1);
- dup2(pts, 2);
-
- close(ptm);
-
- execl(cmd, cmd, arg0, arg1, NULL);
- exit(-1);
- } else {
- *pProcessId = (int) pid;
- return ptm;
- }
-}
-
-
-static jobject android_os_Exec_createSubProcess(JNIEnv *env, jobject clazz,
- jstring cmd, jstring arg0, jstring arg1, jintArray processIdArray)
-{
- const jchar* str = cmd ? env->GetStringCritical(cmd, 0) : 0;
- String8 cmd_8;
- if (str) {
- cmd_8 = String8(str, env->GetStringLength(cmd));
- env->ReleaseStringCritical(cmd, str);
- }
-
- str = arg0 ? env->GetStringCritical(arg0, 0) : 0;
- const char* arg0Str = 0;
- String8 arg0_8;
- if (str) {
- arg0_8 = String8(str, env->GetStringLength(arg0));
- env->ReleaseStringCritical(arg0, str);
- arg0Str = arg0_8.string();
- }
-
- str = arg1 ? env->GetStringCritical(arg1, 0) : 0;
- const char* arg1Str = 0;
- String8 arg1_8;
- if (str) {
- arg1_8 = String8(str, env->GetStringLength(arg1));
- env->ReleaseStringCritical(arg1, str);
- arg1Str = arg1_8.string();
- }
-
- int procId;
- int ptm = create_subprocess(cmd_8.string(), arg0Str, arg1Str, &procId);
-
- if (processIdArray) {
- int procIdLen = env->GetArrayLength(processIdArray);
- if (procIdLen > 0) {
- jboolean isCopy;
-
- int* pProcId = (int*) env->GetPrimitiveArrayCritical(processIdArray, &isCopy);
- if (pProcId) {
- *pProcId = procId;
- env->ReleasePrimitiveArrayCritical(processIdArray, pProcId, 0);
- }
- }
- }
-
- jobject result = env->NewObject(class_fileDescriptor, method_fileDescriptor_init);
-
- if (!result) {
- LOGE("Couldn't create a FileDescriptor.");
- }
- else {
- env->SetIntField(result, field_fileDescriptor_descriptor, ptm);
- }
-
- return result;
-}
-
-
-static void android_os_Exec_setPtyWindowSize(JNIEnv *env, jobject clazz,
- jobject fileDescriptor, jint row, jint col, jint xpixel, jint ypixel)
-{
- int fd;
- struct winsize sz;
-
- fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
- if (env->ExceptionOccurred() != NULL) {
- return;
- }
-
- sz.ws_row = row;
- sz.ws_col = col;
- sz.ws_xpixel = xpixel;
- sz.ws_ypixel = ypixel;
-
- ioctl(fd, TIOCSWINSZ, &sz);
-}
-
-static int android_os_Exec_waitFor(JNIEnv *env, jobject clazz,
- jint procId) {
- int status;
- waitpid(procId, &status, 0);
- int result = 0;
- if (WIFEXITED(status)) {
- result = WEXITSTATUS(status);
- }
- return result;
-}
-
-static JNINativeMethod method_table[] = {
- { "createSubprocess", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I)Ljava/io/FileDescriptor;",
- (void*) android_os_Exec_createSubProcess },
- { "setPtyWindowSize", "(Ljava/io/FileDescriptor;IIII)V",
- (void*) android_os_Exec_setPtyWindowSize},
- { "waitFor", "(I)I",
- (void*) android_os_Exec_waitFor}
-};
-
-int register_android_os_Exec(JNIEnv *env)
-{
- class_fileDescriptor = env->FindClass("java/io/FileDescriptor");
-
- if (class_fileDescriptor == NULL) {
- LOGE("Can't find java/io/FileDescriptor");
- return -1;
- }
-
- field_fileDescriptor_descriptor = env->GetFieldID(class_fileDescriptor, "descriptor", "I");
-
- if (field_fileDescriptor_descriptor == NULL) {
- LOGE("Can't find FileDescriptor.descriptor");
- return -1;
- }
-
- method_fileDescriptor_init = env->GetMethodID(class_fileDescriptor, "<init>", "()V");
- if (method_fileDescriptor_init == NULL) {
- LOGE("Can't find FileDescriptor.init");
- return -1;
- }
-
- return AndroidRuntime::registerNativeMethods(
- env, "android/os/Exec",
- method_table, NELEM(method_table));
-}
-
-};
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index 8643393..1ae3ec7 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -118,7 +118,7 @@ static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDe
}
}
-static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz,
+static jint android_os_MemoryFile_get_mapped_size(JNIEnv* env, jobject clazz,
jobject fileDescriptor) {
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
// Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
@@ -129,13 +129,13 @@ static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject claz
if (errno == ENOTTY) {
// ENOTTY means that the ioctl does not apply to this object,
// i.e., it is not an ashmem region.
- return JNI_FALSE;
+ return (jint) -1;
}
// Some other error, throw exception
jniThrowIOException(env, errno);
- return JNI_FALSE;
+ return (jint) -1;
}
- return JNI_TRUE;
+ return (jint) result;
}
static const JNINativeMethod methods[] = {
@@ -146,8 +146,8 @@ static const JNINativeMethod methods[] = {
{"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
{"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
{"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
- {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z",
- (void*)android_os_MemoryFile_is_ashmem_region}
+ {"native_get_mapped_size", "(Ljava/io/FileDescriptor;)I",
+ (void*)android_os_MemoryFile_get_mapped_size}
};
static const char* const kClassPathName = "android/os/MemoryFile";
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index ca4fa11..406884b 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -36,9 +36,9 @@ static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz,
"key must not be null.");
goto error;
}
-
+
key = env->GetStringUTFChars(keyJ, NULL);
-
+
len = property_get(key, buf, "");
if ((len <= 0) && (defJ != NULL)) {
rvJ = defJ;
@@ -47,9 +47,9 @@ static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz,
} else {
rvJ = env->NewStringUTF("");
}
-
+
env->ReleaseStringUTFChars(keyJ, key);
-
+
error:
return rvJ;
}
@@ -60,6 +60,101 @@ static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,
return SystemProperties_getSS(env, clazz, keyJ, NULL);
}
+static jint SystemProperties_get_int(JNIEnv *env, jobject clazz,
+ jstring keyJ, jint defJ)
+{
+ int len;
+ const char* key;
+ char buf[PROPERTY_VALUE_MAX];
+ jint result = defJ;
+
+ if (keyJ == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "key must not be null.");
+ goto error;
+ }
+
+ key = env->GetStringUTFChars(keyJ, NULL);
+
+ len = property_get(key, buf, "");
+ if (len > 0) {
+ jint temp;
+ if (sscanf(buf, "%d", &temp) == 1)
+ result = temp;
+ }
+
+ env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+ return result;
+}
+
+static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz,
+ jstring keyJ, jlong defJ)
+{
+ int len;
+ const char* key;
+ char buf[PROPERTY_VALUE_MAX];
+ jlong result = defJ;
+
+ if (keyJ == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "key must not be null.");
+ goto error;
+ }
+
+ key = env->GetStringUTFChars(keyJ, NULL);
+
+ len = property_get(key, buf, "");
+ if (len > 0) {
+ jlong temp;
+ if (sscanf(buf, "%lld", &temp) == 1)
+ result = temp;
+ }
+
+ env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+ return result;
+}
+
+static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz,
+ jstring keyJ, jboolean defJ)
+{
+ int len;
+ const char* key;
+ char buf[PROPERTY_VALUE_MAX];
+ jboolean result = defJ;
+
+ if (keyJ == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException",
+ "key must not be null.");
+ goto error;
+ }
+
+ key = env->GetStringUTFChars(keyJ, NULL);
+
+ len = property_get(key, buf, "");
+ if (len == 1) {
+ char ch = buf[0];
+ if (ch == '0' || ch == 'n')
+ result = false;
+ else if (ch == '1' || ch == 'y')
+ result = true;
+ } else if (len > 1) {
+ if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+ result = false;
+ } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
+ result = true;
+ }
+ }
+
+ env->ReleaseStringUTFChars(keyJ, key);
+
+error:
+ return result;
+}
+
static void SystemProperties_set(JNIEnv *env, jobject clazz,
jstring keyJ, jstring valJ)
{
@@ -94,6 +189,12 @@ static JNINativeMethod method_table[] = {
(void*) SystemProperties_getS },
{ "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getSS },
+ { "native_get_int", "(Ljava/lang/String;I)I",
+ (void*) SystemProperties_get_int },
+ { "native_get_long", "(Ljava/lang/String;J)J",
+ (void*) SystemProperties_get_long },
+ { "native_get_boolean", "(Ljava/lang/String;Z)Z",
+ (void*) SystemProperties_get_boolean },
{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) SystemProperties_set },
};
diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp
index 91a8e8e..a185d8d 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -37,12 +37,7 @@
namespace android {
#ifdef HAVE_BLUETOOTH
-static jmethodID method_onHeadsetCreated;
-static jmethodID method_onHeadsetRemoved;
-static jmethodID method_onSinkConnected;
-static jmethodID method_onSinkDisconnected;
-static jmethodID method_onSinkPlaying;
-static jmethodID method_onSinkStopped;
+static jmethodID method_onSinkPropertyChanged;
typedef struct {
JavaVM *vm;
@@ -53,11 +48,11 @@ typedef struct {
static native_data_t *nat = NULL; // global native data
-#endif
-
-#ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat);
-static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat);
+static Properties sink_properties[] = {
+ {"State", DBUS_TYPE_STRING},
+ {"Connected", DBUS_TYPE_BOOLEAN},
+ {"Playing", DBUS_TYPE_BOOLEAN},
+ };
#endif
/* Returns true on success (even if adapter is present but disabled).
@@ -100,91 +95,58 @@ static void cleanupNative(JNIEnv* env, jobject object) {
}
#endif
}
-static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, "/org/bluez/audio",
- "org.bluez.audio.Manager", "ListHeadsets",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-static jstring createHeadsetNative(JNIEnv *env, jobject object,
- jstring address) {
+static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object,
+ jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- LOGV("... address = %s\n", c_address);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, "/org/bluez/audio",
- "org.bluez.audio.Manager", "CreateHeadset",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- return reply ? dbus_returns_string(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
+ DBusMessage *msg, *reply;
+ DBusError err;
+ dbus_error_init(&err);
-static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, "/org/bluez/audio",
- "org.bluez.audio.Manager", "RemoveHeadset",
- DBUS_TYPE_STRING, &c_path,
- DBUS_TYPE_INVALID);
+ reply = dbus_func_args_timeout(env,
+ nat->conn, -1, c_path,
+ "org.bluez.AudioSink", "GetProperties",
+ DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
- return reply ? dbus_returns_string(env, reply) : NULL;
+ if (!reply && dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+ return NULL;
+ } else if (!reply) {
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return NULL;
+ }
+ DBusMessageIter iter;
+ if (dbus_message_iter_init(reply, &iter))
+ return parse_properties(env, &iter, (Properties *)&sink_properties,
+ sizeof(sink_properties) / sizeof(Properties));
}
#endif
return NULL;
}
-static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- if (nat) {
- const char *c_path = env->GetStringUTFChars(path, NULL);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, c_path,
- "org.bluez.audio.Device", "GetAddress",
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(path, c_path);
- return reply ? dbus_returns_string(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
- size_t path_sz = env->GetStringUTFLength(path) + 1;
- char *c_path_copy = (char *)malloc(path_sz); // callback data
- strncpy(c_path_copy, c_path, path_sz);
-
- bool ret =
- dbus_func_args_async(env, nat->conn, -1,
- onConnectSinkResult, (void *)c_path_copy, nat,
- c_path,
- "org.bluez.audio.Sink", "Connect",
- DBUS_TYPE_INVALID);
+ DBusError err;
+ dbus_error_init(&err);
+ DBusMessage *reply =
+ dbus_func_args_timeout(env, nat->conn, -1, c_path,
+ "org.bluez.AudioSink", "Connect",
+ DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
- if (!ret) {
- free(c_path_copy);
+
+ if (!reply && dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+ return JNI_FALSE;
+ } else if (!reply) {
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
return JNI_FALSE;
}
return JNI_TRUE;
@@ -199,19 +161,20 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
- size_t path_sz = env->GetStringUTFLength(path) + 1;
- char *c_path_copy = (char *)malloc(path_sz); // callback data
- strncpy(c_path_copy, c_path, path_sz);
-
- bool ret =
- dbus_func_args_async(env, nat->conn, -1,
- onDisconnectSinkResult, (void *)c_path_copy, nat,
- c_path,
- "org.bluez.audio.Sink", "Disconnect",
- DBUS_TYPE_INVALID);
+ DBusError err;
+ dbus_error_init(&err);
+
+ DBusMessage *reply =
+ dbus_func_args_timeout(env, nat->conn, -1, c_path,
+ "org.bluez.AudioSink", "Disconnect",
+ DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
- if (!ret) {
- free(c_path_copy);
+
+ if (!reply && dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
+ return JNI_FALSE;
+ } else if (!reply) {
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
return JNI_FALSE;
}
return JNI_TRUE;
@@ -220,93 +183,7 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
return JNI_FALSE;
}
-static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- if (nat) {
- const char *c_path = env->GetStringUTFChars(path, NULL);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, c_path,
- "org.bluez.audio.Sink", "IsConnected",
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(path, c_path);
- return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
- }
-#endif
- return JNI_FALSE;
-}
-
-#ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) {
- LOGV(__FUNCTION__);
-
- char *c_path = (char *)user;
- DBusError err;
- JNIEnv *env;
-
- if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
- LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
- return;
- }
-
- dbus_error_init(&err);
-
- LOGV("... path = %s", c_path);
- if (dbus_set_error_from_message(&err, msg)) {
- /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
- LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
- dbus_error_free(&err);
- env->CallVoidMethod(nat->me,
- method_onSinkDisconnected,
- env->NewStringUTF(c_path));
- if (env->ExceptionCheck()) {
- LOGE("VM Exception occurred in native function %s (%s:%d)",
- __FUNCTION__, __FILE__, __LINE__);
- }
- } // else Java callback is triggered by signal in a2dp_event_filter
-
- free(c_path);
-}
-
-static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) {
- LOGV(__FUNCTION__);
-
- char *c_path = (char *)user;
- DBusError err;
- JNIEnv *env;
-
- if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) {
- LOGE("%s: error finding Env for our VM\n", __FUNCTION__);
- return;
- }
-
- dbus_error_init(&err);
-
- LOGV("... path = %s", c_path);
- if (dbus_set_error_from_message(&err, msg)) {
- /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
- LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
- if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) {
- // we were already disconnected, so report disconnect
- env->CallVoidMethod(nat->me,
- method_onSinkDisconnected,
- env->NewStringUTF(c_path));
- } else {
- // Assume it is still connected
- env->CallVoidMethod(nat->me,
- method_onSinkConnected,
- env->NewStringUTF(c_path));
- }
- dbus_error_free(&err);
- if (env->ExceptionCheck()) {
- LOGE("VM Exception occurred in native function %s (%s:%d)",
- __FUNCTION__, __FILE__, __LINE__);
- }
- } // else Java callback is triggered by signal in a2dp_event_filter
-
- free(c_path);
-}
-
DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
DBusError err;
@@ -324,71 +201,19 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- if (dbus_message_is_signal(msg,
- "org.bluez.audio.Manager",
- "HeadsetCreated")) {
- char *c_path;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_path,
- DBUS_TYPE_INVALID)) {
- LOGV("... path = %s", c_path);
- env->CallVoidMethod(nat->me,
- method_onHeadsetCreated,
- env->NewStringUTF(c_path));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- result = DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.audio.Manager",
- "HeadsetRemoved")) {
- char *c_path;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_path,
- DBUS_TYPE_INVALID)) {
- LOGV("... path = %s", c_path);
- env->CallVoidMethod(nat->me,
- method_onHeadsetRemoved,
- env->NewStringUTF(c_path));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- result = DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.audio.Sink",
- "Connected")) {
+ if (dbus_message_is_signal(msg, "org.bluez.AudioSink",
+ "PropertyChanged")) {
+ jobjectArray str_array =
+ parse_property_change(env, msg, (Properties *)&sink_properties,
+ sizeof(sink_properties) / sizeof(Properties));
const char *c_path = dbus_message_get_path(msg);
- LOGV("... path = %s", c_path);
env->CallVoidMethod(nat->me,
- method_onSinkConnected,
- env->NewStringUTF(c_path));
+ method_onSinkPropertyChanged,
+ env->NewStringUTF(c_path),
+ str_array);
result = DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.audio.Sink",
- "Disconnected")) {
- const char *c_path = dbus_message_get_path(msg);
- LOGV("... path = %s", c_path);
- env->CallVoidMethod(nat->me,
- method_onSinkDisconnected,
- env->NewStringUTF(c_path));
- result = DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.audio.Sink",
- "Playing")) {
- const char *c_path = dbus_message_get_path(msg);
- LOGV("... path = %s", c_path);
- env->CallVoidMethod(nat->me,
- method_onSinkPlaying,
- env->NewStringUTF(c_path));
- result = DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.audio.Sink",
- "Stopped")) {
- const char *c_path = dbus_message_get_path(msg);
- LOGV("... path = %s", c_path);
- env->CallVoidMethod(nat->me,
- method_onSinkStopped,
- env->NewStringUTF(c_path));
- result = DBUS_HANDLER_RESULT_HANDLED;
- }
-
- if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) {
+ return result;
+ } else {
LOGV("... ignored");
}
if (env->ExceptionCheck()) {
@@ -407,14 +232,11 @@ static JNINativeMethod sMethods[] = {
{"initNative", "()Z", (void *)initNative},
{"cleanupNative", "()V", (void *)cleanupNative},
- /* Bluez audio 3.36 API */
- {"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative},
- {"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative},
- {"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative},
- {"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative},
- {"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative},
- {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative},
- {"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative},
+ /* Bluez audio 4.40 API */
+ {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative},
+ {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative},
+ {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
+ (void *)getSinkPropertiesNative},
};
int register_android_server_BluetoothA2dpService(JNIEnv *env) {
@@ -425,12 +247,8 @@ int register_android_server_BluetoothA2dpService(JNIEnv *env) {
}
#ifdef HAVE_BLUETOOTH
- method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V");
- method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V");
- method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V");
- method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V");
- method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V");
- method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V");
+ method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged",
+ "(Ljava/lang/String;[Ljava/lang/String;)V");
#endif
return AndroidRuntime::registerNativeMethods(env,
diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp
deleted file mode 100644
index 58ae4f6..0000000
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ /dev/null
@@ -1,1035 +0,0 @@
-/*
-** Copyright 2006, 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.
-*/
-
-#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter"
-#define LOG_TAG "BluetoothDeviceService.cpp"
-
-#include "android_bluetooth_common.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "JNIHelp.h"
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-
-#ifdef HAVE_BLUETOOTH
-#include <dbus/dbus.h>
-#include <bluedroid/bluetooth.h>
-#endif
-
-#include <cutils/properties.h>
-
-namespace android {
-
-#define BLUETOOTH_CLASS_ERROR 0xFF000000
-
-#ifdef HAVE_BLUETOOTH
-// We initialize these variables when we load class
-// android.server.BluetoothDeviceService
-static jfieldID field_mNativeData;
-static jfieldID field_mEventLoop;
-
-typedef struct {
- JNIEnv *env;
- DBusConnection *conn;
- const char *adapter; // dbus object name of the local adapter
-} native_data_t;
-
-extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
- jobject);
-void onCreateBondingResult(DBusMessage *msg, void *user, void *nat);
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat);
-
-/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
- * Perform quick sanity check, if there are any problems return NULL
- */
-static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
- native_data_t *nat =
- (native_data_t *)(env->GetIntField(object, field_mNativeData));
- if (nat == NULL || nat->conn == NULL) {
- LOGE("Uninitialized native data\n");
- return NULL;
- }
- return nat;
-}
-#endif
-
-static void classInitNative(JNIEnv* env, jclass clazz) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- field_mNativeData = get_field(env, clazz, "mNativeData", "I");
- field_mEventLoop = get_field(env, clazz, "mEventLoop",
- "Landroid/server/BluetoothEventLoop;");
-#endif
-}
-
-/* Returns true on success (even if adapter is present but disabled).
- * Return false if dbus is down, or another serious error (out of memory)
-*/
-static bool initializeNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
- if (NULL == nat) {
- LOGE("%s: out of memory!", __FUNCTION__);
- return false;
- }
- nat->env = env;
-
- env->SetIntField(object, field_mNativeData, (jint)nat);
- DBusError err;
- dbus_error_init(&err);
- dbus_threads_init_default();
- nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
- if (dbus_error_is_set(&err)) {
- LOGE("Could not get onto the system bus: %s", err.message);
- dbus_error_free(&err);
- return false;
- }
- dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
-
- nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME;
-#endif /*HAVE_BLUETOOTH*/
- return true;
-}
-
-static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat =
- (native_data_t *)env->GetIntField(object, field_mNativeData);
- if (nat) {
- free(nat);
- nat = NULL;
- }
-#endif
-}
-
-static jstring getNameNative(JNIEnv *env, jobject object){
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetName",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_string(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- return (env->NewStringUTF(nat->adapter));
- }
-#endif
- return NULL;
-}
-
-
-static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- DBusMessage *msg = NULL;
- DBusMessage *reply = NULL;
- DBusError err;
- const char *name;
- jboolean ret = JNI_FALSE;
-
- native_data_t *nat = get_native_data(env, object);
- if (nat == NULL) {
- goto done;
- }
-
- dbus_error_init(&err);
-
- /* Compose the command */
- msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
- DBUS_CLASS_NAME, "DiscoverDevices");
-
- if (msg == NULL) {
- LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
- goto done;
- }
-
- /* Send the command. */
- reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
- if (dbus_error_is_set(&err)) {
- /* We treat the in-progress error code as success. */
- if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
- LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n",
- __FUNCTION__, err.message);
- ret = JNI_TRUE;
- goto done;
- } else {
- LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
- ret = JNI_FALSE;
- goto done;
- }
- }
-
- ret = JNI_TRUE;
-done:
- if (reply) dbus_message_unref(reply);
- if (msg) dbus_message_unref(msg);
- return ret;
-#else
- return JNI_FALSE;
-#endif
-}
-
-static void cancelDiscoveryNative(JNIEnv *env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- DBusMessage *msg = NULL;
- DBusMessage *reply = NULL;
- DBusError err;
- const char *name;
- jstring ret;
- native_data_t *nat;
-
- dbus_error_init(&err);
-
- nat = get_native_data(env, object);
- if (nat == NULL) {
- goto done;
- }
-
- /* Compose the command */
- msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
- DBUS_CLASS_NAME, "CancelDiscovery");
-
- if (msg == NULL) {
- LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__);
- goto done;
- }
-
- /* Send the command. */
- reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
- if (dbus_error_is_set(&err)) {
- if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) {
- // hcid sends this if there is no active discovery to cancel
- LOGV("%s: There was no active discovery to cancel", __FUNCTION__);
- dbus_error_free(&err);
- } else {
- LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- }
- }
-
-done:
- if (msg) dbus_message_unref(msg);
- if (reply) dbus_message_unref(reply);
-#endif
-}
-
-static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- DBusMessage *msg = NULL;
- DBusMessage *reply = NULL;
- DBusError err;
- jboolean ret = JNI_FALSE;
-
- native_data_t *nat = get_native_data(env, object);
- if (nat == NULL) {
- goto done;
- }
-
- dbus_error_init(&err);
- msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
- DBUS_CLASS_NAME, "StartPeriodicDiscovery");
- if (msg == NULL) {
- LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
- goto done;
- }
-
- /* Send the command. */
- reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
- if (dbus_error_is_set(&err)) {
- /* We treat the in-progress error code as success. */
- if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
- LOGW("%s: D-Bus error: %s (%s), treating as "
- "startPeriodicDiscoveryNative success\n",
- __FUNCTION__, err.name, err.message);
- } else {
- LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
- ret = JNI_FALSE;
- goto done;
- }
- }
-
- ret = JNI_TRUE;
-done:
- if (reply) dbus_message_unref(reply);
- if (msg) dbus_message_unref(msg);
- return ret;
-#else
- return JNI_FALSE;
-#endif
-}
-
-static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- DBusMessage *msg = NULL;
- DBusMessage *reply = NULL;
- DBusError err;
- const char *name;
- jboolean ret = JNI_FALSE;
-
- native_data_t *nat = get_native_data(env, object);
- if (nat == NULL) {
- goto done;
- }
-
- dbus_error_init(&err);
- msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter,
- DBUS_CLASS_NAME, "StopPeriodicDiscovery");
- if (msg == NULL) {
- LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__);
- goto done;
- }
-
- /* Send the command. */
- reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
- if (dbus_error_is_set(&err)) {
- /* We treat the in-progress error code as success. */
- if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) {
- LOGW("%s: D-Bus error: %s (%s), treating as "
- "stopPeriodicDiscoveryNative success\n",
- __FUNCTION__, err.name, err.message);
- } else {
- LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
- ret = JNI_FALSE;
- goto done;
- }
- }
-
- ret = JNI_TRUE;
-done:
- if (reply) dbus_message_unref(reply);
- if (msg) dbus_message_unref(msg);
- return ret;
-#else
- return JNI_FALSE;
-#endif
-}
-
-static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "IsPeriodicDiscovery",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
-
- if (timeout_s < 0) {
- return JNI_FALSE;
- }
-
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "SetDiscoverableTimeout",
- DBUS_TYPE_UINT32, &timeout_s,
- DBUS_TYPE_INVALID);
- if (reply != NULL) {
- dbus_message_unref(reply);
- return JNI_TRUE;
- }
- }
-#endif
- return JNI_FALSE;
-}
-
-static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetDiscoverableTimeout",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_uint32(env, reply) : -1;
- }
-#endif
- return -1;
-}
-
-static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "IsConnected",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- // Set a timeout of 5 seconds. Specifying the default timeout is
- // not long enough, as a remote-device disconnect results in
- // signal RemoteDisconnectRequested being sent, followed by a
- // delay of 2 seconds, after which the actual disconnect takes
- // place.
- DBusMessage *reply =
- dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter,
- DBUS_CLASS_NAME, "DisconnectRemoteDevice",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- if (reply) dbus_message_unref(reply);
- }
-#endif
-}
-
-static jstring getModeNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetMode",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_string(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_mode = env->GetStringUTFChars(mode, NULL);
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "SetMode",
- DBUS_TYPE_STRING, &c_mode,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(mode, c_mode);
- if (reply) {
- dbus_message_unref(reply);
- return JNI_TRUE;
- }
- return JNI_FALSE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean createBondingNative(JNIEnv *env, jobject object,
- jstring address, jint timeout_ms) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- 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);
- LOGV("... address = %s", c_address);
- char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
- strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
- bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
- onCreateBondingResult, // callback
- context_address,
- eventLoopNat,
- nat->adapter,
- DBUS_CLASS_NAME, "CreateBonding",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- return ret ? JNI_TRUE : JNI_FALSE;
-
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object,
- jstring address) {
- LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- LOGV("... address = %s", c_address);
- DBusMessage *reply =
- dbus_func_args_timeout(env, nat->conn, -1, nat->adapter,
- DBUS_CLASS_NAME, "CancelBondingProcess",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- if (reply) {
- dbus_message_unref(reply);
- }
- return JNI_TRUE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) {
- LOGV(__FUNCTION__);
- jboolean result = JNI_FALSE;
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- LOGV("... address = %s", c_address);
- DBusError err;
- dbus_error_init(&err);
- DBusMessage *reply =
- dbus_func_args_error(env, nat->conn, &err, nat->adapter,
- DBUS_CLASS_NAME, "RemoveBonding",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- if (dbus_error_is_set(&err)) {
- if (dbus_error_has_name(&err,
- BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) {
- LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message,
- c_address);
- result = JNI_TRUE;
- } else {
- LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name,
- err.message);
- }
- } else {
- result = JNI_TRUE;
- }
-
- env->ReleaseStringUTFChars(address, c_address);
- dbus_error_free(&err);
- if (reply) dbus_message_unref(reply);
- }
-#endif
- return result;
-}
-
-static jobjectArray listBondingsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "ListBondings",
- DBUS_TYPE_INVALID);
- // return String[]
- return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "ListConnections",
- DBUS_TYPE_INVALID);
- // return String[]
- return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "ListRemoteDevices",
- DBUS_TYPE_INVALID);
- return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jstring common_Get(JNIEnv *env, jobject object, const char *func) {
- LOGV("%s:%s", __FUNCTION__, func);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusError err;
- dbus_error_init(&err);
- DBusMessage *reply =
- dbus_func_args_error(env, nat->conn, &err, nat->adapter,
- DBUS_CLASS_NAME, func,
- DBUS_TYPE_INVALID);
- if (reply) {
- return dbus_returns_string(env, reply);
- } else {
- LOG_AND_FREE_DBUS_ERROR(&err);
- return NULL;
- }
- }
-#endif
- return NULL;
-}
-
-static jstring getAddressNative(JNIEnv *env, jobject obj) {
- return common_Get(env, obj, "GetAddress");
-}
-
-static jstring getVersionNative(JNIEnv *env, jobject obj) {
- return common_Get(env, obj, "GetVersion");
-}
-
-static jstring getRevisionNative(JNIEnv *env, jobject obj) {
- return common_Get(env, obj, "GetRevision");
-}
-
-static jstring getManufacturerNative(JNIEnv *env, jobject obj) {
- return common_Get(env, obj, "GetManufacturer");
-}
-
-static jstring getCompanyNative(JNIEnv *env, jobject obj) {
- return common_Get(env, obj, "GetCompany");
-}
-
-static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, obj);
- if (nat) {
- const char *c_name = env->GetStringUTFChars(name, NULL);
- DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "SetName",
- DBUS_TYPE_STRING, &c_name,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(name, c_name);
- if (reply) {
- dbus_message_unref(reply);
- return JNI_TRUE;
- }
- }
-#endif
- return JNI_FALSE;
-}
-
-static jstring common_getRemote(JNIEnv *env, jobject object, const char *func,
- jstring address) {
- LOGV("%s:%s", __FUNCTION__, func);
-#ifdef HAVE_BLUETOOTH
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
- DBusError err;
- dbus_error_init(&err);
-
- LOGV("... address = %s", c_address);
-
- DBusMessage *reply =
- dbus_func_args_error(env, nat->conn, &err, nat->adapter,
- DBUS_CLASS_NAME, func,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- if (reply) {
- return dbus_returns_string(env, reply);
- } else if (!strcmp(func, "GetRemoteName") &&
- dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) {
- // This error occurs if we request name during device discovery,
- // its fine
- LOGV("... %s: %s", func, err.message);
- dbus_error_free(&err);
- return NULL;
- } else {
- LOG_AND_FREE_DBUS_ERROR(&err);
- return NULL;
- }
- }
-#endif
- return NULL;
-}
-
-static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "GetRemoteVersion", address);
-}
-
-static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "GetRemoteRevision", address);
-}
-
-static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "GetRemoteManufacturer", address);
-}
-
-static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "GetRemoteCompany", address);
-}
-
-static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "GetRemoteName", address);
-}
-
-static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "LastSeen", address);
-}
-
-static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) {
- return common_getRemote(env, obj, "LastUsed", address);
-}
-
-static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) {
- jint result = BLUETOOTH_CLASS_ERROR;
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
-
- LOGV("... address = %s", c_address);
-
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetRemoteClass",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- if (reply)
- {
- DBusError err;
- dbus_error_init(&err);
- if (!dbus_message_get_args(reply, &err,
- DBUS_TYPE_UINT32, &result,
- DBUS_TYPE_INVALID)) {
- LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
- }
- dbus_message_unref(reply);
- }
- }
-#endif
- return result;
-}
-
-static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object,
- jstring address) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
-
- LOGV("... address = %s", c_address);
-
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetRemoteFeatures",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- /* array of DBUS_TYPE_BYTE_AS_STRING */
- return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object,
- jstring address, jstring match) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- jintArray intArray = NULL;
- const char *c_address = env->GetStringUTFChars(address, NULL);
- const char *c_match = env->GetStringUTFChars(match, NULL);
-
- LOGV("... address = %s match = %s", c_address, c_match);
-
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetRemoteServiceHandles",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_STRING, &c_match,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- env->ReleaseStringUTFChars(match, c_match);
- if (reply)
- {
- DBusError err;
- jint *list;
- int i, len;
-
- dbus_error_init(&err);
- if (dbus_message_get_args (reply, &err,
- DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32,
- &list, &len,
- DBUS_TYPE_INVALID)) {
- if (len) {
- intArray = env->NewIntArray(len);
- if (intArray)
- env->SetIntArrayRegion(intArray, 0, len, list);
- }
- } else {
- LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
- }
-
- dbus_message_unref(reply);
- }
- return intArray;
- }
-#endif
- return NULL;
-}
-
-static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object,
- jstring address, jint handle) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- const char *c_address = env->GetStringUTFChars(address, NULL);
-
- LOGV("... address = %s", c_address);
-
- DBusMessage *reply =
- dbus_func_args(env, nat->conn, nat->adapter,
- DBUS_CLASS_NAME, "GetRemoteServiceRecord",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_UINT32, &handle,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- return reply ? dbus_returns_array_of_bytes(env, reply) : NULL;
- }
-#endif
- return NULL;
-}
-
-static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object,
- jstring address, jshort uuid16) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- 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);
- char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
- strlcpy(context_address, c_address, BTADDR_SIZE);
-
- LOGV("... address = %s", c_address);
- LOGV("... uuid16 = %#X", uuid16);
-
- bool ret = dbus_func_args_async(env, nat->conn, 20000, // ms
- onGetRemoteServiceChannelResult, context_address,
- eventLoopNat,
- nat->adapter,
- DBUS_CLASS_NAME, "GetRemoteServiceChannel",
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_UINT16, &uuid16,
- DBUS_TYPE_INVALID);
- env->ReleaseStringUTFChars(address, c_address);
- return ret ? JNI_TRUE : JNI_FALSE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jint enableNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- return bt_enable();
-#endif
- return -1;
-}
-
-static jint disableNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- return bt_disable();
-#endif
- return -1;
-}
-
-static jint isEnabledNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- return bt_is_enabled();
-#endif
- return -1;
-}
-
-static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
- jstring pin, int nativeData) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *msg = (DBusMessage *)nativeData;
- DBusMessage *reply = dbus_message_new_method_return(msg);
- if (!reply) {
- LOGE("%s: Cannot create message reply to return PIN code to "
- "D-Bus\n", __FUNCTION__);
- dbus_message_unref(msg);
- return JNI_FALSE;
- }
-
- const char *c_pin = env->GetStringUTFChars(pin, NULL);
-
- dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(nat->conn, reply, NULL);
- dbus_message_unref(msg);
- dbus_message_unref(reply);
- env->ReleaseStringUTFChars(pin, c_pin);
- return JNI_TRUE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address,
- int nativeData) {
-#ifdef HAVE_BLUETOOTH
- LOGV(__FUNCTION__);
- native_data_t *nat = get_native_data(env, object);
- if (nat) {
- DBusMessage *msg = (DBusMessage *)nativeData;
- DBusMessage *reply = dbus_message_new_error(msg,
- "org.bluez.Error.Canceled", "PIN Entry was canceled");
- if (!reply) {
- LOGE("%s: Cannot create message reply to return PIN cancel to "
- "D-BUS\n", __FUNCTION__);
- dbus_message_unref(msg);
- return JNI_FALSE;
- }
-
- dbus_connection_send(nat->conn, reply, NULL);
- dbus_message_unref(msg);
- dbus_message_unref(reply);
- return JNI_TRUE;
- }
-#endif
- return JNI_FALSE;
-}
-
-static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"classInitNative", "()V", (void*)classInitNative},
- {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
- {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
- {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative},
-
- {"isEnabledNative", "()I", (void *)isEnabledNative},
- {"enableNative", "()I", (void *)enableNative},
- {"disableNative", "()I", (void *)disableNative},
-
- {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative},
- {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative},
- {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative},
- {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative},
- {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative},
- {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative},
- {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative},
-
- {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative},
- {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative},
-
- {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative},
- {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative},
-
- {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative},
- {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative},
- {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative},
- {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative},
- {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative},
- {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative},
-
- {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative},
- {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative},
- {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative},
-
- {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative},
- {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative},
- {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative},
- {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative},
-
- {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative},
- {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative},
- {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative},
- {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative},
- {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative},
- {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative},
- {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative},
- {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative},
- {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative},
- {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative},
- {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
- {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative},
-};
-
-int register_android_server_BluetoothDeviceService(JNIEnv *env) {
- return AndroidRuntime::registerNativeMethods(env,
- "android/server/BluetoothDeviceService", sMethods, NELEM(sMethods));
-}
-
-} /* namespace android */
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index ad24136..8fe7487 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -39,33 +39,26 @@ namespace android {
#ifdef HAVE_BLUETOOTH
static jfieldID field_mNativeData;
-static jmethodID method_onModeChanged;
-static jmethodID method_onNameChanged;
-static jmethodID method_onDiscoveryStarted;
-static jmethodID method_onDiscoveryCompleted;
-static jmethodID method_onRemoteDeviceFound;
-static jmethodID method_onRemoteDeviceDisappeared;
-static jmethodID method_onRemoteClassUpdated;
-static jmethodID method_onRemoteNameUpdated;
-static jmethodID method_onRemoteNameFailed;
-static jmethodID method_onRemoteDeviceConnected;
-static jmethodID method_onRemoteDeviceDisconnectRequested;
-static jmethodID method_onRemoteDeviceDisconnected;
-static jmethodID method_onBondingCreated;
-static jmethodID method_onBondingRemoved;
-
-static jmethodID method_onCreateBondingResult;
-static jmethodID method_onGetRemoteServiceChannelResult;
-
-static jmethodID method_onPasskeyAgentRequest;
-static jmethodID method_onPasskeyAgentCancel;
-static jmethodID method_onAuthAgentAuthorize;
-static jmethodID method_onAuthAgentCancel;
-
-static jmethodID method_onRestartRequired;
+static jmethodID method_onPropertyChanged;
+static jmethodID method_onDevicePropertyChanged;
+static jmethodID method_onDeviceFound;
+static jmethodID method_onDeviceDisappeared;
+static jmethodID method_onDeviceCreated;
+static jmethodID method_onDeviceRemoved;
+
+static jmethodID method_onCreatePairedDeviceResult;
+static jmethodID method_onGetDeviceServiceChannelResult;
+
+static jmethodID method_onRequestPinCode;
+static jmethodID method_onRequestPasskey;
+static jmethodID method_onRequestConfirmation;
+static jmethodID method_onAgentAuthorize;
+static jmethodID method_onAgentCancel;
typedef event_loop_native_data_t native_data_t;
+#define EVENT_LOOP_REFS 10
+
static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
return (native_data_t *)(env->GetIntField(object,
field_mNativeData));
@@ -80,30 +73,30 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
LOGV(__FUNCTION__);
#ifdef HAVE_BLUETOOTH
- method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V");
- method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V");
- method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V");
- method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V");
- method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V");
- method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V");
- method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V");
- method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V");
- method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V");
- method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V");
- method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V");
- method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V");
- method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V");
- method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V");
-
- method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V");
-
- method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V");
- method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V");
- method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z");
- method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
- method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V");
-
- method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V");
+ method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged",
+ "([Ljava/lang/String;)V");
+ method_onDevicePropertyChanged = env->GetMethodID(clazz,
+ "onDevicePropertyChanged",
+ "(Ljava/lang/String;[Ljava/lang/String;)V");
+ method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound",
+ "(Ljava/lang/String;[Ljava/lang/String;)V");
+ method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared",
+ "(Ljava/lang/String;)V");
+ method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V");
+ method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V");
+
+ method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
+ "(Ljava/lang/String;I)V");
+
+ method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
+ "(Ljava/lang/String;Ljava/lang/String;)Z");
+ method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
+ method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
+ "(Ljava/lang/String;I)V");
+ method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
+ "(Ljava/lang/String;I)V");
+ method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation",
+ "(Ljava/lang/String;II)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -154,9 +147,11 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
#ifdef HAVE_BLUETOOTH
static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
void *data);
-static DBusHandlerResult agent_event_filter(DBusConnection *conn,
- DBusMessage *msg,
- void *data);
+DBusHandlerResult agent_event_filter(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data);
+static int register_agent(native_data_t *nat,
+ const char *agent_path, const char *capabilities);
static const DBusObjectPathVTable agent_vtable = {
NULL, agent_event_filter, NULL, NULL, NULL, NULL
@@ -178,11 +173,12 @@ static short dbus_flags_to_unix_events(unsigned int flags) {
static jboolean setUpEventLoop(native_data_t *nat) {
LOGV(__FUNCTION__);
- dbus_threads_init_default();
- DBusError err;
- dbus_error_init(&err);
if (nat != NULL && nat->conn != NULL) {
+ dbus_threads_init_default();
+ DBusError err;
+ dbus_error_init(&err);
+
// Add a filter for all incoming messages
if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){
return JNI_FALSE;
@@ -204,108 +200,167 @@ static jboolean setUpEventLoop(native_data_t *nat) {
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
- "type='signal',interface='org.bluez.audio.Manager'",
+ "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
dbus_bus_add_match(nat->conn,
- "type='signal',interface='org.bluez.audio.Device'",
+ "type='signal',interface='org.bluez.AudioSink'",
&err);
if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
return JNI_FALSE;
}
- dbus_bus_add_match(nat->conn,
- "type='signal',interface='org.bluez.audio.Sink'",
- &err);
- if (dbus_error_is_set(&err)) {
- LOG_AND_FREE_DBUS_ERROR(&err);
+
+ const char *agent_path = "/android/bluetooth/agent";
+ const char *capabilities = "DisplayYesNo";
+ if (register_agent(nat, agent_path, capabilities) < 0) {
+ dbus_connection_unregister_object_path (nat->conn, agent_path);
return JNI_FALSE;
}
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
- // Add an object handler for passkey agent method calls
- const char *path = "/android/bluetooth/Agent";
- if (!dbus_connection_register_object_path(nat->conn, path,
- &agent_vtable, nat)) {
- LOGE("%s: Can't register object path %s for agent!",
- __FUNCTION__, path);
- return JNI_FALSE;
+
+const char * get_adapter_path(DBusConnection *conn) {
+ DBusMessage *msg, *reply = NULL;
+ DBusError err;
+ const char *device_path = NULL;
+ int attempt = 0;
+
+ for (attempt = 0; attempt < 1000 && reply == NULL; attempt ++) {
+ msg = dbus_message_new_method_call("org.bluez", "/",
+ "org.bluez.Manager", "DefaultAdapter");
+ if (!msg) {
+ LOGE("%s: Can't allocate new method call for get_adapter_path!",
+ __FUNCTION__);
+ return NULL;
}
+ dbus_message_append_args(msg, DBUS_TYPE_INVALID);
+ dbus_error_init(&err);
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
- // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep
- // trying for 10 seconds.
- int attempt;
- for (attempt = 0; attempt < 1000; attempt++) {
- DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
- BLUEZ_DBUS_BASE_PATH,
- "org.bluez.Security", "RegisterDefaultPasskeyAgent",
- DBUS_TYPE_STRING, &path,
- DBUS_TYPE_INVALID);
- if (reply) {
- // Success
- dbus_message_unref(reply);
- LOGV("Registered agent on attempt %d of 1000\n", attempt);
- break;
- } else if (dbus_error_has_name(&err,
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ if (dbus_error_has_name(&err,
"org.freedesktop.DBus.Error.ServiceUnknown")) {
- // hcid is still down, retry
- dbus_error_free(&err);
- usleep(10000); // 10 ms
- } else {
- // Some other error we weren't expecting
- LOG_AND_FREE_DBUS_ERROR(&err);
- return JNI_FALSE;
+ // bluetoothd is still down, retry
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ usleep(10000); // 10 ms
+ continue;
+ } else {
+ // Some other error we weren't expecting
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ }
}
+ goto failed;
}
- if (attempt == 1000) {
- LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), "
- "is hcid running?");
- return JNI_FALSE;
- }
+ }
+ if (attempt == 1000) {
+ LOGE("Time out while trying to get Adapter path, is bluetoothd up ?");
+ goto failed;
+ }
- // Now register the Auth agent
- DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
- BLUEZ_DBUS_BASE_PATH,
- "org.bluez.Security", "RegisterDefaultAuthorizationAgent",
- DBUS_TYPE_STRING, &path,
- DBUS_TYPE_INVALID);
- if (!reply) {
+ if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH,
+ &device_path, DBUS_TYPE_INVALID)
+ || !device_path){
+ if (dbus_error_is_set(&err)) {
LOG_AND_FREE_DBUS_ERROR(&err);
- return JNI_FALSE;
}
+ goto failed;
+ }
+ dbus_message_unref(msg);
+ return device_path;
- dbus_message_unref(reply);
- return JNI_TRUE;
+failed:
+ dbus_message_unref(msg);
+ return NULL;
+}
+
+static int register_agent(native_data_t *nat,
+ const char * agent_path, const char * capabilities)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+
+ if (!dbus_connection_register_object_path(nat->conn, agent_path,
+ &agent_vtable, nat)) {
+ LOGE("%s: Can't register object path %s for agent!",
+ __FUNCTION__, agent_path);
+ return -1;
}
- return JNI_FALSE;
+ nat->adapter = get_adapter_path(nat->conn);
+ if (nat->adapter == NULL) {
+ return -1;
+ }
+ msg = dbus_message_new_method_call("org.bluez", nat->adapter,
+ "org.bluez.Adapter", "RegisterAgent");
+ if (!msg) {
+ LOGE("%s: Can't allocate new method call for agent!",
+ __FUNCTION__);
+ return -1;
+ }
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capabilities,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ LOGE("%s: Can't register agent!", __FUNCTION__);
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ }
+ return -1;
+ }
+
+ dbus_message_unref(reply);
+ dbus_connection_flush(nat->conn);
+
+ return 0;
}
static void tearDownEventLoop(native_data_t *nat) {
LOGV(__FUNCTION__);
if (nat != NULL && nat->conn != NULL) {
+ DBusMessage *msg, *reply;
DBusError err;
dbus_error_init(&err);
+ const char * agent_path = "/android/bluetooth/agent";
+
+ msg = dbus_message_new_method_call("org.bluez",
+ nat->adapter,
+ "org.bluez.Adapter",
+ "UnregisterAgent");
+ if (msg != NULL) {
+ dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID);
+ reply = dbus_connection_send_with_reply_and_block(nat->conn,
+ msg, -1, &err);
- const char *path = "/android/bluetooth/Agent";
- DBusMessage *reply =
- dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
- "org.bluez.Security", "UnregisterDefaultPasskeyAgent",
- DBUS_TYPE_STRING, &path,
- DBUS_TYPE_INVALID);
- if (reply) dbus_message_unref(reply);
-
- reply =
- dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
- "org.bluez.Security", "UnregisterDefaultAuthorizationAgent",
- DBUS_TYPE_STRING, &path,
- DBUS_TYPE_INVALID);
- if (reply) dbus_message_unref(reply);
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ dbus_error_free(&err);
+ }
+ } else {
+ dbus_message_unref(reply);
+ }
+ dbus_message_unref(msg);
+ } else {
+ LOGE("%s: Can't create new method call!", __FUNCTION__);
+ }
- dbus_connection_unregister_object_path(nat->conn, path);
+ dbus_connection_flush(nat->conn);
+ dbus_connection_unregister_object_path(nat->conn, agent_path);
dbus_bus_remove_match(nat->conn,
"type='signal',interface='org.bluez.audio.Sink'",
@@ -650,6 +705,7 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
native_data_t *nat;
JNIEnv *env;
DBusError err;
+ DBusHandlerResult ret;
dbus_error_init(&err);
@@ -660,321 +716,173 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- LOGV("%s: Received signal %s:%s from %s", __FUNCTION__,
- dbus_message_get_interface(msg), dbus_message_get_member(msg),
- dbus_message_get_path(msg));
+ // STOPSHIP: Change to LOGV
+ LOGE("%s: Received signal %s:%s from %s", __FUNCTION__,
+ dbus_message_get_interface(msg), dbus_message_get_member(msg),
+ dbus_message_get_path(msg));
+ env->PushLocalFrame(EVENT_LOOP_REFS);
if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
- "RemoteDeviceFound")) {
+ "DeviceFound")) {
char *c_address;
- int n_class;
- short n_rssi;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_UINT32, &n_class,
- DBUS_TYPE_INT16, &n_rssi,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class,
- n_rssi);
+ DBusMessageIter iter;
+ jobjectArray str_array = NULL;
+ if (dbus_message_iter_init(msg, &iter)) {
+ dbus_message_iter_get_basic(&iter, &c_address);
+ if (dbus_message_iter_next(&iter))
+ str_array =
+ parse_remote_device_properties(env, &iter);
+ }
+ if (str_array != NULL) {
env->CallVoidMethod(nat->me,
- method_onRemoteDeviceFound,
+ method_onDeviceFound,
env->NewStringUTF(c_address),
- (jint)n_class,
- (jshort)n_rssi);
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ str_array);
+ } else
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ goto success;
} else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "DiscoveryStarted")) {
- LOGI("DiscoveryStarted signal received");
- env->CallVoidMethod(nat->me, method_onDiscoveryStarted);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "DiscoveryCompleted")) {
- LOGI("DiscoveryCompleted signal received");
- env->CallVoidMethod(nat->me, method_onDiscoveryCompleted);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteDeviceDisappeared")) {
+ "org.bluez.Adapter",
+ "DeviceDisappeared")) {
char *c_address;
if (dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID)) {
LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared,
+ env->CallVoidMethod(nat->me, method_onDeviceDisappeared,
env->NewStringUTF(c_address));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteClassUpdated")) {
- char *c_address;
- int n_class;
+ "org.bluez.Adapter",
+ "DeviceCreated")) {
+ char *c_object_path;
if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_UINT32, &n_class,
+ DBUS_TYPE_OBJECT_PATH, &c_object_path,
DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me, method_onRemoteClassUpdated,
- env->NewStringUTF(c_address), (jint)n_class);
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteNameUpdated")) {
- char *c_address;
- char *c_name;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_STRING, &c_name,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s, name = %s", c_address, c_name);
+ LOGV("... address = %s", c_object_path);
env->CallVoidMethod(nat->me,
- method_onRemoteNameUpdated,
- env->NewStringUTF(c_address),
- env->NewStringUTF(c_name));
+ method_onDeviceCreated,
+ env->NewStringUTF(c_object_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteNameFailed")) {
- char *c_address;
+ "org.bluez.Adapter",
+ "DeviceRemoved")) {
+ char *c_object_path;
if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onRemoteNameFailed,
- env->NewStringUTF(c_address));
+ DBUS_TYPE_OBJECT_PATH, &c_object_path,
+ DBUS_TYPE_INVALID)) {
+ LOGV("... Object Path = %s", c_object_path);
+ env->CallVoidMethod(nat->me,
+ method_onDeviceRemoved,
+ env->NewStringUTF(c_object_path));
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else if (dbus_message_is_signal(msg,
"org.bluez.Adapter",
- "RemoteDeviceConnected")) {
- char *c_address;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onRemoteDeviceConnected,
- env->NewStringUTF(c_address));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteDeviceDisconnectRequested")) {
- char *c_address;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onRemoteDeviceDisconnectRequested,
- env->NewStringUTF(c_address));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "RemoteDeviceDisconnected")) {
- char *c_address;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onRemoteDeviceDisconnected,
- env->NewStringUTF(c_address));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "BondingCreated")) {
- char *c_address;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onBondingCreated,
- env->NewStringUTF(c_address));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "BondingRemoved")) {
- char *c_address;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_address,
- DBUS_TYPE_INVALID)) {
- LOGV("... address = %s", c_address);
- env->CallVoidMethod(nat->me,
- method_onBondingRemoved,
- env->NewStringUTF(c_address));
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "ModeChanged")) {
- char *c_mode;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_mode,
- DBUS_TYPE_INVALID)) {
- LOGV("... mode = %s", c_mode);
+ "PropertyChanged")) {
+ jobjectArray str_array = parse_adapter_property_change(env, msg);
+ if (str_array != NULL) {
+ /* Check if bluetoothd has (re)started, if so update the path. */
+ jstring property =(jstring) env->GetObjectArrayElement(str_array, 0);
+ const char *c_property = env->GetStringUTFChars(property, NULL);
+ if (!strncmp(c_property, "Powered", strlen("Powered"))) {
+ jstring value =
+ (jstring) env->GetObjectArrayElement(str_array, 1);
+ const char *c_value = env->GetStringUTFChars(value, NULL);
+ if (!strncmp(c_value, "true", strlen("true")))
+ nat->adapter = get_adapter_path(nat->conn);
+ env->ReleaseStringUTFChars(value, c_value);
+ }
+ env->ReleaseStringUTFChars(property, c_property);
+
env->CallVoidMethod(nat->me,
- method_onModeChanged,
- env->NewStringUTF(c_mode));
+ method_onPropertyChanged,
+ str_array);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else if (dbus_message_is_signal(msg,
- "org.bluez.Adapter",
- "NameChanged")) {
- char *c_name;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_name,
- DBUS_TYPE_INVALID)) {
- LOGV("... name = %s", c_name);
+ "org.bluez.Device",
+ "PropertyChanged")) {
+ jobjectArray str_array = parse_remote_device_property_change(env, msg);
+ if (str_array != NULL) {
+ const char *remote_device_path = dbus_message_get_path(msg);
env->CallVoidMethod(nat->me,
- method_onNameChanged,
- env->NewStringUTF(c_name));
+ method_onDevicePropertyChanged,
+ env->NewStringUTF(remote_device_path),
+ str_array);
} else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
- } else if (dbus_message_is_signal(msg,
- "org.freedesktop.DBus",
- "NameOwnerChanged")) {
- char *c_name;
- char *c_old_owner;
- char *c_new_owner;
- if (dbus_message_get_args(msg, &err,
- DBUS_TYPE_STRING, &c_name,
- DBUS_TYPE_STRING, &c_old_owner,
- DBUS_TYPE_STRING, &c_new_owner,
- DBUS_TYPE_INVALID)) {
- LOGV("... name = %s", c_name);
- LOGV("... old_owner = %s", c_old_owner);
- LOGV("... new_owner = %s", c_new_owner);
- if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') {
- // New owner of org.bluez. This can only happen when hcid
- // restarts. Need to restart framework BT services to recover.
- LOGE("Looks like hcid restarted");
- env->CallVoidMethod(nat->me, method_onRestartRequired);
- }
- } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
}
- return a2dp_event_filter(msg, env);
+ ret = a2dp_event_filter(msg, env);
+ env->PopLocalFrame(NULL);
+ return ret;
+
+success:
+ env->PopLocalFrame(NULL);
+ return DBUS_HANDLER_RESULT_HANDLED;
}
// Called by dbus during WaitForAndDispatchEventNative()
-static DBusHandlerResult agent_event_filter(DBusConnection *conn,
- DBusMessage *msg, void *data) {
+DBusHandlerResult agent_event_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data) {
native_data_t *nat = (native_data_t *)data;
JNIEnv *env;
- nat->vm->GetEnv((void**)&env, nat->envVer);
if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
LOGV("%s: not interested (not a method call).", __FUNCTION__);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
- LOGV("%s: Received method %s:%s", __FUNCTION__,
+ LOGI("%s: Received method %s:%s", __FUNCTION__,
dbus_message_get_interface(msg), dbus_message_get_member(msg));
- if (dbus_message_is_method_call(msg,
- "org.bluez.PasskeyAgent", "Request")) {
-
- const char *adapter;
- const char *address;
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &adapter,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID)) {
- LOGE("%s: Invalid arguments for Request() method", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- LOGV("... address = %s", address);
-
- dbus_message_ref(msg); // increment refcount because we pass to java
-
- env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest,
- env->NewStringUTF(address), (int)msg);
-
- return DBUS_HANDLER_RESULT_HANDLED;
-
- } else if (dbus_message_is_method_call(msg,
- "org.bluez.PasskeyAgent", "Cancel")) {
+ if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED;
- const char *adapter;
- const char *address;
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &adapter,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID)) {
- LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
+ nat->vm->GetEnv((void**)&env, nat->envVer);
+ env->PushLocalFrame(EVENT_LOOP_REFS);
- LOGV("... address = %s", address);
+ if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "Cancel")) {
- env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel,
- env->NewStringUTF(address));
+ env->CallVoidMethod(nat->me, method_onAgentCancel);
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto failure;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
- return DBUS_HANDLER_RESULT_HANDLED;
-
- } else if (dbus_message_is_method_call(msg,
- "org.bluez.PasskeyAgent", "Release")) {
- LOGW("We are no longer the passkey agent!");
+ goto success;
- // reply
- DBusMessage *reply = dbus_message_new_method_return(msg);
- if (!reply) {
- LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- dbus_connection_send(nat->conn, reply, NULL);
- dbus_message_unref(reply);
- return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(msg,
- "org.bluez.AuthorizationAgent", "Authorize")) {
- const char *adapter;
- const char *address;
- const char *service;
+ "org.bluez.Agent", "Authorize")) {
+ char *object_path;
const char *uuid;
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &adapter,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_STRING, &service,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
DBUS_TYPE_STRING, &uuid,
DBUS_TYPE_INVALID)) {
LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto failure;
}
- LOGV("... address = %s", address);
- LOGV("... service = %s", service);
+ LOGV("... object_path = %s", object_path);
LOGV("... uuid = %s", uuid);
- bool auth_granted = env->CallBooleanMethod(nat->me,
- method_onAuthAgentAuthorize, env->NewStringUTF(address),
- env->NewStringUTF(service), env->NewStringUTF(uuid));
+ bool auth_granted =
+ env->CallBooleanMethod(nat->me, method_onAgentAuthorize,
+ env->NewStringUTF(object_path), env->NewStringUTF(uuid));
// reply
if (auth_granted) {
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto failure;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
@@ -983,64 +891,83 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
"org.bluez.Error.Rejected", "Authorization rejected");
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto failure;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
}
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else if (dbus_message_is_method_call(msg,
- "org.bluez.AuthorizationAgent", "Cancel")) {
- const char *adapter;
- const char *address;
- const char *service;
- const char *uuid;
+ "org.bluez.Agent", "RequestPinCode")) {
+ char *object_path;
if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &adapter,
- DBUS_TYPE_STRING, &address,
- DBUS_TYPE_STRING, &service,
- DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
DBUS_TYPE_INVALID)) {
- LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__);
+ goto failure;
}
- LOGV("... address = %s", address);
- LOGV("... service = %s", service);
- LOGV("... uuid = %s", uuid);
-
- env->CallVoidMethod(nat->me,
- method_onAuthAgentCancel, env->NewStringUTF(address),
- env->NewStringUTF(service), env->NewStringUTF(uuid));
-
- // reply
- DBusMessage *reply = dbus_message_new_method_return(msg);
- if (!reply) {
- LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestPinCode,
+ env->NewStringUTF(object_path),
+ int(msg));
+ goto success;
+ } else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "RequestPasskey")) {
+ char *object_path;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
+ goto failure;
}
- dbus_connection_send(nat->conn, reply, NULL);
- dbus_message_unref(reply);
- return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestPasskey,
+ env->NewStringUTF(object_path),
+ int(msg));
+ goto success;
} else if (dbus_message_is_method_call(msg,
- "org.bluez.AuthorizationAgent", "Release")) {
- LOGW("We are no longer the auth agent!");
+ "org.bluez.Agent", "RequestConfirmation")) {
+ char *object_path;
+ uint32_t passkey;
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &object_path,
+ DBUS_TYPE_UINT32, &passkey,
+ DBUS_TYPE_INVALID)) {
+ LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
+ goto failure;
+ }
+ dbus_message_ref(msg); // increment refcount because we pass to java
+ env->CallVoidMethod(nat->me, method_onRequestConfirmation,
+ env->NewStringUTF(object_path),
+ passkey,
+ int(msg));
+ goto success;
+ } else if (dbus_message_is_method_call(msg,
+ "org.bluez.Agent", "Release")) {
// reply
DBusMessage *reply = dbus_message_new_method_return(msg);
if (!reply) {
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ goto failure;
}
dbus_connection_send(nat->conn, reply, NULL);
dbus_message_unref(reply);
- return DBUS_HANDLER_RESULT_HANDLED;
+ goto success;
} else {
- LOGV("... ignored");
+ LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg));
}
+failure:
+ env->PopLocalFrame(NULL);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+success:
+ env->PopLocalFrame(NULL);
+ return DBUS_HANDLER_RESULT_HANDLED;
+
}
#endif
@@ -1055,7 +982,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn,
#define BOND_RESULT_REMOTE_DEVICE_DOWN 4
#define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
-void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
+void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) {
LOGV(__FUNCTION__);
native_data_t *nat = (native_data_t *)n;
@@ -1106,7 +1033,7 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
}
env->CallVoidMethod(nat->me,
- method_onCreateBondingResult,
+ method_onCreatePairedDeviceResult,
env->NewStringUTF(address),
result);
done:
@@ -1114,7 +1041,7 @@ done:
free(user);
}
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
+void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
LOGV(__FUNCTION__);
const char *address = (const char *) user;
@@ -1133,14 +1060,13 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
!dbus_message_get_args(msg, &err,
DBUS_TYPE_INT32, &channel,
DBUS_TYPE_INVALID)) {
- /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */
LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message);
dbus_error_free(&err);
}
done:
env->CallVoidMethod(nat->me,
- method_onGetRemoteServiceChannelResult,
+ method_onGetDeviceServiceChannelResult,
env->NewStringUTF(address),
channel);
free(user);
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
new file mode 100644
index 0000000..de921f1
--- /dev/null
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -0,0 +1,807 @@
+/*
+** Copyright 2006, 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.
+*/
+
+#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
+#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
+#define LOG_TAG "BluetoothService.cpp"
+
+#include "android_bluetooth_common.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "JNIHelp.h"
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+#ifdef HAVE_BLUETOOTH
+#include <dbus/dbus.h>
+#include <bluedroid/bluetooth.h>
+#endif
+
+#include <cutils/properties.h>
+
+namespace android {
+
+#define BLUETOOTH_CLASS_ERROR 0xFF000000
+#define PROPERTIES_NREFS 10
+
+#ifdef HAVE_BLUETOOTH
+// We initialize these variables when we load class
+// android.server.BluetoothService
+static jfieldID field_mNativeData;
+static jfieldID field_mEventLoop;
+
+typedef struct {
+ JNIEnv *env;
+ DBusConnection *conn;
+ const char *adapter; // dbus object name of the local adapter
+} native_data_t;
+
+extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
+ jobject);
+extern DBusHandlerResult agent_event_filter(DBusConnection *conn,
+ DBusMessage *msg,
+ void *data);
+void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
+
+
+/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
+ * Perform quick sanity check, if there are any problems return NULL
+ */
+static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
+ native_data_t *nat =
+ (native_data_t *)(env->GetIntField(object, field_mNativeData));
+ if (nat == NULL || nat->conn == NULL) {
+ LOGE("Uninitialized native data\n");
+ return NULL;
+ }
+ return nat;
+}
+#endif
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ field_mNativeData = get_field(env, clazz, "mNativeData", "I");
+ field_mEventLoop = get_field(env, clazz, "mEventLoop",
+ "Landroid/server/BluetoothEventLoop;");
+#endif
+}
+
+/* Returns true on success (even if adapter is present but disabled).
+ * Return false if dbus is down, or another serious error (out of memory)
+*/
+static bool initializeNativeDataNative(JNIEnv* env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
+ if (NULL == nat) {
+ LOGE("%s: out of memory!", __FUNCTION__);
+ return false;
+ }
+ nat->env = env;
+
+ env->SetIntField(object, field_mNativeData, (jint)nat);
+ DBusError err;
+ dbus_error_init(&err);
+ dbus_threads_init_default();
+ nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err)) {
+ LOGE("Could not get onto the system bus: %s", err.message);
+ dbus_error_free(&err);
+ return false;
+ }
+ dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
+#endif /*HAVE_BLUETOOTH*/
+ return true;
+}
+
+static const char *get_adapter_path(JNIEnv* env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+ event_loop_native_data_t *event_nat =
+ get_EventLoop_native_data(env, env->GetObjectField(object,
+ field_mEventLoop));
+ if (event_nat == NULL)
+ return NULL;
+ return event_nat->adapter;
+#else
+ return NULL;
+#endif
+}
+
+// This function is called when the adapter is enabled.
+static jboolean setupNativeDataNative(JNIEnv* env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat =
+ (native_data_t *)env->GetIntField(object, field_mNativeData);
+ event_loop_native_data_t *event_nat =
+ get_EventLoop_native_data(env, env->GetObjectField(object,
+ field_mEventLoop));
+ // Register agent for remote devices.
+ const char *device_agent_path = "/android/bluetooth/remote_device_agent";
+ static const DBusObjectPathVTable agent_vtable = {
+ NULL, agent_event_filter, NULL, NULL, NULL, NULL };
+
+ if (!dbus_connection_register_object_path(nat->conn, device_agent_path,
+ &agent_vtable, event_nat)) {
+ LOGE("%s: Can't register object path %s for remote device agent!",
+ __FUNCTION__, device_agent_path);
+ return JNI_FALSE;
+ }
+#endif /*HAVE_BLUETOOTH*/
+ return JNI_TRUE;
+}
+
+static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat =
+ (native_data_t *)env->GetIntField(object, field_mNativeData);
+ if (nat != NULL) {
+ const char *device_agent_path =
+ "/android/bluetooth/remote_device_agent";
+ dbus_connection_unregister_object_path (nat->conn, device_agent_path);
+ }
+#endif /*HAVE_BLUETOOTH*/
+ return JNI_TRUE;
+}
+
+static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat =
+ (native_data_t *)env->GetIntField(object, field_mNativeData);
+ if (nat) {
+ free(nat);
+ nat = NULL;
+ }
+#endif
+}
+
+static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ return (env->NewStringUTF(get_adapter_path(env, object)));
+ }
+#endif
+ return NULL;
+}
+
+
+static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ DBusError err;
+ const char *name;
+ jboolean ret = JNI_FALSE;
+
+ native_data_t *nat = get_native_data(env, object);
+ if (nat == NULL) {
+ goto done;
+ }
+
+ dbus_error_init(&err);
+
+ /* Compose the command */
+ msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "StartDiscovery");
+
+ if (msg == NULL) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ }
+ goto done;
+ }
+
+ /* Send the command. */
+ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ ret = JNI_FALSE;
+ goto done;
+ }
+
+ ret = JNI_TRUE;
+done:
+ if (reply) dbus_message_unref(reply);
+ if (msg) dbus_message_unref(msg);
+ return ret;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+static void stopDiscoveryNative(JNIEnv *env, jobject object) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ DBusError err;
+ const char *name;
+ jstring ret;
+ native_data_t *nat;
+
+ dbus_error_init(&err);
+
+ nat = get_native_data(env, object);
+ if (nat == NULL) {
+ goto done;
+ }
+
+ /* Compose the command */
+ msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "StopDiscovery");
+ if (msg == NULL) {
+ if (dbus_error_is_set(&err))
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ goto done;
+ }
+
+ /* Send the command. */
+ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+ if (dbus_error_is_set(&err)) {
+ if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized",
+ strlen(BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized")) == 0) {
+ // hcid sends this if there is no active discovery to cancel
+ LOGV("%s: There was no active discovery to cancel", __FUNCTION__);
+ dbus_error_free(&err);
+ } else {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ }
+ }
+
+done:
+ if (msg) dbus_message_unref(msg);
+ if (reply) dbus_message_unref(reply);
+#endif
+}
+
+static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
+ jstring address, jint timeout_ms) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ 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);
+ LOGV("... address = %s", c_address);
+ char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+ const char *capabilities = "DisplayYesNo";
+ const char *agent_path = "/android/bluetooth/remote_device_agent";
+
+ strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
+ bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
+ onCreatePairedDeviceResult, // callback
+ context_address,
+ eventLoopNat,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE,
+ "CreatePairedDevice",
+ DBUS_TYPE_STRING, &c_address,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_STRING, &capabilities,
+ DBUS_TYPE_INVALID);
+ env->ReleaseStringUTFChars(address, c_address);
+ return ret ? JNI_TRUE : JNI_FALSE;
+
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
+ jstring path,
+ jstring pattern, jint attr_id) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ 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_pattern = env->GetStringUTFChars(pattern, NULL);
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+ LOGV("... pattern = %s", c_pattern);
+ LOGV("... attr_id = %#X", attr_id);
+ DBusMessage *reply =
+ dbus_func_args(env, nat->conn, c_path,
+ DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
+ DBUS_TYPE_STRING, &c_pattern,
+ DBUS_TYPE_UINT16, &attr_id,
+ DBUS_TYPE_INVALID);
+ env->ReleaseStringUTFChars(pattern, c_pattern);
+ env->ReleaseStringUTFChars(path, c_path);
+ return reply ? dbus_returns_int32(env, reply) : -1;
+ }
+#endif
+ return -1;
+}
+
+static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object,
+ jstring address) {
+ LOGV(__FUNCTION__);
+ jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ const char *c_address = env->GetStringUTFChars(address, NULL);
+ DBusError err;
+ dbus_error_init(&err);
+ LOGV("... address = %s", c_address);
+ DBusMessage *reply =
+ dbus_func_args_timeout(env, nat->conn, -1,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "CancelDeviceCreation",
+ DBUS_TYPE_STRING, &c_address,
+ DBUS_TYPE_INVALID);
+ env->ReleaseStringUTFChars(address, c_address);
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return JNI_FALSE;
+ } else {
+ result = JNI_TRUE;
+ }
+ dbus_message_unref(reply);
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) {
+ LOGV(__FUNCTION__);
+ jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ const char *c_object_path = env->GetStringUTFChars(object_path, NULL);
+ DBusError err;
+ dbus_error_init(&err);
+ DBusMessage *reply =
+ dbus_func_args_error(env, nat->conn, &err,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "RemoveDevice",
+ DBUS_TYPE_OBJECT_PATH, &c_object_path,
+ DBUS_TYPE_INVALID);
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ result = JNI_FALSE;
+ } else {
+ result = JNI_TRUE;
+ }
+ env->ReleaseStringUTFChars(object_path, c_object_path);
+ if (reply) dbus_message_unref(reply);
+ }
+#endif
+ return result;
+}
+
+static jint enableNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ return bt_enable();
+#endif
+ return -1;
+}
+
+static jint disableNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ return bt_disable();
+#endif
+ return -1;
+}
+
+static jint isEnabledNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ return bt_is_enabled();
+#endif
+ return -1;
+}
+
+static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object,
+ jstring address, bool confirm,
+ int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply;
+ if (confirm) {
+ reply = dbus_message_new_method_return(msg);
+ } else {
+ reply = dbus_message_new_error(msg,
+ "org.bluez.Error.Rejected", "User rejected confirmation");
+ }
+
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to RequestConfirmation to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
+ int passkey, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to return Passkey code to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
+ jstring pin, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to return PIN code to "
+ "D-Bus\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ const char *c_pin = env->GetStringUTFChars(pin, NULL);
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin,
+ DBUS_TYPE_INVALID);
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ env->ReleaseStringUTFChars(pin, c_pin);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object,
+ jstring address, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg = (DBusMessage *)nativeData;
+ DBusMessage *reply = dbus_message_new_error(msg,
+ "org.bluez.Error.Canceled", "Pairing User Input was canceled");
+ if (!reply) {
+ LOGE("%s: Cannot create message reply to return cancelUserInput to"
+ "D-BUS\n", __FUNCTION__);
+ dbus_message_unref(msg);
+ return JNI_FALSE;
+ }
+
+ dbus_connection_send(nat->conn, reply, NULL);
+ dbus_message_unref(msg);
+ dbus_message_unref(reply);
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object,
+ jstring path)
+{
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg, *reply;
+ DBusError err;
+ dbus_error_init(&err);
+
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+ reply = dbus_func_args_timeout(env,
+ nat->conn, -1, c_path,
+ DBUS_DEVICE_IFACE, "GetProperties",
+ DBUS_TYPE_INVALID);
+ env->ReleaseStringUTFChars(path, c_path);
+
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return NULL;
+ }
+ env->PushLocalFrame(PROPERTIES_NREFS);
+
+ DBusMessageIter iter;
+ jobjectArray str_array = NULL;
+ if (dbus_message_iter_init(reply, &iter))
+ str_array = parse_remote_device_properties(env, &iter);
+ dbus_message_unref(reply);
+
+ env->PopLocalFrame(NULL);
+
+ return str_array;
+ }
+#endif
+ return NULL;
+}
+
+static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *msg, *reply;
+ DBusError err;
+ dbus_error_init(&err);
+
+ reply = dbus_func_args_timeout(env,
+ nat->conn, -1, get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "GetProperties",
+ DBUS_TYPE_INVALID);
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return NULL;
+ }
+ env->PushLocalFrame(PROPERTIES_NREFS);
+
+ DBusMessageIter iter;
+ jobjectArray str_array = NULL;
+ if (dbus_message_iter_init(reply, &iter))
+ str_array = parse_adapter_properties(env, &iter);
+ dbus_message_unref(reply);
+
+ env->PopLocalFrame(NULL);
+ return str_array;
+ }
+#endif
+ return NULL;
+}
+
+static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,
+ void *value, jint type) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *reply, *msg;
+ DBusMessageIter iter;
+ DBusError err;
+ const char *c_key = env->GetStringUTFChars(key, NULL);
+ dbus_error_init(&err);
+
+ msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+ get_adapter_path(env, object),
+ DBUS_ADAPTER_IFACE, "SetProperty");
+ if (!msg) {
+ LOGE("%s: Can't allocate new method call for GetProperties!",
+ __FUNCTION__);
+ env->ReleaseStringUTFChars(key, c_key);
+ return JNI_FALSE;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
+ dbus_message_iter_init_append(msg, &iter);
+ append_variant(&iter, type, value);
+
+ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+ dbus_message_unref(msg);
+
+ env->ReleaseStringUTFChars(key, c_key);
+
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key,
+ jstring value) {
+#ifdef HAVE_BLUETOOTH
+ const char *c_value = env->GetStringUTFChars(value, NULL);
+ jboolean ret = setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING);
+ env->ReleaseStringUTFChars(value, (char *)c_value);
+ return ret;
+#else
+ return JNI_FALSE;
+#endif
+}
+
+static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key,
+ jint value) {
+#ifdef HAVE_BLUETOOTH
+ return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32);
+#else
+ return JNI_FALSE;
+#endif
+}
+
+static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key,
+ jint value) {
+#ifdef HAVE_BLUETOOTH
+ return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN);
+#else
+ return JNI_FALSE;
+#endif
+}
+
+static jboolean setDevicePropertyNative(JNIEnv *env, jobject object, jstring path,
+ jstring key, void *value, jint type) {
+#ifdef HAVE_BLUETOOTH
+ LOGV(__FUNCTION__);
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *reply, *msg;
+ DBusMessageIter iter;
+ DBusError err;
+
+ const char *c_key = env->GetStringUTFChars(key, NULL);
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+
+ dbus_error_init(&err);
+ msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
+ c_path, DBUS_DEVICE_IFACE, "SetProperty");
+ if (!msg) {
+ LOGE("%s: Can't allocate new method call for device SetProperty!", __FUNCTION__);
+ env->ReleaseStringUTFChars(key, c_key);
+ env->ReleaseStringUTFChars(path, c_path);
+ return JNI_FALSE;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
+ dbus_message_iter_init_append(msg, &iter);
+ append_variant(&iter, type, value);
+
+ reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
+ dbus_message_unref(msg);
+
+ env->ReleaseStringUTFChars(key, c_key);
+ env->ReleaseStringUTFChars(path, c_path);
+ if (!reply) {
+ if (dbus_error_is_set(&err)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ } else
+ LOGE("DBus reply is NULL in function %s", __FUNCTION__);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean setDevicePropertyBooleanNative(JNIEnv *env, jobject object,
+ jstring path, jstring key, jint value) {
+#ifdef HAVE_BLUETOOTH
+ return setDevicePropertyNative(env, object, path, key,
+ (void *)&value, DBUS_TYPE_BOOLEAN);
+#else
+ return JNI_FALSE;
+#endif
+}
+
+static JNINativeMethod sMethods[] = {
+ /* name, signature, funcPtr */
+ {"classInitNative", "()V", (void*)classInitNative},
+ {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
+ {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative},
+ {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative},
+ {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
+ {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative},
+
+ {"isEnabledNative", "()I", (void *)isEnabledNative},
+ {"enableNative", "()I", (void *)enableNative},
+ {"disableNative", "()I", (void *)disableNative},
+
+ {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative},
+ {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
+ (void *)getDevicePropertiesNative},
+ {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
+ (void *)setAdapterPropertyStringNative},
+ {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z",
+ (void *)setAdapterPropertyBooleanNative},
+ {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z",
+ (void *)setAdapterPropertyIntegerNative},
+
+ {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
+ {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
+
+ {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
+ {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
+ {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
+ {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
+ (void *)getDeviceServiceChannelNative},
+
+ {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
+ (void *)setPairingConfirmationNative},
+ {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
+ {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
+ {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
+ (void *)cancelPairingUserInputNative},
+ {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
+ (void *)setDevicePropertyBooleanNative},
+};
+
+int register_android_server_BluetoothService(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/server/BluetoothService", sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 7325432..f0885fd 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -25,12 +25,12 @@
#include <stdio.h>
#include <utils/Atomic.h>
-#include <utils/IInterface.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
#include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <android_runtime/AndroidRuntime.h>
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 16d993d..495e76a 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -15,7 +15,7 @@
** limitations under the License.
*/
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
#include "jni.h"
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 5e5103a..34b7c89 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -51,17 +51,17 @@ struct ByteBuf {
size_t len;
size_t capacity;
uint8_t* buf;
-
+
ByteBuf(size_t initSize) {
buf = (uint8_t*)malloc(initSize);
len = 0;
- capacity = initSize;
+ capacity = initSize;
}
-
+
~ByteBuf() {
free(buf);
}
-
+
bool ensureExtraCapacity(size_t extra) {
size_t spaceNeeded = len + extra;
if (spaceNeeded > capacity) {
@@ -77,7 +77,7 @@ struct ByteBuf {
return true;
}
}
-
+
void putIntEvent(jint value) {
bool succeeded = ensureExtraCapacity(INT_BUFFER_SIZE);
buf[len++] = EVENT_TYPE_INT;
@@ -162,7 +162,7 @@ static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env, jobject clazz,
* In class android.util.EventLog:
* static native int writeEvent(long tag, long value)
*/
-static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz,
+static jint android_util_EventLog_writeEvent_Long(JNIEnv* env, jobject clazz,
jint tag, jlong value)
{
return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value));
@@ -210,6 +210,8 @@ static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz,
/*
* In class android.util.EventLog:
* static native void readEvents(int[] tags, Collection<Event> output)
+ *
+ * Reads events from the event log, typically /dev/log/events
*/
static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz,
jintArray tags,
@@ -273,6 +275,80 @@ static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz,
env->ReleaseIntArrayElements(tags, tagValues, 0);
}
+/*
+ * In class android.util.EventLog:
+ * static native void readEvents(String path, Collection<Event> output)
+ *
+ * Reads events from a file (See Checkin.Aggregation). Events are stored in
+ * native raw format (logger_entry + payload).
+ */
+static void android_util_EventLog_readEventsFile(JNIEnv* env, jobject clazz, jstring path,
+ jobject out) {
+ if (path == NULL || out == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return;
+ }
+
+ const char *pathString = env->GetStringUTFChars(path, 0);
+ int fd = open(pathString, O_RDONLY | O_NONBLOCK);
+ env->ReleaseStringUTFChars(path, pathString);
+
+ if (fd < 0) {
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ uint8_t buf[LOGGER_ENTRY_MAX_LEN];
+ for (;;) {
+ // read log entry structure from file
+ int len = read(fd, buf, sizeof(logger_entry));
+ if (len == 0) {
+ break; // end of file
+ } else if (len < 0) {
+ jniThrowIOException(env, errno);
+ } else if ((size_t) len < sizeof(logger_entry)) {
+ jniThrowException(env, "java/io/IOException", "Event header too short");
+ break;
+ }
+
+ // read event payload
+ logger_entry* entry = (logger_entry*) buf;
+ if (entry->len > LOGGER_ENTRY_MAX_PAYLOAD) {
+ jniThrowException(env,
+ "java/lang/IllegalArgumentException",
+ "Too much data for event payload. Corrupt file?");
+ break;
+ }
+
+ len = read(fd, buf + sizeof(logger_entry), entry->len);
+ if (len == 0) {
+ break; // end of file
+ } else if (len < 0) {
+ jniThrowIOException(env, errno);
+ } else if ((size_t) len < entry->len) {
+ jniThrowException(env, "java/io/IOException", "Event payload too short");
+ break;
+ }
+
+ // create EventLog$Event and add it to the collection
+ int buffer_size = sizeof(logger_entry) + entry->len;
+ jbyteArray array = env->NewByteArray(buffer_size);
+ if (array == NULL) break;
+
+ jbyte *bytes = env->GetByteArrayElements(array, NULL);
+ memcpy(bytes, buf, buffer_size);
+ env->ReleaseByteArrayElements(array, bytes, 0);
+
+ jobject event = env->NewObject(gEventClass, gEventInitID, array);
+ if (event == NULL) break;
+
+ env->CallBooleanMethod(out, gCollectionAddID, event);
+ env->DeleteLocalRef(event);
+ env->DeleteLocalRef(array);
+ }
+
+ close(fd);
+}
/*
* JNI registration.
@@ -292,6 +368,10 @@ static JNINativeMethod gRegisterMethods[] = {
{ "readEvents",
"([ILjava/util/Collection;)V",
(void*) android_util_EventLog_readEvents
+ },
+ { "readEvents",
+ "(Ljava/lang/String;Ljava/util/Collection;)V",
+ (void*) android_util_EventLog_readEventsFile
}
};
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 98fe0e65..7748aba 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -18,9 +18,9 @@
#define LOG_TAG "Process"
#include <utils/Log.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -50,14 +50,6 @@ pid_t gettid() { return syscall(__NR_gettid);}
#undef __KERNEL__
#endif
-/*
- * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h
- * and Process.java
- * These names are used to construct the path to the cgroup control dir
- */
-
-static const char *cgroup_names[] = { NULL, "bg_non_interactive", "fg_boost" };
-
using namespace android;
static void signalExceptionForPriorityError(JNIEnv* env, jobject obj, int err)
@@ -194,14 +186,13 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name)
return -1;
}
-static int add_pid_to_cgroup(int pid, int grp)
+static int add_pid_to_cgroup(int pid, const char *grp_name)
{
int fd;
char path[255];
char text[64];
- sprintf(path, "/dev/cpuctl/%s/tasks",
- (cgroup_names[grp] ? cgroup_names[grp] : ""));
+ sprintf(path, "/dev/cpuctl/%s/tasks", grp_name);
if ((fd = open(path, O_WRONLY)) < 0)
return -1;
@@ -216,6 +207,37 @@ static int add_pid_to_cgroup(int pid, int grp)
return 0;
}
+void setSchedPolicy(JNIEnv* env, jobject clazz, int pid, SchedPolicy policy)
+{
+ static int __sys_supports_schedgroups = -1;
+
+ if (__sys_supports_schedgroups < 0) {
+ if (!access("/dev/cpuctl/tasks", F_OK)) {
+ __sys_supports_schedgroups = 1;
+ } else {
+ __sys_supports_schedgroups = 0;
+ }
+ }
+
+ if (__sys_supports_schedgroups) {
+ const char *grp = NULL;
+
+ if (policy == SP_BACKGROUND) {
+ grp = "bg_non_interactive";
+ }
+
+ if (add_pid_to_cgroup(pid, grp)) {
+ if (errno != ESRCH && errno != ENOENT)
+ signalExceptionForGroupError(env, clazz, errno);
+ }
+ } else {
+ struct sched_param param;
+
+ param.sched_priority = 0;
+ sched_setscheduler(pid, (policy == SP_BACKGROUND) ? 5 : 0, &param);
+ }
+}
+
void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
{
if (grp > ANDROID_TGROUP_MAX || grp < 0) {
@@ -223,11 +245,9 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint
return;
}
- if (add_pid_to_cgroup(pid, grp)) {
- // If the thread exited on us, don't generate an exception
- if (errno != ESRCH && errno != ENOENT)
- signalExceptionForGroupError(env, clazz, errno);
- }
+ setSchedPolicy(env, clazz, pid,
+ (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+ SP_BACKGROUND : SP_FOREGROUND);
}
void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
@@ -271,14 +291,9 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin
continue;
}
- if (add_pid_to_cgroup(t_pid, grp)) {
- // If the thread exited on us, ignore it and keep going
- if (errno != ESRCH && errno != ENOENT) {
- signalExceptionForGroupError(env, clazz, errno);
- closedir(d);
- return;
- }
- }
+ setSchedPolicy(env, clazz, t_pid,
+ (grp == ANDROID_TGROUP_BG_NONINTERACT) ?
+ SP_BACKGROUND : SP_FOREGROUND);
}
closedir(d);
}
@@ -287,9 +302,9 @@ void android_os_Process_setThreadPriority(JNIEnv* env, jobject clazz,
jint pid, jint pri)
{
if (pri >= ANDROID_PRIORITY_BACKGROUND) {
- add_pid_to_cgroup(pid, ANDROID_TGROUP_BG_NONINTERACT);
+ setSchedPolicy(env, clazz, pid, SP_BACKGROUND);
} else if (getpriority(PRIO_PROCESS, pid) >= ANDROID_PRIORITY_BACKGROUND) {
- add_pid_to_cgroup(pid, ANDROID_TGROUP_DEFAULT);
+ setSchedPolicy(env, clazz, pid, SP_FOREGROUND);
}
if (setpriority(PRIO_PROCESS, pid, pri) < 0) {
@@ -385,7 +400,7 @@ static int pid_compare(const void* v1, const void* v2)
return *((const jint*)v1) - *((const jint*)v2);
}
-jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
+static jlong android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
{
int fd = open("/proc/meminfo", O_RDONLY);
@@ -405,7 +420,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
buffer[len] = 0;
int numFound = 0;
- int mem = 0;
+ jlong mem = 0;
static const char* const sums[] = { "MemFree:", "Cached:", NULL };
static const int sumsLen[] = { strlen("MemFree:"), strlen("Cached:"), NULL };
@@ -424,7 +439,7 @@ jint android_os_Process_getFreeMemory(JNIEnv* env, jobject clazz)
p++;
if (*p == 0) p--;
}
- mem += atoi(num) * 1024;
+ mem += atoll(num) * 1024;
numFound++;
break;
}
@@ -874,7 +889,7 @@ static const JNINativeMethod methods[] = {
{"setGid", "(I)I", (void*)android_os_Process_setGid},
{"sendSignal", "(II)V", (void*)android_os_Process_sendSignal},
{"supportsProcesses", "()Z", (void*)android_os_Process_supportsProcesses},
- {"getFreeMemory", "()I", (void*)android_os_Process_getFreeMemory},
+ {"getFreeMemory", "()J", (void*)android_os_Process_getFreeMemory},
{"readProcLines", "(Ljava/lang/String;[Ljava/lang/String;[J)V", (void*)android_os_Process_readProcLines},
{"getPids", "(Ljava/lang/String;[I)[I", (void*)android_os_Process_getPids},
{"readProcFile", "(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z", (void*)android_os_Process_readProcFile},
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 076775f..40c8aa0 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -24,6 +24,7 @@
#include <SkCanvas.h>
#include <SkBitmap.h>
+#include <SkRegion.h>
#include "jni.h"
#include <android_runtime/AndroidRuntime.h>
@@ -45,6 +46,7 @@ struct sso_t {
static sso_t sso;
struct so_t {
+ jfieldID surfaceControl;
jfieldID surface;
jfieldID saveCount;
jfieldID canvas;
@@ -121,10 +123,50 @@ static void SurfaceSession_kill(JNIEnv* env, jobject clazz)
// ----------------------------------------------------------------------------
+static sp<SurfaceControl> getSurfaceControl(JNIEnv* env, jobject clazz)
+{
+ SurfaceControl* const p =
+ (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
+ return sp<SurfaceControl>(p);
+}
+
+static void setSurfaceControl(JNIEnv* env, jobject clazz,
+ const sp<SurfaceControl>& surface)
+{
+ SurfaceControl* const p =
+ (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
+ if (surface.get()) {
+ surface->incStrong(clazz);
+ }
+ if (p) {
+ p->decStrong(clazz);
+ }
+ env->SetIntField(clazz, so.surfaceControl, (int)surface.get());
+}
+
static sp<Surface> getSurface(JNIEnv* env, jobject clazz)
{
- Surface* const p = (Surface*)env->GetIntField(clazz, so.surface);
- return sp<Surface>(p);
+ sp<Surface> result((Surface*)env->GetIntField(clazz, so.surface));
+ if (result == 0) {
+ /*
+ * if this method is called from the WindowManager's process, it means
+ * the client is is not remote, and therefore is allowed to have
+ * a Surface (data), so we create it here.
+ * If we don't have a SurfaceControl, it means we're in a different
+ * process.
+ */
+
+ SurfaceControl* const control =
+ (SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);
+ if (control) {
+ result = control->getSurface();
+ if (result != 0) {
+ result->incStrong(clazz);
+ env->SetIntField(clazz, so.surface, (int)result.get());
+ }
+ }
+ }
+ return result;
}
static void setSurface(JNIEnv* env, jobject clazz, const sp<Surface>& surface)
@@ -153,12 +195,12 @@ static void Surface_init(
SurfaceComposerClient* client =
(SurfaceComposerClient*)env->GetIntField(session, sso.client);
- sp<Surface> surface(client->createSurface(pid, dpy, w, h, format, flags));
+ sp<SurfaceControl> surface(client->createSurface(pid, dpy, w, h, format, flags));
if (surface == 0) {
doThrow(env, OutOfResourcesException);
return;
}
- setSurface(env, clazz, surface);
+ setSurfaceControl(env, clazz, surface);
}
static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
@@ -168,28 +210,44 @@ static void Surface_initParcel(JNIEnv* env, jobject clazz, jobject argParcel)
doThrow(env, "java/lang/NullPointerException", NULL);
return;
}
- const sp<Surface>& rhs = Surface::readFromParcel(parcel);
+ sp<Surface> rhs = new Surface(*parcel);
setSurface(env, clazz, rhs);
}
-static void Surface_clear(JNIEnv* env, jobject clazz, uintptr_t *ostack)
+static void Surface_destroy(JNIEnv* env, jobject clazz, uintptr_t *ostack)
+{
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (SurfaceControl::isValid(surface)) {
+ surface->clear();
+ }
+ setSurfaceControl(env, clazz, 0);
+ setSurface(env, clazz, 0);
+}
+
+static void Surface_release(JNIEnv* env, jobject clazz, uintptr_t *ostack)
{
+ setSurfaceControl(env, clazz, 0);
setSurface(env, clazz, 0);
}
static jboolean Surface_isValid(JNIEnv* env, jobject clazz)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- return surface->isValid() ? JNI_TRUE : JNI_FALSE;
+ const sp<SurfaceControl>& surfaceControl(getSurfaceControl(env, clazz));
+ if (surfaceControl != 0) {
+ return SurfaceControl::isValid(surfaceControl) ? JNI_TRUE : JNI_FALSE;
+ }
+ const sp<Surface>& surface(getSurface(env, clazz));
+ return Surface::isValid(surface) ? JNI_TRUE : JNI_FALSE;
}
static inline SkBitmap::Config convertPixelFormat(PixelFormat format)
{
- /* note: if PIXEL_FORMAT_XRGB_8888 means that all alpha bytes are 0xFF, then
+ /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
we can map to SkBitmap::kARGB_8888_Config, and optionally call
bitmap.setIsOpaque(true) on the resulting SkBitmap (as an accelerator)
*/
switch (format) {
+ case PIXEL_FORMAT_RGBX_8888: return SkBitmap::kARGB_8888_Config;
case PIXEL_FORMAT_RGBA_8888: return SkBitmap::kARGB_8888_Config;
case PIXEL_FORMAT_RGBA_4444: return SkBitmap::kARGB_4444_Config;
case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config;
@@ -200,8 +258,8 @@ static inline SkBitmap::Config convertPixelFormat(PixelFormat format)
static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (!surface->isValid())
+ const sp<Surface>& surface(getSurface(env, clazz));
+ if (!Surface::isValid(surface))
return 0;
// get dirty region
@@ -212,7 +270,7 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
dirty.top = env->GetIntField(dirtyRect, ro.t);
dirty.right = env->GetIntField(dirtyRect, ro.r);
dirty.bottom= env->GetIntField(dirtyRect, ro.b);
- if (dirty.left < dirty.right && dirty.top < dirty.bottom) {
+ if (!dirty.isEmpty()) {
dirtyRegion.set(dirty);
}
} else {
@@ -235,7 +293,11 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
SkBitmap bitmap;
- bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, info.bpr);
+ ssize_t bpr = info.s * bytesPerPixel(info.format);
+ bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);
+ if (info.format == PIXEL_FORMAT_RGBX_8888) {
+ bitmap.setIsOpaque(true);
+ }
if (info.w > 0 && info.h > 0) {
bitmap.setPixels(info.bits);
} else {
@@ -243,13 +305,27 @@ static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)
bitmap.setPixels(NULL);
}
nativeCanvas->setBitmapDevice(bitmap);
- nativeCanvas->clipRegion(dirtyRegion.toSkRegion());
+
+ SkRegion clipReg;
+ if (dirtyRegion.isRect()) { // very common case
+ const Rect& b(dirtyRegion.getBounds());
+ clipReg.setRect(b.left, b.top, b.right, b.bottom);
+ } else {
+ size_t count;
+ Rect const* r = dirtyRegion.getArray(&count);
+ while (count) {
+ clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);
+ r++, count--;
+ }
+ }
+
+ nativeCanvas->clipRegion(clipReg);
int saveCount = nativeCanvas->save();
env->SetIntField(clazz, so.saveCount, saveCount);
if (dirtyRect) {
- Rect bounds(dirtyRegion.bounds());
+ const Rect& bounds(dirtyRegion.getBounds());
env->SetIntField(dirtyRect, ro.l, bounds.left);
env->SetIntField(dirtyRect, ro.t, bounds.top);
env->SetIntField(dirtyRect, ro.r, bounds.right);
@@ -268,8 +344,8 @@ static void Surface_unlockCanvasAndPost(
return;
}
- const sp<Surface>& surface = getSurface(env, clazz);
- if (!surface->isValid())
+ const sp<Surface>& surface(getSurface(env, clazz));
+ if (!Surface::isValid(surface))
return;
// detach the canvas from the surface
@@ -289,26 +365,8 @@ static void Surface_unlockCanvasAndPost(
static void Surface_unlockCanvas(
JNIEnv* env, jobject clazz, jobject argCanvas)
{
- jobject canvas = env->GetObjectField(clazz, so.canvas);
- if (canvas != argCanvas) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- const sp<Surface>& surface = getSurface(env, clazz);
- if (!surface->isValid())
- return;
-
- status_t err = surface->unlock();
- if (err < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
- SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);
- int saveCount = env->GetIntField(clazz, so.saveCount);
- nativeCanvas->restoreToCount(saveCount);
- nativeCanvas->setBitmapDevice(SkBitmap());
- env->SetIntField(clazz, so.saveCount, 0);
+ // XXX: this API has been removed
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_openTransaction(
@@ -353,138 +411,140 @@ static void Surface_unfreezeDisplay(
static void Surface_setLayer(
JNIEnv* env, jobject clazz, jint zorder)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setLayer(zorder) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setLayer(zorder);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setPosition(
JNIEnv* env, jobject clazz, jint x, jint y)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setPosition(x, y) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setPosition(x, y);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setSize(
JNIEnv* env, jobject clazz, jint w, jint h)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setSize(w, h) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setSize(w, h);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_hide(
JNIEnv* env, jobject clazz)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->hide() < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->hide();
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_show(
JNIEnv* env, jobject clazz)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->show() < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->show();
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_freeze(
JNIEnv* env, jobject clazz)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->freeze() < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->freeze();
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_unfreeze(
JNIEnv* env, jobject clazz)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->unfreeze() < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->unfreeze();
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setFlags(
JNIEnv* env, jobject clazz, jint flags, jint mask)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setFlags(flags, mask) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setFlags(flags, mask);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setTransparentRegion(
JNIEnv* env, jobject clazz, jobject argRegion)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region);
- if (surface->setTransparentRegionHint(Region(*nativeRegion)) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ SkRegion* nativeRegion = (SkRegion*)env->GetIntField(argRegion, no.native_region);
+
+ const SkIRect& b(nativeRegion->getBounds());
+ Region reg(Rect(b.fLeft, b.fTop, b.fRight, b.fBottom));
+ if (nativeRegion->isComplex()) {
+ SkRegion::Iterator it(*nativeRegion);
+ while (!it.done()) {
+ const SkIRect& r(it.rect());
+ reg.addRectUnchecked(r.fLeft, r.fTop, r.fRight, r.fBottom);
+ it.next();
}
}
+
+ status_t err = surface->setTransparentRegionHint(reg);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setAlpha(
JNIEnv* env, jobject clazz, jfloat alpha)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setAlpha(alpha) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setAlpha(alpha);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setMatrix(
JNIEnv* env, jobject clazz,
jfloat dsdx, jfloat dtdx, jfloat dsdy, jfloat dtdy)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setMatrix(dsdx, dtdx, dsdy, dtdy) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setMatrix(dsdx, dtdx, dsdy, dtdy);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
static void Surface_setFreezeTint(
JNIEnv* env, jobject clazz,
jint tint)
{
- const sp<Surface>& surface = getSurface(env, clazz);
- if (surface->isValid()) {
- if (surface->setFreezeTint(tint) < 0) {
- doThrow(env, "java/lang/IllegalArgumentException", NULL);
- }
- }
+ const sp<SurfaceControl>& surface(getSurfaceControl(env, clazz));
+ if (surface == 0) return;
+ status_t err = surface->setFreezeTint(tint);
+ if (err<0 && err!=NO_INIT)
+ doThrow(env, "java/lang/IllegalArgumentException", NULL);
}
+// ----------------------------------------------------------------------------
+
static void Surface_copyFrom(
JNIEnv* env, jobject clazz, jobject other)
{
@@ -496,16 +556,21 @@ static void Surface_copyFrom(
return;
}
- const sp<Surface>& surface = getSurface(env, clazz);
- const sp<Surface>& rhs = getSurface(env, other);
- if (!Surface::isSameSurface(surface, rhs)) {
+ /*
+ * This is used by the WindowManagerService just after constructing
+ * a Surface and is necessary for returning the Surface reference to
+ * the caller. At this point, we should only have a SurfaceControl.
+ */
+
+ const sp<SurfaceControl>& surface = getSurfaceControl(env, clazz);
+ const sp<SurfaceControl>& rhs = getSurfaceControl(env, other);
+ if (!SurfaceControl::isSameSurface(surface, rhs)) {
// we reassign the surface only if it's a different one
// otherwise we would loose our client-side state.
- setSurface(env, clazz, rhs->dup());
+ setSurfaceControl(env, clazz, rhs);
}
}
-
static void Surface_readFromParcel(
JNIEnv* env, jobject clazz, jobject argParcel)
{
@@ -515,9 +580,9 @@ static void Surface_readFromParcel(
return;
}
- const sp<Surface>& surface = getSurface(env, clazz);
- const sp<Surface>& rhs = Surface::readFromParcel(parcel);
- if (!Surface::isSameSurface(surface, rhs)) {
+ const sp<Surface>& control(getSurface(env, clazz));
+ sp<Surface> rhs = new Surface(*parcel);
+ if (!Surface::isSameSurface(control, rhs)) {
// we reassign the surface only if it's a different one
// otherwise we would loose our client-side state.
setSurface(env, clazz, rhs);
@@ -535,8 +600,8 @@ static void Surface_writeToParcel(
return;
}
- const sp<Surface>& surface = getSurface(env, clazz);
- Surface::writeToParcel(surface, parcel);
+ const sp<SurfaceControl>& control(getSurfaceControl(env, clazz));
+ SurfaceControl::writeSurfaceToParcel(control, parcel);
}
// ----------------------------------------------------------------------------
@@ -557,7 +622,8 @@ static JNINativeMethod gSurfaceMethods[] = {
{"nativeClassInit", "()V", (void*)nativeClassInit },
{"init", "(Landroid/view/SurfaceSession;IIIIII)V", (void*)Surface_init },
{"init", "(Landroid/os/Parcel;)V", (void*)Surface_initParcel },
- {"clear", "()V", (void*)Surface_clear },
+ {"destroy", "()V", (void*)Surface_destroy },
+ {"release", "()V", (void*)Surface_release },
{"copyFrom", "(Landroid/view/Surface;)V", (void*)Surface_copyFrom },
{"isValid", "()Z", (void*)Surface_isValid },
{"lockCanvasNative", "(Landroid/graphics/Rect;)Landroid/graphics/Canvas;", (void*)Surface_lockCanvas },
@@ -586,7 +652,8 @@ static JNINativeMethod gSurfaceMethods[] = {
void nativeClassInit(JNIEnv* env, jclass clazz)
{
- so.surface = env->GetFieldID(clazz, "mSurface", "I");
+ so.surface = env->GetFieldID(clazz, "mSurface", "I");
+ so.surfaceControl = env->GetFieldID(clazz, "mSurfaceControl", "I");
so.saveCount = env->GetFieldID(clazz, "mSaveCount", "I");
so.canvas = env->GetFieldID(clazz, "mCanvas", "Landroid/graphics/Canvas;");
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 4452065..974fc0b 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -21,7 +21,6 @@
#include <EGL/egl.h>
#include <GLES/gl.h>
-#include <ui/EGLNativeWindowSurface.h>
#include <ui/Surface.h>
#include <SkBitmap.h>
#include <SkPixelRef.h>
@@ -338,7 +337,7 @@ not_valid_surface:
goto not_valid_surface;
jint* base = beginNativeAttribList(_env, attrib_list);
- EGLSurface sur = eglCreateWindowSurface(dpy, cnf, new EGLNativeWindowSurface(window), base);
+ EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window, base);
endNativeAttributeList(_env, attrib_list, base);
return (jint)sur;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cf2184c..53e0125 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -25,7 +25,7 @@
<!-- Special broadcasts that only the system can send -->
<!-- ================================================ -->
<eat-comment />
-
+
<protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
<protected-broadcast android:name="android.intent.action.SCREEN_ON" />
<protected-broadcast android:name="android.intent.action.USER_PRESENT" />
@@ -51,12 +51,12 @@
<protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
<protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
<protected-broadcast android:name="android.intent.action.REBOOT" />
-
+
<!-- ====================================== -->
<!-- Permissions for things that cost money -->
<!-- ====================================== -->
<eat-comment />
-
+
<!-- Used for permissions that can be used to make the user spend money
without their direct involvement. For example, this is the group
for permissions that allow you to directly place phone calls,
@@ -85,7 +85,7 @@
<!-- Permissions for accessing messages -->
<!-- ================================== -->
<eat-comment />
-
+
<!-- Used for permissions that allow an application to send messages
on behalf of the user or intercept messages being received by the
user. This is primarily intended for SMS/MMS messaging, such as
@@ -135,7 +135,7 @@
<!-- Permissions for accessing personal info (contacts and calendar) -->
<!-- =============================================================== -->
<eat-comment />
-
+
<!-- Used for permissions that provide access to the user's private data,
such as contacts, calendar events, e-mail messages, etc. This includes
both reading and writing of this data (which should generally be
@@ -189,8 +189,8 @@
android:description="@string/permdesc_writeCalendar" />
<!-- Allows an application to read the user dictionary. This should
- really only be required by an IME, or a dictionary editor like
- the Settings app.
+ really only be required by an IME, or a dictionary editor like
+ the Settings app.
@hide Pending API council approval -->
<permission android:name="android.permission.READ_USER_DICTIONARY"
android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -226,7 +226,7 @@
<!-- Permissions for accessing location info -->
<!-- ======================================= -->
<eat-comment />
-
+
<!-- Used for permissions that allow access to the user's current
location. -->
<permission-group android:name="android.permission-group.LOCATION"
@@ -271,7 +271,7 @@
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
<eat-comment />
-
+
<!-- Used for permissions that provide access to networking services. The
main permission here is internet access, but this is also an
appropriate group for accessing or modifying any network configuration
@@ -308,11 +308,19 @@
android:description="@string/permdesc_bluetooth"
android:label="@string/permlab_bluetooth" />
+ <!-- Allows applications to call into AccountAuthenticators. Only
+ the system can get this permission. -->
+ <permission android:name="android.permission.ACCOUNT_MANAGER_SERVICE"
+ android:permissionGroup="android.permission-group.ACCOUNTS"
+ android:protectionLevel="signature"
+ android:description="@string/permdesc_accountManagerService"
+ android:label="@string/permlab_accountManagerService" />
+
<!-- ================================== -->
<!-- Permissions for accessing accounts -->
<!-- ================================== -->
<eat-comment />
-
+
<!-- Permissions for direct access to Google accounts.
Note that while right now this is only used for Google accounts,
we expect in the future to have a more general account management
@@ -329,11 +337,33 @@
android:description="@string/permdesc_getAccounts"
android:label="@string/permlab_getAccounts" />
+ <!-- Allows an application to act as an AccountAuthenticator for
+ the AccountManager -->
+ <permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
+ android:permissionGroup="android.permission-group.ACCOUNTS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_authenticateAccounts"
+ android:description="@string/permdesc_authenticateAccounts" />
+
+ <!-- Allows an application to request authtokens from the AccountManager -->
+ <permission android:name="android.permission.USE_CREDENTIALS"
+ android:permissionGroup="android.permission-group.ACCOUNTS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_useCredentials"
+ android:description="@string/permdesc_useCredentials" />
+
+ <!-- Allows an application to manage the list of accounts in the AccountManager -->
+ <permission android:name="android.permission.MANAGE_ACCOUNTS"
+ android:permissionGroup="android.permission-group.ACCOUNTS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_manageAccounts"
+ android:description="@string/permdesc_manageAccounts" />
+
<!-- ================================== -->
<!-- Permissions for accessing hardware -->
<!-- ================================== -->
<eat-comment />
-
+
<!-- Used for permissions that provide direct access to the hardware on
the device. This includes audio, the camera, vibrator, etc. -->
<permission-group android:name="android.permission-group.HARDWARE_CONTROLS"
@@ -386,7 +416,7 @@
<!-- Permissions associated with telephony state -->
<!-- =========================================== -->
<eat-comment />
-
+
<!-- Used for permissions that are associated with accessing and modifyign
telephony state: intercepting outgoing calls, reading
and modifying the phone state. Note that
@@ -440,7 +470,7 @@
<!-- Permissions for low-level system interaction -->
<!-- ============================================ -->
<eat-comment />
-
+
<!-- Group of permissions that are related to system APIs. Many
of these are not permissions the user will be expected to understand,
and such permissions should generally be marked as "normal" protection
@@ -664,7 +694,7 @@
android:description="@string/permdesc_writeApnSettings"
android:label="@string/permlab_writeApnSettings" />
- <!-- Allows an application to allow access the subscribed feeds
+ <!-- Allows an application to allow access the subscribed feeds
ContentProvider. -->
<permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -676,7 +706,7 @@
android:label="@string/permlab_subscribedFeedsWrite"
android:description="@string/permdesc_subscribedFeedsWrite"
android:protectionLevel="dangerous" />
-
+
<!-- Allows applications to change network connectivity state -->
<permission android:name="android.permission.CHANGE_NETWORK_STATE"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -727,7 +757,7 @@
<!-- Permissions for special development tools -->
<!-- ========================================= -->
<eat-comment />
-
+
<!-- Group of permissions that are related to development features. These
are not permissions that should appear in normal applications; they
protect APIs that are intended only to be used for development
@@ -863,6 +893,13 @@
android:description="@string/permdesc_bindInputMethod"
android:protectionLevel="signature" />
+ <!-- Must be required by wallpaper services, to ensure that only the
+ system can bind to them. -->
+ <permission android:name="android.permission.BIND_WALLPAPER"
+ android:label="@string/permlab_bindWallpaper"
+ android:description="@string/permdesc_bindWallpaper"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- Allows low-level access to setting the orientation (actually
rotation) of the screen. Not for use by normal applications. -->
<permission android:name="android.permission.SET_ORIENTATION"
@@ -974,6 +1011,12 @@
android:description="@string/permdesc_callPrivileged"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows an application to perform CDMA OTA provisioning @hide -->
+ <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
+ android:label="@string/permlab_performCdmaProvisioning"
+ android:description="@string/permdesc_performCdmaProvisioning"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- Allows enabling/disabling location update notifications from
the radio. Not for use by normal applications. -->
<permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
@@ -1008,6 +1051,13 @@
android:description="@string/permdesc_backup"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows an application to participate in the backup and restore process
+ @hide -->
+ <permission android:name="android.permission.BACKUP_DATA"
+ android:label="@string/permlab_backup_data"
+ android:description="@string/permdesc_backup_data"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- Allows an application to tell the AppWidget service which application
can access AppWidget's data. The normal user flow is that a user
picks an AppWidget to go into a particular host, thereby giving that
@@ -1051,12 +1101,19 @@
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signature" />
+ <!-- Allows applications to set a live wallpaper.
+ @hide -->
+ <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
+ android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+ android:protectionLevel="signature" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
android:label="@string/android_system_label"
android:allowClearUserData="false"
- android:backupAgent="com.android.internal.backup.SystemBackupAgent"
+ android:backupAgent="com.android.server.SystemBackupAgent"
+ android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_android">
<activity android:name="com.android.internal.app.ChooserActivity"
android:theme="@style/Theme.Dialog.Alert"
@@ -1089,8 +1146,35 @@
android:excludeFromRecents="true">
</activity>
+ <activity android:name="android.accounts.ChooseAccountActivity"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ </activity>
+
+ <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
+ android:excludeFromRecents="true"
+ android:exported="true">
+ </activity>
+
+ <activity android:name="com.android.server.ShutdownActivity"
+ android:permission="android.permission.SHUTDOWN"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <activity android:name="com.android.internal.app.NetInitiatedActivity"
+ android:theme="@style/Theme.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
+
<service android:name="com.android.server.LoadAverageService"
- android:exported="true" />
+ android:exported="true" />
+
+ <service android:name="com.android.internal.service.wallpaper.ImageWallpaper"
+ android:permission="android.permission.BIND_WALLPAPER">
+ </service>
<receiver android:name="com.android.server.BootReceiver" >
<intent-filter>
@@ -1101,7 +1185,7 @@
<receiver android:name="com.android.server.MasterClearReceiver"
android:permission="android.permission.MASTER_CLEAR" >
<intent-filter>
- <action android:name="android.intent.action.GTALK_DATA_MESSAGE_RECEIVED" />
+ <action android:name="android.intent.action.REMOTE_INTENT" />
<category android:name="android.intent.category.MASTER_CLEAR" />
</intent-filter>
</receiver>
diff --git a/core/res/res/anim/accelerate_decelerate_interpolator.xml b/core/res/res/anim/accelerate_decelerate_interpolator.xml
index 724ed0a..4a0216b 100644
--- a/core/res/res/anim/accelerate_decelerate_interpolator.xml
+++ b/core/res/res/anim/accelerate_decelerate_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<accelerateDecelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"/>
+<accelerateDecelerateInterpolator />
diff --git a/core/res/res/anim/accelerate_interpolator.xml b/core/res/res/anim/accelerate_interpolator.xml
index c689c35..13f87f3 100644
--- a/core/res/res/anim/accelerate_interpolator.xml
+++ b/core/res/res/anim/accelerate_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android" factor="1" />
+<accelerateInterpolator />
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
new file mode 100644
index 0000000..9d1ef53
--- /dev/null
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <translate android:fromXDelta="-100%" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
new file mode 100644
index 0000000..47cb6d6
--- /dev/null
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <translate android:fromXDelta="0%" android:toXDelta="33%"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
new file mode 100644
index 0000000..e4c7e9b
--- /dev/null
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <translate android:fromXDelta="33%" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
new file mode 100644
index 0000000..9d47b7f
--- /dev/null
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <translate android:fromXDelta="0%" android:toXDelta="-100%"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/anticipate_interpolator.xml b/core/res/res/anim/anticipate_interpolator.xml
index 50a555a..7a16b5f 100644
--- a/core/res/res/anim/anticipate_interpolator.xml
+++ b/core/res/res/anim/anticipate_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<anticipateInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
+<anticipateInterpolator />
diff --git a/core/res/res/anim/anticipate_overshoot_interpolator.xml b/core/res/res/anim/anticipate_overshoot_interpolator.xml
index 440a899..d61ddd1 100644
--- a/core/res/res/anim/anticipate_overshoot_interpolator.xml
+++ b/core/res/res/anim/anticipate_overshoot_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<anticipateOvershootInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
+<anticipateOvershootInterpolator />
diff --git a/core/res/res/anim/bounce_interpolator.xml b/core/res/res/anim/bounce_interpolator.xml
index 406fbb9..d89ba49 100644
--- a/core/res/res/anim/bounce_interpolator.xml
+++ b/core/res/res/anim/bounce_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<bounceInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
+<bounceInterpolator />
diff --git a/core/res/res/anim/decelerate_interpolator.xml b/core/res/res/anim/decelerate_interpolator.xml
index fff6616..7b29fb3 100644
--- a/core/res/res/anim/decelerate_interpolator.xml
+++ b/core/res/res/anim/decelerate_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android" factor="1" />
+<decelerateInterpolator />
diff --git a/core/res/res/anim/linear_interpolator.xml b/core/res/res/anim/linear_interpolator.xml
index 70bbecc..f4d256a 100644
--- a/core/res/res/anim/linear_interpolator.xml
+++ b/core/res/res/anim/linear_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android"/>
+<linearInterpolator />
diff --git a/core/res/res/anim/overshoot_interpolator.xml b/core/res/res/anim/overshoot_interpolator.xml
index c614e0b..725ea48 100644
--- a/core/res/res/anim/overshoot_interpolator.xml
+++ b/core/res/res/anim/overshoot_interpolator.xml
@@ -18,4 +18,4 @@
*/
-->
-<overshootInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
+<overshootInterpolator />
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index c289869..303cfd6 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/options_panel_exit.xml
-**
-** Copyright 2007, The Android Open Source Project
+/*
+** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -19,8 +18,9 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator"
- android:zAdjustment="top">
- <translate android:fromXDelta="-100%" android:toXDelta="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:interpolator="@anim/decelerate_interpolator">
+ <scale android:fromXScale="2.0" android:toXScale="1.0"
+ android:fromYScale="2.0" android:toYScale="1.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 96fff94..a28ac3b 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/options_panel_exit.xml
-**
-** Copyright 2007, The Android Open Source Project
+/*
+** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -19,7 +18,12 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <translate android:fromXDelta="0%" android:toXDelta="33%"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale="1.0" android:toXScale=".5"
+ android:fromYScale="1.0" android:toYScale=".5"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
</set>
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index 8e7d049..234abb2 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/options_panel_exit.xml
-**
-** Copyright 2007, The Android Open Source Project
+/*
+** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -19,7 +18,12 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:interpolator="@anim/decelerate_interpolator">
- <translate android:fromXDelta="33%" android:toXDelta="0"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale=".5" android:toXScale="1.0"
+ android:fromYScale=".5" android:toYScale="1.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
</set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 5e5c66d..98975fb 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* //device/apps/common/res/anim/options_panel_exit.xml
-**
-** Copyright 2007, The Android Open Source Project
+/*
+** Copyright 2009, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -21,6 +20,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@anim/decelerate_interpolator"
android:zAdjustment="top">
- <translate android:fromXDelta="0%" android:toXDelta="-100%"
- android:duration="@android:integer/config_mediumAnimTime"/>
+ <scale android:fromXScale="1.0" android:toXScale="2.0"
+ android:fromYScale="1.0" android:toYScale="2.0"
+ android:pivotX="50%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
</set>
diff --git a/core/res/res/anim/translucent_enter.xml b/core/res/res/anim/translucent_enter.xml
new file mode 100644
index 0000000..fb4c1c3
--- /dev/null
+++ b/core/res/res/anim/translucent_enter.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <translate android:fromXDelta="75%" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/translucent_exit.xml b/core/res/res/anim/translucent_exit.xml
new file mode 100644
index 0000000..1d424e1
--- /dev/null
+++ b/core/res/res/anim/translucent_exit.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator">
+ <translate android:fromXDelta="0%" android:toXDelta="75%"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_close_enter.xml b/core/res/res/anim/wallpaper_close_enter.xml
new file mode 100644
index 0000000..e4c7e9b
--- /dev/null
+++ b/core/res/res/anim/wallpaper_close_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <translate android:fromXDelta="33%" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_close_exit.xml b/core/res/res/anim/wallpaper_close_exit.xml
new file mode 100644
index 0000000..16edec1
--- /dev/null
+++ b/core/res/res/anim/wallpaper_close_exit.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale="1.0" android:toXScale="2.0"
+ android:fromYScale="1.0" android:toYScale="2.0"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="0%" android:toXDelta="-100%"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_enter.xml b/core/res/res/anim/wallpaper_enter.xml
new file mode 100644
index 0000000..c240a9a
--- /dev/null
+++ b/core/res/res/anim/wallpaper_enter.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*\
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <scale android:fromXScale="3.0" android:toXScale="1.0"
+ android:fromYScale="3.0" android:toYScale="1.0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:duration="@android:integer/config_longAnimTime" />
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_longAnimTime" />
+</set>
diff --git a/core/res/res/anim/wallpaper_exit.xml b/core/res/res/anim/wallpaper_exit.xml
new file mode 100644
index 0000000..742286f
--- /dev/null
+++ b/core/res/res/anim/wallpaper_exit.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator">
+ <scale android:fromXScale="1.0" android:toXScale="3.0"
+ android:fromYScale="1.0" android:toYScale="3.0"
+ android:pivotX="50%" android:pivotY="50%"
+ android:duration="@android:integer/config_longAnimTime" />
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_longAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
new file mode 100644
index 0000000..5c4f5c9
--- /dev/null
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale="2.0" android:toXScale="1.0"
+ android:fromYScale="2.0" android:toYScale="1.0"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="-150%p" android:toXDelta="0%p"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
new file mode 100644
index 0000000..acaf773
--- /dev/null
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator">
+ <scale android:fromXScale="1.0" android:toXScale=".5"
+ android:fromYScale="1.0" android:toYScale=".5"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="0%p" android:toXDelta="100%p"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
new file mode 100644
index 0000000..81c9991
--- /dev/null
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <scale android:fromXScale=".5" android:toXScale="1.0"
+ android:fromYScale=".5" android:toYScale="1.0"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="100%p" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set>
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
new file mode 100644
index 0000000..28c4287
--- /dev/null
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/accelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale="1.0" android:toXScale="2.0"
+ android:fromYScale="1.0" android:toYScale="2.0"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="0" android:toXDelta="-150%p"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_open_enter.xml b/core/res/res/anim/wallpaper_open_enter.xml
new file mode 100644
index 0000000..af22b47
--- /dev/null
+++ b/core/res/res/anim/wallpaper_open_enter.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale android:fromXScale="2.0" android:toXScale="1.0"
+ android:fromYScale="2.0" android:toYScale="1.0"
+ android:pivotX="100%p" android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <translate android:fromXDelta="-100%" android:toXDelta="0"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/anim/wallpaper_open_exit.xml b/core/res/res/anim/wallpaper_open_exit.xml
new file mode 100644
index 0000000..47cb6d6
--- /dev/null
+++ b/core/res/res/anim/wallpaper_open_exit.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@anim/decelerate_interpolator">
+ <translate android:fromXDelta="0%" android:toXDelta="33%"
+ android:duration="@android:integer/config_mediumAnimTime"/>
+</set>
diff --git a/core/res/res/color/tab_indicator_text.xml b/core/res/res/color/tab_indicator_text.xml
index ce321db..5f5c2a4 100644
--- a/core/res/res/color/tab_indicator_text.xml
+++ b/core/res/res/color/tab_indicator_text.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_selected="true" android:color="#323232"/>
- <item android:color="#FFF"/> <!-- not selected -->
+ <item android:state_selected="true" android:color="#ffffff"/>
+ <item android:color="#808080"/> <!-- not selected -->
</selector>
diff --git a/core/res/res/drawable-hdpi/activity_title_bar.9.png b/core/res/res/drawable-hdpi/activity_title_bar.9.png
new file mode 100644
index 0000000..48d60c4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/activity_title_bar.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/arrow_down_float.png b/core/res/res/drawable-hdpi/arrow_down_float.png
new file mode 100644
index 0000000..2466c8f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/arrow_down_float.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/arrow_up_float.png b/core/res/res/drawable-hdpi/arrow_up_float.png
new file mode 100644
index 0000000..d1301c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/arrow_up_float.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_background.png b/core/res/res/drawable-hdpi/battery_charge_background.png
new file mode 100644
index 0000000..19023a9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/battery_charge_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png
new file mode 100644
index 0000000..c4e70a8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/battery_charge_fill_empty.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png
new file mode 100644
index 0000000..ac66f5a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/battery_charge_fill_full.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png b/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png
new file mode 100644
index 0000000..32d99c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/battery_charge_fill_warning.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/battery_low_battery.png b/core/res/res/drawable-hdpi/battery_low_battery.png
new file mode 100644
index 0000000..d894f7b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/battery_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/blank_tile.png b/core/res/res/drawable-hdpi/blank_tile.png
new file mode 100644
index 0000000..e2a386c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/blank_tile.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/bottom_bar.png b/core/res/res/drawable-hdpi/bottom_bar.png
new file mode 100644
index 0000000..1f38f3c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/bottom_bar.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_buttonless_off.png b/core/res/res/drawable-hdpi/btn_check_buttonless_off.png
new file mode 100644
index 0000000..baf9010
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_buttonless_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_buttonless_on.png b/core/res/res/drawable-hdpi/btn_check_buttonless_on.png
new file mode 100644
index 0000000..2a77e4c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_buttonless_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_label_background.9.png b/core/res/res/drawable-hdpi/btn_check_label_background.9.png
new file mode 100644
index 0000000..97e6806
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off.png b/core/res/res/drawable-hdpi/btn_check_off.png
new file mode 100644
index 0000000..aad9ef7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable.png b/core/res/res/drawable-hdpi/btn_check_off_disable.png
new file mode 100644
index 0000000..eaee9e0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
new file mode 100644
index 0000000..6d2c293
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_pressed.png b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
new file mode 100644
index 0000000..1c442e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_off_selected.png b/core/res/res/drawable-hdpi/btn_check_off_selected.png
new file mode 100644
index 0000000..b852b2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on.png b/core/res/res/drawable-hdpi/btn_check_on.png
new file mode 100644
index 0000000..cd5c181
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable.png b/core/res/res/drawable-hdpi/btn_check_on_disable.png
new file mode 100644
index 0000000..b4fc51a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
new file mode 100644
index 0000000..bf34647
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_pressed.png b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
new file mode 100644
index 0000000..fa5c7a2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_on_selected.png b/core/res/res/drawable-hdpi/btn_check_on_selected.png
new file mode 100644
index 0000000..a6a21ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable.png b/core/res/res/drawable-hdpi/btn_circle_disable.png
new file mode 100644
index 0000000..d829716
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_disable_focused.png b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
new file mode 100644
index 0000000..c1b5b6e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_normal.png b/core/res/res/drawable-hdpi/btn_circle_normal.png
new file mode 100644
index 0000000..bf3fb5a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_pressed.png b/core/res/res/drawable-hdpi/btn_circle_pressed.png
new file mode 100644
index 0000000..50e22e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_circle_selected.png b/core/res/res/drawable-hdpi/btn_circle_selected.png
new file mode 100644
index 0000000..cfc68fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_normal.png b/core/res/res/drawable-hdpi/btn_close_normal.png
new file mode 100644
index 0000000..38b49f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_close_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_pressed.png b/core/res/res/drawable-hdpi/btn_close_pressed.png
new file mode 100644
index 0000000..aa9ea49
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_close_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_close_selected.png b/core/res/res/drawable-hdpi/btn_close_selected.png
new file mode 100644
index 0000000..870c670
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_close_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_default.png b/core/res/res/drawable-hdpi/btn_code_lock_default.png
new file mode 100644
index 0000000..df3137f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_code_lock_touched.png b/core/res/res/drawable-hdpi/btn_code_lock_touched.png
new file mode 100644
index 0000000..bf9e46a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal.9.png b/core/res/res/drawable-hdpi/btn_default_normal.9.png
new file mode 100644
index 0000000..329ce6e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png b/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png
new file mode 100644
index 0000000..a518c6b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png b/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png
new file mode 100644
index 0000000..71a05b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_pressed.9.png b/core/res/res/drawable-hdpi/btn_default_pressed.9.png
new file mode 100644
index 0000000..d9d02bf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_selected.9.png b/core/res/res/drawable-hdpi/btn_default_selected.9.png
new file mode 100644
index 0000000..ab7c612
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..baafed6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..175197b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..ec1feff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png b/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..c1f9a0f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_default_small_selected.9.png b/core/res/res/drawable-hdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..0ea3f40
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_disable.png b/core/res/res/drawable-hdpi/btn_dialog_disable.png
new file mode 100644
index 0000000..2fc5d1a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dialog_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_normal.png b/core/res/res/drawable-hdpi/btn_dialog_normal.png
new file mode 100644
index 0000000..c4a1026
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dialog_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_pressed.png b/core/res/res/drawable-hdpi/btn_dialog_pressed.png
new file mode 100644
index 0000000..846f8bf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dialog_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dialog_selected.png b/core/res/res/drawable-hdpi/btn_dialog_selected.png
new file mode 100644
index 0000000..659c289
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dialog_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
new file mode 100644
index 0000000..9392495
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
new file mode 100644
index 0000000..beaba45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
new file mode 100644
index 0000000..ec51fc9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_erase_default.9.png b/core/res/res/drawable-hdpi/btn_erase_default.9.png
new file mode 100644
index 0000000..30984f4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_erase_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_erase_pressed.9.png b/core/res/res/drawable-hdpi/btn_erase_pressed.9.png
new file mode 100644
index 0000000..a8225e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_erase_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_erase_selected.9.png b/core/res/res/drawable-hdpi/btn_erase_selected.9.png
new file mode 100644
index 0000000..f020f77
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_erase_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_global_search_normal.9.png b/core/res/res/drawable-hdpi/btn_global_search_normal.9.png
new file mode 100644
index 0000000..5bec4f8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_global_search_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png
new file mode 100644
index 0000000..5697369
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
new file mode 100644
index 0000000..9940245
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
new file mode 100644
index 0000000..5a26d83
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
new file mode 100644
index 0000000..089dbf3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100644
index 0000000..c10a3db
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100644
index 0000000..9e83ace
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal.9.png
new file mode 100644
index 0000000..9c7e483
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed.9.png
new file mode 100644
index 0000000..e01a49d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_keyboard_key_trans_selected.9.png b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_selected.9.png
new file mode 100644
index 0000000..544655e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_keyboard_key_trans_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player.9.png b/core/res/res/drawable-hdpi/btn_media_player.9.png
new file mode 100644
index 0000000..bf16315
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_media_player.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png b/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png
new file mode 100644
index 0000000..d7b8ed5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_media_player_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png b/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png
new file mode 100644
index 0000000..1a35c31
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_media_player_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png b/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png
new file mode 100644
index 0000000..17dd3fc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_media_player_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_media_player_selected.9.png b/core/res/res/drawable-hdpi/btn_media_player_selected.9.png
new file mode 100644
index 0000000..a146d8f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_media_player_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_minus_default.png b/core/res/res/drawable-hdpi/btn_minus_default.png
new file mode 100644
index 0000000..f2831af
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_minus_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_minus_disable.png b/core/res/res/drawable-hdpi/btn_minus_disable.png
new file mode 100644
index 0000000..24ce695
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_minus_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_minus_disable_focused.png b/core/res/res/drawable-hdpi/btn_minus_disable_focused.png
new file mode 100644
index 0000000..e92c2b1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_minus_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_minus_pressed.png b/core/res/res/drawable-hdpi/btn_minus_pressed.png
new file mode 100644
index 0000000..ba2ed26
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_minus_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_minus_selected.png b/core/res/res/drawable-hdpi/btn_minus_selected.png
new file mode 100644
index 0000000..6b938b3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_minus_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_plus_default.png b/core/res/res/drawable-hdpi/btn_plus_default.png
new file mode 100644
index 0000000..441d1fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_plus_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_plus_disable.png b/core/res/res/drawable-hdpi/btn_plus_disable.png
new file mode 100644
index 0000000..4e965c1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_plus_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_plus_disable_focused.png b/core/res/res/drawable-hdpi/btn_plus_disable_focused.png
new file mode 100644
index 0000000..0c938eb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_plus_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_plus_pressed.png b/core/res/res/drawable-hdpi/btn_plus_pressed.png
new file mode 100644
index 0000000..8dd5a68
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_plus_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_plus_selected.png b/core/res/res/drawable-hdpi/btn_plus_selected.png
new file mode 100644
index 0000000..8fe30ed
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_plus_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_label_background.9.png b/core/res/res/drawable-hdpi/btn_radio_label_background.9.png
new file mode 100644
index 0000000..45c5c6a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off.png b/core/res/res/drawable-hdpi/btn_radio_off.png
new file mode 100644
index 0000000..c0b14aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_pressed.png b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
new file mode 100644
index 0000000..3189581
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_off_selected.png b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
new file mode 100644
index 0000000..f337703
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on.png b/core/res/res/drawable-hdpi/btn_radio_on.png
new file mode 100644
index 0000000..c90d2eb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_pressed.png b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
new file mode 100644
index 0000000..d79450b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_on_selected.png b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
new file mode 100644
index 0000000..db50c43
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_off_normal.png b/core/res/res/drawable-hdpi/btn_rating_star_off_normal.png
new file mode 100644
index 0000000..d119807
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_off_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_off_pressed.png b/core/res/res/drawable-hdpi/btn_rating_star_off_pressed.png
new file mode 100644
index 0000000..6f76da3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_off_selected.png b/core/res/res/drawable-hdpi/btn_rating_star_off_selected.png
new file mode 100644
index 0000000..566090d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_on_normal.png b/core/res/res/drawable-hdpi/btn_rating_star_on_normal.png
new file mode 100644
index 0000000..c55c1f6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_on_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_on_pressed.png b/core/res/res/drawable-hdpi/btn_rating_star_on_pressed.png
new file mode 100644
index 0000000..89b8161
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_rating_star_on_selected.png b/core/res/res/drawable-hdpi/btn_rating_star_on_selected.png
new file mode 100644
index 0000000..1a76a26
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_rating_star_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
new file mode 100644
index 0000000..fe38843
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
new file mode 100644
index 0000000..e673b27
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
new file mode 100644
index 0000000..64b689d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
new file mode 100644
index 0000000..f65fa14
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
new file mode 100644
index 0000000..17cc0dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
new file mode 100644
index 0000000..0509e14
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png b/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png
new file mode 100644
index 0000000..71a037e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_disabled_focused.png b/core/res/res/drawable-hdpi/btn_square_overlay_disabled_focused.png
new file mode 100644
index 0000000..a474605
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_disabled_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_normal.png b/core/res/res/drawable-hdpi/btn_square_overlay_normal.png
new file mode 100644
index 0000000..bf5da22
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png b/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png
new file mode 100644
index 0000000..52a302d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_square_overlay_selected.png b/core/res/res/drawable-hdpi/btn_square_overlay_selected.png
new file mode 100644
index 0000000..e065682
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_square_overlay_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_off.png b/core/res/res/drawable-hdpi/btn_star_big_off.png
new file mode 100644
index 0000000..4be0f5d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_off_disable.png b/core/res/res/drawable-hdpi/btn_star_big_off_disable.png
new file mode 100644
index 0000000..faba665
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_off_disable_focused.png b/core/res/res/drawable-hdpi/btn_star_big_off_disable_focused.png
new file mode 100644
index 0000000..fc8aca4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_off_pressed.png b/core/res/res/drawable-hdpi/btn_star_big_off_pressed.png
new file mode 100644
index 0000000..b8c8e70
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_off_selected.png b/core/res/res/drawable-hdpi/btn_star_big_off_selected.png
new file mode 100644
index 0000000..86250bb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_on.png b/core/res/res/drawable-hdpi/btn_star_big_on.png
new file mode 100644
index 0000000..4213050
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_on_disable.png b/core/res/res/drawable-hdpi/btn_star_big_on_disable.png
new file mode 100644
index 0000000..5629849
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_on_disable_focused.png b/core/res/res/drawable-hdpi/btn_star_big_on_disable_focused.png
new file mode 100644
index 0000000..cb9f79c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_on_pressed.png b/core/res/res/drawable-hdpi/btn_star_big_on_pressed.png
new file mode 100644
index 0000000..648cd1b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_big_on_selected.png b/core/res/res/drawable-hdpi/btn_star_big_on_selected.png
new file mode 100644
index 0000000..cb15673
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_big_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_star_label_background.9.png b/core/res/res/drawable-hdpi/btn_star_label_background.9.png
new file mode 100644
index 0000000..6008067
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_star_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_off.9.png b/core/res/res/drawable-hdpi/btn_toggle_off.9.png
new file mode 100644
index 0000000..9e141d8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on.9.png b/core/res/res/drawable-hdpi/btn_toggle_on.9.png
new file mode 100644
index 0000000..dba2fa5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_toggle_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_down_disabled.9.png b/core/res/res/drawable-hdpi/btn_zoom_down_disabled.9.png
new file mode 100644
index 0000000..6441f4d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_down_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_down_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_zoom_down_disabled_focused.9.png
new file mode 100644
index 0000000..cfb0613
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_down_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_down_normal.9.png b/core/res/res/drawable-hdpi/btn_zoom_down_normal.9.png
new file mode 100644
index 0000000..b30f834
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_down_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_down_pressed.9.png b/core/res/res/drawable-hdpi/btn_zoom_down_pressed.9.png
new file mode 100644
index 0000000..4ff9910
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_down_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_down_selected.9.png b/core/res/res/drawable-hdpi/btn_zoom_down_selected.9.png
new file mode 100644
index 0000000..5542695
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_down_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_page_normal.png b/core/res/res/drawable-hdpi/btn_zoom_page_normal.png
new file mode 100644
index 0000000..15d60a0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_page_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_page_press.png b/core/res/res/drawable-hdpi/btn_zoom_page_press.png
new file mode 100644
index 0000000..28f437e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_page_press.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_up_disabled.9.png b/core/res/res/drawable-hdpi/btn_zoom_up_disabled.9.png
new file mode 100644
index 0000000..9813201
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_up_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_up_disabled_focused.9.png b/core/res/res/drawable-hdpi/btn_zoom_up_disabled_focused.9.png
new file mode 100644
index 0000000..8710c72
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_up_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_up_normal.9.png b/core/res/res/drawable-hdpi/btn_zoom_up_normal.9.png
new file mode 100644
index 0000000..763d271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_up_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_up_pressed.9.png b/core/res/res/drawable-hdpi/btn_zoom_up_pressed.9.png
new file mode 100644
index 0000000..b77936a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_up_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_zoom_up_selected.9.png b/core/res/res/drawable-hdpi/btn_zoom_up_selected.9.png
new file mode 100644
index 0000000..a8e4af1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_zoom_up_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/button_onoff_indicator_off.png b/core/res/res/drawable-hdpi/button_onoff_indicator_off.png
new file mode 100644
index 0000000..9af36e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/button_onoff_indicator_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/button_onoff_indicator_on.png b/core/res/res/drawable-hdpi/button_onoff_indicator_on.png
new file mode 100644
index 0000000..bde297e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/button_onoff_indicator_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/call_contact.png b/core/res/res/drawable-hdpi/call_contact.png
new file mode 100644
index 0000000..57fea24
--- /dev/null
+++ b/core/res/res/drawable-hdpi/call_contact.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_off_background.png b/core/res/res/drawable-hdpi/checkbox_off_background.png
new file mode 100644
index 0000000..a8e4785
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_off_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/checkbox_on_background.png b/core/res/res/drawable-hdpi/checkbox_on_background.png
new file mode 100644
index 0000000..800d3d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/checkbox_on_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/clock_dial.png b/core/res/res/drawable-hdpi/clock_dial.png
new file mode 100644
index 0000000..9de29bc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/clock_dial.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/clock_hand_hour.png b/core/res/res/drawable-hdpi/clock_hand_hour.png
new file mode 100644
index 0000000..9f7e5c0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/clock_hand_hour.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/clock_hand_minute.png b/core/res/res/drawable-hdpi/clock_hand_minute.png
new file mode 100644
index 0000000..2eec380
--- /dev/null
+++ b/core/res/res/drawable-hdpi/clock_hand_minute.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/code_lock_bottom.9.png b/core/res/res/drawable-hdpi/code_lock_bottom.9.png
new file mode 100644
index 0000000..e72d0f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/code_lock_bottom.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/code_lock_left.9.png b/core/res/res/drawable-hdpi/code_lock_left.9.png
new file mode 100644
index 0000000..76ff1f5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/code_lock_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/code_lock_top.9.png b/core/res/res/drawable-hdpi/code_lock_top.9.png
new file mode 100644
index 0000000..20af255
--- /dev/null
+++ b/core/res/res/drawable-hdpi/code_lock_top.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/compass_arrow.png b/core/res/res/drawable-hdpi/compass_arrow.png
new file mode 100644
index 0000000..6dbd900
--- /dev/null
+++ b/core/res/res/drawable-hdpi/compass_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/compass_base.png b/core/res/res/drawable-hdpi/compass_base.png
new file mode 100644
index 0000000..298fc09
--- /dev/null
+++ b/core/res/res/drawable-hdpi/compass_base.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/create_contact.png b/core/res/res/drawable-hdpi/create_contact.png
new file mode 100644
index 0000000..19d59b4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/create_contact.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dark_header.9.png b/core/res/res/drawable-hdpi/dark_header.9.png
new file mode 100644
index 0000000..a2fa569
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dark_header.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_divider_horizontal_light.9.png b/core/res/res/drawable-hdpi/dialog_divider_horizontal_light.9.png
new file mode 100644
index 0000000..441ef32
--- /dev/null
+++ b/core/res/res/drawable-hdpi/dialog_divider_horizontal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
new file mode 100644
index 0000000..c7803a2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
new file mode 100644
index 0000000..d8d8aa9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
new file mode 100644
index 0000000..63859f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
new file mode 100644
index 0000000..ced2832
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
new file mode 100644
index 0000000..63859f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_horizontal_textfield.9.png b/core/res/res/drawable-hdpi/divider_horizontal_textfield.9.png
new file mode 100644
index 0000000..23b0b51
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_horizontal_textfield.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/divider_vertical_bright.9.png b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
new file mode 100644
index 0000000..1035656
--- /dev/null
+++ b/core/res/res/drawable-hdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/editbox_background_focus_yellow.9.png b/core/res/res/drawable-hdpi/editbox_background_focus_yellow.9.png
new file mode 100644
index 0000000..70cd52b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/editbox_background_focus_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/editbox_background_normal.9.png b/core/res/res/drawable-hdpi/editbox_background_normal.9.png
new file mode 100644
index 0000000..ce12b3b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/editbox_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/editbox_dropdown_background.9.png b/core/res/res/drawable-hdpi/editbox_dropdown_background.9.png
new file mode 100644
index 0000000..e7967d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/editbox_dropdown_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/editbox_dropdown_background_dark.9.png b/core/res/res/drawable-hdpi/editbox_dropdown_background_dark.9.png
new file mode 100644
index 0000000..4cce373
--- /dev/null
+++ b/core/res/res/drawable-hdpi/editbox_dropdown_background_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_angel.png b/core/res/res/drawable-hdpi/emo_im_angel.png
new file mode 100644
index 0000000..10742a6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_angel.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_cool.png b/core/res/res/drawable-hdpi/emo_im_cool.png
new file mode 100644
index 0000000..e3c8654
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_cool.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_crying.png b/core/res/res/drawable-hdpi/emo_im_crying.png
new file mode 100644
index 0000000..b23791c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_crying.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_foot_in_mouth.png b/core/res/res/drawable-hdpi/emo_im_foot_in_mouth.png
new file mode 100644
index 0000000..050b7be
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_foot_in_mouth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_happy.png b/core/res/res/drawable-hdpi/emo_im_happy.png
new file mode 100644
index 0000000..69e3bed
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_happy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_kissing.png b/core/res/res/drawable-hdpi/emo_im_kissing.png
new file mode 100644
index 0000000..0cca68e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_kissing.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_laughing.png b/core/res/res/drawable-hdpi/emo_im_laughing.png
new file mode 100644
index 0000000..8406ad0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_laughing.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_lips_are_sealed.png b/core/res/res/drawable-hdpi/emo_im_lips_are_sealed.png
new file mode 100644
index 0000000..222f175
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_lips_are_sealed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_money_mouth.png b/core/res/res/drawable-hdpi/emo_im_money_mouth.png
new file mode 100644
index 0000000..d711bfb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_money_mouth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_sad.png b/core/res/res/drawable-hdpi/emo_im_sad.png
new file mode 100644
index 0000000..40017f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_sad.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_surprised.png b/core/res/res/drawable-hdpi/emo_im_surprised.png
new file mode 100644
index 0000000..4b2af7a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_surprised.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_tongue_sticking_out.png b/core/res/res/drawable-hdpi/emo_im_tongue_sticking_out.png
new file mode 100644
index 0000000..42ac80d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_tongue_sticking_out.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_undecided.png b/core/res/res/drawable-hdpi/emo_im_undecided.png
new file mode 100644
index 0000000..2cf5bd2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_undecided.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_winking.png b/core/res/res/drawable-hdpi/emo_im_winking.png
new file mode 100644
index 0000000..a3a0876
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_winking.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_wtf.png b/core/res/res/drawable-hdpi/emo_im_wtf.png
new file mode 100644
index 0000000..86c4bda
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_wtf.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/emo_im_yelling.png b/core/res/res/drawable-hdpi/emo_im_yelling.png
new file mode 100644
index 0000000..cfd991a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/emo_im_yelling.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_maximized.9.png b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
new file mode 100644
index 0000000..4dcb984
--- /dev/null
+++ b/core/res/res/drawable-hdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/expander_ic_minimized.9.png b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
new file mode 100644
index 0000000..45b7322
--- /dev/null
+++ b/core/res/res/drawable-hdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/focused_application_background_static.png b/core/res/res/drawable-hdpi/focused_application_background_static.png
new file mode 100644
index 0000000..6872f26
--- /dev/null
+++ b/core/res/res/drawable-hdpi/focused_application_background_static.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/frame_gallery_thumb.9.png b/core/res/res/drawable-hdpi/frame_gallery_thumb.9.png
new file mode 100644
index 0000000..8edbd3f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/frame_gallery_thumb.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/frame_gallery_thumb_pressed.9.png b/core/res/res/drawable-hdpi/frame_gallery_thumb_pressed.9.png
new file mode 100644
index 0000000..986e6d5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/frame_gallery_thumb_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/frame_gallery_thumb_selected.9.png b/core/res/res/drawable-hdpi/frame_gallery_thumb_selected.9.png
new file mode 100644
index 0000000..95f3ab5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/frame_gallery_thumb_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/gallery_selected_default.9.png b/core/res/res/drawable-hdpi/gallery_selected_default.9.png
new file mode 100644
index 0000000..99403aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/gallery_selected_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/gallery_selected_focused.9.png b/core/res/res/drawable-hdpi/gallery_selected_focused.9.png
new file mode 100644
index 0000000..3aa2e17
--- /dev/null
+++ b/core/res/res/drawable-hdpi/gallery_selected_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/gallery_selected_pressed.9.png b/core/res/res/drawable-hdpi/gallery_selected_pressed.9.png
new file mode 100644
index 0000000..8f1e752
--- /dev/null
+++ b/core/res/res/drawable-hdpi/gallery_selected_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/gallery_unselected_default.9.png b/core/res/res/drawable-hdpi/gallery_unselected_default.9.png
new file mode 100644
index 0000000..855dca1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/gallery_unselected_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/gallery_unselected_pressed.9.png b/core/res/res/drawable-hdpi/gallery_unselected_pressed.9.png
new file mode 100644
index 0000000..5ec400c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/gallery_unselected_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/grid_selector_background_focus.9.png b/core/res/res/drawable-hdpi/grid_selector_background_focus.9.png
new file mode 100644
index 0000000..be2aeed
--- /dev/null
+++ b/core/res/res/drawable-hdpi/grid_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/grid_selector_background_pressed.9.png b/core/res/res/drawable-hdpi/grid_selector_background_pressed.9.png
new file mode 100644
index 0000000..27e455a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/grid_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/highlight_disabled.9.png b/core/res/res/drawable-hdpi/highlight_disabled.9.png
new file mode 100644
index 0000000..46f755d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/highlight_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/highlight_pressed.9.png b/core/res/res/drawable-hdpi/highlight_pressed.9.png
new file mode 100644
index 0000000..91d7db1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/highlight_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/highlight_selected.9.png b/core/res/res/drawable-hdpi/highlight_selected.9.png
new file mode 100644
index 0000000..6e92dd5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/highlight_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png b/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png
new file mode 100644
index 0000000..0125944
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png b/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png
new file mode 100644
index 0000000..33d7f89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_search.png b/core/res/res/drawable-hdpi/ic_btn_search.png
new file mode 100644
index 0000000..23eebf5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_search.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_speak_now.png b/core/res/res/drawable-hdpi/ic_btn_speak_now.png
new file mode 100644
index 0000000..6dc01fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_speak_now.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_disabled.png b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_disabled.png
new file mode 100644
index 0000000..19fe4d4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_normal.png b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_normal.png
new file mode 100644
index 0000000..c5a0594
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_fit_page_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_disabled.png b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_disabled.png
new file mode 100644
index 0000000..e5b22e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_disabled.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_normal.png b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_normal.png
new file mode 100644
index 0000000..0612d01
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_btn_square_browser_zoom_page_overview_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_bullet_key_permission.png b/core/res/res/drawable-hdpi/ic_bullet_key_permission.png
new file mode 100644
index 0000000..98d95dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_bullet_key_permission.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_contact_picture.png b/core/res/res/drawable-hdpi/ic_contact_picture.png
new file mode 100644
index 0000000..a60565a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_contact_picture.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_delete.png b/core/res/res/drawable-hdpi/ic_delete.png
new file mode 100644
index 0000000..f3e53d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_alert.png b/core/res/res/drawable-hdpi/ic_dialog_alert.png
new file mode 100644
index 0000000..7f905ba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_alert.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_dialer.png b/core/res/res/drawable-hdpi/ic_dialog_dialer.png
new file mode 100644
index 0000000..2ded243
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_dialer.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_email.png b/core/res/res/drawable-hdpi/ic_dialog_email.png
new file mode 100644
index 0000000..faea271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_email.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_info.png b/core/res/res/drawable-hdpi/ic_dialog_info.png
new file mode 100644
index 0000000..efee1ef
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_info.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_map.png b/core/res/res/drawable-hdpi/ic_dialog_map.png
new file mode 100644
index 0000000..f102b08
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_map.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png b/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png
new file mode 100644
index 0000000..ef8a877
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_menu_generic.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_time.png b/core/res/res/drawable-hdpi/ic_dialog_time.png
new file mode 100644
index 0000000..337a43a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_time.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_dialog_usb.png b/core/res/res/drawable-hdpi/ic_dialog_usb.png
new file mode 100644
index 0000000..e69e14a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_dialog_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_emergency.png b/core/res/res/drawable-hdpi/ic_emergency.png
new file mode 100644
index 0000000..a2dd372
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_emergency.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_input_add.png b/core/res/res/drawable-hdpi/ic_input_add.png
new file mode 100644
index 0000000..d26ebac
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_input_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_input_delete.png b/core/res/res/drawable-hdpi/ic_input_delete.png
new file mode 100644
index 0000000..f35f89f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_input_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_input_get.png b/core/res/res/drawable-hdpi/ic_input_get.png
new file mode 100644
index 0000000..e2b665a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_input_get.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_launcher_android.png b/core/res/res/drawable-hdpi/ic_launcher_android.png
new file mode 100644
index 0000000..d70b8ca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_airplane_mode.png b/core/res/res/drawable-hdpi/ic_lock_airplane_mode.png
new file mode 100644
index 0000000..610f9d0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_airplane_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off.png b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off.png
new file mode 100644
index 0000000..cd50647
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-hdpi/ic_lock_idle_alarm.png
new file mode 100644
index 0000000..3f3af06
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_charging.png b/core/res/res/drawable-hdpi/ic_lock_idle_charging.png
new file mode 100644
index 0000000..42572ee
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_lock.png b/core/res/res/drawable-hdpi/ic_lock_idle_lock.png
new file mode 100644
index 0000000..11163d8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-hdpi/ic_lock_idle_low_battery.png
new file mode 100644
index 0000000..30ff905
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_lock.png b/core/res/res/drawable-hdpi/ic_lock_lock.png
new file mode 100644
index 0000000..0fc79e1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_lock.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_power_off.png b/core/res/res/drawable-hdpi/ic_lock_power_off.png
new file mode 100644
index 0000000..2f120c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_power_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_silent_mode.png b/core/res/res/drawable-hdpi/ic_lock_silent_mode.png
new file mode 100644
index 0000000..00e1960
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_silent_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png b/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png
new file mode 100644
index 0000000..6b4ce89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_silent_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_maps_indicator_current_position.png b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position.png
new file mode 100644
index 0000000..bc9160d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim1.png b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim1.png
new file mode 100644
index 0000000..d3d9339
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim2.png b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim2.png
new file mode 100644
index 0000000..e32c223
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim3.png b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim3.png
new file mode 100644
index 0000000..cf2db7c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_maps_indicator_current_position_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_ff.png b/core/res/res/drawable-hdpi/ic_media_ff.png
new file mode 100644
index 0000000..b0dc05b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_next.png b/core/res/res/drawable-hdpi/ic_media_next.png
new file mode 100644
index 0000000..2552f4e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_pause.png b/core/res/res/drawable-hdpi/ic_media_pause.png
new file mode 100644
index 0000000..d4670c2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_play.png b/core/res/res/drawable-hdpi/ic_media_play.png
new file mode 100644
index 0000000..e67ec80
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_previous.png b/core/res/res/drawable-hdpi/ic_media_previous.png
new file mode 100644
index 0000000..05eba71
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_media_rew.png b/core/res/res/drawable-hdpi/ic_media_rew.png
new file mode 100644
index 0000000..88eed2e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_media_rew.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_account_list.png b/core/res/res/drawable-hdpi/ic_menu_account_list.png
new file mode 100644
index 0000000..f858d2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_account_list.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_add.png b/core/res/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..65cc01e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_agenda.png b/core/res/res/drawable-hdpi/ic_menu_agenda.png
new file mode 100644
index 0000000..6bb5cc8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_agenda.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_allfriends.png b/core/res/res/drawable-hdpi/ic_menu_allfriends.png
new file mode 100644
index 0000000..8d11ca1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_allfriends.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png b/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png
new file mode 100644
index 0000000..7ae1760
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_always_landscape_portrait.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_archive.png b/core/res/res/drawable-hdpi/ic_menu_archive.png
new file mode 100644
index 0000000..9ca5c62
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_archive.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_attachment.png b/core/res/res/drawable-hdpi/ic_menu_attachment.png
new file mode 100644
index 0000000..8f11153
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_attachment.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_back.png b/core/res/res/drawable-hdpi/ic_menu_back.png
new file mode 100644
index 0000000..a6cd712
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_back.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_block.png b/core/res/res/drawable-hdpi/ic_menu_block.png
new file mode 100644
index 0000000..e1f9c2c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_block.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_blocked_user.png b/core/res/res/drawable-hdpi/ic_menu_blocked_user.png
new file mode 100644
index 0000000..3dd9a4a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_blocked_user.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_call.png b/core/res/res/drawable-hdpi/ic_menu_call.png
new file mode 100644
index 0000000..2ccc087
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_camera.png b/core/res/res/drawable-hdpi/ic_menu_camera.png
new file mode 100644
index 0000000..5a3850f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_camera.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_cc.png b/core/res/res/drawable-hdpi/ic_menu_cc.png
new file mode 100644
index 0000000..47905a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png b/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png
new file mode 100644
index 0000000..dde6741
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_chat_dashboard.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png b/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png
new file mode 100644
index 0000000..e6be48b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_clear_playlist.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png b/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png
new file mode 100644
index 0000000..a54ea9d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_close_clear_cancel.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_compass.png b/core/res/res/drawable-hdpi/ic_menu_compass.png
new file mode 100644
index 0000000..104270f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_compass.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_compose.png b/core/res/res/drawable-hdpi/ic_menu_compose.png
new file mode 100644
index 0000000..6ad379e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_compose.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_crop.png b/core/res/res/drawable-hdpi/ic_menu_crop.png
new file mode 100644
index 0000000..0d4c9fe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_crop.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_day.png b/core/res/res/drawable-hdpi/ic_menu_day.png
new file mode 100644
index 0000000..84429aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_day.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_delete.png b/core/res/res/drawable-hdpi/ic_menu_delete.png
new file mode 100644
index 0000000..2aed26a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_delete.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_directions.png b/core/res/res/drawable-hdpi/ic_menu_directions.png
new file mode 100644
index 0000000..23f6eb3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_directions.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_edit.png b/core/res/res/drawable-hdpi/ic_menu_edit.png
new file mode 100644
index 0000000..602dd10
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_edit.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_emoticons.png b/core/res/res/drawable-hdpi/ic_menu_emoticons.png
new file mode 100644
index 0000000..2fab515
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_emoticons.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_end_conversation.png b/core/res/res/drawable-hdpi/ic_menu_end_conversation.png
new file mode 100644
index 0000000..c05a207
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_end_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_forward.png b/core/res/res/drawable-hdpi/ic_menu_forward.png
new file mode 100644
index 0000000..606e6aa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_forward.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_friendslist.png b/core/res/res/drawable-hdpi/ic_menu_friendslist.png
new file mode 100644
index 0000000..a90e573
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_friendslist.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_gallery.png b/core/res/res/drawable-hdpi/ic_menu_gallery.png
new file mode 100644
index 0000000..76dfbc3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_gallery.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_goto.png b/core/res/res/drawable-hdpi/ic_menu_goto.png
new file mode 100644
index 0000000..a1acecb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_goto.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_help.png b/core/res/res/drawable-hdpi/ic_menu_help.png
new file mode 100644
index 0000000..4300e86
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_help.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_home.png b/core/res/res/drawable-hdpi/ic_menu_home.png
new file mode 100644
index 0000000..35cb52a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_home.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_info_details.png b/core/res/res/drawable-hdpi/ic_menu_info_details.png
new file mode 100644
index 0000000..7696ceb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_info_details.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_invite.png b/core/res/res/drawable-hdpi/ic_menu_invite.png
new file mode 100644
index 0000000..3cb129f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_invite.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_login.png b/core/res/res/drawable-hdpi/ic_menu_login.png
new file mode 100644
index 0000000..d23ebf0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_login.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_manage.png b/core/res/res/drawable-hdpi/ic_menu_manage.png
new file mode 100644
index 0000000..c7c4cbc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_manage.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mapmode.png b/core/res/res/drawable-hdpi/ic_menu_mapmode.png
new file mode 100644
index 0000000..c895ccb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_mapmode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mark.png b/core/res/res/drawable-hdpi/ic_menu_mark.png
new file mode 100644
index 0000000..724d787
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_mark.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_month.png b/core/res/res/drawable-hdpi/ic_menu_month.png
new file mode 100644
index 0000000..3e55ae6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_month.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_more.png b/core/res/res/drawable-hdpi/ic_menu_more.png
new file mode 100644
index 0000000..ed4376f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_more.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_my_calendar.png b/core/res/res/drawable-hdpi/ic_menu_my_calendar.png
new file mode 100644
index 0000000..3d6ea1f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_my_calendar.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_mylocation.png b/core/res/res/drawable-hdpi/ic_menu_mylocation.png
new file mode 100644
index 0000000..1bcb0cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_mylocation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_myplaces.png b/core/res/res/drawable-hdpi/ic_menu_myplaces.png
new file mode 100644
index 0000000..5f726d8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_myplaces.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_notifications.png b/core/res/res/drawable-hdpi/ic_menu_notifications.png
new file mode 100644
index 0000000..fb63937
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_notifications.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_play_clip.png b/core/res/res/drawable-hdpi/ic_menu_play_clip.png
new file mode 100644
index 0000000..ddde03a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_play_clip.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_preferences.png b/core/res/res/drawable-hdpi/ic_menu_preferences.png
new file mode 100644
index 0000000..64c42d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_preferences.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_recent_history.png b/core/res/res/drawable-hdpi/ic_menu_recent_history.png
new file mode 100644
index 0000000..0dd1627
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_recent_history.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_refresh.png b/core/res/res/drawable-hdpi/ic_menu_refresh.png
new file mode 100644
index 0000000..53cacca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_refresh.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_report_image.png b/core/res/res/drawable-hdpi/ic_menu_report_image.png
new file mode 100644
index 0000000..b6aa5d6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_report_image.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_revert.png b/core/res/res/drawable-hdpi/ic_menu_revert.png
new file mode 100644
index 0000000..11860a4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_revert.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_rotate.png b/core/res/res/drawable-hdpi/ic_menu_rotate.png
new file mode 100644
index 0000000..85115af
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_rotate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_save.png b/core/res/res/drawable-hdpi/ic_menu_save.png
new file mode 100644
index 0000000..fc26c5d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_save.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_search.png b/core/res/res/drawable-hdpi/ic_menu_search.png
new file mode 100644
index 0000000..f78234e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_send.png b/core/res/res/drawable-hdpi/ic_menu_send.png
new file mode 100644
index 0000000..2567b58
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_send.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_set_as.png b/core/res/res/drawable-hdpi/ic_menu_set_as.png
new file mode 100644
index 0000000..7e79c15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_set_as.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_share.png b/core/res/res/drawable-hdpi/ic_menu_share.png
new file mode 100644
index 0000000..b41b348
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_share.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_slideshow.png b/core/res/res/drawable-hdpi/ic_menu_slideshow.png
new file mode 100644
index 0000000..53b1a6f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_slideshow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png b/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png
new file mode 100644
index 0000000..5d68998
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_sort_alphabetically.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png b/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png
new file mode 100644
index 0000000..c9388fd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_sort_by_size.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_star.png b/core/res/res/drawable-hdpi/ic_menu_star.png
new file mode 100644
index 0000000..21a2c4b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_star.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_start_conversation.png b/core/res/res/drawable-hdpi/ic_menu_start_conversation.png
new file mode 100644
index 0000000..d63d3a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_start_conversation.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_stop.png b/core/res/res/drawable-hdpi/ic_menu_stop.png
new file mode 100644
index 0000000..7c99ed4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_stop.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_today.png b/core/res/res/drawable-hdpi/ic_menu_today.png
new file mode 100644
index 0000000..4a9352d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_today.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_upload.png b/core/res/res/drawable-hdpi/ic_menu_upload.png
new file mode 100644
index 0000000..d845112
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_upload.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png b/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png
new file mode 100644
index 0000000..df5fa7f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_upload_you_tube.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_view.png b/core/res/res/drawable-hdpi/ic_menu_view.png
new file mode 100644
index 0000000..75155d4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_view.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_week.png b/core/res/res/drawable-hdpi/ic_menu_week.png
new file mode 100644
index 0000000..c216eca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_week.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_menu_zoom.png b/core/res/res/drawable-hdpi/ic_menu_zoom.png
new file mode 100644
index 0000000..9fa4d7e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_menu_zoom.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_clear_all.png b/core/res/res/drawable-hdpi/ic_notification_clear_all.png
new file mode 100644
index 0000000..6bff858
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notification_clear_all.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_notification_overlay.9.png b/core/res/res/drawable-hdpi/ic_notification_overlay.9.png
new file mode 100644
index 0000000..744178f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_notification_overlay.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_partial_secure.png b/core/res/res/drawable-hdpi/ic_partial_secure.png
new file mode 100644
index 0000000..70bd08d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_partial_secure.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_disk_full.png b/core/res/res/drawable-hdpi/ic_popup_disk_full.png
new file mode 100644
index 0000000..b3c00a7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_reminder.png b/core/res/res/drawable-hdpi/ic_popup_reminder.png
new file mode 100644
index 0000000..9652dde
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_reminder.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_1.png b/core/res/res/drawable-hdpi/ic_popup_sync_1.png
new file mode 100644
index 0000000..a248f59
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_2.png b/core/res/res/drawable-hdpi/ic_popup_sync_2.png
new file mode 100644
index 0000000..756bfc6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_3.png b/core/res/res/drawable-hdpi/ic_popup_sync_3.png
new file mode 100644
index 0000000..e06825b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_4.png b/core/res/res/drawable-hdpi/ic_popup_sync_4.png
new file mode 100644
index 0000000..87ce8f6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_5.png b/core/res/res/drawable-hdpi/ic_popup_sync_5.png
new file mode 100644
index 0000000..635480c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_5.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_popup_sync_6.png b/core/res/res/drawable-hdpi/ic_popup_sync_6.png
new file mode 100644
index 0000000..b339eb2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_popup_sync_6.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_search_category_default.png b/core/res/res/drawable-hdpi/ic_search_category_default.png
new file mode 100644
index 0000000..4038129
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_search_category_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_secure.png b/core/res/res/drawable-hdpi/ic_secure.png
new file mode 100644
index 0000000..5fb62c2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_secure.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_text_dot.png b/core/res/res/drawable-hdpi/ic_text_dot.png
new file mode 100644
index 0000000..a7eaec5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_text_dot.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_vibrate.png b/core/res/res/drawable-hdpi/ic_vibrate.png
new file mode 100644
index 0000000..3d1e9a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume.png b/core/res/res/drawable-hdpi/ic_volume.png
new file mode 100644
index 0000000..ec555f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_bluetooth_ad2p.png b/core/res/res/drawable-hdpi/ic_volume_bluetooth_ad2p.png
new file mode 100644
index 0000000..31851e1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume_bluetooth_ad2p.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_bluetooth_in_call.png b/core/res/res/drawable-hdpi/ic_volume_bluetooth_in_call.png
new file mode 100644
index 0000000..108a9e1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume_bluetooth_in_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_off.png b/core/res/res/drawable-hdpi/ic_volume_off.png
new file mode 100644
index 0000000..3ace6bf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_off_small.png b/core/res/res/drawable-hdpi/ic_volume_off_small.png
new file mode 100644
index 0000000..4b36677
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume_off_small.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_volume_small.png b/core/res/res/drawable-hdpi/ic_volume_small.png
new file mode 100644
index 0000000..538e304
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_volume_small.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/icon_highlight_rectangle.9.png b/core/res/res/drawable-hdpi/icon_highlight_rectangle.9.png
new file mode 100644
index 0000000..a4da974
--- /dev/null
+++ b/core/res/res/drawable-hdpi/icon_highlight_rectangle.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/icon_highlight_square.9.png b/core/res/res/drawable-hdpi/icon_highlight_square.9.png
new file mode 100644
index 0000000..6f9a442
--- /dev/null
+++ b/core/res/res/drawable-hdpi/icon_highlight_square.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ime_qwerty.png b/core/res/res/drawable-hdpi/ime_qwerty.png
new file mode 100644
index 0000000..f9967cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ime_qwerty.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
new file mode 100644
index 0000000..6560696
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
new file mode 100644
index 0000000..698c3ec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
new file mode 100644
index 0000000..0102a61
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
new file mode 100644
index 0000000..82ad8f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
new file mode 100644
index 0000000..f9d0d33
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/indicator_input_error.png b/core/res/res/drawable-hdpi/indicator_input_error.png
new file mode 100644
index 0000000..213976c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/indicator_input_error.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_accessory_bg_landscape.9.png b/core/res/res/drawable-hdpi/keyboard_accessory_bg_landscape.9.png
new file mode 100644
index 0000000..5b0f6c5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_accessory_bg_landscape.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_background.9.png b/core/res/res/drawable-hdpi/keyboard_background.9.png
new file mode 100644
index 0000000..7a03b8e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png b/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png
new file mode 100644
index 0000000..dc3e1f9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png b/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
new file mode 100644
index 0000000..c67ed53
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/core/res/res/drawable-hdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 0000000..8e2461b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png b/core/res/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 0000000..fd7366e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
new file mode 100644
index 0000000..61db22c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/light_header.9.png b/core/res/res/drawable-hdpi/light_header.9.png
new file mode 100644
index 0000000..27db59d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/light_header.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
new file mode 100644
index 0000000..c40233e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_focus.9.png b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
new file mode 100644
index 0000000..d8e16b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
new file mode 100644
index 0000000..1676ca7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
new file mode 100644
index 0000000..ba79cf7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/loading_tile.png b/core/res/res/drawable-hdpi/loading_tile.png
new file mode 100644
index 0000000..691ca45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/loading_tile.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/maps_google_logo.png b/core/res/res/drawable-hdpi/maps_google_logo.png
new file mode 100644
index 0000000..5956ee2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/maps_google_logo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background.9.png b/core/res/res/drawable-hdpi/menu_background.9.png
new file mode 100644
index 0000000..cbe62af
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
new file mode 100644
index 0000000..0812487
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_background_fill_parent_width.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_separator.9.png b/core/res/res/drawable-hdpi/menu_separator.9.png
new file mode 100644
index 0000000..3685b4e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_separator.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menu_submenu_background.9.png b/core/res/res/drawable-hdpi/menu_submenu_background.9.png
new file mode 100644
index 0000000..cbd4400
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menu_submenu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menuitem_background_focus.9.png b/core/res/res/drawable-hdpi/menuitem_background_focus.9.png
new file mode 100644
index 0000000..d8e16b9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menuitem_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menuitem_background_pressed.9.png b/core/res/res/drawable-hdpi/menuitem_background_pressed.9.png
new file mode 100644
index 0000000..ba79cf7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menuitem_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/menuitem_background_solid_focused.9.png b/core/res/res/drawable-hdpi/menuitem_background_solid_focused.9.png
index 99dd9b1..99dd9b1 100644
--- a/core/res/res/drawable/menuitem_background_solid_focused.9.png
+++ b/core/res/res/drawable-hdpi/menuitem_background_solid_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/menuitem_background_solid_pressed.9.png b/core/res/res/drawable-hdpi/menuitem_background_solid_pressed.9.png
index 389063a..389063a 100644
--- a/core/res/res/drawable/menuitem_background_solid_pressed.9.png
+++ b/core/res/res/drawable-hdpi/menuitem_background_solid_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/menuitem_checkbox_on.png b/core/res/res/drawable-hdpi/menuitem_checkbox_on.png
new file mode 100644
index 0000000..e90f631
--- /dev/null
+++ b/core/res/res/drawable-hdpi/menuitem_checkbox_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/no_tile_128.png b/core/res/res/drawable-hdpi/no_tile_128.png
new file mode 100644
index 0000000..86b998d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/no_tile_128.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/panel_background.9.png b/core/res/res/drawable-hdpi/panel_background.9.png
new file mode 100644
index 0000000..bfe5713
--- /dev/null
+++ b/core/res/res/drawable-hdpi/panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/panel_picture_frame_bg_focus_blue.9.png b/core/res/res/drawable-hdpi/panel_picture_frame_bg_focus_blue.9.png
new file mode 100644
index 0000000..fdafdf5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/panel_picture_frame_bg_focus_blue.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/panel_picture_frame_bg_normal.9.png b/core/res/res/drawable-hdpi/panel_picture_frame_bg_normal.9.png
new file mode 100644
index 0000000..a654277
--- /dev/null
+++ b/core/res/res/drawable-hdpi/panel_picture_frame_bg_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/panel_picture_frame_bg_pressed_blue.9.png b/core/res/res/drawable-hdpi/panel_picture_frame_bg_pressed_blue.9.png
new file mode 100644
index 0000000..73162a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/panel_picture_frame_bg_pressed_blue.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pickerbox_background.png b/core/res/res/drawable-hdpi/pickerbox_background.png
new file mode 100644
index 0000000..9315a31
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pickerbox_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pickerbox_selected.9.png b/core/res/res/drawable-hdpi/pickerbox_selected.9.png
new file mode 100644
index 0000000..a88ec63
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pickerbox_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pickerbox_unselected.9.png b/core/res/res/drawable-hdpi/pickerbox_unselected.9.png
new file mode 100644
index 0000000..9f6b7cb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pickerbox_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/picture_emergency.png b/core/res/res/drawable-hdpi/picture_emergency.png
new file mode 100644
index 0000000..b0f10f9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/picture_emergency.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/picture_frame.9.png b/core/res/res/drawable-hdpi/picture_frame.9.png
new file mode 100644
index 0000000..c038b2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/picture_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_bright.9.png b/core/res/res/drawable-hdpi/popup_bottom_bright.9.png
new file mode 100644
index 0000000..cca47d3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_dark.9.png b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
new file mode 100644
index 0000000..62a0bd0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_bottom_medium.9.png b/core/res/res/drawable-hdpi/popup_bottom_medium.9.png
new file mode 100644
index 0000000..6ebb4f7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_bright.9.png b/core/res/res/drawable-hdpi/popup_center_bright.9.png
new file mode 100644
index 0000000..756e9ed
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_dark.9.png b/core/res/res/drawable-hdpi/popup_center_dark.9.png
new file mode 100644
index 0000000..77b4524
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_center_medium.9.png b/core/res/res/drawable-hdpi/popup_center_medium.9.png
new file mode 100644
index 0000000..de4be2a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_full_bright.9.png b/core/res/res/drawable-hdpi/popup_full_bright.9.png
new file mode 100644
index 0000000..6c30bec
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_full_dark.9.png b/core/res/res/drawable-hdpi/popup_full_dark.9.png
new file mode 100644
index 0000000..fc8c00e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_inline_error.9.png b/core/res/res/drawable-hdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..b188d81
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_inline_error_above.9.png b/core/res/res/drawable-hdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..3d4e8ba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_top_bright.9.png b/core/res/res/drawable-hdpi/popup_top_bright.9.png
new file mode 100644
index 0000000..ddd30ab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/popup_top_dark.9.png b/core/res/res/drawable-hdpi/popup_top_dark.9.png
new file mode 100644
index 0000000..144d0fc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_away.png b/core/res/res/drawable-hdpi/presence_away.png
new file mode 100644
index 0000000..d4aa66b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_busy.png b/core/res/res/drawable-hdpi/presence_busy.png
new file mode 100644
index 0000000..4b27853
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_invisible.png b/core/res/res/drawable-hdpi/presence_invisible.png
new file mode 100644
index 0000000..0e27fba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_offline.png b/core/res/res/drawable-hdpi/presence_offline.png
new file mode 100644
index 0000000..e511aa4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/presence_online.png b/core/res/res/drawable-hdpi/presence_online.png
new file mode 100644
index 0000000..d787d2f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pressed_application_background_static.png b/core/res/res/drawable-hdpi/pressed_application_background_static.png
new file mode 100644
index 0000000..dae96e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pressed_application_background_static.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate1.png b/core/res/res/drawable-hdpi/progressbar_indeterminate1.png
new file mode 100644
index 0000000..ea88e32
--- /dev/null
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate2.png b/core/res/res/drawable-hdpi/progressbar_indeterminate2.png
new file mode 100644
index 0000000..436c48c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/progressbar_indeterminate3.png b/core/res/res/drawable-hdpi/progressbar_indeterminate3.png
new file mode 100644
index 0000000..ea88e32
--- /dev/null
+++ b/core/res/res/drawable-hdpi/progressbar_indeterminate3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/radiobutton_off_background.png b/core/res/res/drawable-hdpi/radiobutton_off_background.png
new file mode 100644
index 0000000..5ce33cd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/radiobutton_off_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/radiobutton_on_background.png b/core/res/res/drawable-hdpi/radiobutton_on_background.png
new file mode 100644
index 0000000..0f46071
--- /dev/null
+++ b/core/res/res/drawable-hdpi/radiobutton_on_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_big_half.png b/core/res/res/drawable-hdpi/rate_star_big_half.png
new file mode 100644
index 0000000..61906a5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_big_half.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_big_off.png b/core/res/res/drawable-hdpi/rate_star_big_off.png
new file mode 100644
index 0000000..a688a45
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_big_on.png b/core/res/res/drawable-hdpi/rate_star_big_on.png
new file mode 100644
index 0000000..77cb067
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_small_half.png b/core/res/res/drawable-hdpi/rate_star_small_half.png
new file mode 100644
index 0000000..6b2efd9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_small_half.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_small_off.png b/core/res/res/drawable-hdpi/rate_star_small_off.png
new file mode 100644
index 0000000..8422919
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_small_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/rate_star_small_on.png b/core/res/res/drawable-hdpi/rate_star_small_on.png
new file mode 100644
index 0000000..45e2324
--- /dev/null
+++ b/core/res/res/drawable-hdpi/rate_star_small_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/reticle.png b/core/res/res/drawable-hdpi/reticle.png
new file mode 100644
index 0000000..a3e8c1b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/reticle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/screen_progress_frame.9.png b/core/res/res/drawable-hdpi/screen_progress_frame.9.png
new file mode 100644
index 0000000..3f9d738
--- /dev/null
+++ b/core/res/res/drawable-hdpi/screen_progress_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/screen_progress_inner.9.png b/core/res/res/drawable-hdpi/screen_progress_inner.9.png
new file mode 100644
index 0000000..10c7da5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/screen_progress_inner.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png
new file mode 100644
index 0000000..c916780
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_accelerated_anim2.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_horizontal.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_horizontal.9.png
new file mode 100644
index 0000000..cd206e2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_horizontal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrollbar_handle_vertical.9.png b/core/res/res/drawable-hdpi/scrollbar_handle_vertical.9.png
new file mode 100644
index 0000000..3ec0791
--- /dev/null
+++ b/core/res/res/drawable-hdpi/scrollbar_handle_vertical.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/search_dropdown_background.9.png b/core/res/res/drawable-hdpi/search_dropdown_background.9.png
new file mode 100644
index 0000000..e6945db
--- /dev/null
+++ b/core/res/res/drawable-hdpi/search_dropdown_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/search_plate.9.png b/core/res/res/drawable-hdpi/search_plate.9.png
new file mode 100644
index 0000000..561c9fa
--- /dev/null
+++ b/core/res/res/drawable-hdpi/search_plate.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/search_plate_global.9.png b/core/res/res/drawable-hdpi/search_plate_global.9.png
new file mode 100644
index 0000000..32c6dc3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/search_plate_global.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/seek_thumb_normal.png b/core/res/res/drawable-hdpi/seek_thumb_normal.png
new file mode 100644
index 0000000..cc83a7d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/seek_thumb_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/seek_thumb_pressed.png b/core/res/res/drawable-hdpi/seek_thumb_pressed.png
new file mode 100644
index 0000000..15f79ea
--- /dev/null
+++ b/core/res/res/drawable-hdpi/seek_thumb_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/seek_thumb_selected.png b/core/res/res/drawable-hdpi/seek_thumb_selected.png
new file mode 100644
index 0000000..8a7cf68
--- /dev/null
+++ b/core/res/res/drawable-hdpi/seek_thumb_selected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/settings_header_raw.9.png b/core/res/res/drawable-hdpi/settings_header_raw.9.png
new file mode 100644
index 0000000..6857c0f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/settings_header_raw.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_16.png b/core/res/res/drawable-hdpi/spinner_black_16.png
new file mode 100644
index 0000000..eb34867
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_20.png b/core/res/res/drawable-hdpi/spinner_black_20.png
new file mode 100644
index 0000000..dac06d7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_48.png b/core/res/res/drawable-hdpi/spinner_black_48.png
new file mode 100644
index 0000000..337f72a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_black_76.png b/core/res/res/drawable-hdpi/spinner_black_76.png
new file mode 100644
index 0000000..2edc3e7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_dropdown_background_down.9.png b/core/res/res/drawable-hdpi/spinner_dropdown_background_down.9.png
new file mode 100644
index 0000000..566543d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_dropdown_background_down.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_dropdown_background_up.9.png b/core/res/res/drawable-hdpi/spinner_dropdown_background_up.9.png
new file mode 100644
index 0000000..7b883cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_dropdown_background_up.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_normal.9.png b/core/res/res/drawable-hdpi/spinner_normal.9.png
new file mode 100644
index 0000000..b1f25f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_press.9.png b/core/res/res/drawable-hdpi/spinner_press.9.png
new file mode 100644
index 0000000..6aab271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_select.9.png b/core/res/res/drawable-hdpi/spinner_select.9.png
new file mode 100644
index 0000000..9024d07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_select.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_16.png b/core/res/res/drawable-hdpi/spinner_white_16.png
new file mode 100644
index 0000000..7914a68
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_48.png b/core/res/res/drawable-hdpi/spinner_white_48.png
new file mode 100644
index 0000000..faee8ca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_white_76.png b/core/res/res/drawable-hdpi/spinner_white_76.png
new file mode 100644
index 0000000..cd26379
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png
new file mode 100644
index 0000000..af80855
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinnerbox_arrow_first.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png
new file mode 100644
index 0000000..dc47275
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinnerbox_arrow_last.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png
new file mode 100644
index 0000000..007f279
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinnerbox_arrow_middle.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png b/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png
new file mode 100644
index 0000000..24592a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinnerbox_arrow_single.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/star_big_off.png b/core/res/res/drawable-hdpi/star_big_off.png
new file mode 100644
index 0000000..ee6c942
--- /dev/null
+++ b/core/res/res/drawable-hdpi/star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/star_big_on.png b/core/res/res/drawable-hdpi/star_big_on.png
new file mode 100644
index 0000000..17b4d73
--- /dev/null
+++ b/core/res/res/drawable-hdpi/star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/star_off.png b/core/res/res/drawable-hdpi/star_off.png
new file mode 100644
index 0000000..e1897c7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/star_off.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/star_on.png b/core/res/res/drawable-hdpi/star_on.png
new file mode 100644
index 0000000..b7440ea
--- /dev/null
+++ b/core/res/res/drawable-hdpi/star_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_ecb_mode.png b/core/res/res/drawable-hdpi/stat_ecb_mode.png
new file mode 100644
index 0000000..91e6c1e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_ecb_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_alarm.png b/core/res/res/drawable-hdpi/stat_notify_alarm.png
new file mode 100644
index 0000000..adac8a9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_call_mute.png b/core/res/res/drawable-hdpi/stat_notify_call_mute.png
new file mode 100644
index 0000000..a43139b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_call_mute.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_chat.png b/core/res/res/drawable-hdpi/stat_notify_chat.png
new file mode 100644
index 0000000..7b837b3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_chat.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_disk_full.png b/core/res/res/drawable-hdpi/stat_notify_disk_full.png
new file mode 100644
index 0000000..2163fa7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_error.png b/core/res/res/drawable-hdpi/stat_notify_error.png
new file mode 100644
index 0000000..9853cda
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_missed_call.png b/core/res/res/drawable-hdpi/stat_notify_missed_call.png
new file mode 100644
index 0000000..a313360
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_missed_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_more.png b/core/res/res/drawable-hdpi/stat_notify_more.png
new file mode 100644
index 0000000..bbbd59b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_more.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard.png b/core/res/res/drawable-hdpi/stat_notify_sdcard.png
new file mode 100644
index 0000000..0161419
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png
new file mode 100644
index 0000000..23b2f0a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sdcard_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png b/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png
new file mode 100644
index 0000000..401fbb0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sim_toolkit.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync.png b/core/res/res/drawable-hdpi/stat_notify_sync.png
new file mode 100644
index 0000000..dacfc3a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sync.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png b/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png
new file mode 100644
index 0000000..00e2ebb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sync_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_sync_error.png b/core/res/res/drawable-hdpi/stat_notify_sync_error.png
new file mode 100644
index 0000000..3083b44
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_sync_error.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_voicemail.png b/core/res/res/drawable-hdpi/stat_notify_voicemail.png
new file mode 100644
index 0000000..7f62e94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
new file mode 100644
index 0000000..712e071
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_0.png b/core/res/res/drawable-hdpi/stat_sys_battery_0.png
new file mode 100644
index 0000000..171b8b5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_10.png b/core/res/res/drawable-hdpi/stat_sys_battery_10.png
new file mode 100644
index 0000000..77e8793
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_10.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_100.png b/core/res/res/drawable-hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..d13d083
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_20.png b/core/res/res/drawable-hdpi/stat_sys_battery_20.png
new file mode 100644
index 0000000..c14dc07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_20.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_40.png b/core/res/res/drawable-hdpi/stat_sys_battery_40.png
new file mode 100644
index 0000000..a3077b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_40.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_60.png b/core/res/res/drawable-hdpi/stat_sys_battery_60.png
new file mode 100644
index 0000000..621e905
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_60.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_80.png b/core/res/res/drawable-hdpi/stat_sys_battery_80.png
new file mode 100644
index 0000000..29830e9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_80.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png
new file mode 100644
index 0000000..61556c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png
new file mode 100644
index 0000000..985dc0f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.png
new file mode 100644
index 0000000..b319d07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.png
new file mode 100644
index 0000000..cbf21c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.png
new file mode 100644
index 0000000..51565c3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.png b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.png
new file mode 100644
index 0000000..bf41a89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_charge_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png b/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png
new file mode 100644
index 0000000..8c5daba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
new file mode 100644
index 0000000..7cf5962
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
new file mode 100644
index 0000000..3e3f4f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png
new file mode 100644
index 0000000..58d2dbb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png
new file mode 100644
index 0000000..42af121
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png
new file mode 100644
index 0000000..6106dd4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png b/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png
new file mode 100644
index 0000000..cc6e284
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png
new file mode 100644
index 0000000..cd1afe9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png
new file mode 100644
index 0000000..5848981
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_e.png b/core/res/res/drawable-hdpi/stat_sys_data_in_e.png
new file mode 100644
index 0000000..e4c6c2b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_in_g.png b/core/res/res/drawable-hdpi/stat_sys_data_in_g.png
new file mode 100644
index 0000000..05a910b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png
new file mode 100644
index 0000000..c398d6f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png
new file mode 100644
index 0000000..b4383e2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png
new file mode 100644
index 0000000..78b04db
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png b/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png
new file mode 100644
index 0000000..9475159
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png b/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png
new file mode 100644
index 0000000..e1eb5b0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png b/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png
new file mode 100644
index 0000000..2f49d60
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_e.png b/core/res/res/drawable-hdpi/stat_sys_data_out_e.png
new file mode 100644
index 0000000..433fa8d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_out_g.png b/core/res/res/drawable-hdpi/stat_sys_data_out_g.png
new file mode 100644
index 0000000..eb43a91
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_data_usb.png b/core/res/res/drawable-hdpi/stat_sys_data_usb.png
new file mode 100644
index 0000000..7a83544
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_data_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim0.png b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
new file mode 100644
index 0000000..a907d79
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim1.png b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png
new file mode 100644
index 0000000..e9f42ad
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim2.png b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png
new file mode 100644
index 0000000..d1682e0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim3.png b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png
new file mode 100644
index 0000000..70e757b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim4.png b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png
new file mode 100644
index 0000000..3b2d7d1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_download_anim5.png b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png
new file mode 100644
index 0000000..ced2bb2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png b/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png
new file mode 100644
index 0000000..fe7b2cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_gps_on.png b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
new file mode 100644
index 0000000..6ab4720
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_headset.png b/core/res/res/drawable-hdpi/stat_sys_headset.png
new file mode 100644
index 0000000..7a70aea
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_headset.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_no_sim.png b/core/res/res/drawable-hdpi/stat_sys_no_sim.png
new file mode 100644
index 0000000..48d1ca3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call.png b/core/res/res/drawable-hdpi/stat_sys_phone_call.png
new file mode 100644
index 0000000..0aa15d6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
new file mode 100644
index 0000000..a143f87
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png
new file mode 100644
index 0000000..b1ab8ac
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_forward.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png
new file mode 100644
index 0000000..b881a67
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png
new file mode 100644
index 0000000..938cca7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png
new file mode 100644
index 0000000..5ddd177
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png
new file mode 100644
index 0000000..6f82aa9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png
new file mode 100644
index 0000000..fa4265c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png
new file mode 100644
index 0000000..db9752a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png
new file mode 100644
index 0000000..c99af17
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png
new file mode 100644
index 0000000..70594e5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png
new file mode 100644
index 0000000..5e0235f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png
new file mode 100644
index 0000000..d361263
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png b/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png
new file mode 100644
index 0000000..8b110a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_r_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png
new file mode 100644
index 0000000..84b9f94
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png
new file mode 100644
index 0000000..657b572
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png
new file mode 100644
index 0000000..236d44e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png
new file mode 100644
index 0000000..e140735
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png b/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png
new file mode 100644
index 0000000..0e4f854
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ra_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png b/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png
new file mode 100644
index 0000000..d5c301c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png b/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png
new file mode 100644
index 0000000..21c1c08
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png
new file mode 100644
index 0000000..f7f5757
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png
new file mode 100644
index 0000000..5f01083
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png
new file mode 100644
index 0000000..f7f5757
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_roaming_cdma_flash_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_signal_0.png
new file mode 100644
index 0000000..83ef6a3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_signal_1.png
new file mode 100644
index 0000000..a1ca717
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_signal_2.png
new file mode 100644
index 0000000..a478bd0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_signal_3.png
new file mode 100644
index 0000000..a3978de
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_signal_4.png
new file mode 100644
index 0000000..1b553e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png b/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png
new file mode 100644
index 0000000..6ee35fb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_signal_null.png b/core/res/res/drawable-hdpi/stat_sys_signal_null.png
new file mode 100644
index 0000000..6b103f5
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_speakerphone.png b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
new file mode 100644
index 0000000..62eadb3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tty_mode.png b/core/res/res/drawable-hdpi/stat_sys_tty_mode.png
new file mode 100644
index 0000000..4e161c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tty_mode.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png
new file mode 100644
index 0000000..4b7e942
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
new file mode 100644
index 0000000..a416350
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
new file mode 100644
index 0000000..8d199dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png
new file mode 100644
index 0000000..dfef071
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png
new file mode 100644
index 0000000..abe56f1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png b/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png
new file mode 100644
index 0000000..1e917c1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_upload_anim5.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png
new file mode 100644
index 0000000..d6b25d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
new file mode 100644
index 0000000..a143f87
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
new file mode 100644
index 0000000..53608cf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_warning.png b/core/res/res/drawable-hdpi/stat_sys_warning.png
new file mode 100644
index 0000000..8f7bd5d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_warning.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png
new file mode 100644
index 0000000..9a0aa21
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png
new file mode 100644
index 0000000..39db490
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png
new file mode 100644
index 0000000..5c0ae76
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png
new file mode 100644
index 0000000..f7e0b38
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png
new file mode 100644
index 0000000..5ad5d12
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_background.png b/core/res/res/drawable-hdpi/status_bar_background.png
new file mode 100644
index 0000000..e6a865a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_close_on.9.png b/core/res/res/drawable-hdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..5acf638
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_header_background.9.png b/core/res/res/drawable-hdpi/status_bar_header_background.9.png
new file mode 100644
index 0000000..be36ff2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
new file mode 100644
index 0000000..4fbfa4f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
new file mode 100644
index 0000000..0876bc6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
new file mode 100644
index 0000000..c01c018
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
new file mode 100644
index 0000000..343e4ca
--- /dev/null
+++ b/core/res/res/drawable-hdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/statusbar_background.png b/core/res/res/drawable-hdpi/statusbar_background.png
new file mode 100644
index 0000000..c2b3a5e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/submenu_arrow_nofocus.png b/core/res/res/drawable-hdpi/submenu_arrow_nofocus.png
new file mode 100644
index 0000000..afc0891
--- /dev/null
+++ b/core/res/res/drawable-hdpi/submenu_arrow_nofocus.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_action_add.png b/core/res/res/drawable-hdpi/sym_action_add.png
new file mode 100644
index 0000000..6e028b2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_action_add.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_action_call.png b/core/res/res/drawable-hdpi/sym_action_call.png
new file mode 100644
index 0000000..105f7d0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_action_call.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_action_chat.png b/core/res/res/drawable-hdpi/sym_action_chat.png
new file mode 100644
index 0000000..7fd34f0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_action_chat.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_action_email.png b/core/res/res/drawable-hdpi/sym_action_email.png
new file mode 100644
index 0000000..1d933e4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_action_email.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_call_incoming.png b/core/res/res/drawable-hdpi/sym_call_incoming.png
new file mode 100644
index 0000000..83c75dc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_call_incoming.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_call_missed.png b/core/res/res/drawable-hdpi/sym_call_missed.png
new file mode 100644
index 0000000..abcbbbf
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_call_missed.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_call_outgoing.png b/core/res/res/drawable-hdpi/sym_call_outgoing.png
new file mode 100644
index 0000000..6cb8653
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_call_outgoing.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_contact_card.png b/core/res/res/drawable-hdpi/sym_contact_card.png
new file mode 100644
index 0000000..fe9c751
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_contact_card.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/sym_def_app_icon.png b/core/res/res/drawable-hdpi/sym_def_app_icon.png
new file mode 100644
index 0000000..4b5384f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus.9.png b/core/res/res/drawable-hdpi/tab_focus.9.png
new file mode 100644
index 0000000..6e8a71f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png b/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png
new file mode 100644
index 0000000..51194a4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_focus_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png b/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png
new file mode 100644
index 0000000..51194a4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_focus_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press.9.png b/core/res/res/drawable-hdpi/tab_press.9.png
new file mode 100644
index 0000000..119b2c6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press_bar_left.9.png b/core/res/res/drawable-hdpi/tab_press_bar_left.9.png
new file mode 100644
index 0000000..dc2fbce
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_press_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_press_bar_right.9.png b/core/res/res/drawable-hdpi/tab_press_bar_right.9.png
new file mode 100644
index 0000000..dc2fbce
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_press_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected.9.png b/core/res/res/drawable-hdpi/tab_selected.9.png
new file mode 100644
index 0000000..29d45a1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png b/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png
new file mode 100644
index 0000000..aa935fe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png b/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png
new file mode 100644
index 0000000..aa935fe
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_selected_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/tab_unselected.9.png b/core/res/res/drawable-hdpi/tab_unselected.9.png
new file mode 100644
index 0000000..f5781ab
--- /dev/null
+++ b/core/res/res/drawable-hdpi/tab_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_default.9.png b/core/res/res/drawable-hdpi/textfield_default.9.png
new file mode 100644
index 0000000..a2f022a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled.9.png b/core/res/res/drawable-hdpi/textfield_disabled.9.png
new file mode 100644
index 0000000..6a28cb4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png
new file mode 100644
index 0000000..0de9cda
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_pressed.9.png b/core/res/res/drawable-hdpi/textfield_pressed.9.png
new file mode 100644
index 0000000..d5892c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_default.9.png b/core/res/res/drawable-hdpi/textfield_search_default.9.png
new file mode 100644
index 0000000..db64da1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_pressed.9.png b/core/res/res/drawable-hdpi/textfield_search_pressed.9.png
new file mode 100644
index 0000000..cde51e4
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_search_selected.9.png b/core/res/res/drawable-hdpi/textfield_search_selected.9.png
new file mode 100644
index 0000000..f4bf352
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_search_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/textfield_selected.9.png b/core/res/res/drawable-hdpi/textfield_selected.9.png
new file mode 100644
index 0000000..7a072dd
--- /dev/null
+++ b/core/res/res/drawable-hdpi/textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled.9.png b/core/res/res/drawable-hdpi/timepicker_down_disabled.9.png
new file mode 100644
index 0000000..73b6915
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_disabled_focused.9.png b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused.9.png
new file mode 100644
index 0000000..046e60f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_down_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_normal.9.png b/core/res/res/drawable-hdpi/timepicker_down_normal.9.png
new file mode 100644
index 0000000..9baf7cc
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_down_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_pressed.9.png b/core/res/res/drawable-hdpi/timepicker_down_pressed.9.png
new file mode 100644
index 0000000..d95fdd3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_down_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_down_selected.9.png b/core/res/res/drawable-hdpi/timepicker_down_selected.9.png
new file mode 100644
index 0000000..a84448f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_down_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_input_disabled.9.png b/core/res/res/drawable-hdpi/timepicker_input_disabled.9.png
new file mode 100644
index 0000000..aa17a98
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_input_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_input_normal.9.png b/core/res/res/drawable-hdpi/timepicker_input_normal.9.png
new file mode 100644
index 0000000..be78a58
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_input_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_input_pressed.9.png b/core/res/res/drawable-hdpi/timepicker_input_pressed.9.png
new file mode 100644
index 0000000..b28f66c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_input_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_input_selected.9.png b/core/res/res/drawable-hdpi/timepicker_input_selected.9.png
new file mode 100644
index 0000000..2e175e8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_input_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled.9.png b/core/res/res/drawable-hdpi/timepicker_up_disabled.9.png
new file mode 100644
index 0000000..348e48c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_disabled_focused.9.png b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused.9.png
new file mode 100644
index 0000000..93cf3a0
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_up_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_normal.9.png b/core/res/res/drawable-hdpi/timepicker_up_normal.9.png
new file mode 100644
index 0000000..b4acced
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_up_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_pressed.9.png b/core/res/res/drawable-hdpi/timepicker_up_pressed.9.png
new file mode 100644
index 0000000..bd29510
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_up_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/timepicker_up_selected.9.png b/core/res/res/drawable-hdpi/timepicker_up_selected.9.png
new file mode 100644
index 0000000..a666998
--- /dev/null
+++ b/core/res/res/drawable-hdpi/timepicker_up_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/title_bar_portrait.9.png b/core/res/res/drawable-hdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..161432f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/title_bar_shadow.9.png b/core/res/res/drawable-hdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..e67f457
--- /dev/null
+++ b/core/res/res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/title_bar_tall.png b/core/res/res/drawable-hdpi/title_bar_tall.png
new file mode 100644
index 0000000..f177440
--- /dev/null
+++ b/core/res/res/drawable-hdpi/title_bar_tall.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/toast_frame.9.png b/core/res/res/drawable-hdpi/toast_frame.9.png
new file mode 100644
index 0000000..8f5d811
--- /dev/null
+++ b/core/res/res/drawable-hdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/unknown_image.png b/core/res/res/drawable-hdpi/unknown_image.png
new file mode 100644
index 0000000..76341db
--- /dev/null
+++ b/core/res/res/drawable-hdpi/unknown_image.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/zoom_plate.9.png b/core/res/res/drawable-hdpi/zoom_plate.9.png
new file mode 100644
index 0000000..e97dac1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/zoom_plate.9.png
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/statusbar_background.png b/core/res/res/drawable-land-hdpi/statusbar_background.png
new file mode 100644
index 0000000..4a955c5
--- /dev/null
+++ b/core/res/res/drawable-land-hdpi/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable-land-hdpi/title_bar_tall.png b/core/res/res/drawable-land-hdpi/title_bar_tall.png
new file mode 100644
index 0000000..96b5ffe
--- /dev/null
+++ b/core/res/res/drawable-land-hdpi/title_bar_tall.png
Binary files differ
diff --git a/core/res/res/drawable-land/statusbar_background.png b/core/res/res/drawable-land-mdpi/statusbar_background.png
index ef61e52..ef61e52 100644
--- a/core/res/res/drawable-land/statusbar_background.png
+++ b/core/res/res/drawable-land-mdpi/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable-land/title_bar_tall.png b/core/res/res/drawable-land-mdpi/title_bar_tall.png
index 16290fb..16290fb 100644
--- a/core/res/res/drawable-land/title_bar_tall.png
+++ b/core/res/res/drawable-land-mdpi/title_bar_tall.png
Binary files differ
diff --git a/core/res/res/drawable/activity_title_bar.9.png b/core/res/res/drawable-mdpi/activity_title_bar.9.png
index bd0680d..bd0680d 100644
--- a/core/res/res/drawable/activity_title_bar.9.png
+++ b/core/res/res/drawable-mdpi/activity_title_bar.9.png
Binary files differ
diff --git a/core/res/res/drawable/arrow_down_float.png b/core/res/res/drawable-mdpi/arrow_down_float.png
index dd82523..dd82523 100644
--- a/core/res/res/drawable/arrow_down_float.png
+++ b/core/res/res/drawable-mdpi/arrow_down_float.png
Binary files differ
diff --git a/core/res/res/drawable/arrow_up_float.png b/core/res/res/drawable-mdpi/arrow_up_float.png
index 9bc3d1c..9bc3d1c 100644
--- a/core/res/res/drawable/arrow_up_float.png
+++ b/core/res/res/drawable-mdpi/arrow_up_float.png
Binary files differ
diff --git a/core/res/res/drawable/battery_charge_background.png b/core/res/res/drawable-mdpi/battery_charge_background.png
index 9219745..9219745 100644
--- a/core/res/res/drawable/battery_charge_background.png
+++ b/core/res/res/drawable-mdpi/battery_charge_background.png
Binary files differ
diff --git a/core/res/res/drawable/battery_charge_fill_empty.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
index 9ed20ba..9ed20ba 100644
--- a/core/res/res/drawable/battery_charge_fill_empty.9.png
+++ b/core/res/res/drawable-mdpi/battery_charge_fill_empty.9.png
Binary files differ
diff --git a/core/res/res/drawable/battery_charge_fill_full.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png
index 8e6aaca..8e6aaca 100644
--- a/core/res/res/drawable/battery_charge_fill_full.9.png
+++ b/core/res/res/drawable-mdpi/battery_charge_fill_full.9.png
Binary files differ
diff --git a/core/res/res/drawable/battery_charge_fill_warning.9.png b/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png
index d3287db..d3287db 100644
--- a/core/res/res/drawable/battery_charge_fill_warning.9.png
+++ b/core/res/res/drawable-mdpi/battery_charge_fill_warning.9.png
Binary files differ
diff --git a/core/res/res/drawable/battery_low_battery.png b/core/res/res/drawable-mdpi/battery_low_battery.png
index 60bbe6c..60bbe6c 100644
--- a/core/res/res/drawable/battery_low_battery.png
+++ b/core/res/res/drawable-mdpi/battery_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable/blank_tile.png b/core/res/res/drawable-mdpi/blank_tile.png
index 63b9296..63b9296 100644
--- a/core/res/res/drawable/blank_tile.png
+++ b/core/res/res/drawable-mdpi/blank_tile.png
Binary files differ
diff --git a/core/res/res/drawable/bottom_bar.png b/core/res/res/drawable-mdpi/bottom_bar.png
index 1fdb078..1fdb078 100644
--- a/core/res/res/drawable/bottom_bar.png
+++ b/core/res/res/drawable-mdpi/bottom_bar.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_buttonless_off.png b/core/res/res/drawable-mdpi/btn_check_buttonless_off.png
index f8972fc..f8972fc 100755
--- a/core/res/res/drawable/btn_check_buttonless_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_buttonless_off.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_buttonless_on.png b/core/res/res/drawable-mdpi/btn_check_buttonless_on.png
index a10a37a..a10a37a 100755
--- a/core/res/res/drawable/btn_check_buttonless_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_buttonless_on.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_label_background.9.png b/core/res/res/drawable-mdpi/btn_check_label_background.9.png
index 79367b8..79367b8 100644
--- a/core/res/res/drawable/btn_check_label_background.9.png
+++ b/core/res/res/drawable-mdpi/btn_check_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_off.png b/core/res/res/drawable-mdpi/btn_check_off.png
index 56d3861..56d3861 100644
--- a/core/res/res/drawable/btn_check_off.png
+++ b/core/res/res/drawable-mdpi/btn_check_off.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_off_disable.png b/core/res/res/drawable-mdpi/btn_check_off_disable.png
index e012afd..e012afd 100644
--- a/core/res/res/drawable/btn_check_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
index 0837bbd..0837bbd 100644
--- a/core/res/res/drawable/btn_check_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_off_pressed.png b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
index 984dfd7..984dfd7 100644
--- a/core/res/res/drawable/btn_check_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_off_selected.png b/core/res/res/drawable-mdpi/btn_check_off_selected.png
index 20842d4..20842d4 100644
--- a/core/res/res/drawable/btn_check_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_on.png b/core/res/res/drawable-mdpi/btn_check_on.png
index 791ac1d..791ac1d 100644
--- a/core/res/res/drawable/btn_check_on.png
+++ b/core/res/res/drawable-mdpi/btn_check_on.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_on_disable.png b/core/res/res/drawable-mdpi/btn_check_on_disable.png
index 6cb02f3..6cb02f3 100644
--- a/core/res/res/drawable/btn_check_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
index 8a73b33..8a73b33 100644
--- a/core/res/res/drawable/btn_check_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_on_pressed.png b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
index 300d64a..300d64a 100644
--- a/core/res/res/drawable/btn_check_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_on_selected.png b/core/res/res/drawable-mdpi/btn_check_on_selected.png
index 0b36adb..0b36adb 100644
--- a/core/res/res/drawable/btn_check_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_check_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_disable.png b/core/res/res/drawable-mdpi/btn_circle_disable.png
index 33b74a6..33b74a6 100644
--- a/core/res/res/drawable/btn_circle_disable.png
+++ b/core/res/res/drawable-mdpi/btn_circle_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_disable_focused.png b/core/res/res/drawable-mdpi/btn_circle_disable_focused.png
index 005ad8d..005ad8d 100644
--- a/core/res/res/drawable/btn_circle_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_circle_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_normal.png b/core/res/res/drawable-mdpi/btn_circle_normal.png
index fc5af1c..fc5af1c 100644
--- a/core/res/res/drawable/btn_circle_normal.png
+++ b/core/res/res/drawable-mdpi/btn_circle_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_pressed.png b/core/res/res/drawable-mdpi/btn_circle_pressed.png
index 8f40afd..8f40afd 100644
--- a/core/res/res/drawable/btn_circle_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_circle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_circle_selected.png b/core/res/res/drawable-mdpi/btn_circle_selected.png
index c74fac2..c74fac2 100644
--- a/core/res/res/drawable/btn_circle_selected.png
+++ b/core/res/res/drawable-mdpi/btn_circle_selected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_normal.png b/core/res/res/drawable-mdpi/btn_close_normal.png
new file mode 100644
index 0000000..4c6e79d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_close_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_pressed.png b/core/res/res/drawable-mdpi/btn_close_pressed.png
new file mode 100644
index 0000000..fc983af
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_close_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_close_selected.png b/core/res/res/drawable-mdpi/btn_close_selected.png
new file mode 100644
index 0000000..f2bf91a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_close_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png
index c2e0b05..c2e0b05 100755
--- a/core/res/res/drawable/btn_code_lock_default.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
index 70e95a2..70e95a2 100755
--- a/core/res/res/drawable/btn_code_lock_touched.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_normal.9.png b/core/res/res/drawable-mdpi/btn_default_normal.9.png
index a2d5ccd..a2d5ccd 100644
--- a/core/res/res/drawable/btn_default_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_normal_disable.9.png b/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png
index edd3a3e..edd3a3e 100644
--- a/core/res/res/drawable/btn_default_normal_disable.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_normal_disable_focused.9.png b/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png
index f506179..f506179 100644
--- a/core/res/res/drawable/btn_default_normal_disable_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_pressed.9.png b/core/res/res/drawable-mdpi/btn_default_pressed.9.png
index 033bf89..033bf89 100644
--- a/core/res/res/drawable/btn_default_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_selected.9.png b/core/res/res/drawable-mdpi/btn_default_selected.9.png
index 1e900bf..1e900bf 100644
--- a/core/res/res/drawable/btn_default_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_small_normal.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal.9.png
index bcedd5f..bcedd5f 100644
--- a/core/res/res/drawable/btn_default_small_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_small_normal_disable.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png
index ac6260f..ac6260f 100644
--- a/core/res/res/drawable/btn_default_small_normal_disable.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png b/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
index 4ee1b3f..4ee1b3f 100644
--- a/core/res/res/drawable/btn_default_small_normal_disable_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_small_pressed.9.png b/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png
index 25e38f4..25e38f4 100644
--- a/core/res/res/drawable/btn_default_small_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_default_small_selected.9.png b/core/res/res/drawable-mdpi/btn_default_small_selected.9.png
index cc209c6..cc209c6 100644
--- a/core/res/res/drawable/btn_default_small_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dialog_disable.png b/core/res/res/drawable-mdpi/btn_dialog_disable.png
index f041cab..f041cab 100755
--- a/core/res/res/drawable/btn_dialog_disable.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dialog_normal.png b/core/res/res/drawable-mdpi/btn_dialog_normal.png
index a2d27fa..a2d27fa 100755
--- a/core/res/res/drawable/btn_dialog_normal.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dialog_pressed.png b/core/res/res/drawable-mdpi/btn_dialog_pressed.png
index 9c9922a..9c9922a 100755
--- a/core/res/res/drawable/btn_dialog_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dialog_selected.png b/core/res/res/drawable-mdpi/btn_dialog_selected.png
index 7656de5..7656de5 100755
--- a/core/res/res/drawable/btn_dialog_selected.png
+++ b/core/res/res/drawable-mdpi/btn_dialog_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dropdown_normal.9.png b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
index f6e9a19..f6e9a19 100644
--- a/core/res/res/drawable/btn_dropdown_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dropdown_pressed.9.png b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
index 3bdf52d..3bdf52d 100644
--- a/core/res/res/drawable/btn_dropdown_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_dropdown_selected.9.png b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
index 087956e..087956e 100644
--- a/core/res/res/drawable/btn_dropdown_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_dropdown_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_erase_default.9.png b/core/res/res/drawable-mdpi/btn_erase_default.9.png
index c3bf60c..c3bf60c 100755
--- a/core/res/res/drawable/btn_erase_default.9.png
+++ b/core/res/res/drawable-mdpi/btn_erase_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_erase_pressed.9.png b/core/res/res/drawable-mdpi/btn_erase_pressed.9.png
index 727aafe..727aafe 100755
--- a/core/res/res/drawable/btn_erase_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_erase_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_erase_selected.9.png b/core/res/res/drawable-mdpi/btn_erase_selected.9.png
index c6bd020..c6bd020 100755
--- a/core/res/res/drawable/btn_erase_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_erase_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_global_search_normal.9.png b/core/res/res/drawable-mdpi/btn_global_search_normal.9.png
index 9b7d3e5..9b7d3e5 100644
--- a/core/res/res/drawable/btn_global_search_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_global_search_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_normal.9.png
index 7ba18dd..7ba18dd 100644
--- a/core/res/res/drawable/btn_keyboard_key_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png
index bda9b83..bda9b83 100644
--- a/core/res/res/drawable/btn_keyboard_key_normal_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png
index 0c16ed5..0c16ed5 100644
--- a/core/res/res/drawable/btn_keyboard_key_normal_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed.9.png
index 39b9314..39b9314 100755
--- a/core/res/res/drawable/btn_keyboard_key_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png
index bdcf06e..bdcf06e 100644
--- a/core/res/res/drawable/btn_keyboard_key_pressed_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png
index 79621a9..79621a9 100644
--- a/core/res/res/drawable/btn_keyboard_key_pressed_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal.9.png
new file mode 100644
index 0000000..652c05f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed.9.png
new file mode 100644
index 0000000..1d1e9c0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_trans_selected.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_selected.9.png
new file mode 100644
index 0000000..b168e0c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_trans_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_media_player.9.png b/core/res/res/drawable-mdpi/btn_media_player.9.png
index 3ec3f68..3ec3f68 100755
--- a/core/res/res/drawable/btn_media_player.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_media_player_disabled.9.png b/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png
index e74335b..e74335b 100755
--- a/core/res/res/drawable/btn_media_player_disabled.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_media_player_disabled_selected.9.png b/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png
index 2c6517f..2c6517f 100755
--- a/core/res/res/drawable/btn_media_player_disabled_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_media_player_pressed.9.png b/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png
index 40bee47..40bee47 100755
--- a/core/res/res/drawable/btn_media_player_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_media_player_selected.9.png b/core/res/res/drawable-mdpi/btn_media_player_selected.9.png
index 28d809f..28d809f 100755
--- a/core/res/res/drawable/btn_media_player_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_media_player_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_minus_default.png b/core/res/res/drawable-mdpi/btn_minus_default.png
index ee95879..ee95879 100644
--- a/core/res/res/drawable/btn_minus_default.png
+++ b/core/res/res/drawable-mdpi/btn_minus_default.png
Binary files differ
diff --git a/core/res/res/drawable/btn_minus_disable.png b/core/res/res/drawable-mdpi/btn_minus_disable.png
index dff7bf7..dff7bf7 100644
--- a/core/res/res/drawable/btn_minus_disable.png
+++ b/core/res/res/drawable-mdpi/btn_minus_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_minus_disable_focused.png b/core/res/res/drawable-mdpi/btn_minus_disable_focused.png
index 3f04557..3f04557 100644
--- a/core/res/res/drawable/btn_minus_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_minus_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_minus_pressed.png b/core/res/res/drawable-mdpi/btn_minus_pressed.png
index 758d958..758d958 100644
--- a/core/res/res/drawable/btn_minus_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_minus_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_minus_selected.png b/core/res/res/drawable-mdpi/btn_minus_selected.png
index 752a249..752a249 100644
--- a/core/res/res/drawable/btn_minus_selected.png
+++ b/core/res/res/drawable-mdpi/btn_minus_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_plus_default.png b/core/res/res/drawable-mdpi/btn_plus_default.png
index aa31e37..aa31e37 100644
--- a/core/res/res/drawable/btn_plus_default.png
+++ b/core/res/res/drawable-mdpi/btn_plus_default.png
Binary files differ
diff --git a/core/res/res/drawable/btn_plus_disable.png b/core/res/res/drawable-mdpi/btn_plus_disable.png
index c373cd3..c373cd3 100644
--- a/core/res/res/drawable/btn_plus_disable.png
+++ b/core/res/res/drawable-mdpi/btn_plus_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_plus_disable_focused.png b/core/res/res/drawable-mdpi/btn_plus_disable_focused.png
index 8f72a5f..8f72a5f 100644
--- a/core/res/res/drawable/btn_plus_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_plus_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_plus_pressed.png b/core/res/res/drawable-mdpi/btn_plus_pressed.png
index 1fb8413..1fb8413 100644
--- a/core/res/res/drawable/btn_plus_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_plus_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_plus_selected.png b/core/res/res/drawable-mdpi/btn_plus_selected.png
index 47fe9bf..47fe9bf 100644
--- a/core/res/res/drawable/btn_plus_selected.png
+++ b/core/res/res/drawable-mdpi/btn_plus_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_label_background.9.png b/core/res/res/drawable-mdpi/btn_radio_label_background.9.png
index 16e8939..16e8939 100644
--- a/core/res/res/drawable/btn_radio_label_background.9.png
+++ b/core/res/res/drawable-mdpi/btn_radio_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_off.png b/core/res/res/drawable-mdpi/btn_radio_off.png
index 407632b..407632b 100644
--- a/core/res/res/drawable/btn_radio_off.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_off_pressed.png b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
index d6d8a9d..d6d8a9d 100644
--- a/core/res/res/drawable/btn_radio_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_off_selected.png b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
index 53f3e87..53f3e87 100644
--- a/core/res/res/drawable/btn_radio_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_on.png b/core/res/res/drawable-mdpi/btn_radio_on.png
index 25a3ccc..25a3ccc 100644
--- a/core/res/res/drawable/btn_radio_on.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_on_pressed.png b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
index c904a35..c904a35 100644
--- a/core/res/res/drawable/btn_radio_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_radio_on_selected.png b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
index 78e1fc0..78e1fc0 100644
--- a/core/res/res/drawable/btn_radio_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_radio_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_off_normal.png b/core/res/res/drawable-mdpi/btn_rating_star_off_normal.png
index a99441d..a99441d 100644
--- a/core/res/res/drawable/btn_rating_star_off_normal.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_off_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_off_pressed.png b/core/res/res/drawable-mdpi/btn_rating_star_off_pressed.png
index f47a454..f47a454 100644
--- a/core/res/res/drawable/btn_rating_star_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_off_selected.png b/core/res/res/drawable-mdpi/btn_rating_star_off_selected.png
index 5f71e08..5f71e08 100644
--- a/core/res/res/drawable/btn_rating_star_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_on_normal.png b/core/res/res/drawable-mdpi/btn_rating_star_on_normal.png
index b7825d3..b7825d3 100644
--- a/core/res/res/drawable/btn_rating_star_on_normal.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_on_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_on_pressed.png b/core/res/res/drawable-mdpi/btn_rating_star_on_pressed.png
index 4052445..4052445 100644
--- a/core/res/res/drawable/btn_rating_star_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_rating_star_on_selected.png b/core/res/res/drawable-mdpi/btn_rating_star_on_selected.png
index 5d139b6..5d139b6 100644
--- a/core/res/res/drawable/btn_rating_star_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_rating_star_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_default.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_default.9.png
index 7275231..7275231 100644
--- a/core/res/res/drawable/btn_search_dialog_default.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_pressed.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_pressed.9.png
index 50a9209..50a9209 100644
--- a/core/res/res/drawable/btn_search_dialog_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_selected.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_selected.9.png
index 14b774a..14b774a 100644
--- a/core/res/res/drawable/btn_search_dialog_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
index febf222..febf222 100644
--- a/core/res/res/drawable/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
index 70a200b..70a200b 100644
--- a/core/res/res/drawable/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
index 6f2989f..6f2989f 100644
--- a/core/res/res/drawable/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_square_overlay_disabled.png b/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png
index cf7e4ea..cf7e4ea 100644
--- a/core/res/res/drawable/btn_square_overlay_disabled.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/btn_square_overlay_disabled_focused.png b/core/res/res/drawable-mdpi/btn_square_overlay_disabled_focused.png
index 5aa1143..5aa1143 100644
--- a/core/res/res/drawable/btn_square_overlay_disabled_focused.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_disabled_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_square_overlay_normal.png b/core/res/res/drawable-mdpi/btn_square_overlay_normal.png
index 45fa4fe..45fa4fe 100644
--- a/core/res/res/drawable/btn_square_overlay_normal.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_square_overlay_pressed.png b/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png
index 952571b..952571b 100644
--- a/core/res/res/drawable/btn_square_overlay_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_square_overlay_selected.png b/core/res/res/drawable-mdpi/btn_square_overlay_selected.png
index ce4515e..ce4515e 100644
--- a/core/res/res/drawable/btn_square_overlay_selected.png
+++ b/core/res/res/drawable-mdpi/btn_square_overlay_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_off.png b/core/res/res/drawable-mdpi/btn_star_big_off.png
index 7e9342b..7e9342b 100755
--- a/core/res/res/drawable/btn_star_big_off.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_off_disable.png b/core/res/res/drawable-mdpi/btn_star_big_off_disable.png
index 066d920..066d920 100755
--- a/core/res/res/drawable/btn_star_big_off_disable.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_off_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_off_disable_focused.png b/core/res/res/drawable-mdpi/btn_star_big_off_disable_focused.png
index 1855d2c..1855d2c 100755
--- a/core/res/res/drawable/btn_star_big_off_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_off_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_off_pressed.png b/core/res/res/drawable-mdpi/btn_star_big_off_pressed.png
index f1b8912..f1b8912 100755
--- a/core/res/res/drawable/btn_star_big_off_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_off_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_off_selected.png b/core/res/res/drawable-mdpi/btn_star_big_off_selected.png
index 0be64c4..0be64c4 100755
--- a/core/res/res/drawable/btn_star_big_off_selected.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_off_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_on.png b/core/res/res/drawable-mdpi/btn_star_big_on.png
index a9bdb05..a9bdb05 100755
--- a/core/res/res/drawable/btn_star_big_on.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_on_disable.png b/core/res/res/drawable-mdpi/btn_star_big_on_disable.png
index 5e65a2f..5e65a2f 100755
--- a/core/res/res/drawable/btn_star_big_on_disable.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_on_disable.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_on_disable_focused.png b/core/res/res/drawable-mdpi/btn_star_big_on_disable_focused.png
index de57571..de57571 100755
--- a/core/res/res/drawable/btn_star_big_on_disable_focused.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_on_disable_focused.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_on_pressed.png b/core/res/res/drawable-mdpi/btn_star_big_on_pressed.png
index 159a84b..159a84b 100755
--- a/core/res/res/drawable/btn_star_big_on_pressed.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_on_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_big_on_selected.png b/core/res/res/drawable-mdpi/btn_star_big_on_selected.png
index 0592d51..0592d51 100755
--- a/core/res/res/drawable/btn_star_big_on_selected.png
+++ b/core/res/res/drawable-mdpi/btn_star_big_on_selected.png
Binary files differ
diff --git a/core/res/res/drawable/btn_star_label_background.9.png b/core/res/res/drawable-mdpi/btn_star_label_background.9.png
index e493171..e493171 100644
--- a/core/res/res/drawable/btn_star_label_background.9.png
+++ b/core/res/res/drawable-mdpi/btn_star_label_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_toggle_off.9.png b/core/res/res/drawable-mdpi/btn_toggle_off.9.png
index 26ee1c2..26ee1c2 100644
--- a/core/res/res/drawable/btn_toggle_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_off.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_toggle_on.9.png b/core/res/res/drawable-mdpi/btn_toggle_on.9.png
index 53e95af..53e95af 100644
--- a/core/res/res/drawable/btn_toggle_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_toggle_on.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_down_disabled.9.png b/core/res/res/drawable-mdpi/btn_zoom_down_disabled.9.png
index 7780bd7..7780bd7 100644
--- a/core/res/res/drawable/btn_zoom_down_disabled.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_down_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_down_disabled_focused.9.png b/core/res/res/drawable-mdpi/btn_zoom_down_disabled_focused.9.png
index 39131c5..39131c5 100644
--- a/core/res/res/drawable/btn_zoom_down_disabled_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_down_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_down_normal.9.png b/core/res/res/drawable-mdpi/btn_zoom_down_normal.9.png
index b589a84..b589a84 100644
--- a/core/res/res/drawable/btn_zoom_down_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_down_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_down_pressed.9.png b/core/res/res/drawable-mdpi/btn_zoom_down_pressed.9.png
index a8ca467..a8ca467 100644
--- a/core/res/res/drawable/btn_zoom_down_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_down_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_down_selected.9.png b/core/res/res/drawable-mdpi/btn_zoom_down_selected.9.png
index af551e7..af551e7 100644
--- a/core/res/res/drawable/btn_zoom_down_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_down_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_page_normal.png b/core/res/res/drawable-mdpi/btn_zoom_page_normal.png
index 839915b..839915b 100644
--- a/core/res/res/drawable/btn_zoom_page_normal.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_page_normal.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_page_press.png b/core/res/res/drawable-mdpi/btn_zoom_page_press.png
index e8af276..e8af276 100644
--- a/core/res/res/drawable/btn_zoom_page_press.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_page_press.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_up_disabled.9.png b/core/res/res/drawable-mdpi/btn_zoom_up_disabled.9.png
index 5203630..5203630 100644
--- a/core/res/res/drawable/btn_zoom_up_disabled.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_up_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_up_disabled_focused.9.png b/core/res/res/drawable-mdpi/btn_zoom_up_disabled_focused.9.png
index c72c9cd..c72c9cd 100644
--- a/core/res/res/drawable/btn_zoom_up_disabled_focused.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_up_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_up_normal.9.png b/core/res/res/drawable-mdpi/btn_zoom_up_normal.9.png
index 5027348..5027348 100644
--- a/core/res/res/drawable/btn_zoom_up_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_up_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_up_pressed.9.png b/core/res/res/drawable-mdpi/btn_zoom_up_pressed.9.png
index 1a93e3a..1a93e3a 100644
--- a/core/res/res/drawable/btn_zoom_up_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_up_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_zoom_up_selected.9.png b/core/res/res/drawable-mdpi/btn_zoom_up_selected.9.png
index 26aafee..26aafee 100644
--- a/core/res/res/drawable/btn_zoom_up_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_zoom_up_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/button_onoff_indicator_off.png b/core/res/res/drawable-mdpi/button_onoff_indicator_off.png
index 91e7244..91e7244 100644
--- a/core/res/res/drawable/button_onoff_indicator_off.png
+++ b/core/res/res/drawable-mdpi/button_onoff_indicator_off.png
Binary files differ
diff --git a/core/res/res/drawable/button_onoff_indicator_on.png b/core/res/res/drawable-mdpi/button_onoff_indicator_on.png
index 361364b..361364b 100644
--- a/core/res/res/drawable/button_onoff_indicator_on.png
+++ b/core/res/res/drawable-mdpi/button_onoff_indicator_on.png
Binary files differ
diff --git a/core/res/res/drawable/call_contact.png b/core/res/res/drawable-mdpi/call_contact.png
index 1abeb5d..1abeb5d 100644
--- a/core/res/res/drawable/call_contact.png
+++ b/core/res/res/drawable-mdpi/call_contact.png
Binary files differ
diff --git a/core/res/res/drawable/checkbox_off_background.png b/core/res/res/drawable-mdpi/checkbox_off_background.png
index 6b2124f..6b2124f 100644
--- a/core/res/res/drawable/checkbox_off_background.png
+++ b/core/res/res/drawable-mdpi/checkbox_off_background.png
Binary files differ
diff --git a/core/res/res/drawable/checkbox_on_background.png b/core/res/res/drawable-mdpi/checkbox_on_background.png
index 56495fc..56495fc 100644
--- a/core/res/res/drawable/checkbox_on_background.png
+++ b/core/res/res/drawable-mdpi/checkbox_on_background.png
Binary files differ
diff --git a/core/res/res/drawable/clock_dial.png b/core/res/res/drawable-mdpi/clock_dial.png
index 82f73fe..82f73fe 100644
--- a/core/res/res/drawable/clock_dial.png
+++ b/core/res/res/drawable-mdpi/clock_dial.png
Binary files differ
diff --git a/core/res/res/drawable/clock_hand_hour.png b/core/res/res/drawable-mdpi/clock_hand_hour.png
index 1f0aec8..1f0aec8 100644
--- a/core/res/res/drawable/clock_hand_hour.png
+++ b/core/res/res/drawable-mdpi/clock_hand_hour.png
Binary files differ
diff --git a/core/res/res/drawable/clock_hand_minute.png b/core/res/res/drawable-mdpi/clock_hand_minute.png
index 6cd8a4b..6cd8a4b 100644
--- a/core/res/res/drawable/clock_hand_minute.png
+++ b/core/res/res/drawable-mdpi/clock_hand_minute.png
Binary files differ
diff --git a/core/res/res/drawable/code_lock_bottom.9.png b/core/res/res/drawable-mdpi/code_lock_bottom.9.png
index 812cf00..812cf00 100644
--- a/core/res/res/drawable/code_lock_bottom.9.png
+++ b/core/res/res/drawable-mdpi/code_lock_bottom.9.png
Binary files differ
diff --git a/core/res/res/drawable/code_lock_left.9.png b/core/res/res/drawable-mdpi/code_lock_left.9.png
index 215dcc8..215dcc8 100644
--- a/core/res/res/drawable/code_lock_left.9.png
+++ b/core/res/res/drawable-mdpi/code_lock_left.9.png
Binary files differ
diff --git a/core/res/res/drawable/code_lock_top.9.png b/core/res/res/drawable-mdpi/code_lock_top.9.png
index 2b75a7c..2b75a7c 100644
--- a/core/res/res/drawable/code_lock_top.9.png
+++ b/core/res/res/drawable-mdpi/code_lock_top.9.png
Binary files differ
diff --git a/core/res/res/drawable/compass_arrow.png b/core/res/res/drawable-mdpi/compass_arrow.png
index 5a4d8c1..5a4d8c1 100644
--- a/core/res/res/drawable/compass_arrow.png
+++ b/core/res/res/drawable-mdpi/compass_arrow.png
Binary files differ
diff --git a/core/res/res/drawable/compass_base.png b/core/res/res/drawable-mdpi/compass_base.png
index 3d694f0..3d694f0 100644
--- a/core/res/res/drawable/compass_base.png
+++ b/core/res/res/drawable-mdpi/compass_base.png
Binary files differ
diff --git a/core/res/res/drawable/create_contact.png b/core/res/res/drawable-mdpi/create_contact.png
index 5c5718b..5c5718b 100644
--- a/core/res/res/drawable/create_contact.png
+++ b/core/res/res/drawable-mdpi/create_contact.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dark_header.9.png b/core/res/res/drawable-mdpi/dark_header.9.png
new file mode 100644
index 0000000..7242b61
--- /dev/null
+++ b/core/res/res/drawable-mdpi/dark_header.9.png
Binary files differ
diff --git a/core/res/res/drawable/dialog_divider_horizontal_light.9.png b/core/res/res/drawable-mdpi/dialog_divider_horizontal_light.9.png
index b69619b..b69619b 100755
--- a/core/res/res/drawable/dialog_divider_horizontal_light.9.png
+++ b/core/res/res/drawable-mdpi/dialog_divider_horizontal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
new file mode 100644
index 0000000..395227a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
new file mode 100644
index 0000000..5c537ee
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
new file mode 100644
index 0000000..548d0bd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
new file mode 100644
index 0000000..8f35315
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable/divider_horizontal_dim_dark.9.png b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
index 08838ca..08838ca 100644
--- a/core/res/res/drawable/divider_horizontal_dim_dark.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_dim_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/divider_horizontal_textfield.9.png b/core/res/res/drawable-mdpi/divider_horizontal_textfield.9.png
index 43eb51d..43eb51d 100644
--- a/core/res/res/drawable/divider_horizontal_textfield.9.png
+++ b/core/res/res/drawable-mdpi/divider_horizontal_textfield.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/divider_vertical_bright.9.png b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
new file mode 100644
index 0000000..395227a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/divider_vertical_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable/editbox_background_focus_yellow.9.png b/core/res/res/drawable-mdpi/editbox_background_focus_yellow.9.png
index faf52ed..faf52ed 100644
--- a/core/res/res/drawable/editbox_background_focus_yellow.9.png
+++ b/core/res/res/drawable-mdpi/editbox_background_focus_yellow.9.png
Binary files differ
diff --git a/core/res/res/drawable/editbox_background_normal.9.png b/core/res/res/drawable-mdpi/editbox_background_normal.9.png
index 9b8be77..9b8be77 100644
--- a/core/res/res/drawable/editbox_background_normal.9.png
+++ b/core/res/res/drawable-mdpi/editbox_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/editbox_dropdown_background.9.png b/core/res/res/drawable-mdpi/editbox_dropdown_background.9.png
index ed1bc29..ed1bc29 100644
--- a/core/res/res/drawable/editbox_dropdown_background.9.png
+++ b/core/res/res/drawable-mdpi/editbox_dropdown_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/editbox_dropdown_background_dark.9.png b/core/res/res/drawable-mdpi/editbox_dropdown_background_dark.9.png
index 88c1d9d..88c1d9d 100644
--- a/core/res/res/drawable/editbox_dropdown_background_dark.9.png
+++ b/core/res/res/drawable-mdpi/editbox_dropdown_background_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_angel.png b/core/res/res/drawable-mdpi/emo_im_angel.png
index c34dfa6..c34dfa6 100644
--- a/core/res/res/drawable/emo_im_angel.png
+++ b/core/res/res/drawable-mdpi/emo_im_angel.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_cool.png b/core/res/res/drawable-mdpi/emo_im_cool.png
index d8eeb34..d8eeb34 100644
--- a/core/res/res/drawable/emo_im_cool.png
+++ b/core/res/res/drawable-mdpi/emo_im_cool.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_crying.png b/core/res/res/drawable-mdpi/emo_im_crying.png
index 1cafdb3..1cafdb3 100644
--- a/core/res/res/drawable/emo_im_crying.png
+++ b/core/res/res/drawable-mdpi/emo_im_crying.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_foot_in_mouth.png b/core/res/res/drawable-mdpi/emo_im_foot_in_mouth.png
index 09d1fba..09d1fba 100644
--- a/core/res/res/drawable/emo_im_foot_in_mouth.png
+++ b/core/res/res/drawable-mdpi/emo_im_foot_in_mouth.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_happy.png b/core/res/res/drawable-mdpi/emo_im_happy.png
index b86602a..b86602a 100644
--- a/core/res/res/drawable/emo_im_happy.png
+++ b/core/res/res/drawable-mdpi/emo_im_happy.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_kissing.png b/core/res/res/drawable-mdpi/emo_im_kissing.png
index 56378f6..56378f6 100644
--- a/core/res/res/drawable/emo_im_kissing.png
+++ b/core/res/res/drawable-mdpi/emo_im_kissing.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_laughing.png b/core/res/res/drawable-mdpi/emo_im_laughing.png
index 980bf28..980bf28 100644
--- a/core/res/res/drawable/emo_im_laughing.png
+++ b/core/res/res/drawable-mdpi/emo_im_laughing.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_lips_are_sealed.png b/core/res/res/drawable-mdpi/emo_im_lips_are_sealed.png
index f2de993..f2de993 100644
--- a/core/res/res/drawable/emo_im_lips_are_sealed.png
+++ b/core/res/res/drawable-mdpi/emo_im_lips_are_sealed.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_money_mouth.png b/core/res/res/drawable-mdpi/emo_im_money_mouth.png
index 08c53fd..08c53fd 100644
--- a/core/res/res/drawable/emo_im_money_mouth.png
+++ b/core/res/res/drawable-mdpi/emo_im_money_mouth.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_sad.png b/core/res/res/drawable-mdpi/emo_im_sad.png
index 31c08d0..31c08d0 100644
--- a/core/res/res/drawable/emo_im_sad.png
+++ b/core/res/res/drawable-mdpi/emo_im_sad.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_surprised.png b/core/res/res/drawable-mdpi/emo_im_surprised.png
index abe8c7a..abe8c7a 100644
--- a/core/res/res/drawable/emo_im_surprised.png
+++ b/core/res/res/drawable-mdpi/emo_im_surprised.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_tongue_sticking_out.png b/core/res/res/drawable-mdpi/emo_im_tongue_sticking_out.png
index 6f0f47b..6f0f47b 100644
--- a/core/res/res/drawable/emo_im_tongue_sticking_out.png
+++ b/core/res/res/drawable-mdpi/emo_im_tongue_sticking_out.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_undecided.png b/core/res/res/drawable-mdpi/emo_im_undecided.png
index eb4f8c5..eb4f8c5 100644
--- a/core/res/res/drawable/emo_im_undecided.png
+++ b/core/res/res/drawable-mdpi/emo_im_undecided.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_winking.png b/core/res/res/drawable-mdpi/emo_im_winking.png
index 568562a..568562a 100644
--- a/core/res/res/drawable/emo_im_winking.png
+++ b/core/res/res/drawable-mdpi/emo_im_winking.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_wtf.png b/core/res/res/drawable-mdpi/emo_im_wtf.png
index 41dd47f..41dd47f 100644
--- a/core/res/res/drawable/emo_im_wtf.png
+++ b/core/res/res/drawable-mdpi/emo_im_wtf.png
Binary files differ
diff --git a/core/res/res/drawable/emo_im_yelling.png b/core/res/res/drawable-mdpi/emo_im_yelling.png
index c3c8612..c3c8612 100644
--- a/core/res/res/drawable/emo_im_yelling.png
+++ b/core/res/res/drawable-mdpi/emo_im_yelling.png
Binary files differ
diff --git a/core/res/res/drawable/expander_ic_maximized.9.png b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
index 465cabd..465cabd 100644
--- a/core/res/res/drawable/expander_ic_maximized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_maximized.9.png
Binary files differ
diff --git a/core/res/res/drawable/expander_ic_minimized.9.png b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
index 9967ecb..9967ecb 100644
--- a/core/res/res/drawable/expander_ic_minimized.9.png
+++ b/core/res/res/drawable-mdpi/expander_ic_minimized.9.png
Binary files differ
diff --git a/core/res/res/drawable/focused_application_background_static.png b/core/res/res/drawable-mdpi/focused_application_background_static.png
index fd18d30..fd18d30 100644
--- a/core/res/res/drawable/focused_application_background_static.png
+++ b/core/res/res/drawable-mdpi/focused_application_background_static.png
Binary files differ
diff --git a/core/res/res/drawable/frame_gallery_thumb.9.png b/core/res/res/drawable-mdpi/frame_gallery_thumb.9.png
index 804f6f3..804f6f3 100755
--- a/core/res/res/drawable/frame_gallery_thumb.9.png
+++ b/core/res/res/drawable-mdpi/frame_gallery_thumb.9.png
Binary files differ
diff --git a/core/res/res/drawable/frame_gallery_thumb_pressed.9.png b/core/res/res/drawable-mdpi/frame_gallery_thumb_pressed.9.png
index e1ffa06..e1ffa06 100755
--- a/core/res/res/drawable/frame_gallery_thumb_pressed.9.png
+++ b/core/res/res/drawable-mdpi/frame_gallery_thumb_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/frame_gallery_thumb_selected.9.png b/core/res/res/drawable-mdpi/frame_gallery_thumb_selected.9.png
index 8bae932..8bae932 100755
--- a/core/res/res/drawable/frame_gallery_thumb_selected.9.png
+++ b/core/res/res/drawable-mdpi/frame_gallery_thumb_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/gallery_selected_default.9.png b/core/res/res/drawable-mdpi/gallery_selected_default.9.png
index 22122b2..22122b2 100755
--- a/core/res/res/drawable/gallery_selected_default.9.png
+++ b/core/res/res/drawable-mdpi/gallery_selected_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/gallery_selected_focused.9.png b/core/res/res/drawable-mdpi/gallery_selected_focused.9.png
index 1332745..1332745 100755
--- a/core/res/res/drawable/gallery_selected_focused.9.png
+++ b/core/res/res/drawable-mdpi/gallery_selected_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/gallery_selected_pressed.9.png b/core/res/res/drawable-mdpi/gallery_selected_pressed.9.png
index 306e543..306e543 100755
--- a/core/res/res/drawable/gallery_selected_pressed.9.png
+++ b/core/res/res/drawable-mdpi/gallery_selected_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/gallery_unselected_default.9.png b/core/res/res/drawable-mdpi/gallery_unselected_default.9.png
index 0df06fa..0df06fa 100755
--- a/core/res/res/drawable/gallery_unselected_default.9.png
+++ b/core/res/res/drawable-mdpi/gallery_unselected_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/gallery_unselected_pressed.9.png b/core/res/res/drawable-mdpi/gallery_unselected_pressed.9.png
index 4b25c3f..4b25c3f 100644
--- a/core/res/res/drawable/gallery_unselected_pressed.9.png
+++ b/core/res/res/drawable-mdpi/gallery_unselected_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/grid_selector_background_focus.9.png b/core/res/res/drawable-mdpi/grid_selector_background_focus.9.png
index 2e28232..2e28232 100644
--- a/core/res/res/drawable/grid_selector_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/grid_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable/grid_selector_background_pressed.9.png b/core/res/res/drawable-mdpi/grid_selector_background_pressed.9.png
index e20f091..e20f091 100644
--- a/core/res/res/drawable/grid_selector_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/grid_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/highlight_disabled.9.png b/core/res/res/drawable-mdpi/highlight_disabled.9.png
index 1393262..1393262 100644
--- a/core/res/res/drawable/highlight_disabled.9.png
+++ b/core/res/res/drawable-mdpi/highlight_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/highlight_pressed.9.png b/core/res/res/drawable-mdpi/highlight_pressed.9.png
index 9bd2b50..9bd2b50 100644
--- a/core/res/res/drawable/highlight_pressed.9.png
+++ b/core/res/res/drawable-mdpi/highlight_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/highlight_selected.9.png b/core/res/res/drawable-mdpi/highlight_selected.9.png
index ecf0cad..ecf0cad 100644
--- a/core/res/res/drawable/highlight_selected.9.png
+++ b/core/res/res/drawable-mdpi/highlight_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_round_more_disabled.png b/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png
index 1ab98c9..1ab98c9 100644
--- a/core/res/res/drawable/ic_btn_round_more_disabled.png
+++ b/core/res/res/drawable-mdpi/ic_btn_round_more_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_round_more_normal.png b/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png
index ebdc55c..ebdc55c 100644
--- a/core/res/res/drawable/ic_btn_round_more_normal.png
+++ b/core/res/res/drawable-mdpi/ic_btn_round_more_normal.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_search.png b/core/res/res/drawable-mdpi/ic_btn_search.png
index 3f8913e..3f8913e 100644
--- a/core/res/res/drawable/ic_btn_search.png
+++ b/core/res/res/drawable-mdpi/ic_btn_search.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_speak_now.png b/core/res/res/drawable-mdpi/ic_btn_speak_now.png
index 83ee68b..83ee68b 100644
--- a/core/res/res/drawable/ic_btn_speak_now.png
+++ b/core/res/res/drawable-mdpi/ic_btn_speak_now.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_disabled.png b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_disabled.png
index 914662d..914662d 100644
--- a/core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_disabled.png
+++ b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_normal.png b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_normal.png
index 3b67c6d..3b67c6d 100644
--- a/core/res/res/drawable/ic_btn_square_browser_zoom_fit_page_normal.png
+++ b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_fit_page_normal.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_disabled.png b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_disabled.png
index 859900a..859900a 100644
--- a/core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_disabled.png
+++ b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_disabled.png
Binary files differ
diff --git a/core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_normal.png b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_normal.png
index 4e8ff35..4e8ff35 100644
--- a/core/res/res/drawable/ic_btn_square_browser_zoom_page_overview_normal.png
+++ b/core/res/res/drawable-mdpi/ic_btn_square_browser_zoom_page_overview_normal.png
Binary files differ
diff --git a/core/res/res/drawable/ic_bullet_key_permission.png b/core/res/res/drawable-mdpi/ic_bullet_key_permission.png
index ccb010f..ccb010f 100644
--- a/core/res/res/drawable/ic_bullet_key_permission.png
+++ b/core/res/res/drawable-mdpi/ic_bullet_key_permission.png
Binary files differ
diff --git a/core/res/res/drawable/ic_contact_picture.png b/core/res/res/drawable-mdpi/ic_contact_picture.png
index 3a338e8..3a338e8 100644
--- a/core/res/res/drawable/ic_contact_picture.png
+++ b/core/res/res/drawable-mdpi/ic_contact_picture.png
Binary files differ
diff --git a/core/res/res/drawable/ic_delete.png b/core/res/res/drawable-mdpi/ic_delete.png
index f074db3..f074db3 100644
--- a/core/res/res/drawable/ic_delete.png
+++ b/core/res/res/drawable-mdpi/ic_delete.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_alert.png b/core/res/res/drawable-mdpi/ic_dialog_alert.png
index 0a7de04..0a7de04 100644
--- a/core/res/res/drawable/ic_dialog_alert.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_alert.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_dialer.png b/core/res/res/drawable-mdpi/ic_dialog_dialer.png
index f0c1838..f0c1838 100644
--- a/core/res/res/drawable/ic_dialog_dialer.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_dialer.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_email.png b/core/res/res/drawable-mdpi/ic_dialog_email.png
index 20ebb13..20ebb13 100644
--- a/core/res/res/drawable/ic_dialog_email.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_email.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_info.png b/core/res/res/drawable-mdpi/ic_dialog_info.png
index e8b0229..e8b0229 100755
--- a/core/res/res/drawable/ic_dialog_info.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_info.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_map.png b/core/res/res/drawable-mdpi/ic_dialog_map.png
index b126354..b126354 100644
--- a/core/res/res/drawable/ic_dialog_map.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_map.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_menu_generic.png b/core/res/res/drawable-mdpi/ic_dialog_menu_generic.png
index de07bda..de07bda 100755
--- a/core/res/res/drawable/ic_dialog_menu_generic.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_menu_generic.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_time.png b/core/res/res/drawable-mdpi/ic_dialog_time.png
index dffec29..dffec29 100755
--- a/core/res/res/drawable/ic_dialog_time.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_time.png
Binary files differ
diff --git a/core/res/res/drawable/ic_dialog_usb.png b/core/res/res/drawable-mdpi/ic_dialog_usb.png
index fbc8a9d..fbc8a9d 100644
--- a/core/res/res/drawable/ic_dialog_usb.png
+++ b/core/res/res/drawable-mdpi/ic_dialog_usb.png
Binary files differ
diff --git a/core/res/res/drawable/ic_emergency.png b/core/res/res/drawable-mdpi/ic_emergency.png
index 45d0f21..45d0f21 100755
--- a/core/res/res/drawable/ic_emergency.png
+++ b/core/res/res/drawable-mdpi/ic_emergency.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_add.png b/core/res/res/drawable-mdpi/ic_input_add.png
index 00770f8..00770f8 100644
--- a/core/res/res/drawable/ic_input_add.png
+++ b/core/res/res/drawable-mdpi/ic_input_add.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_delete.png b/core/res/res/drawable-mdpi/ic_input_delete.png
index ee4c911..ee4c911 100644
--- a/core/res/res/drawable/ic_input_delete.png
+++ b/core/res/res/drawable-mdpi/ic_input_delete.png
Binary files differ
diff --git a/core/res/res/drawable/ic_input_get.png b/core/res/res/drawable-mdpi/ic_input_get.png
index 2f2cfcf..2f2cfcf 100644
--- a/core/res/res/drawable/ic_input_get.png
+++ b/core/res/res/drawable-mdpi/ic_input_get.png
Binary files differ
diff --git a/core/res/res/drawable/ic_launcher_android.png b/core/res/res/drawable-mdpi/ic_launcher_android.png
index 855484a..855484a 100644
--- a/core/res/res/drawable/ic_launcher_android.png
+++ b/core/res/res/drawable-mdpi/ic_launcher_android.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_airplane_mode.png b/core/res/res/drawable-mdpi/ic_lock_airplane_mode.png
index caafcb2..caafcb2 100755
--- a/core/res/res/drawable/ic_lock_airplane_mode.png
+++ b/core/res/res/drawable-mdpi/ic_lock_airplane_mode.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_airplane_mode_off.png b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off.png
index cb2cbdf..cb2cbdf 100755
--- a/core/res/res/drawable/ic_lock_airplane_mode_off.png
+++ b/core/res/res/drawable-mdpi/ic_lock_airplane_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
index 8c8899f..8c8899f 100644
--- a/core/res/res/drawable/ic_lock_idle_alarm.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_idle_charging.png b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
index 20d6320..20d6320 100755
--- a/core/res/res/drawable/ic_lock_idle_charging.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_idle_lock.png b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
index 0206aee..0206aee 100755
--- a/core/res/res/drawable/ic_lock_idle_lock.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_idle_low_battery.png b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
index bb96782..bb96782 100755
--- a/core/res/res/drawable/ic_lock_idle_low_battery.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_lock.png b/core/res/res/drawable-mdpi/ic_lock_lock.png
index b662b03..b662b03 100644
--- a/core/res/res/drawable/ic_lock_lock.png
+++ b/core/res/res/drawable-mdpi/ic_lock_lock.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_power_off.png b/core/res/res/drawable-mdpi/ic_lock_power_off.png
index 4405b43..4405b43 100644
--- a/core/res/res/drawable/ic_lock_power_off.png
+++ b/core/res/res/drawable-mdpi/ic_lock_power_off.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_silent_mode.png b/core/res/res/drawable-mdpi/ic_lock_silent_mode.png
index 439a6f5..439a6f5 100644
--- a/core/res/res/drawable/ic_lock_silent_mode.png
+++ b/core/res/res/drawable-mdpi/ic_lock_silent_mode.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lock_silent_mode_off.png b/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png
index fc7e960..fc7e960 100644
--- a/core/res/res/drawable/ic_lock_silent_mode_off.png
+++ b/core/res/res/drawable-mdpi/ic_lock_silent_mode_off.png
Binary files differ
diff --git a/core/res/res/drawable/ic_maps_indicator_current_position.png b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position.png
index 4e427d8..4e427d8 100644
--- a/core/res/res/drawable/ic_maps_indicator_current_position.png
+++ b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position.png
Binary files differ
diff --git a/core/res/res/drawable/ic_maps_indicator_current_position_anim1.png b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim1.png
index 47bb9fa..47bb9fa 100644
--- a/core/res/res/drawable/ic_maps_indicator_current_position_anim1.png
+++ b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim1.png
Binary files differ
diff --git a/core/res/res/drawable/ic_maps_indicator_current_position_anim2.png b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim2.png
index b1167bc..b1167bc 100644
--- a/core/res/res/drawable/ic_maps_indicator_current_position_anim2.png
+++ b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim2.png
Binary files differ
diff --git a/core/res/res/drawable/ic_maps_indicator_current_position_anim3.png b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim3.png
index f681a4c..f681a4c 100644
--- a/core/res/res/drawable/ic_maps_indicator_current_position_anim3.png
+++ b/core/res/res/drawable-mdpi/ic_maps_indicator_current_position_anim3.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_ff.png b/core/res/res/drawable-mdpi/ic_media_ff.png
index ce7e195..ce7e195 100755
--- a/core/res/res/drawable/ic_media_ff.png
+++ b/core/res/res/drawable-mdpi/ic_media_ff.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_next.png b/core/res/res/drawable-mdpi/ic_media_next.png
index 84f38e8..84f38e8 100755
--- a/core/res/res/drawable/ic_media_next.png
+++ b/core/res/res/drawable-mdpi/ic_media_next.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_pause.png b/core/res/res/drawable-mdpi/ic_media_pause.png
index 688118e..688118e 100755
--- a/core/res/res/drawable/ic_media_pause.png
+++ b/core/res/res/drawable-mdpi/ic_media_pause.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_play.png b/core/res/res/drawable-mdpi/ic_media_play.png
index 7aa7af8..7aa7af8 100755
--- a/core/res/res/drawable/ic_media_play.png
+++ b/core/res/res/drawable-mdpi/ic_media_play.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_previous.png b/core/res/res/drawable-mdpi/ic_media_previous.png
index 1bba544..1bba544 100755
--- a/core/res/res/drawable/ic_media_previous.png
+++ b/core/res/res/drawable-mdpi/ic_media_previous.png
Binary files differ
diff --git a/core/res/res/drawable/ic_media_rew.png b/core/res/res/drawable-mdpi/ic_media_rew.png
index 132df7f..132df7f 100755
--- a/core/res/res/drawable/ic_media_rew.png
+++ b/core/res/res/drawable-mdpi/ic_media_rew.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_account_list.png b/core/res/res/drawable-mdpi/ic_menu_account_list.png
index f0945b2..f0945b2 100644
--- a/core/res/res/drawable/ic_menu_account_list.png
+++ b/core/res/res/drawable-mdpi/ic_menu_account_list.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_add.png b/core/res/res/drawable-mdpi/ic_menu_add.png
index 6752bfd..6752bfd 100755
--- a/core/res/res/drawable/ic_menu_add.png
+++ b/core/res/res/drawable-mdpi/ic_menu_add.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_agenda.png b/core/res/res/drawable-mdpi/ic_menu_agenda.png
index 9f2c1dc..9f2c1dc 100755
--- a/core/res/res/drawable/ic_menu_agenda.png
+++ b/core/res/res/drawable-mdpi/ic_menu_agenda.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_allfriends.png b/core/res/res/drawable-mdpi/ic_menu_allfriends.png
index a5bd331..a5bd331 100755
--- a/core/res/res/drawable/ic_menu_allfriends.png
+++ b/core/res/res/drawable-mdpi/ic_menu_allfriends.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_always_landscape_portrait.png b/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png
index 68911c4..68911c4 100644
--- a/core/res/res/drawable/ic_menu_always_landscape_portrait.png
+++ b/core/res/res/drawable-mdpi/ic_menu_always_landscape_portrait.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_archive.png b/core/res/res/drawable-mdpi/ic_menu_archive.png
index a4599e3..a4599e3 100644
--- a/core/res/res/drawable/ic_menu_archive.png
+++ b/core/res/res/drawable-mdpi/ic_menu_archive.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_attachment.png b/core/res/res/drawable-mdpi/ic_menu_attachment.png
index 89d626f..89d626f 100644
--- a/core/res/res/drawable/ic_menu_attachment.png
+++ b/core/res/res/drawable-mdpi/ic_menu_attachment.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_back.png b/core/res/res/drawable-mdpi/ic_menu_back.png
index 5ce50eb..5ce50eb 100644
--- a/core/res/res/drawable/ic_menu_back.png
+++ b/core/res/res/drawable-mdpi/ic_menu_back.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_block.png b/core/res/res/drawable-mdpi/ic_menu_block.png
index 422eeb1..422eeb1 100644
--- a/core/res/res/drawable/ic_menu_block.png
+++ b/core/res/res/drawable-mdpi/ic_menu_block.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_blocked_user.png b/core/res/res/drawable-mdpi/ic_menu_blocked_user.png
index 5a5619b..5a5619b 100644
--- a/core/res/res/drawable/ic_menu_blocked_user.png
+++ b/core/res/res/drawable-mdpi/ic_menu_blocked_user.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_call.png b/core/res/res/drawable-mdpi/ic_menu_call.png
index a63f86b..a63f86b 100644
--- a/core/res/res/drawable/ic_menu_call.png
+++ b/core/res/res/drawable-mdpi/ic_menu_call.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_camera.png b/core/res/res/drawable-mdpi/ic_menu_camera.png
index cdf7ca3..cdf7ca3 100755
--- a/core/res/res/drawable/ic_menu_camera.png
+++ b/core/res/res/drawable-mdpi/ic_menu_camera.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_cc.png b/core/res/res/drawable-mdpi/ic_menu_cc.png
index 4876021..4876021 100644
--- a/core/res/res/drawable/ic_menu_cc.png
+++ b/core/res/res/drawable-mdpi/ic_menu_cc.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_chat_dashboard.png b/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png
index 37fd3cb..37fd3cb 100644
--- a/core/res/res/drawable/ic_menu_chat_dashboard.png
+++ b/core/res/res/drawable-mdpi/ic_menu_chat_dashboard.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_clear_playlist.png b/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png
index 750db62..750db62 100644
--- a/core/res/res/drawable/ic_menu_clear_playlist.png
+++ b/core/res/res/drawable-mdpi/ic_menu_clear_playlist.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_close_clear_cancel.png b/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png
index 78222ea..78222ea 100644
--- a/core/res/res/drawable/ic_menu_close_clear_cancel.png
+++ b/core/res/res/drawable-mdpi/ic_menu_close_clear_cancel.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_compass.png b/core/res/res/drawable-mdpi/ic_menu_compass.png
index 7717dde..7717dde 100644
--- a/core/res/res/drawable/ic_menu_compass.png
+++ b/core/res/res/drawable-mdpi/ic_menu_compass.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_compose.png b/core/res/res/drawable-mdpi/ic_menu_compose.png
index 1b4733e..1b4733e 100644
--- a/core/res/res/drawable/ic_menu_compose.png
+++ b/core/res/res/drawable-mdpi/ic_menu_compose.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_crop.png b/core/res/res/drawable-mdpi/ic_menu_crop.png
index c0df996..c0df996 100755
--- a/core/res/res/drawable/ic_menu_crop.png
+++ b/core/res/res/drawable-mdpi/ic_menu_crop.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_day.png b/core/res/res/drawable-mdpi/ic_menu_day.png
index db5d3a4..db5d3a4 100755
--- a/core/res/res/drawable/ic_menu_day.png
+++ b/core/res/res/drawable-mdpi/ic_menu_day.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_delete.png b/core/res/res/drawable-mdpi/ic_menu_delete.png
index 7d95494..7d95494 100755
--- a/core/res/res/drawable/ic_menu_delete.png
+++ b/core/res/res/drawable-mdpi/ic_menu_delete.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_directions.png b/core/res/res/drawable-mdpi/ic_menu_directions.png
index 00a288f..00a288f 100755
--- a/core/res/res/drawable/ic_menu_directions.png
+++ b/core/res/res/drawable-mdpi/ic_menu_directions.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_edit.png b/core/res/res/drawable-mdpi/ic_menu_edit.png
index 41a9c2e..41a9c2e 100755
--- a/core/res/res/drawable/ic_menu_edit.png
+++ b/core/res/res/drawable-mdpi/ic_menu_edit.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_emoticons.png b/core/res/res/drawable-mdpi/ic_menu_emoticons.png
index e8c4e47..e8c4e47 100644
--- a/core/res/res/drawable/ic_menu_emoticons.png
+++ b/core/res/res/drawable-mdpi/ic_menu_emoticons.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_end_conversation.png b/core/res/res/drawable-mdpi/ic_menu_end_conversation.png
index 0ea0fcb..0ea0fcb 100644
--- a/core/res/res/drawable/ic_menu_end_conversation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_end_conversation.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_forward.png b/core/res/res/drawable-mdpi/ic_menu_forward.png
index 0936fac..0936fac 100644
--- a/core/res/res/drawable/ic_menu_forward.png
+++ b/core/res/res/drawable-mdpi/ic_menu_forward.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_friendslist.png b/core/res/res/drawable-mdpi/ic_menu_friendslist.png
index 8ec6b1a..8ec6b1a 100644
--- a/core/res/res/drawable/ic_menu_friendslist.png
+++ b/core/res/res/drawable-mdpi/ic_menu_friendslist.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_gallery.png b/core/res/res/drawable-mdpi/ic_menu_gallery.png
index f61bbd8..f61bbd8 100755
--- a/core/res/res/drawable/ic_menu_gallery.png
+++ b/core/res/res/drawable-mdpi/ic_menu_gallery.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_goto.png b/core/res/res/drawable-mdpi/ic_menu_goto.png
index 40183eb..40183eb 100644
--- a/core/res/res/drawable/ic_menu_goto.png
+++ b/core/res/res/drawable-mdpi/ic_menu_goto.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_help.png b/core/res/res/drawable-mdpi/ic_menu_help.png
index 7c55dfd..7c55dfd 100644
--- a/core/res/res/drawable/ic_menu_help.png
+++ b/core/res/res/drawable-mdpi/ic_menu_help.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_home.png b/core/res/res/drawable-mdpi/ic_menu_home.png
index 34943f6..34943f6 100644
--- a/core/res/res/drawable/ic_menu_home.png
+++ b/core/res/res/drawable-mdpi/ic_menu_home.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_info_details.png b/core/res/res/drawable-mdpi/ic_menu_info_details.png
index 1786d1e..1786d1e 100755
--- a/core/res/res/drawable/ic_menu_info_details.png
+++ b/core/res/res/drawable-mdpi/ic_menu_info_details.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_invite.png b/core/res/res/drawable-mdpi/ic_menu_invite.png
index 7577e6d..7577e6d 100644
--- a/core/res/res/drawable/ic_menu_invite.png
+++ b/core/res/res/drawable-mdpi/ic_menu_invite.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_login.png b/core/res/res/drawable-mdpi/ic_menu_login.png
index 2b856bc..2b856bc 100644
--- a/core/res/res/drawable/ic_menu_login.png
+++ b/core/res/res/drawable-mdpi/ic_menu_login.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_manage.png b/core/res/res/drawable-mdpi/ic_menu_manage.png
index f155bbc..f155bbc 100755
--- a/core/res/res/drawable/ic_menu_manage.png
+++ b/core/res/res/drawable-mdpi/ic_menu_manage.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_mapmode.png b/core/res/res/drawable-mdpi/ic_menu_mapmode.png
index d85cab5..d85cab5 100644
--- a/core/res/res/drawable/ic_menu_mapmode.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mapmode.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_mark.png b/core/res/res/drawable-mdpi/ic_menu_mark.png
index 5e95da7..5e95da7 100644
--- a/core/res/res/drawable/ic_menu_mark.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mark.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_month.png b/core/res/res/drawable-mdpi/ic_menu_month.png
index bf6cb89..bf6cb89 100755
--- a/core/res/res/drawable/ic_menu_month.png
+++ b/core/res/res/drawable-mdpi/ic_menu_month.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_more.png b/core/res/res/drawable-mdpi/ic_menu_more.png
index b9fc5fa..b9fc5fa 100644
--- a/core/res/res/drawable/ic_menu_more.png
+++ b/core/res/res/drawable-mdpi/ic_menu_more.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_my_calendar.png b/core/res/res/drawable-mdpi/ic_menu_my_calendar.png
index 0c88fd3..0c88fd3 100755
--- a/core/res/res/drawable/ic_menu_my_calendar.png
+++ b/core/res/res/drawable-mdpi/ic_menu_my_calendar.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_mylocation.png b/core/res/res/drawable-mdpi/ic_menu_mylocation.png
index fdbd5ca..fdbd5ca 100755
--- a/core/res/res/drawable/ic_menu_mylocation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_mylocation.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_myplaces.png b/core/res/res/drawable-mdpi/ic_menu_myplaces.png
index 06f11ba..06f11ba 100644
--- a/core/res/res/drawable/ic_menu_myplaces.png
+++ b/core/res/res/drawable-mdpi/ic_menu_myplaces.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_notifications.png b/core/res/res/drawable-mdpi/ic_menu_notifications.png
index 866d4e0..866d4e0 100644
--- a/core/res/res/drawable/ic_menu_notifications.png
+++ b/core/res/res/drawable-mdpi/ic_menu_notifications.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_play_clip.png b/core/res/res/drawable-mdpi/ic_menu_play_clip.png
index 4669947..4669947 100644
--- a/core/res/res/drawable/ic_menu_play_clip.png
+++ b/core/res/res/drawable-mdpi/ic_menu_play_clip.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_preferences.png b/core/res/res/drawable-mdpi/ic_menu_preferences.png
index b8e7141..b8e7141 100644
--- a/core/res/res/drawable/ic_menu_preferences.png
+++ b/core/res/res/drawable-mdpi/ic_menu_preferences.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_recent_history.png b/core/res/res/drawable-mdpi/ic_menu_recent_history.png
index 4ccae5d..4ccae5d 100644
--- a/core/res/res/drawable/ic_menu_recent_history.png
+++ b/core/res/res/drawable-mdpi/ic_menu_recent_history.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_refresh.png b/core/res/res/drawable-mdpi/ic_menu_refresh.png
index 77d70dd..77d70dd 100644
--- a/core/res/res/drawable/ic_menu_refresh.png
+++ b/core/res/res/drawable-mdpi/ic_menu_refresh.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_report_image.png b/core/res/res/drawable-mdpi/ic_menu_report_image.png
index 393d727..393d727 100644
--- a/core/res/res/drawable/ic_menu_report_image.png
+++ b/core/res/res/drawable-mdpi/ic_menu_report_image.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_revert.png b/core/res/res/drawable-mdpi/ic_menu_revert.png
index e7e04f5..e7e04f5 100644
--- a/core/res/res/drawable/ic_menu_revert.png
+++ b/core/res/res/drawable-mdpi/ic_menu_revert.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_rotate.png b/core/res/res/drawable-mdpi/ic_menu_rotate.png
index 27368b2..27368b2 100755
--- a/core/res/res/drawable/ic_menu_rotate.png
+++ b/core/res/res/drawable-mdpi/ic_menu_rotate.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_save.png b/core/res/res/drawable-mdpi/ic_menu_save.png
index 36d50b3..36d50b3 100644
--- a/core/res/res/drawable/ic_menu_save.png
+++ b/core/res/res/drawable-mdpi/ic_menu_save.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_search.png b/core/res/res/drawable-mdpi/ic_menu_search.png
index 94446db..94446db 100755
--- a/core/res/res/drawable/ic_menu_search.png
+++ b/core/res/res/drawable-mdpi/ic_menu_search.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_send.png b/core/res/res/drawable-mdpi/ic_menu_send.png
index 74c096d..74c096d 100755
--- a/core/res/res/drawable/ic_menu_send.png
+++ b/core/res/res/drawable-mdpi/ic_menu_send.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_set_as.png b/core/res/res/drawable-mdpi/ic_menu_set_as.png
index cb9dc49..cb9dc49 100755
--- a/core/res/res/drawable/ic_menu_set_as.png
+++ b/core/res/res/drawable-mdpi/ic_menu_set_as.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_share.png b/core/res/res/drawable-mdpi/ic_menu_share.png
index 44db9b1..44db9b1 100755
--- a/core/res/res/drawable/ic_menu_share.png
+++ b/core/res/res/drawable-mdpi/ic_menu_share.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_slideshow.png b/core/res/res/drawable-mdpi/ic_menu_slideshow.png
index 38dd8f0..38dd8f0 100644
--- a/core/res/res/drawable/ic_menu_slideshow.png
+++ b/core/res/res/drawable-mdpi/ic_menu_slideshow.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_sort_alphabetically.png b/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png
index 2583eb8..2583eb8 100755
--- a/core/res/res/drawable/ic_menu_sort_alphabetically.png
+++ b/core/res/res/drawable-mdpi/ic_menu_sort_alphabetically.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_sort_by_size.png b/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png
index 65e2786..65e2786 100755
--- a/core/res/res/drawable/ic_menu_sort_by_size.png
+++ b/core/res/res/drawable-mdpi/ic_menu_sort_by_size.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_star.png b/core/res/res/drawable-mdpi/ic_menu_star.png
index 527d74a..527d74a 100755
--- a/core/res/res/drawable/ic_menu_star.png
+++ b/core/res/res/drawable-mdpi/ic_menu_star.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_start_conversation.png b/core/res/res/drawable-mdpi/ic_menu_start_conversation.png
index aadcc2f..aadcc2f 100644
--- a/core/res/res/drawable/ic_menu_start_conversation.png
+++ b/core/res/res/drawable-mdpi/ic_menu_start_conversation.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_stop.png b/core/res/res/drawable-mdpi/ic_menu_stop.png
index 4fc825c..4fc825c 100644
--- a/core/res/res/drawable/ic_menu_stop.png
+++ b/core/res/res/drawable-mdpi/ic_menu_stop.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_today.png b/core/res/res/drawable-mdpi/ic_menu_today.png
index c63b6af..c63b6af 100755
--- a/core/res/res/drawable/ic_menu_today.png
+++ b/core/res/res/drawable-mdpi/ic_menu_today.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_upload.png b/core/res/res/drawable-mdpi/ic_menu_upload.png
index 1c0dd3f..1c0dd3f 100755
--- a/core/res/res/drawable/ic_menu_upload.png
+++ b/core/res/res/drawable-mdpi/ic_menu_upload.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_upload_you_tube.png b/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png
index 0095564..0095564 100755
--- a/core/res/res/drawable/ic_menu_upload_you_tube.png
+++ b/core/res/res/drawable-mdpi/ic_menu_upload_you_tube.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_view.png b/core/res/res/drawable-mdpi/ic_menu_view.png
index 69828a9..69828a9 100755
--- a/core/res/res/drawable/ic_menu_view.png
+++ b/core/res/res/drawable-mdpi/ic_menu_view.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_week.png b/core/res/res/drawable-mdpi/ic_menu_week.png
index 62cd65e..62cd65e 100755
--- a/core/res/res/drawable/ic_menu_week.png
+++ b/core/res/res/drawable-mdpi/ic_menu_week.png
Binary files differ
diff --git a/core/res/res/drawable/ic_menu_zoom.png b/core/res/res/drawable-mdpi/ic_menu_zoom.png
index 0b8c4e8..0b8c4e8 100644
--- a/core/res/res/drawable/ic_menu_zoom.png
+++ b/core/res/res/drawable-mdpi/ic_menu_zoom.png
Binary files differ
diff --git a/core/res/res/drawable/ic_notification_clear_all.png b/core/res/res/drawable-mdpi/ic_notification_clear_all.png
index f2114d7..f2114d7 100644
--- a/core/res/res/drawable/ic_notification_clear_all.png
+++ b/core/res/res/drawable-mdpi/ic_notification_clear_all.png
Binary files differ
diff --git a/core/res/res/drawable/ic_notification_overlay.9.png b/core/res/res/drawable-mdpi/ic_notification_overlay.9.png
index 1a3063c..1a3063c 100644
--- a/core/res/res/drawable/ic_notification_overlay.9.png
+++ b/core/res/res/drawable-mdpi/ic_notification_overlay.9.png
Binary files differ
diff --git a/core/res/res/drawable/ic_partial_secure.png b/core/res/res/drawable-mdpi/ic_partial_secure.png
index 76ba96a..76ba96a 100644
--- a/core/res/res/drawable/ic_partial_secure.png
+++ b/core/res/res/drawable-mdpi/ic_partial_secure.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_disk_full.png b/core/res/res/drawable-mdpi/ic_popup_disk_full.png
index e6da5d0..e6da5d0 100644
--- a/core/res/res/drawable/ic_popup_disk_full.png
+++ b/core/res/res/drawable-mdpi/ic_popup_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_reminder.png b/core/res/res/drawable-mdpi/ic_popup_reminder.png
index af15279..af15279 100755
--- a/core/res/res/drawable/ic_popup_reminder.png
+++ b/core/res/res/drawable-mdpi/ic_popup_reminder.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_1.png b/core/res/res/drawable-mdpi/ic_popup_sync_1.png
index 13d8cdd..13d8cdd 100644
--- a/core/res/res/drawable/ic_popup_sync_1.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_1.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_2.png b/core/res/res/drawable-mdpi/ic_popup_sync_2.png
index 6ca162a..6ca162a 100644
--- a/core/res/res/drawable/ic_popup_sync_2.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_2.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_3.png b/core/res/res/drawable-mdpi/ic_popup_sync_3.png
index a7c21dd..a7c21dd 100644
--- a/core/res/res/drawable/ic_popup_sync_3.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_3.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_4.png b/core/res/res/drawable-mdpi/ic_popup_sync_4.png
index e9be04e..e9be04e 100644
--- a/core/res/res/drawable/ic_popup_sync_4.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_4.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_5.png b/core/res/res/drawable-mdpi/ic_popup_sync_5.png
index 65d87c4..65d87c4 100644
--- a/core/res/res/drawable/ic_popup_sync_5.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_5.png
Binary files differ
diff --git a/core/res/res/drawable/ic_popup_sync_6.png b/core/res/res/drawable-mdpi/ic_popup_sync_6.png
index 2015c88..2015c88 100644
--- a/core/res/res/drawable/ic_popup_sync_6.png
+++ b/core/res/res/drawable-mdpi/ic_popup_sync_6.png
Binary files differ
diff --git a/core/res/res/drawable/ic_search_category_default.png b/core/res/res/drawable-mdpi/ic_search_category_default.png
index 7eea584..7eea584 100755
--- a/core/res/res/drawable/ic_search_category_default.png
+++ b/core/res/res/drawable-mdpi/ic_search_category_default.png
Binary files differ
diff --git a/core/res/res/drawable/ic_secure.png b/core/res/res/drawable-mdpi/ic_secure.png
index 4f15fc4..4f15fc4 100644
--- a/core/res/res/drawable/ic_secure.png
+++ b/core/res/res/drawable-mdpi/ic_secure.png
Binary files differ
diff --git a/core/res/res/drawable/ic_text_dot.png b/core/res/res/drawable-mdpi/ic_text_dot.png
index 47913f6..47913f6 100644
--- a/core/res/res/drawable/ic_text_dot.png
+++ b/core/res/res/drawable-mdpi/ic_text_dot.png
Binary files differ
diff --git a/core/res/res/drawable/ic_vibrate.png b/core/res/res/drawable-mdpi/ic_vibrate.png
index eb24e50..eb24e50 100755
--- a/core/res/res/drawable/ic_vibrate.png
+++ b/core/res/res/drawable-mdpi/ic_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume.png b/core/res/res/drawable-mdpi/ic_volume.png
index cee70f0..cee70f0 100755
--- a/core/res/res/drawable/ic_volume.png
+++ b/core/res/res/drawable-mdpi/ic_volume.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume_bluetooth_ad2p.png b/core/res/res/drawable-mdpi/ic_volume_bluetooth_ad2p.png
index cf86ab3..cf86ab3 100644
--- a/core/res/res/drawable/ic_volume_bluetooth_ad2p.png
+++ b/core/res/res/drawable-mdpi/ic_volume_bluetooth_ad2p.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume_bluetooth_in_call.png b/core/res/res/drawable-mdpi/ic_volume_bluetooth_in_call.png
index 94801fc..94801fc 100644
--- a/core/res/res/drawable/ic_volume_bluetooth_in_call.png
+++ b/core/res/res/drawable-mdpi/ic_volume_bluetooth_in_call.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume_off.png b/core/res/res/drawable-mdpi/ic_volume_off.png
index f3850fc..f3850fc 100644
--- a/core/res/res/drawable/ic_volume_off.png
+++ b/core/res/res/drawable-mdpi/ic_volume_off.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume_off_small.png b/core/res/res/drawable-mdpi/ic_volume_off_small.png
index ae55bd6..ae55bd6 100755
--- a/core/res/res/drawable/ic_volume_off_small.png
+++ b/core/res/res/drawable-mdpi/ic_volume_off_small.png
Binary files differ
diff --git a/core/res/res/drawable/ic_volume_small.png b/core/res/res/drawable-mdpi/ic_volume_small.png
index 00a4f89..00a4f89 100755
--- a/core/res/res/drawable/ic_volume_small.png
+++ b/core/res/res/drawable-mdpi/ic_volume_small.png
Binary files differ
diff --git a/core/res/res/drawable/icon_highlight_rectangle.9.png b/core/res/res/drawable-mdpi/icon_highlight_rectangle.9.png
index 3dafde3..3dafde3 100644
--- a/core/res/res/drawable/icon_highlight_rectangle.9.png
+++ b/core/res/res/drawable-mdpi/icon_highlight_rectangle.9.png
Binary files differ
diff --git a/core/res/res/drawable/icon_highlight_square.9.png b/core/res/res/drawable-mdpi/icon_highlight_square.9.png
index a93a3f8..a93a3f8 100644
--- a/core/res/res/drawable/icon_highlight_square.9.png
+++ b/core/res/res/drawable-mdpi/icon_highlight_square.9.png
Binary files differ
diff --git a/core/res/res/drawable/ime_qwerty.png b/core/res/res/drawable-mdpi/ime_qwerty.png
index e6e5cda..e6e5cda 100644
--- a/core/res/res/drawable/ime_qwerty.png
+++ b/core/res/res/drawable-mdpi/ime_qwerty.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
index ef91dc4..ef91dc4 100644
--- a/core/res/res/drawable/indicator_code_lock_drag_direction_green_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
index f3d4204..f3d4204 100644
--- a/core/res/res/drawable/indicator_code_lock_drag_direction_red_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
index 4e88b37..4e88b37 100755
--- a/core/res/res/drawable/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
index 8020846..8020846 100755
--- a/core/res/res/drawable/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
index b7aee1ba..b7aee1ba 100755
--- a/core/res/res/drawable/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable/indicator_input_error.png b/core/res/res/drawable-mdpi/indicator_input_error.png
index ee60165..ee60165 100755
--- a/core/res/res/drawable/indicator_input_error.png
+++ b/core/res/res/drawable-mdpi/indicator_input_error.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_accessory_bg_landscape.9.png b/core/res/res/drawable-mdpi/keyboard_accessory_bg_landscape.9.png
index 8f828f6..8f828f6 100644
--- a/core/res/res/drawable/keyboard_accessory_bg_landscape.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_accessory_bg_landscape.9.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_background.9.png b/core/res/res/drawable-mdpi/keyboard_background.9.png
index 1d3ce05..1d3ce05 100644
--- a/core/res/res/drawable/keyboard_background.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_key_feedback_background.9.png b/core/res/res/drawable-mdpi/keyboard_key_feedback_background.9.png
index 2a80f09..2a80f09 100644
--- a/core/res/res/drawable/keyboard_key_feedback_background.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_key_feedback_more_background.9.png b/core/res/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
index 29aa285..29aa285 100755
--- a/core/res/res/drawable/keyboard_key_feedback_more_background.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_popup_panel_background.9.png b/core/res/res/drawable-mdpi/keyboard_popup_panel_background.9.png
index 36d75df..36d75df 100644
--- a/core/res/res/drawable/keyboard_popup_panel_background.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png b/core/res/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 0000000..4ba2a49
--- /dev/null
+++ b/core/res/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
index 6e703af..6e703af 100644
--- a/core/res/res/drawable/keyboard_textfield_selected.9.png
+++ b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/light_header.9.png b/core/res/res/drawable-mdpi/light_header.9.png
new file mode 100644
index 0000000..fcd9e2d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/light_header.9.png
Binary files differ
diff --git a/core/res/res/drawable/list_selector_background_disabled.9.png b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
index bf970b0..bf970b0 100644
--- a/core/res/res/drawable/list_selector_background_disabled.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/list_selector_background_focus.9.png b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
index c3e2415..c3e2415 100644
--- a/core/res/res/drawable/list_selector_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable/list_selector_background_longpress.9.png b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
index 5cbb251..5cbb251 100644
--- a/core/res/res/drawable/list_selector_background_longpress.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_longpress.9.png
Binary files differ
diff --git a/core/res/res/drawable/list_selector_background_pressed.9.png b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
index 02b4e9a..02b4e9a 100644
--- a/core/res/res/drawable/list_selector_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/loading_tile.png b/core/res/res/drawable-mdpi/loading_tile.png
index f5a80c9..f5a80c9 100644
--- a/core/res/res/drawable/loading_tile.png
+++ b/core/res/res/drawable-mdpi/loading_tile.png
Binary files differ
diff --git a/core/res/res/drawable/maps_google_logo.png b/core/res/res/drawable-mdpi/maps_google_logo.png
index 1374aaa..1374aaa 100644
--- a/core/res/res/drawable/maps_google_logo.png
+++ b/core/res/res/drawable-mdpi/maps_google_logo.png
Binary files differ
diff --git a/core/res/res/drawable/menu_background.9.png b/core/res/res/drawable-mdpi/menu_background.9.png
index ee99583..ee99583 100644
--- a/core/res/res/drawable/menu_background.9.png
+++ b/core/res/res/drawable-mdpi/menu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_background_fill_parent_width.9.png b/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png
index d368983..d368983 100644
--- a/core/res/res/drawable/menu_background_fill_parent_width.9.png
+++ b/core/res/res/drawable-mdpi/menu_background_fill_parent_width.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_separator.9.png b/core/res/res/drawable-mdpi/menu_separator.9.png
index 8a1a336..8a1a336 100644
--- a/core/res/res/drawable/menu_separator.9.png
+++ b/core/res/res/drawable-mdpi/menu_separator.9.png
Binary files differ
diff --git a/core/res/res/drawable/menu_submenu_background.9.png b/core/res/res/drawable-mdpi/menu_submenu_background.9.png
index a153532..a153532 100644
--- a/core/res/res/drawable/menu_submenu_background.9.png
+++ b/core/res/res/drawable-mdpi/menu_submenu_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/menuitem_background_focus.9.png b/core/res/res/drawable-mdpi/menuitem_background_focus.9.png
index c3e2415..c3e2415 100644
--- a/core/res/res/drawable/menuitem_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/menuitem_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable/menuitem_background_pressed.9.png b/core/res/res/drawable-mdpi/menuitem_background_pressed.9.png
index 02b4e9a..02b4e9a 100644
--- a/core/res/res/drawable/menuitem_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/menuitem_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menuitem_background_solid_focused.9.png b/core/res/res/drawable-mdpi/menuitem_background_solid_focused.9.png
new file mode 100644
index 0000000..99dd9b1
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menuitem_background_solid_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/menuitem_background_solid_pressed.9.png b/core/res/res/drawable-mdpi/menuitem_background_solid_pressed.9.png
new file mode 100644
index 0000000..389063a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/menuitem_background_solid_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/menuitem_checkbox_on.png b/core/res/res/drawable-mdpi/menuitem_checkbox_on.png
index bd8ff93..bd8ff93 100644
--- a/core/res/res/drawable/menuitem_checkbox_on.png
+++ b/core/res/res/drawable-mdpi/menuitem_checkbox_on.png
Binary files differ
diff --git a/core/res/res/drawable/no_tile_128.png b/core/res/res/drawable-mdpi/no_tile_128.png
index a9b007d..a9b007d 100644
--- a/core/res/res/drawable/no_tile_128.png
+++ b/core/res/res/drawable-mdpi/no_tile_128.png
Binary files differ
diff --git a/core/res/res/drawable/panel_background.9.png b/core/res/res/drawable-mdpi/panel_background.9.png
index 2305be4..2305be4 100644
--- a/core/res/res/drawable/panel_background.9.png
+++ b/core/res/res/drawable-mdpi/panel_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/panel_picture_frame_bg_focus_blue.9.png b/core/res/res/drawable-mdpi/panel_picture_frame_bg_focus_blue.9.png
index 7ebdbe5..7ebdbe5 100644
--- a/core/res/res/drawable/panel_picture_frame_bg_focus_blue.9.png
+++ b/core/res/res/drawable-mdpi/panel_picture_frame_bg_focus_blue.9.png
Binary files differ
diff --git a/core/res/res/drawable/panel_picture_frame_bg_normal.9.png b/core/res/res/drawable-mdpi/panel_picture_frame_bg_normal.9.png
index fd17d09..fd17d09 100644
--- a/core/res/res/drawable/panel_picture_frame_bg_normal.9.png
+++ b/core/res/res/drawable-mdpi/panel_picture_frame_bg_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/panel_picture_frame_bg_pressed_blue.9.png b/core/res/res/drawable-mdpi/panel_picture_frame_bg_pressed_blue.9.png
index 7bb0216..7bb0216 100644
--- a/core/res/res/drawable/panel_picture_frame_bg_pressed_blue.9.png
+++ b/core/res/res/drawable-mdpi/panel_picture_frame_bg_pressed_blue.9.png
Binary files differ
diff --git a/core/res/res/drawable/pickerbox_background.png b/core/res/res/drawable-mdpi/pickerbox_background.png
index 6494cd8..6494cd8 100644
--- a/core/res/res/drawable/pickerbox_background.png
+++ b/core/res/res/drawable-mdpi/pickerbox_background.png
Binary files differ
diff --git a/core/res/res/drawable/pickerbox_selected.9.png b/core/res/res/drawable-mdpi/pickerbox_selected.9.png
index d986a31..d986a31 100644
--- a/core/res/res/drawable/pickerbox_selected.9.png
+++ b/core/res/res/drawable-mdpi/pickerbox_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/pickerbox_unselected.9.png b/core/res/res/drawable-mdpi/pickerbox_unselected.9.png
index 27ec6b9..27ec6b9 100644
--- a/core/res/res/drawable/pickerbox_unselected.9.png
+++ b/core/res/res/drawable-mdpi/pickerbox_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable/picture_emergency.png b/core/res/res/drawable-mdpi/picture_emergency.png
index 3690b07..3690b07 100644
--- a/core/res/res/drawable/picture_emergency.png
+++ b/core/res/res/drawable-mdpi/picture_emergency.png
Binary files differ
diff --git a/core/res/res/drawable/picture_frame.9.png b/core/res/res/drawable-mdpi/picture_frame.9.png
index ba71570..ba71570 100644
--- a/core/res/res/drawable/picture_frame.9.png
+++ b/core/res/res/drawable-mdpi/picture_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_bottom_bright.9.png b/core/res/res/drawable-mdpi/popup_bottom_bright.9.png
index e8e203b..e8e203b 100644
--- a/core/res/res/drawable/popup_bottom_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_bottom_dark.9.png b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
index 76a2a7f..76a2a7f 100644
--- a/core/res/res/drawable/popup_bottom_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_bottom_medium.9.png b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
index dee6d6b..dee6d6b 100755
--- a/core/res/res/drawable/popup_bottom_medium.9.png
+++ b/core/res/res/drawable-mdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_center_bright.9.png b/core/res/res/drawable-mdpi/popup_center_bright.9.png
index c817338..c817338 100644
--- a/core/res/res/drawable/popup_center_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_center_dark.9.png b/core/res/res/drawable-mdpi/popup_center_dark.9.png
index 79ffdaa..79ffdaa 100644
--- a/core/res/res/drawable/popup_center_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_center_medium.9.png b/core/res/res/drawable-mdpi/popup_center_medium.9.png
index ba2e9bf..ba2e9bf 100755
--- a/core/res/res/drawable/popup_center_medium.9.png
+++ b/core/res/res/drawable-mdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_full_bright.9.png b/core/res/res/drawable-mdpi/popup_full_bright.9.png
index d33ff2b..d33ff2b 100644
--- a/core/res/res/drawable/popup_full_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_full_dark.9.png b/core/res/res/drawable-mdpi/popup_full_dark.9.png
index 2305be4..2305be4 100644
--- a/core/res/res/drawable/popup_full_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_inline_error.9.png b/core/res/res/drawable-mdpi/popup_inline_error.9.png
index 6a8297a..6a8297a 100755
--- a/core/res/res/drawable/popup_inline_error.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_inline_error_above.9.png b/core/res/res/drawable-mdpi/popup_inline_error_above.9.png
index 2e601d0..2e601d0 100644
--- a/core/res/res/drawable/popup_inline_error_above.9.png
+++ b/core/res/res/drawable-mdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_top_bright.9.png b/core/res/res/drawable-mdpi/popup_top_bright.9.png
index 727a948..727a948 100644
--- a/core/res/res/drawable/popup_top_bright.9.png
+++ b/core/res/res/drawable-mdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable/popup_top_dark.9.png b/core/res/res/drawable-mdpi/popup_top_dark.9.png
index af511f2..af511f2 100644
--- a/core/res/res/drawable/popup_top_dark.9.png
+++ b/core/res/res/drawable-mdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/presence_away.png b/core/res/res/drawable-mdpi/presence_away.png
index f8120df..f8120df 100644
--- a/core/res/res/drawable/presence_away.png
+++ b/core/res/res/drawable-mdpi/presence_away.png
Binary files differ
diff --git a/core/res/res/drawable/presence_busy.png b/core/res/res/drawable-mdpi/presence_busy.png
index 9d7620b..9d7620b 100644
--- a/core/res/res/drawable/presence_busy.png
+++ b/core/res/res/drawable-mdpi/presence_busy.png
Binary files differ
diff --git a/core/res/res/drawable/presence_invisible.png b/core/res/res/drawable-mdpi/presence_invisible.png
index 21399a4..21399a4 100644
--- a/core/res/res/drawable/presence_invisible.png
+++ b/core/res/res/drawable-mdpi/presence_invisible.png
Binary files differ
diff --git a/core/res/res/drawable/presence_offline.png b/core/res/res/drawable-mdpi/presence_offline.png
index 3941b82..3941b82 100644
--- a/core/res/res/drawable/presence_offline.png
+++ b/core/res/res/drawable-mdpi/presence_offline.png
Binary files differ
diff --git a/core/res/res/drawable/presence_online.png b/core/res/res/drawable-mdpi/presence_online.png
index 22d5683..22d5683 100644
--- a/core/res/res/drawable/presence_online.png
+++ b/core/res/res/drawable-mdpi/presence_online.png
Binary files differ
diff --git a/core/res/res/drawable/pressed_application_background_static.png b/core/res/res/drawable-mdpi/pressed_application_background_static.png
index 070f6fd..070f6fd 100644
--- a/core/res/res/drawable/pressed_application_background_static.png
+++ b/core/res/res/drawable-mdpi/pressed_application_background_static.png
Binary files differ
diff --git a/core/res/res/drawable/progressbar_indeterminate1.png b/core/res/res/drawable-mdpi/progressbar_indeterminate1.png
index 5eddb30..5eddb30 100644
--- a/core/res/res/drawable/progressbar_indeterminate1.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate1.png
Binary files differ
diff --git a/core/res/res/drawable/progressbar_indeterminate2.png b/core/res/res/drawable-mdpi/progressbar_indeterminate2.png
index 4ca3a63..4ca3a63 100644
--- a/core/res/res/drawable/progressbar_indeterminate2.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate2.png
Binary files differ
diff --git a/core/res/res/drawable/progressbar_indeterminate3.png b/core/res/res/drawable-mdpi/progressbar_indeterminate3.png
index da8e601..da8e601 100644
--- a/core/res/res/drawable/progressbar_indeterminate3.png
+++ b/core/res/res/drawable-mdpi/progressbar_indeterminate3.png
Binary files differ
diff --git a/core/res/res/drawable/radiobutton_off_background.png b/core/res/res/drawable-mdpi/radiobutton_off_background.png
index 1b94e21..1b94e21 100644
--- a/core/res/res/drawable/radiobutton_off_background.png
+++ b/core/res/res/drawable-mdpi/radiobutton_off_background.png
Binary files differ
diff --git a/core/res/res/drawable/radiobutton_on_background.png b/core/res/res/drawable-mdpi/radiobutton_on_background.png
index 636a803..636a803 100644
--- a/core/res/res/drawable/radiobutton_on_background.png
+++ b/core/res/res/drawable-mdpi/radiobutton_on_background.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_big_half.png b/core/res/res/drawable-mdpi/rate_star_big_half.png
index 9762292..9762292 100644
--- a/core/res/res/drawable/rate_star_big_half.png
+++ b/core/res/res/drawable-mdpi/rate_star_big_half.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_big_off.png b/core/res/res/drawable-mdpi/rate_star_big_off.png
index 6b5039f..6b5039f 100644
--- a/core/res/res/drawable/rate_star_big_off.png
+++ b/core/res/res/drawable-mdpi/rate_star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_big_on.png b/core/res/res/drawable-mdpi/rate_star_big_on.png
index a972db2..a972db2 100644
--- a/core/res/res/drawable/rate_star_big_on.png
+++ b/core/res/res/drawable-mdpi/rate_star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_small_half.png b/core/res/res/drawable-mdpi/rate_star_small_half.png
index 437a11c..437a11c 100644
--- a/core/res/res/drawable/rate_star_small_half.png
+++ b/core/res/res/drawable-mdpi/rate_star_small_half.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_small_off.png b/core/res/res/drawable-mdpi/rate_star_small_off.png
index 6fb0a36..6fb0a36 100644
--- a/core/res/res/drawable/rate_star_small_off.png
+++ b/core/res/res/drawable-mdpi/rate_star_small_off.png
Binary files differ
diff --git a/core/res/res/drawable/rate_star_small_on.png b/core/res/res/drawable-mdpi/rate_star_small_on.png
index 5392361..5392361 100644
--- a/core/res/res/drawable/rate_star_small_on.png
+++ b/core/res/res/drawable-mdpi/rate_star_small_on.png
Binary files differ
diff --git a/core/res/res/drawable/reticle.png b/core/res/res/drawable-mdpi/reticle.png
index c6ccf8e..c6ccf8e 100644
--- a/core/res/res/drawable/reticle.png
+++ b/core/res/res/drawable-mdpi/reticle.png
Binary files differ
diff --git a/core/res/res/drawable/screen_progress_frame.9.png b/core/res/res/drawable-mdpi/screen_progress_frame.9.png
index 0e92429..0e92429 100644
--- a/core/res/res/drawable/screen_progress_frame.9.png
+++ b/core/res/res/drawable-mdpi/screen_progress_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable/screen_progress_inner.9.png b/core/res/res/drawable-mdpi/screen_progress_inner.9.png
index 1799a53..1799a53 100644
--- a/core/res/res/drawable/screen_progress_inner.9.png
+++ b/core/res/res/drawable-mdpi/screen_progress_inner.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrollbar_handle_accelerated_anim2.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png
index 85caddd..85caddd 100755
--- a/core/res/res/drawable/scrollbar_handle_accelerated_anim2.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_accelerated_anim2.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrollbar_handle_horizontal.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_horizontal.9.png
index 8584d1f..8584d1f 100755
--- a/core/res/res/drawable/scrollbar_handle_horizontal.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_horizontal.9.png
Binary files differ
diff --git a/core/res/res/drawable/scrollbar_handle_vertical.9.png b/core/res/res/drawable-mdpi/scrollbar_handle_vertical.9.png
index 331a05d..331a05d 100755
--- a/core/res/res/drawable/scrollbar_handle_vertical.9.png
+++ b/core/res/res/drawable-mdpi/scrollbar_handle_vertical.9.png
Binary files differ
diff --git a/core/res/res/drawable/search_dropdown_background.9.png b/core/res/res/drawable-mdpi/search_dropdown_background.9.png
index 804260a..804260a 100644
--- a/core/res/res/drawable/search_dropdown_background.9.png
+++ b/core/res/res/drawable-mdpi/search_dropdown_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/search_plate.9.png b/core/res/res/drawable-mdpi/search_plate.9.png
index 8c42f10..8c42f10 100755
--- a/core/res/res/drawable/search_plate.9.png
+++ b/core/res/res/drawable-mdpi/search_plate.9.png
Binary files differ
diff --git a/core/res/res/drawable/search_plate_global.9.png b/core/res/res/drawable-mdpi/search_plate_global.9.png
index 1cad902..1cad902 100644
--- a/core/res/res/drawable/search_plate_global.9.png
+++ b/core/res/res/drawable-mdpi/search_plate_global.9.png
Binary files differ
diff --git a/core/res/res/drawable/seek_thumb_normal.png b/core/res/res/drawable-mdpi/seek_thumb_normal.png
index e9f2e23..e9f2e23 100644
--- a/core/res/res/drawable/seek_thumb_normal.png
+++ b/core/res/res/drawable-mdpi/seek_thumb_normal.png
Binary files differ
diff --git a/core/res/res/drawable/seek_thumb_pressed.png b/core/res/res/drawable-mdpi/seek_thumb_pressed.png
index 3ea5051..3ea5051 100644
--- a/core/res/res/drawable/seek_thumb_pressed.png
+++ b/core/res/res/drawable-mdpi/seek_thumb_pressed.png
Binary files differ
diff --git a/core/res/res/drawable/seek_thumb_selected.png b/core/res/res/drawable-mdpi/seek_thumb_selected.png
index 98b7ba0..98b7ba0 100644
--- a/core/res/res/drawable/seek_thumb_selected.png
+++ b/core/res/res/drawable-mdpi/seek_thumb_selected.png
Binary files differ
diff --git a/core/res/res/drawable/settings_header_raw.9.png b/core/res/res/drawable-mdpi/settings_header_raw.9.png
index 6b8134d..6b8134d 100644
--- a/core/res/res/drawable/settings_header_raw.9.png
+++ b/core/res/res/drawable-mdpi/settings_header_raw.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_16.png b/core/res/res/drawable-mdpi/spinner_black_16.png
index 5ee33ce..5ee33ce 100644
--- a/core/res/res/drawable/spinner_black_16.png
+++ b/core/res/res/drawable-mdpi/spinner_black_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_20.png b/core/res/res/drawable-mdpi/spinner_black_20.png
index e55b60d..e55b60d 100755
--- a/core/res/res/drawable/spinner_black_20.png
+++ b/core/res/res/drawable-mdpi/spinner_black_20.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_48.png b/core/res/res/drawable-mdpi/spinner_black_48.png
index 3a68192..3a68192 100644
--- a/core/res/res/drawable/spinner_black_48.png
+++ b/core/res/res/drawable-mdpi/spinner_black_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_black_76.png b/core/res/res/drawable-mdpi/spinner_black_76.png
index ec57460..ec57460 100644
--- a/core/res/res/drawable/spinner_black_76.png
+++ b/core/res/res/drawable-mdpi/spinner_black_76.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_dropdown_background_down.9.png b/core/res/res/drawable-mdpi/spinner_dropdown_background_down.9.png
index 8fd22f4..8fd22f4 100644
--- a/core/res/res/drawable/spinner_dropdown_background_down.9.png
+++ b/core/res/res/drawable-mdpi/spinner_dropdown_background_down.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_dropdown_background_up.9.png b/core/res/res/drawable-mdpi/spinner_dropdown_background_up.9.png
index 1354feb..1354feb 100644
--- a/core/res/res/drawable/spinner_dropdown_background_up.9.png
+++ b/core/res/res/drawable-mdpi/spinner_dropdown_background_up.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_normal.9.png b/core/res/res/drawable-mdpi/spinner_normal.9.png
index e0bab34..e0bab34 100644
--- a/core/res/res/drawable/spinner_normal.9.png
+++ b/core/res/res/drawable-mdpi/spinner_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_press.9.png b/core/res/res/drawable-mdpi/spinner_press.9.png
index a51c7ad..a51c7ad 100644
--- a/core/res/res/drawable/spinner_press.9.png
+++ b/core/res/res/drawable-mdpi/spinner_press.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_select.9.png b/core/res/res/drawable-mdpi/spinner_select.9.png
index 1bb19be..1bb19be 100644
--- a/core/res/res/drawable/spinner_select.9.png
+++ b/core/res/res/drawable-mdpi/spinner_select.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_16.png b/core/res/res/drawable-mdpi/spinner_white_16.png
index dd2e1fd..dd2e1fd 100644
--- a/core/res/res/drawable/spinner_white_16.png
+++ b/core/res/res/drawable-mdpi/spinner_white_16.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_48.png b/core/res/res/drawable-mdpi/spinner_white_48.png
index d25a33e..d25a33e 100644
--- a/core/res/res/drawable/spinner_white_48.png
+++ b/core/res/res/drawable-mdpi/spinner_white_48.png
Binary files differ
diff --git a/core/res/res/drawable/spinner_white_76.png b/core/res/res/drawable-mdpi/spinner_white_76.png
index f53e8ff..f53e8ff 100644
--- a/core/res/res/drawable/spinner_white_76.png
+++ b/core/res/res/drawable-mdpi/spinner_white_76.png
Binary files differ
diff --git a/core/res/res/drawable/spinnerbox_arrow_first.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png
index d8e268d..d8e268d 100644
--- a/core/res/res/drawable/spinnerbox_arrow_first.9.png
+++ b/core/res/res/drawable-mdpi/spinnerbox_arrow_first.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinnerbox_arrow_last.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png
index 087e650..087e650 100644
--- a/core/res/res/drawable/spinnerbox_arrow_last.9.png
+++ b/core/res/res/drawable-mdpi/spinnerbox_arrow_last.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinnerbox_arrow_middle.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png
index f1f2ff5..f1f2ff5 100644
--- a/core/res/res/drawable/spinnerbox_arrow_middle.9.png
+++ b/core/res/res/drawable-mdpi/spinnerbox_arrow_middle.9.png
Binary files differ
diff --git a/core/res/res/drawable/spinnerbox_arrow_single.9.png b/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png
index f537b3b..f537b3b 100644
--- a/core/res/res/drawable/spinnerbox_arrow_single.9.png
+++ b/core/res/res/drawable-mdpi/spinnerbox_arrow_single.9.png
Binary files differ
diff --git a/core/res/res/drawable/star_big_off.png b/core/res/res/drawable-mdpi/star_big_off.png
index 34ab4ab..34ab4ab 100644
--- a/core/res/res/drawable/star_big_off.png
+++ b/core/res/res/drawable-mdpi/star_big_off.png
Binary files differ
diff --git a/core/res/res/drawable/star_big_on.png b/core/res/res/drawable-mdpi/star_big_on.png
index 7aaf2bc..7aaf2bc 100644
--- a/core/res/res/drawable/star_big_on.png
+++ b/core/res/res/drawable-mdpi/star_big_on.png
Binary files differ
diff --git a/core/res/res/drawable/star_off.png b/core/res/res/drawable-mdpi/star_off.png
index ada53fc..ada53fc 100644
--- a/core/res/res/drawable/star_off.png
+++ b/core/res/res/drawable-mdpi/star_off.png
Binary files differ
diff --git a/core/res/res/drawable/star_on.png b/core/res/res/drawable-mdpi/star_on.png
index 49a57b6..49a57b6 100644
--- a/core/res/res/drawable/star_on.png
+++ b/core/res/res/drawable-mdpi/star_on.png
Binary files differ
diff --git a/core/res/res/drawable/stat_ecb_mode.png b/core/res/res/drawable-mdpi/stat_ecb_mode.png
index a948770..a948770 100644
--- a/core/res/res/drawable/stat_ecb_mode.png
+++ b/core/res/res/drawable-mdpi/stat_ecb_mode.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_alarm.png b/core/res/res/drawable-mdpi/stat_notify_alarm.png
index 1b01b85..1b01b85 100644
--- a/core/res/res/drawable/stat_notify_alarm.png
+++ b/core/res/res/drawable-mdpi/stat_notify_alarm.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_call_mute.png b/core/res/res/drawable-mdpi/stat_notify_call_mute.png
index 6da8313..6da8313 100644
--- a/core/res/res/drawable/stat_notify_call_mute.png
+++ b/core/res/res/drawable-mdpi/stat_notify_call_mute.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_chat.png b/core/res/res/drawable-mdpi/stat_notify_chat.png
index 238f043..238f043 100644
--- a/core/res/res/drawable/stat_notify_chat.png
+++ b/core/res/res/drawable-mdpi/stat_notify_chat.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_disk_full.png b/core/res/res/drawable-mdpi/stat_notify_disk_full.png
index 9120f00..9120f00 100755
--- a/core/res/res/drawable/stat_notify_disk_full.png
+++ b/core/res/res/drawable-mdpi/stat_notify_disk_full.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_error.png b/core/res/res/drawable-mdpi/stat_notify_error.png
index 6ced2b7..6ced2b7 100644
--- a/core/res/res/drawable/stat_notify_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_error.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_missed_call.png b/core/res/res/drawable-mdpi/stat_notify_missed_call.png
index fe746b3..fe746b3 100644
--- a/core/res/res/drawable/stat_notify_missed_call.png
+++ b/core/res/res/drawable-mdpi/stat_notify_missed_call.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_more.png b/core/res/res/drawable-mdpi/stat_notify_more.png
index e129ba9..e129ba9 100644
--- a/core/res/res/drawable/stat_notify_more.png
+++ b/core/res/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sdcard.png b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
index aaf1f74..aaf1f74 100644
--- a/core/res/res/drawable/stat_notify_sdcard.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sdcard_usb.png b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
index 8bc6661..8bc6661 100644
--- a/core/res/res/drawable/stat_notify_sdcard_usb.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sdcard_usb.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sim_toolkit.png b/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png
index c1ce8f2..c1ce8f2 100755
--- a/core/res/res/drawable/stat_notify_sim_toolkit.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sim_toolkit.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sync.png b/core/res/res/drawable-mdpi/stat_notify_sync.png
index 0edf692..0edf692 100644
--- a/core/res/res/drawable/stat_notify_sync.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sync_anim0.png b/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png
index 0edf692..0edf692 100644
--- a/core/res/res/drawable/stat_notify_sync_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync_anim0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_sync_error.png b/core/res/res/drawable-mdpi/stat_notify_sync_error.png
index 3078b8c..3078b8c 100644
--- a/core/res/res/drawable/stat_notify_sync_error.png
+++ b/core/res/res/drawable-mdpi/stat_notify_sync_error.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_voicemail.png b/core/res/res/drawable-mdpi/stat_notify_voicemail.png
index 658fa05..658fa05 100644
--- a/core/res/res/drawable/stat_notify_voicemail.png
+++ b/core/res/res/drawable-mdpi/stat_notify_voicemail.png
Binary files differ
diff --git a/core/res/res/drawable/stat_notify_wifi_in_range.png b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
index e9c74b4..e9c74b4 100644
--- a/core/res/res/drawable/stat_notify_wifi_in_range.png
+++ b/core/res/res/drawable-mdpi/stat_notify_wifi_in_range.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_0.png b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
index 4a5e99e..4a5e99e 100644
--- a/core/res/res/drawable/stat_sys_battery_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_10.png b/core/res/res/drawable-mdpi/stat_sys_battery_10.png
index b789f23..b789f23 100755
--- a/core/res/res/drawable/stat_sys_battery_10.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_10.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_100.png b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
index d280aeb..d280aeb 100644
--- a/core/res/res/drawable/stat_sys_battery_100.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_20.png b/core/res/res/drawable-mdpi/stat_sys_battery_20.png
index 009a9fd..009a9fd 100644
--- a/core/res/res/drawable/stat_sys_battery_20.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_20.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_40.png b/core/res/res/drawable-mdpi/stat_sys_battery_40.png
index 15b57f4..15b57f4 100644
--- a/core/res/res/drawable/stat_sys_battery_40.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_40.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_60.png b/core/res/res/drawable-mdpi/stat_sys_battery_60.png
index 21078fd..21078fd 100644
--- a/core/res/res/drawable/stat_sys_battery_60.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_60.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_80.png b/core/res/res/drawable-mdpi/stat_sys_battery_80.png
index 9268f7b..9268f7b 100644
--- a/core/res/res/drawable/stat_sys_battery_80.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_80.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim0.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
index ff3cabd..ff3cabd 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim1.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
index b563701..b563701 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim2.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
index 904989e..904989e 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim3.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
index ba011c9..ba011c9 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim4.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
index 4f1c485..4f1c485 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim4.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_charge_anim5.png b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
index 4d3396d..4d3396d 100644
--- a/core/res/res/drawable/stat_sys_battery_charge_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_charge_anim5.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_battery_unknown.png b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
index ed72ebf..ed72ebf 100644
--- a/core/res/res/drawable/stat_sys_battery_unknown.png
+++ b/core/res/res/drawable-mdpi/stat_sys_battery_unknown.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png
index 7a8b78f..7a8b78f 100644
--- a/core/res/res/drawable/stat_sys_data_bluetooth.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_bluetooth_connected.png b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
index f09b83b..f09b83b 100755
--- a/core/res/res/drawable/stat_sys_data_bluetooth_connected.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_bluetooth_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png
new file mode 100644
index 0000000..130724f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_connected_1x.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_connected_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png
index a109280..a109280 100644
--- a/core/res/res/drawable/stat_sys_data_connected_3g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_connected_3g.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_connected_e.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png
index c552644..c552644 100644
--- a/core/res/res/drawable/stat_sys_data_connected_e.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_connected_e.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_connected_g.png b/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png
index f7edb49..f7edb49 100644
--- a/core/res/res/drawable/stat_sys_data_connected_g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_connected_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png
new file mode 100644
index 0000000..3155e632
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_in_1x.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png
index 01b003c..01b003c 100644
--- a/core/res/res/drawable/stat_sys_data_in_3g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_in_3g.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_e.png b/core/res/res/drawable-mdpi/stat_sys_data_in_e.png
index bffa0eb..bffa0eb 100644
--- a/core/res/res/drawable/stat_sys_data_in_e.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_in_e.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_g.png b/core/res/res/drawable-mdpi/stat_sys_data_in_g.png
index 8884b48..8884b48 100644
--- a/core/res/res/drawable/stat_sys_data_in_g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_in_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png
new file mode 100644
index 0000000..1017e3b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_inandout_1x.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png
index 3651300..3651300 100644
--- a/core/res/res/drawable/stat_sys_data_inandout_3g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_inandout_3g.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_e.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png
index 99533e0..99533e0 100644
--- a/core/res/res/drawable/stat_sys_data_inandout_e.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_inandout_e.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_g.png b/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png
index f4e5a12..f4e5a12 100644
--- a/core/res/res/drawable/stat_sys_data_inandout_g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_inandout_g.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png b/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png
new file mode 100644
index 0000000..5418791
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_data_out_1x.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_3g.png b/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png
index f7f0f89..f7f0f89 100644
--- a/core/res/res/drawable/stat_sys_data_out_3g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_out_3g.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_e.png b/core/res/res/drawable-mdpi/stat_sys_data_out_e.png
index c915426..c915426 100644
--- a/core/res/res/drawable/stat_sys_data_out_e.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_out_e.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_g.png b/core/res/res/drawable-mdpi/stat_sys_data_out_g.png
index 5d36035..5d36035 100644
--- a/core/res/res/drawable/stat_sys_data_out_g.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_out_g.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_usb.png b/core/res/res/drawable-mdpi/stat_sys_data_usb.png
index 2d0da4c..2d0da4c 100644
--- a/core/res/res/drawable/stat_sys_data_usb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_data_usb.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim0.png b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
index 69b95cd..69b95cd 100755
--- a/core/res/res/drawable/stat_sys_download_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim1.png b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
index 1e18eb5..1e18eb5 100755
--- a/core/res/res/drawable/stat_sys_download_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim2.png b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
index d7f2312..d7f2312 100755
--- a/core/res/res/drawable/stat_sys_download_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim3.png b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
index 83f8d0f..83f8d0f 100755
--- a/core/res/res/drawable/stat_sys_download_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim4.png b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
index 9c1bd47..9c1bd47 100755
--- a/core/res/res/drawable/stat_sys_download_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim4.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_download_anim5.png b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
index 3a81164..3a81164 100755
--- a/core/res/res/drawable/stat_sys_download_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_download_anim5.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_gps_acquiring.png b/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png
index 31bc94e..31bc94e 100644
--- a/core/res/res/drawable/stat_sys_gps_acquiring.png
+++ b/core/res/res/drawable-mdpi/stat_sys_gps_acquiring.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_gps_on.png b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
index a2c677d..a2c677d 100755
--- a/core/res/res/drawable/stat_sys_gps_on.png
+++ b/core/res/res/drawable-mdpi/stat_sys_gps_on.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_headset.png b/core/res/res/drawable-mdpi/stat_sys_headset.png
index 45fbea2..45fbea2 100644
--- a/core/res/res/drawable/stat_sys_headset.png
+++ b/core/res/res/drawable-mdpi/stat_sys_headset.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_no_sim.png b/core/res/res/drawable-mdpi/stat_sys_no_sim.png
index 2134d49..2134d49 100644
--- a/core/res/res/drawable/stat_sys_no_sim.png
+++ b/core/res/res/drawable-mdpi/stat_sys_no_sim.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call.png b/core/res/res/drawable-mdpi/stat_sys_phone_call.png
index c44d062..c44d062 100644
--- a/core/res/res/drawable/stat_sys_phone_call.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
index 7abfd19..7abfd19 100644
--- a/core/res/res/drawable/stat_sys_phone_call_bluetooth.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call_forward.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png
index ed4b6ec..ed4b6ec 100755
--- a/core/res/res/drawable/stat_sys_phone_call_forward.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_forward.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_phone_call_on_hold.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png
index 9216447..9216447 100644
--- a/core/res/res/drawable/stat_sys_phone_call_on_hold.png
+++ b/core/res/res/drawable-mdpi/stat_sys_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png
index bfbf18e..bfbf18e 100644
--- a/core/res/res/drawable/stat_sys_r_signal_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_0_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png
index f615681..f615681 100644
--- a/core/res/res/drawable/stat_sys_r_signal_0_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png
index 896ba4d..896ba4d 100644
--- a/core/res/res/drawable/stat_sys_r_signal_1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_1_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png
index c3962a2..c3962a2 100644
--- a/core/res/res/drawable/stat_sys_r_signal_1_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png
index af79eff..af79eff 100644
--- a/core/res/res/drawable/stat_sys_r_signal_2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_2_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png
index 68bfe94..68bfe94 100644
--- a/core/res/res/drawable/stat_sys_r_signal_2_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png
index 92c09c8..92c09c8 100644
--- a/core/res/res/drawable/stat_sys_r_signal_3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_3_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png
index 4ccd416..4ccd416 100644
--- a/core/res/res/drawable/stat_sys_r_signal_3_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png
index f04fb11..f04fb11 100644
--- a/core/res/res/drawable/stat_sys_r_signal_4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_r_signal_4_cdma.png b/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png
index 0b25570..0b25570 100644
--- a/core/res/res/drawable/stat_sys_r_signal_4_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_r_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ra_signal_0_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png
index 9a38733..9a38733 100644
--- a/core/res/res/drawable/stat_sys_ra_signal_0_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_0_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ra_signal_1_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png
index c70e283..c70e283 100644
--- a/core/res/res/drawable/stat_sys_ra_signal_1_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_1_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ra_signal_2_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png
index a09564c..a09564c 100644
--- a/core/res/res/drawable/stat_sys_ra_signal_2_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_2_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ra_signal_3_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png
index 2637dec..2637dec 100644
--- a/core/res/res/drawable/stat_sys_ra_signal_3_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_3_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ra_signal_4_cdma.png b/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png
index aba13e7..aba13e7 100644
--- a/core/res/res/drawable/stat_sys_ra_signal_4_cdma.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ra_signal_4_cdma.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ringer_silent.png b/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png
index d62f32d..d62f32d 100644
--- a/core/res/res/drawable/stat_sys_ringer_silent.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ringer_silent.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_ringer_vibrate.png b/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png
index 665ca38..665ca38 100644
--- a/core/res/res/drawable/stat_sys_ringer_vibrate.png
+++ b/core/res/res/drawable-mdpi/stat_sys_ringer_vibrate.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_0.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png
index c61cce7..c61cce7 100755
--- a/core/res/res/drawable/stat_sys_roaming_cdma_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png
index d62502d..d62502d 100755
--- a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png
index c61cce7..c61cce7 100755
--- a/core/res/res/drawable/stat_sys_roaming_cdma_flash_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_roaming_cdma_flash_anim1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_signal_0.png
index cb7b7b3..cb7b7b3 100644
--- a/core/res/res/drawable/stat_sys_signal_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_signal_1.png
index 5376e92..5376e92 100644
--- a/core/res/res/drawable/stat_sys_signal_1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_signal_2.png
index fd54363..fd54363 100644
--- a/core/res/res/drawable/stat_sys_signal_2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_signal_3.png
index 6c4873a..6c4873a 100644
--- a/core/res/res/drawable/stat_sys_signal_3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_signal_4.png
index a3320cb..a3320cb 100644
--- a/core/res/res/drawable/stat_sys_signal_4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_flightmode.png b/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png
index 2f4fd4f..2f4fd4f 100644..100755
--- a/core/res/res/drawable/stat_sys_signal_flightmode.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_flightmode.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_null.png b/core/res/res/drawable-mdpi/stat_sys_signal_null.png
index 5aa23f6..5aa23f6 100644
--- a/core/res/res/drawable/stat_sys_signal_null.png
+++ b/core/res/res/drawable-mdpi/stat_sys_signal_null.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_speakerphone.png b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
index 642dfd4..642dfd4 100644
--- a/core/res/res/drawable/stat_sys_speakerphone.png
+++ b/core/res/res/drawable-mdpi/stat_sys_speakerphone.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_tty_mode.png b/core/res/res/drawable-mdpi/stat_sys_tty_mode.png
index ed157a8..ed157a8 100644
--- a/core/res/res/drawable/stat_sys_tty_mode.png
+++ b/core/res/res/drawable-mdpi/stat_sys_tty_mode.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim0.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
index b7a5978..b7a5978 100755
--- a/core/res/res/drawable/stat_sys_upload_anim0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim1.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
index a203e15..a203e15 100755
--- a/core/res/res/drawable/stat_sys_upload_anim1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim2.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
index 4af7630..4af7630 100755
--- a/core/res/res/drawable/stat_sys_upload_anim2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim3.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
index 1dd76b1..1dd76b1 100755
--- a/core/res/res/drawable/stat_sys_upload_anim3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim4.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
index 36c18bf..36c18bf 100755
--- a/core/res/res/drawable/stat_sys_upload_anim4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim4.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_upload_anim5.png b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
index 748331f..748331f 100755
--- a/core/res/res/drawable/stat_sys_upload_anim5.png
+++ b/core/res/res/drawable-mdpi/stat_sys_upload_anim5.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_vp_phone_call.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png
index aa03b4f..aa03b4f 100644
--- a/core/res/res/drawable/stat_sys_vp_phone_call.png
+++ b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
index 7abfd19..7abfd19 100644
--- a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png
+++ b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_on_hold.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png
index 5f45440..5f45440 100644
--- a/core/res/res/drawable/stat_sys_vp_phone_call_on_hold.png
+++ b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_on_hold.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_warning.png b/core/res/res/drawable-mdpi/stat_sys_warning.png
index be00f47..be00f47 100644
--- a/core/res/res/drawable/stat_sys_warning.png
+++ b/core/res/res/drawable-mdpi/stat_sys_warning.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_wifi_signal_0.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png
index 8ee3421..8ee3421 100644
--- a/core/res/res/drawable/stat_sys_wifi_signal_0.png
+++ b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_0.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_wifi_signal_1.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png
index 184fa36..184fa36 100644
--- a/core/res/res/drawable/stat_sys_wifi_signal_1.png
+++ b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_1.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_wifi_signal_2.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png
index 79935bb..79935bb 100644
--- a/core/res/res/drawable/stat_sys_wifi_signal_2.png
+++ b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_2.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_wifi_signal_3.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png
index d2099e6..d2099e6 100644
--- a/core/res/res/drawable/stat_sys_wifi_signal_3.png
+++ b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_3.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_wifi_signal_4.png b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png
index 2062aad..2062aad 100644
--- a/core/res/res/drawable/stat_sys_wifi_signal_4.png
+++ b/core/res/res/drawable-mdpi/stat_sys_wifi_signal_4.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_background.png b/core/res/res/drawable-mdpi/status_bar_background.png
index cd11166..cd11166 100644
--- a/core/res/res/drawable/status_bar_background.png
+++ b/core/res/res/drawable-mdpi/status_bar_background.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_close_on.9.png b/core/res/res/drawable-mdpi/status_bar_close_on.9.png
index 9cbd9fe..9cbd9fe 100644
--- a/core/res/res/drawable/status_bar_close_on.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_header_background.9.png b/core/res/res/drawable-mdpi/status_bar_header_background.9.png
index fa9a90c..fa9a90c 100644
--- a/core/res/res/drawable/status_bar_header_background.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_item_app_background_normal.9.png b/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
index c079615..c079615 100644
--- a/core/res/res/drawable/status_bar_item_app_background_normal.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_app_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_item_background_focus.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png
index c3e2415..c3e2415 100644
--- a/core/res/res/drawable/status_bar_item_background_focus.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_item_background_normal.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png
index b8e399d..b8e399d 100644
--- a/core/res/res/drawable/status_bar_item_background_normal.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/status_bar_item_background_pressed.9.png b/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png
index 02b4e9a..02b4e9a 100644
--- a/core/res/res/drawable/status_bar_item_background_pressed.9.png
+++ b/core/res/res/drawable-mdpi/status_bar_item_background_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/statusbar_background.png b/core/res/res/drawable-mdpi/statusbar_background.png
index 204d76a..204d76a 100644
--- a/core/res/res/drawable/statusbar_background.png
+++ b/core/res/res/drawable-mdpi/statusbar_background.png
Binary files differ
diff --git a/core/res/res/drawable/submenu_arrow_nofocus.png b/core/res/res/drawable-mdpi/submenu_arrow_nofocus.png
index cead09e..cead09e 100644
--- a/core/res/res/drawable/submenu_arrow_nofocus.png
+++ b/core/res/res/drawable-mdpi/submenu_arrow_nofocus.png
Binary files differ
diff --git a/core/res/res/drawable/sym_action_add.png b/core/res/res/drawable-mdpi/sym_action_add.png
index af637b3..af637b3 100644
--- a/core/res/res/drawable/sym_action_add.png
+++ b/core/res/res/drawable-mdpi/sym_action_add.png
Binary files differ
diff --git a/core/res/res/drawable/sym_action_call.png b/core/res/res/drawable-mdpi/sym_action_call.png
index a442758..a442758 100644
--- a/core/res/res/drawable/sym_action_call.png
+++ b/core/res/res/drawable-mdpi/sym_action_call.png
Binary files differ
diff --git a/core/res/res/drawable/sym_action_chat.png b/core/res/res/drawable-mdpi/sym_action_chat.png
index 9f6419e..9f6419e 100644
--- a/core/res/res/drawable/sym_action_chat.png
+++ b/core/res/res/drawable-mdpi/sym_action_chat.png
Binary files differ
diff --git a/core/res/res/drawable/sym_action_email.png b/core/res/res/drawable-mdpi/sym_action_email.png
index 5fea417..5fea417 100644
--- a/core/res/res/drawable/sym_action_email.png
+++ b/core/res/res/drawable-mdpi/sym_action_email.png
Binary files differ
diff --git a/core/res/res/drawable/sym_call_incoming.png b/core/res/res/drawable-mdpi/sym_call_incoming.png
index 652b882..652b882 100644
--- a/core/res/res/drawable/sym_call_incoming.png
+++ b/core/res/res/drawable-mdpi/sym_call_incoming.png
Binary files differ
diff --git a/core/res/res/drawable/sym_call_missed.png b/core/res/res/drawable-mdpi/sym_call_missed.png
index ed859d0..ed859d0 100644
--- a/core/res/res/drawable/sym_call_missed.png
+++ b/core/res/res/drawable-mdpi/sym_call_missed.png
Binary files differ
diff --git a/core/res/res/drawable/sym_call_outgoing.png b/core/res/res/drawable-mdpi/sym_call_outgoing.png
index bdf675d..bdf675d 100644
--- a/core/res/res/drawable/sym_call_outgoing.png
+++ b/core/res/res/drawable-mdpi/sym_call_outgoing.png
Binary files differ
diff --git a/core/res/res/drawable/sym_contact_card.png b/core/res/res/drawable-mdpi/sym_contact_card.png
index 023ea6f..023ea6f 100644
--- a/core/res/res/drawable/sym_contact_card.png
+++ b/core/res/res/drawable-mdpi/sym_contact_card.png
Binary files differ
diff --git a/core/res/res/drawable/sym_def_app_icon.png b/core/res/res/drawable-mdpi/sym_def_app_icon.png
index 8be3b54..8be3b54 100644
--- a/core/res/res/drawable/sym_def_app_icon.png
+++ b/core/res/res/drawable-mdpi/sym_def_app_icon.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus.9.png b/core/res/res/drawable-mdpi/tab_focus.9.png
new file mode 100755
index 0000000..d9bcc57
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_focus.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png b/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png
new file mode 100755
index 0000000..2536d94
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_focus_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png b/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png
new file mode 100755
index 0000000..2536d94
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_focus_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press.9.png b/core/res/res/drawable-mdpi/tab_press.9.png
new file mode 100755
index 0000000..3332660
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_press.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press_bar_left.9.png b/core/res/res/drawable-mdpi/tab_press_bar_left.9.png
new file mode 100755
index 0000000..d2c75e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_press_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_press_bar_right.9.png b/core/res/res/drawable-mdpi/tab_press_bar_right.9.png
new file mode 100755
index 0000000..d2c75e3
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_press_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected.9.png b/core/res/res/drawable-mdpi/tab_selected.9.png
new file mode 100644
index 0000000..54190ea
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png b/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png
new file mode 100755
index 0000000..d20f3a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_bar_left.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png b/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png
new file mode 100755
index 0000000..d20f3a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_selected_bar_right.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/tab_unselected.9.png b/core/res/res/drawable-mdpi/tab_unselected.9.png
new file mode 100644
index 0000000..1b8a69c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/tab_unselected.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_default.9.png b/core/res/res/drawable-mdpi/textfield_default.9.png
index cc78e6c..cc78e6c 100644
--- a/core/res/res/drawable/textfield_default.9.png
+++ b/core/res/res/drawable-mdpi/textfield_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_disabled.9.png b/core/res/res/drawable-mdpi/textfield_disabled.9.png
index 9c77149..9c77149 100644
--- a/core/res/res/drawable/textfield_disabled.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_disabled_selected.9.png b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png
index 6d47708..6d47708 100644
--- a/core/res/res/drawable/textfield_disabled_selected.9.png
+++ b/core/res/res/drawable-mdpi/textfield_disabled_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_pressed.9.png b/core/res/res/drawable-mdpi/textfield_pressed.9.png
index c909ad2..c909ad2 100644
--- a/core/res/res/drawable/textfield_pressed.9.png
+++ b/core/res/res/drawable-mdpi/textfield_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_search_default.9.png b/core/res/res/drawable-mdpi/textfield_search_default.9.png
index 7dc5b27..7dc5b27 100755
--- a/core/res/res/drawable/textfield_search_default.9.png
+++ b/core/res/res/drawable-mdpi/textfield_search_default.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_search_pressed.9.png b/core/res/res/drawable-mdpi/textfield_search_pressed.9.png
index da00c25..da00c25 100644
--- a/core/res/res/drawable/textfield_search_pressed.9.png
+++ b/core/res/res/drawable-mdpi/textfield_search_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_search_selected.9.png b/core/res/res/drawable-mdpi/textfield_search_selected.9.png
index a9fd3b2..a9fd3b2 100755
--- a/core/res/res/drawable/textfield_search_selected.9.png
+++ b/core/res/res/drawable-mdpi/textfield_search_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/textfield_selected.9.png b/core/res/res/drawable-mdpi/textfield_selected.9.png
index 0c1b446..0c1b446 100644
--- a/core/res/res/drawable/textfield_selected.9.png
+++ b/core/res/res/drawable-mdpi/textfield_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_down_disabled.9.png b/core/res/res/drawable-mdpi/timepicker_down_disabled.9.png
index 596294b..596294b 100755
--- a/core/res/res/drawable/timepicker_down_disabled.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_down_disabled_focused.9.png b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused.9.png
index 662cffd..662cffd 100755
--- a/core/res/res/drawable/timepicker_down_disabled_focused.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_down_normal.9.png b/core/res/res/drawable-mdpi/timepicker_down_normal.9.png
index f17e8f9..f17e8f9 100755
--- a/core/res/res/drawable/timepicker_down_normal.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_down_pressed.9.png b/core/res/res/drawable-mdpi/timepicker_down_pressed.9.png
index 777bcf5..777bcf5 100755
--- a/core/res/res/drawable/timepicker_down_pressed.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_down_selected.9.png b/core/res/res/drawable-mdpi/timepicker_down_selected.9.png
index b45db62..b45db62 100755
--- a/core/res/res/drawable/timepicker_down_selected.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_down_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_input_disabled.9.png b/core/res/res/drawable-mdpi/timepicker_input_disabled.9.png
index f73658e..f73658e 100755
--- a/core/res/res/drawable/timepicker_input_disabled.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_input_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_input_normal.9.png b/core/res/res/drawable-mdpi/timepicker_input_normal.9.png
index 8032ada..8032ada 100755
--- a/core/res/res/drawable/timepicker_input_normal.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_input_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_input_pressed.9.png b/core/res/res/drawable-mdpi/timepicker_input_pressed.9.png
index 30d8d5f..30d8d5f 100755
--- a/core/res/res/drawable/timepicker_input_pressed.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_input_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_input_selected.9.png b/core/res/res/drawable-mdpi/timepicker_input_selected.9.png
index 874f18f..874f18f 100755
--- a/core/res/res/drawable/timepicker_input_selected.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_input_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_up_disabled.9.png b/core/res/res/drawable-mdpi/timepicker_up_disabled.9.png
index 327b0b5..327b0b5 100755
--- a/core/res/res/drawable/timepicker_up_disabled.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_up_disabled_focused.9.png b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused.9.png
index 4c96680..4c96680 100755
--- a/core/res/res/drawable/timepicker_up_disabled_focused.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_disabled_focused.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_up_normal.9.png b/core/res/res/drawable-mdpi/timepicker_up_normal.9.png
index dcd26e0..dcd26e0 100755
--- a/core/res/res/drawable/timepicker_up_normal.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_up_pressed.9.png b/core/res/res/drawable-mdpi/timepicker_up_pressed.9.png
index 7dac778..7dac778 100755
--- a/core/res/res/drawable/timepicker_up_pressed.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/timepicker_up_selected.9.png b/core/res/res/drawable-mdpi/timepicker_up_selected.9.png
index 35dae8e..35dae8e 100755
--- a/core/res/res/drawable/timepicker_up_selected.9.png
+++ b/core/res/res/drawable-mdpi/timepicker_up_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable/title_bar_portrait.9.png b/core/res/res/drawable-mdpi/title_bar_portrait.9.png
index 482d82e..482d82e 100644
--- a/core/res/res/drawable/title_bar_portrait.9.png
+++ b/core/res/res/drawable-mdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/core/res/res/drawable/title_bar_shadow.9.png b/core/res/res/drawable-mdpi/title_bar_shadow.9.png
index 0872366..0872366 100644
--- a/core/res/res/drawable/title_bar_shadow.9.png
+++ b/core/res/res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/core/res/res/drawable/title_bar_tall.png b/core/res/res/drawable-mdpi/title_bar_tall.png
index cd565dc..cd565dc 100644
--- a/core/res/res/drawable/title_bar_tall.png
+++ b/core/res/res/drawable-mdpi/title_bar_tall.png
Binary files differ
diff --git a/core/res/res/drawable/toast_frame.9.png b/core/res/res/drawable-mdpi/toast_frame.9.png
index 08c4f86..08c4f86 100755
--- a/core/res/res/drawable/toast_frame.9.png
+++ b/core/res/res/drawable-mdpi/toast_frame.9.png
Binary files differ
diff --git a/core/res/res/drawable/unknown_image.png b/core/res/res/drawable-mdpi/unknown_image.png
index b1c3e92..b1c3e92 100644
--- a/core/res/res/drawable/unknown_image.png
+++ b/core/res/res/drawable-mdpi/unknown_image.png
Binary files differ
diff --git a/core/res/res/drawable/zoom_plate.9.png b/core/res/res/drawable-mdpi/zoom_plate.9.png
index c8c1a08..c8c1a08 100644
--- a/core/res/res/drawable/zoom_plate.9.png
+++ b/core/res/res/drawable-mdpi/zoom_plate.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_close.xml b/core/res/res/drawable/btn_close.xml
index 9d90e4b..598ab0f 100644
--- a/core/res/res/drawable/btn_close.xml
+++ b/core/res/res/drawable/btn_close.xml
@@ -16,10 +16,12 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="false"
+ <item android:state_pressed="false" android:state_focused="false"
android:drawable="@android:drawable/btn_close_normal" />
<item android:state_pressed="true"
android:drawable="@android:drawable/btn_close_pressed" />
+ <item android:state_focused="true"
+ android:drawable="@android:drawable/btn_close_selected" />
</selector>
diff --git a/core/res/res/drawable/btn_close_normal.png b/core/res/res/drawable/btn_close_normal.png
deleted file mode 100644
index ecc4dde..0000000
--- a/core/res/res/drawable/btn_close_normal.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_close_pressed.png b/core/res/res/drawable/btn_close_pressed.png
deleted file mode 100644
index 49223c5..0000000
--- a/core/res/res/drawable/btn_close_pressed.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_keyboard_key_trans.xml b/core/res/res/drawable/btn_keyboard_key_trans.xml
new file mode 100644
index 0000000..970aed7
--- /dev/null
+++ b/core/res/res/drawable/btn_keyboard_key_trans.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_selected="true" android:state_pressed="false"
+ android:drawable="@drawable/btn_keyboard_key_trans_selected" />
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_trans_pressed" />
+
+ <item android:state_pressed="false" android:state_focused="false"
+ android:drawable="@drawable/btn_keyboard_key_trans_normal" />
+
+</selector>
diff --git a/core/res/res/drawable/contact_header_bg.9.png b/core/res/res/drawable/contact_header_bg.9.png
new file mode 100644
index 0000000..7f9a5a3
--- /dev/null
+++ b/core/res/res/drawable/contact_header_bg.9.png
Binary files differ
diff --git a/core/res/res/drawable/dark_header.9.png b/core/res/res/drawable/dark_header.9.png
deleted file mode 100644
index 8fa5f09..0000000
--- a/core/res/res/drawable/dark_header.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/dark_header_dither.xml b/core/res/res/drawable/dark_header_dither.xml
new file mode 100644
index 0000000..0741fa4
--- /dev/null
+++ b/core/res/res/drawable/dark_header_dither.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/dark_header"
+ android:dither="true"
+/>
diff --git a/core/res/res/drawable/divider_horizontal_bright.9.png b/core/res/res/drawable/divider_horizontal_bright.9.png
deleted file mode 100644
index 144fc22..0000000
--- a/core/res/res/drawable/divider_horizontal_bright.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/divider_horizontal_bright_opaque.9.png b/core/res/res/drawable/divider_horizontal_bright_opaque.9.png
deleted file mode 100644
index 30c9b2b..0000000
--- a/core/res/res/drawable/divider_horizontal_bright_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/divider_horizontal_dark.9.png b/core/res/res/drawable/divider_horizontal_dark.9.png
deleted file mode 100644
index 08838ca..0000000
--- a/core/res/res/drawable/divider_horizontal_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/divider_horizontal_dark_opaque.9.png b/core/res/res/drawable/divider_horizontal_dark_opaque.9.png
deleted file mode 100644
index ce21acd..0000000
--- a/core/res/res/drawable/divider_horizontal_dark_opaque.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/divider_vertical_bright.9.png b/core/res/res/drawable/divider_vertical_bright.9.png
deleted file mode 100644
index da6e4ec..0000000
--- a/core/res/res/drawable/divider_vertical_bright.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/divider_vertical_bright_opaque.9.png b/core/res/res/drawable/divider_vertical_bright_opaque.9.png
new file mode 100644
index 0000000..5c537ee
--- /dev/null
+++ b/core/res/res/drawable/divider_vertical_bright_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable/divider_vertical_dark.9.png b/core/res/res/drawable/divider_vertical_dark.9.png
new file mode 100644
index 0000000..548d0bd
--- /dev/null
+++ b/core/res/res/drawable/divider_vertical_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable/divider_vertical_dark_opaque.9.png b/core/res/res/drawable/divider_vertical_dark_opaque.9.png
new file mode 100644
index 0000000..8f35315
--- /dev/null
+++ b/core/res/res/drawable/divider_vertical_dark_opaque.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_dark.xml b/core/res/res/drawable/fasttrack_badge_dark.xml
new file mode 100644
index 0000000..c60d403
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_dark.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_focused="false"
+ android:state_selected="false"
+ android:state_pressed="false"
+ android:drawable="@drawable/fasttrack_badge_dark_normal" />
+
+ <item
+ android:state_pressed="true"
+ android:drawable="@drawable/fasttrack_badge_dark_pressed" />
+
+</selector>
diff --git a/core/res/res/drawable/fasttrack_badge_dark_normal.9.png b/core/res/res/drawable/fasttrack_badge_dark_normal.9.png
new file mode 100644
index 0000000..52bb08c
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_dark_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png b/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png
new file mode 100644
index 0000000..84a6783
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_dark_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_light.xml b/core/res/res/drawable/fasttrack_badge_light.xml
new file mode 100644
index 0000000..fd81258
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_light.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_focused="false"
+ android:state_selected="false"
+ android:state_pressed="false"
+ android:drawable="@drawable/fasttrack_badge_light_normal" />
+
+ <item
+ android:state_pressed="true"
+ android:drawable="@drawable/fasttrack_badge_light_pressed" />
+
+</selector>
diff --git a/core/res/res/drawable/fasttrack_badge_light_normal.9.png b/core/res/res/drawable/fasttrack_badge_light_normal.9.png
new file mode 100644
index 0000000..595b179
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_light_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_light_pressed.9.png b/core/res/res/drawable/fasttrack_badge_light_pressed.9.png
new file mode 100644
index 0000000..8e3f557
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_light_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_middle.xml b/core/res/res/drawable/fasttrack_badge_middle.xml
new file mode 100644
index 0000000..6df230a
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_middle.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_focused="false"
+ android:state_selected="false"
+ android:state_pressed="false"
+ android:drawable="@drawable/fasttrack_badge_middle_normal" />
+
+ <item
+ android:state_pressed="true"
+ android:drawable="@drawable/fasttrack_badge_middle_pressed" />
+
+</selector> \ No newline at end of file
diff --git a/core/res/res/drawable/fasttrack_badge_middle_normal.9.png b/core/res/res/drawable/fasttrack_badge_middle_normal.9.png
new file mode 100644
index 0000000..07df063
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_middle_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable/fasttrack_badge_middle_pressed.9.png b/core/res/res/drawable/fasttrack_badge_middle_pressed.9.png
new file mode 100644
index 0000000..ded95f6
--- /dev/null
+++ b/core/res/res/drawable/fasttrack_badge_middle_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable/ic_contact_picture_2.png b/core/res/res/drawable/ic_contact_picture_2.png
new file mode 100644
index 0000000..8b184af
--- /dev/null
+++ b/core/res/res/drawable/ic_contact_picture_2.png
Binary files differ
diff --git a/core/res/res/drawable/ic_contact_picture_3.png b/core/res/res/drawable/ic_contact_picture_3.png
new file mode 100644
index 0000000..a2d08b5
--- /dev/null
+++ b/core/res/res/drawable/ic_contact_picture_3.png
Binary files differ
diff --git a/core/res/res/drawable/light_header.9.png b/core/res/res/drawable/light_header.9.png
deleted file mode 100644
index ad5dce1..0000000
--- a/core/res/res/drawable/light_header.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/light_header_dither.xml b/core/res/res/drawable/light_header_dither.xml
new file mode 100644
index 0000000..c54b6c2
--- /dev/null
+++ b/core/res/res/drawable/light_header_dither.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/light_header"
+ android:dither="true"
+/>
diff --git a/core/res/res/drawable/stat_sys_data_connected_1xrtt.png b/core/res/res/drawable/stat_sys_data_connected_1xrtt.png
deleted file mode 100644
index c2fbbdf..0000000
--- a/core/res/res/drawable/stat_sys_data_connected_1xrtt.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_connected_evdo.png b/core/res/res/drawable/stat_sys_data_connected_evdo.png
deleted file mode 100644
index db9f282..0000000
--- a/core/res/res/drawable/stat_sys_data_connected_evdo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_connected_h.png b/core/res/res/drawable/stat_sys_data_connected_h.png
new file mode 100644
index 0000000..7d5413a
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_data_connected_h.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png b/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png
deleted file mode 100755
index 11c2eae..0000000
--- a/core/res/res/drawable/stat_sys_data_dormant_1xrtt.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_dormant_evdo.png b/core/res/res/drawable/stat_sys_data_dormant_evdo.png
deleted file mode 100755
index 811fcb5..0000000
--- a/core/res/res/drawable/stat_sys_data_dormant_evdo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_1xrtt.png b/core/res/res/drawable/stat_sys_data_in_1xrtt.png
deleted file mode 100644
index a421a8a..0000000
--- a/core/res/res/drawable/stat_sys_data_in_1xrtt.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_evdo.png b/core/res/res/drawable/stat_sys_data_in_evdo.png
deleted file mode 100644
index 54f55ba..0000000
--- a/core/res/res/drawable/stat_sys_data_in_evdo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_in_h.png b/core/res/res/drawable/stat_sys_data_in_h.png
new file mode 100644
index 0000000..695b80c
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_data_in_h.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_1xrtt.png b/core/res/res/drawable/stat_sys_data_inandout_1xrtt.png
deleted file mode 100644
index 1a94cdf..0000000
--- a/core/res/res/drawable/stat_sys_data_inandout_1xrtt.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_evdo.png b/core/res/res/drawable/stat_sys_data_inandout_evdo.png
deleted file mode 100644
index 7aa6f00..0000000
--- a/core/res/res/drawable/stat_sys_data_inandout_evdo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_inandout_h.png b/core/res/res/drawable/stat_sys_data_inandout_h.png
new file mode 100644
index 0000000..467acd1
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_data_inandout_h.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_1xrtt.png b/core/res/res/drawable/stat_sys_data_out_1xrtt.png
deleted file mode 100644
index 74fd351..0000000
--- a/core/res/res/drawable/stat_sys_data_out_1xrtt.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_evdo.png b/core/res/res/drawable/stat_sys_data_out_evdo.png
deleted file mode 100644
index 21e19a7..0000000
--- a/core/res/res/drawable/stat_sys_data_out_evdo.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_data_out_h.png b/core/res/res/drawable/stat_sys_data_out_h.png
new file mode 100644
index 0000000..da50305
--- /dev/null
+++ b/core/res/res/drawable/stat_sys_data_out_h.png
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_0_cdma.png b/core/res/res/drawable/stat_sys_signal_0_cdma.png
deleted file mode 100644
index 0ef7d53..0000000
--- a/core/res/res/drawable/stat_sys_signal_0_cdma.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_1_cdma.png b/core/res/res/drawable/stat_sys_signal_1_cdma.png
deleted file mode 100644
index f4839d4..0000000
--- a/core/res/res/drawable/stat_sys_signal_1_cdma.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_2_cdma.png b/core/res/res/drawable/stat_sys_signal_2_cdma.png
deleted file mode 100644
index e25a99c..0000000
--- a/core/res/res/drawable/stat_sys_signal_2_cdma.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_3_cdma.png b/core/res/res/drawable/stat_sys_signal_3_cdma.png
deleted file mode 100644
index d828d99..0000000
--- a/core/res/res/drawable/stat_sys_signal_3_cdma.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_4_cdma.png b/core/res/res/drawable/stat_sys_signal_4_cdma.png
deleted file mode 100644
index 53a31ea..0000000
--- a/core/res/res/drawable/stat_sys_signal_4_cdma.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_cdma_0.png b/core/res/res/drawable/stat_sys_signal_cdma_0.png
deleted file mode 100755
index 0ef7d53..0000000
--- a/core/res/res/drawable/stat_sys_signal_cdma_0.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_cdma_1.png b/core/res/res/drawable/stat_sys_signal_cdma_1.png
deleted file mode 100755
index f4839d4..0000000
--- a/core/res/res/drawable/stat_sys_signal_cdma_1.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_cdma_2.png b/core/res/res/drawable/stat_sys_signal_cdma_2.png
deleted file mode 100755
index e25a99c..0000000
--- a/core/res/res/drawable/stat_sys_signal_cdma_2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_cdma_3.png b/core/res/res/drawable/stat_sys_signal_cdma_3.png
deleted file mode 100755
index d828d99..0000000
--- a/core/res/res/drawable/stat_sys_signal_cdma_3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_cdma_4.png b/core/res/res/drawable/stat_sys_signal_cdma_4.png
deleted file mode 100755
index 53a31ea..0000000
--- a/core/res/res/drawable/stat_sys_signal_cdma_4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_evdo_0.png b/core/res/res/drawable/stat_sys_signal_evdo_0.png
deleted file mode 100755
index 1b8aec7..0000000
--- a/core/res/res/drawable/stat_sys_signal_evdo_0.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_evdo_1.png b/core/res/res/drawable/stat_sys_signal_evdo_1.png
deleted file mode 100755
index 7ce01fd..0000000
--- a/core/res/res/drawable/stat_sys_signal_evdo_1.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_evdo_2.png b/core/res/res/drawable/stat_sys_signal_evdo_2.png
deleted file mode 100755
index 890cd59..0000000
--- a/core/res/res/drawable/stat_sys_signal_evdo_2.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_evdo_3.png b/core/res/res/drawable/stat_sys_signal_evdo_3.png
deleted file mode 100755
index 712c640..0000000
--- a/core/res/res/drawable/stat_sys_signal_evdo_3.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/stat_sys_signal_evdo_4.png b/core/res/res/drawable/stat_sys_signal_evdo_4.png
deleted file mode 100755
index f0537dd..0000000
--- a/core/res/res/drawable/stat_sys_signal_evdo_4.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_focus.9.png b/core/res/res/drawable/tab_focus.9.png
deleted file mode 100755
index 2806da9..0000000
--- a/core/res/res/drawable/tab_focus.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_focus_bar_left.9.png b/core/res/res/drawable/tab_focus_bar_left.9.png
deleted file mode 100755
index 21421cb..0000000
--- a/core/res/res/drawable/tab_focus_bar_left.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_focus_bar_right.9.png b/core/res/res/drawable/tab_focus_bar_right.9.png
deleted file mode 100755
index b6304d9..0000000
--- a/core/res/res/drawable/tab_focus_bar_right.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_press.9.png b/core/res/res/drawable/tab_press.9.png
deleted file mode 100755
index 3fb717c..0000000
--- a/core/res/res/drawable/tab_press.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_press_bar_left.9.png b/core/res/res/drawable/tab_press_bar_left.9.png
deleted file mode 100755
index 95ef2d3..0000000
--- a/core/res/res/drawable/tab_press_bar_left.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_press_bar_right.9.png b/core/res/res/drawable/tab_press_bar_right.9.png
deleted file mode 100755
index 7ae938b5..0000000
--- a/core/res/res/drawable/tab_press_bar_right.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_selected.9.png b/core/res/res/drawable/tab_selected.9.png
deleted file mode 100644
index f929d99..0000000
--- a/core/res/res/drawable/tab_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_selected_bar_left.9.png b/core/res/res/drawable/tab_selected_bar_left.9.png
deleted file mode 100755
index 58e2d35..0000000
--- a/core/res/res/drawable/tab_selected_bar_left.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_selected_bar_right.9.png b/core/res/res/drawable/tab_selected_bar_right.9.png
deleted file mode 100755
index 0c9c8dd..0000000
--- a/core/res/res/drawable/tab_selected_bar_right.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/tab_unselected.9.png b/core/res/res/drawable/tab_unselected.9.png
deleted file mode 100644
index 9036c1d..0000000
--- a/core/res/res/drawable/tab_unselected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/layout-ja/contact_header_name.xml b/core/res/res/layout-ja/contact_header_name.xml
new file mode 100644
index 0000000..9dceeb6
--- /dev/null
+++ b/core/res/res/layout-ja/contact_header_name.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- In Japanese-language locales, the "Name" field contains two separate
+ TextViews: the name itself, and also the phonetic ("furigana") field. -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+
+ <TextView android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ />
+
+ <TextView android:id="@+id/phonetic_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ />
+
+</LinearLayout>
diff --git a/core/res/res/layout/character_picker.xml b/core/res/res/layout/character_picker.xml
index 0344849..70867d0 100644
--- a/core/res/res/layout/character_picker.xml
+++ b/core/res/res/layout/character_picker.xml
@@ -15,35 +15,35 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+ android:orientation="horizontal"
android:layout_width="304dp"
- android:layout_height="fill_parent">
+ android:layout_height="fill_parent"
+ android:background="@drawable/keyboard_popup_panel_trans_background">
<GridView
android:id="@+id/characterPicker"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
android:padding="4dp"
- android:verticalSpacing="4dp"
+ android:verticalSpacing="8dp"
android:horizontalSpacing="8dp"
- android:stretchMode="spacingWidth"
android:gravity="left"
android:drawSelectorOnTop="false"
- android:listSelector="@drawable/grid_selector_background"
android:numColumns="4"
- android:columnWidth="64dp"
+ android:columnWidth="48dp"
android:fadingEdge="none"
- android:layout_gravity="center_horizontal"
+ android:layout_gravity="center_vertical"
+ android:listSelector="#0000"
/>
<Button
android:id="@+id/cancel"
- android:text="@string/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="50dp"
- android:paddingRight="50dp"
- android:gravity="center"
- android:layout_gravity="center_horizontal"
+ android:layout_marginLeft="12dp"
+ android:layout_marginRight="12dp"
+ android:background="@drawable/btn_close"
+ android:layout_gravity="center_vertical"
/>
</LinearLayout>
diff --git a/core/res/res/layout/character_picker_button.xml b/core/res/res/layout/character_picker_button.xml
index 40078fe..b74e620 100644
--- a/core/res/res/layout/character_picker_button.xml
+++ b/core/res/res/layout/character_picker_button.xml
@@ -15,11 +15,12 @@
-->
<Button xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clickable="false"
+ android:clickable="true"
android:focusable="false"
- android:textAppearance="?android:attr/textAppearanceLargeInverse"
- android:textColor="#FF000000"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:background="@drawable/btn_keyboard_key_trans"
+ android:textColor="#FFFFFFFF"
/>
diff --git a/core/res/res/layout/contact_header.xml b/core/res/res/layout/contact_header.xml
new file mode 100644
index 0000000..ba91e00
--- /dev/null
+++ b/core/res/res/layout/contact_header.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/banner"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/title_bar_tall"
+ android:paddingRight="5dip"
+ android:gravity="center_vertical">
+
+ <ImageView android:id="@+id/photo"
+ android:layout_width="48dip"
+ android:layout_height="52dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginLeft="10dip"
+ android:scaleType="fitCenter"
+ android:background="@drawable/fasttrack_badge_middle"/>
+
+ <LinearLayout
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="5dip"
+ android:orientation="vertical">
+
+ <!-- "Name" field is locale-specific. -->
+ <include layout="@layout/contact_header_name"/>
+
+ <TextView android:id="@+id/status"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:maxLines="2"
+ android:ellipsize="end"/>
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/presence"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="3dip"
+ android:paddingRight="6dip"/>
+
+ <CheckBox
+ android:id="@+id/star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ style="?android:attr/starStyle" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/contact_header_name.xml b/core/res/res/layout/contact_header_name.xml
new file mode 100644
index 0000000..9a56fb4
--- /dev/null
+++ b/core/res/res/layout/contact_header_name.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- In the default locale, the "Name" field is a single TextView -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="end"
+ />
diff --git a/core/res/res/layout/grant_credentials_permission.xml b/core/res/res/layout/grant_credentials_permission.xml
new file mode 100644
index 0000000..fe1c22e
--- /dev/null
+++ b/core/res/res/layout/grant_credentials_permission.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/message" />
+ <Button android:id="@+id/allow"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/allow" />
+
+ <Button android:id="@+id/deny"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/deny" />
+
+ <ListView android:id="@+id/packages_list"
+ android:layout_width="fill_parent" android:layout_height="fill_parent"/>
+
+</LinearLayout>
diff --git a/core/res/res/layout/keyguard_screen_glogin_unlock.xml b/core/res/res/layout/keyguard_screen_glogin_unlock.xml
index 74ff3b0..6e00d11 100644
--- a/core/res/res/layout/keyguard_screen_glogin_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_glogin_unlock.xml
@@ -42,7 +42,6 @@
android:gravity="center_vertical"
android:drawableLeft="@drawable/ic_lock_idle_lock"
android:drawablePadding="5dip"
- android:text="@android:string/lockscreen_glogin_too_many_attempts"
/>
<!-- spacer below header -->
diff --git a/core/res/res/layout/preference_dialog.xml b/core/res/res/layout/preference_dialog.xml
new file mode 100644
index 0000000..5cf0f6e
--- /dev/null
+++ b/core/res/res/layout/preference_dialog.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<!-- Layout used by DialogPreference widgets. This is inflated inside
+ android.R.layout.preference. -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="4dip"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/btn_circle"
+ android:src="@drawable/ic_btn_round_more" />
+
diff --git a/core/res/res/layout/tab_indicator.xml b/core/res/res/layout/tab_indicator.xml
index fcf0b5e..e3ea555 100644
--- a/core/res/res/layout/tab_indicator.xml
+++ b/core/res/res/layout/tab_indicator.xml
@@ -18,6 +18,8 @@
android:layout_width="0dip"
android:layout_height="64dip"
android:layout_weight="1"
+ android:layout_marginLeft="-4px"
+ android:layout_marginRight="-4px"
android:orientation="vertical"
android:background="@android:drawable/tab_indicator">
diff --git a/core/res/res/layout/zoom_browser_accessory_buttons.xml b/core/res/res/layout/zoom_browser_accessory_buttons.xml
index 69afca9..4bf2bdf 100644
--- a/core/res/res/layout/zoom_browser_accessory_buttons.xml
+++ b/core/res/res/layout/zoom_browser_accessory_buttons.xml
@@ -18,11 +18,18 @@
*/
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <ImageView android:id="@+id/zoom_page_overview"
- android:background="@android:drawable/btn_browser_zoom_page_overview"
+ <ImageView android:id="@+id/zoom_fit_page"
+ android:background="@android:drawable/btn_browser_zoom_fit_page"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|left"
android:layout_marginLeft="7dip"
/>
+ <ImageView android:id="@+id/zoom_page_overview"
+ android:background="@android:drawable/btn_browser_zoom_page_overview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom|right"
+ android:layout_marginRight="7dip"
+ />
</merge>
diff --git a/core/res/res/raw/loaderror.html b/core/res/res/raw/loaderror.html
index 359a1e7..fd3d766 100644
--- a/core/res/res/raw/loaderror.html
+++ b/core/res/res/raw/loaderror.html
@@ -1,5 +1,6 @@
<html>
<head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
<title>Web page not available</title>
<style type="text/css">
body { margin-top: 0px; padding-top: 0px; }
diff --git a/core/res/res/raw/nodomain.html b/core/res/res/raw/nodomain.html
index 7a107fb..a71dbcd 100644
--- a/core/res/res/raw/nodomain.html
+++ b/core/res/res/raw/nodomain.html
@@ -1,5 +1,6 @@
<html>
<head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"/>
<title>Web page not available</title>
<style type="text/css">
body { margin-top: 0px; padding-top: 0px; }
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8c6704e..85e1007 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"K souboru nelze získat přístup."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Požadovaný soubor nebyl nalezen."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Je zpracováváno příliš mnoho požadavků. Opakujte akci později."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Chyba přihlášení"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchronizace"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronizace"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Příliš mnoho smazaných položek služby <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"změna statistických údajů o baterii"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Umožňuje zmÄ›nu shromáždÄ›ných statistických údajů o baterii. Není urÄeno pro běžné aplikace."</string>
<string name="permlab_backup" msgid="470013022865453920">"ovládání zálohování a obnovy systému"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Umožňuje aplikaci ovládat mechanizmus zálohování a obnovy systému. Není urÄeno k použití v běžných aplikacích."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"zobrazení nepovolených oken"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Umožňuje vytvoření oken, která mají být použita interním systémem uživatelského rozhraní. Běžné aplikace toto nastavení nepoužívají."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"zobrazení upozornění systémové úrovně"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Umožňuje aplikacím sledovat, které klávesy používáte, a to i při práci s jinými aplikacemi (například při zadávání hesla). Běžné aplikace by toto nastavení nikdy neměly vyžadovat."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"vazba k metodě zadávání dat"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Umožňuje držiteli vázat se na nejvyšší úroveň rozhraní pro zadávání dat. Běžné aplikace by toto nastavení nikdy neměly využívat."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"vazba na tapetu"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Umožňuje držiteli navázat se na nejvyšší úroveň rozhraní tapety. Běžné aplikace by toto oprávnění nikdy neměly potřebovat."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"změna orientace obrazovky"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikaci kdykoli změnit orientaci obrazovky. Běžné aplikace by toto nastavení nikdy neměly využívat."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odeslání signálů Linux aplikacím"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"změna globálních nastavení systému"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Umožňuje aplikaci upravit data nastavení systému. Škodlivé aplikace mohou poškodit konfiguraci vašeho systému."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"zmÄ›ny zabezpeÄených nastavení systému"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Umožňuje aplikaci zmÄ›nit data zabezpeÄených nastavení systému. Běžné aplikace toto nastavení nevyužívají."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"změna mapy služeb Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Umožňuje aplikaci změnit mapu služeb Google. Běžné aplikace toto nastavení nevyužívají."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"automatické spuštění při startu"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"přístup ke službě SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Umožňuje aplikaci používat nízkoúrovňové funkce SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Ätení vyrovnávací pamÄ›ti snímků"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Umožňuje aplikaci naÄíst obsah vyrovnávací pamÄ›ti snímků."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Umožňuje aplikaci Äíst obsah vyrovnávací pamÄ›ti snímků."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"změna vašeho nastavení zvuku"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Umožňuje aplikaci zmÄ›nit globální nastavení zvuku, například hlasitost Äi smÄ›rování."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"nahrání zvuku"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Umožňuje aplikaci bez vaÅ¡eho zásahu volat na telefonní Äísla. Å kodlivé aplikace mohou na váš telefonní úÄet pÅ™ipsat neoÄekávané hovory. Toto nastavení aplikaci neumožňuje volat na tísňové linky."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"přímé volání na libovolná telefonní Äísla"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Umožňuje aplikaci bez vaÅ¡eho zásahu vytoÄit jakékoli telefonní Äíslo, vÄetnÄ› Äísel tísňového volání. Å kodlivé aplikace mohou provádÄ›t zbyteÄná a nezákonná volání na tísňové linky."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"ovládání oznámení o aktualizaci polohy"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Umožňuje povolit Äi zakázat aktualizace polohy prostÅ™ednictvím bezdrátového pÅ™ipojení. Aplikace toto nastavení obvykle nepoužívají."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"přístup k vlastnostem Checkin"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Umožňuje aplikaci kompletně obnovit systém do továrního nastavení a vymazat všechna data, konfiguraci a nainstalované aplikace."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"nastavení Äasového pásma"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Umožňuje aplikaci zmÄ›nit Äasové pásmo telefonu."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"role služby AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Umožňuje aplikaci volat funkce AccountAuthenticator"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"odhalení známých úÄtů"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Umožňuje aplikaci získat seznam úÄtů v telefonu."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"role ověřovatele úÄtu"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Umožňuje aplikaci používat funkce aplikace AccountManager související s ověřováním úÄtů – vÄetnÄ› vytváření úÄtů a získávání a nastavování hesel."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"správa seznamu úÄtů"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Umožňuje aplikaci provádÄ›t operace, jako je pÅ™idávání nebo odebírání úÄtů nebo mazání jejich hesel."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"používání ověřovacích pověření úÄtu"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Umožňuje aplikaci požadovat ověřovací tokeny."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"zobrazení stavu sítě"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Umožňuje aplikaci zobrazit stav všech sítí."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"plný přístup k Internetu"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Ostatní"</item>
<item msgid="2374913952870110618">"Vlastní"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobilní"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Domů"</item>
<item msgid="5629153956045109251">"Práce"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"<xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávnÄ› nakreslili své bezpeÄnostní gesto. Po dalších neúspěšných pokusech (<xliff:g id="NUMBER_1">%d</xliff:g>) budete požádáni o odemÄení telefonu pomocí pÅ™ihlášení do úÄtu Google."\n\n" Akci prosím opakujte za nÄ›kolik sekund (<xliff:g id="NUMBER_2">%d</xliff:g>)."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Sekundy zbývající do dalšího pokusu: <xliff:g id="NUMBER">%d</xliff:g>."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Zapomněli jste gesto?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Gesta: Příliš mnoho pokusů"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Chcete-li telefon odemknout, pÅ™ihlaste se pomocí svého úÄtu Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Uživatelské jméno (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Heslo"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Přihlásit se"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Neplatné uživatelské jméno nebo heslo."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Vymazat"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Prosím připojte dobíjecí zařízení"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Baterie je vybitá:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"zbývá méně než <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"ProÄ?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Test továrního nastavení se nezdařil"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Test FACTORY_TEST lze provést pouze u balíÄků nainstalovaných ve složce /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nebyl nalezen žádný balíÄek umožňující test FACTORY_TEST."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"zítra"</item>
<item quantity="other" msgid="2973062968038355991">"zbývající poÄet dní: <xliff:g id="COUNT">%d</xliff:g>"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"v roce %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"den"</string>
<string name="days" msgid="4774547661021344602">"d."</string>
<string name="hour" msgid="2126771916426189481">"hodina"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Kopírovat vše"</string>
<string name="paste" msgid="5629880836805036433">"Vložit"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopírovat adresu URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Metoda zadávání dat"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Přidat „%s“ do slovníku"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Úpravy textu"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Málo paměti"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"V telefonu zbývá málo místa pro ukládání dat."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Hlasitost vyzvánění"</string>
<string name="volume_music" msgid="5421651157138628171">"Hlasitost médií"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Přehrávání pomocí rozhraní Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Je vybrán tichý vyzváněcí tón"</string>
<string name="volume_call" msgid="3941680041282788711">"Hlasitost hovoru"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Hlasitost příchozích hovorů při připojení Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Hlasitost budíku"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"PÅ™ed vypnutím úložiÅ¡tÄ› USB se pÅ™esvÄ›dÄte, zda byl hostitel USB odpojen. ÚložiÅ¡tÄ› USB vypnete volbou Vypnout."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Vypnout"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Zrušit"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Při vypínání úložiště USB došlo k problémům. Zkontrolujte, zda byl hostitel USB odpojen, a zkuste to znovu."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formátovat kartu SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Opravdu chcete kartu SD naformátovat? Všechna data na kartě budou ztracena."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formátovat"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Vytvořit kontakt"\n"pro <xliff:g id="NUMBER">%s</xliff:g>."</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"Zaškrtnuto"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"Nezaškrtnuto"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Povolit"</string>
+ <string name="deny" msgid="2081879885755434506">"Odepřít"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Požadováno oprávnění"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ef8490b..ec4c911 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"Gb"</string>
<string name="terabyteShort" msgid="231613018159186962">"Tb"</string>
<string name="petabyteShort" msgid="5637816680144990219">"Pb"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;uden navn&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Intet telefonnummer)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Opkaldsspærring"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Ændring af adgangskode"</string>
<string name="PinMmi" msgid="3113117780361190304">"ændring af PIN-kode"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Opkaldsnummer til stede"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Opkaldsnummer begrænset"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Trevejsopkald"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Afvisning af uønskede, irriterende opkald"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Opkaldsnummer levering"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Forstyr ikke"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Standarder for opkalds-id til begrænset. Næste opkald: Begrænset"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Standarder for opkalds-id til begrænset. Næste opkald: Ikke begrænset"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Standarder for opkalds-id til ikke begrænset. Næste opkald: Begrænset"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Synkroniser"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Pakke"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Roamingindikator til"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Roamingindikator fra"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Roamingindikator blinker"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Ude af kvarteret"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Ude af bygningen"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Roaming – Foretrukket system"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming – Tilgængeligt system"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming – Alliance Partner"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming – Premium Partner"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming – Fuld servicefunktionalitet"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming – Delvis servicefunktionalitet"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Roamingbanner til"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Roamingbanner fra"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Søger efter tjeneste"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke videresendt"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke videresendt"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke videresendt"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Funktionskode komplet."</string>
+ <string name="fcError" msgid="3327560126588500777">"Forbindelsesproblemer eller ugyldig funktionskode."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="2567300624552921790">"Websiden indeholder en fejl."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Webadressen kunne ikke findes."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Der kunne ikke oprettes adgang til filen."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Den anmodede fil blev ikke fundet."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Der behandles for mange anmodninger. Prøv igen senere."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Loginfejl"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synkroniser"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkroniser"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"For mange <xliff:g id="CONTENT_TYPE">%s</xliff:g> sletninger"</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Adgang og kontrol til systemet på lavere niveau."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Udviklingsværktøjer"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Funktioner kun til programudviklere."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"FÃ¥ adgang til SD-kortet."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"deaktiver eller rediger statuslinje"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Tillader et program at deaktivere statuslinjen eller tilføje eller fjerne systemikoner."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"udvid/skjul statuslinje"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Tillader et program at tvinge alle programmer, der er i forgrunden, til at lukke og gå tilbage. Bør aldrig være nødvendigt til normale programmer."</string>
<string name="permlab_dump" msgid="1681799862438954752">"hent intern systemtilstand"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Tillader et program at hente systemets interne tilstand. Ondsindede programmer kan hente en række private og sikre oplysninger, som de normalt aldrig bør have brug for."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"delvis lukning"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Sætter aktivitetsadministratoren i lukningstilstand. Lukker ikke helt ned."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"undgå programskift"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Forhindrer brugeren i at skifte til et andet program."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"overvåg og kontroller start af alle programmer"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Tillader et program at overvåge og kontrollere, hvordan systemet starter aktiviteter. Ondsindede programmer kan fuldstændig kompromittere systemet. Denne tilladelse er kun nødvendig til udvikling, aldrig til normal telefonbrug."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"send udsendelse om fjernet pakke"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Tillader et program at kontrollere, om aktiviteter altid afsluttes, så snart de går i baggrunden. Aldrig nødvendigt til normale programmer."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"rediger batteristatistikker"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Tillader ændring af indsamlede batteristatistikker. Ikke til brug for normale programmer."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"kontroller sikkerhedskopiering af system, og gendan"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserede vinduer"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillader oprettelse af vinduer, der er beregnet til at blive brugt af den interne systembrugergrænseflade. Ikke til brug for normale programmer."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Tillader et program at holde øje med de taster, du trykker på, selv når du interagerer med andre programmer (som f.eks. indtastning af adgangskode). Bør aldrig være nødvendigt til normale programmer."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"forpligt til en inputmetode"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Tillader brugeren at forpligte sig på en inputmetodes grænseflade på øverste niveau. Bør aldrig være nødvendig til normale programmer."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"forpligt til et tapet"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Tillader brugeren at forpligte sig på et tapets grænseflade på øverste niveau. Bør aldrig være nødvendig til normale programmer."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"skift skærmretning"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillader et program at ændre rotationen af skærmen når som helst. Bør aldrig være nødvendigt til normale programmer."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux-signaler til programmer"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"rediger globale systemindstillinger"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Tillader et program at ændre systemets indstillingsdata. Ondsindede programmer kan skade systemets konfiguration."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"rediger sikre systemindstillinger"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Tillader et program at ændre systemernes sikre indstillingsdata. Ikke til brug til almindelige programmer."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"rediger kortet over Google-tjenester"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Tillader et program at ændre kortet over Google-tjenester. Ikke til brug til normale programmer."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"start automatisk ved opstart"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Opret imiterede placeringskilder til testning. Ondsindede programmer kan bruge dette til at tilsidesætte den returnerede placering og/eller status fra rigtige placeringskilder som f.eks. GPS eller netværksudbydere."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få adgang til ekstra kommandoer for placeringsudbyder"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"FÃ¥ adgang til ekstra kommandoer fra placeringsudbyder. Ondsindede programmer kan bruge dette til at gribe ind i driften af GPS\'en eller andre placeringskilder."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"tilladelse til at installere en placeringsudbyder"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Opret imiterede placeringskilder til testning. Ondsindede programmer kan bruge dette til at tilsidesætte den returnerede placering og/eller status fra rigtige placeringskilder som f.eks. GPS eller netværksudbydere eller til at overvåge og rapportere din placering til en ekstern kilde."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"fin (GPS) placering"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Få adgang til gode placeringskilder, som f.eks. Global Positioning System på telefonen, når det er tilgængeligt. Ondsindede programmer kan bruge dette til at finde ud af, hvor du er og kan eventuelt bruge yderligere batterikapacitet."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"grov (netværksbaseret) placering"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"få adgang til SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Tillader et program at bruge SurfaceFlinger-funktioner på lavt niveau."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"læs rammebuffer"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Tillader et program at læse/bruge indholdet fra rammebufferen."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Tillader et program at læse indholdet fra rammebufferen."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"skift dine lydindstillinger"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Tillader et program at ændre globale lydindstillinger som f.eks. lydstyrke og kanalisering."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"optag lyd"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Tillader programmet at ringe til telefonnumre uden din indgriben. Ondsindede programmer kan forårsage uventede opkald på din telefonregning. Vær opmærksom på, at det ikke tillader programmet at ringe til nødnumre."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"ring direkte op til alle telefonnumre"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Tillader programmet at ringe til alle telefonnumre inklusive nødnumre uden din indgriben. Ondsindede programmer kan eventuelt foretage unødvendige og ulovlige opkald til nødtjenester."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"kontroller meddelelser om placeringsopdatering"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Tillader aktivering/deaktivering af placeringsdata fra radioen. Ikke til brug til normale programmer."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"egenskaber for adgangskontrol"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Tillader programmet at fortælle systemet, hvilke widgets der kan bruges af hvilke programmer. Med denne tilladelse kan programmer give adgang til personlige data til andre programmer. Ikke til brug til normale programmer."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"rediger telefontilstand"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Tillader programmet at kontrollere enhedens telefonfunktioner. Et program med denne tilladelse kan skifte netværk, slå telefonens radio til og fra og lignende uden nogensinde at underrette dig."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"læs telefontilstand og identitet"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Tillader programmet at få adgang til enhedens telefonfunktioner. Et program med denne tilladelse kan afgøre denne telefons telefon- og serienummer, om et opkald er aktivt, nummeret som opkaldet er forbundet til osv."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"afhold telefonen fra at gå i dvale"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Tillader et program at forhindre telefonen i at gå i dvale."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"Tænd eller sluk for telefonen"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Tillader et program fuldstændig at nulstille systemet til fabriksindstillingerne, slette alle data, konfigurationen og installerede programmer."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"indstil tidszone"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Tillader et program at ændre telefonens tidszone."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"fungerer som kontoadministrationstjeneste"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Tillader et program at foretage opkald til kontogodkendere"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"opdag kendte konti"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Tillader et program at hente listen over konti, der er kendt af telefonen."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"fungerer som en kontogodkender"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Tillader et program at bruge kontoadministratorens kontogodkendelsesegenskaber, bl.a. oprettelse af konti samt hentning og indstilling af deres adgangskoder."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrer kontilisten"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Tillader et program at foretage handlinger, som f.eks. at tilføje og fjerne konti samt slette deres adgangskode."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"brug en kontos godkendelsesoplysninger"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Tillader et program at anmode om godkendelsestokens"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"vis netværkstilstand"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Tillader et program at vise tilstanden for alle netværk."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"Fuld internetadgang"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Tillader et program at vise oplysninger om Wi-Fi-tilstanden."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"skift Wi-Fi-tilstand"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Tillader et program at oprette og afbryde forbindelse fra Wi-Fi-adgangspunkter og foretage ændringer til konfigurerede Wi-Fi-netværk."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillad Wi-Fi-multicastmodtagelse"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Tillader et program at modtage pakker, der ikke er direkte adresseret til din enhed. Dette kan være nyttigt, hvis du finder tjenester, der er tilbudt i nærheden. Det bruger mere strøm end multicasttilstanden."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"bluetooth-administration"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Tillader et program at konfigurere den lokale Bluetooth-telefon samt at opdage og parre med fjerne enheder."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"opret Bluetooth-forbindelser"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Tillader et program at læse alle private ord, navne og sætninger, som brugeren eventuelt har gemt i brugerordbogen."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"skriv til den brugerdefinerede mappe"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Tillader et program at skrive nye ord i brugermappen."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"ret/slet indholdet af SD-kort"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Tillader et program at skrive til SD-kortet."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Start"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Andre"</item>
<item msgid="2374913952870110618">"Tilpasset"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Start"</item>
<item msgid="5629153956045109251">"Arbejde"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Rigtigt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Beklager! Prøv igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Oplader (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Opladt."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Tilslut din oplader."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Der er ikke noget SIM-kort."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Der er ikke noget SIM-kort i telefonen."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har tegnet dit mønster til at låse op forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> forsøg mere vil du blive bedt om at låse din telefon op ved hjælp af dit Google-login"\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Prøv igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Har du glemt mønster?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"For mange mønsterforsøg!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"For at låse op skal du logge ind med din Google-konto"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Brugernavn (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Adgangskode"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Log ind"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Ugyldigt brugernavn eller ugyldig adgangskode."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ryd"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ingen meddelelser"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Løbende"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelelser"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Forbind oplader"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er ved at blive tomt:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"mindre end <xliff:g id="NUMBER">%d%%</xliff:g> tilbage."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrikstest mislykkedes"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Handlingen FACTORY_TEST understøttes kun af pakker installeret i /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"Javascript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger væk fra denne side?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" Vælg OK for at fortsætte eller Annuller for at blive på den aktuelle side."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekræft"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader programmet at læse alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillader et program at ændre browseroversigten eller bogmærker, der er gemt på din telefon. Ondsindede programmer kan bruge dette til at slette eller ændre din browsers data."</string>
<string name="save_password_message" msgid="767344687139195790">"Ønsker du, at browseren skal huske denne adgangskode?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Ikke nu"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Husk"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"i morgen"</item>
<item quantity="other" msgid="2973062968038355991">"om <xliff:g id="COUNT">%d</xliff:g> dage"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"den %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"kl. %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"i %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dag"</string>
<string name="days" msgid="4774547661021344602">"dage"</string>
<string name="hour" msgid="2126771916426189481">"time"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Kopier alle"</string>
<string name="paste" msgid="5629880836805036433">"Indsæt"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopier webadresse"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Inputmetode"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Føj \"%s\" til ordbog"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Rediger tekst"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Der er ikke så meget plads tilbage"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Der er næsten ikke mere plads på telefonen."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"Programmet <xliff:g id="APPLICATION">%1$s</xliff:g> (igangværende <xliff:g id="PROCESS">%2$s</xliff:g>) svarer ikke."</string>
<string name="anr_process" msgid="1246866008169975783">"Processen <xliff:g id="PROCESS">%1$s</xliff:g> svarer ikke."</string>
<string name="force_close" msgid="3653416315450806396">"Tving til at lukke"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Rapporter"</string>
<string name="wait" msgid="7147118217226317732">"Vent"</string>
<string name="debug" msgid="9103374629678531849">"Fejlretning"</string>
<string name="sendText" msgid="5132506121645618310">"Vælg en handling for teksten"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Opkaldslydstyrke"</string>
<string name="volume_music" msgid="5421651157138628171">"Medielydstyrke"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Spiller gennem Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Lydløs ringetone valgt"</string>
<string name="volume_call" msgid="3941680041282788711">"Opkaldslydstyrke"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Bluetooth-lydstyrke under opkald"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Alarmlydstyrke"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Inden du slår USB-lagringen fra, skal du sørge for, at du demonterer USB-værten. Vælg \"Slå fra\" for at slå USB-lagringen fra."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Slå fra"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Annuller"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Der opstod et problem med at slå USB-lagringen fra. Sørg for, at du har demonteret USB-værten, og prøv så igen."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formater SD-kort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Er du sikker på, du ønsker at formatere SD-kortet? Alle data på kortet mistes."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formater"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-fejlretning forbundet"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Der er forbundet en computer til din telefon."</string>
<string name="select_input_method" msgid="2086499663193509436">"Vælg inputmetode"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"Forbereder SD-kort"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Kontrollerer for fejl."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Blankt SD-kort"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-kortet er tomt eller har et ikke understøttet filsystem."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Beskadiget SD-kort"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD-kort beskadiget. Du bliver muligvis nødt til at omformatere det."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD-kort blev uventet fjernet"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Demonter SD-kortet inden fjernelse for at undgå tab af data."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD-kortet kan fjernes sikkert"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Du kan nu fjerne SD-kortet."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"SD-kortet er fjernet"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD-kortet er fjernet. Indsæt et nyt."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Der blev ikke fundet nogen matchende aktiviteter"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"opdater brugerstatistikker for komponenter"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Tillader ændring af indsamlede brugerstatistikker for komponenter. Ikke til brug til normale programmer."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Udfør"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Ring til nummer"\n"ved hjælp af <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Opret kontakt"\n"ved hjælp af <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"kontrolleret"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke kontrolleret"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Tillad"</string>
+ <string name="deny" msgid="2081879885755434506">"Afvis"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Der er anmodet om tilladelse"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 10a736c..8792dd1 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Auf die Datei konnte nicht zugegriffen werden."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Die angeforderte Datei wurde nicht gefunden."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Es werden zurzeit zu viele Anfragen verarbeitet. Versuchen Sie es später erneut."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Fehler bei der Anmeldung"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchronisieren"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisieren"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Zu viele <xliff:g id="CONTENT_TYPE">%s</xliff:g> gelöscht."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"Akku-Daten ändern"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Ermöglicht die Änderung von gesammelten Akku-Daten. Nicht für normale Anwendungen vorgesehen."</string>
<string name="permlab_backup" msgid="470013022865453920">"Systemsicherung und -wiederherstellung kontrollieren"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Ermöglicht der Anwendung, den Sicherungs- und Wiederherstellungsmechanismus des Systems zu kontrollieren. Nicht für normale Anwendungen vorgesehen."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"nicht autorisierte Fenster anzeigen"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Ermöglicht die Erstellung von Fenstern, die von der Benutzeroberfläche des internen Systems verwendet werden. Nicht für normale Anwendungen geeignet."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"Warnungen auf Systemebene anzeigen"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Ermöglicht Anwendungen, die von Ihnen gedrückten Tasten zu überwachen (etwa die Eingabe eines Passworts). Dies gilt auch für die Interaktion mit anderen Anwendungen. Sollte für normale Anwendungen nicht benötigt werden."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"An eine Eingabemethode binden"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Ermöglicht dem Halter, sich an die Oberfläche einer Eingabemethode auf oberster Ebene zu binden. Sollte nie für normale Anwendungen benötigt werden."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"An ein Hintergrundbild binden"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Ermöglicht dem Halter, sich an die Oberfläche einer Eingabemethode auf oberster Ebene zu binden. Sollte nie für normale Anwendungen benötigt werden."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"Bildschirmausrichtung ändern"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Ermöglicht der Anwendung, die Bildschirmdrehung jederzeit zu ändern. Sollte nicht für normale Anwendungen benötigt werden."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-Signale an Anwendungen senden"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"Allgemeine Systemeinstellungen ändern"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Ermöglicht einer Anwendung, die Einstellungsdaten des Systems zu ändern. Schädliche Anwendungen können so die Systemkonfiguration beschädigen."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"Sicherheitseinstellungen für das System ändern"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Ermöglicht einer Anwendung, die Daten der Sicherheitseinstellungen des Systems zu ändern. Nicht für normale Anwendungen vorgesehen."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"Google Services Map ändern"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Ermöglicht einer Anwendung, Änderungen an der Google Services Map vorzunehmen. Nicht für normale Anwendungen vorgesehen."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"Automatisch nach dem Booten starten"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Auf SurfaceFlinger zugreifen"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Ermöglicht einer Anwendung, die systemnahen SurfaceFlinger-Funktionen zu verwenden."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Frame-Puffer lesen"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Ermöglicht einer Anwendung, den Inhalt des Frame-Puffers zu lesen."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Ermöglicht einer Anwendung, den Content des Frame-Puffers zu lesen."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Audio-Einstellungen ändern"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Ermöglicht der Anwendung, Änderungen an allgemeinen Audioeinstellungen wie Lautstärke und Weiterleitung vorzunehmen."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"Audio aufnehmen"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Ermöglicht dem Anwendungen, Rufnummern ohne Ihr Eingreifen zu wählen. Schädliche Anwendungen können für unerwartete Anrufe auf Ihrer Telefonrechnung verantwortlich sein. Das Wählen von Notrufnummern ist allerdings nicht möglich."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"Alle Telefonnummern direkt anrufen"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Ermöglicht der Anwendung, ohne Ihr Eingreifen eine beliebige Telefonnummer zu wählen, einschließlich Notfallnummern. Schädliche Anwendungen können so unnötige und illegale Anrufe an Notdienste tätigen."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"Benachrichtigungen für Standortaktualisierung steuern"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Ermöglicht die Aktivierung/Deaktivierung der Mobilfunkbenachrichtigungen über Standort-Updates. Nicht für normale Anwendungen vorgesehen."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"Auf Check-In-Eigenschaften zugreifen"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Ermöglicht einer Anwendung, das System komplett auf Werkseinstellung zurückzusetzen. Hierbei werden alle Daten, Konfigurationen und installierten Anwendungen gelöscht."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"Zeitzone festlegen"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Ermöglicht einer Anwendung, die Zeitzone des Telefons zu ändern."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"Als Konto-Manager fungieren"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Ermöglicht einer Anwendung, Anrufe an Konto-Authentifizierer zu tätigen."</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"bekannte Konten suchen"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Ermöglicht einer Anwendung, eine Liste der dem Telefon bekannten Konten abzurufen."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"Als Kontoauthentifizierer fungieren"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Ermöglicht einer Anwendung, die Kontoauthentifizierungsfunktionen des Konto-Managers zu verwenden, einschließlich die Funktionen zum Erstellen von Konten und zum Abrufen und Einstellen der entsprechenden Passwörter."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"Kontoliste verwalten"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Ermöglicht einer Anwendung, Konten hinzuzufügen und zu entfernen oder deren Passwörter zu löschen."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"Authentifizierungsinformationen eines Kontos verwenden"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Ermöglicht einer Anwendung, Authentifizierungs-Token anzufordern."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"Netzwerkstatus anzeigen"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Ermöglicht einer Anwendung, den Status aller Netzwerke anzuzeigen."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"Uneingeschränkter Internetzugriff"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Andere"</item>
<item msgid="2374913952870110618">"Benutzerdefiniert"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Privat"</item>
<item msgid="5629153956045109251">"Arbeit"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe Ihrer Google-Anmeldeinformationen zu entsperren. "\n\n"Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Versuchen Sie es in <xliff:g id="NUMBER">%d</xliff:g> Sekunden erneut."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Muster vergessen?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Zu viele Versuche!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Melden Sie sich zum Entsperren mit Ihrem Google-Konto an."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nutzername (E-Mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Passwort"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Anmelden"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Ungültiger Nutzername oder ungültiges Passwort."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Löschen"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Ladegerät anschließen"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Akku ist fast leer."</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"Nur noch weniger als <xliff:g id="NUMBER">%d%%</xliff:g> vorhanden."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Warum?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Werkstest fehlgeschlagen"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Die Aktion FACTORY_TEST wird nur für unter \"/system/app\" gespeicherte Pakete unterstützt."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Es wurden kein Paket mit der Aktion FACTORY_TEST gefunden."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"morgen"</item>
<item quantity="other" msgid="2973062968038355991">"in <xliff:g id="COUNT">%d</xliff:g> Tagen"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"am %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"am %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"in %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"Tag"</string>
<string name="days" msgid="4774547661021344602">"Tage"</string>
<string name="hour" msgid="2126771916426189481">"Stunde"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Alles kopieren"</string>
<string name="paste" msgid="5629880836805036433">"Einfügen"</string>
<string name="copyUrl" msgid="2538211579596067402">"URL kopieren"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Eingabemethode"</string>
- <string name="addToDictionary" msgid="726256909274177272">"\"%s\" dem Wörterbuch hinzufügen"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Text bearbeiten"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Geringer Speicher"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Kaum noch freier Telefonspeicher verfügbar."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Klingeltonlautstärke"</string>
<string name="volume_music" msgid="5421651157138628171">"Medienlautstärke"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Wiedergabe durch Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Lautlos-Modus ausgewählt"</string>
<string name="volume_call" msgid="3941680041282788711">"Hörerlautstärke"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Lautstärke bei eingehendem Bluetooth-Anruf"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Lautstärke für Alarm"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Bevor Sie den USB-Speicher deaktivieren, stellen Sie sicher, dass Sie Ihn vom USB-Host getrennt haben. Wählen Sie \"Deaktivieren\", um den USB-Speicher zu deaktivieren."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Ausschalten"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Abbrechen"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Wir haben beim Deaktivieren des USB-Speichers ein Problem festgestellt. Überprüfen Sie, ob Sie den USB-Host getrennt haben, und versuchen Sie es erneut."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"SD-Karte formatieren"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Möchten Sie die SD-Karte wirklich formatieren? Alle Daten auf Ihrer Karte gehen dann verloren."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Neuer Kontakt"\n"mit <xliff:g id="NUMBER">%s</xliff:g> erstellen"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aktiviert"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"nicht aktiviert"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Zulassen"</string>
+ <string name="deny" msgid="2081879885755434506">"Ablehnen"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Berechtigung angefordert"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ba39b94..6516883 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;χωÏίς τίτλο&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Δεν υπάÏχει τηλεφωνικός αÏιθμός)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"ΦÏαγή κλήσεων"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης"</string>
<string name="PinMmi" msgid="3113117780361190304">"Αλλαγή αÏÎ¹Î¸Î¼Î¿Ï PIN"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"ΥπάÏχει αÏιθμός κλήσης"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"ΑπόκÏυψη αÏÎ¹Î¸Î¼Î¿Ï ÎºÎ»Î®ÏƒÎ·Ï‚"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"ΤÏιμεÏής κλήση"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"ΑπόÏÏιψη ανεπιθÏμητων, ενοχλητικών κλήσεων"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"ΠαÏάδοση καλοÏμενου αÏιθμοÏ"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Μην ενοχλείτε"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Η αναγνώÏιση κλήσης βÏίσκεται από Ï€Ïοεπιλογή στην \"πεÏιοÏισμένη\". Επόμενη κλήση: ΠεÏιοÏισμένη."</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Η αναγνώÏιση κλήσης βÏίσκεται από Ï€Ïοεπιλογή στην \"πεÏιοÏισμένη\". Επόμενη κλήση: Μη πεÏιοÏισμένη"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Η αναγνώÏιση κλήσης βÏίσκεται από Ï€Ïοεπιλογή στην \"μη πεÏιοÏισμένη\". Επόμενη κλήση: ΠεÏιοÏισμένη."</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"ΣυγχÏονισμός"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Πακέτο"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"ΕνεÏγή ένδειξη πεÏιαγωγής"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"ΑνενεÏγή ένδειξη πεÏιαγωγής"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Η ένδειξη πεÏιαγωγής αναβοσβήνει"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Εκτός γειτονίας"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Εκτός κτιÏίου"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"ΠεÏιαγωγή - ΠÏοτιμώμενο σÏστημα"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"ΠεÏιαγωγή - Διαθέσιμο σÏστημα"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"ΠεÏιαγωγή - ΣυνεÏγάτης Alliance"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"ΠεÏιαγωγή - ΣυνεÏγάτης με λογαÏιασμό Premium"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"ΠεÏιαγωγή - ΠλήÏης λειτουÏγικότητα υπηÏεσίας"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"ΠεÏιαγωγή - ΜεÏική λειτουÏγικότητα υπηÏεσίας"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"ΕνεÏγό διαφημιστικό πλαίσιο πεÏιαγωγής"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Διαφημιστικό πλαίσιο πεÏιαγωγής απενεÏγοποιημένο"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Αναζήτηση υπηÏεσιών"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν Ï€Ïοωθήθηκε"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> μετά από <xliff:g id="TIME_DELAY">{2}</xliff:g> δευτεÏόλεπτα"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν Ï€Ïοωθήθηκε"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν Ï€Ïοωθήθηκε"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Ο κωδικός λειτουÏγίας ολοκληÏώθηκε."</string>
+ <string name="fcError" msgid="3327560126588500777">"ΠÏόβλημα σÏνδεσης ή μη έγκυÏος κώδικας δυνατότητας."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="2567300624552921790">"Η ιστοσελίδα πεÏιέχει ένα σφάλμα."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Δεν ήταν δυνατή η εÏÏεση της διεÏθυνσης URL."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Η Ï€Ïόσβαση στο αÏχείο δεν ήταν δυνατή."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Το αÏχείο που ζητήθηκε δεν βÏέθηκε."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"ΠÏαγματοποιείται επεξεÏγασία πάÏα πολλών αιτημάτων. ΠÏοσπαθήστε ξανά αÏγότεÏα."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Σφάλμα σÏνδεσης"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"ΣυγχÏονισμός"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"ΣυγχÏονισμός"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"ΠάÏα πολλές <xliff:g id="CONTENT_TYPE">%s</xliff:g> διαγÏαφές."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Î§Î±Î¼Î·Î»Î¿Ï ÎµÏ€Î¹Ï€Î­Î´Î¿Ï… Ï€Ïόσβαση και έλεγχος του συστήματος."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"ΕÏγαλεία ανάπτυξης"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Δυνατότητες που είναι απαÏαίτητες μόνο σε Ï€ÏογÏαμματιστές εφαÏμογών."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Αποθηκευτικός χώÏος"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"ΠÏόσβαση στην κάÏτα SD."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"απενεÏγοποίηση ή Ï„Ïοποποίηση γÏαμμής κατάστασης"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"ΕπιτÏέπει στην εφαÏμογή να απενεÏγοποιεί τη γÏαμμή κατάστασης ή να Ï€Ïοσθέτει και να αφαιÏεί εικονίδια συστήματος."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ανάπτυξη/σÏμπτυξη γÏαμμής κατάστασης"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"ΕπιτÏέπει σε μια εφαÏμογή να εξαναγκάσει οποιαδήποτε δÏαστηÏιότητα που βÏίσκεται στο Ï€Ïοσκήνιο να κλείσει και να μεταβεί στο φόντο. Δεν είναι απαÏαίτητο για κανονικές εφαÏμογές."</string>
<string name="permlab_dump" msgid="1681799862438954752">"ανάκτηση εσωτεÏικής κατάστασης συστήματος"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"ΕπιτÏέπει σε μια εφαÏμογή να ανακτήσει την εσωτεÏική κατάσταση του συστήματος. Κακόβουλες εφαÏμογές ενδέχεται να ανακτήσουν μεγάλο εÏÏος ιδιωτικών και ασφαλών πληÏοφοÏιών, τις οποίες δεν χÏειάζονται."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"μεÏικός τεÏματισμός λειτουÏγίας"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Θέτει το Ï€ÏόγÏαμμα διαχείÏισης δÏαστηÏιοτήτων σε κατάσταση τεÏÎ¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Î»ÎµÎ¹Ï„Î¿Ï…Ïγιών. Δεν εκτελεί πλήÏη τεÏματισμό λειτουÏγιών."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"αποτÏοπή εναλλαγών εφαÏμογών"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"ΑποτÏέπει το χÏήστη από τη μετάβαση σε άλλη εφαÏμογή."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"παÏακολοÏθηση και έλεγχος όλων των εκκινήσεων εφαÏμογών"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"ΕπιτÏέπει σε μια εφαÏμογή να παÏακολουθεί και να ελέγχει τον Ï„Ïόπο με τον οποίο το σÏστημα εκκινεί δÏαστηÏιότητες. Κακόβουλες εφαÏμογές ενδέχεται να θέσουν σε κίνδυνο το σÏστημα. Αυτή η άδεια είναι απαÏαίτητη μόνο για ανάπτυξη και ποτέ για κανονική χÏήση τηλεφώνου."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"αποστολή εκπομπής χωÏίς πακέτο"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"ΕπιτÏέπει σε μια εφαÏμογή να ελέγχει εάν οι δÏαστηÏιότητες ολοκληÏώνονται πάντοτε μόλις μεταβοÏν στο φόντο. Δεν είναι ποτέ απαÏαίτητο για κανονικές εφαÏμογές."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"Ï„Ïοποποίηση στατιστικών μπαταÏίας"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"ΕπιτÏέπει την Ï„Ïοποποίηση στατιστικών μπαταÏίας που έχουν συλλεχθεί. Δεν Ï€Ïέπει να χÏησιμοποιείται από συνήθεις εφαÏμογές."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"αντίγÏαφο ασφαλείας και επαναφοÏά συστήματος"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"Ï€Ïοβολή μη εξουσιοδοτημένων παÏαθÏÏων"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"ΕπιτÏέπει τη δημιουÏγία παÏαθÏÏων που Ï€Ïόκειται να χÏησιμοποιηθοÏν από την εσωτεÏική διεπαφή χÏήστη του συστήματος. Δεν Ï€Ïέπει να χÏησιμοποιείται από κανονικές εφαÏμογές."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"ΕπιτÏέπει σε εφαÏμογές να παÏακολουθοÏν τα πλήκτÏα που πατάτε, ακόμη και σε μια άλλη εφαÏμογή (όπως Ï€.χ. η καταχώÏηση ενός ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης). Δεν είναι απαÏαίτητο για συνήθεις εφαÏμογές."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"δέσμευση σε μέθοδο εισόδου"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"ΕπιτÏέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας μεθόδου εισόδου. Δεν είναι απαÏαίτητο για συνήθεις εφαÏμογές."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"δέσμευση σε ταπετσαÏία"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"ΕπιτÏέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας ταπετσαÏίας. Δεν είναι απαÏαίτητο για συνήθεις εφαÏμογές."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"αλλαγή Ï€ÏÎ¿ÏƒÎ±Î½Î±Ï„Î¿Î»Î¹ÏƒÎ¼Î¿Ï Î¿Î¸ÏŒÎ½Î·Ï‚"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"ΕπιτÏέπει σε μια εφαÏμογή την αλλαγή της πεÏιστÏοφής της οθόνης οποιαδήποτε στιγμή. Δεν είναι απαÏαίτητο για κανονικές εφαÏμογές."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"αποστολή σημάτων Linux σε εφαÏμογές"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"Ï„Ïοποποίηση καθολικών Ïυθμίσεων συστήματος"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"ΕπιτÏέπει σε μια εφαÏμογή την Ï„Ïοποποίηση των δεδομένων των Ïυθμίσεων του συστήματος. Κακόβουλες εφαÏμογές μποÏοÏν να καταστÏέψουν τη διαμόÏφωση του συστήματός σας."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"Ï„Ïοποποίηση ασφαλών Ïυθμίσεων συστήματος"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"ΕπιτÏέπει σε μια εφαÏμογή να Ï„Ïοποποιεί τα δεδομένα ασφαλών Ïυθμίσεων συστήματος. Δεν Ï€Ïέπει να χÏησιμοποιείται από κανονικές εφαÏμογές."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"μετατÏοπή του χάÏτη υπηÏεσιών Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"ΕπιτÏέπει σε μια εφαÏμογή την Ï„Ïοποποίηση του χάÏτη υπηÏεσιών Google. Δεν Ï€Ïέπει να χÏησιμοποιείται από κανονικές εφαÏμογές."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"αυτόματη εκκίνηση κατά την εκκίνηση του υπολογιστή"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"ΔημιουÏγία εικονικών πηγών τοποθεσίας για δοκιμή. Κακόβουλες εφαÏμογές μποÏοÏν να το χÏησιμοποιήσουν για να παÏακάμψουν την τοποθεσία και/ή την κατάσταση που βÏίσκουν Ï€Ïαγματικές πηγές τοποθεσίας, όπως πάÏοχοι GPS ή πάÏοχοι δικτÏου."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"Ï€Ïόσβαση σε επιπλέον εντολές παÏόχου τοποθεσίας"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"ΠÏόσβαση σε επιπλέον εντολές παÏόχου τοποθεσίας. Κακόβουλες εφαÏμογές ενδέχεται να το χÏησιμοποιήσουν ώστε να παÏέμβουν στη λειτουÏγία του GPS ή άλλων πηγών τοποθεσίας."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"άδεια για εγκατάσταση ενός παÏόχου τοποθεσίας"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"ΔημιουÏγία πηγών Ï€ÏόχειÏης τοποθεσίας για έλεγχο. Οι κακόβουλες εφαÏμογές μποÏοÏν να τη χÏησιμοποιήσουν για αντιγÏαφή της τοποθεσίας και/ή η κατάσταση που επιστÏέφεται από τις αληθινές πηγές τοποθεσίας όπως GPS ή ΠάÏοχοι δικτÏου ή έλεγχος και αναφοÏά της τοποθεσίας σας σε μια εξωτεÏική πηγή."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"ακÏιβής τοποθεσία (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"ΠÏόσβαση σε πηγές ακÏιβοÏÏ‚ τοποθεσίας, όπως το Παγκόσμιο ΣÏστημα Î•Î½Ï„Î¿Ï€Î¹ÏƒÎ¼Î¿Ï (GPS) στο τηλέφωνο, όπου αυτό είναι διαθέσιμο. Κακόβουλες εφαÏμογές μποÏοÏν να το χÏησιμοποιήσουν για να Ï€ÏοσδιοÏίσουν τη θέση που βÏίσκεστε και ενδέχεται να καταναλώσουν επιπλέον Î¹ÏƒÏ‡Ï Î¼Ï€Î±Ï„Î±Ïίας."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"κατά Ï€Ïοσέγγιση (βασισμένη στο δίκτυο) τοποθεσία"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Ï€Ïόσβαση στο SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"ΕπιτÏέπει σε μια εφαÏμογή να χÏησιμοποιεί λειτουÏγίες SurfaceFlinger Ï‡Î±Î¼Î·Î»Î¿Ï ÎµÏ€Î¹Ï€Î­Î´Î¿Ï…."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ανάγνωση Ï€ÏοσωÏινής μνήμης πλαισίου"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"ΕπιτÏέπει στην εφαÏμογή να χÏησιμοποιήσει και να αναγνώσει το πεÏιεχόμενο της Ï€ÏοσωÏινής μνήμης πλαισίου."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"ΕπιτÏέπει στην εφαÏμογή την ανάγνωση του πεÏιεχομένου της Ï€ÏοσωÏινής μνήμης πλαισίου."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"αλλαγή των Ïυθμίσεων ήχου"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"ΕπιτÏέπει σε μια εφαÏμογή να Ï„Ïοποποιήσει καθολικές Ïυθμίσεις ήχου όπως ένταση ήχου και δÏομολόγηση ήχου."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"εγγÏαφή ήχου"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"ΕπιτÏέπει σε μια εφαÏμογή την κλήση τηλεφωνικών αÏιθμών χωÏίς την παÏέμβασή σας. Κακόβουλες εφαÏμογές ενδέχεται να ευθÏνονται για μη αναμενόμενες κλήσεις στον λογαÏιασμό τηλεφώνου σας. Λάβετε υπόψη ότι αυτό δεν επιτÏέπει την κλήση αÏιθμών έκτακτης ανάγκης."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"απευθείας κλήση τηλεφωνικών αÏιθμών"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"ΕπιτÏέπει στην εφαÏμογή την κλήση Ï„Î·Î»ÎµÏ†Ï‰Î½Î¹ÎºÎ¿Ï Î±ÏιθμοÏ, συμπεÏιλαμβανομένων και αÏιθμών έκτακτης ανάγκης, χωÏίς την παÏέμβασή σας. Κακόβουλες εφαÏμογές ενδέχεται να Ï€Ïαγματοποιήσουν μη αναγκαίες και παÏάνομες κλήσεις σε υπηÏεσίες έκτακτης ανάγκης."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"έλεγχος ειδοποιήσεων ενημέÏωσης τοποθεσίας"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"ΕπιτÏέπει την ενεÏγοποίηση/απενεÏγοποίηση ειδοποιήσεων ενημέÏωσης τοποθεσίας από τον πομπό. Δεν Ï€Ïέπει να χÏησιμοποιείται από κανονικές εφαÏμογές."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"Ï€Ïόσβαση σε ιδιότητες ελέγχου εισόδου"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"ΕπιτÏέπει στην εφαÏμογή να οÏίσει στο σÏστημα ποια γÏαφικά στοιχεία μποÏεί να χÏησιμοποιήσει κάθε εφαÏμογή. Με αυτή την άδεια, οι εφαÏμογές μποÏοÏν να παÏέχουν Ï€Ïόσβαση σε Ï€Ïοσωπικά δεδομένα σε άλλες εφαÏμογές. Δεν Ï€Ïέπει να χÏησιμοποιείται από συνήθεις εφαÏμογές."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"Ï„Ïοποποίηση κατάστασης τηλεφώνου"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"ΕπιτÏέπει στην εφαÏμογή τον έλεγχο των τηλεφωνικών δυνατοτήτων της συσκευής. Μια εφαÏμογή με αυτήν την άδεια μποÏεί να Ï€Ïαγματοποιήσει εναλλαγή Î¼ÎµÏ„Î±Î¾Ï Î´Î¹ÎºÏ„Ïων, να ενεÏγοποιήσει και να απενεÏγοποιήσει τον πομπό του τηλεφώνου κ.λπ. χωÏίς να σας ειδοποιήσει."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"ανάγνωση κατάστασης και ταυτότητας τηλεφώνου"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"ΕπιτÏέπει στην εφαÏμογή την Ï€Ïόσβαση στις λειτουÏγίες τηλεφώνου της συσκευής. Μια εφαÏμογή με αυτή την άδεια μποÏεί να Ï€ÏοσδιοÏίσει τον τηλεφωνικό αÏιθμό του τηλεφώνου, αν μια κλήση είναι ενεÏγή ή όχι, τον τηλεφωνικό αÏιθμό της κλήσης κ.λπ.."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"παÏεμπόδιση μετάβασης του τηλεφώνου σε κατάσταση αδÏάνειας"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"ΕπιτÏέπει σε μια εφαÏμογή την παÏεμπόδιση της μετάβασης του τηλεφώνου σε κατάσταση αδÏάνειας."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"ενεÏγοποίηση και απενεÏγοποίηση τηλεφώνου"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"ΕπιτÏέπει σε μια εφαÏμογή να επαναφέÏει πλήÏως το σÏστημα στις εÏγοστασιακές Ïυθμίσεις, διαγÏάφοντας όλα τα δεδομένα, τις διαμοÏφώσεις και τις εγκατεστημένες εφαÏμογές."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"οÏισμός ζώνης ÏŽÏας"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"ΕπιτÏέπει σε μια εφαÏμογή την αλλαγή της ζώνης ÏŽÏας του τηλεφώνου."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"ενεÏγεί ως AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"ΕπιτÏέπει σε μια εφαÏμογή την Ï€Ïαγματοποίηση κλήσεων σε AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"ανακάλυψη γνωστών λογαÏιασμών"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"ΕπιτÏέπει σε μια εφαÏμογή να λάβει τη λίστα λογαÏιασμών του τηλεφώνου."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"ενεÏγεί ως Ï€ÏόγÏαμμα ελέγχου ταυτότητας λογαÏιασμοÏ"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"ΕπιτÏέπει σε μια εφαÏμογή τη χÏήση των δυνατοτήτων του Ï€ÏογÏάμματος ελέγχου ταυτότητας λογαÏÎ¹Î±ÏƒÎ¼Î¿Ï Ï„Î¿Ï… AccountManager, συμπεÏιλαμβανομένης της δημιουÏγίας λογαÏιασμών και της λήψης και ÏÏθμισης των κωδικών Ï€Ïόσβασής τους."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"διαχείÏιση λίστας λογαÏιασμών"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"ΕπιτÏέπει σε μια εφαÏμογή να εκτελεί ενέÏγειες όπως Ï€Ïοσθήκη και κατάÏγηση λογαÏιασμών και διαγÏαφή των κωδικών Ï€Ïόσβασής τους."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"χÏήση διαπιστευτηÏίων ελέγχου ταυτότητας ενός λογαÏιασμοÏ"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"ΕπιτÏέπει σε μια εφαÏμογή να ζητά διακÏιτικά ελέγχου ταυτότητας."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"Ï€Ïοβολή κατάστασης δικτÏου"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"ΕπιτÏέπει σε μια εφαÏμογή την Ï€Ïοβολή της κατάστασης όλων των δικτÏων."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"πλήÏης Ï€Ïόσβαση στο Διαδίκτυο"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"ΕπιτÏέπει σε μια εφαÏμογή την Ï€Ïοβολή των πληÏοφοÏιών σχετικά με την κατάσταση του Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"αλλαγή κατάστασης Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"ΕπιτÏέπει σε μια εφαÏμογή τη σÏνδεση σε σημεία Ï€Ïόσβασης Wi-Fi και την αποσÏνδεση από αυτά, καθώς και την Ï€Ïαγματοποίηση αλλαγών σε διαμοÏφωμένα δίκτυα Wi-Fi."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"να επιτÏέπεται η λήψη πολλαπλής διανομής Wi-Fi"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"ΕπιτÏέπει στην εφαÏμογή να λαμβάνει πακέτα τα οποία δεν αποστέλλονται απευθείας στη συσκευή σας. Αυτό μποÏεί να φανεί χÏήσιμο κατά την ανακάλυψη υπηÏεσιών που Ï€ÏοσφέÏονται σε κοντινές τοποθεσίες. ΧÏησιμοποιεί πεÏισσότεÏη ενέÏγεια σε σχέση με την κατάσταση μη πολλαπλής διανομής."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"διαχείÏιση Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"ΕπιτÏέπει σε μια εφαÏμογή τη διαμόÏφωση του Ï„Î¿Ï€Î¹ÎºÎ¿Ï Ï„Î·Î»ÎµÏ†ÏŽÎ½Î¿Ï… Bluetooth και την ανακάλυψη και σÏζευξη με απομακÏυσμένες συσκευές."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"δημιουÏγία συνδέσεων Bluetooth"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"ΕπιτÏέπει σε μια εφαÏμογή να αναγνώσει ιδιωτικές λέξεις και φÏάσεις και ιδιωτικά ονόματα, τα οποία ο χÏήστης ενδέχεται να έχει αποθηκεÏσει στο λεξικό χÏήστη."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"εγγÏαφή σε καθοÏισμένο από τον χÏήστη λεξικό"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"ΕπιτÏέπει σε μια εφαÏμογή την εγγÏαφή νέων λέξεων στο λεξικό χÏήστη."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"Ï„Ïοποποίηση/διαγÏαφή πεÏιεχομένων κάÏτας SD"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"ΕπιτÏέπει στην εφαÏμογή την εγγÏαφή στην κάÏτα SD."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Οικία"</item>
<item msgid="869923650527136615">"Κινητό"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Άλλο"</item>
<item msgid="2374913952870110618">"ΠÏοσαÏμοσμένο"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Κινητό"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Οικία"</item>
<item msgid="5629153956045109251">"ΕÏγασία"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Σωστό!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"ΠÏοσπαθήστε αÏγότεÏα"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"ΦόÏτιση (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"ΦοÏτίστηκε."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Συνδέστε τον φοÏτιστή."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Δεν υπάÏχει κάÏτα SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Δεν υπάÏχει κάÏτα SIM στο τηλέφωνο."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φοÏές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς Ï€Ïοσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χÏήση της σÏνδεσής σας Google."\n\n" ΠÏοσπαθήστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> \nδευτεÏόλεπτα."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"ΠÏοσπαθήστε ξανά σε <xliff:g id="NUMBER">%d</xliff:g> δευτεÏόλεπτα."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Ξεχάσατε το μοτίβο;"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"ΠάÏα πολλές Ï€Ïοσπάθειες μοτίβου!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Για ξεκλείδωμα, συνδεθείτε με τον λογαÏιασμό σας Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Όνομα χÏήστη (διεÏθυνση ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Κωδικός Ï€Ïόσβασης"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ΣÏνδεση"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Μη έγκυÏο όνομα χÏήστη ή κωδικός Ï€Ïόσβασης."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ΕκκαθάÏιση"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Δεν υπάÏχουν ειδοποιήσεις"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Εν εξελίξει"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ειδοποιήσεις"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Συνδέστε τον φοÏτιστή"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Η στάθμη της μπαταÏίας είναι χαμηλή:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"απομένουν λιγότεÏο από <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Η εÏγοστασιακή δοκιμή απέτυχε"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Η ενέÏγεια FACTORY_TEST υποστηÏίζεται μόνο για πακέτα που είναι εγκατεστημένα στον κατάλογο /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"ΑπομάκÏυνση από αυτή τη σελίδα;"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Επιλέξτε OK για συνέχεια, ή ΑκÏÏωση για παÏαμονή στην Ï„Ïέχουσα σελίδα."</string>
<string name="save_password_label" msgid="6860261758665825069">"Επιβεβαίωση"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστοÏÎ¹ÎºÎ¿Ï ÎºÎ±Î¹ σελιδοδεικτών Ï€ÏογÏάμματος πεÏιήγησης"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ΕπιτÏέπει στην εφαÏμογή την ανάγνωση όλων των διευθÏνσεων URL που το Ï€ÏόγÏαμμα πεÏιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του Ï€ÏογÏάμματος πεÏιήγησης."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγÏαφή ιστοÏÎ¹ÎºÎ¿Ï ÎºÎ±Î¹ σελιδοδεικτών Ï€ÏογÏάμματος πεÏιήγησης"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"ΕπιτÏέπει σε μια εφαÏμογή να Ï„Ïοποποιήσει το ιστοÏικό ή τους σελιδοδείκτες του Ï€ÏογÏάμματος πεÏιήγησης που βÏίσκονται αποθηκευμένα στο τηλέφωνό σας. Κακόβουλες εφαÏμογές μποÏοÏν να το χÏησιμοποιήσουν για να διαγÏάψουν ή να Ï„Ïοποποιήσουν τα δεδομένα του Ï€ÏογÏάμματος πεÏιήγησης."</string>
<string name="save_password_message" msgid="767344687139195790">"Θέλετε το Ï€ÏόγÏαμμα πεÏιήγησης να διατηÏήσει αυτόν τον κωδικό Ï€Ïόσβασης;"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Îα μην γίνει Ï„ÏŽÏα"</string>
<string name="save_password_remember" msgid="6491879678996749466">"ΔιατήÏηση"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"αÏÏιο"</item>
<item quantity="other" msgid="2973062968038355991">"σε <xliff:g id="COUNT">%d</xliff:g> ημέÏες"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"σε %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"στο %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"σε %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"ημέÏα"</string>
<string name="days" msgid="4774547661021344602">"ημέÏες"</string>
<string name="hour" msgid="2126771916426189481">"ÏŽÏα"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"ΑντιγÏαφή όλων"</string>
<string name="paste" msgid="5629880836805036433">"Επικόλληση"</string>
<string name="copyUrl" msgid="2538211579596067402">"ΑντιγÏαφή διεÏθυνσης URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Μέθοδος εισόδου"</string>
- <string name="addToDictionary" msgid="726256909274177272">"ΠÏοσθήκη \"%s\" στο λεξικό"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"ΕπεξεÏγασία κειμένου"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Απομένει λίγος ελεÏθεÏος χώÏος"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Έχει απομείνει λίγος ελεÏθεÏος αποθηκευτικός χώÏος στο τηλέφωνο."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"Η εφαÏμογή <xliff:g id="APPLICATION">%1$s</xliff:g> (στη διαδικασία <xliff:g id="PROCESS">%2$s</xliff:g>) δεν αποκÏίνεται."</string>
<string name="anr_process" msgid="1246866008169975783">"Η διαδικασία <xliff:g id="PROCESS">%1$s</xliff:g> δεν αποκÏίνεται."</string>
<string name="force_close" msgid="3653416315450806396">"Αναγκαστικό κλείσιμο"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"ΑναφοÏά"</string>
<string name="wait" msgid="7147118217226317732">"Αναμονή"</string>
<string name="debug" msgid="9103374629678531849">"Εντοπισμός σφαλμάτων"</string>
<string name="sendText" msgid="5132506121645618310">"Επιλέξτε μια ενέÏγεια για το κείμενο"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Ένταση ειδοποίησης ήχου"</string>
<string name="volume_music" msgid="5421651157138628171">"Ένταση ήχου πολυμέσων"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"ΑναπαÏαγωγή μέσω Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Επιλέχτηκε αθόÏυβος ήχος κλήσης"</string>
<string name="volume_call" msgid="3941680041282788711">"Ένταση ήχου κλήσης"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Ένταση ήχου για εισεÏχόμενη κληση μέσω Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Ένταση ήχου ξυπνητηÏιοÏ"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"ΠÏιν από την απενεÏγοποίηση του Î±Ï€Î¿Î¸Î·ÎºÎµÏ…Ï„Î¹ÎºÎ¿Ï Ï‡ÏŽÏου USB, βεβαιωθείτε ότι έχετε αποπÏοσαÏτήσει την υποδοχή USB. Επιλέξτε \"ΑπενεÏγοποίηση\" για να απενεÏγοποιήσετε τον αποθηκευτικό χώÏο USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"ΑπενεÏγοποίηση"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"ΑκÏÏωση"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"ΠαÏουσιάστηκε ένα Ï€Ïόβλημα κατά την απενεÏγοποίηση του Î±Ï€Î¿Î¸Î·ÎºÎµÏ…Ï„Î¹ÎºÎ¿Ï Ï‡ÏŽÏου USB. Βεβαιωθείτε ότι έχετε αποπÏοσαÏτήσει την υποδοχή USB και Ï€Ïοσπαθήστε ξανά."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"ΔιαμόÏφωση κάÏτας SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Είστε βέβαιοι ότι θέλετε να διαμοÏφώσετε την κάÏτα SD; Όλα τα δεδομένα στην κάÏτα σας θα χαθοÏν."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"ΔιαμόÏφωση"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Ένας υπολογιστής είναι συνδεδεμένος στο τηλέφωνό σας."</string>
<string name="select_input_method" msgid="2086499663193509436">"Επιλογή μεθόδου εισόδου"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"υποψήφιοι"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"ΠÏοετοιμασία κάÏτας SD"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Έλεγχος για σφάλματα."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Κενή κάÏτα SD"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"Η κάÏτα SD είναι κενή ή έχει μη υποστηÏιζόμενο σÏστημα αÏχείων."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"ΚατεστÏαμμένη κάÏτα SD"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"Η κάÏτα SD παÏουσιάζει βλάβη. Ενδεχομένως θα Ï€Ïέπει να Ï€Ïοβείτε σε διαμόÏφωσή της."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"Μη αναμενόμενη αφαίÏεση κάÏτας SD"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"ΑποπÏοσαÏτήστε την κάÏτα SD Ï€Ïιν την αφαιÏέσετε για την αποφυγή απώλειας δεδομένων."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"Η κάÏτα SD μποÏεί να αφαιÏεθεί με ασφάλεια"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"ΜποÏείτε να αφαιÏέσετε με ασφάλεια της κάÏτα SD."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"Η κάÏτα SD αφαιÏέθηκε"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"ΑφαιÏέθηκε η κάÏτα SD. Τοποθετήστε μια νέα κάÏτα."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Δεν βÏέθηκαν δÏαστηÏιότητες που να αντιστοιχοÏν"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"ενημέÏωση στατιστικών χÏήσης στοιχείου"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"ΕπιτÏέπει την Ï„Ïοποποίηση στατιστικών χÏήσης στοιχείων που έχουν συλλεχθεί. Δεν Ï€Ïέπει να χÏησιμοποιείται από κανονικές εφαÏμογές."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Εκτέλεση"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Κλήση αÏιθμοÏ"\n"με τη χÏήση <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"ΔημιουÏγία επαφής"\n"με τη χÏήση του <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"επιλεγμένο"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"δεν ελέγχθηκε"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Îα επιτÏέπεται"</string>
+ <string name="deny" msgid="2081879885755434506">"ΆÏνηση"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Απαιτείται άδεια"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-en-rUS/donottranslate-names.xml b/core/res/res/values-en-rUS/donottranslate-names.xml
new file mode 100644
index 0000000..82ba310
--- /dev/null
+++ b/core/res/res/values-en-rUS/donottranslate-names.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- various string resources for Contacts -->
+ <string-array name="common_nicknames">
+ <item>Albert, Al, Bert, Bertie</item>
+ <item>Alexander, Al, Alex, Lex, Sasha</item>
+ <item>Alexandra, Al, Alex, Allie, Ally, Lex, Lexie, Sandra, Sandy, Sasha</item>
+ <item>Alice, Allie, Ally</item>
+ <item>Alison, Allie, Ally</item>
+ <item>Allison, Allie, Ally</item>
+ <item>Amanda, Mandi, Mandy</item>
+ <item>Andrea, Andie</item>
+ <item>Andrew, Andy, Drew</item>
+ <item>Anthony, Tony, Toni, Tone</item>
+ <item>Arthur, Art, Arty</item>
+ <item>Barbara, Babs, Barb, Barbie</item>
+ <item>Benjamin, Ben, Benji, Benny</item>
+ <item>Bernard, Bern, Bernie</item>
+ <item>Bertram, Bert, Bertie</item>
+ <item>Bradly, Brad</item>
+ <item>Catherine, Cat, Cate, Cath, Catie, Cathy, Kat, Kate, Katie, Kathy</item>
+ <item>Charles, Chuck, Chaz, Charlie, Buck</item>
+ <item>Christine, Chris, Chrissy, Chrissie</item>
+ <item>Christopher, Chris</item>
+ <item>Cynthia, Cindy, Cynth</item>
+ <item>Daniel, Dan, Danny</item>
+ <item>David, Dave</item>
+ <item>Deborah, Deb, Debbie</item>
+ <item>Dennis, Den, Denny, Dean</item>
+ <item>Dolores, Dolly</item>
+ <item>Donald, Don, Donny</item>
+ <item>Donnatella, Donna</item>
+ <item>Dorothea, Dot, Dotty</item>
+ <item>Dorothy, Dot, Dotty</item>
+ <item>Douglas, Doug</item>
+ <item>Edward, Ed, Eddie, Ned, Neddie, Neddy, Ted, Teddy, Teddie</item>
+ <item>Eleanor, Ella, Ellie, Elle</item>
+ <item>Elisabetta, Betta</item>
+ <item>Elizabeth, Beth, Bess, Bessie, Betsy, Betty, Bette, Eliza, Lisa, Liza, Liz</item>
+ <item>Emily, Em, Ems, Emmy</item>
+ <item>Emma, Em, Ems, Emmy</item>
+ <item>Erica, Rikki, Rikkie, Ricky</item>
+ <item>Eugene, Gene</item>
+ <item>Florence, Flo</item>
+ <item>Frances, Fran, Francie</item>
+ <item>Francis, Fran, Frank</item>
+ <item>Frederick, Fred, Freddy</item>
+ <item>Gabriel, Gabe</item>
+ <item>Geoffrey, Jeff</item>
+ <item>Gerald, Gerry</item>
+ <item>Gerard, Gerry</item>
+ <item>Gregory, Greg</item>
+ <item>Harold, Hal, Hank, Harry</item>
+ <item>Henry, Hal, Hank, Harry</item>
+ <item>Herbert, Bert, Bertie</item>
+ <item>Irving, Irv</item>
+ <item>Isabella, Isa, Izzy</item>
+ <item>Jacob, Jake</item>
+ <item>Jacqueline, Jackie</item>
+ <item>James, Jim, Jimmy, Jamie, Jock</item>
+ <item>Janet, Jan</item>
+ <item>Janice, Jan</item>
+ <item>Jason, Jay</item>
+ <item>Jefferson, Jeff</item>
+ <item>Jeffrey, Jeff</item>
+ <item>Jennifer, Jen, Jenny</item>
+ <item>Jerome, Jerry</item>
+ <item>Jessica, Jessie</item>
+ <item>John, Jack, Jacky, Johnny, Jon</item>
+ <item>Jonathan, Jon, John</item>
+ <item>Joseph, Joe, Joey</item>
+ <item>Joshua, Josh</item>
+ <item>Kaitlyn, Cat, Cate, Catie, Cath, Cathy, Kat, Kate, Katie, Kathy</item>
+ <item>Katherine, Cat, Cate, Catie, Cath, Cathy, Kat, Kate, Katie, Kathy</item>
+ <item>Kathleen, Cat, Cate, Catie, Cath, Cathy, Kat, Kate, Katie, Kathy</item>
+ <item>Katrina, Cat, Cate, Catie, Cath, Cathy, Kat, Kate, Katie, Kathy</item>
+ <item>Kenneth, Ken</item>
+ <item>Kevin, Kev</item>
+ <item>Laura, Lauri, Laurie</item>
+ <item>Lauren, Lauri, Laurie</item>
+ <item>Laurence, Larry, Lauri, Laurie</item>
+ <item>Lawrence, Larry, Lauri, Laurie</item>
+ <item>Leonard, Leo, Len, Lenny</item>
+ <item>Leopold, Leo, Len, Lenny</item>
+ <item>Madeline, Maddie, Maddy</item>
+ <item>Margaret, Marge, Marg, Maggie, Mags, Meg, Peggy</item>
+ <item>Matthew, Matt, Mattie</item>
+ <item>Maureen, Mo</item>
+ <item>Maurice, Mo</item>
+ <item>Megan, Meg</item>
+ <item>Michael, Mickey, Mick, Mike, Mikey</item>
+ <item>Morris, Mo</item>
+ <item>Nancy, Nan</item>
+ <item>Nathan, Nat, Nate</item>
+ <item>Nathaniel, Nat, Nate</item>
+ <item>Nicholas, Nick</item>
+ <item>Pamela, Pam</item>
+ <item>Patricia, Pat, Patsy, Patty, Trish, Tricia</item>
+ <item>Patrick, Paddy, Pat, Patty, Patter, Rick, Ricky</item>
+ <item>Peter, Pete</item>
+ <item>Raymond, Ray</item>
+ <item>Philip, Phil</item>
+ <item>Rebecca, Becca</item>
+ <item>Richard, Rick, Rich, Dick</item>
+ <item>Robert, Bob, Rob, Robbie, Bobby, Rab</item>
+ <item>Roberta, Bobbie</item>
+ <item>Rodney. Rod</item>
+ <item>Ronald, Ron, Ronnie</item>
+ <item>Rosemary, Rosie, Rose</item>
+ <item>Russell, Russ, Rusty</item>
+ <item>Ryan, Ry</item>
+ <item>Samantha, Sam</item>
+ <item>Samuel, Sam, Sammy</item>
+ <item>Sophia, Sophie</item>
+ <item>Stephanie, Steph, Stephie</item>
+ <item>Stephen, Steve</item>
+ <item>Steven, Steve</item>
+ <item>Stuart, Stu</item>
+ <item>Susan, Sue, Susie, Suzie</item>
+ <item>Suzanne, Sue, Susie, Suzie</item>
+ <item>Teresa, Terrie, Terry</item>
+ <item>Theodora, Teddie, Thea, Theo</item>
+ <item>Theodore, Ted, Teddy, Theo</item>
+ <item>Thomas, Tom, Thom, Tommy</item>
+ <item>Timothy, Tim, Timmy</item>
+ <item>Valerie, Val</item>
+ <item>Veronica, Ronnie, Roni, Nica, Nikki, Nikka</item>
+ <item>Victor, Vic</item>
+ <item>Victoria, Vicky, Vicki, Vickie, Tori</item>
+ <item>Vincent, Vince, Vin, Vinnie</item>
+ <item>Vivian, Vivi</item>
+ <item>Walter, Walt, Wally</item>
+ <item>Wendy, Wen, Wendel</item>
+ <item>William, Bill, Billy, Will, Willy, Liam</item>
+ <item>Yvonna, Vonna</item>
+ <item>Zachary, Zach, Zack, Zac</item>
+ </string-array>
+ <string name="common_name_prefixes">
+ 1LT, 1ST, 2LT, 2ND, 3RD, ADMIRAL, CAPT, CAPTAIN, COL, CPT, DR,
+ GEN, GENERAL, LCDR, LT, LTC, LTG, LTJG, MAJ, MAJOR, MG, MR,
+ MRS, MS, PASTOR, PROF, REP, REVEREND, REV, SEN, ST
+ </string>
+ <string name="common_name_suffixes">
+ B.A., BA, D.D.S., DDS, I, II, III, IV, IX, JR, M.A., M.D, MA,
+ MD, MS, PH.D., PHD, SR, V, VI, VII, VIII, X
+ </string>
+ <string name="common_last_name_prefixes">
+ D\', DE, DEL, DI, LA, LE, MC, SAN, ST, TER, VAN, VON
+ </string>
+ <string name="common_name_conjunctions">
+ &amp;, AND, OR
+ </string>
+</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index f34adcb..1b6c838 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;sin título&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(No hay número de teléfono)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Restricción de llamadas"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Cambio de contraseña"</string>
<string name="PinMmi" msgid="3113117780361190304">"Cambio de PIN"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Número de llamada presente"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Número de llamada restringido"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Llamada de tres direcciones"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Rechazo de llamadas molestas no deseadas"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Entrega de número de llamada"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"No molestar"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"El identificador de llamadas está predeterminado en restringido. Llamada siguiente: restringida"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"El Identificador de llamadas está predeterminado en restringido. Llamada siguiente: no restringido"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"El identificador de llamadas está predeterminado en no restringido. Llamada siguiente: restringida"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Sincronización"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Paquete"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Indicador de roaming encendido"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Indicador de roaming apagado"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Indicador de roaming titilando"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Fuera del vecindario"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Fuera de construcción"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Roaming: sistema preferido"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming: sistema disponible"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming: socio de alianza"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming: socio premium"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming: funcionalidad de servicio completa"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming: funcionalidad de servicio parcial"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Banner de roaming encendido"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Banner de roaming apagado"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Buscando servicio"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> después de <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Código de función completo."</string>
+ <string name="fcError" msgid="3327560126588500777">"Problema de conexión o código de función no válido."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"Aceptar"</string>
<string name="httpError" msgid="2567300624552921790">"La página web contiene un error."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"No se ha podido encontrar la URL."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"No se ha podido acceder al archivo."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"No se ha encontrado el archivo solicitado."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Se están procesando demasiadas solicitudes. Vuelve a intentarlo más tarde."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Error al iniciar la sesión"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sincronización"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronización"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Demasiadas eliminaciones de <xliff:g id="CONTENT_TYPE">%s</xliff:g>"</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Acceso y control de nivel más bajo del sistema."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Herramientas de desarrollo"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Las funciones sólo son necesarias para los desarrolladores de aplicaciones."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Espacio de almacenamiento"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"Acceder a la tarjeta SD."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar o modificar la barra de estado"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Admite que la aplicación desactive la barra de estado, o agregue y elimine íconos del sistema."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir o reducir la barra de estado"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Admite una aplicación que provoca que cualquier actividad del fondo se acerque y vuelva a alejarse. Se debe evitar utilizarlo en aplicaciones normales."</string>
<string name="permlab_dump" msgid="1681799862438954752">"recuperar el estado interno del sistema"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Admite que la aplicación recupere el estado interno del sistema. Las aplicaciones maliciosas pueden recuperar una gran variedad de información privada y segura que normalmente nunca necesitaría."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"cierre parcial"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Pone al administrador de la actividad en estado de cierre. No realiza un cierre completo."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir conmutadores de aplicación"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Impide que el usuario cambie a otra aplicación."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"verificar y controlar todos los lanzamientos de actividades"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Admite una aplicación que verifica y controla el lanzamiento de actividades por parte del sistema. Las aplicaciones maliciosas pueden comprometer totalmente al sistema. Este permiso sólo es necesario para el desarrollo, nunca para el uso normal del teléfono."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar emisión de paquete eliminado"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Admite una aplicación que controla si las actividades siempre finalizan cuando van al fondo. No se utiliza nunca en aplicaciones normales."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"modificar la estadística de la batería"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Admite la modificación de estadísticas recopiladas sobre la batería. Las aplicaciones normales no deben utilizarlo."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"copia de seguridad y restauración del sistema de control"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite la creación de ventanas que la interfaz interna del usuario del sistema pretenda utilizar. Las aplicaciones normales no deben utilizarlo."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Admite que las aplicaciones observen las teclas que presionas, incluso al interactuar con otra aplicación (como el ingreso de una contraseña). Se debe evitar utilizarlo en aplicaciones normales."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"vincular a un método de entrada"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Permite al propietario vincularse a la interfaz de nivel superior de un método de entrada. Se debe evitar utilizarlo en aplicaciones normales."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a un fondo de pantalla"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Permite al propietario vincularse a la interfaz de nivel superior de un fondo de pantalla. Se debe evitar utilizarlo en aplicaciones normales."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar la orientación de la pantalla"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Admite una aplicación que cambia la rotación de la pantalla en cualquier momento. Se debe evitar utilizarlo en aplicaciones normales."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales de Linux a las aplicaciones"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuración global del sistema"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Admite una aplicación que modifica los datos de configuración del sistema. Las aplicaciones maliciosas pueden corromper la configuración de tu sistema."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modificar la configuración segura del sistema"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Admite una aplicación que modifica los datos de la configuración segura de los sistemas. Las aplicaciones normales no deben utilizarlo."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"modificar el mapa de servicios de Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Admite una aplicación que modifica el mapa de servicios de Google. Las aplicaciones normales no deben utilizarlo."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"iniciar automáticamente durante la inicialización"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Crea fuentes de ubicación de prueba. Las aplicaciones maliciosas pueden utilizarlo para invalidar la ubicación o el estado que arrojen las fuentes de ubicación real, como GPS o proveedores de red."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acceder a comandos adicionales del proveedor del lugar"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Accede a comandos adicionales del proveedor del lugar. Las aplicaciones maliciosas pueden utilizarlo para interferir en la operación del GPS u otras fuentes de ubicación."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autorización para instalar un proveedor de ubicación"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Crear fuentes de ubicación simuladas para las pruebas. Las aplicaciones maliciosas pueden utilizar esta opción para anular la ubicación y el estado que devuelven las fuentes de ubicación actuales, como por ejemplo los proveedores de GPS o redes, o para monitorear y notificar tu ubicación a una fuente externa."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"ubicación precisa (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Accede a las fuentes de ubicación precisa, como el Sistema de posicionamiento global en el teléfono, si está disponible. Las aplicaciones maliciosas pueden utilizarlo para determinar donde te encuentras y puede consumir energía adicional de la batería."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"ubicación aproximada (basada en la red)"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"acceder a SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Admite que la aplicación utilice funciones de bajo nivel de SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer el búfer de tramas"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Admite que la aplicación que se utilizará lea el contenido del búfer de marco."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Admite que la aplicación lea el contenido del búfer de marco."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar tu configuración de audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Admite que la aplicación modifique la configuración global de audio, como el volumen y el enrutamiento."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"grabar audio"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Admite que la aplicación llame a ciertos números de teléfono sin tu permiso. Las aplicaciones maliciosas pueden ocasionar llamadas imprevistas en tu factura telefónica. Ten en cuenta que esto no admite que la aplicación llame a los números de emergencia."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"llamar directamente a cualquier número de teléfono"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Admite que la aplicación llame a cualquier número de teléfono, incluidos los números de emergencia, sin tu intervención. Las aplicaciones maliciosas pueden realizar llamadas innecesarias e ilegales a los servicios de emergencia."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"controlar las notificaciones de actualización de ubicación"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Permite activar y desactivar las notificaciones de actualización de ubicación de la radio. Las aplicaciones normales no deben utilizarlo."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"acceder a las propiedades de protección"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Admite que la aplicación indique al sistema cuáles controles puede utilizar cada aplicación. Con este permiso, las aplicaciones pueden brindar acceso a los datos personales a otras aplicaciones. Las aplicaciones normales no deben utilizarlo."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"modificar el estado del teléfono"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Admite que la aplicación controle las funciones telefónicas del dispositivo. Una aplicación con este permiso puede cambiar las redes, encender y apagar la radio del teléfono y funciones similares sin notificarte en ningún momento."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"leer el estado del teléfono y la identidad"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Admite que la aplicación acceda a las funciones telefónicas del dispositivo. Una aplicación con este permiso puede determinar el número de teléfono y el número de serie de este teléfono, si una llamada está activa, el número al cual está conectado esa llamada y funciones similares."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"evitar que el teléfono entre en estado de inactividad"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Admite una aplicación que evita que el teléfono entre en estado de inactividad."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"apagar o encender el teléfono"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Admite una aplicación que restablece el sistema completamente con su configuración de fábrica, y borra todos los datos, las configuraciones y las aplicaciones instaladas."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"establecer zona horaria"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Admite una aplicación que cambia la zona horaria del teléfono."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"actuar como cuenta, administrador o servicio"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Permite a una aplicación realizar llamadas a los autenticadores de cuenta"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"descubrir cuentas conocidas"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Admite una aplicación que obtiene la lista de cuentas conocidas del teléfono."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"actuar como autenticador de cuenta"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Permite a una aplicación utilizar las capacidades del autenticador de cuentas del Administrador de la cuenta, incluida la creación de cuentas, y la obtención y configuración de sus contraseñas."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrar la lista de cuentas"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Permite a una aplicación ejecutar operaciones, tales como agregar y eliminar cuentas, y suprimir sus contraseñas."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"usar las credenciales de autenticación de una cuenta"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Permite a una aplicación solicitar tokens de autenticación."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ver estado de la red"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Admite una aplicación que ve el estado de todas las redes."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"acceso total a Internet"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Admite una aplicación que observa la información sobre el estado de Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"cambiar el estado de Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Admite una aplicación que se conecta y desconecta de los puntos de acceso de Wi-Fi y que hace cambios en las redes de Wi-Fi configuradas."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"permitir recepción de multidifusión Wi-Fi"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Permite a una aplicación recibir paquetes que no están dirigidos directamente a tu dispositivo. Esta opción puede ser útil al descubrir servicios ofrecidos. Además, ejerce más potencia que el modo que no es de multidifusión."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"administración de bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Admite una aplicación que configura el teléfono Bluetooth local y descubre y se vincula con dispositivos remotos."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"crear conexiones de Bluetooth"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Admite una aplicación para leer palabras, nombres y frases privadas que posiblemente el usuario haya almacenado en el diccionario del usuario."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"escribir al diccionario definido por el usuario"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Admite una aplicación que escribe palabras nuevas en el diccionario del usuario."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"modificar/suprimir el contenido de la tarjeta SD"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Admite que una aplicación escriba en la tarjeta SD."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Página principal"</item>
<item msgid="869923650527136615">"Celular"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Otros"</item>
<item msgid="2374913952870110618">"Personalización"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Celular"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Página principal"</item>
<item msgid="5629153956045109251">"Trabajo"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lo sentimos, vuelve a intentarlo"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Cargada."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecta tu cargador."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"No hay tarjeta SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"No hay tarjeta SIM en el teléfono."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Has extraído incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos, se te solicitará que desbloquees tu teléfono al iniciar sesión en Google. "\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Vuelve a intentarlo en <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"¿Olvidaste el patrón?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Demasiados intentos de patrón."</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Para desbloquear, regístrate en tu cuenta de Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nombre de usuario (correo electrónico)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Contraseña"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Inicia sesión"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nombre de usuario o contraseña incorrecta."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"No hay notificaciones"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Continuo"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificaciones"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Hay poca batería:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"menos de <xliff:g id="NUMBER">%d%%</xliff:g> restante."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Error en la prueba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"La acción FACTORY_TEST se admite solamente en paquetes instalados en /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Deseas salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona Aceptar para continuar o Cancelar para permanecer en la página actual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer historial y marcadores del navegador"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los marcadores del navegador."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir historial y marcadores del navegador"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite a una aplicación modificar el historial y los marcadores del navegador almacenados en tu teléfono. Las aplicaciones maliciosas pueden utilizarlo para borrar o modificar tus datos."</string>
<string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string>
<string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"mañana"</item>
<item quantity="other" msgid="2973062968038355991">"en <xliff:g id="COUNT">%d</xliff:g> días"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"en %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"en %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"en %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"día"</string>
<string name="days" msgid="4774547661021344602">"días"</string>
<string name="hour" msgid="2126771916426189481">"hora"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Copiar todo"</string>
<string name="paste" msgid="5629880836805036433">"Pegar"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Método de entrada"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Agregar \"%s\" al diccionario"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Hay poco espacio de almacenamiento en el teléfono."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"La aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> (en proceso <xliff:g id="PROCESS">%2$s</xliff:g>) no responde."</string>
<string name="anr_process" msgid="1246866008169975783">"El proceso <xliff:g id="PROCESS">%1$s</xliff:g> no responde."</string>
<string name="force_close" msgid="3653416315450806396">"Provocar acercamiento"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Notificar"</string>
<string name="wait" msgid="7147118217226317732">"Espera"</string>
<string name="debug" msgid="9103374629678531849">"Depurar"</string>
<string name="sendText" msgid="5132506121645618310">"Selecciona una acción para el texto"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Volumen del timbre"</string>
<string name="volume_music" msgid="5421651157138628171">"Volumen de los medios"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Reproduciendo a través de Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Timbre seleccionado silencioso"</string>
<string name="volume_call" msgid="3941680041282788711">"Volumen de llamadas entrantes"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volumen en llamada de Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volumen de la alarma"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Antes de desactivar el almacenamiento USB, asegúrate de haberlo desmontado en el servidor USB al seleccionar \"Desactivar\"."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Apagar"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Cancelar"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Se ha producido un problema al desactivar el almacenamiento USB. Verifica para asegurarte de haber desmontado el servidor USB, luego vuelve a intentarlo."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatear tarjeta SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"¿Estás seguro de que quieres formatear la tarjeta SD? Se perderán todos los datos de tu tarjeta."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formato"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración de USB conectada"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Hay una computadora conectada a tu teléfono."</string>
<string name="select_input_method" msgid="2086499663193509436">"Seleccionar método de entrada"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"Preparación de la tarjeta SD"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Verificando errores"</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Tarjeta SD vacía"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"Tarjeta SD en blanco o el sistema de archivos no es compatible."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Tarjeta SD dañada"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"Tarjeta SD dañada. Es posible que debas reformatearla."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"Tarjeta SD extraída de forma imprevista"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Desmontar la tarjeta SD antes de extraerla para evitar la pérdida de datos."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"Tarjeta SD fácil de extraer"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Puedes eliminar la tarjeta SD sin riesgos."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"Tarjeta SD extraída"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"Tarjeta SD eliminada. Inserta una nueva."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"No se encontraron actividades coincidentes"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"actualizar la estadística de uso de los componentes"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Permite la modificación de estadísticas recopiladas sobre el uso de componentes. Las aplicaciones normales no deben utilizarlo."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Ejecutar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marcar el número"\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Crear contacto "\n"con <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no verificado"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Permitir"</string>
+ <string name="deny" msgid="2081879885755434506">"Denegar"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Permiso solicitado"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c00a4be..215666a 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"No se ha podido acceder al archivo."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"No se ha encontrado el archivo solicitado."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Se están procesando demasiadas solicitudes. Vuelve a intentarlo más tarde."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Error de acceso"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sincronización"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronización"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Demasiadas eliminaciones de <xliff:g id="CONTENT_TYPE">%s</xliff:g>"</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"modificar estadísticas de la batería"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite la modificación de estadísticas recopiladas sobre la batería. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_backup" msgid="470013022865453920">"controlar las copias de seguridad y las restauraciones del sistema"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Permite que la aplicación controle el mecanismo de copia de seguridad y restauración del sistema. Este permiso no está destinado a aplicaciones normales."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"mostrar ventanas no autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite la creación de ventanas destinadas al uso por parte de la interfaz de usuario interna del sistema. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"mostrar alertas de nivel del sistema"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Permite que las aplicaciones observen las teclas que pulsas incluso cuando interactúas con otra aplicación (como, por ejemplo, al introducir una contraseña). No debería ser necesario nunca para las aplicaciones normales."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"enlazar con un método de introducción de texto"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Permite enlazar con la interfaz de nivel superior de un método de introducción de texto. No debe ser necesario para las aplicaciones normales."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"enlazar con un fondo de pantalla"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Permite enlazar con la interfaz de nivel superior de un fondo de pantalla. No debe ser necesario para las aplicaciones normales."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar orientación de la pantalla"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesario nunca para las aplicaciones normales."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales Linux a aplicaciones"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modificar la configuración global del sistema"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Permite que una aplicación modifique los datos de configuración del sistema. Las aplicaciones malintencionadas pueden dañar la configuración del sistema."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modificar la configuración segura del sistema"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Permite que una aplicación modifique los datos de configuración segura del sistema. No está destinado al uso por parte de aplicaciones normales."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"modificar la asignación de servicios de Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Permite que una aplicación modifique la asignación de servicios de Google. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"ejecutar automáticamente al iniciar"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"acceder a SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Permite que la aplicación utilice funciones de SurfaceFlinger de nivel inferior."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer memoria de almacenamiento intermedio"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Permite que la aplicación que se va a utilizar lea el contenido de la memoria de almacenamiento intermedio."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Permite que la aplicación lea el contenido de la memoria de almacenamiento intermedio."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"cambiar la configuración de audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Permite que la aplicación modifique la configuración de audio global como, por ejemplo, el volumen y la salida."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"grabar sonido"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Permite que la aplicación llame a números de teléfono sin la intervención del usuario. Las aplicaciones malintencionadas pueden originar llamadas inesperadas en la factura telefónica. Ten en cuenta que con este permiso la aplicación no puede realizar llamadas a números de emergencia."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"llamar directamente a cualquier número de teléfono"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Permite que la aplicación llame a cualquier número de teléfono, incluidos los números de emergencia, sin que el usuario intervenga. Las aplicaciones malintencionadas pueden realizar llamadas innecesarias e ilícitas a los servicios de emergencias."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"controlar las notificaciones de actualización de la ubicación"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Permite habilitar/inhabilitar las notificaciones de actualización de la señal móvil. No está destinado al uso por parte de aplicaciones normales."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"acceder a propiedades de registro"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Permite que una aplicación restablezca por completo el sistema a su configuración de fábrica, borrando todos los datos, la configuración y las aplicaciones instaladas."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"establecer zona horaria"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Permite que una aplicación cambie la zona horaria del teléfono."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"actuar como servicio de administrador de cuentas"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Permite que una aplicación realice llamadas a los autenticadores de cuentas."</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"ver cuentas reconocidas"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Permite que una aplicación obtenga una lista de cuentas reconocidas por el teléfono."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"actuar como autenticador de cuentas"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Permite que una aplicación utilice las funciones de autenticador de cuentas del administrador de cuentas, incluida la creación de cuentas y la obtención y el establecimiento de sus contraseñas."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrar la lista de cuentas"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Permite que una aplicación realice operaciones tales como la adición y eliminación de cuentas y la eliminación de su contraseña."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"utilizar las credenciales de autenticación de una cuenta"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Permite que una aplicación solicite tokens de autenticación."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ver estado de la red"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Permite que una aplicación vea el estado de todas las redes."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"acceso íntegro a Internet"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Otra"</item>
<item msgid="2374913952870110618">"Personalizar"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Móvil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Casa"</item>
<item msgid="5629153956045109251">"Trabajo"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Has realizado <xliff:g id="NUMBER_0">%d</xliff:g> intentos fallidos de creación del patrón de desbloqueo. Si realizas <xliff:g id="NUMBER_1">%d</xliff:g> intentos fallidos más, se te pedirá que desbloquees el teléfono con tus credenciales de acceso de Google."\n\n" Espera <xliff:g id="NUMBER_2">%d</xliff:g> segundos e inténtalo de nuevo."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Espera <xliff:g id="NUMBER">%d</xliff:g> segundos y vuelve a intentarlo."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"¿Has olvidado el patrón?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Se han realizado demasiados intentos incorrectos de creación del patrón."</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Para desbloquear el teléfono, accede a tu cuenta de Google."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nombre de usuario (correo electrónico)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Contraseña"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Acceder"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nombre de usuario o contraseña no válido"</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Borrar"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Conecta el cargador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Se está agotando la batería:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"menos del <xliff:g id="NUMBER">%d%%</xliff:g> disponible."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"¿Por qué?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Fallo en la prueba de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"La acción FACTORY_TEST sólo es compatible con los paquetes instalados en /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No se ha encontrado ningún paquete que proporcione la acción FACTORY_TEST."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"mañana"</item>
<item quantity="other" msgid="2973062968038355991">"dentro de <xliff:g id="COUNT">%d</xliff:g> días"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"el %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"a las %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"en %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"día"</string>
<string name="days" msgid="4774547661021344602">"días"</string>
<string name="hour" msgid="2126771916426189481">"hora"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Copiar todo"</string>
<string name="paste" msgid="5629880836805036433">"Pegar"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Método de introducción de texto"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Añadir \"%s\" al diccionario"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Se está agotando el espacio de almacenamiento del teléfono."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Volumen del timbre"</string>
<string name="volume_music" msgid="5421651157138628171">"Volumen multimedia"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Reproduciendo a través de Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Tono de silencio seleccionado"</string>
<string name="volume_call" msgid="3941680041282788711">"Volumen de la llamada"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volumen de la llamada de Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volumen de alarma"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Antes de desactivar el almacenamiento USB, asegúrate de haber desactivado el host USB. Selecciona \"Desactivar\" para desactivar el almacenamiento USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Desactivar"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Cancelar"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Se ha producido un problema al desactivar el almacenamiento USB. Asegúrate de que has desactivado el host USB e inténtalo de nuevo."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatear tarjeta SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"¿Estás seguro de que quieres formatear la tarjeta SD? Se perderán todos los datos de la tarjeta."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formato"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Crear un contacto"\n"a partir de <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seleccionado"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"no seleccionado"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Permitir"</string>
+ <string name="deny" msgid="2081879885755434506">"Denegar"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Permiso solicitado"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 377d202..a0cc5a3 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Impossible d\'accéder au fichier."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Le fichier demandé est introuvable."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Trop de requêtes sont en cours de traitement. Veuillez réessayer ultérieurement."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Erreur de connexion"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchroniser"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronisation"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Trop de contenus supprimés (<xliff:g id="CONTENT_TYPE">%s</xliff:g>)."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"Modification des statistiques de la batterie"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Autoriser la modification des statistiques de la batterie. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_backup" msgid="470013022865453920">"contrôler la sauvegarde et la restauration du système"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Autorise l\'application à contrôler le mécanisme de sauvegarde et de restauration du système. Ne pas utiliser pour les applications standard."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"Affichage de fenêtres non autorisées"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permet de créer des fenêtres conçues pour l\'interface utilisateur du système interne. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"Affichage d\'alertes système"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Permet à des applications d\'identifier les touches sur lesquelles vous appuyez même lorsque vous utilisez une autre application (lors de la saisie d\'un mot de passe, par exemple). Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"Association à un mode de saisie"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Permet au support de se connecter à l\'interface de plus haut niveau d\'un mode de saisie. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"Se fixer sur un fond d\'écran"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Permet au support de se fixer sur l\'interface de plus haut niveau d\'un fond d\'écran. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"Changement d\'orientation de l\'écran"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Envoi de signaux Linux aux applications"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"Modification des paramètres généraux du système"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Permet à une application de modifier les données des paramètres système. Des applications malveillantes peuvent utiliser cette fonctionnalité pour endommager la configuration de votre système."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"Modifier les paramètres de sécurité du système"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Permet à une application de modifier les données des paramètres de sécurité du système. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"Modification de la carte des services Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Permet à une application de modifier la carte des services Google. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"Lancement automatique au démarrage"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"Accès à SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Permet à certaines applications d\'utiliser les fonctionnalités SurfaceFlinger de bas niveau."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Lecture de la mémoire tampon graphique"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Permet aux applications de lire/utiliser le contenu de la mémoire tampon graphique."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Permet aux applications de lire le contenu de la mémoire tampon graphique."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"Modification de vos paramètres audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Permet à l\'application de modifier les paramètres audio généraux (p. ex. le volume et le routage)."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"Enregistrement de fichier audio"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Permet à l\'application d\'appeler des numéros sans votre intervention. Des applications malveillantes peuvent ainsi passer des appels à votre insu qui s\'ajoutent à votre facture téléphonique. Cette fonctionnalité ne permet pas à l\'application d\'appeler des numéros d\'urgence."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"Appel direct de tout numéro de téléphone"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Permet à une application d\'appeler tout numéro de téléphone (y compris les numéros d\'urgence) sans votre intervention. Des applications malveillantes peuvent passer des appels non nécessaires ou illégitimes à des services d\'urgence."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"Contrôle des notifications de mise à jour de position géo."</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Permet l\'activation/la désactivation des notifications de mises à jour de la position géographique provenant du signal radio. Les applications normales n\'utilisent pas cette fonctionnalité."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"Accès aux propriétés d\'enregistrement"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Permet à une application de réinitialiser entièrement le système afin de rétablir ses valeurs d\'usine et d\'effacer toutes les données, configurations et applications installées."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"Sélection du fuseau horaire"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Permet à l\'application de modifier le fuseau horaire du téléphone."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"Agir en tant que service AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Permet à l\'application d\'appeler le service AccountAuthenticators."</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"Identification des comptes connus"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Permet à une application d\'obtenir la liste des comptes connus du téléphone."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"Agir en tant que fonction d\'authentification de compte"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Permet à l\'application d\'utiliser les fonctionnalités d\'authentification de compte du service AccountManager, notamment la création de comptes, l\'obtention et la définition des mots de passe associés."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"Gérer la liste des comptes"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Permet à l\'application d\'effectuer des opérations, notamment ajout ou suppression de comptes, et effacement des mots de passe associés."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"Utiliser les informations d\'authentification d\'un compte"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Permet à une application de demander des jetons d\'authentification."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"Affichage de l\'état du réseau"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Permet à une application d\'afficher l\'état de tous les réseaux."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"Accès Internet complet"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Autre"</item>
<item msgid="2374913952870110618">"Personnalisée"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobile"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Domicile"</item>
<item msgid="5629153956045109251">"Bureau"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Vous avez mal saisi le schéma de déverrouillage <xliff:g id="NUMBER_0">%d</xliff:g> fois. Au bout de <xliff:g id="NUMBER_1">%d</xliff:g> tentatives supplémentaires, vous devrez débloquer votre téléphone à l\'aide de votre identifiant Google."\n\n"Merci de réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Réessayez dans <xliff:g id="NUMBER">%d</xliff:g> secondes."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Schéma oublié ?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Trop de tentatives !"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Pour déverrouiller le téléphone, connectez-vous à l\'aide de votre compte Google."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nom d\'utilisateur (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Mot de passe"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Se connecter"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nom d\'utilisateur ou mot de passe incorrect."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Effacer"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Branchez le chargeur"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Le niveau de la batterie est bas :"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"Batterie restante inférieure à <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Pourquoi ?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Échec du test usine"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'action FACTORY_TEST est uniquement prise en charge pour les paquets de données installés dans in/system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Impossible de trouver un paquet proposant l\'action FACTORY_TEST."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"demain"</item>
<item quantity="other" msgid="2973062968038355991">"dans <xliff:g id="COUNT">%d</xliff:g> jours"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"à %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"en %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"jour"</string>
<string name="days" msgid="4774547661021344602">"jours"</string>
<string name="hour" msgid="2126771916426189481">"heure"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Tout copier"</string>
<string name="paste" msgid="5629880836805036433">"Coller"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copier l\'URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Mode de saisie"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Ajouter \"%s\" au dictionnaire"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Modifier le texte"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Espace disponible faible"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"La mémoire du téléphone commence à être pleine."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Volume de la sonnerie"</string>
<string name="volume_music" msgid="5421651157138628171">"Volume"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Lecture via Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Sonnerie silencieuse sélectionnée"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume des appels entrants"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume d\'appels entrants sur Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volume"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Avant d\'éteindre le périphérique de stockage USB, assurez-vous d\'avoir désactivé l\'hôte USB. Sélectionnez \"Éteindre\" pour éteindre le périphérique de stockage USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Éteindre"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Annuler"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Un problème est survenu lors de la mise hors tension du périphérique de stockage USB. Assurez-vous que l\'hôte USB a bien été désactivé, puis essayez à nouveau."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formater la carte SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Voulez-vous vraiment formater la carte SD ? Toutes les données de cette carte seront perdues."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Ajouter un contact"\n"en utilisant <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"sélectionné"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non sélectionné"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Autoriser"</string>
+ <string name="deny" msgid="2081879885755434506">"Refuser"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Autorisation demandée"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index fde4f23..6fdf1b3 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Impossibile accedere al file."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Impossibile trovare il file richiesto."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Troppe richieste in fase di elaborazione. Riprova più tardi."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Errore di accesso"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sinc"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizzazione"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Troppe eliminazioni di <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"modifica statistiche batteria"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Consente la modifica delle statistiche sulla batteria raccolte. Da non usare per normali applicazioni."</string>
<string name="permlab_backup" msgid="470013022865453920">"controllo del backup di sistema e ripristino"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Consente all\'applicazione di controllare i backup dei sistemi e il meccanismo di ripristino. Da non usare per normali applicazioni."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visualizzazione finestre non autorizzate"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Consente la creazione di finestre destinate all\'uso nell\'interfaccia utente di sistema interna. Da non usare per normali applicazioni."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"visualizzazione avvisi di sistema"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Consente il rilevamento da parte delle applicazioni dei tasti premuti anche durante l\'interazione con un\'altra applicazione (come nel caso di inserimento di una password). Non dovrebbe essere mai necessario per le normali applicazioni."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"associaz. a un metodo di inserimento"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Consente l\'associazione all\'interfaccia principale di un metodo di inserimento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"associazione a sfondo"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Consente l\'associazione di uno sfondo all\'interfaccia principale. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"modifica orientamento dello schermo"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"invio segnali Linuz alle applicazioni"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modifica impostazioni di sistema globali"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Consente la modifica in un\'applicazione dei dati delle impostazioni del sistema. Le applicazioni dannose possono danneggiare la configurazione del sistema."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modificare le impostazioni di protezione del sistema"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Consente a un\'applicazione di modificare i dati delle impostazioni di protezione del sistema. Da non usare per normali applicazioni."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"modifica mappa servizi Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Consente a un\'applicazione di modificare la mappa dei servizi Google. Da non usare per normali applicazioni."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"apertura automatica all\'avvio"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"accesso a SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Consente l\'utilizzo dell\'applicazione di funzioni di basso livello SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lettura buffer di frame"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Consente la lettura da parte dell\'applicazione dei contenuti del buffer di frame."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Consente la lettura da parte dell\'applicazione dei contenuti del buffer di frame."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"modifica impostazioni audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Consente all\'applicazione di modificare impostazioni audio globali come volume e routing."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"registrazione audio"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Consente all\'applicazione di chiamare numeri automaticamente. Le applicazioni dannose potrebbero far risultare chiamate impreviste sulla bolletta telefonica. Questa autorizzazione non consente all\'applicazione di chiamare numeri di emergenza."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"chiamata diretta di tutti i n. telefono"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Consente all\'applicazione di chiamare qualsiasi numero, compresi quelli di emergenza, automaticamente. Le applicazioni dannose potrebbero effettuare chiamate non necessarie e illegali a servizi di emergenza."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"controllo notifiche aggiornamento posizione"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Consente l\'attivazione/disattivazione delle notifiche di aggiornamento della posizione dal segnale cellulare. Da non usare per normali applicazioni."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"accesso a proprietà di archiviazione"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Consente a un\'applicazione di ripristinare le impostazioni di fabbrica del sistema, eliminando tutti i dati, le configurazioni e le applicazioni installate."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"impostazione fuso orario"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Consente a un\'applicazione di modificare il fuso orario del telefono."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"agire da AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Consente a un\'applicazione di effettuare chiamate verso AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"rilevamento account noti"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Consente a un\'applicazione di recuperare l\'elenco di account memorizzati sul telefono."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"agire da autenticatore account"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Consente a un\'applicazione di utilizzare le funzionalità di autenticatore account di AccountManager, compresi la creazione degli account e il recupero o l\'impostazione delle relative password."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"gestione dell\'elenco degli account"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Consente a un\'applicazione di eseguire operazioni quali l\'aggiunta o la rimozione degli account e l\'eliminazione delle relative password."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"utilizzo delle credenziali di autenticazione di un account"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Consente a un\'applicazione di richiedere i token di autenticazione."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"visualizzazione stato della rete"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Consente a un\'applicazione di visualizzare lo stato di tutte le reti."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"accesso completo a Internet"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Altro"</item>
<item msgid="2374913952870110618">"Personalizzato"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Cellulare"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Casa"</item>
<item msgid="5629153956045109251">"Ufficio"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono tramite i dati di accesso di Google."\n\n"Riprova fra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Riprova fra <xliff:g id="NUMBER">%d</xliff:g> secondi."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Hai dimenticato la sequenza?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Troppi tentativi di inserimento della sequenza."</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Per sbloccare, accedi tramite il tuo account Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome utente (email)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Password"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Accedi"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Password o nome utente non valido."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Cancella"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Collegare il caricabatterie"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteria quasi scarica:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"energia residua inferiore a <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Perché?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Test di fabbrica non riuscito"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"L\'azione FACTORY_TEST è supportata soltanto per i pacchetti installati in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nessun pacchetto trovato che fornisca l\'azione FACTORY_TEST."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"domani"</item>
<item quantity="other" msgid="2973062968038355991">"tra <xliff:g id="COUNT">%d</xliff:g> giorni"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"il %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"alle %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"nel %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"giorno"</string>
<string name="days" msgid="4774547661021344602">"giorni"</string>
<string name="hour" msgid="2126771916426189481">"ora"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Copia tutto"</string>
<string name="paste" msgid="5629880836805036433">"Incolla"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copia URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Metodo inserimento"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Aggiungi \"%s\" al dizionario"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Modifica testo"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Spazio in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Spazio di archiviazione del telefono in esaurimento."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Volume suoneria"</string>
<string name="volume_music" msgid="5421651157138628171">"Volume app. multimediali"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Riproduzione tramite Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Suoneria silenziosa selezionata"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume chiamate"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume chiamate Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volume allarme"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Prima di disattivare l\'archivio USB, verifica di aver smontato l\'host USB. A tale scopo, seleziona \"Disattiva\"."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Disattiva"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Annulla"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Abbiamo riscontrato un problema disattivando l\'archivio USB. Verifica di aver smontato l\'host USB e riprova."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatta scheda SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Formattare la scheda SD? Tutti i dati sulla scheda verranno persi."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatta"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Crea contatto"\n"utilizzando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selezionato"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"non selezionato"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Consenti"</string>
+ <string name="deny" msgid="2081879885755434506">"Nega"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Autorizzazione richiesta"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index f6a546f..9b7e09f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"ファイルã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"è¦æ±‚ã•ã‚ŒãŸãƒ•ã‚¡ã‚¤ãƒ«ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"処ç†ä¸­ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆãŒå¤šã™ãŽã¾ã™ã€‚ã—ã°ã‚‰ãã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦è©¦ã—ã¦ãã ã•ã„。"</string>
+ <string name="notification_title" msgid="4254304601929719937">"ログインエラー"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"åŒæœŸ"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"åŒæœŸ"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"<xliff:g id="CONTENT_TYPE">%s</xliff:g>ã§ã®å‰Šé™¤ãŒå¤šã™ãŽã¾ã™ã€‚"</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"電池統計情報ã®å¤‰å›½"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"åŽé›†ã—ãŸé›»æ± çµ±è¨ˆæƒ…å ±ã®å¤‰æ›´ã‚’許å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
<string name="permlab_backup" msgid="470013022865453920">"システムã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã¨å¾©å…ƒã‚’制御ã™ã‚‹"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"システムã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãŠã‚ˆã³å¾©å…ƒãƒ¡ã‚«ãƒ‹ã‚ºãƒ ã®åˆ¶å¾¡ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"未許å¯ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã®è¡¨ç¤º"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"内部システムã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ã‚§ãƒ¼ã‚¹ã§ä½¿ç”¨ã™ã‚‹ãŸã‚ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ä½œæˆã‚’許å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"システムレベルã®è­¦å‘Šã®è¡¨ç¤º"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"別ã®ã‚¢ãƒ—リケーションã¸ã®å…¥åŠ›ï¼ˆãƒ‘スワードãªã©ï¼‰ã§ã‚‚キー入力を監視ã™ã‚‹ã“ã¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ã¾ã£ãŸãå¿…è¦ã‚ã‚Šã¾ã›ã‚“。"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"入力方法ã«é–¢é€£ä»˜ã‘ã‚‹"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"入力方法ã®ãƒˆãƒƒãƒ—レベルインターフェースã«é–¢é€£ä»˜ã‘ã‚‹ã“ã¨ã‚’所有者ã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã«ã¯ã¾ã£ãŸãå¿…è¦ã‚ã‚Šã¾ã›ã‚“。"</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"å£ç´™ã«ãƒã‚¤ãƒ³ãƒ‰"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"å£ç´™ã®ãƒˆãƒƒãƒ—レベルインターフェースã¸ã®ãƒã‚¤ãƒ³ãƒ‰ã‚’所有者ã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä¸è¦ã§ã™ã€‚"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ç”»é¢ã®å‘ãã®å¤‰æ›´"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"ã„ã¤ã§ã‚‚ç”»é¢ã®å›žè»¢ã‚’変更ã™ã‚‹ã“ã¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã«ã¯ã¾ã£ãŸãå¿…è¦ã‚ã‚Šã¾ã›ã‚“。"</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linuxã®ã‚·ã‚°ãƒŠãƒ«ã‚’アプリケーションã«é€ä¿¡"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"システムã®å…¨èˆ¬è¨­å®šã®å¤‰æ›´"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"システム設定データã®å¤‰æ›´ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚悪æ„ã®ã‚るアプリケーションãŒã‚·ã‚¹ãƒ†ãƒ è¨­å®šã‚’破壊ã™ã‚‹æã‚ŒãŒã‚ã‚Šã¾ã™ã€‚"</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"システムã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨­å®šã®å¤‰æ›´"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"システムã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£è¨­å®šã®å¤‰æ›´ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"Googleサービスã®åœ°å›³ã®å¤‰æ›´"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Googleサービスマップã®å¤‰æ›´ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"起動時ã«è‡ªå‹•çš„ã«é–‹å§‹"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlingerã¸ã®ã‚¢ã‚¯ã‚»ã‚¹"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"SurfaceFlingerã®ä½Žãƒ¬ãƒ™ãƒ«ã®æ©Ÿèƒ½ã®ä½¿ç”¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"フレームãƒãƒƒãƒ•ã‚¡ã®èª­ã¿å–ã‚Š"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"フレームãƒãƒƒãƒ•ã‚¡ã®å†…容ã®èª­ã¿å–ã‚Šã¨ä½¿ç”¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"フレームãƒãƒƒãƒ•ã‚¡ã®å†…容ã®èª­ã¿å–りをアプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"音声設定ã®å¤‰æ›´"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"音é‡ã‚„転é€ãªã©ã®éŸ³å£°å…¨èˆ¬ã®è¨­å®šã®å¤‰æ›´ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"録音"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"電話番å·ã®è‡ªå‹•ç™ºä¿¡ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚悪æ„ã®ã‚るアプリケーションãŒæ„図ã—ãªã„電話をã‹ã‘ã¦æ–™é‡‘ãŒç™ºç”Ÿã™ã‚‹æã‚ŒãŒã‚ã‚Šã¾ã™ã€‚緊急通報ã¸ã®ç™ºä¿¡ã¯è¨±å¯ã—ã¾ã›ã‚“。"</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"電話番å·ç™ºä¿¡"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"緊急通報をå«ã‚ã‚らゆる電話番å·ã«è‡ªå‹•ç™ºä¿¡ã™ã‚‹ã“ã¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚悪æ„ã®ã‚るアプリケーションãŒç·Šæ€¥ã‚µãƒ¼ãƒ“スã«ä¸æ­£ãªé€šå ±ã‚’ã™ã‚‹æã‚ŒãŒã‚ã‚Šã¾ã™ã€‚"</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"ä½ç½®æƒ…å ±ã®æ›´æ–°é€šçŸ¥"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"無線通信ã‹ã‚‰ã®ä½ç½®æ›´æ–°é€šçŸ¥ã‚’有効/無効ã«ã™ã‚‹ã“ã¨ã‚’許å¯ã—ã¾ã™ã€‚通常ã®ã‚¢ãƒ—リケーションã§ã¯ä½¿ç”¨ã—ã¾ã›ã‚“。"</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"ãƒã‚§ãƒƒã‚¯ã‚¤ãƒ³ãƒ—ロパティã¸ã®ã‚¢ã‚¯ã‚»ã‚¹"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"データã€è¨­å®šã€ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ãŸã‚¢ãƒ—リケーションをã™ã¹ã¦æ¶ˆåŽ»ã—ã¦ã€å®Œå…¨ã«å‡ºè·æ™‚ã®è¨­å®šã«ã‚·ã‚¹ãƒ†ãƒ ã‚’リセットã™ã‚‹ã“ã¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"タイムゾーンã®è¨­å®š"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"端末ã®ã‚¿ã‚¤ãƒ ã‚¾ãƒ¼ãƒ³ã®å¤‰æ›´ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"AccountManagerServiceã¨ã—ã¦æ©Ÿèƒ½"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"AccountAuthenticatorsã®å‘¼ã³å‡ºã—をアプリケーションã«è¨±å¯ã—ã¾ã™"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"既知ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®å–å¾—"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"端末内ã«ã‚るアカウントã®ãƒªã‚¹ãƒˆã®å–得をアプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"アカウントèªè¨¼ã‚·ã‚¹ãƒ†ãƒ ã¨ã—ã¦æ©Ÿèƒ½"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"アカウントã®ä½œæˆã€ãƒ‘スワードã®å–得や設定ãªã©ã€AccountManagerã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆèªè¨¼æ©Ÿèƒ½ã®ä½¿ç”¨ã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"アカウントリストを管ç†"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"アカウントã®è¿½åŠ ã‚„削除ã€ãƒ‘スワードã®å‰Šé™¤ãªã©ã®æ“作ã®å®Ÿè¡Œã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"アカウントã®èªè¨¼æƒ…報を使用"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"èªè¨¼ãƒˆãƒ¼ã‚¯ãƒ³ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯çŠ¶æ…‹ã®è¡¨ç¤º"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"ã™ã¹ã¦ã®ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯çŠ¶æ…‹ã®è¡¨ç¤ºã‚’アプリケーションã«è¨±å¯ã—ã¾ã™ã€‚"</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"完全ãªã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆã‚¢ã‚¯ã‚»ã‚¹"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"ãã®ä»–"</item>
<item msgid="2374913952870110618">"カスタム"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"æºå¸¯"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"自宅"</item>
<item msgid="5629153956045109251">"仕事"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"指定ã—ãŸãƒ‘ターンã¯<xliff:g id="NUMBER_0">%d</xliff:g>回ã¨ã‚‚æ­£ã—ãã‚ã‚Šã¾ã›ã‚“。ã‚ã¨<xliff:g id="NUMBER_1">%d</xliff:g>回指定ã«å¤±æ•—ã™ã‚‹ã¨ã€æºå¸¯é›»è©±ã®ãƒ­ãƒƒã‚¯ã®è§£é™¤ã«Googleã¸ã®ãƒ­ã‚°ã‚¤ãƒ³ãŒå¿…è¦ã«ãªã‚Šã¾ã™ã€‚"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後ã«ã‚‚ã†ä¸€åº¦æŒ‡å®šã—ã¦ãã ã•ã„。"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>秒後ã«ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"パターンを忘れãŸå ´åˆ"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"パターンã®ã‚¨ãƒ©ãƒ¼ãŒå¤šã™ãŽã¾ã™"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Googleアカウントã§ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ãƒ­ãƒƒã‚¯è§£é™¤"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ユーザーå (メール)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"パスワード"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ログイン"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ユーザーåã¾ãŸã¯ãƒ‘スワードãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“。"</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"通知を消去"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"å……é›»ã—ã¦ãã ã•ã„"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"電池ãŒæ®‹ã‚Šå°‘ãªããªã£ã¦ã„ã¾ã™:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"残é‡<xliff:g id="NUMBER">%d%%</xliff:g>以下"</string>
- <string name="battery_low_why" msgid="7655196144309694753">"ç†ç”±"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"出è·æ™‚試験ãŒå¤±æ•—"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TESTæ“作ã¯ã€/system/appã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚ŒãŸãƒ‘ッケージã®ã¿ãŒå¯¾è±¡ã§ã™ã€‚"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"FACTORY_TESTæ“作を行ã†ãƒ‘ッケージã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚"</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"明日"</item>
<item quantity="other" msgid="2973062968038355991">"<xliff:g id="COUNT">%d</xliff:g>日後"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%så¹´"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"æ—¥"</string>
<string name="days" msgid="4774547661021344602">"æ—¥"</string>
<string name="hour" msgid="2126771916426189481">"時間"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"ã™ã¹ã¦ã‚³ãƒ”ー"</string>
<string name="paste" msgid="5629880836805036433">"貼り付ã‘"</string>
<string name="copyUrl" msgid="2538211579596067402">"URLをコピー"</string>
- <string name="inputMethod" msgid="7673923508389094672">"入力方法"</string>
- <string name="addToDictionary" msgid="726256909274177272">"辞書ã«ã€Œ%sã€ã‚’追加"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"テキストを編集"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"空ã容é‡ä½Žä¸‹"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"æºå¸¯é›»è©±ã®ç©ºã容é‡ãŒå°‘ãªããªã£ã¦ã„ã¾ã™ã€‚"</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"ç€ä¿¡éŸ³é‡"</string>
<string name="volume_music" msgid="5421651157138628171">"メディアã®éŸ³é‡"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Bluetooth経由ã§å†ç”Ÿä¸­ã§ã™"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"マナーモードãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã™"</string>
<string name="volume_call" msgid="3941680041282788711">"通話音é‡"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Bluetoothç€ä¿¡éŸ³é‡"</string>
<string name="volume_alarm" msgid="1985191616042689100">"アラームã®éŸ³é‡"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"USBストレージをOFFã«ã™ã‚‹å‰ã«USBホストã®ãƒžã‚¦ãƒ³ãƒˆã‚’解除ã—ãŸã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。USBストレージをOFFã«ã™ã‚‹ã«ã¯[OFF]ã‚’é¸æŠžã—ã¾ã™ã€‚"</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"OFF"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"キャンセル"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"USBストレージをOFFã«ã™ã‚‹éš›ã«å•é¡ŒãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚USBホストã®ãƒžã‚¦ãƒ³ãƒˆãŒè§£é™¤ã•ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"SDカードをフォーマット"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SDカードをフォーマットã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿã‚«ãƒ¼ãƒ‰å†…ã®ã™ã¹ã¦ã®ãƒ‡ãƒ¼ã‚¿ãŒå¤±ã‚ã‚Œã¾ã™ã€‚"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"フォーマット"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>を使ã£ã¦"\n"連絡先を新è¦ç™»éŒ²"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"オン"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"オフ"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"許å¯"</string>
+ <string name="deny" msgid="2081879885755434506">"æ‹’å¦"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"リクエスト済ã¿æ¨©é™"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 2e85a10..8030cad 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;제목없ìŒ&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(전화번호 ì—†ìŒ)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"착발신 제한"</string>
<string name="PwdMmi" msgid="7043715687905254199">"비밀번호 변경"</string>
<string name="PinMmi" msgid="3113117780361190304">"PIN 변경"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"통화 번호 존재"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"ì œí•œëœ í†µí™” 번호"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"3ìž í†µí™”"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"ì›í•˜ì§€ 않는 통화 수신 거부"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"통화 번호 전달"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"ì‘답 거부"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"ë°œì‹ ìž ë²ˆí˜¸ê°€ 기본ì ìœ¼ë¡œ 제한ë¨ìœ¼ë¡œ 설정ë©ë‹ˆë‹¤. ë‹¤ìŒ í†µí™”: 제한ë¨"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"ë°œì‹ ìž ë²ˆí˜¸ê°€ 기본ì ìœ¼ë¡œ 제한ë¨ìœ¼ë¡œ 설정ë©ë‹ˆë‹¤. ë‹¤ìŒ í†µí™”: 제한ë˜ì§€ ì•ŠìŒ"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"ë°œì‹ ìž ë²ˆí˜¸ê°€ 기본ì ìœ¼ë¡œ 제한ë˜ì§€ ì•ŠìŒìœ¼ë¡œ 설정ë©ë‹ˆë‹¤. ë‹¤ìŒ í†µí™”: 제한ë¨"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"ë™ê¸°í™”"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"패킷"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"ë¡œë° í‘œì‹œê¸° 사용"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"ë¡œë° í‘œì‹œê¸° 사용 안함"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"ë¡œë° í‘œì‹œê¸° 깜박임"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"ì¸ê·¼ 지역 외부"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"건물 밖"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"ë¡œë° - 기본 시스템"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"ë¡œë° - 사용 가능한 시스템"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"ë¡œë° - 제휴 파트너"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"ë¡œë° - 프리미엄 파트너"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"ë¡œë° - ì „ì²´ 서비스 기능"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"ë¡œë° - 부분 서비스 기능"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"ë¡œë° ë°°ë„ˆ 사용"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"ë¡œë° ë°°ë„ˆ 사용 안함"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"서비스 검색 중"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안ë¨"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g><xliff:g id="TIME_DELAY">{2}</xliff:g>초 후"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안ë¨"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안ë¨"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"기능 코드가 완료ë˜ì—ˆìŠµë‹ˆë‹¤."</string>
+ <string name="fcError" msgid="3327560126588500777">"ì—°ê²°ì— ë¬¸ì œê°€ 있거나 기능 코드가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"확ì¸"</string>
<string name="httpError" msgid="2567300624552921790">"웹페ì´ì§€ì— 오류가 있습니다."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"URLì„ ì°¾ì„ ìˆ˜ 없습니다."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"파ì¼ì— 액세스할 수 없습니다."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"요청한 파ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"처리 ì¤‘ì¸ ìš”ì²­ì´ ë„ˆë¬´ 많습니다. ìž ì‹œ í›„ì— ë‹¤ì‹œ ì‹œë„í•´ 주세요."</string>
+ <string name="notification_title" msgid="4254304601929719937">"ë¡œê·¸ì¸ ì˜¤ë¥˜"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"ë™ê¸°í™”"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"ë™ê¸°í™”"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> 삭제가 너무 많습니다."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"ì‹œìŠ¤í…œì„ í•˜ìœ„ 수준ì—ì„œ 액세스하고 제어합니다."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"개발 ë„구"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"ì‘용프로그램 개발ìžì—게만 필요한 기능입니다."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"저장"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"SD ì¹´ë“œì— ì•¡ì„¸ìŠ¤í•©ë‹ˆë‹¤."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"ìƒíƒœ 표시줄 사용 중지 ë˜ëŠ” 수정"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ìƒíƒœ í‘œì‹œì¤„ì„ ì‚¬ìš© 중지하거나 시스템 ì•„ì´ì½˜ì„ 추가 ë° ì œê±°í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"ìƒíƒœ 표시줄 확장/축소"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ í¬ê·¸ë¼ìš´ë“œì— 있는 활ë™ì„ 강제로 ë‹«ê³  ë˜ëŒì•„ê°ˆ 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
<string name="permlab_dump" msgid="1681799862438954752">"시스템 내부 ìƒíƒœ 검색"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‹œìŠ¤í…œì˜ ë‚´ë¶€ ìƒíƒœë¥¼ 검색할 수 있ë„ë¡ í•©ë‹ˆë‹¤. 단, 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì´ ê¸°ëŠ¥ì„ ì´ìš©í•˜ì—¬ ì¼ë°˜ì ìœ¼ë¡œ 필요하지 ì•Šì€ ë‹¤ì–‘í•œ ê°œì¸ì •ë³´ì™€ 보안정보를 검색할 수 있습니다."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"부분 종료"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"ìž‘ì—… 관리ìžë¥¼ 종료 ìƒíƒœë¡œ 설정하며 ì „ì²´ 종료를 수행하지 않습니다."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ì‘용프로그램 전환 방지"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"사용ìžê°€ 다른 ì‘용프로그램으로 전환하지 못하게 합니다."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"실행 ì¤‘ì¸ ëª¨ë“  ì‘용프로그램 ëª¨ë‹ˆí„°ë§ ë° ì œì–´"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‹œìŠ¤í…œì—ì„œ 활ë™ì´ 시작ë˜ëŠ” ë°©ì‹ì„ 모니터ë§í•˜ê³  제어할 수 있ë„ë¡ í•©ë‹ˆë‹¤. 단, 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì´ ê¸°ëŠ¥ì„ ì´ìš©í•˜ì—¬ ì‹œìŠ¤í…œì„ ì™„ì „ížˆ ì†ìƒì‹œí‚¬ 수 있습니다. ì´ ê¶Œí•œì€ ê°œë°œ 과정ì—만 필요하며 ì¼ë°˜ 휴대전화 사용 ì‹œì—는 필요하지 않습니다."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"패키지 제거 브로드ìºìŠ¤íŠ¸ 보내기"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë°±ê·¸ë¼ìš´ë“œë¡œ ì´ë™í•œ 활ë™ì„ í•­ìƒ ë°”ë¡œ 종료할지 여부를 제어할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"배터리 통계 수정"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"ìˆ˜ì§‘ëœ ë°°í„°ë¦¬ 통계를 수정할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—서는 사용할 수 없습니다."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"시스템 백업 ë° ë³µì› ê´€ë¦¬"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"ì¸ì¦ë˜ì§€ ì•Šì€ ì°½ 표시"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"내부 시스템 ì‚¬ìš©ìž ì¸í„°íŽ˜ì´ìŠ¤ì—ì„œ 사용하는 ì°½ì„ ë§Œë“¤ 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—서는 사용하지 않습니다."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë‹¤ë¥¸ ì‘용프로그램과 ìƒí˜¸ìž‘ìš©í•  ë•Œì—ë„ ì‚¬ìš©ìžê°€ 누르는 키(예: 비밀번호 ìž…ë ¥)를 ë³¼ 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"입력 방법 고정"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"보유ìžê°€ ìž…ë ¥ ë°©ë²•ì˜ ìµœìƒìœ„ ì¸í„°íŽ˜ì´ìŠ¤ë§Œ 사용하ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"배경화면만 사용"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"보유ìžê°€ ë°°ê²½í™”ë©´ì˜ ìµœìƒìœ„ ì¸í„°íŽ˜ì´ìŠ¤ë§Œ 사용하ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì–¸ì œë“ ì§€ 화면 íšŒì „ì„ ë³€ê²½í•  수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—는 필요하지 않습니다."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ì‘ìš©í”„ë¡œê·¸ëž¨ì— Linux 신호 보내기"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"전체 시스템 설정 수정"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‹œìŠ¤í…œì˜ ì„¤ì • ë°ì´í„°ë¥¼ 수정할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê²½ìš° 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‹œìŠ¤í…œ êµ¬ì„±ì„ ì†ìƒì‹œí‚¬ 수 있습니다."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"보안 시스템 설정 수정"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‹œìŠ¤í…œ 보안 설정값 ë°ì´í„°ë¥¼ 수정할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—서는 사용하지 않습니다."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"Google 서비스 ì§€ë„ ìˆ˜ì •"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ Google 서비스 지ë„를 수정할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—서는 사용하지 않습니다."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"부팅할 ë•Œ ìžë™ 시작"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"테스트용 가짜 위치 소스를 만듭니다. 단, 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì´ ê¸°ëŠ¥ì„ ì´ìš©í•˜ì—¬ GPS, ë„¤íŠ¸ì›Œí¬ ì œê³µì—…ì²´ ê°™ì€ ì‹¤ì œ 위치 소스ì—ì„œ 반환한 위치 ë°/ë˜ëŠ” ìƒíƒœë¥¼ ë®ì–´ì“¸ 수 있습니다."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"추가 위치 제공업체 명령 액세스"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"추가 위치 제공업체 ëª…ë ¹ì— ì•¡ì„¸ìŠ¤í•©ë‹ˆë‹¤. 단, 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì´ ê¸°ëŠ¥ì„ ì´ìš©í•˜ì—¬ GPS ë˜ëŠ” 기타 위치 ì†ŒìŠ¤ì˜ ìž‘ë™ì„ ë°©í•´í•  수 있습니다."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"위치 ì •ë³´ ê³µê¸‰ìž ì„¤ì¹˜ 권한"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"테스트용 가짜 위치 소스를 만듭니다. 단, 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì´ ê¸°ëŠ¥ì„ ì´ìš©í•˜ì—¬ GPS, ë„¤íŠ¸ì›Œí¬ ì œê³µì—…ì²´ ê°™ì€ ì‹¤ì œ 위치 소스ì—ì„œ 반환한 위치 ë°/ë˜ëŠ” ìƒíƒœë¥¼ ë®ì–´ì“°ê±°ë‚˜ 사용ìžì˜ 위치를 모니터ë§í•˜ì—¬ 외부 소스로 ë³´ê³ í•  수 있습니다."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"ìžì„¸í•œ (GPS) 위치"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"가능한 경우 휴대전화ì—ì„œ GPS(범지구 위치 측정 시스템) ë“±ì˜ ìžì„¸í•œ 위치 ì†ŒìŠ¤ì— ì•¡ì„¸ìŠ¤í•©ë‹ˆë‹¤. ì´ ê²½ìš° 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‚¬ìš©ìžì˜ 위치를 확ì¸í•˜ê³  추가 배터리 ì „ì›ì„ 소비할 수 있습니다."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"광범위한 ë„¤íŠ¸ì›Œí¬ ê¸°ë°˜ 위치"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlinger 액세스"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ SurfaceFlingerì˜ í•˜ìœ„ 수준 ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"프레임 ë²„í¼ ì½ê¸°"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ í”„ë ˆìž„ 버í¼ì˜ 콘í…츠를 ì½ì„ 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ í”„ë ˆìž„ 버í¼ì˜ 콘í…츠를 ì½ì„ 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"오디오 설정 변경"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë³¼ë¥¨ ë° ê²½ë¡œ 지정 ê°™ì€ ì „ì²´ 오디오 ì„¤ì •ì„ ìˆ˜ì •í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"오디오 ë…¹ìŒ"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‚¬ìš©ìžì˜ ì¡°ìž‘ ì—†ì´ ì „í™”ë²ˆí˜¸ë¡œ 전화를 걸 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê²½ìš° 악성 ì‘용프로그램으로 ì¸í•´ 예ìƒì¹˜ 못한 통화 ìš”ê¸ˆì´ ë¶€ê³¼ë  ìˆ˜ 있습니다. ì´ ê¶Œí•œìœ¼ë¡œ ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë¹„ìƒ ì „í™”ë¥¼ 걸게 í•  수는 없습니다."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"전화번호로 ì§ì ‘ 전화걸기"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‚¬ìš©ìžì˜ ì¡°ìž‘ ì—†ì´ ë¹„ìƒ ë²ˆí˜¸ë¥¼ í¬í•¨í•œ 전화번호로 전화를 걸 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê²½ìš° 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‘급 서비스를 불필요하거나 불법ì ìœ¼ë¡œ 호출할 수 있습니다."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"위치 ì—…ë°ì´íŠ¸ 알림 제어"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"ë¬´ì„ ì˜ ìœ„ì¹˜ ì—…ë°ì´íŠ¸ ì•Œë¦¼ì„ ì‚¬ìš©í•˜ê±°ë‚˜ 사용 중지할 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘용프로그램ì—서는 사용하지 않습니다."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"ì²´í¬ì¸ ì†ì„± 액세스"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‘용프로그램ì—ì„œ 사용할 수 있는 ìœ„ì ¯ì„ ì‹œìŠ¤í…œì— ì•Œë¦´ 수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê¶Œí•œì„ ê°–ëŠ” ì‘ìš©í”„ë¡œê·¸ëž¨ì€ ê°œì¸ ì •ë³´ì— ëŒ€í•œ 액세스 ê¶Œí•œì„ ë‹¤ë¥¸ ì‘ìš©í”„ë¡œê·¸ëž¨ì— ë¶€ì—¬í•  수 있습니다. ì¼ë°˜ ì‘용프로그램ì—서는 사용하지 않습니다."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"휴대전화 ìƒíƒœ 수정"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ìž¥ì¹˜ì˜ íœ´ëŒ€ì „í™” ê¸°ëŠ¥ì„ ì œì–´í•  수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê¶Œí•œì„ ê°–ëŠ” ì‘ìš©í”„ë¡œê·¸ëž¨ì€ ì‚¬ìš©ìžì—게 알리지 ì•Šê³  네트워í¬ë¥¼ 전환하거나 휴대전화 무선 ê¸°ëŠ¥ì„ ì¼œê³  ë„는 ë“±ì˜ ìž‘ì—…ì„ ìˆ˜í–‰í•  수 있습니다."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"휴대전화 ìƒíƒœ ë° ID ì½ê¸°"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ìž¥ì¹˜ì˜ íœ´ëŒ€ì „í™” ê¸°ëŠ¥ì— ì•¡ì„¸ìŠ¤í•  수 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê¶Œí•œì„ ê°–ëŠ” ì‘ìš©í”„ë¡œê·¸ëž¨ì€ íœ´ëŒ€ì „í™”ì˜ ì „í™”ë²ˆí˜¸ ë° ì¼ë ¨ë²ˆí˜¸, 통화가 활성ì¸ì§€ 여부, 해당 통화가 ì—°ê²°ëœ ë²ˆí˜¸ ë“±ì„ í™•ì¸í•  수 있습니다."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"휴대전화가 절전 모드로 전환ë˜ì§€ ì•Šë„ë¡ ì„¤ì •"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ íœ´ëŒ€ì „í™”ê°€ 절전 모드로 전환ë˜ì§€ ì•Šë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"휴대전화 ì „ì› ì¼œê³  ë„기"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ëª¨ë“  ë°ì´í„°, 구성 ë° ì„¤ì¹˜ëœ ì‘ìš©í”„ë¡œê·¸ëž¨ì„ ì§€ì›Œì„œ ì‹œìŠ¤í…œì„ ì™„ì „ížˆ 초기화할 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"표준시간대 설정"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ íœ´ëŒ€ì „í™”ì˜ í‘œì¤€ì‹œê°„ëŒ€ë¥¼ 변경할 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"AccountManagerServiceë¡œ 활ë™"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ AccountAuthenticators으로 전화를 걸 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"알려진 계정 검색"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ íœ´ëŒ€ì „í™”ì— ì•Œë ¤ì§„ 계정 목ë¡ì„ 가져올 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"계정 ì¸ì¦ìžë¡œ 활ë™"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ê³„ì • 만들기, 비밀번호 가져오기 ë° ì„¤ì • 등과 ê°™ì€ AccountManagerì˜ ê³„ì • ì¸ì¦ìž ê¸°ëŠ¥ì„ ì‚¬ìš©í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"계정 ëª©ë¡ ê´€ë¦¬"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ê³„ì • 추가, ì‚­ì œ ë° ë¹„ë°€ë²ˆí˜¸ ì‚­ì œ ë“±ì˜ ìž‘ì—…ì„ ìˆ˜í–‰í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"ê³„ì •ì˜ ì¸ì¦ ìžê²©ì¦ëª… 사용"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì¸ì¦ 토í°ì„ 요청하ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ë„¤íŠ¸ì›Œí¬ ìƒíƒœ 보기"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ëª¨ë“  네트워í¬ì˜ ìƒíƒœë¥¼ ë³¼ 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"ì¸í„°ë„·ì— 완전히 액세스"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ Wi-Fiì˜ ìƒíƒœì— 대한 정보를 ë³¼ 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"Wi-Fi ìƒíƒœ 변경"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ Wi-Fi 액세스í¬ì¸íŠ¸ì— 연결하거나 ì—°ê²°ì„ ëŠê³ , êµ¬ì„±ëœ Wi-Fi 네트워í¬ë¥¼ 변경할 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"Wi-Fi 멀티ìºìŠ¤íŠ¸ 수신 허용"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ íœ´ëŒ€ê¸°ê¸°ë¡œ ì§ì ‘ 주소가 지정ë˜ì§€ ì•Šì€ íŒ¨í‚·ì„ ë°›ì„ ìˆ˜ 있ë„ë¡ í•©ë‹ˆë‹¤. ì´ ê¸°ëŠ¥ì€ ê°€ê¹Œìš´ ê³³ì—ì„œ 제공ë˜ëŠ” 서비스를 검색할 ë•Œ 유용하며 비멀티ìºìŠ¤íŠ¸ 모드보다 ì „ì›ì„ ë” ë§Žì´ ì†Œë¹„í•©ë‹ˆë‹¤."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"Bluetooth 관리"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë¡œì»¬ Bluetooth 휴대전화를 구성한 ë‹¤ìŒ ì›ê²© 장치를 검색하여 페어ë§í•  수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"Bluetooth 연결 만들기"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‚¬ìš©ìž ì‚¬ì „ì— ë³´ê´€ë˜ì–´ 있는 비공개 단어, ì´ë¦„ ë° êµ¬ë¬¸ì„ ì½ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"ìƒìš©ìž ì •ì˜ ì‚¬ì „ì— ìž‘ì„±"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ì‚¬ìš©ìž ì‚¬ì „ì— ìƒˆ 단어를 입력할 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"SD ì¹´ë“œ 콘í…츠 수정/ì‚­ì œ"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ SD ì¹´ë“œì— ì“¸ 수 있ë„ë¡ í•©ë‹ˆë‹¤."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"집"</item>
<item msgid="869923650527136615">"모바ì¼"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"기타"</item>
<item msgid="2374913952870110618">"맞춤설정"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"모바ì¼"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"집"</item>
<item msgid="5629153956045109251">"회사"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"맞습니다."</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"죄송합니다. 다시 ì‹œë„하세요."</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"충전 중(<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"충전ë˜ì—ˆìŠµë‹ˆë‹¤."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"충전기를 연결하세요."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"SIM 카드가 없습니다."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"íœ´ëŒ€ì „í™”ì— SIM 카드가 없습니다."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"잠금해제 íŒ¨í„´ì„ <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 ë” ì‹¤íŒ¨í•˜ë©´ Google 로그ì¸ì„ 통해 휴대전화를 잠금해제하ë„ë¡ ìš”ì²­ë©ë‹ˆë‹¤. "\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>ì´ˆ í›„ì— ë‹¤ì‹œ ì‹œë„하세요."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g>ì´ˆ í›„ì— ë‹¤ì‹œ ì‹œë„하세요."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"íŒ¨í„´ì„ ìžŠìœ¼ì…¨ë‚˜ìš”?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"íŒ¨í„´ì„ ë„ˆë¬´ ë§Žì´ ì‹œë„했습니다."</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"잠금해제하려면 Google 계정으로 로그ì¸í•˜ì„¸ìš”."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ì‚¬ìš©ìž ì´ë¦„(ì´ë©”ì¼)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"비밀번호"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"로그ì¸"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ì‚¬ìš©ìž ì´ë¦„ ë˜ëŠ” 비밀번호가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"지우기"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"알림 ì—†ìŒ"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"진행 중"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"알림"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"충전기를 연결하세요."</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"배터리 ì „ì›ì´ 부족합니다."</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"<xliff:g id="NUMBER">%d%%</xliff:g> 미만 남ìŒ"</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"출고 테스트 불합격"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST ìž‘ì—…ì€ /system/app ë””ë ‰í† ë¦¬ì— ì„¤ì¹˜ëœ íŒ¨í‚¤ì§€ì— ëŒ€í•´ì„œë§Œ 지ì›ë©ë‹ˆë‹¤."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"ìžë°”스í¬ë¦½íŠ¸"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"다른 페ì´ì§€ë¥¼ íƒìƒ‰í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"계ì†í•˜ë ¤ë©´ \'확ì¸\'ì„ ì„ íƒí•˜ê³  현재 페ì´ì§€ì— 그대로 있으려면 \'취소\'를 ì„ íƒí•˜ì„¸ìš”."</string>
<string name="save_password_label" msgid="6860261758665825069">"확ì¸"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브ë¼ìš°ì €ì˜ ê¸°ë¡ ë° ë¶ë§ˆí¬ ì½ê¸°"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë¸Œë¼ìš°ì €ë¡œ 방문한 모든 URLê³¼ 브ë¼ìš°ì €ì˜ 모든 ë¶ë§ˆí¬ë¥¼ ì½ë„ë¡ í—ˆìš©í•©ë‹ˆë‹¤."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브ë¼ìš°ì €ì˜ ê¸°ë¡ ë° ë¶ë§ˆí¬ 쓰기"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"ì‘ìš©í”„ë¡œê·¸ëž¨ì´ íœ´ëŒ€ì „í™”ì— ì €ìž¥ëœ ë¸Œë¼ìš°ì € ê¸°ë¡ ë˜ëŠ” ë¶ë§ˆí¬ë¥¼ 수정할 수 있ë„ë¡ í—ˆìš©í•©ë‹ˆë‹¤. ì´ ê²½ìš° 악성 ì‘ìš©í”„ë¡œê·¸ëž¨ì´ ë¸Œë¼ìš°ì €ì˜ ë°ì´í„°ë¥¼ 지우거나 수정할 수 있습니다."</string>
<string name="save_password_message" msgid="767344687139195790">"브ë¼ìš°ì €ì— ì´ ë¹„ë°€ë²ˆí˜¸ë¥¼ 저장하시겠습니까?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"나중ì—"</string>
<string name="save_password_remember" msgid="6491879678996749466">"저장"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"ë‚´ì¼"</item>
<item quantity="other" msgid="2973062968038355991">"<xliff:g id="COUNT">%d</xliff:g>ì¼ í›„"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%së…„"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"ì¼"</string>
<string name="days" msgid="4774547661021344602">"ì¼"</string>
<string name="hour" msgid="2126771916426189481">"시간"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"ëª¨ë‘ ë³µì‚¬"</string>
<string name="paste" msgid="5629880836805036433">"붙여넣기"</string>
<string name="copyUrl" msgid="2538211579596067402">"URL 복사"</string>
- <string name="inputMethod" msgid="7673923508389094672">"입력 방법"</string>
- <string name="addToDictionary" msgid="726256909274177272">"ì‚¬ì „ì— \'%s\' 추가"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"í…스트 수정"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"저장공간 부족"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"휴대전화 ì €ìž¥ê³µê°„ì´ ë¶€ì¡±í•©ë‹ˆë‹¤."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g> ì‘용프로그램(<xliff:g id="PROCESS">%2$s</xliff:g> 프로세스)ì´ ì‘답하지 않습니다."</string>
<string name="anr_process" msgid="1246866008169975783">"<xliff:g id="PROCESS">%1$s</xliff:g> 프로세스가 ì‘답하지 않습니다."</string>
<string name="force_close" msgid="3653416315450806396">"닫기"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"ì‹ ê³ "</string>
<string name="wait" msgid="7147118217226317732">"대기"</string>
<string name="debug" msgid="9103374629678531849">"디버그"</string>
<string name="sendText" msgid="5132506121645618310">"í…ìŠ¤íŠ¸ì— ëŒ€í•œ ìž‘ì—… ì„ íƒ"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"벨소리 볼륨"</string>
<string name="volume_music" msgid="5421651157138628171">"미디어 볼륨"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Bluetooth를 통해 재ìƒ"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"고주파 벨소리 ì„ íƒ"</string>
<string name="volume_call" msgid="3941680041282788711">"통화 볼륨"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Bluetooth 통화 볼륨"</string>
<string name="volume_alarm" msgid="1985191616042689100">"알람 볼륨"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"USB 저장소를 ë„기 ì „ì— ë°˜ë“œì‹œ USB 호스트ì—ì„œ 마운트 해제하세요. USB 저장소를 ë„려면 \'ë„기\'를 ì„ íƒí•˜ì„¸ìš”."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"USB 저장소 ë„기"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"취소"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"USB 저장소를 ë„는 ë™ì•ˆ Weveì— ë¬¸ì œê°€ 발행했습니다. USB 호스트를 마운트 해제했는지 확ì¸í•œ 후 다시 ì‹œë„하세요."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"SD ì¹´ë“œ í¬ë§·"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SD 카드를 í¬ë§·í•˜ì‹œê² ìŠµë‹ˆê¹Œ? í¬ë§·í•˜ë©´ ì¹´ë“œì˜ ëª¨ë“  ë°ì´í„°ë¥¼ 잃게 ë©ë‹ˆë‹¤."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"í¬ë§·"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB 디버깅 ì—°ê²°ë¨"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"컴퓨터가 íœ´ëŒ€ì „í™”ì— ì—°ê²°ë˜ì–´ 있습니다."</string>
<string name="select_input_method" msgid="2086499663193509436">"ìž…ë ¥ 방법 ì„ íƒ"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"가능한 ì›ì¸"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"SD 카드 준비 중"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"오류 í™•ì¸ ì¤‘ìž…ë‹ˆë‹¤."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"빈 SD 카드"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD 카드가 비어 있거나 지ì›ë˜ì§€ 않는 íŒŒì¼ ì‹œìŠ¤í…œì„ ì‚¬ìš©í•©ë‹ˆë‹¤."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"ì†ìƒëœ SD ì¹´ë“œ"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD 카드가 ì†ìƒë˜ì—ˆìŠµë‹ˆë‹¤. 카드를 다시 í¬ë§·í•´ì•¼ í•  수 있습니다."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD 카드가 예ìƒì¹˜ 않게 제거ë˜ì—ˆìŠµë‹ˆë‹¤."</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"ë°ì´í„° ì†ì‹¤ì„ 피하려면 SD 카드를 제거하기 ì „ì— ë§ˆìš´íŠ¸ 해제합니다."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD 카드를 안전하게 제거할 수 있습니다."</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"안전하게 SD 카드를 제거할 수 있습니다."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"SD 카드를 제거했습니다."</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD 카드가 제거ë˜ì—ˆìŠµë‹ˆë‹¤. 새 카드를 넣으세요."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"ì¼ì¹˜í•˜ëŠ” 활ë™ì´ 없습니다."</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"구성 요소 사용 통계 ì—…ë°ì´íŠ¸"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"ìˆ˜ì§‘ëœ êµ¬ì„±ìš”ì†Œ 사용 통계를 수정할 수 있는 ê¶Œí•œì„ ë¶€ì—¬í•©ë‹ˆë‹¤. ì¼ë°˜ ì‘ìš©í”„ë¡œê·¸ëž¨ì€ ì´ ê¶Œí•œì„ ì‚¬ìš©í•  수 없습니다."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"실행"</string>
<string name="dial_number_using" msgid="5789176425167573586">"전화하기 "\n"<xliff:g id="NUMBER">%s</xliff:g>ì— ì—°ê²°"</string>
<string name="create_contact_using" msgid="4947405226788104538">"전화번호부ì—"\n"<xliff:g id="NUMBER">%s</xliff:g> 추가"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"ì„ íƒí•¨"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ì„ íƒ ì•ˆí•¨"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"허용"</string>
+ <string name="deny" msgid="2081879885755434506">"거부"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"권한 요청"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-mcc204-el/strings.xml b/core/res/res/values-mcc204-el/strings.xml
index f7ca3d2..94786f1 100644
--- a/core/res/res/values-mcc204-el/strings.xml
+++ b/core/res/res/values-mcc204-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="2972154133076909542">"el_GR"</string>
+ <string name="locale_replacement" msgid="2972154133076909542">"nl_nl"</string>
</resources>
diff --git a/core/res/res/values-mcc204-nb/strings.xml b/core/res/res/values-mcc204-nb/strings.xml
new file mode 100644
index 0000000..94786f1
--- /dev/null
+++ b/core/res/res/values-mcc204-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="2972154133076909542">"nl_nl"</string>
+</resources>
diff --git a/core/res/res/values-mcc230-el/strings.xml b/core/res/res/values-mcc230-el/strings.xml
index 49ea9f3..63ade62 100644
--- a/core/res/res/values-mcc230-el/strings.xml
+++ b/core/res/res/values-mcc230-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="9011721823833053909">"el_GR"</string>
+ <string name="locale_replacement" msgid="9011721823833053909">"cs_cz"</string>
</resources>
diff --git a/core/res/res/values-mcc230-nb/strings.xml b/core/res/res/values-mcc230-nb/strings.xml
new file mode 100644
index 0000000..63ade62
--- /dev/null
+++ b/core/res/res/values-mcc230-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="9011721823833053909">"cs_cz"</string>
+</resources>
diff --git a/core/res/res/values-mcc232-el/strings.xml b/core/res/res/values-mcc232-el/strings.xml
index 3698d2c..b028927 100644
--- a/core/res/res/values-mcc232-el/strings.xml
+++ b/core/res/res/values-mcc232-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="166900303893651370">"el_GR"</string>
+ <string name="locale_replacement" msgid="166900303893651370">"de_at"</string>
</resources>
diff --git a/core/res/res/values-mcc232-nb/strings.xml b/core/res/res/values-mcc232-nb/strings.xml
new file mode 100644
index 0000000..b028927
--- /dev/null
+++ b/core/res/res/values-mcc232-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="166900303893651370">"de_at"</string>
+</resources>
diff --git a/core/res/res/values-mcc234-el/strings.xml b/core/res/res/values-mcc234-el/strings.xml
index e04aa93..bd391e1 100644
--- a/core/res/res/values-mcc234-el/strings.xml
+++ b/core/res/res/values-mcc234-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="233769627858930762">"el_GR"</string>
+ <string name="locale_replacement" msgid="233769627858930762">"en_gb"</string>
</resources>
diff --git a/core/res/res/values-mcc234-nb/strings.xml b/core/res/res/values-mcc234-nb/strings.xml
new file mode 100644
index 0000000..bd391e1
--- /dev/null
+++ b/core/res/res/values-mcc234-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="233769627858930762">"en_gb"</string>
+</resources>
diff --git a/core/res/res/values-mcc260-el/strings.xml b/core/res/res/values-mcc260-el/strings.xml
index ea6f87b..13ea1b2 100644
--- a/core/res/res/values-mcc260-el/strings.xml
+++ b/core/res/res/values-mcc260-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="6306793451973344945">"el_GR"</string>
+ <string name="locale_replacement" msgid="6306793451973344945">"pl_pl"</string>
</resources>
diff --git a/core/res/res/values-mcc260-nb/strings.xml b/core/res/res/values-mcc260-nb/strings.xml
new file mode 100644
index 0000000..13ea1b2
--- /dev/null
+++ b/core/res/res/values-mcc260-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="6306793451973344945">"pl_pl"</string>
+</resources>
diff --git a/core/res/res/values-mcc262-el/strings.xml b/core/res/res/values-mcc262-el/strings.xml
index 38827c5..a90e7cf 100644
--- a/core/res/res/values-mcc262-el/strings.xml
+++ b/core/res/res/values-mcc262-el/strings.xml
@@ -15,5 +15,5 @@
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="locale_replacement" msgid="273407696421660814">"el_GR"</string>
+ <string name="locale_replacement" msgid="273407696421660814">"de_de"</string>
</resources>
diff --git a/core/res/res/values-mcc262-nb/strings.xml b/core/res/res/values-mcc262-nb/strings.xml
new file mode 100644
index 0000000..a90e7cf
--- /dev/null
+++ b/core/res/res/values-mcc262-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="locale_replacement" msgid="273407696421660814">"de_de"</string>
+</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c19d057..c4d8292 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -21,7 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;uten navn&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Mangler telefonnummer)"</string>
@@ -80,11 +80,11 @@
<string name="roamingText3" msgid="5148255027043943317">"Ute av nabolaget"</string>
<string name="roamingText4" msgid="8808456682550796530">"Ute av bygningen"</string>
<string name="roamingText5" msgid="7604063252850354350">"Roaming - foretrukket system"</string>
- <string name="roamingText6" msgid="2059440825782871513">"Roaming - tilgjengelig system"</string>
- <string name="roamingText7" msgid="7112078724097233605">"Roaming - alliansepartner"</string>
- <string name="roamingText8" msgid="5989569778604089291">"Roaming - gullpartner"</string>
- <string name="roamingText9" msgid="7969296811355152491">"Roaming - full tjenestefunksjoanlitet"</string>
- <string name="roamingText10" msgid="3992906999815316417">"Roaming - delvis tjenestefunksjonalitet"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming – tilgjengelig system"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming – alliansepartner"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming – gullpartner"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming – full tjenestefunksjonalitet"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming – delvis tjenestefunksjonalitet"</string>
<string name="roamingText11" msgid="4154476854426920970">"Roaming-banner på"</string>
<string name="roamingText12" msgid="1189071119992726320">"Roaming-banner av"</string>
<string name="roamingTextSearching" msgid="8360141885972279963">"Leter etter tjeneste"</string>
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Kunne ikke åpne filen."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Fant ikke den forespurte filen."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"For mange forespørsler blir behandlet. Prøv igjen senere."</string>
+ <string name="notification_title" msgid="4254304601929719937">"PÃ¥loggingsfeil"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synkronisering"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkronisering"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"For mange slettinger av <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -201,7 +202,7 @@
<string name="permlab_broadcastSmsReceived" msgid="5689095009030336593">"kringkaste melding om mottatt SMS"</string>
<string name="permdesc_broadcastSmsReceived" msgid="9122419277306740155">"Lar applikasjonen kringkaste en melding om at en SMS-melding er mottatt. Ondsinnede applikasjoner kan bruke dette til å forfalske innkommende SMS-meldinger."</string>
<string name="permlab_broadcastWapPush" msgid="3145347413028582371">"kringkaste melding om mottatt WAP-PUSH"</string>
- <string name="permdesc_broadcastWapPush" msgid="3955303669461378091">"Lar applikasjonen kringkaste en melding om at en WAP-PUSH-melding er blitt mottatt. Ondsinnede applikasjoner kan bruke dette til å forfalske MMS-kvitteringer eller i det stille erstatte innholdet av vilkårlige nettsider med ondsinnede varianter."</string>
+ <string name="permdesc_broadcastWapPush" msgid="3955303669461378091">"Lar applikasjonen kringkaste en melding om at en WAP-PUSH-melding er blitt mottatt. Ondsinnede applikasjoner kan bruke dette til å forfalske MMS-kvitteringer eller i det stille erstatte innholdet av vilkårlige nettsteder med ondsinnede varianter."</string>
<string name="permlab_setProcessLimit" msgid="2451873664363662666">"begrense antallet kjørende prosesser"</string>
<string name="permdesc_setProcessLimit" msgid="7824786028557379539">"Lar applikasjonen kontrollere maksimalt antall kjørende prosesser. Behøves aldri for vanlige applikasjoner."</string>
<string name="permlab_setAlwaysFinish" msgid="5342837862439543783">"få alle bakgrunnsapplikasjoner til å lukkes"</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"endre batteristatistikk"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Lar applikasjonen endre på innsamlet batteristatistikk. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrollere backup og gjenoppretting"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Lar applikasjonen kontrollere systemets backup- og gjenopprettingsmekanisme. Ikke ment for vanlige applikasjoner."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"vis uautoriserte vinduer"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillater at det opprettes vinduer ment for bruk av systemets interne brukergrensesnitt. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"vise advarsler på systemnivå"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Lar applikasjonen overvåke tastetrykk selv når interaksjonen er med et annet program (som å skrive inn et passord). Vanlige applikasjoner bør aldri trenge dette."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"binde til en inndatametode"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Lar applikasjonen binde til toppnivågrensesnittet for en inndatametode. Vanlige applikasjoner bør aldri trenge dette."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"bind til bakgrunnsbilde"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Lar innehaveren binde det øverste nivået av grensesnittet til en bakgrunnsbilder. Skal ikke være nødvendig for vanlige programmer."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"snu skjermen"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sende Linux-signaler til applikasjoner"</string>
@@ -243,9 +251,9 @@
<string name="permlab_clearAppCache" msgid="4747698311163766540">"slette hurtigbufferdata for alle applikasjoner"</string>
<string name="permdesc_clearAppCache" msgid="7740465694193671402">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string>
<string name="permlab_readLogs" msgid="4811921703882532070">"lese systemets loggfiler"</string>
- <string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen to lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
+ <string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string>
<string name="permlab_diagnostic" msgid="8076743953908000342">"lese/skrive ressurser eid av diag"</string>
- <string name="permdesc_diagnostic" msgid="3121238373951637049">"Lar applikasjonen to lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string>
+ <string name="permdesc_diagnostic" msgid="3121238373951637049">"Lar applikasjonen lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string>
<string name="permlab_changeComponentState" msgid="79425198834329406">"aktivere eller deaktigere applikasjonskomponenter"</string>
<string name="permdesc_changeComponentState" msgid="4569107043246700630">"Lar applikasjonen endre om en komponent i en annen applikasjon er aktivert eller ikke. Ondsinnede applikasjoner kan bruke dette til å deaktivere viktige telefonfunksjoner. Denne rettigheten må brukes med forsiktighet, ettersom det er mulig å få applikasjonskomponenter inn i en ubrukelig, inkonsistent eller ustabil tilstand."</string>
<string name="permlab_setPreferredApplications" msgid="3393305202145172005">"velge foretrukne applikasjoner"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"endre globale systeminnstillinger"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Lar applikasjonen endre systemets innstillingsdata. Ondsinnede applikasjoner kan skade systemets innstillinger."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"endre sikre systeminnstillinger"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Lar applikasjonen endre systemets sikre innstillingsdata. Ikke ment for bruk av vanlige applikasjoner."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"redigere Google-tjenestekartet"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Lar applikasjonen redigere Google-tjenestekartet. Ikke ment for bruk av vanlige applikasjoner."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"starte automatisk sammen med systemet"</string>
@@ -272,8 +281,8 @@
<string name="permdesc_readCalendar" msgid="5533029139652095734">"Lar applikasjonen lese alle kalenderhendelser lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å sende kalenderhendelser til andre."</string>
<string name="permlab_writeCalendar" msgid="377926474603567214">"skrive kalenderinformasjon"</string>
<string name="permdesc_writeCalendar" msgid="8674240662630003173">"Lar applikasjonen endre kalenderhendelser lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å slette eller endre kalenderinformasjon."</string>
- <string name="permlab_accessMockLocation" msgid="8688334974036823330">"lage manuelle plasseringskilder for testing"</string>
- <string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Lage manuelle plassingskilder for testing. Ondsinnede applikasjoner kan bruke dette til å overstyre plasseringen og/eller statusen rapportert av ekte plasseringskilder slik som GPS eller nettverksoperatører."</string>
+ <string name="permlab_accessMockLocation" msgid="8688334974036823330">"lage simulerte plasseringskilder for testing"</string>
+ <string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Lage simulerte plassingskilder for testing. Ondsinnede applikasjoner kan bruke dette til å overstyre plasseringen og/eller statusen rapportert av ekte plasseringskilder slik som GPS eller nettverksoperatører."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få tilgang til ekstra plasseringskommandoer"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Få tilgang til ekstra kommandoer for plasseringskilder. Ondsinnede applikasjoner kan bruke dette til å forstyrre GPS eller andre plasseringskilder."</string>
<string name="permlab_installLocationProvider" msgid="6578101199825193873">"installere plasseringskilder"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"få tilgang til SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Lar applikasjonen bruke lavnivåfunksjonalitet i SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lese skjermbufferet"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Lar applikasjonen lese innholdet i skjermbufferet."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Tillater at programmet leser innholdet av rammebufferen."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"endre lydinnstillinger"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Lar applikasjonen endre globale lydinnstillinger som volum og ruting."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ta opp lyd"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Lar applikasjonen ringe telefonnummer uten inngripen fra brukeren. Ondsinnede applikasjoner kan forårsake uventede oppringinger på telefonregningen. Merk at dette ikke gir applikasjonen lov til å ringe nødnummer."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"ringe vilkårlige telefonnummer direkte"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Lar applikasjonen ringe hvilket som helst telefonnummer, inkludert nødnummer, uten inngripen fra brukeren. Ondsinnede applikasjoner kan forårsake unødvendige og ulovlige samtaler til nødtjenester."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"kontrollere varsling for plasseringsendring"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Lar applikasjonen slå av/på varsling om plasseringsendringer fra radioen. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"få tilgang til egenskaper for innsjekking"</string>
@@ -318,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Lar applikasjonen fortelle systemet hvilke gadgeter som kan brukes av hvilke applikasjoner. Med denne rettigheten kan applikasjoner andre applikasjoner tilgang til personlig data. Ikke ment for vanlige applikasjoner."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"endre telefontilstand"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Lar applikasjonen kontrollere telefonfunksjonaliteten i enheten. En applikasjon med denne rettigheten kan endre nettverk, slå telefonens radio av eller på og lignende uten noensinne å varsle brukeren."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"lese telefontilstand og -identitet"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Gir applikasjonen tilgang til telefonfunksjonaliteten i enheten. En applikasjon med denne rettigheten kan finne telefonens telefonnummer og seriellnummer, om en samtale er aktiv, nummeret det ringes til og lignende."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"forhindre telefonen fra å sove"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Lar applikasjonen forhindre telefonen fra å gå i hvilemodus."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"slå telefonen av eller på"</string>
@@ -332,12 +343,20 @@
<string name="permdesc_setWallpaper" msgid="6417041752170585837">"Lar applikasjonen sette systemets bakgrunnsbilde."</string>
<string name="permlab_setWallpaperHints" msgid="3600721069353106851">"sette størrelseshint for bakgrunn"</string>
<string name="permdesc_setWallpaperHints" msgid="6019479164008079626">"Lar applikasjonen sette størrelseshint for systemets bakgrunnsbilde."</string>
- <string name="permlab_masterClear" msgid="2315750423139697397">"nullstille systemet til fabrikkinnstillinger"</string>
- <string name="permdesc_masterClear" msgid="5033465107545174514">"Lar applikasjonen nullstille systemet til fabrikkinnstillinger, noe som vil fjerne alle data, alt oppsett, og alle installerte applikasjoner."</string>
+ <string name="permlab_masterClear" msgid="2315750423139697397">"tilbakestille systemet til fabrikkinnstillinger"</string>
+ <string name="permdesc_masterClear" msgid="5033465107545174514">"Lar applikasjonen tilbakestille systemet til fabrikkinnstillinger, noe som vil fjerne alle data, alt oppsett, og alle installerte applikasjoner."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"endre tidssone"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Lar applikasjonen endre telefonens tidssone."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"fungere som kontoadministrasjonstjenesten"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Lar et program foreta anrop til kontogodkjennere"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"oppdage kjente kontoer"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Lar applikasjonen hente listen over kontoer telefonen kjenner til."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"fungere som en kontogodkjenner"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Lar et program bruke kontogodkjenningsfunksjonaliteten til kontoadministratoren, herunder opprette kontoer og få og angi passord."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"administrere kontolisten"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Lar et program utføre handlinger som å legge til og fjerne kontoer samt slette passord."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"bruke godkjenningsopplysningene for en konto"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Lar et program be om godkjenningsinformasjon"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"se nettverkstilstand"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Lar applikasjonen se tilstanden til alle nettverk."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"full internett-tilgang"</string>
@@ -365,7 +384,7 @@
<string name="permlab_writeSyncSettings" msgid="6297138566442486462">"skrive synkroniseringsinnstillinger"</string>
<string name="permdesc_writeSyncSettings" msgid="2498201614431360044">"Lar applikasjonen to endre på synkroniseringsinnstillingene, som for eksempel om kontakter blir synkronisert."</string>
<string name="permlab_readSyncStats" msgid="7396577451360202448">"lese synkroniseringsstatistikk"</string>
- <string name="permdesc_readSyncStats" msgid="7511448343374465000">"Lar applikasjonen lese synkroniseringsstatistikk, som for eksempel historien over alle synkroniseringer utført."</string>
+ <string name="permdesc_readSyncStats" msgid="7511448343374465000">"Lar applikasjonen lese synkroniseringsstatistikk, som for eksempel loggen over alle synkroniseringer utført."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"lese abonnement på nyhetskilder"</string>
<string name="permdesc_subscribedFeedsRead" msgid="3622200625634207660">"Lar applikasjonen hente detaljer om hvilke nyhetskilder som synkroniseres."</string>
<string name="permlab_subscribedFeedsWrite" msgid="9015246325408209296">"endre abonnement på nyhetskilder"</string>
@@ -392,7 +411,6 @@
<item msgid="1112044410659011023">"Annen"</item>
<item msgid="2374913952870110618">"Egendefinert…"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Hjemme"</item>
<item msgid="5629153956045109251">"Arbeid"</item>
@@ -422,7 +440,7 @@
</string-array>
<string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string>
<string name="keyguard_password_wrong_pin_code" msgid="1295984114338107718">"Gal PIN-kode!"</string>
- <string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på Meny og deretter 0."</string>
+ <string name="keyguard_label_text" msgid="861796461028298424">"For å låse opp, trykk på menyknappen og deretter 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Nødnummer"</string>
<string name="lockscreen_carrier_default" msgid="8812714795156374435">"(Ingen operatør)"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Skjermen er låst"</string>
@@ -447,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using your Google sign-in."\n\n" Please try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Prøv igjen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glemt mønsteret?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"For mange mønsterforsøk!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"For å låse opp, logg på med Google-kontoen din"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Brukernavn (e-post)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Passord"</string>
- <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Logg inn"</string>
+ <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Logg på"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Ugyldig brukernavn eller passord."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Fjern"</string>
@@ -464,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Koble til en lader"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet er nesten tomt:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"mindre enn <xliff:g id="NUMBER">%d%%</xliff:g> igjen."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Hvorfor?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Factory test failed"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"The FACTORY_TEST action is only supported for packages installed in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"No package was found that provides the FACTORY_TEST action."</string>
@@ -473,10 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Velg OK for å fortsette, eller Avbryt for å forbli på denne siden."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekreft"</string>
- <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens historie og bokmerker"</string>
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string>
<string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string>
- <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens historie og bokmerker"</string>
- <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Lar applikasjonen endre nettleserens historie og bokmerker lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å fjerne eller redigere nettleserens data."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Lar applikasjonen endre nettleserens logg og bokmerker lagret på telefonen. Ondsinnede applikasjoner kan bruke dette til å fjerne eller redigere nettleserens data."</string>
<string name="save_password_message" msgid="767344687139195790">"Ønsker du at nettleseren skal huske dette passordet?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Ikke nå"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Husk"</string>
@@ -555,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"i morgen"</item>
<item quantity="other" msgid="2973062968038355991">"om <xliff:g id="COUNT">%d</xliff:g> d"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dag"</string>
<string name="days" msgid="4774547661021344602">"dager"</string>
<string name="hour" msgid="2126771916426189481">"time"</string>
@@ -595,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Kopier alt"</string>
<string name="paste" msgid="5629880836805036433">"Lim inn"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopier URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Inndatametode"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Legg «%s» til ordlisten"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Rediger tekst"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Lite plass"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Det begynner å bli lite lagringsplass på telefonen."</string>
@@ -628,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Ringetonevolum"</string>
<string name="volume_music" msgid="5421651157138628171">"Medievolum"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Spiller over Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Stille ringetone valgt"</string>
<string name="volume_call" msgid="3941680041282788711">"Samtalevolum"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Bluetooth-samtalevolum"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Alarmvolum"</string>
@@ -671,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Før du slår av USB-lagring, sjekk at du har avmontert enheten i USB-verten. Velg «slå av» for å slå av USB-lagring."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Slå av"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Avbryt"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Det oppsto et problem under avslutningen av USB-lagring. Sjekk at USB-verten har avmontert og prøv igjen."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatere minnekort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Er du sikker på at du ønsker å formatere minnekortet? Alle data på kortet vil gå tapt."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
@@ -708,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Lag kontakt"\n"med nummeret <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"valgt"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"ikke valgt"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Tillat"</string>
+ <string name="deny" msgid="2081879885755434506">"Avslå"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Tillatelse forespurt"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 709e7cc..9a65f95 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Het bestand kan niet worden geopend."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Het opgevraagde bestand is niet gevonden."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Er worden te veel aanvragen verwerkt. Probeer het later opnieuw."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Fout bij aanmelding"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchroniseren"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchroniseren"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Te veel verwijderen voor <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"accustatistieken aanpassen"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Hiermee kunnen verzamelde accustatistieken worden gewijzigd. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_backup" msgid="470013022865453920">"systeemback-up en -herstel beheren"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Hiermee kan de toepassing het mechanisme voor systeemback-up en -herstel beheren. Niet voor gebruik door normale toepassingen."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"niet-geautoriseerde vensters weergeven"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Hiermee kunnen vensters worden gemaakt die door de interne systeemgebruikersinterface worden gebruikt. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"waarschuwingen op systeemniveau weergeven"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Hiermee kan een toepassing uw toetsaanslagen registreren, zelfs tijdens de interactie met een andere toepassing (zoals de invoer van een wachtwoord). Nooit vereist voor normale toepassingen."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"verbinden aan een invoermethode"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Hiermee staat u de houder toe zich te verbinden met de hoofdinterface van een invoermethode. Nooit vereist voor normale toepassingen."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"verbinden met een achtergrond"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Hiermee staat u de houder toe zich te verbinden met de hoofdinterface van een achtergrond. Nooit vereist voor normale toepassingen."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"schermstand wijzigen"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Hiermee kan een toepassing op elk gewenst moment de oriëntatie van het scherm wijzigen. Nooit vereist voor normale toepassingen."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-signalen verzenden naar toepassingen"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"algemene systeeminstellingen wijzigen"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Hiermee kan een toepassing de systeeminstellingen wijzigen. Schadelijke toepassingen kunnen hiermee uw systeemconfiguratie beschadigen."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"beveiligde systeeminstellingen wijzigen"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Hiermee kan een toepassing beveiligde systeeminstellingen wijzigen. Niet voor gebruik door normale toepassingen."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"de Google-serviceskaart wijzigen"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Hiermee kan een toepassing de Google-serviceskaart wijzigen. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"automatisch starten bij opstarten"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"toegang tot SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Hiermee kan een toepassing SurfaceFlinger-functies op laag niveau gebruiken."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"framebuffer lezen"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Hiermee kan een toepassing de inhoud van de framebuffer lezen."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Hiermee kan een toepassing de inhoud van de framebuffer lezen."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"uw audio-instellingen wijzigen"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Hiermee kan een toepassing de algemene audio-instellingen, zoals volume en omleiding, wijzigen."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"audio opnemen"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Hiermee kan de toepassing telefoonnummers bellen zonder uw tussenkomst. Door schadelijke toepassingen kunnen onverwachte oproepen op uw telefoonrekening verschijnen. De toepassing kan hiermee geen alarmnummers bellen."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"alle telefoonnummers rechtstreeks bellen"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Hiermee kan een toepassing elk telefoonnummer, inclusief alarmnummers, bellen zonder uw tussenkomst. Schadelijke toepassingen kunnen onnodige en illegale oproepen uitvoeren naar alarmdiensten."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"meldingen over locatie-updates beheren"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Hiermee kunnen updatemeldingen voor locaties van de radio worden ingeschakeld/uitgeschakeld. Niet voor gebruik door normale toepassingen."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"toegang tot checkin-eigenschappen"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Hiermee kan een toepassing het systeem terugzetten op de fabrieksinstellingen, waarbij alle gegevens, configuraties en geïnstalleerde toepassingen worden verwijderd."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"tijdzone instellen"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Hiermee kan een toepassing de tijdzone van de telefoon wijzigen."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"fungeren als de AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Hiermee kan een toepassing aanroepen plaatsen naar AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"bekende accounts zoeken"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Hiermee kan een toepassing de lijst met accounts van een telefoon ophalen."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"fungeren als verificatie-instantie voor het account"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Hiermee kan een toepassing de mogelijkheden voor verificatie-instanties voor het account van de AccountManager gebruiken, waaronder het maken van accounts en het ophalen en instellen van de bijbehorende wachtwoorden."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"de lijst met accounts beheren"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Hiermee kan een toepassing bewerkingen uitvoeren, zoals het toevoegen en verwijderen van accounts en het verwijderen van de bijbehorende wachtwoorden."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"de verificatiegegevens van een account gebruiken"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Hiermee kan een toepassing verificatietokens aanvragen."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"netwerkstatus bekijken"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Hiermee kan een toepassing de status van alle netwerken bekijken."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"volledige internettoegang"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Overig"</item>
<item msgid="2374913952870110618">"Aangepast"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobiel"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Thuis"</item>
<item msgid="5629153956045109251">"Werk"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen, wordt u gevraagd om uw telefoon te ontgrendelen met uw Google aanmelding."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Probeer het over <xliff:g id="NUMBER">%d</xliff:g> seconden opnieuw."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Patroon vergeten?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Te veel patroonpogingen!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Om te ontgrendelen, moet U zich eerst bij uw Google-account aanmelden"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Gebruikersnaam (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Wachtwoord"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Aanmelden"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Gebruikersnaam of wachtwoord ongeldig."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wissen"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Sluit de oplader aan"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"De accu raakt op:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"minder dan <xliff:g id="NUMBER">%d%%</xliff:g> resterend."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Waarom?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Fabriekstest mislukt"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"De actie FACTORY_TEST wordt alleen ondersteund voor pakketten die zijn geïnstalleerd in /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Er is geen pakket gevonden dat de actie FACTORY_TEST levert."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"morgen"</item>
<item quantity="other" msgid="2973062968038355991">"over <xliff:g id="COUNT">%d</xliff:g> dagen"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"op %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"om %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"in %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dag"</string>
<string name="days" msgid="4774547661021344602">"dagen"</string>
<string name="hour" msgid="2126771916426189481">"uur"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Alles kopiëren"</string>
<string name="paste" msgid="5629880836805036433">"Plakken"</string>
<string name="copyUrl" msgid="2538211579596067402">"URL kopiëren"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Invoermethode"</string>
- <string name="addToDictionary" msgid="726256909274177272">"%s\' toevoegen aan woordenboek"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Tekst bewerken"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Weinig ruimte"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Opslagruimte van telefoon raakt op."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Belvolume"</string>
<string name="volume_music" msgid="5421651157138628171">"Mediavolume"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Afspelen via Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Stille beltoon geselecteerd"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume inkomende oproep"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume tijdens gesprek in Bluetooth-modus"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Alarmvolume"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Voordat u de USB-opslag uitschakelt, moet u de koppeling met de USB-host verbreken. Selecteer \'Uitschakelen\' om USB-opslag uit te schakelen."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Uitschakelen"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Annuleren"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Er is een probleem opgetreden tijdens het uitschakelen van USB-opslag. Controleer of u de USB-host heeft losgekoppeld en probeer het opnieuw."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"SD-kaart formatteren"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Weet u zeker dat u de SD-kaart wilt formatteren? Alle gegevens op uw kaart gaan dan verloren."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatteren"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Contact maken"\n"met <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"aangevinkt"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niet aangevinkt"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Toestaan"</string>
+ <string name="deny" msgid="2081879885755434506">"Weigeren"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Toestemming gevraagd"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index a33e94e..de1221a 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Nie można uzyskać dostępu do pliku."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Nie znaleziono żądanego pliku."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Zbyt wiele żądań jest przetwarzanych. Spróbuj ponownie później."</string>
+ <string name="notification_title" msgid="4254304601929719937">"BÅ‚Ä…d logowania"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synchronizacja"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synchronizuj"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Zbyt wiele usuwanych <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"zmienianie statystyk dotyczÄ…cych baterii"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Pozwala na zmianÄ™ zebranych statystyk dotyczÄ…cych baterii. Nie do wykorzystania przez normalne aplikacje."</string>
<string name="permlab_backup" msgid="470013022865453920">"kontrolowanie tworzenia i przywracania kopii zapasowych systemu"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"Umożliwia aplikacji kontrolowanie mechanizmu tworzenia i przywracania kopii zapasowych systemu. Nie jest przeznaczone do użytku dla zwykłych aplikacji."</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"wyświetlanie nieuwierzytelnionych okien"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Pozwala na tworzenie okien, które przeznaczone są do wykorzystania przez wewnętrzny interfejs użytkownika systemu. Nie do wykorzystania przez normalne aplikacje."</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"wyświetlanie ostrzeżeń systemowych"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Pozwala aplikacjom na śledzenie naciskanych klawiszy, nawet podczas pracy z innym programem (na przykład podczas wpisywania hasła). Nigdy nie powinno być potrzebne normalnym aplikacjom."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"tworzenie powiązania z metodą wejściową"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Pozwala na tworzenie powiązania z interfejsem najwyższego poziomu metody wejściowej. To uprawnienie nie powinno być nigdy wymagane przez zwykłe aplikacje."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"powiÄ…zanie z tapetÄ…"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Umożliwia posiadaczowi powiązać interfejs najwyższego poziomu dla tapety. Nie powinno być nigdy potrzebne w przypadku zwykłych aplikacji."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"zmienianie orientacji ekranu"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Pozwala aplikacji na zmianę orientacji ekranu w dowolnym momencie. Nigdy nie powinno być potrzeby stosowania w normalnych aplikacjach."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"wysyłanie sygnałów systemu Linux do aplikacji"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modyfikowanie ogólnych ustawień systemu"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Pozwala aplikacji na zmianę danych ustawień systemowych. Szkodliwe aplikacje mogą uszkodzić konfigurację systemu."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modyfikowanie ustawień systemu dotyczących zabezpieczeń"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Pozwala aplikacji na modyfikowanie danych ustawień zabezpieczeń systemu. To uprawnienie nie jest wykorzystywane przez normalne aplikacje."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"zmienianie mapy usług Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Pozwala aplikacji na modyfikowanie mapy usług Google. Nie wykorzystywane przez normalne aplikacje."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"automatyczne uruchamianie podczas uruchamiania urzÄ…dzenia"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"dostęp do usługi SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Pozwala aplikacji na wykorzystanie funkcji niskiego poziomu usługi SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"czytanie bufora ramki"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Pozwala aplikacji na wykorzystanie odczytanej zawartości bufora ramki."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Umożliwia aplikacji odczytanie zawartości bufora ramki."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"zmienianie ustawień audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Pozwala aplikacjom na zmianę globalnych ustawień audio, takich jak głośność i routing."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"nagrywanie dźwięku"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Pozwala aplikacjom na dzwonienie pod numery telefonów bez interwencji użytkownika. Szkodliwe aplikacje mogą powodować występowanie niespodziewanych połączeń na rachunku telefonicznym. Należy zauważyć, że aplikacje nie mogą dzwonić na numery alarmowe."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"bezpośrednie wybieranie dowolnych numerów telefonu"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Pozwala aplikacji dzwonić na dowolny numer telefonu, włącznie z numerami alarmowymi, bez interwencji użytkownika. Szkodliwe aplikacje mogą wykonywać niepotrzebne i nielegalne połączenia z usługami alarmowymi."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"kontrolowanie powiadomień o aktualizacjach lokalizacji"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Pozwala włączyć/wyłączyć powiadomienia o aktualizacjach lokalizacji przez sieć bezprzewodową. Nie wykorzystywane przez normalne aplikacje."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"dostęp do właściwości usługi rezerwacji"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Pozwala aplikacji na całkowite zresetowanie systemu do ustawień fabrycznych, z wymazaniem wszystkich danych, konfiguracji oraz zainstalowanych aplikacji."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"ustawianie strefy czasowej"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Pozwala aplikacji na zmianÄ™ strefy czasowej w telefonie."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"działanie jako usługa AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Umożliwia aplikacji wywoływanie usług AccountAuthenticator"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"wykrywanie znanych kont"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Pozwala aplikacji na pobranie listy kont zapisanych w telefonie."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"działanie jako moduł uwierzytelniania konta"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Umożliwia aplikacji korzystanie z funkcji modułu uwierzytelniania konta usługi AccountManager, w tym funkcji tworzenia kont oraz pobierania i ustawiania ich haseł."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"zarzÄ…dzanie listÄ… kont"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Umożliwia aplikacji wykonywanie operacji takich jak dodawanie i usuwanie kont, a także usuwanie ich haseł."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"korzystanie z danych uwierzytelniania konta"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Umożliwia aplikacji żądanie tokenów uwierzytelniania."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"wyświetlanie stanu sieci"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Pozwala aplikacji na wyświetlanie stanu wszystkich sieci."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"pełen dostęp do internetu"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"Inne"</item>
<item msgid="2374913952870110618">"Niestandardowy"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Komórka"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Dom"</item>
<item msgid="5629153956045109251">"Praca"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Wzór odblokowania został narysowany nieprawidłowo <xliff:g id="NUMBER_0">%d</xliff:g> razy. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach telefon trzeba będzie odblokować przez zalogowanie na koncie Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> sekund."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Spróbuj ponownie za <xliff:g id="NUMBER">%d</xliff:g> sekund."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Zapomniałeś wzoru?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Zbyt wiele prób narysowania wzoru!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Aby odblokować, zaloguj się za pomocą konta Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nazwa użytkownika (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Hasło"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Zaloguj"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Błędna nazwa użytkownika lub hasło."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"Wyczyść"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"Podłącz ładowarkę"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Bateria się rozładowuje:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"pozostało mniej niż <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <string name="battery_low_why" msgid="7655196144309694753">"Dlaczego?"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Nieudany test fabryczny"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Czynność FACTORY_TEST jest obsługiwana tylko dla pakietów zainstalowanych w katalogu /system/app."</string>
<string name="factorytest_no_action" msgid="872991874799998561">"Nie znaleziono żadnego pakietu, który zapewnia działanie FACTORY_TEST."</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"jutro"</item>
<item quantity="other" msgid="2973062968038355991">"za <xliff:g id="COUNT">%d</xliff:g> dni"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"dnia %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"o %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"w %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dzień"</string>
<string name="days" msgid="4774547661021344602">"dni"</string>
<string name="hour" msgid="2126771916426189481">"godzina"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Kopiuj wszystko"</string>
<string name="paste" msgid="5629880836805036433">"Wklej"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopiuj adres URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Wprowadzanie tekstu"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Dodaj „%s†do słownika"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Edytuj tekst"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Mało miejsca"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Maleje ilość dostępnej pamięci telefonu."</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"Głośność dzwonka"</string>
<string name="volume_music" msgid="5421651157138628171">"Głośność multimediów"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Odtwarzanie przez Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Wybrano cichy dzwonek"</string>
<string name="volume_call" msgid="3941680041282788711">"Głośność podczas połączenia"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Głośność Bluetooth w czasie połączenia"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Głośność alarmu"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Przed wyÅ‚Ä…czeniem noÅ›nika USB upewnij siÄ™, że odÅ‚Ä…czono go od hosta USB. Wybierz „WyÅ‚Ä…czâ€, aby wyÅ‚Ä…czyć noÅ›nik USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Wyłącz"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Anuluj"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Napotkano problem przy wyłączaniu nośnika USB. Sprawdź, czy host USB został odłączony i spróbuj ponownie."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatuj kartÄ™ SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Czy na pewno sformatować kartę SD? Wszystkie dane na karcie zostaną utracone."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatuj"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"Utwórz kontakt"\n"dla numeru <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"zaznaczone"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"niezaznaczone"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Zezwól"</string>
+ <string name="deny" msgid="2081879885755434506">"Odmów"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Żądane pozwolenie"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index a6ab576..3e389ef 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;sem título&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Nenhum número de telefone)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Barramento de chamadas"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Alteração de palavra-passe"</string>
<string name="PinMmi" msgid="3113117780361190304">"Alteração de PIN"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Apresentação do número chamador"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Número chamador restringido"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Chamada de conferência entre três interlocutores"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Rejeição de chamadas inoportunas indesejadas"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Entrega do número chamador"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Não incomodar"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"ID do autor da chamada é predefinido como restrito. Chamada seguinte: Restrita"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"ID do autor da chamada é predefinido como restrito. Chamada seguinte: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"ID do autor da chamada é predefinido como não restrito. Chamada seguinte: Restrita"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Sincronização"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Pacote"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Indicador de Roaming activado"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Indicador de Roaming desactivado"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Indicador de Roaming intermitente"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Fora da Vizinhança"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"No Exterior"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Roaming - Sistema Preferencial"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming - Sistema Disponível"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming - Parceiro Alliance"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming - Parceiro Premium"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming - Funcionalidade de Serviço Total"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming - Funcionalidade de Serviço Parcial"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Faixa de Roaming activada"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Faixa de Roaming desactivada"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"A procurar Serviço"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Código de funcionalidade completo."</string>
+ <string name="fcError" msgid="3327560126588500777">"Problema de ligação ou código de funcionalidade inválido."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="2567300624552921790">"A página Web contém um erro."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Não foi possível localizar o URL."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Não foi possível aceder ao ficheiro."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Não foi possível localizar o ficheiro pedido."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Existem demasiados pedidos em processamento. Tente novamente mais tarde."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Erro de início de sessão"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sincronização"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronização"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Demasiadas eliminações de <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Acesso e controlo de nível inferior do sistema."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Ferramentas de desenvolvimento"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Funcionalidades apenas necessárias para programadores de aplicações."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"Aceder ao cartão SD."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desactivar ou modificar barra de estado"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Permite à aplicação desactivar a barra de estado ou adicionar e remover ícones do sistema."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/fechar barra de estado"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Permite a uma aplicação forçar qualquer actividade em primeiro plano a fechar e retroceder. Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_dump" msgid="1681799862438954752">"obter estado interno do sistema"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Permite à aplicação obter o estado interno do sistema. Algumas aplicações maliciosas podem obter uma ampla variedade de dados privados e seguros de que, normalmente, nunca devem necessitar."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"encerramento parcial"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gestor de actividade num estado de encerramento. Não executa um encerramento completo."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"impedir trocas de aplicações"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Impede que o utilizador mude para outra aplicação."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"monitorizar a controlar a iniciação de todas as aplicações"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Permite a uma aplicação monitorizar e controlar a forma como o sistema inicia actividades. Algumas aplicações maliciosas podem comprometer totalmente o sistema. Esta autorização apenas é necessária para desenvolvimento, nunca para a utilização normal do telefone."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar difusão de pacote removido"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Permite a uma aplicação controlar se as actividades são sempre terminadas assim que passam para o fundo. Nunca é necessário para aplicações normais."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"modificar estatísticas da bateria"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite a modificação das estatísticas recolhidas sobre a bateria. Não se destina a utilização por aplicações normais."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"controlar a cópia de segurança e restauro do sistema"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"apresentar janelas não autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite a criação de janelas destinadas a utilização pela interface de utilizador interna do sistema. Não se destina a utilização por aplicações normais."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Permite às aplicações verificar as teclas que o utilizador prime, mesmo ao interagir com outras aplicações (como, por exemplo, ao introduzir uma palavra-passe). Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"vincular a um método de entrada"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Permite ao titular vincular a interface de nível superior a um método de entrada de som. Nunca deve ser necessário para aplicações normais."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"vincular a uma imagem de fundo"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Permite ao titular vincular a interface de nível superior de uma imagem de fundo. Nunca deverá ser necessário para aplicações normais."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"mudar orientação do ecrã"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite a uma aplicação mudar a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais Linux para aplicações"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modificar definições globais do sistema"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Permite a uma aplicação modificar os dados das definições do sistema. Algumas aplicações maliciosas podem danificar as configurações do sistema."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modificar definições seguras do sistema"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Permite a uma aplicação modificar os dados de definições seguras dos sistemas. Não se destina a utilização por aplicações normais."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"modificar o mapa de serviços do Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Permite a uma aplicação modificar o mapa de serviços do Google. Não se destina a utilização por aplicações normais."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"iniciar automaticamente no arranque"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Crie fontes de localização fictícias para fins de teste. Algumas aplicações maliciosas podem utilizar este item para substituir a localização e/ou o estado devolvido por fontes de localização reais, tais como fornecedores de GPS ou de Rede."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"aceder a comandos adicionais do fornecedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Aceda a comandos adicionais do fornecedor de localização. Algumas aplicações maliciosas podem utilizar este item para interferir com o funcionamento do GPS ou de outras fontes de localização."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autorização para instalar um fornecedor de localização"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Criar origens de localização simuladas para testes. Aplicações maliciosas podem utilizar esta situação para substituir a localização e/ou o estado indicado por origens de localização reais, tais como fornecedores de rede ou GPS, ou monitorizar e informar a localização a uma origem externa."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"localização exacta (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Aceder a fontes de localização específicas, tais como o Sistema de Posicionamento Global (GPS), no telefone, se disponível. Algumas aplicações maliciosas podem utilizar este item para determinar a localização do utilizador e podem consumir energia adicional da bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"localização aproximada (baseada na rede)"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"aceder a SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Permite às aplicações utilizar funcionalidades de SurfaceFlinger de nível inferior."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler memória intermédia de fotogramas"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Permite à aplicação ler o conteúdo da memória intermédia de fotogramas."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Permite à aplicação ler o conteúdo da memória intermédia de fotogramas."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas definições de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Permite à aplicação modificar as definições de áudio globais, tais como o volume e o encaminhamento."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Permite à aplicação marcar números de telefone sem a intervenção do utilizador. Algumas aplicações maliciosas podem provocar o aparecimento de chamadas inesperadas na sua conta telefónica. Tenha em atenção que isto não permite à aplicação marcar números de emergência."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"marcar directamente quaisquer números de telefone"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Permite à aplicação marcar qualquer número de telefone, incluindo números de emergência, sem a intervenção do utilizador. Algumas aplicações maliciosas podem efectuar chamadas desnecessárias e ilegais para serviços de emergência."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"controlar notificações de actualização de localização"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Permite a activação/desactivação de notificações de actualização de localização a partir do rádio. Não se destina a utilização por aplicações normais."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"aceder a propriedades de verificação"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Permite à aplicação informar o sistema acerca das miniaplicações que podem ser utilizadas com cada aplicação. Com esta autorização, algumas aplicações podem conceder acesso a dados pessoais a outras aplicações. Não se destina a utilização por aplicações normais."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"modificar estado do telefone"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Permite à aplicação controlar as funcionalidades do telefone do dispositivo. Uma aplicação com esta autorização pode alternar entre redes, ligar e desligar o rádio do telefone, etc., sem nunca notificar o utilizador."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"ler identidade e estado do telefone"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Permite à aplicação aceder às funcionalidades do dispositivo. Uma aplicação com esta autorização pode determinar o número deste telefone assim como o número de série do mesmo, se existe uma chamada activa, o número a que a essa chamada está ligada, etc."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"impedir que o telefone entre em inactividade"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Permite a uma aplicação impedir o telefone de entrar em inactividade."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"ligar ou desligar o telefone"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Permite a uma aplicação repor totalmente as definições de fábrica do sistema, apagando todos os dados, configurações e aplicações instaladas."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"definir fuso horário"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Permite a uma aplicação mudar o fuso horário do telefone."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"funciona como Serviço de Gestor de Conta"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Permite que uma aplicação efectue chamadas para Autenticadores de Conta"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"descobrir contas reconhecidas"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Permite a uma aplicação obter a lista de contas reconhecidas pelo telefone."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"funciona como autenticador de conta"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Permite que uma aplicação utilize as funcionalidades de autenticador de conta do Gestor de Conta, incluindo a criação de contas e a obtenção e definição das respectivas palavras-passe."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"gerir a lista de contas"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Permite que uma aplicação execute operações como adicionar e remover contas e eliminar a respectiva palavra-passe."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"utilizar as credenciais de autenticação de uma conta"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Permitir que uma aplicação solicite tokens de autenticação."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ver estado da rede"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Permite a uma aplicação ver o estado de todas as redes."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"acesso total à Internet"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Permite a uma aplicação ver as informações acerca do estado do Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"alterar estado de Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Permite a uma aplicação ligar e desligar de pontos de acesso de Wi-Fi, bem como efectuar alterações a redes Wi-Fi configuradas."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"permitir recepção Multicast Wi-Fi"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Permite que uma aplicação receba pacotes não enviados directamente para o dispositivo. Esta opção pode ser útil para descobrir serviços oferecidos na vizinhança. Utiliza mais energia do que o modo não multicast."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"administração de Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Permite a uma aplicação configurar o telefone Bluetooth local, bem como descobrir e emparelhar com dispositivos remotos."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"criar ligações Bluetooth"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Permite a uma aplicação ler quaisquer palavras, nomes e expressões privadas que o utilizador possa ter armazenado no dicionário do utilizador."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"escrever no dicionário definido pelo utilizador"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Permite a uma aplicação escrever novas palavras no dicionário do utilizador."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"modificar/eliminar os conteúdos do cartão SD"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Permite que uma aplicação escreva no cartão SD."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Residência"</item>
<item msgid="869923650527136615">"Móvel"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Outro"</item>
<item msgid="2374913952870110618">"Personalizado"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Móvel"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Residência"</item>
<item msgid="5629153956045109251">"Emprego"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Lamentamos, tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"A carregar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Ligue o carregador."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Nenhum cartão SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Nenhum cartão SIM no telefone."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Efectuou incorrectamente o seu padrão de desbloqueio <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Após outras <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telefone utilizando o seu início de sessão no Google."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Tente novamente dentro de <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Esqueceu-se do padrão?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Demasiadas tentativas de efectuar o padrão!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Para desbloquear, inicie sessão com a sua Conta Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome de utilizador (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Palavra-passe"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Iniciar sessão"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nome de utilizador ou palavra-passe inválidos."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em curso"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Ligue o carregador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está a ficar fraca:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"resta menos de <xliff:g id="NUMBER">%d%%</xliff:g>."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"O teste de fábrica falhou"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A acção FACTORY_TEST apenas é suportada para pacotes instalados em /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Navegar para outra página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleccione OK para continuar ou Cancelar para permanecer na página actual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que uma aplicação modifique o histórico e os marcadores do browser armazenados no telefone. As aplicações maliciosas podem utilizar esta permissão para apagar ou modificar os dados do browser."</string>
<string name="save_password_message" msgid="767344687139195790">"Quer que o browser memorize esta palavra-passe?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"amanhã"</item>
<item quantity="other" msgid="2973062968038355991">"daqui a <xliff:g id="COUNT">%d</xliff:g> dias"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"a %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"às %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"em %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dia"</string>
<string name="days" msgid="4774547661021344602">"dias"</string>
<string name="hour" msgid="2126771916426189481">"hora"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Copiar tudo"</string>
<string name="paste" msgid="5629880836805036433">"Colar"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Método de entrada"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Adicionar \"%s\" ao dicionário"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Pouco espaço livre"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"O espaço de armazenamento do telefone está a ficar reduzido."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"A aplicação <xliff:g id="APPLICATION">%1$s</xliff:g> (no processo <xliff:g id="PROCESS">%2$s</xliff:g>) não está a responder."</string>
<string name="anr_process" msgid="1246866008169975783">"O processo <xliff:g id="PROCESS">%1$s</xliff:g> não está a responder."</string>
<string name="force_close" msgid="3653416315450806396">"Forçar fecho"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Relatório"</string>
<string name="wait" msgid="7147118217226317732">"Esperar"</string>
<string name="debug" msgid="9103374629678531849">"Depuração"</string>
<string name="sendText" msgid="5132506121645618310">"Seleccionar uma acção para texto"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Volume da campainha"</string>
<string name="volume_music" msgid="5421651157138628171">"Volume de multimédia"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"A reproduzir através de Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Toque silencioso seleccionado"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume da chamada recebida"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume de chamada recebida em Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volume do alarme"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Antes de desactivar o armazenamento USB, certifique-se de que o desmontou no anfitrião USB. Seleccione \"Desactivar\" para desactivar o armazenamento USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Desactivar"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Cancelar"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Detectámos um problema ao desactivar o armazenamento USB. Verifique para se certificar de que desmontou o anfitrião USB e, em seguida, tente novamente."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatar cartão SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Tem a certeza de que pretende formatar o cartão SD? Perder-se-ão todos os dados no cartão."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatar"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"O telefone está ligado a um computador."</string>
<string name="select_input_method" msgid="2086499663193509436">"Seleccionar método de entrada"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"A preparar cartão SD"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"A verificar a presença de erros."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Cartão SD vazio"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"Cartão SD vazio ou sistema de ficheiros não suportado."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Cartão SD danificado"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"Cartão SD danificado. Poderá ser necessário reformatá-lo."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"Cartão SD removido de forma inesperada"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Desmonte o cartão SD antes de retirá-lo para evitar a perda de dados."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"É seguro retirar o cartão SD"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Pode remover o cartão SD com segurança."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"Cartão SD removido"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"Cartão SD removido. Insira um novo cartão."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Nenhuma actividade correspondente encontrada"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"actualizar estatísticas de utilização de componentes"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Permite a modificação de estatísticas de utilização de componentes recolhidas. Não se destina a utilização por aplicações normais."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Marcar número"\n"utilizando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Criar contacto"\n"utilizando <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"verificado"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não verificado"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Permitir"</string>
+ <string name="deny" msgid="2081879885755434506">"Recusar"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Autorização Solicitada"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a241e36..06bf879 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;sem título&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Nenhum número de telefone)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Bloqueio de chamadas"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Alteração da senha"</string>
<string name="PinMmi" msgid="3113117780361190304">"Alteração do PIN"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Chamando número atual"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Chamando número restrito"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Chamada com três participantes"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Rejeição das chamadas indesejadas"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Chamando número de entrega"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Não perturbe"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"O ID do chamador assume o padrão de restrito. Próxima chamada: Restrita"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"O ID do chamador assume o padrão de restrito. Próxima chamada: Não restrita"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"O ID do chamador assume o padrão de não restrito. Próxima chamada: Restrita"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Sincronizar"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Pacote"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Indicador de roaming ativado"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Indicador de roaming desativado"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Indicador de roaming piscando"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Fora da vizinhança"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Ao ar livre"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Roaming - Sistema recomendado"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming - Sistema disponível"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming - Parceiro do Alliance"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming - Parceiro do Google Premium"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming - Funcionalidade de serviço completo"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming - Funcionalidade de serviço parcial"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Banner de roaming ativado"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Banner de roaming desativado"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Pesquisando serviço"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Código de recurso concluído."</string>
+ <string name="fcError" msgid="3327560126588500777">"Problema de conexão ou código de recurso inválido."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="2567300624552921790">"A página da web contém um erro."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Não foi possível encontrar o URL."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Não foi possível acessar o arquivo."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"O arquivo solicitado não foi encontrado."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Há muitas solicitações sendo processadas. Tente novamente mais tarde."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Erro de login"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Sincronizar"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Sincronizar"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Muitas exclusões de <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Acesso de nível inferior e controle do sistema."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Ferramentas de desenvolvimento"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Recursos necessários apenas para desenvolvedores de aplicativo."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Armazenamento"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"Acessar o cartão SD."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"desativar ou modificar a barra de status"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Permite que o aplicativo desative a barra de status ou adicione e remova ícones do sistema."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandir/recolher barra de status"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Permite que um aplicativo force o fechamento de qualquer atividade que esteja em primeiro plano. Aplicativos normais não devem precisar disso em momento algum."</string>
<string name="permlab_dump" msgid="1681799862438954752">"recuperar o estado interno do sistema"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Permite que um aplicativo recupere o estado interno do sistema. Aplicativos maliciosos podem recuperar uma grande variedade de informações privadas e de segurança que normalmente não precisariam."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"desligamento parcial"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Coloca o gerenciador de atividades em um estado de desligamento. Não executa o desligamento completo."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"evitar trocas de aplicativo"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Impede que o usuário passe para outro aplicativo."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"monitorar e controlar toda inicialização de aplicativo"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Permite que um aplicativo monitore e controle a maneira como o sistema inicia as atividades. Aplicativos maliciosos podem comprometer todo o sistema. Essa permissão é necessária apenas para desenvolvimento, nunca para uso normal do telefone."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"enviar transmissão removida do pacote"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Permite que um aplicativo controle se as atividades são sempre concluídas assim que vão para o segundo plano. Aplicativos normais não precisam disso em momento algum."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"modificar estatísticas da bateria"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Permite a modificação das estatísticas de bateria coletadas. Não deve ser usado por aplicativos normais."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"controlar backup e restauração do sistema"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"exibir janelas não autorizadas"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Permite a criação de janelas destinadas ao uso pela interface de usuário do sistema interno. Não deve ser usado por aplicativos normais."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Permite que os aplicativos vejam as teclas que você pressiona, mesmo quando estiver interagindo com outro aplicativo (como ao digitar uma senha). Aplicativos normais não devem precisar disso em momento algum."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"vincular a um método de entrada"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Permite que o detentor se sujeite à interface de nível superior de um método de entrada. Aplicativos normais não devem precisar disso em momento algum."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"sujeitar-se a um plano de fundo"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Permite que o detentor se sujeite à interface de nível superior de um plano de fundo. Aplicativos normais não devem precisar disso em momento algum."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"alterar orientação da tela"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Aplicativos normais não devem precisar disso em momento algum."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais de Linux para os aplicativos"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"modificar configurações globais do sistema"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Permite que um aplicativo modifique os dados de configuração do sistema. Aplicativos maliciosos podem corromper a configuração do seu sistema."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"modificar configurações do sistema de segurança"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Permite que um aplicativo modifique os dados de configuração de segurança dos sistemas. Não deve ser usado por aplicativos normais."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"modificar o mapa de serviços do Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Permite que um aplicativo modifique o mapa de serviços do Google. Não deve ser usado por aplicativos normais."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"iniciar automaticamente na inicialização"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Cria fontes de locais fictícios para teste. Aplicativos maliciosos podem usar isso para substituir o local e/ou o status retornado pelas fontes de locais reais como GPS ou provedores de rede."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"acessar comandos extras do provedor de localização"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Acessa comandos extras do provedor de localização. Aplicativos maliciosos podem usar isso para interferir na operação do GPS ou de outras fontes de localização."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"autorização para instalar um provedor de localização"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Cria fontes de locais fictícios para teste. Aplicativos maliciosos podem usar isso para substituir o local e/ou o status retornado pelas fontes de locais reais como GPS ou provedores de rede ou para monitorar e informar sua localização para uma fonte externa."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"Localização precisa (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Acessa fontes de localização precisa como o GPS (Global Positioning System) no telefone, onde disponível. Aplicativos maliciosos podem usar isso para determinar onde você está e podem consumir energia adicional da bateria."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"local aproximado (com base na rede)"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"acessar SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Permite que o aplicativo use recursos de nível inferior do SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler o buffer do frame"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Permite que o aplicativo a ser usado leia o conteúdo do buffer de frame."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Permite que o aplicativo leia o conteúdo do buffer de frame."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"alterar as suas configurações de áudio"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Permite que o aplicativo modifique as configurações globais de áudio como volume e roteamento."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"gravar áudio"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Permite que o aplicativo ligue para números de telefones sem a sua intervenção. Aplicativos maliciosos podem causar chamadas inesperadas na conta do seu telefone. Observe que isso não permite que o aplicativo ligue para números de emergência."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"chamar diretamente quaisquer números de telefone"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Permite que o aplicativo ligue para qualquer número de telefone, incluindo números de emergência, sem a sua intervenção. Aplicativos maliciosos podem fazer chamadas desnecessárias e ilegais para serviços de emergência."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"controlar as notificações de atualização do local"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Permite a ativação/desativação de notificações de atualização do local a partir do rádio. Não deve ser usado por aplicativos normais."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"acessar propriedades de verificação"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Permite que o aplicativo informe ao sistema quais widgets podem ser usados por quais aplicativos. Com essa permissão, os aplicativos podem conceder acesso aos dados pessoais a outros aplicativos. Não deve ser usado por aplicativos normais."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"modificar estado do telefone"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Permite que o aplicativo controle os recursos de telefone do dispositivo. Um aplicativo com essa permissão pode alternar redes, ligar e desligar o rádio do telefone e outras ações parecidas sem notificá-lo."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"ler estado e identidade do telefone"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Permite que o aplicativo acesse os recursos de telefone do aparelho. Um aplicativo com essa autorização pode determinar o número e o número de série desse telefone, se uma chamada está ativa, o número ao qual a chamada está conectada e outras informações parecidas."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"impedir modo de inatividade do telefone"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Permite que um aplicativo impeça o telefone de entrar no modo de inatividade."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"ligar ou desligar o telefone"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Permite que um aplicativo redefina completamente o sistema para as configurações de fábrica, apagando todos os dados, configuração e aplicativos instalados."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"definir fuso horário"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Permite que um aplicativo altere o fuso horário do telefone."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"agir como AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Permite que um aplicativo faça chamadas para AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"descobrir contas conhecidas"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Permite que um aplicativo obtenha a lista de contas conhecidas pelo telefone."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"agir como autenticador da conta"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Permite que um aplicativo use os recursos do autenticador de conta do AccountManager, incluindo a criação de contas e a obtenção e definição de senhas."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"gerenciar a lista de contas"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Permite que um aplicativo execute operações como adição e remoção de contas e exclusão de senhas."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"usar as credenciais de autenticação de uma conta"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Permite que um aplicativo solicite tokens de autenticação."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ver estado da rede"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Permite que um aplicativo veja o estado de todas as redes."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"acesso total da internet"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Permite que um aplicativo veja as informações sobre o estado de Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"alterar o estado de Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Permite que um aplicativo se conecte e desconecte dos pontos de acesso Wi-Fi e faça alterações nas redes Wi-Fi configuradas."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"permitir recebimento de multicast Wi-Fi"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Permite que um aplicativo receba pacotes não endereçados diretamente para o seu aparelho. Isso pode ser útil ao detectar os serviços oferecidos nas proximidades. Ele consome mais energia do que o modo não-multicast."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"administração de Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Permite que um aplicativo configure o telefone Bluetooth local, descubra e pareie com dispositivos remotos."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"criar conexões Bluetooth"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Permite que um aplicativo leia quaisquer palavras, nomes e frases particulares armazenados pelo usuário no dicionário do usuário."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"gravar no dicionário definido pelo usuário"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Permite que um aplicativo grave novas palavras no dicionário do usuário."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"modificar/excluir conteúdo do cartão SD"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Permite que um aplicativo grave no cartão SD."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Página inicial"</item>
<item msgid="869923650527136615">"Celular"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Outros"</item>
<item msgid="2374913952870110618">"Personalizado"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Celular"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Página inicial"</item>
<item msgid="5629153956045109251">"Trabalho"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Tente novamente"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Carregando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Carregado."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Conecte o seu carregador."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Sem cartão SIM."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Não há um cartão SIM no telefone."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Você desenhou o seu padrão de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas incorretas e você receberá uma solicitação para desbloquear o seu telefone usando o seu login do Google."\n\n" Tente novamente em <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Tente novamente em <xliff:g id="NUMBER">%d</xliff:g> segundos."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Esqueceu o padrão?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Muitas tentativas de padrão!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Para desbloquear, faça login com a sua Conta do Google."</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Nome de usuário (e-mail)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Senha"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Fazer login"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Nome de usuário ou senha inválida."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Limpar"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Sem notificações"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Conecte o carregador"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"A bateria está ficando baixa:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"menos de <xliff:g id="NUMBER">%d%%</xliff:g> restantes."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Falha no teste de fábrica"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"A ação FACTORY_TEST é suportada apenas para pacotes instalados em /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Deseja sair desta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecione OK para continuar ou Cancelar para permanecer na página atual."</string>
<string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Permite que um aplicativo modifique o histórico ou os favoritos do Navegador armazenados no seu telefone. Aplicativos maliciosos podem usar isso para apagar ou modificar os dados do seu Navegador."</string>
<string name="save_password_message" msgid="767344687139195790">"Deseja que o navegador lembre desta senha?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"amanhã"</item>
<item quantity="other" msgid="2973062968038355991">"em <xliff:g id="COUNT">%d</xliff:g> dias"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"em %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"às %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"em %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dia"</string>
<string name="days" msgid="4774547661021344602">"dias"</string>
<string name="hour" msgid="2126771916426189481">"hora"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Copiar tudo"</string>
<string name="paste" msgid="5629880836805036433">"Colar"</string>
<string name="copyUrl" msgid="2538211579596067402">"Copiar URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Método de entrada"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Adicionar \"%s\" ao dicionário"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Editar texto"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Pouco espaço"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"O espaço de armazenamento do telefone está ficando baixo."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"O aplicativo <xliff:g id="APPLICATION">%1$s</xliff:g> (no processo <xliff:g id="PROCESS">%2$s</xliff:g>) não está respondendo."</string>
<string name="anr_process" msgid="1246866008169975783">"O processo <xliff:g id="PROCESS">%1$s</xliff:g> não está respondendo."</string>
<string name="force_close" msgid="3653416315450806396">"Forçar fechamento"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Informar"</string>
<string name="wait" msgid="7147118217226317732">"Aguardar"</string>
<string name="debug" msgid="9103374629678531849">"Depurar"</string>
<string name="sendText" msgid="5132506121645618310">"Selecione uma ação para o texto"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Volume da campainha"</string>
<string name="volume_music" msgid="5421651157138628171">"Volume da mídia"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Reproduzindo por meio de Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Toque silencioso selecionado"</string>
<string name="volume_call" msgid="3941680041282788711">"Volume na chamada"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volume de chamada Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volume do alarme"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Antes de desativar o armazenamento USB, desmonte o host USB. Selecione \"Desativar\" para desativar o armazenamento USB."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Desativar"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Cancelar"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Encontramos um problema ao desativar o armazenamento USB. Verifique se desmontou o host USB e tente novamente."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatar cartão SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Tem certeza de que deseja formatar o cartão SD? Todos os dados no seu cartão serão perdidos."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Formatar"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Um computador está conectado ao seu telefone."</string>
<string name="select_input_method" msgid="2086499663193509436">"Selecionar método de entrada"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"candidatos"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"Preparando o cartão SD"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Procurando erros."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Cartão SD em branco"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"Cartão SD vazio ou com sistema de arquivos incompatível."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Cartão SD danificado"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"O cartão SD está danificado. Talvez seja necessário reformatá-lo."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"Cartão SD removido inesperadamente."</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Desmonte o cartão SD antes da remoção para evitar a perda de dados."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"O cartão SD já pode ser removido com segurança."</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Você pode remover o cartão SD com segurança."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"Cartão SD removido"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"Cartão SD removido. Insira um novo."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Nenhum atividade correspondente foi encontrada"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"atualizar estatísticas de uso do componente"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Permite a modificação das estatísticas de uso do componente coletadas. Não deve ser usado por aplicativos normais."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Executar"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Discar número"\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Criar contato "\n"usando <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"selecionado"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"não selecionado"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Permitir"</string>
+ <string name="deny" msgid="2081879885755434506">"Negar"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Autorização solicitada"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 8613540..24d1573 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"ГБ"</string>
<string name="terabyteShort" msgid="231613018159186962">"TБ"</string>
<string name="petabyteShort" msgid="5637816680144990219">"ПБ"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;без названиÑ&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Ðет номера телефона)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Запрет вызовов"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Смена паролÑ"</string>
<string name="PinMmi" msgid="3113117780361190304">"Смена PIN"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Вызывающий номер приÑутÑтвует"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Вызывающий номер запрещен"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Вызов Ñ ÑƒÑ‡Ð°Ñтием трех абонентов"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Отклонение нежелательных звонков"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"ДоÑтавка номера вызывающего абонента"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Ðе беÑпокоить"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Ð˜Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð°Ð±Ð¾Ð½ÐµÐ½Ñ‚Ð° по умолчанию запрещена. След. вызов: запрещена"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Ð˜Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð°Ð±Ð¾Ð½ÐµÐ½Ñ‚Ð° по умолчанию запрещена. След. вызов: разрешена"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Ð˜Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð°Ð±Ð¾Ð½ÐµÐ½Ñ‚Ð° по умолчанию не запрещена. След. вызов: запрещена"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Синхр."</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Пакет"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Индикатор роуминга включен"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Индикатор роуминга отключен"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Индикатор роуминга мигает"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"За пределами домашней Ñети"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Ðа улице"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Роуминг – Ð¿Ñ€ÐµÐ´Ð¿Ð¾Ñ‡Ñ‚Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ ÑиÑтема"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Роуминг – доÑÑ‚ÑƒÐ¿Ð½Ð°Ñ ÑиÑтема"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Роуминг – аÑÑоциированный партнер"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Роуминг – оÑновной партнер"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Роуминг – Ð¿Ð¾Ð»Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¾Ð½Ð°Ð»ÑŒÐ½Ð¾ÑÑ‚ÑŒ Ñлужбы"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Роуминг – чаÑÑ‚Ð¸Ñ‡Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ð¾Ð½Ð°Ð»ÑŒÐ½Ð¾ÑÑ‚ÑŒ Ñлужбы"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Баннер роуминга включен"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Баннер роуминга выключен"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"ПоиÑк Ñлужбы"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переадреÑовано"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> через <xliff:g id="TIME_DELAY">{2}</xliff:g> Ñ."</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ðе переадреÑовано"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ðе переадреÑовано"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Код функции выполнен."</string>
+ <string name="fcError" msgid="3327560126588500777">"Ðеполадки Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ неверный код функции."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"ОК"</string>
<string name="httpError" msgid="2567300624552921790">"Ошибка на веб-Ñтранице."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Ðе удалоÑÑŒ найти URL."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Ðе удаетÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к файлу."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Ðе удалоÑÑŒ найти указанные файлы."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"ОбрабатываетÑÑ Ñлишком много запроÑов. Повторите попытку позднее."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Ошибка входа"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Синхр."</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Синхр."</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Слишком много удалений <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"ДоÑтуп нижнего ÑƒÑ€Ð¾Ð²Ð½Ñ Ð¸ управление ÑиÑтемой."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"ИнÑтрументы разработки"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Функции, необходимые только разработчикам приложений."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"ПамÑÑ‚ÑŒ"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"ДоÑтуп к SD-карте."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменÑÑ‚ÑŒ Ñтроку ÑоÑтоÑниÑ"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"ПозволÑет приложению отключать Ñтроку ÑоÑтоÑÐ½Ð¸Ñ Ð¸Ð»Ð¸ добавлÑÑ‚ÑŒ/удалÑÑ‚ÑŒ ÑиÑтемные значки."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/Ñворачивать Ñтроку ÑоÑтоÑниÑ"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"ПозволÑет приложению принудительно закрыть или вернуть в иÑходное ÑоÑтоÑние процеÑÑÑ‹, выполнÑемые в активном режиме. Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
<string name="permlab_dump" msgid="1681799862438954752">"извлекать данные о внутреннем ÑоÑтоÑнии ÑиÑтемы"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"ПозволÑет приложению извлекать внутренние ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ ÑоÑтоÑнии ÑиÑтемы. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñмогут извлечь разнообразные личные и защищенные ÑведениÑ, в которых обычно нет необходимоÑти."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"чаÑтичное завершение работы"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Завершает работу диÑпетчера активноÑти. Ðе выполнÑет полное завершение работы."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"запретить переключение приложений"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Запрещает пользователÑм переключатьÑÑ Ð¼ÐµÐ¶Ð´Ñƒ приложениÑми."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"отÑлеживать и управлÑÑ‚ÑŒ запуÑком вÑех приложений"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"ПозволÑет приложению отÑлеживать и управлÑÑ‚ÑŒ ÑпоÑобом Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÑиÑтемы. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ разглаÑить конфиденциальную информацию о ÑиÑтеме. Это разрешение необходимо только при разработке, но не при обычной работе Ñ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð¾Ð¼."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"отправлÑÑ‚ÑŒ раÑÑылку об удалении пакета"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"ПозволÑет приложению контролировать, были ли дейÑÑ‚Ð²Ð¸Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ñ‹ Ñразу же поÑле их перехода в фоновый режим. Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"изменÑÑ‚ÑŒ ÑтатиÑтику батареи"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"ПозволÑет изменÑÑ‚ÑŒ Ñобранную ÑтатиÑтику батареи. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"управление резервным копированием и воÑÑтановлением ÑиÑтемы"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"показывать неавторизованные окна"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Разрешает Ñоздание окон, предназначенных Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½Ð¸Ð¼ пользовательÑким интерфейÑом ÑиÑтемы. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"ПозволÑет приложению раÑпознавать нажатые пользователем клавиши даже при работе Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ приложением (например, при вводе паролÑ). Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"ÑвÑзывать Ñ Ð¼ÐµÑ‚Ð¾Ð´Ð¾Ð¼ ввода"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"ПозволÑет выполнÑÑ‚ÑŒ привÑзку к интерфейÑу ввода верхнего уровнÑ. Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"ÑвÑзать Ñ Ñ„Ð¾Ð½Ð¾Ð²Ñ‹Ð¼ риÑунком"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Разрешает выполнÑÑ‚ÑŒ привÑзку к интерфейÑу фонового риÑунка верхнего уровнÑ. Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"изменÑÑ‚ÑŒ ориентацию Ñкрана"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"ПозволÑет приложению изменÑÑ‚ÑŒ ориентацию Ñкрана в любое времÑ. Ðе требуетÑÑ Ð´Ð»Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ñ… приложений."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"отправлÑÑ‚ÑŒ приложениÑм Ñигналы Linux"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"изменÑÑ‚ÑŒ общие наÑтройки ÑиÑтемы"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"ПозволÑет приложению изменÑÑ‚ÑŒ данные наÑтроек ÑиÑтемы. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ повредить конфигурацию ÑиÑтемы."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"изменÑÑ‚ÑŒ наÑтройки ÑиÑтемы безопаÑноÑти"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"ПозволÑет приложению изменÑÑ‚ÑŒ данные наÑтроек безопаÑноÑти ÑиÑтемы. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"изменÑÑ‚ÑŒ карту Ñлужб Google"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"ПозволÑет приложению изменÑÑ‚ÑŒ карту Ñлужб Google. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"автоматичеÑки запуÑкать при загрузке"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Создавать копии иÑточников данных о меÑтоположении Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐºÐ¸. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользовать Ñту возможноÑÑ‚ÑŒ Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ·Ð°Ð¿Ð¸Ñи меÑта и/или ÑоÑтоÑниÑ, возвращаемого дейÑтвительными иÑточниками данных о меÑтоположении, такими как GPS или операторы ÑвÑзи."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"получать доÑтуп к дополнительным командам иÑточника данных о меÑтоположении"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Получать доÑтуп к дополнительным командам поÑтавщика данных о меÑтоположении. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользовать Ñту возможноÑÑ‚ÑŒ Ð´Ð»Ñ Ð²Ð¼ÐµÑˆÐ°Ñ‚ÐµÐ»ÑŒÑтва в работу GPS или других иÑточников меÑта."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"разрешение на уÑтановку поÑтавщика меÑтоположениÑ"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Создайте фиктивные иÑточники меÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ‚ÐµÑтированиÑ. ВредоноÑное ПО может иÑпользовать их Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¼ÐµÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸/или ÑтатуÑа, возвращаемого дейÑтвительными иÑточниками меÑтоположениÑ, такими как GPS или Ñетевые провайдеры, а также отÑлеживать ваше положение и передавать внешним иÑточникам."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"точное меÑтоположение (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Получать доÑтуп к иÑточникам точного меÑтоположениÑ, таким как GPS, еÑли возможно. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользовать Ñто разрешение Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ меÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸ раÑходовать реÑÑƒÑ€Ñ Ð±Ð°Ñ‚Ð°Ñ€ÐµÐ¸."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"отÑлеживать меÑтоположение по Ñигналам Ñети"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"получать доÑтуп к SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"ПозволÑет приложению иÑпользовать функции SurfaceFlinger нижнего уровнÑ."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Ñчитывать буфер фреймов"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"ПозволÑет приложению иÑпользовать функцию Ñ‡Ñ‚ÐµÐ½Ð¸Ñ Ñодержимого буфера фреймов."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Разрешает приложению Ñчитывать Ñодержание буфера фреймов."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"изменÑÑ‚ÑŒ наÑтройки аудио"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"ПозволÑет приложению изменÑÑ‚ÑŒ глобальные аудионаÑтройки, такие как громкоÑÑ‚ÑŒ и маршрутизацию."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"запиÑывать аудио"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"ПозволÑет приложению вызывать телефонные номера без вмешательÑтва пользователÑ. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ оÑущеÑтвлÑÑ‚ÑŒ нежелательные вызовы. Это разрешение не позволÑет приложению Ñовершать вызовы Ñлужб ÑкÑтренной помощи."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"поÑылать прÑмые вызовы на любые номера"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"ПозволÑет приложению оÑущеÑтвлÑÑ‚ÑŒ вызов любого номера, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ð½Ð¾Ð¼ÐµÑ€Ð° ÑкÑтренной помощи, без вмешательÑтва пользователÑ. ВредоноÑные Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ оÑущеÑтвить нежелательные или незаконные вызовы Ñлужб ÑкÑтренной помощи."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"управлÑÑ‚ÑŒ уведомлениÑми об обновлении меÑтоположениÑ"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"ПозволÑет включать/отключать отправку уведомлений об обновлениÑÑ… меÑÑ‚Ð¾Ð¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¿Ð¾ радиоÑвÑзи. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"получать доÑтуп к ÑвойÑтвам региÑтрации"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"ПозволÑет приложению Ñообщить ÑиÑтеме, какие Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¼Ð¾Ð³ÑƒÑ‚ иÑпользовать какие виджеты. Это разрешение позволÑет приложениÑм предоÑтавлÑÑ‚ÑŒ другим приложениÑм доÑтуп к личной информации. Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"изменÑÑ‚ÑŒ ÑоÑтоÑние телефона"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"ПозволÑет приложению управлÑÑ‚ÑŒ функциÑми телефона в уÑтройÑтве. Приложение, обладающее Ñтим разрешением, может переключать Ñети, включать и выключать радио на телефоне и выполнÑÑ‚ÑŒ другие подобные дейÑÑ‚Ð²Ð¸Ñ Ð±ÐµÐ· ÑоответÑтвующего уведомлениÑ."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"Ñчитывать ÑоÑтоÑние и идентификаторы телефона"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Разрешает приложению получать доÑтуп к функциÑм телефона на уÑтройÑтве. Приложение Ñ Ñ‚Ð°ÐºÐ¸Ð¼ разрешением может определить номер телефона и Ñерийный номер уÑтройÑтва, наличие активного вызова, номер вызываемого/вызывающего абонента и Ñ‚.п."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"предотвратить переключение телефона в ÑпÑщий режим"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"ПозволÑет приложению запретить переход телефона в ÑпÑщий режим"</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"включать и выключать питание телефона"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"ПозволÑет приложению воÑÑтановить Ñтандартные наÑтройки ÑиÑтемы, удалив вÑе данные, конфигурацию и уÑтановленные приложениÑ."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"наÑтраивать чаÑовой поÑÑ"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"ПозволÑет приложению изменÑÑ‚ÑŒ чаÑовой поÑÑ Ñ‚ÐµÐ»ÐµÑ„Ð¾Ð½Ð°."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"выÑтупать в качеÑтве Ñлужбы ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð°ÐºÐºÐ°ÑƒÐ½Ñ‚Ð¾Ð¼"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Разрешает приложению вызывать Ñлужбы аутентификации аккаунта."</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"обнаруживать извеÑтные аккаунты"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"ПозволÑет приложению получать ÑпиÑок аккаунтов, извеÑтных телефону."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"выÑтупать в качеÑтве Ñлужбы аутентификации аккаунта"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Разрешает приложению иÑпользовать возможноÑти аутентификации диÑпетчера аккаунта, в том чиÑле Ñоздавать аккаунты, получать и уÑтанавливать пароли Ð´Ð»Ñ Ð½Ð¸Ñ…."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"управление ÑпиÑком аккаунтов"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Разрешает приложению добавлÑÑ‚ÑŒ и удалÑÑ‚ÑŒ аккаунты и Ñтирать их пароли."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"иÑпользование региÑтрационных данных аккаунта Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Разрешает приложению запрашивать маркеры аутентификации."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"проÑматривать ÑоÑтоÑние Ñети"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"ПозволÑет приложению проÑматривать ÑоÑтоÑние вÑех Ñетей."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"неограниченный доÑтуп в Интернет"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"ПозволÑет приложению проÑматривать ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ ÑоÑтоÑнии Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"изменÑÑ‚ÑŒ ÑоÑтоÑние Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"ПозволÑет приложению подключатьÑÑ Ðº точкам доÑтупа Wi-Fi и отключатьÑÑ Ð¾Ñ‚ них, а также вноÑить Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð² конфигурацию Ñетей Wi-Fi."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"разрешить принимать многоадреÑный Ñигнал Wi-Fi"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Разрешает приложению получать пакеты, не адреÑованные напрÑмую вашему уÑтройÑтву. Это может быть полезно при поиÑке находÑщихÑÑ Ñ€Ñдом Ñлужб. РаÑход зарÑда батареи при Ñтом выше, чем при одноадреÑной передаче."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"управление Bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"ПозволÑет приложению наÑтраивать локальный телефон Bluetooth, обнаруживать и выполнÑÑ‚ÑŒ ÑопрÑжение удаленных уÑтройÑтв."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"Ñоздавать Ð¿Ð¾Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ Bluetooth"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"ПозволÑет приложению Ñчитывать любые Ñлова, имена и фразы личного пользованиÑ, которые могут хранитьÑÑ Ð² пользовательÑком Ñловаре."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"запиÑывать в пользовательÑкий Ñловарь"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"ПозволÑет приложению запиÑывать новые Ñлова в пользовательÑкий Ñловарь."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"изменить/удалить Ñодержание SD-карты"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Разрешает приложению запиÑÑŒ на SD-карту"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Домашний"</item>
<item msgid="869923650527136615">"Мобильный"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Другой"</item>
<item msgid="2374913952870110618">"ОÑобый"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Мобильный"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Домашний"</item>
<item msgid="5629153956045109251">"Рабочий"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Правильно!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Повторите попытку"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Идет зарÑдка (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"ЗарÑжена."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Подключите зарÑдное уÑтройÑтво."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Ðет SIM-карты."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"SIM-карта не уÑтановлена."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"КоличеÑтво неудачных попыток ввода графичеÑкого ключа разблокировки: <xliff:g id="NUMBER_0">%d</xliff:g>. ПоÑле <xliff:g id="NUMBER_1">%d</xliff:g> неудачных попыток вам будет предложено разблокировать телефон Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ учетных данных Google.\n "\n\n" Повторите попытку через <xliff:g id="NUMBER_2">%d</xliff:g> Ñ."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Повторите попытку через <xliff:g id="NUMBER">%d</xliff:g> Ñ."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Забыли графичеÑкий ключ?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Слишком много попыток ввода графичеÑкого ключа!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Ð”Ð»Ñ Ñ€Ð°Ð·Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ войдите Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ñвоего аккаунта Google"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (ÑÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð°)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Пароль"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Вход"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ пароль."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"ОчиÑтить"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ðет уведомлений"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Текущие"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"УведомлениÑ"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Подключите зарÑдное уÑтройÑтво"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Ð‘Ð°Ñ‚Ð°Ñ€ÐµÑ Ñ€Ð°Ð·Ñ€Ñжена:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"оÑталоÑÑŒ менее <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Ðе удалоÑÑŒ провеÑти Ñтандартный теÑÑ‚"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"ДейÑтвие FACTORY_TEST поддерживаетÑÑ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ Ð´Ð»Ñ Ð¿Ð°ÐºÐµÑ‚Ð¾Ð², уÑтановленных в /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Перейти Ñ Ñтой Ñтраницы?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Ðажмите \"ОК\", чтобы продолжить, или \"Отмена\", чтобы оÑтатьÑÑ Ð½Ð° текущей Ñтранице."</string>
<string name="save_password_label" msgid="6860261758665825069">"Подтвердите"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Ñчитывать иÑторию и закладки браузера"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению Ñчитывать вÑе URL, поÑещенные браузером, и вÑе его закладки."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"запиÑывать иÑторию и закладки браузера"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Разрешает приложению изменÑÑ‚ÑŒ иÑторию и закладки браузера, Ñохраненные в вашем телефоне. ВредоноÑное ПО может пользоватьÑÑ Ñтим, чтобы Ñтирать или изменÑÑ‚ÑŒ данные вашего браузера."</string>
<string name="save_password_message" msgid="767344687139195790">"Ð’Ñ‹ хотите, чтобы браузер запомнил Ñтот пароль?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Ðе ÑейчаÑ"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Запомнить"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"завтра"</item>
<item quantity="other" msgid="2973062968038355991">"через <xliff:g id="COUNT">%d</xliff:g> дн."</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"в %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"в %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"через %s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"дн."</string>
<string name="days" msgid="4774547661021344602">"дн."</string>
<string name="hour" msgid="2126771916426189481">"ч."</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Копировать вÑе"</string>
<string name="paste" msgid="5629880836805036433">"Ð’Ñтавить"</string>
<string name="copyUrl" msgid="2538211579596067402">"Копировать URL"</string>
- <string name="inputMethod" msgid="7673923508389094672">"СпоÑоб ввода"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Добавить \"%s\" в Ñловарь"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Изменить текÑÑ‚"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"ÐедоÑтаточно меÑта"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"ЗаканчиваетÑÑ Ð¼ÐµÑто в памÑти телефона."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (в процеÑÑе <xliff:g id="PROCESS">%2$s</xliff:g>) не отвечает."</string>
<string name="anr_process" msgid="1246866008169975783">"ПроцеÑÑ <xliff:g id="PROCESS">%1$s</xliff:g> не отвечает."</string>
<string name="force_close" msgid="3653416315450806396">"Принудительное закрытие"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Отчет"</string>
<string name="wait" msgid="7147118217226317732">"Подождите"</string>
<string name="debug" msgid="9103374629678531849">"Выполнить отладку"</string>
<string name="sendText" msgid="5132506121645618310">"Выберите дейÑтвие Ð´Ð»Ñ Ñ‚ÐµÐºÑта"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"ГромкоÑÑ‚ÑŒ звонка"</string>
<string name="volume_music" msgid="5421651157138628171">"ГромкоÑÑ‚ÑŒ мультимедиа"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"ВоÑпроизведение по каналу Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Выбран режим \"Без звука\""</string>
<string name="volume_call" msgid="3941680041282788711">"ГромкоÑÑ‚ÑŒ входÑщего вызова"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"ГромкоÑÑ‚ÑŒ входÑщего вызова Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"ГромкоÑÑ‚ÑŒ Ñигнала предупреждениÑ"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Перед выключением USB-Ð½Ð°ÐºÐ¾Ð¿Ð¸Ñ‚ÐµÐ»Ñ Ð¾Ð±Ñзательно отключите USB-хоÑÑ‚. Выберите \"Выключить\", чтобы выключить USB-накопитель."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Выключить"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Отмена"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"При выключении USB-Ð½Ð°ÐºÐ¾Ð¿Ð¸Ñ‚ÐµÐ»Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° неполадка. УбедитеÑÑŒ, что USB-хоÑÑ‚ отключен, и повторите попытку."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Форматировать карту SD"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Отформатировать карту SD? Ð’Ñе данные, находÑщиеÑÑ Ð½Ð° карте, будут уничтожены."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Формат"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка USB подключена"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Компьютер подключен к телефону."</string>
<string name="select_input_method" msgid="2086499663193509436">"Выберите ÑпоÑоб ввода"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"варианты"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"Подготовка карты SD"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Проверка ошибок."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"ПуÑÑ‚Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° SD"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-карта пуÑта или иÑпользует неподдерживаемую файловую ÑиÑтему"</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"ÐŸÐ¾Ð²Ñ€ÐµÐ¶Ð´ÐµÐ½Ð½Ð°Ñ ÐºÐ°Ñ€Ñ‚Ð° SD"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD-карта повреждена. Попробуйте отформатировать ее."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"Карта SD неожиданно извлечена"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Перед извлечением карты SD отключите ее во избежание потери данных."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"БезопаÑное удаление карты SD"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Можно безопаÑно извлечь SD-карту."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"ОтÑутÑтвует карта SD"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD-карта извлечена. Ð’Ñтавьте новую карту."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"ПодходÑщих дейÑтвий не найдено"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"обновлÑÑ‚ÑŒ ÑтатиÑтику иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚Ð¾Ð²"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"ПозволÑет изменÑÑ‚ÑŒ Ñобранную ÑтатиÑтику иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð½ÐµÐ½Ñ‚Ð¾Ð². Ðе предназначено Ð´Ð»Ñ Ð¸ÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¾Ð±Ñ‹Ñ‡Ð½Ñ‹Ð¼Ð¸ приложениÑми."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Выполнить"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Ðабрать номер"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Создать контакт"\n"Ñ Ð½Ð¾Ð¼ÐµÑ€Ð¾Ð¼ <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"отмечено"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"не проверено"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Разрешить"</string>
+ <string name="deny" msgid="2081879885755434506">"Отклонить"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Разрешение запрошено"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 611bfaa..cfe5ef9 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;utan titel&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Inget telefonnummer)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Samtalsspärr"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Byt lösenord"</string>
<string name="PinMmi" msgid="3113117780361190304">"Byt PIN-kod"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Nummer för inkommande samtal tillgängligt"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Begränsad åtkomst till nummer för inkommande samtal"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Trepartssamtal"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"Avvisande av oönskade irriterande samtal"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Leverans av nummer för inkommande samtal"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Stör ej"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Nummerpresentatören är begränsad som standard. Nästa samtal: Begränsad"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Nummerpresentatörens standardinställning är begränsad. Nästa samtal: Inte begränsad"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Nummerpresentatörens standardinställning är inte begränsad. Nästa samtal: Begränsad"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Synkronisera"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Paket"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Indikator för roaming på"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Indikator för roaming av"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Blinkande roamingindikator"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Utanför kvarteret"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Utanför huset"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Roaming – föredraget system"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Roaming - tillgängligt system"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Roaming – alllianspartner"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Roaming – premiumpartner"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Roaming – full funktionalitet för tjänst"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Roaming – delvis funktionalitet för tjänst"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Roamingbanner på"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Roamingbanner av"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Söker efter tjänst"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Vidarebefordras inte"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Funktionskoden är fullständig."</string>
+ <string name="fcError" msgid="3327560126588500777">"Anslutningsproblem eller ogiltig funktionskod."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"OK"</string>
<string name="httpError" msgid="2567300624552921790">"Webbsidan innehåller ett fel."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"Webbadressen kunde inte hittas."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Det gick inte att komma åt filen."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"Den begärda filen hittades inte."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"För många begäranden bearbetas. Försök igen senare."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Fel vid inloggningen"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Synkronisera"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Synkronisera"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"För många <xliff:g id="CONTENT_TYPE">%s</xliff:g>-borttagningar."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Åtkomst och kontroll av systemet på lägre nivå."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Utvecklingsverktyg"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Funktioner som endast behövs för programutvecklare."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Lagring"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"Få åtkomst till SD-kortet."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"inaktivera eller ändra statusfält"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Tillåter att programmet inaktiverar statusfältet eller lägger till och tar bort systemikoner."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"expandera/komprimera statusfält"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Tillåter att ett program tvingar en aktivitet som finns i förgrunden att avsluta och gå tillbaka. Behövs inte för vanliga program."</string>
<string name="permlab_dump" msgid="1681799862438954752">"hämta systemets interna status"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Tillåter att ett program hämtar systemets interna status. Skadliga program kan hämta privat och skyddad information som de normalt aldrig ska behöva."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"avsluta delvis"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Sätter aktivitetshanteraren i avstängningsläge. Utför inte en fullständig avstängning."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"förhindrar programbyten"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Hindrar att användaren byter till ett annat program."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"övervaka och styra alla program som öppnas"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Tillåter att ett program övervakar och styr hur systemet startar aktiviteter. Skadliga program kan bryta systemet helt. Den här behörigheten behövs bara för programmering, aldrig för vanlig telefonanvändning."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"skicka meddelande om borttaget paket"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Tillåter att ett program bestämmer om aktiviteter alltid är slutförda när de hamnar i bakgrunden. Ska inte behövas för vanliga program."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"ändra batteristatistik"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Tillåter att samlad batteristatistik ändras. Används inte av vanliga program."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"kontrollera säkerhetskopiering och återställning av systemet"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"visa otillåtna fönster"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Tillåter att fönster skapas och används av det interna systemgränssnittet. Används inte av vanliga program."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Tillåter att program övervakar knapparna som du trycker på, till och med när du använder andra program (till exempel när du anger ett lösenord). Ska inte behövas för vanliga program."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"binda till en metod för indata"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en inmatningsmetod. Ska inte behövas för vanliga program."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"binda till en bakgrund"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en bakgrund. Ska inte behövas för vanliga program."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ändra bildskärmens rikting"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillåter att ett program när som helst ändrar skärmens rotering. Behövs inte för vanliga program."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"skicka Linux-signaler till program"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"ändra globala systeminställningar"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Tillåter att ett program ändrar systemets inställningar. Skadliga program kan skada systemets konfiguration."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"ändra skyddade systeminställningar"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Tillåter att ett program ändrar systemets data för skyddade inställningar. Används inte av vanliga program."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"ändra kartan för Googles tjänster"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Tillåter att ett program ändrar kartan för Google-tjänster. Används inte av vanliga program."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"starta automatiskt vid systemstart"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Skapa skenplatser för att testa. Skadliga program kan använda detta för att åsidosätta platsen och/eller statusen som returneras av riktiga platser, till exempel GPS- eller nätverksleverantörer."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"få åtkomst till extra kommandon för platsleverantör"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Få åtkomst till extra kommandon för platsleverantörer. Skadliga program kan använda detta för att störa hur GPS eller andra platskällor fungerar."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"behörighet att installera en platsleverantör"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Skapa skenplatser för att testa. Skadliga program kan använda detta för att åsidosätta platsen och/eller statusen som returneras av riktiga platser, till exempel GPS- eller nätverksleverantörer, eller övervaka och rapportera din plats till en extern källa."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"hitta plats (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Få åtkomst till detaljerade platskällor som Global Positioning System på telefonen, om det är tillgängligt. Skadliga program kan använda detta för att identifiera var du befinner dig, vilket drar mycket batteri."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"grov (nätverksbaserad) plats"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"få åtkomst till SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Tillåter att program använder lågnivåfunktioner i SurfaceFlinger."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"läsa rambuffert"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Tillåter att program använder innehållet i rambufferten."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Tillåter att program läser innehållet i rambufferten."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ändra dina ljudinställningar"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Tillåter att ett program ändrar globala ljudinställningar, till exempel volym och routning."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"spela in ljud"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Tillåter att programmet ringer telefonnummer utan åtgärd från dig. Skadliga program kan orsaka oväntade samtal på din telefonräkning. Observera att programmet inte tillåts att ringa nödsamtal."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"ringa telefonnummer direkt"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Tillåter att programmet ringer ett telefonnummer, inklusive nödnummer, utan att du behöver göra något. Skadliga program kan ringa onödiga och olagliga samtal till räddningtjänsten."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"styra meddelanden för platsuppdatering"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Tillåter aktivering och inaktivering av avisering om platsuppdatering i radion. Används inte av vanliga program."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"få åtkomst till incheckningsegenskaper"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Tillåter att programmet instruerar systemet vilka widgetar som kan användas av vilket program. Med den här behörigheten kan åtkomst till personliga data beviljas andra program. Används inte av vanliga program."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"ändra telefonstatus"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Tillåter att programmet styr enhetens telefonfunktioner. Ett program med denna behörighet kan växla nätverk, aktivera och inaktivera telefonens radio och så vidare utan att ens meddela dig."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"läsa telefonstatus och identitet"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Tillåter att programmet kommer åt enhetens telefonfunktioner. Ett program som har den här behörigheten kan identifiera telefonens telefonnummer och serienummer, om ett samtal pågår, numret som samtalet är kopplat till och så vidare."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"förhindra att telefonen sätts i viloläge"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Tillåter att ett program förhindrar att telefonen går in i viloläge."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"sätta på eller stänga av telefonen"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Tillåter att ett program helt återställer systemets fabriksinställningar. Alla data, inställningar och installerade program raderas."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"ange tidszon"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Tillåter att ett program ändrar telefonens tidszon."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"fungera som AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Tillåter att ett program anropar AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"upptäcka kända konton"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Tillåter att ett program hämtar en lista över konton som telefonen känner till."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"fungera som kontoautentiserare"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Tillåter att ett program använder kontohanterarens funktioner för AccountManager, inklusive funktioner för att skapa konton och hämta och ange lösenord för dem."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"hantera kontolistan"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Tillåter att ett program utför åtgärder som att lägga till och ta bort konton och ta bort lösenord."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"använda ett kontos autentiseringsuppgifter"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Tillåter att ett program begär autentiseringstokens."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"visa nätverksstatus"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Tillåter att ett program ser status för alla nätverk."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"fullständig Internetåtkomst"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Tillåter att ett program visar information om statusen för Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"byta Wi-Fi-status"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Tillåter att ett program ansluter till och kopplar från Wi-Fi-åtkomstpunkter och gör ändringar i konfigurerade Wi-Fi-nätverk."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"tillåt Wi-Fi multicast-mottagning"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Tillåter att ett program tar emot paket som inte är adresserade direkt till din enhet. Detta är användbart om du vill upptäcka tillgängliga tjänster i närheten. Det drar mer batteri än om telefonen inte är i multicast-läge."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"administrera bluetooth"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Tillåter att ett program konfigurerar den lokala Bluetooth-telefonen samt upptäcker och parkopplar den med fjärranslutna enheter."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"skapa Bluetooth-anslutningar"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Tillåt att ett program läser alla privata ord, namn och fraser som användaren lagrar i sin ordlista."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"skriva till användardefinierad ordlista"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Tillåter att ett program skriver in nya ord i användarordlistan."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"ändra/ta bort innehåll på SD-kortet"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Tillåter att ett program skriver till SD-kortet."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Hem"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"Övrigt"</item>
<item msgid="2374913952870110618">"Anpassad"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Hem"</item>
<item msgid="5629153956045109251">"Arbete"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Försök igen"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Laddar (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Laddad."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Anslut din laddare."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"Inget SIM-kort."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Inget SIM-kort i telefonen."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter <xliff:g id="NUMBER_1">%d</xliff:g> försök till kommer du att uppmanas att låsa upp telefonen med din Google-inloggning."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"Försök igen om <xliff:g id="NUMBER">%d</xliff:g> sekunder."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Glömt ditt grafiska lösenord?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"För många försök med grafiskt lösenord!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Logga in med ditt Google-konto om du vill låsa upp"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Användarnamn (e-post)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Lösenord"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Logga in"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Ogiltigt användarnamn eller lösenord."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Ta bort"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Inga aviseringar"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Pågående"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Meddelanden"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Anslut laddaren"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Batteriet håller på att ta slut:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"Mindre än <xliff:g id="NUMBER">%d%%</xliff:g> återstår."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Det gick fel vid fabrikstestet"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"Åtgärden FACTORY_TEST stöds endast för paket som har installerats i /system/app."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Vill du lämna den här den här sidan?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryck på OK om du vill fortsätta eller på Avbryt om du vill vara kvar på den aktuella sidan."</string>
<string name="save_password_label" msgid="6860261758665825069">"Bekräfta"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Tillåter att ett program ändrar webbläsarhistoriken och bokmärkena i din telefon. Skadliga program kan använda detta för att ta bort eller ändra data i webbläsaren."</string>
<string name="save_password_message" msgid="767344687139195790">"Vill du att webbläsaren ska komma ihåg lösenordet?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Inte nu"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Kom ihåg"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"imorgon"</item>
<item quantity="other" msgid="2973062968038355991">"om <xliff:g id="COUNT">%d</xliff:g> dagar"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"den %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"vid %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"dag"</string>
<string name="days" msgid="4774547661021344602">"dagar"</string>
<string name="hour" msgid="2126771916426189481">"timme"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Kopiera alla"</string>
<string name="paste" msgid="5629880836805036433">"Klistra in"</string>
<string name="copyUrl" msgid="2538211579596067402">"Kopiera webbadress"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Indatametod"</string>
- <string name="addToDictionary" msgid="726256909274177272">"Lägg till %s i ordlistan"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Redigera text"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"DÃ¥ligt med utrymme"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Telefonens lagringsutrymme håller på att ta slut."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"Programmet <xliff:g id="APPLICATION">%1$s</xliff:g> (i processen <xliff:g id="PROCESS">%2$s</xliff:g>) svarar inte."</string>
<string name="anr_process" msgid="1246866008169975783">"Processen <xliff:g id="PROCESS">%1$s</xliff:g> svarar inte."</string>
<string name="force_close" msgid="3653416315450806396">"Tvinga fram en stängning"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Rapportera"</string>
<string name="wait" msgid="7147118217226317732">"Vänta"</string>
<string name="debug" msgid="9103374629678531849">"Felsökning"</string>
<string name="sendText" msgid="5132506121645618310">"Välj en åtgärd för text"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Ringvolym"</string>
<string name="volume_music" msgid="5421651157138628171">"Mediavolym"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Spelar upp genom Bluetooth"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Tyst ringsignal har valts"</string>
<string name="volume_call" msgid="3941680041282788711">"Samtalsvolym"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Samtalsvolym för Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Larmvolym"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"Innan du inaktiverar USB-lagring måste du kontrollera att du har demonterat USB-värden. Välj Inaktivera om du vill inaktivera USB-lagring."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Inaktivera"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Avbryt"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"Ett problem uppstod när vi skulle inaktivera USB-lagringsplatsen. Kontrollera att USB-värden har demonterats och försök igen."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"Formatera SD-kort"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"Vill du formatera SD-kortet? Alla data på ditt kort kommer att gå förlorade."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Format"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"En dator är ansluten till din telefon."</string>
<string name="select_input_method" msgid="2086499663193509436">"Välj indatametod"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"kandidater"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"Förbereder SD-kort"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Söker efter fel."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"Tomt SD-kort"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-kortet är tomt eller så har det ett filsystem som inte stöds."</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Skadat SD-kort"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD-kortet är skadat. Du måste eventuellt formatera om det."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD-kort togs oväntat bort"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Demontera SD-kort innan borttagning för att undvika dataförlust."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"Säkert att ta bort SD-kort"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"Det är nu säkert att ta bort SD-kortet."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"Borttaget SD-kort"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD-kortet har tagits bort. Sätt i ett nytt."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Inga matchande aktiviteter hittades"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"uppdatera statistik över användning av komponenter"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Tillåter att samlad komponentstatistik ändras. Används inte av vanliga program."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Utför"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Slå nummer "\n"med <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"Skapa kontakt"\n"med <xliff:g id="NUMBER">%s</xliff:g>"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"markerad"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"inte markerad"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Tillåt"</string>
+ <string name="deny" msgid="2081879885755434506">"Neka"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Begärd behörighet"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index a8625fd..1f73bab 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g> <xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;başlıksız&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"…"</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(Telefon numarası yok)"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"Çağrı engelleme"</string>
<string name="PwdMmi" msgid="7043715687905254199">"Åžifre deÄŸiÅŸikliÄŸi"</string>
<string name="PinMmi" msgid="3113117780361190304">"PIN kodu deÄŸiÅŸikliÄŸi"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"Arayan numara mevcut"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"Arayan numara engellendi"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"Üç yönlü arama"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"İstenmeyen sinir bozucu aramaların reddi"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"Aranan numara iletimi"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"Rahatsız etmeyin"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"Arayan kimliği varsayılanları kısıtlanmıştır. Sonraki çağrı: Kısıtlanmış"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"Arayan kimliği varsayılanları kısıtlanmıştır. Sonraki çağrı: Kısıtlanmamış"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"Arayan kimliği varsayılanları kısıtlanmamıştır. Sonraki çağrı: Kısıtlanmış"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"Senk."</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"Paket"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"Dolaşım Göstergesi Açık"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"Dolaşım Göstergesi Kapalı"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"Dolaşım Göstergesi Yanıp Sönüyor"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"Semt Dışında"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"Bina Dışında"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"Dolaşım - Tercih Edilen Sistem"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"Dolaşım - Kullanılabilir Sistem"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"Dolaşım - İş Ortağı"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"Dolaşım - Özel Ortak"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"Dolaşım - Tam Hizmet İşlevselliği"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"Dolaşım - Kısmi Hizmet İşlevselliği"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"Dolaşım Başlığı Açık"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"Dolaşım Başlığı Kapalı"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"Hizmet Aranıyor"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> saniye sonra <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"Özellik kodu tamamlandı."</string>
+ <string name="fcError" msgid="3327560126588500777">"Bağlantı sorunu veya geçersiz özellik kodu."</string>
<string name="httpErrorOk" msgid="1191919378083472204">"Tamam"</string>
<string name="httpError" msgid="2567300624552921790">"Web sayfası hata içeriyor."</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"URL bulunamadı."</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"Dosyaya eriÅŸilemedi."</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"İstenen dosya bulunamadı."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Çok fazla sayıda istek işleniyor. Daha sonra yeniden deneyin."</string>
+ <string name="notification_title" msgid="4254304601929719937">"Oturum açma hatası"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"Senk."</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"Senk."</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"Çok fazla <xliff:g id="CONTENT_TYPE">%s</xliff:g> silme var."</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"Sisteme alt düzey erişim ve denetimi."</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"Geliştirme araçları"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"Yalnızca uygulama geliştiriciler için gerekli özellikler."</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"Depolama"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"SD karta eriÅŸin."</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"durum çubuğunu devre dışı bırak veya değiştir"</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"Uygulamanın durum çubuğunu devre dışı bırakmasına veya sistem simgeleri ekleyip kaldırmasına izin verir."</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"durum çubuğunu genişlet/daralt"</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"Uygulamaların, ön plandaki herhangi bir etkinliği kapanmaya ve arka plana geçmeye zorlamasına izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string>
<string name="permlab_dump" msgid="1681799862438954752">"sistemin dahili durumunu al"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"Uygulamanın dahili sistem durumunu almasına izin verir. Kötü amaçlı uygulamalar, normalde gerekli olmaması gereken çok çeşitli özel ve koruma altındaki bilgiyi alabilir."</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"kısmi kapatma"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"Eylem yöneticisini kapalı duruma getirir. Tam kapatma işlemi gerçekleştirmez."</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"uygulama deÄŸiÅŸimlerini engelle"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"Kullanıcının başka bir uygulamaya geçiş yapmasını engeller."</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"tüm uygulama başlatma işlemlerini izle ve denetle"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"Uygulamaların, sistemin etkinlikleri nasıl başlattığını izlemesine ve denetlemesine izin verir. Kötü amaçlı uygulamalar sistemin güvenliğini tamamen tehlikeye atabilir. Bu izin yalnızca program geliştirme amacıyla gereklidir, normal telefon kullanımı için gerekli değildir."</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"paket ile kaldırılan yayını gönder"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"Uygulamaların, etkinliklerin arka planda daima tamamlanıp tamamlanmadığını denetlemesine izin verir. Normal uygulamalar için hiçbir zaman gerekli değildir."</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"pil istatistiklerini deÄŸiÅŸtir"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"Toplanan pil istatistiklerinin değiştirilmesine izin verir. Normal uygulamalarda kullanılmamalıdır."</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"sistem yedeğini kontrol et ve geri yükle"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"yetkisiz pencereleri görüntüle"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"Dahili sistem kullanıcı arayüzü tarafından kullanılmak üzere tasarlanmış pencerelerin oluşturulmasına izin verir. Normal uygulamalarda kullanılmaz."</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"Uygulamaların, başka bir uygulama ile etkileşim halindeyken (örneğin bir şifre girerken) bile bastığınız tuşları izlemesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"bir giriş yöntemine bağla"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"Tutucunun bir giriş yönteminin en üst düzey arayüzüne bağlanmasına izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"bir duvar kağıdına tabi kıl"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"Hesap sahibine bir duvar kağıdının en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"ekran yönünü değiştir"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"Uygulamaların ekran yönünü istedikleri zaman değiştirmesine izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"uygulamalara Linux sinyalleri gönder"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"genel sistem ayarlarını değiştir"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"Uygulamaların sistem ayar verilerini değiştirmesine izin verir. Kötü amaçlı uygulamalar sisteminizin yapılandırmasını bozabilir."</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"güvenli sistem ayarlarını değiştir"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"Uygulamaların sistemin güvenlik ayarları verilerini değiştirmesine izin verir. Normal uygulamalarda kullanılmaz."</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"Google hizmetler haritasını değiştir"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"Uygulamanın, Google hizmetleri haritasını değiştirmesine izin verir. Normal uygulamalarda kullanılmaz."</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"açılışta otomatik başlat"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"Test amacıyla sahte konum kaynakları oluşturur. Kötü amaçlı uygulamalar bu işlevi GPS veya Ağ Hizmeti sağlayıcılar gibi gerçek kaynaklardan gelen konum ve/veya durum bilgilerini geçersiz kılmak için kullanabilir."</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"ek konum sağlayıcı komutlarına eriş"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"Ek konum sağlayıcı komutlarına erişin. Kötü amaçlı uygulamalar bu işlevi GPS veya diğer konum kaynaklarının işleyişine müdahale etmek için kullanabilir."</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"konum sağlayıcı yükleme izni"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"Test için sahte konum kaynakları oluşturun. Kötü amaçlı uygulamalar bunu GPS veya Ağ sağlayıcılarının gerçek konum kaynakları tarafından gönderilen konum ve/veya durumu geçersiz kılmak veya konumunuzu izleyerek bir dış kaynağa bildirmek için kullanabilir."</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"kesinliği yüksek (GPS) konum"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"Bulunduğu yerlerde telefondan Küresel Konumlandırma Sistemi gibi hassas konum bulma kaynaklarına erişin. Kötü amaçlı uygulamalar bu işlevi bulunduğunuz yeri belirlemek için kullanabilir ve ek pil gücü tüketebilir."</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"kesinliği düşük (ağ tabanlı) konum"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"SurfaceFlinger\'a eriÅŸ"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"Uygulamanın SurfaceFlinger alt düzey özelliklerini kullanmasına izin verir."</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"çerçeve arabelleğini oku"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"Kullanılacak uygulamanın çerçeve arabelleğinin içeriğini okumasına izin verir."</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"Uygulamaya çerçeve arabelleğinin içeriğini okuma izni verir."</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ses ayarlarınızı değiştirin"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"Uygulamaların, ses düzeyi ve yönlendirme gibi genel ses ayarlarını değiştirmesine izin verir."</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"ses kaydet"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"Uygulamanın müdahaleniz olmadan telefon numaralarını aramasına izin verir. Kötü amaçlı uygulamalar, telefon faturanızda beklenmedik görüşmeler çıkmasına neden olabilir. Bu işlev, uygulamanın acil numara aramasına izin vermez."</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"herhangi bir telefon numarasını doğrudan ara"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"Uygulamanın, siz müdahale etmeden acil numaralar dahil herhangi bir numarayı aramasına izin verir. Kötü amaçlı uygulamalar acil servisleri gereksiz yere ve yasal olmayan şekilde arayabilir."</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"konum güncelleme bildirimlerini denetle"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"Radyo ile alınan konum güncelleme bildirimlerini etkinleştirmeye/devreden çıkarmaya izin verir. Normal uygulamalarda kullanılmamalıdır."</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"erişim giriş özellikleri"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"Uygulamaların sisteme hangi uygulamalar tarafından hangi widget\'ların kullanılabileceğini söylemesine izin verir. Bu izin sayesinde uygulamalar, başka uygulamalara kişisel verilere erişim verebilir. Normal uygulamalarda kullanılmaz."</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"telefon durumunu deÄŸiÅŸtir"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"Uygulamaların cihazın telefon özelliklerini kullanmasına izin verir. Bu izne sahip bir uygulama, size bildirmeden ağ değiştirebilir ve telefon radyosunu kapatıp açabilir."</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"telefon durumunu ve kimliÄŸini oku"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"Uygulamaların cihazın telefon özelliklerine erişmesine izin verir. Bu izne sahip bir uygulama telefonun telefon numarasını, bu telefonun seri numarasını, o anda bir çağrı sürmekte olup olmadığını, çağrının bağlanmış olduğu numarayı ve benzerini belirleyebilir."</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"telefonunun uykuya geçmesini önle"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"Uygulamaların telefonun uykuya geçmesini önlemesine izin verir."</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"telefonu aç veya kapat"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"Uygulamanın, sistemi tamamen fabrika ayarlarına sıfırlamasına; verileri, yapılandırmayı ve yüklü uygulamaları silmesine izin verir."</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"saat dilimini ayarla"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"Uygulamaların telefonun saat dilimini değiştirmesine izin verir."</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"Hesap Yönetici Hizmeti gibi davran"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"Bir uygulamaya Hesap Doğrulayıcılarını arama izni verir"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"bilinen hesapları keşfet"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"Uygulamaların telefonda bilinen hesapların listesini almasına izin verir."</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"hesap denetleyicisi gibi davran"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"Bir uygulamaya, hesaplar oluşturma ve bunların şifrelerini alma ve ayarlama da dahil olmak üzere Hesap Yöneticisi\'nin hesap doğrulama yetkilerini kullanma izni verir."</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"hesaplar listesini yönet"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"Bir uygulamaya hesapları ekleme, kaldırma ve hesapların şifrelerini silme gibi işlemleri yapma izni verir."</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"bir hesabın kimlik doğrulama bilgilerini kullan"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"Bir uygulamaya kimlik doÄŸrulama bilgilerini isteme izni verir."</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"ağ durumunu görüntüle"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"Uygulamaların, tüm ağların durumunu görmesine izin verir."</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"tam Ä°nternet eriÅŸimi"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"Uygulamaların, kablosuz bağlantının durumu ile ilgili bilgileri görüntülemesine izin verir."</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"Kablosuz durumunu deÄŸiÅŸtir"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"Uygulamaların kablosuz erişim noktalarına bağlanıp bunlarla bağlantısını kesmesine ve yapılandırılmış kablosuz ağlarda değişiklikler yapmasına izin verir."</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"Kablosuz Çoklu Yayın alımına izin ver"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"Bir uygulamaya doğrudan cihazınıza yönlendirilmemiş paketleri alma izni verir. Yakın yerlerde sunulan hizmetlerin keşfedilmesi sırasında faydalı olabilir. Birden fazla noktaya yayın yapmayan moda göre daha fazla güç harcar."</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"bluetooth yönetimi"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"Uygulamaların yerel Bluetooth telefonunu yapılandırmasına ve uzak cihazları keşfedip bunlar ile eşleşmesine izin verir."</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"Bluetooth bağlantıları oluştur"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"Kullanıcının kullanıcı sözlüğünde depolamış olabileceği kişisel kelimeleri, adları ve kelime öbeklerini uygulamaların okumasına izin verir."</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"kullanıcı tanımlı sözlüğe yaz"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"Uygulamaların kullanıcı sözlüğüne yeni kelimeler yazmasına izin verir."</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"SD kart içeriklerini değiştir/sil"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"Bir uygulamaya SD karta yazma izni verir."</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"Ev"</item>
<item msgid="869923650527136615">"Mobil"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"DiÄŸer"</item>
<item msgid="2374913952870110618">"Özel"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"Mobil"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"Ev"</item>
<item msgid="5629153956045109251">"Ä°ÅŸ"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"DoÄŸru!"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"Üzgünüz, lütfen yeniden deneyin"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"Åžarj oluyor (<xliff:g id="PERCENT">%%</xliff:g><xliff:g id="NUMBER">%d</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"Åžarj oldu."</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"Şarj cihazınızı bağlayın."</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"SIM kart yok."</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"Telefonda SIM kart yok."</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu Google oturum açma bilgilerinizi kullanarak açmanız istenir."\n\n" Lütfen <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde yeniden deneyin."</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> saniye içinde yeniden deneyin."</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"Deseni unuttunuz mu?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"Çok fazla sayıda desen denemesi yapıldı!"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"Kilidi açmak için Google hesabınızla oturum açın"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"Kullanıcı adı (e-posta)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"Åžifre"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"Oturum aç"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"Geçersiz kullanıcı adı veya şifre."</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g> <xliff:g id="AMPM">%p</xliff:g>"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"Temizle"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Bildirim yok"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sürüyor"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Bildirimler"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"Lütfen şarj cihazını takın"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"Pil tükeniyor:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"<xliff:g id="NUMBER">%d%%</xliff:g> adetten daha az kaldı."</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"Fabrika testi yapılamadı"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"FACTORY_TEST işlemi yalnızca /system/app dizinine yüklenmiş paketler için desteklenir."</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"Bu sayfadan ayrılıyor musunuz?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Devam etmek için Tamam\'ı, sayfada kalmak için İptal\'i tıklatın."</string>
<string name="save_password_label" msgid="6860261758665825069">"Onayla"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"Uygulamaya telefonunuzda depolanan Tarayıcı geçmişini veya favorileri değiştirme izni verir. Kötü amaçlı uygulamalar bunu Tarayıcı verilerinizi silmek veya değiştirmek için kullanabilir."</string>
<string name="save_password_message" msgid="767344687139195790">"Tarayıcının bu şifreyi anımsamasını istiyor musunuz?"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"Åžimdi deÄŸil"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Anımsa"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"yarın"</item>
<item quantity="other" msgid="2973062968038355991">"<xliff:g id="COUNT">%d</xliff:g> gün içinde"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s üzerinde"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"saat: %s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%s içinde"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"gün"</string>
<string name="days" msgid="4774547661021344602">"gün"</string>
<string name="hour" msgid="2126771916426189481">"saat"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"Tümünü kopyala"</string>
<string name="paste" msgid="5629880836805036433">"Yapıştır"</string>
<string name="copyUrl" msgid="2538211579596067402">"URL\'yi kopyala"</string>
- <string name="inputMethod" msgid="7673923508389094672">"Giriş Yöntemi"</string>
- <string name="addToDictionary" msgid="726256909274177272">"\"%s\" kelimesini sözlüğe ekle"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"Metin düzenle"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"Yer az"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"Telefonun depolama alanı azalıyor."</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işleminde) yanıt vermiyor."</string>
<string name="anr_process" msgid="1246866008169975783">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi yanıt vermiyor."</string>
<string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"Rapor"</string>
<string name="wait" msgid="7147118217226317732">"Bekle"</string>
<string name="debug" msgid="9103374629678531849">"Hata ayıkla"</string>
<string name="sendText" msgid="5132506121645618310">"Metin için bir işlem seçin"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"Zil sesi düzeyi"</string>
<string name="volume_music" msgid="5421651157138628171">"Medya ses düzeyi"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"Bluetooth üzerinden çalıyor"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"Sessiz zil sesi seçildi"</string>
<string name="volume_call" msgid="3941680041282788711">"Gelen çağrı ses düzeyi"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Bluetooth gelen çağrı ses düzeyi"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Alarm ses düzeyi"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"USB depolama birimini kapatmadan önce USB ana makinesinde bağlantıyı kestiğinizden emin olun. USB depolama birimini kapatmak için \"Kapat\"ı seçin."</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"Kapat"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"Ä°ptal"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"USB depolama birimini kapatırken bir sorunla karşılaştık. USB ana makinesinde bağlantıyı kestiğinizden emin olun, sonra yeniden deneyin."</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"SD kartı biçimlendir"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"SD kartı biçimlendirmek istediğinizden emin misiniz? Kartınızdaki tüm veriler yok olacak."</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"Biçimlendir"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"Telefonunuza bir bilgisayar bağlanmış."</string>
<string name="select_input_method" msgid="2086499663193509436">"Giriş Yöntemini Seç"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"adaylar"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"SD kart hazırlanıyor"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Hatalar denetleniyor."</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"BoÅŸ SD kart"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD kart boş veya desteklenmeyen dosya sistemi içeriyor"</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Hasarlı SD kart"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD kart zarar gördü. Yeniden biçimlendirmeniz gerekebilir."</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD kart beklenmedik biçimde çıkarıldı"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"Veri kaybından kaçınmak için SD kartı çıkarmadan önce bağlantısını kesin."</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD kart güvenle çıkarılabilir"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"SD kartı güvenle kaldırabilirsiniz."</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"SD kart çıkarılmış"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD kart çıkarıldı. Yeni bir SD kart takın."</string>
<string name="activity_list_empty" msgid="4168820609403385789">"Eşleşen hiçbir etkinlik bulunamadı"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"bileşen kullanım istatistiklerini güncelle"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"Toplanmış bileşen istatistiklerinin değiştirilmesine izin verir. Normal uygulamalarda kullanılmamalıdır."</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"Çalıştır"</string>
<string name="dial_number_using" msgid="5789176425167573586">"Numarayı çevir:"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"<xliff:g id="NUMBER">%s</xliff:g>"\n" ile kiÅŸi oluÅŸtur"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"seçildi"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"seçilmedi"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"Ä°zin Ver"</string>
+ <string name="deny" msgid="2081879885755434506">"Reddet"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"Ä°zin Ä°stendi"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c7eb522..86807dd 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -21,8 +21,7 @@
<string name="gigabyteShort" msgid="3259882455212193214">"GB"</string>
<string name="terabyteShort" msgid="231613018159186962">"TB"</string>
<string name="petabyteShort" msgid="5637816680144990219">"PB"</string>
- <!-- no translation found for fileSizeSuffix (7670819340156489359) -->
- <skip />
+ <string name="fileSizeSuffix" msgid="7670819340156489359">"<xliff:g id="NUMBER">%1$s</xliff:g><xliff:g id="UNIT">%2$s</xliff:g>"</string>
<string name="untitled" msgid="6071602020171759109">"&lt;无标题&gt;"</string>
<string name="ellipsis" msgid="7899829516048813237">"..."</string>
<string name="emptyPhoneNumber" msgid="7694063042079676517">"(无电è¯å·ç ï¼‰"</string>
@@ -50,18 +49,12 @@
<string name="BaMmi" msgid="455193067926770581">"呼å«é™åˆ¶"</string>
<string name="PwdMmi" msgid="7043715687905254199">"密ç æ›´æ”¹"</string>
<string name="PinMmi" msgid="3113117780361190304">"PIN ç æ›´æ”¹"</string>
- <!-- no translation found for CnipMmi (3110534680557857162) -->
- <skip />
- <!-- no translation found for CnirMmi (3062102121430548731) -->
- <skip />
- <!-- no translation found for ThreeWCMmi (9051047170321190368) -->
- <skip />
- <!-- no translation found for RuacMmi (7827887459138308886) -->
- <skip />
- <!-- no translation found for CndMmi (3116446237081575808) -->
- <skip />
- <!-- no translation found for DndMmi (1265478932418334331) -->
- <skip />
+ <string name="CnipMmi" msgid="3110534680557857162">"æ¥ç”µæ˜¾ç¤º"</string>
+ <string name="CnirMmi" msgid="3062102121430548731">"æ¥ç”µæ˜¾ç¤ºå·²ç¦ç”¨"</string>
+ <string name="ThreeWCMmi" msgid="9051047170321190368">"三方通è¯"</string>
+ <string name="RuacMmi" msgid="7827887459138308886">"æ‹’ç»ä¸æƒ³æŽ¥å¬çš„骚扰电è¯"</string>
+ <string name="CndMmi" msgid="3116446237081575808">"主å«å·ç ä¼ é€"</string>
+ <string name="DndMmi" msgid="1265478932418334331">"请勿打扰"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"æ¥ç”µæ˜¾ç¤ºé»˜è®¤è®¾ç½®ä¸ºå—é™åˆ¶ã€‚下一个呼å«ï¼šå—é™åˆ¶"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"æ¥ç”µæ˜¾ç¤ºé»˜è®¤è®¾ç½®ä¸ºå—é™åˆ¶ã€‚下一个呼å«ï¼šä¸å—é™åˆ¶"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"æ¥ç”µæ˜¾ç¤ºé»˜è®¤è®¾ç½®ä¸ºä¸å—é™åˆ¶ã€‚下一个呼å«ï¼šå—é™åˆ¶"</string>
@@ -81,43 +74,27 @@
<string name="serviceClassDataSync" msgid="7530000519646054776">"åŒæ­¥"</string>
<string name="serviceClassPacket" msgid="6991006557993423453">"å°åŒ…"</string>
<string name="serviceClassPAD" msgid="3235259085648271037">"PAD"</string>
- <!-- no translation found for roamingText0 (7170335472198694945) -->
- <skip />
- <!-- no translation found for roamingText1 (5314861519752538922) -->
- <skip />
- <!-- no translation found for roamingText2 (8969929049081268115) -->
- <skip />
- <!-- no translation found for roamingText3 (5148255027043943317) -->
- <skip />
- <!-- no translation found for roamingText4 (8808456682550796530) -->
- <skip />
- <!-- no translation found for roamingText5 (7604063252850354350) -->
- <skip />
- <!-- no translation found for roamingText6 (2059440825782871513) -->
- <skip />
- <!-- no translation found for roamingText7 (7112078724097233605) -->
- <skip />
- <!-- no translation found for roamingText8 (5989569778604089291) -->
- <skip />
- <!-- no translation found for roamingText9 (7969296811355152491) -->
- <skip />
- <!-- no translation found for roamingText10 (3992906999815316417) -->
- <skip />
- <!-- no translation found for roamingText11 (4154476854426920970) -->
- <skip />
- <!-- no translation found for roamingText12 (1189071119992726320) -->
- <skip />
- <!-- no translation found for roamingTextSearching (8360141885972279963) -->
- <skip />
+ <string name="roamingText0" msgid="7170335472198694945">"å¯ç”¨æ¼«æ¸¸æŒ‡ç¤ºç¬¦"</string>
+ <string name="roamingText1" msgid="5314861519752538922">"ç¦ç”¨æ¼«æ¸¸æŒ‡ç¤ºç¬¦"</string>
+ <string name="roamingText2" msgid="8969929049081268115">"漫游指示符正在闪çƒ"</string>
+ <string name="roamingText3" msgid="5148255027043943317">"ä¸åœ¨é™„è¿‘"</string>
+ <string name="roamingText4" msgid="8808456682550796530">"室外"</string>
+ <string name="roamingText5" msgid="7604063252850354350">"漫游 - 首选系统"</string>
+ <string name="roamingText6" msgid="2059440825782871513">"漫游 - å¯ç”¨ç³»ç»Ÿ"</string>
+ <string name="roamingText7" msgid="7112078724097233605">"漫游 - è”盟åˆä½œä¼™ä¼´"</string>
+ <string name="roamingText8" msgid="5989569778604089291">"漫游 - 高级åˆä½œä¼™ä¼´"</string>
+ <string name="roamingText9" msgid="7969296811355152491">"漫游 - 全部æœåŠ¡åŠŸèƒ½"</string>
+ <string name="roamingText10" msgid="3992906999815316417">"漫游 - æœåŠ¡åŠŸèƒ½ä¸å…¨"</string>
+ <string name="roamingText11" msgid="4154476854426920970">"å¯ç”¨æ¼«æ¸¸æ¨ªå¹…"</string>
+ <string name="roamingText12" msgid="1189071119992726320">"ç¦ç”¨æ¼«æ¸¸æ¨ªå¹…"</string>
+ <string name="roamingTextSearching" msgid="8360141885972279963">"正在æœç´¢æœåŠ¡"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒åŽæ‹¨æ‰“ <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
<string name="cfTemplateRegistered" msgid="5073237827620166285">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:没有转接"</string>
<string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:没有转接"</string>
- <!-- no translation found for fcComplete (3118848230966886575) -->
- <skip />
- <!-- no translation found for fcError (3327560126588500777) -->
- <skip />
+ <string name="fcComplete" msgid="3118848230966886575">"功能代ç å·²æ‹¨å®Œã€‚"</string>
+ <string name="fcError" msgid="3327560126588500777">"出现连接问题或功能代ç æ— æ•ˆã€‚"</string>
<string name="httpErrorOk" msgid="1191919378083472204">"确定"</string>
<string name="httpError" msgid="2567300624552921790">"网页包å«é”™è¯¯ã€‚"</string>
<string name="httpErrorLookup" msgid="4517085806977851374">"找ä¸åˆ°ç½‘å€ã€‚"</string>
@@ -134,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"无法访问该文件。"</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"找ä¸åˆ°è¯·æ±‚的文件。"</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"正在处ç†çš„请求太多,请ç¨åŽé‡è¯•ã€‚"</string>
+ <string name="notification_title" msgid="4254304601929719937">"登录错误"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"åŒæ­¥"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"åŒæ­¥"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"太多<xliff:g id="CONTENT_TYPE">%s</xliff:g>删除项。"</string>
@@ -179,10 +157,8 @@
<string name="permgroupdesc_systemTools" msgid="8162102602190734305">"对系统的低级别访问和控制。"</string>
<string name="permgrouplab_developmentTools" msgid="3446164584710596513">"å¼€å‘工具"</string>
<string name="permgroupdesc_developmentTools" msgid="9056431193893809814">"åªæœ‰åº”用程åºå¼€å‘人员需è¦è¿™äº›åŠŸèƒ½ã€‚"</string>
- <!-- no translation found for permgrouplab_storage (1971118770546336966) -->
- <skip />
- <!-- no translation found for permgroupdesc_storage (9203302214915355774) -->
- <skip />
+ <string name="permgrouplab_storage" msgid="1971118770546336966">"存储"</string>
+ <string name="permgroupdesc_storage" msgid="9203302214915355774">"访问 SD å¡ã€‚"</string>
<string name="permlab_statusBar" msgid="7417192629601890791">"åœç”¨æˆ–修改状æ€æ "</string>
<string name="permdesc_statusBar" msgid="1365473595331989732">"å…许应用程åºåœç”¨çŠ¶æ€æ ï¼Œæˆ–者添加和删除系统图标。"</string>
<string name="permlab_expandStatusBar" msgid="1148198785937489264">"展开/折å çŠ¶æ€æ "</string>
@@ -215,14 +191,10 @@
<string name="permdesc_forceBack" msgid="6534109744159919013">"å…许应用程åºå¼ºè¡Œå…³é—­å‰å°ä¸­çš„任何活动并返回。普通应用程åºä»Žä¸éœ€è¦ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_dump" msgid="1681799862438954752">"检索系统内部状æ€"</string>
<string name="permdesc_dump" msgid="2198776174276275220">"å…许应用程åºæ£€ç´¢ç³»ç»Ÿçš„内部状æ€ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤æ£€ç´¢å®ƒä»¬é€šå¸¸å¹¶ä¸éœ€è¦çš„å„ç§ç§æœ‰ä¿¡æ¯å’Œå®‰å…¨ä¿¡æ¯ã€‚"</string>
- <!-- no translation found for permlab_shutdown (7185747824038909016) -->
- <skip />
- <!-- no translation found for permdesc_shutdown (7046500838746291775) -->
- <skip />
- <!-- no translation found for permlab_stopAppSwitches (4138608610717425573) -->
- <skip />
- <!-- no translation found for permdesc_stopAppSwitches (3857886086919033794) -->
- <skip />
+ <string name="permlab_shutdown" msgid="7185747824038909016">"部分关机"</string>
+ <string name="permdesc_shutdown" msgid="7046500838746291775">"使活动管ç†å™¨è¿›å…¥å…³é—­çŠ¶æ€ã€‚ä¸æ‰§è¡Œå½»åº•å…³æœºã€‚"</string>
+ <string name="permlab_stopAppSwitches" msgid="4138608610717425573">"ç¦æ­¢åˆ‡æ¢åº”用程åº"</string>
+ <string name="permdesc_stopAppSwitches" msgid="3857886086919033794">"ç¦æ­¢ç”¨æˆ·åˆ‡æ¢åˆ°å¦ä¸€åº”用程åºã€‚"</string>
<string name="permlab_runSetActivityWatcher" msgid="7811586187574696296">"监控所有应用程åºçš„å¯åŠ¨"</string>
<string name="permdesc_runSetActivityWatcher" msgid="3228701938345388092">"å…许应用程åºç›‘控系统å¯åŠ¨æ´»åŠ¨çš„æ–¹å¼ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤å½»åº•æŸå系统。这一æƒé™åªåœ¨å¼€å‘过程中需è¦ï¼Œæ™®é€šçš„手机æ“作ä¸éœ€è¦ã€‚"</string>
<string name="permlab_broadcastPackageRemoved" msgid="2576333434893532475">"å‘é€å·²åˆ é™¤åŒ…的广播"</string>
@@ -237,9 +209,12 @@
<string name="permdesc_setAlwaysFinish" msgid="8773936403987091620">"å…许应用程åºæŽ§åˆ¶æ´»åŠ¨æ˜¯å¦å§‹ç»ˆæ˜¯ä¸€è½¬è‡³åŽå°å°±å®Œæˆã€‚普通应用程åºä»Žä¸éœ€è¦ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_batteryStats" msgid="7863923071360031652">"修改电池使用情况统计信æ¯"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"å…许修改收集的电池使用情况统计信æ¯ã€‚普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
- <!-- no translation found for permlab_backup (470013022865453920) -->
+ <string name="permlab_backup" msgid="470013022865453920">"控制系统备份和还原"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
<skip />
- <!-- no translation found for permdesc_backup (2305432853944929371) -->
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
<skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"显示未授æƒçš„窗å£"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"å…许创建专供内部系统用户界é¢ä½¿ç”¨çš„窗å£ã€‚普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
@@ -255,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"å…许应用程åºæŸ¥çœ‹æ‚¨æŒ‰çš„键,å³ä½¿åœ¨ä¸Žå…¶ä»–应用程åºäº¤äº’(例如输入密ç ï¼‰æ—¶ä¹Ÿä¸ä¾‹å¤–。普通应用程åºä»Žä¸éœ€è¦ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"绑定至输入法"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"å…许手机用户绑定至输入法的顶级界é¢ã€‚普通应用程åºä»Žä¸éœ€è¦ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"绑定到å£çº¸"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"å…许手机用户绑定到å£çº¸çš„顶级界é¢ã€‚应该从ä¸éœ€è¦å°†æ­¤æƒé™æŽˆäºˆæ™®é€šåº”用程åºã€‚"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"更改å±å¹•æµè§ˆæ¨¡å¼"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"å…许应用程åºéšæ—¶æ›´æ”¹å±å¹•çš„旋转方å‘。普通应用程åºä»Žä¸éœ€è¦ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"å‘应用程åºå‘é€ Linux ä¿¡å·"</string>
@@ -284,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"修改全局系统设置"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"å…许应用程åºä¿®æ”¹ç³»ç»Ÿçš„设置数æ®ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤ç ´å您的系统é…置。"</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"修改安全系统设置"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"å…许应用程åºä¿®æ”¹ç³»ç»Ÿå®‰å…¨è®¾ç½®æ•°æ®ã€‚普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"修改 Google æœåŠ¡åœ°å›¾"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"å…许应用程åºä¿®æ”¹ Google æœåŠ¡åœ°å›¾ã€‚普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"开机时自动å¯åŠ¨"</string>
@@ -307,10 +285,8 @@
<string name="permdesc_accessMockLocation" msgid="7648286063459727252">"创建用于测试的模拟ä½ç½®æºã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤æ›¿ä»£çœŸæ­£çš„ä½ç½®æºï¼ˆå¦‚ GPS 或网络æ供商)返回的ä½ç½®å’Œ/或状æ€ã€‚"</string>
<string name="permlab_accessLocationExtraCommands" msgid="2836308076720553837">"接收é¢å¤–çš„ä½ç½®æ供者命令"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="1948144701382451721">"访问é¢å¤–çš„ä½ç½®æ供程åºå‘½ä»¤ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤å¹²æ‰° GPS 或其他ä½ç½®æºçš„è¿ä½œã€‚"</string>
- <!-- no translation found for permlab_installLocationProvider (6578101199825193873) -->
- <skip />
- <!-- no translation found for permdesc_installLocationProvider (5449175116732002106) -->
- <skip />
+ <string name="permlab_installLocationProvider" msgid="6578101199825193873">"安装地点信æ¯æ供程åºçš„æƒé™"</string>
+ <string name="permdesc_installLocationProvider" msgid="5449175116732002106">"创建用于测试的模拟地点信æ¯æºã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤è¦†ç›–真正的地点信æ¯æºï¼ˆå¦‚ GPS 或网络æ供商)返回的地点和/或状æ€ï¼Œæˆ–者监视您的地点并å‘外部信æ¯æºæŠ¥å‘Šã€‚"</string>
<string name="permlab_accessFineLocation" msgid="8116127007541369477">"精准ä½ç½® (GPS)"</string>
<string name="permdesc_accessFineLocation" msgid="7411213317434337331">"访问精准的ä½ç½®æºï¼Œä¾‹å¦‚手机上的全çƒå®šä½ç³»ç»Ÿï¼ˆå¦‚果适用)。æ¶æ„应用程åºå¯èƒ½å€Ÿæ­¤ç¡®å®šæ‚¨æ‰€å¤„çš„ä½ç½®ï¼Œå¹¶æ¶ˆè€—é¢å¤–的电池电é‡ã€‚"</string>
<string name="permlab_accessCoarseLocation" msgid="4642255009181975828">"ç²—ç•¥ä½ç½®ï¼ˆä»¥ç½‘络为基础)"</string>
@@ -318,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"访问 SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"å…许应用程åºä½¿ç”¨ SurfaceFlinger 低级别功能。"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"读å–帧缓冲区"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"å…许应用程åºè¯»å–帧缓冲区的内容。"</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"å…许应用程åºè¯»å–帧缓冲区中的内容。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"更改您的音频设置"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"å…许应用程åºä¿®æ”¹å…¨å±€éŸ³é¢‘设置,如音é‡å’Œè·¯ç”±ã€‚"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string>
@@ -343,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"å…许应用程åºåœ¨æ²¡æœ‰æ‚¨å¹²é¢„的情况下呼å«ç”µè¯å·ç ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤åœ¨æ‚¨çš„è¯è´¹å•ä¸Šäº§ç”Ÿæ„外通è¯è´¹ã€‚请注æ„,此æƒé™ä¸å…许应用程åºå‘¼å«ç´§æ€¥ç”µè¯å·ç ã€‚"</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"直接呼å«ä»»ä½•ç”µè¯å·ç "</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"å…许应用程åºåœ¨æ²¡æœ‰æ‚¨å¹²é¢„的情况下呼å«ä»»ä½•ç”µè¯å·ç ï¼ˆåŒ…括紧急电è¯å·ç ï¼‰ã€‚æ¶æ„应用程åºå¯å€Ÿæ­¤å¯¹ç´§æ€¥æœåŠ¡æ‹¨æ‰“骚扰电è¯å’Œéžæ³•ç”µè¯ã€‚"</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"控制ä½ç½®æ›´æ–°é€šçŸ¥"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"å…许å¯ç”¨/åœç”¨æ”¶éŸ³æœºçš„ä½ç½®æ›´æ–°é€šçŸ¥ã€‚普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"访问检入属性"</string>
@@ -351,10 +331,8 @@
<string name="permdesc_bindGadget" msgid="2098697834497452046">"å…许应用程åºå‘Šè¯‰ç³»ç»Ÿå“ªä¸ªåº”用程åºå¯ä»¥ä½¿ç”¨å“ªäº›çª—å£å°éƒ¨ä»¶ã€‚具有该æƒé™çš„应用程åºå¯ä»¥å…许其他应用程åºè®¿é—®ä¸ªäººæ•°æ®ã€‚普通应用程åºä¸é€‚åˆä½¿ç”¨æ­¤æƒé™ã€‚"</string>
<string name="permlab_modifyPhoneState" msgid="8423923777659292228">"修改手机状æ€"</string>
<string name="permdesc_modifyPhoneState" msgid="3302284561346956587">"å…许应用程åºæŽ§åˆ¶è®¾å¤‡çš„手机功能。具有此æƒé™çš„应用程åºå¯åˆ‡æ¢ç½‘络ã€æ‰“开和关闭手机收音机等,而ä¸é€šçŸ¥æ‚¨ã€‚"</string>
- <!-- no translation found for permlab_readPhoneState (2326172951448691631) -->
- <skip />
- <!-- no translation found for permdesc_readPhoneState (188877305147626781) -->
- <skip />
+ <string name="permlab_readPhoneState" msgid="2326172951448691631">"读å–手机状æ€å’Œèº«ä»½"</string>
+ <string name="permdesc_readPhoneState" msgid="188877305147626781">"å…许应用程åºè®¿é—®è®¾å¤‡çš„手机功能。有此æƒé™çš„应用程åºå¯ç¡®å®šæ­¤æ‰‹æœºçš„å·ç å’Œåºåˆ—å·ï¼Œæ˜¯å¦æ­£åœ¨é€šè¯ï¼Œä»¥åŠå¯¹æ–¹çš„å·ç ç­‰ã€‚"</string>
<string name="permlab_wakeLock" msgid="573480187941496130">"防止手机休眠"</string>
<string name="permdesc_wakeLock" msgid="7584036471227467099">"å…许应用程åºé˜²æ­¢æ‰‹æœºè¿›å…¥ä¼‘眠状æ€ã€‚"</string>
<string name="permlab_devicePower" msgid="4928622470980943206">"开机或关机"</string>
@@ -369,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"å…许应用程åºå°†ç³»ç»Ÿå®Œå…¨é‡ç½®ä¸ºå‡ºåŽ‚设置,å³æ¸…除所有数æ®ã€é…置和安装的应用程åºã€‚"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"设置时区"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"å…许应用程åºæ›´æ”¹æ‰‹æœºçš„时区。"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"作为 AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"å…许应用程åºå‘¼å« AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"å‘现已知å¸æˆ·"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"å…许应用程åºèŽ·å–手机已知的å¸æˆ·åˆ—表。"</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"作为å¸æˆ·èº«ä»½éªŒè¯ç¨‹åº"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"å…许应用程åºä½¿ç”¨ AccountManager çš„å¸æˆ·èº«ä»½éªŒè¯ç¨‹åºåŠŸèƒ½ï¼ŒåŒ…括创建å¸æˆ·ä»¥åŠèŽ·å–和设置其密ç ã€‚"</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"管ç†å¸æˆ·åˆ—表"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"å…许应用程åºæ‰§è¡Œæ·»åŠ ã€åˆ é™¤å¸æˆ·åŠåˆ é™¤å…¶å¯†ç ä¹‹ç±»çš„æ“作。"</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"使用å¸æˆ·çš„身份验è¯å‡­æ®"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"å…许应用程åºè¯·æ±‚身份验è¯æ ‡è®°ã€‚"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"查看网络状æ€"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"å…许应用程åºæŸ¥çœ‹æ‰€æœ‰ç½‘络的状æ€ã€‚"</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"互è”网完全访问"</string>
@@ -385,10 +371,8 @@
<string name="permdesc_accessWifiState" msgid="485796529139236346">"å…许应用程åºæŸ¥çœ‹æœ‰å…³ Wi-Fi 状æ€çš„ä¿¡æ¯ã€‚"</string>
<string name="permlab_changeWifiState" msgid="7280632711057112137">"更改 Wi-Fi 状æ€"</string>
<string name="permdesc_changeWifiState" msgid="2950383153656873267">"å…许应用程åºè¿žæŽ¥åˆ° Wi-Fi 接入点以åŠä¸Ž Wi-Fi 接入点断开连接,并对é…置的 Wi-Fi 网络进行更改。"</string>
- <!-- no translation found for permlab_changeWifiMulticastState (1368253871483254784) -->
- <skip />
- <!-- no translation found for permdesc_changeWifiMulticastState (8199464507656067553) -->
- <skip />
+ <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"å…许接收 Wi-Fi 多播"</string>
+ <string name="permdesc_changeWifiMulticastState" msgid="8199464507656067553">"å…许应用程åºæŽ¥æ”¶å¹¶éžç›´æŽ¥å‘您的设备å‘é€çš„æ•°æ®åŒ…。这样在查找附近æ供的æœåŠ¡æ—¶å¾ˆæœ‰ç”¨ã€‚è¿™ç§æ“作所耗电é‡å¤§äºŽéžå¤šæ’­æ¨¡å¼ã€‚"</string>
<string name="permlab_bluetoothAdmin" msgid="1092209628459341292">"è“牙管ç†"</string>
<string name="permdesc_bluetoothAdmin" msgid="7256289774667054555">"å…许应用程åºé…置本地è“牙手机,以åŠæŸ¥æ‰¾è¿œç¨‹è®¾å¤‡å¹¶ä¸Žä¹‹é…对。"</string>
<string name="permlab_bluetooth" msgid="8361038707857018732">"创建è“牙连接"</string>
@@ -409,10 +393,8 @@
<string name="permdesc_readDictionary" msgid="1082972603576360690">"å…许应用程åºè¯»å–用户在用户è¯å…¸ä¸­å­˜å‚¨çš„ä»»æ„ç§æœ‰å­—è¯ã€å称和短语。"</string>
<string name="permlab_writeDictionary" msgid="6703109511836343341">"写入用户定义的è¯å…¸"</string>
<string name="permdesc_writeDictionary" msgid="2241256206524082880">"å…许应用程åºå‘用户è¯å…¸ä¸­å†™å…¥æ–°è¯ã€‚"</string>
- <!-- no translation found for permlab_sdcardWrite (8079403759001777291) -->
- <skip />
- <!-- no translation found for permdesc_sdcardWrite (6643963204976471878) -->
- <skip />
+ <string name="permlab_sdcardWrite" msgid="8079403759001777291">"修改/删除 SD å¡å†…容"</string>
+ <string name="permdesc_sdcardWrite" msgid="6643963204976471878">"å…许应用程åºå†™å…¥ SD å¡ã€‚"</string>
<string-array name="phoneTypes">
<item msgid="8901098336658710359">"ä½å®…电è¯"</item>
<item msgid="869923650527136615">"手机"</item>
@@ -429,7 +411,6 @@
<item msgid="1112044410659011023">"其他邮箱"</item>
<item msgid="2374913952870110618">"自定义邮箱"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"手机"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"ä½å®…地å€"</item>
<item msgid="5629153956045109251">"å•ä½åœ°å€"</item>
@@ -470,8 +451,7 @@
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"正确ï¼"</string>
<string name="lockscreen_pattern_wrong" msgid="4817583279053112312">"很抱歉,请é‡è¯•"</string>
<string name="lockscreen_plugged_in" msgid="613343852842944435">"正在充电 (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
- <!-- no translation found for lockscreen_charged (4938930459620989972) -->
- <skip />
+ <string name="lockscreen_charged" msgid="4938930459620989972">"电已充满。"</string>
<string name="lockscreen_low_battery" msgid="1482873981919249740">"连接您的充电器。"</string>
<string name="lockscreen_missing_sim_message_short" msgid="7381499217732227295">"没有 SIM å¡ã€‚"</string>
<string name="lockscreen_missing_sim_message" msgid="2186920585695169078">"手机中无 SIM å¡ã€‚"</string>
@@ -485,16 +465,19 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"æ‚¨å·²ç» <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解é”图案。如果å†å°è¯• <xliff:g id="NUMBER_1">%d</xliff:g> 次åŽä»ä¸æˆåŠŸï¼Œç³»ç»Ÿä¼šè¦æ±‚您使用自己的 Google 登录信æ¯è§£é”手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒åŽé‡è¯•ã€‚"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒åŽé‡è¯•ã€‚"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘记了图案?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"图案å°è¯•æ¬¡æ•°è¿‡å¤šï¼"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"è¦è§£é™¤é”定,请使用您的 Google å¸æˆ·ç™»å½•"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"用户å(电å­é‚®ä»¶ï¼‰"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"密ç "</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"登录"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"用户å或密ç æ— æ•ˆã€‚"</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="AMPM">%P</xliff:g><xliff:g id="HOUR">%-l</xliff:g>点"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="AMPM">%p</xliff:g><xliff:g id="HOUR">%-l</xliff:g>点"</string>
- <!-- no translation found for status_bar_clear_all_button (7774721344716731603) -->
- <skip />
+ <string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"无通知"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"正在进行"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
@@ -503,7 +486,7 @@
<string name="battery_low_title" msgid="7923774589611311406">"请连接充电器"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"电é‡åœ¨å‡å°‘:"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"剩余电é‡ä¸è¶³ <xliff:g id="NUMBER">%d%%</xliff:g>。"</string>
- <!-- no translation found for battery_low_why (7655196144309694753) -->
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
<skip />
<string name="factorytest_failed" msgid="5410270329114212041">"出厂测试失败"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"åªæœ‰ /system/app ä¸­å®‰è£…çš„åŒ…æ”¯æŒ FACTORY_TEST æ“作。"</string>
@@ -513,14 +496,10 @@
<string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
<string name="js_dialog_before_unload" msgid="1901675448179653089">"是å¦ä»Žè¯¥é¡µé¢å¯¼èˆªè‡³å®ƒå¤„?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"选择“确定â€ç»§ç»­ï¼Œæˆ–选择“å–消â€ç•™åœ¨å½“å‰é¡µé¢ã€‚"</string>
<string name="save_password_label" msgid="6860261758665825069">"确认"</string>
- <!-- no translation found for permlab_readHistoryBookmarks (1284843728203412135) -->
- <skip />
- <!-- no translation found for permdesc_readHistoryBookmarks (4981489815467617191) -->
- <skip />
- <!-- no translation found for permlab_writeHistoryBookmarks (9009434109836280374) -->
- <skip />
- <!-- no translation found for permdesc_writeHistoryBookmarks (945571990357114950) -->
- <skip />
+ <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读å–æµè§ˆå™¨çš„历å²è®°å½•å’Œä¹¦ç­¾"</string>
+ <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"å…许应用程åºè¯»å–用æµè§ˆå™¨è®¿é—®è¿‡çš„所有网å€ï¼Œä»¥åŠæµè§ˆå™¨çš„所有书签。"</string>
+ <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入æµè§ˆå™¨çš„历å²è®°å½•å’Œä¹¦ç­¾"</string>
+ <string name="permdesc_writeHistoryBookmarks" msgid="945571990357114950">"å…许应用程åºä¿®æ”¹å­˜å‚¨åœ¨æ‰‹æœºä¸­çš„æµè§ˆå™¨åŽ†å²è®°å½•æˆ–书签。æ¶æ„应用程åºå¯å€Ÿæ­¤æ¸…除或修改æµè§ˆå™¨æ•°æ®ã€‚"</string>
<string name="save_password_message" msgid="767344687139195790">"是å¦å¸Œæœ›æµè§ˆå™¨è®°ä½æ­¤å¯†ç ï¼Ÿ"</string>
<string name="save_password_notnow" msgid="6389675316706699758">"此时ä¸ä¿å­˜å¯†ç "</string>
<string name="save_password_remember" msgid="6491879678996749466">"è®°ä½"</string>
@@ -599,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"明天"</item>
<item quantity="other" msgid="2973062968038355991">"<xliff:g id="COUNT">%d</xliff:g> 天åŽ"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"在 %s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"在%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%s å¹´"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"天"</string>
<string name="days" msgid="4774547661021344602">"天"</string>
<string name="hour" msgid="2126771916426189481">"å°æ—¶"</string>
@@ -639,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"全部å¤åˆ¶"</string>
<string name="paste" msgid="5629880836805036433">"粘贴"</string>
<string name="copyUrl" msgid="2538211579596067402">"å¤åˆ¶ç½‘å€"</string>
- <string name="inputMethod" msgid="7673923508389094672">"输入法"</string>
- <string name="addToDictionary" msgid="726256909274177272">"将“%sâ€æ·»åŠ åˆ°è¯å…¸"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"编辑文字"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"空间ä¸è¶³"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"手机存储空间正在å‡å°‘。"</string>
@@ -665,14 +649,14 @@
<string name="anr_application_process" msgid="4185842666452210193">"<xliff:g id="APPLICATION">%1$s</xliff:g>应用程åºï¼ˆåœ¨ <xliff:g id="PROCESS">%2$s</xliff:g> 进程中)无å“应。"</string>
<string name="anr_process" msgid="1246866008169975783">"<xliff:g id="PROCESS">%1$s</xliff:g> 进程无å“应。"</string>
<string name="force_close" msgid="3653416315450806396">"强行关闭"</string>
- <!-- no translation found for report (4060218260984795706) -->
- <skip />
+ <string name="report" msgid="4060218260984795706">"报告"</string>
<string name="wait" msgid="7147118217226317732">"等待"</string>
<string name="debug" msgid="9103374629678531849">"调试"</string>
<string name="sendText" msgid="5132506121645618310">"选择è¦å¯¹æ–‡å­—执行的æ“作"</string>
<string name="volume_ringtone" msgid="6885421406845734650">"铃声音é‡"</string>
<string name="volume_music" msgid="5421651157138628171">"媒体音é‡"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"通过è“牙播放"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"选择的是é™éŸ³é“ƒå£°"</string>
<string name="volume_call" msgid="3941680041282788711">"通è¯éŸ³é‡"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"使用è“牙时的通è¯éŸ³é‡"</string>
<string name="volume_alarm" msgid="1985191616042689100">"闹钟音é‡"</string>
@@ -716,35 +700,29 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"在关闭 USB 存储设备å‰ï¼Œè¯·ç¡®ä¿æ‚¨å·²å¸è½½äº† USB 主设备。选择“关闭â€å…³é—­ USB 存储设备。"</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"关闭"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"å–消"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"关闭 USB 存储设备时é‡åˆ°é—®é¢˜ã€‚请检查是å¦å¸è½½äº† USB 主设备,然åŽé‡è¯•ã€‚"</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"æ ¼å¼åŒ– SD å¡"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"您确定è¦æ ¼å¼åŒ– SD å¡ï¼Ÿå¡ä¸Šçš„所有数æ®éƒ½ä¼šä¸¢å¤±ã€‚"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"æ ¼å¼åŒ–"</string>
- <!-- no translation found for adb_active_notification_title (6729044778949189918) -->
- <skip />
- <!-- no translation found for adb_active_notification_message (4661997077344501389) -->
- <skip />
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"已连接 USB 调试接å£"</string>
+ <string name="adb_active_notification_message" msgid="4661997077344501389">"计算机已与手机相连。"</string>
<string name="select_input_method" msgid="2086499663193509436">"选择输入法"</string>
<string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="candidates_style" msgid="4333913089637062257"><u>"候选"</u></string>
<string name="ext_media_checking_notification_title" msgid="5457603418970994050">"正在准备 SD å¡"</string>
- <!-- no translation found for ext_media_checking_notification_message (8287319882926737053) -->
- <skip />
+ <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"正在检查是å¦æœ‰é”™è¯¯ã€‚"</string>
<string name="ext_media_nofs_notification_title" msgid="780477838241212997">"空 SD å¡"</string>
- <!-- no translation found for ext_media_nofs_notification_message (3817704088027829380) -->
- <skip />
+ <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD å¡æ˜¯ç©ºçš„或其文件系统ä¸å—支æŒã€‚"</string>
<string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"SD å¡å—æŸ"</string>
- <!-- no translation found for ext_media_unmountable_notification_message (6902531775948238989) -->
- <skip />
+ <string name="ext_media_unmountable_notification_message" msgid="6902531775948238989">"SD å¡å·²æŸå。å¯èƒ½å¿…é¡»é‡æ–°æ ¼å¼åŒ–。"</string>
<string name="ext_media_badremoval_notification_title" msgid="6872152882604407837">"SD å¡è¢«æ„外拔除"</string>
<string name="ext_media_badremoval_notification_message" msgid="7260183293747448241">"å…ˆå¸è½½ SD å¡å†æ‹”除,以é¿å…æ•°æ®ä¸¢å¤±ã€‚"</string>
<string name="ext_media_safe_unmount_notification_title" msgid="6729801130790616200">"SD å¡å·²å®‰å…¨ç§»é™¤"</string>
- <!-- no translation found for ext_media_safe_unmount_notification_message (568841278138377604) -->
- <skip />
+ <string name="ext_media_safe_unmount_notification_message" msgid="568841278138377604">"å¯å®‰å…¨åœ°å–出 SD å¡ã€‚"</string>
<string name="ext_media_nomedia_notification_title" msgid="8902518030404381318">"已移除 SD å¡"</string>
- <!-- no translation found for ext_media_nomedia_notification_message (3870120652983659641) -->
- <skip />
+ <string name="ext_media_nomedia_notification_message" msgid="3870120652983659641">"SD å¡å·²å–出。请æ’å…¥å¦ä¸€ SD å¡ã€‚"</string>
<string name="activity_list_empty" msgid="4168820609403385789">"找ä¸åˆ°åŒ¹é…的活动"</string>
<string name="permlab_pkgUsageStats" msgid="8787352074326748892">"更新组件使用情况统计"</string>
<string name="permdesc_pkgUsageStats" msgid="891553695716752835">"å…许修改收集的组件使用情况统计。普通应用程åºä¸èƒ½ä½¿ç”¨æ­¤æƒé™ã€‚"</string>
@@ -758,8 +736,25 @@
<string name="ime_action_default" msgid="2840921885558045721">"执行"</string>
<string name="dial_number_using" msgid="5789176425167573586">"拨打电è¯"\n"<xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="4947405226788104538">"创建电è¯å·ç ä¸º"\n"<xliff:g id="NUMBER">%s</xliff:g> çš„è”系人"</string>
- <!-- no translation found for accessibility_compound_button_selected (5612776946036285686) -->
+ <string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已选中"</string>
+ <string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未选中"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"å…许"</string>
+ <string name="deny" msgid="2081879885755434506">"æ‹’ç»"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"已请求æƒé™"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
<skip />
- <!-- no translation found for accessibility_compound_button_unselected (8864512895673924091) -->
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
<skip />
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 7b4b962..4ee4544 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -111,6 +111,7 @@
<string name="httpErrorFile" msgid="8250549644091165175">"無法存å–此檔案。"</string>
<string name="httpErrorFileNotFound" msgid="5588380756326017105">"找ä¸åˆ°è¦æ±‚的檔案。"</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"太多執行è¦æ±‚。請ç¨å¾Œå†è©¦ä¸€æ¬¡ã€‚"</string>
+ <string name="notification_title" msgid="4254304601929719937">"登入錯誤"</string>
<string name="contentServiceSync" msgid="8353523060269335667">"åŒæ­¥è™•ç†"</string>
<string name="contentServiceSyncNotificationTitle" msgid="397743349191901458">"åŒæ­¥è™•ç†"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="8100981435080696431">"åŒæ™‚刪除太多 <xliff:g id="CONTENT_TYPE">%s</xliff:g>。"</string>
@@ -209,7 +210,12 @@
<string name="permlab_batteryStats" msgid="7863923071360031652">"編輯電池狀態"</string>
<string name="permdesc_batteryStats" msgid="5847319823772230560">"å…許修改電池狀態。一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_backup" msgid="470013022865453920">"控制系統備份與還原"</string>
- <string name="permdesc_backup" msgid="2305432853944929371">"å…許應用程å¼æŽ§åˆ¶ç³»çµ±å‚™ä»½èˆ‡é‚„原機制,但一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
+ <!-- no translation found for permdesc_backup (4837493065154256525) -->
+ <skip />
+ <!-- no translation found for permlab_backup_data (4057625941707926463) -->
+ <skip />
+ <!-- no translation found for permdesc_backup_data (8274426305151227766) -->
+ <skip />
<string name="permlab_internalSystemWindow" msgid="2148563628140193231">"顯示未授權視窗"</string>
<string name="permdesc_internalSystemWindow" msgid="5895082268284998469">"å…許內部系統使用介é¢å»ºç«‹è¦–窗。一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_systemAlertWindow" msgid="3372321942941168324">"顯示系統警示"</string>
@@ -224,6 +230,8 @@
<string name="permdesc_readInputState" msgid="5132879321450325445">"å…許應用程å¼åœ¨ä½¿ç”¨è€…æ“作其他程å¼æ™‚ (例如:輸入密碼),ä»å¯ç›£çœ‹è¼¸å…¥çš„按éµã€‚一般應用程å¼æ‡‰ä¸éœ€è¦æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_bindInputMethod" msgid="3360064620230515776">"連çµè‡³è¼¸å…¥æ³•"</string>
<string name="permdesc_bindInputMethod" msgid="3734838321027317228">"å…許æ“有人連çµè‡³è¼¸å…¥æ³•çš„最頂層介é¢ã€‚一般應用程å¼ä¸éœ€ä½¿ç”¨æ­¤é¸é …。"</string>
+ <string name="permlab_bindWallpaper" msgid="8716400279937856462">"連çµè‡³æ¡Œå¸ƒ"</string>
+ <string name="permdesc_bindWallpaper" msgid="5287754520361915347">"å…許æ“有人連çµè‡³æ¡Œå¸ƒçš„最頂層介é¢ï¼Œä¸€èˆ¬æ‡‰ç”¨ç¨‹å¼ä¸éœ€ä½¿ç”¨æ­¤é¸é …。"</string>
<string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方å‘"</string>
<string name="permdesc_setOrientation" msgid="6335814461615851863">"å…許應用程å¼éš¨æ™‚變更螢幕顯示方å‘。一般應用程å¼ä¸éœ€è¦æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"å‚³é€ Linux 訊號到應用程å¼"</string>
@@ -253,7 +261,8 @@
<string name="permlab_writeSettings" msgid="1365523497395143704">"編輯全域系統設定"</string>
<string name="permdesc_writeSettings" msgid="838789419871034696">"å…許應用程å¼ä¿®æ”¹ç³»çµ±è¨­å®šã€‚請注æ„:惡æ„程å¼å¯èƒ½ä½¿ç”¨æ­¤åŠŸèƒ½æ毀系統設定。"</string>
<string name="permlab_writeSecureSettings" msgid="204676251876718288">"編輯安全系統設定"</string>
- <string name="permdesc_writeSecureSettings" msgid="4116616249170428132">"å…許應用程å¼ä¿®æ”¹ç³»çµ±çš„安全設定資料。一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
+ <!-- no translation found for permdesc_writeSecureSettings (5497873143539034724) -->
+ <skip />
<string name="permlab_writeGservices" msgid="2149426664226152185">"修改 Google æœå‹™åœ°åœ–"</string>
<string name="permdesc_writeGservices" msgid="6602362746516676175">"å…許應用程å¼ä¿®æ”¹ Google æœå‹™åœ°åœ–。一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_receiveBootCompleted" msgid="7776779842866993377">"開機時自動啟用"</string>
@@ -285,7 +294,7 @@
<string name="permlab_accessSurfaceFlinger" msgid="2363969641792388947">"å­˜å– SurfaceFlinger"</string>
<string name="permdesc_accessSurfaceFlinger" msgid="6805241830020733025">"å…許應用程å¼ä½¿ç”¨ SurfaceFlinger 低階功能。"</string>
<string name="permlab_readFrameBuffer" msgid="6690504248178498136">"讀å–框架緩è¡"</string>
- <string name="permdesc_readFrameBuffer" msgid="5777679658669057819">"å…許應用程å¼è®€å–框架緩è¡çš„內容。"</string>
+ <string name="permdesc_readFrameBuffer" msgid="7530020370469942528">"å…許應用程å¼è®€å–ç•«é¢ç·©è¡å€çš„內容。"</string>
<string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"變更音訊設定"</string>
<string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"å…許應用程å¼ç·¨è¼¯å…¨åŸŸéŸ³è¨Šè¨­å®šï¼Œä¾‹å¦‚音é‡èˆ‡è·¯ç”±ã€‚"</string>
<string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string>
@@ -310,6 +319,10 @@
<string name="permdesc_callPhone" msgid="3369867353692722456">"å…許應用程å¼è‡ªè¡Œæ’¥æ‰“電話。請注æ„:惡æ„程å¼å¯èƒ½ä»»æ„撥打電話,造æˆé æœŸå¤–的支出。但此é¸é …並ä¸å…許應用程å¼æ’¥æ‰“緊急電話號碼。"</string>
<string name="permlab_callPrivileged" msgid="4198349211108497879">"直接撥打任何電話號碼"</string>
<string name="permdesc_callPrivileged" msgid="244405067160028452">"å…許應用程å¼è‡ªè¡Œæ’¥æ‰“任何電話號碼,包括緊急電話號碼。請注æ„:惡æ„程å¼å¯èƒ½åˆ©ç”¨æ­¤åŠŸèƒ½æ¿«ç”¨ç·Šæ€¥æœå‹™ï¼Œæ’¥æ‰“ä¸å¿…è¦æˆ–é•æ³•çš„電話。"</string>
+ <!-- no translation found for permlab_performCdmaProvisioning (5604848095315421425) -->
+ <skip />
+ <!-- no translation found for permdesc_performCdmaProvisioning (6457447676108355905) -->
+ <skip />
<string name="permlab_locationUpdates" msgid="7785408253364335740">"控制ä½ç½®æ›´æ–°é€šçŸ¥"</string>
<string name="permdesc_locationUpdates" msgid="2300018303720930256">"å…許啟用/åœç”¨ç„¡ç·šé€šè¨Šä½ç½®æ›´æ–°é€šçŸ¥ã€‚一般應用程å¼ä¸æœƒä½¿ç”¨æ­¤åŠŸèƒ½ã€‚"</string>
<string name="permlab_checkinProperties" msgid="7855259461268734914">"å­˜å–登機é¸é …"</string>
@@ -334,8 +347,16 @@
<string name="permdesc_masterClear" msgid="5033465107545174514">"å…許應用程å¼å°‡æ‰‹æ©Ÿå®Œå…¨é‡è¨­è‡³å‡ºå» è¨­å®šï¼Œæ¸…除所有資料ã€è¨­å®šèˆ‡å·²å®‰è£ç¨‹å¼ã€‚"</string>
<string name="permlab_setTimeZone" msgid="2945079801013077340">"設定時å€"</string>
<string name="permdesc_setTimeZone" msgid="1902540227418179364">"å…許應用程å¼è®Šæ›´æ™‚å€ã€‚"</string>
+ <string name="permlab_accountManagerService" msgid="4829262349691386986">"作為 AccountManagerService"</string>
+ <string name="permdesc_accountManagerService" msgid="6056903274106394752">"å…許應用程å¼æ’¥æ‰“電話至 AccountAuthenticators"</string>
<string name="permlab_getAccounts" msgid="4549918644233460103">"發ç¾å·²çŸ¥å¸³æˆ¶ã€‚"</string>
<string name="permdesc_getAccounts" msgid="6839262446413155394">"å…許應用程å¼å–得手機上的帳戶清單。"</string>
+ <string name="permlab_authenticateAccounts" msgid="3940505577982882450">"作為帳戶驗證器"</string>
+ <string name="permdesc_authenticateAccounts" msgid="4006839406474208874">"å…許應用程å¼ä½¿ç”¨ AccountManager 的帳戶驗證器功能,包括建立帳戶和å–å¾—åŠè¨­å®šå¸³æˆ¶å¯†ç¢¼ã€‚"</string>
+ <string name="permlab_manageAccounts" msgid="4440380488312204365">"管ç†å¸³æˆ¶æ¸…å–®"</string>
+ <string name="permdesc_manageAccounts" msgid="8804114016661104517">"å…許應用程å¼åŸ·è¡Œæ–°å¢žã€ç§»é™¤å¸³æˆ¶å’Œåˆªé™¤å¸³æˆ¶å¯†ç¢¼ç­‰ä½œæ¥­ã€‚"</string>
+ <string name="permlab_useCredentials" msgid="6401886092818819856">"使用帳戶的驗證èªè­‰"</string>
+ <string name="permdesc_useCredentials" msgid="7416570544619546974">"å…許應用程å¼è¦æ±‚驗證憑證。"</string>
<string name="permlab_accessNetworkState" msgid="6865575199464405769">"檢視網路狀態"</string>
<string name="permdesc_accessNetworkState" msgid="558721128707712766">"å…許應用程å¼æª¢è¦–網路狀態。"</string>
<string name="permlab_createNetworkSockets" msgid="9121633680349549585">"網際網路完整存å–"</string>
@@ -390,7 +411,6 @@
<item msgid="1112044410659011023">"其他信箱"</item>
<item msgid="2374913952870110618">"自訂"</item>
</string-array>
- <string name="mobileEmailTypeName" msgid="2858957283716687707">"行動è£ç½®"</string>
<string-array name="postalAddressTypes">
<item msgid="6880257626740047286">"ä½å®¶"</item>
<item msgid="5629153956045109251">"å…¬å¸"</item>
@@ -445,12 +465,16 @@
<string name="lockscreen_failed_attempts_almost_glogin" msgid="3351013842320127827">"畫出解鎖圖形已錯誤 <xliff:g id="NUMBER_0">%d</xliff:g> 次。å†éŒ¯èª¤ <xliff:g id="NUMBER_1">%d</xliff:g> 次後,系統會è¦æ±‚使用 Google 登入來解鎖。"\n\n" 請在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒後å†è©¦ä¸€æ¬¡ã€‚"</string>
<string name="lockscreen_too_many_failed_attempts_countdown" msgid="6251480343394389665">"<xliff:g id="NUMBER">%d</xliff:g> 秒後å†è©¦ä¸€æ¬¡ã€‚"</string>
<string name="lockscreen_forgot_pattern_button_text" msgid="2626999449610695930">"忘記解鎖圖形?"</string>
+ <!-- no translation found for lockscreen_glogin_forgot_pattern (2588521501166032747) -->
+ <skip />
<string name="lockscreen_glogin_too_many_attempts" msgid="2446246026221678244">"解鎖圖形出錯次數éŽå¤šï¼"</string>
<string name="lockscreen_glogin_instructions" msgid="1816635201812207709">"如è¦è§£é™¤éŽ–定,請使用 Google 帳戶登入"</string>
<string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"使用者å稱 (é›»å­éƒµä»¶)"</string>
<string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"密碼"</string>
<string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"登入"</string>
<string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"使用者å稱或密碼錯誤。"</string>
+ <!-- no translation found for lockscreen_glogin_checking_password (6758890536332363322) -->
+ <skip />
<string name="hour_ampm" msgid="4329881288269772723">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%P</xliff:g>"</string>
<string name="hour_cap_ampm" msgid="1829009197680861107">"<xliff:g id="HOUR">%-l</xliff:g><xliff:g id="AMPM">%p</xliff:g>"</string>
<string name="status_bar_clear_all_button" msgid="7774721344716731603">"清除"</string>
@@ -462,7 +486,8 @@
<string name="battery_low_title" msgid="7923774589611311406">"請連接充電器"</string>
<string name="battery_low_subtitle" msgid="7388781709819722764">"電池電é‡å³å°‡ä¸è¶³ï¼š"</string>
<string name="battery_low_percent_format" msgid="6564958083485073855">"電池電é‡ä¸åˆ° <xliff:g id="NUMBER">%d%%</xliff:g>。"</string>
- <string name="battery_low_why" msgid="7655196144309694753">"原因"</string>
+ <!-- no translation found for battery_low_why (7279169609518386372) -->
+ <skip />
<string name="factorytest_failed" msgid="5410270329114212041">"出廠測試失敗"</string>
<string name="factorytest_not_system" msgid="4435201656767276723">"åªæœ‰å®‰è£åœ¨ /system/app 裡的程å¼æ‰èƒ½æ”¯æ´ FACTORY_TEST æ“作。"</string>
<string name="factorytest_no_action" msgid="872991874799998561">"找ä¸åˆ°æä¾› FACTORY_TEST 的程å¼ã€‚"</string>
@@ -553,9 +578,12 @@
<item quantity="one" msgid="2178576254385739855">"明天"</item>
<item quantity="other" msgid="2973062968038355991">"<xliff:g id="COUNT">%d</xliff:g> 天內"</item>
</plurals>
- <string name="preposition_for_date" msgid="4316283606614248634">"%s"</string>
- <string name="preposition_for_time" msgid="6179700075291054938">"%s"</string>
- <string name="preposition_for_year" msgid="3852279354896963571">"%s"</string>
+ <!-- no translation found for preposition_for_date (9093949757757445117) -->
+ <skip />
+ <!-- no translation found for preposition_for_time (5506831244263083793) -->
+ <skip />
+ <!-- no translation found for preposition_for_year (5040395640711867177) -->
+ <skip />
<string name="day" msgid="8144195776058119424">"天"</string>
<string name="days" msgid="4774547661021344602">"天"</string>
<string name="hour" msgid="2126771916426189481">"å°æ™‚"</string>
@@ -593,8 +621,10 @@
<string name="copyAll" msgid="2590829068100113057">"全部複製"</string>
<string name="paste" msgid="5629880836805036433">"貼上"</string>
<string name="copyUrl" msgid="2538211579596067402">"複製網å€"</string>
- <string name="inputMethod" msgid="7673923508389094672">"輸入法"</string>
- <string name="addToDictionary" msgid="726256909274177272">"將「%sã€æ–°å¢žåˆ°å­—å…¸"</string>
+ <!-- no translation found for inputMethod (1653630062304567879) -->
+ <skip />
+ <!-- no translation found for addToDictionary (8793624991686948709) -->
+ <skip />
<string name="editTextMenuTitle" msgid="1672989176958581452">"編輯文字"</string>
<string name="low_internal_storage_view_title" msgid="1399732408701697546">"儲存空間å³å°‡ä¸è¶³"</string>
<string name="low_internal_storage_view_text" msgid="635106544616378836">"手機儲存空間å³å°‡ä¸è¶³ã€‚"</string>
@@ -626,6 +656,7 @@
<string name="volume_ringtone" msgid="6885421406845734650">"鈴è²éŸ³é‡"</string>
<string name="volume_music" msgid="5421651157138628171">"媒體音é‡"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="9165984379394601533">"é€éŽè—牙播放"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="6158339745293431194">"å·²é¸å–éœéŸ³éˆ´è²"</string>
<string name="volume_call" msgid="3941680041282788711">"來電音é‡"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"è—牙通話音é‡"</string>
<string name="volume_alarm" msgid="1985191616042689100">"鬧é˜éŸ³é‡"</string>
@@ -669,7 +700,8 @@
<string name="usb_storage_stop_message" msgid="2390958966725232848">"關閉 USB 儲存è£ç½®ä¹‹å‰ï¼Œè«‹ç¢ºå®šå…ˆå¾ž USB Host å¸è¼‰ã€‚é¸å– [關閉] å³å¯é—œé–‰ USB 儲存è£ç½®ã€‚"</string>
<string name="usb_storage_stop_button_mount" msgid="1181858854166273345">"關閉"</string>
<string name="usb_storage_stop_button_unmount" msgid="3774611918660582898">"å–消"</string>
- <string name="usb_storage_stop_error_message" msgid="3917317248440072084">"關閉 USB 儲存è£ç½®æ™‚發生å•é¡Œã€‚請檢查確èªæ‚¨æ˜¯å¦å·²å¸è¼‰ USB Host,然後å†è©¦ä¸€æ¬¡ã€‚"</string>
+ <!-- no translation found for usb_storage_stop_error_message (3746037090369246731) -->
+ <skip />
<string name="extmedia_format_title" msgid="8663247929551095854">"å°‡ SD å¡æ ¼å¼åŒ–"</string>
<string name="extmedia_format_message" msgid="3621369962433523619">"確定è¦å°‡ SD å¡æ ¼å¼åŒ–嗎?該 SD å¡ä¸­çš„所有資料將會éºå¤±ã€‚"</string>
<string name="extmedia_format_button_format" msgid="4131064560127478695">"æ ¼å¼åŒ–"</string>
@@ -706,4 +738,23 @@
<string name="create_contact_using" msgid="4947405226788104538">"建立手機號碼為 <xliff:g id="NUMBER">%s</xliff:g>"\n"çš„è¯çµ¡äºº"</string>
<string name="accessibility_compound_button_selected" msgid="5612776946036285686">"已勾é¸"</string>
<string name="accessibility_compound_button_unselected" msgid="8864512895673924091">"未勾é¸"</string>
+ <!-- no translation found for grant_credentials_permission_message_desc (6883276587034335667) -->
+ <skip />
+ <!-- no translation found for grant_credentials_permission_message_with_authtokenlabel_desc (3159007601893584687) -->
+ <skip />
+ <string name="allow" msgid="7225948811296386551">"å…許"</string>
+ <string name="deny" msgid="2081879885755434506">"拒絕"</string>
+ <string name="permission_request_notification_title" msgid="5390555465778213840">"å·²è¦æ±‚權é™"</string>
+ <!-- no translation found for permission_request_notification_subtitle (21911977892390675) -->
+ <skip />
+ <!-- no translation found for input_method_binding_label (1283557179944992649) -->
+ <skip />
+ <!-- no translation found for sync_binding_label (3687969138375092423) -->
+ <skip />
+ <!-- no translation found for accessibility_binding_label (4148120742096474641) -->
+ <skip />
+ <!-- no translation found for wallpaper_binding_label (1240087844304687662) -->
+ <skip />
+ <!-- no translation found for chooser_wallpaper (7873476199295190279) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index fd78f83..1fcfe63 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -215,6 +215,9 @@
<attr name="windowIsFloating" format="boolean" />
<!-- Flag indicating whether this is a translucent window. -->
<attr name="windowIsTranslucent" format="boolean" />
+ <!-- Flag indicating that this window's background should be the
+ user's current wallpaper. -->
+ <attr name="windowShowWallpaper" format="boolean" />
<!-- This Drawable is overlaid over the foreground of the Window's content area, usually
to place a shadow below the title. -->
<attr name="windowContentOverlay" format="reference" />
@@ -358,7 +361,7 @@
<!-- Small inverse ProgressBar style. This is a small circular progress bar. -->
<attr name="progressBarStyleSmallInverse" format="reference" />
<!-- Large inverse ProgressBar style. This is a large circular progress bar. -->
- <attr name="progressBarStyleLargeInverse" format="reference" />
+ <attr name="progressBarStyleLargeInverse" format="reference" />
<!-- Default SeekBar style. -->
<attr name="seekBarStyle" format="reference" />
<!-- Default RatingBar style. -->
@@ -598,7 +601,7 @@
<flag name="time" value="0x00000024" />
</attr>
- <!-- Additional features you can enable in an IME associated with an editor,
+ <!-- Additional features you can enable in an IME associated with an editor
to improve the integration with your application. The constants
here correspond to those defined by
{@link android.view.inputmethod.EditorInfo#imeOptions}. -->
@@ -682,7 +685,7 @@
<attr name="y" format="dimension" />
<!-- Specifies how to place the content of an object, both
- on the x and y axis, within the object itself. -->
+ on the x- and y-axis, within the object itself. -->
<attr name="gravity">
<!-- Push object to the top of its container, not changing its size. -->
<flag name="top" value="0x30" />
@@ -738,7 +741,7 @@
<attr name="entries" format="reference" />
<!-- Standard gravity constant that a child can supply to its parent.
- Defines how to place the view, both its x and y axis, within its parent view group. -->
+ Defines how to place the view, both its x- and y-axis, within its parent view group. -->
<attr name="layout_gravity">
<!-- Push object to the top of its container, not changing its size. -->
<flag name="top" value="0x30" />
@@ -900,6 +903,7 @@
<attr name="windowFullscreen" />
<attr name="windowIsFloating" />
<attr name="windowIsTranslucent" />
+ <attr name="windowShowWallpaper" />
<attr name="windowAnimationStyle" />
<attr name="windowSoftInputMode" />
<attr name="windowDisablePreview" />
@@ -933,18 +937,89 @@
<attr name="windowShowAnimation" format="reference" />
<!-- The animation used when a window is going from VISIBLE to INVISIBLE. -->
<attr name="windowHideAnimation" format="reference" />
+
+ <!-- When opening a new activity, this is the animation that is
+ run on the next activity (which is entering the screen). -->
<attr name="activityOpenEnterAnimation" format="reference" />
+ <!-- When opening a new activity, this is the animation that is
+ run on the previous activity (which is exiting the screen). -->
<attr name="activityOpenExitAnimation" format="reference" />
+ <!-- When closing the current activity, this is the animation that is
+ run on the next activity (which is entering the screen). -->
<attr name="activityCloseEnterAnimation" format="reference" />
+ <!-- When closing the current activity, this is the animation that is
+ run on the current activity (which is exiting the screen). -->
<attr name="activityCloseExitAnimation" format="reference" />
+ <!-- When opening an activity in a new task, this is the animation that is
+ run on the activity of the new task (which is entering the screen). -->
<attr name="taskOpenEnterAnimation" format="reference" />
+ <!-- When opening an activity in a new task, this is the animation that is
+ run on the activity of the old task (which is exiting the screen). -->
<attr name="taskOpenExitAnimation" format="reference" />
+ <!-- When closing the last activity of a task, this is the animation that is
+ run on the activity of the next task (which is entering the screen). -->
<attr name="taskCloseEnterAnimation" format="reference" />
+ <!-- When opening an activity in a new task, this is the animation that is
+ run on the activity of the old task (which is exiting the screen). -->
<attr name="taskCloseExitAnimation" format="reference" />
+ <!-- When bringing an existing task to the foreground, this is the
+ animation that is run on the top activity of the task being brought
+ to the foreground (which is entering the screen). -->
<attr name="taskToFrontEnterAnimation" format="reference" />
+ <!-- When bringing an existing task to the foreground, this is the
+ animation that is run on the current foreground activity
+ (which is exiting the screen). -->
<attr name="taskToFrontExitAnimation" format="reference" />
+ <!-- When sending the current task to the background, this is the
+ animation that is run on the top activity of the task behind
+ it (which is entering the screen). -->
<attr name="taskToBackEnterAnimation" format="reference" />
+ <!-- When sending the current task to the background, this is the
+ animation that is run on the top activity of the current task
+ (which is exiting the screen). -->
<attr name="taskToBackExitAnimation" format="reference" />
+
+ <!-- When opening a new activity that shows the wallpaper, while
+ currently not showing the wallpaper, this is the animation that
+ is run on the new wallpaper activity (which is entering the screen). -->
+ <attr name="wallpaperOpenEnterAnimation" format="reference" />
+ <!-- When opening a new activity that shows the wallpaper, while
+ currently not showing the wallpaper, this is the animation that
+ is run on the current activity (which is exiting the screen). -->
+ <attr name="wallpaperOpenExitAnimation" format="reference" />
+ <!-- When opening a new activity that hides the wallpaper, while
+ currently showing the wallpaper, this is the animation that
+ is run on the new activity (which is entering the screen). -->
+ <attr name="wallpaperCloseEnterAnimation" format="reference" />
+ <!-- When opening a new activity that hides the wallpaper, while
+ currently showing the wallpaper, this is the animation that
+ is run on the old wallpaper activity (which is exiting the screen). -->
+ <attr name="wallpaperCloseExitAnimation" format="reference" />
+
+ <!-- When opening a new activity that is on top of the wallpaper
+ when the current activity is also on top of the wallpaper,
+ this is the animation that is run on the new activity
+ (which is entering the screen). The wallpaper remains
+ static behind the animation. -->
+ <attr name="wallpaperIntraOpenEnterAnimation" format="reference" />
+ <!-- When opening a new activity that is on top of the wallpaper
+ when the current activity is also on top of the wallpaper,
+ this is the animation that is run on the current activity
+ (which is exiting the screen). The wallpaper remains
+ static behind the animation. -->
+ <attr name="wallpaperIntraOpenExitAnimation" format="reference" />
+ <!-- When closing a foreround activity that is on top of the wallpaper
+ when the previous activity is also on top of the wallpaper,
+ this is the animation that is run on the previous activity
+ (which is entering the screen). The wallpaper remains
+ static behind the animation. -->
+ <attr name="wallpaperIntraCloseEnterAnimation" format="reference" />
+ <!-- When closing a foreround activity that is on top of the wallpaper
+ when the previous activity is also on top of the wallpaper,
+ this is the animation that is run on the current activity
+ (which is exiting the screen). The wallpaper remains
+ static behind the animation. -->
+ <attr name="wallpaperIntraCloseExitAnimation" format="reference" />
</declare-styleable>
<!-- ============================= -->
@@ -1817,7 +1892,7 @@
<attr name="minEms" format="integer" min="0" />
<!-- Makes the TextView be at least this many pixels wide -->
<attr name="minWidth" />
- <!-- Specifies how to align the text by the view's x and/or y axis
+ <!-- Specifies how to align the text by the view's x- and/or y-axis
when the text is smaller than the view. -->
<attr name="gravity" />
<!-- Whether the text is allowed to be wider than the view (and
@@ -2220,6 +2295,10 @@
of the states. If false, the size will vary based on the
current state. -->
<attr name="constantSize" format="boolean" />
+ <!-- Enables or disables dithering of the bitmap if the bitmap does not have the
+ same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
+ an RGB 565 screen.) -->
+ <attr name="dither" format="boolean" />
</declare-styleable>
<declare-styleable name="AnimationDrawable">
@@ -2344,7 +2423,7 @@
<attr name="pivotY" />
<attr name="drawable" />
</declare-styleable>
-
+
<declare-styleable name="InsetDrawable">
<attr name="visible" />
<attr name="drawable" />
@@ -2366,7 +2445,7 @@
<!-- Enables or disables dithering of the bitmap if the bitmap does not have the
same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
an RGB 565 screen.) -->
- <attr name="dither" format="boolean" />
+ <attr name="dither" />
<!-- Defines the gravity for the bitmap. The gravity indicates where to position
the drawable in its container if the bitmap is smaller than the container. -->
<attr name="gravity" />
@@ -2882,7 +2961,6 @@
results for "bo", it would not be queried again for "bob".
The default value is <code>false</code>. <i>Optional attribute.</i>. -->
<attr name="queryAfterZeroResults" format="boolean" />
-
<!-- If provided, this string will be used to describe the searchable item in the
searchable items settings within system search settings. <i>Optional
attribute.</i> -->
@@ -3328,6 +3406,67 @@
<attr name="configure" format="string" />
</declare-styleable>
+ <!-- =============================== -->
+ <!-- App package class attributes -->
+ <!-- =============================== -->
+
+ <!-- Use <code>wallpaper</code> as the root tag of the XML resource that
+ describes an
+ {@link android.service.wallpaper.WallpaperService}, which is
+ referenced from its
+ {@link android.service.wallpaper.WallpaperService#SERVICE_META_DATA}
+ meta-data entry. Described here are the attributes that can be
+ included in that tag.
+ @hide Live Wallpaper -->
+ <declare-styleable name="Wallpaper">
+ <!-- Component name of an activity that allows the user to modify
+ the current settings for this wallpaper. -->
+ <attr name="settingsActivity" />
+ </declare-styleable>
+
+ <!-- =============================== -->
+ <!-- Accounts package class attributes -->
+ <!-- =============================== -->
+
+ <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
+ describes an account authenticator.
+ -->
+ <declare-styleable name="AccountAuthenticator">
+ <!-- the account type this authenticator handles. -->
+ <attr name="accountType" format="string"/>
+ <!-- the user-visible name of the authenticator. -->
+ <attr name="label"/>
+ <!-- the icon of the authenticator. -->
+ <attr name="icon"/>
+ </declare-styleable>
+
+ <!-- =============================== -->
+ <!-- Accounts package class attributes -->
+ <!-- =============================== -->
+
+ <!-- Use <code>account-authenticator</code> as the root tag of the XML resource that
+ describes an account authenticator.
+ -->
+ <declare-styleable name="SyncAdapter">
+ <!-- the authority of a content provider. -->
+ <attr name="contentAuthority" format="string"/>
+ <attr name="accountType"/>
+ <attr name="userVisible" format="boolean"/>
+ <attr name="supportsUploading" format="boolean"/>
+ </declare-styleable>
+
+ <!-- =============================== -->
+ <!-- Contacts meta-data attributes -->
+ <!-- =============================== -->
+
+ <declare-styleable name="Icon">
+ <attr name="icon" />
+ <attr name="mimeType" />
+ </declare-styleable>
+
+ <declare-styleable name="IconDefault">
+ <attr name="icon" />
+ </declare-styleable>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 442357e..7aaf218 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -580,6 +580,15 @@
<!-- This is not the attribute you are looking for. -->
<attr name="allowBackup" format="boolean" />
+ <!-- Whether the application in question should be terminated after its
+ settings have been restored. The default is to do so. -->
+ <attr name="killAfterRestore" format="boolean" />
+
+ <!-- Whether the application needs to have its own Application subclass
+ active during restore. The default is to run restore with a minimal
+ Application class to avoid interference with application logic. -->
+ <attr name="restoreNeedsApplication" format="boolean" />
+
<!-- The <code>manifest</code> tag is the root of an
<code>AndroidManifest.xml</code> file,
describing the contents of an Android package (.apk) file. One
@@ -656,6 +665,8 @@
<attr name="testOnly" />
<attr name="backupAgent" />
<attr name="allowBackup" />
+ <attr name="killAfterRestore" />
+ <attr name="restoreNeedsApplication" />
</declare-styleable>
<!-- The <code>permission</code> tag declares a security permission that can be
@@ -781,6 +792,15 @@
represent the minor number. For example for GL 1.2 referring to
0x00000102, the actual value should be set as 0x00010002. -->
<attr name="glEsVersion" format="integer"/>
+ <!-- The name of the feature that is being used. -->
+ <attr name="name" />
+ <!-- Specify whether this feature is required for the application.
+ The default is true, meaning the application requires the
+ feature, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ not impose a restriction on where the application can be
+ installed. -->
+ <attr name="required" format="boolean" />
</declare-styleable>
<!-- The <code>uses-sdk</code> tag describes the SDK features that the
@@ -823,6 +843,14 @@
<declare-styleable name="AndroidManifestUsesLibrary" parent="AndroidManifestApplication">
<!-- Required name of the library you use. -->
<attr name="name" />
+ <!-- Specify whether this library is required for the application.
+ The default is true, meaning the application requires the
+ library, and does not want to be installed on devices that
+ don't support it. If you set this to false, then this will
+ allow the application to be installed even if the library
+ doesn't exist, and you will need to check for its presence
+ dynamically at runtime. -->
+ <attr name="required" />
</declare-styleable>
<!-- The <code>supports-screens</code> specifies the screen dimensions an
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 0fd6861..560796a 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -19,16 +19,17 @@
-->
<resources>
<drawable name="screen_background_light">#fff9f9f9</drawable>
- <drawable name="screen_background_dark">#ff1a1a1a</drawable>
+ <drawable name="screen_background_dark">#ff202020</drawable>
<drawable name="status_bar_closed_default_background">#ff000000</drawable>
<drawable name="status_bar_opened_default_background">#ff000000</drawable>
<drawable name="search_bar_default_color">#ff000000</drawable>
<drawable name="safe_mode_background">#60000000</drawable>
+ <drawable name="screen_background_dark_transparent">#80000000</drawable>
<color name="safe_mode_text">#80ffffff</color>
<color name="white">#ffffffff</color>
<color name="black">#ff000000</color>
<color name="transparent">#00000000</color>
- <color name="background_dark">#ff1a1a1a</color>
+ <color name="background_dark">#ff202020</color>
<color name="bright_foreground_dark">#ffffffff</color>
<color name="bright_foreground_dark_disabled">#80ffffff</color>
<color name="bright_foreground_dark_inverse">#ff000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7215685..abb575c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -35,7 +35,50 @@
<!-- The duration (in milliseconds) of a long animation. -->
<integer name="config_longAnimTime">300</integer>
- <!-- Flag indicating whether Last Name comes before First Name.
- This becomes true in Japan, for example.-->
- <bool name="config_lastname_comes_before_firstname">false</bool>
+ <!-- This string array should be overridden by the device to present a list of network attributes. This is used by the connectivity manager to decide which networks can coexist based on the hardward -->
+ <!-- An Array of "[type-name],[associated radio-name],[priority] -->
+ <string-array translatable="false" name="networkAttributes">
+ <item>"default,wifi,0"</item>
+ <item>"default,mobile,0"</item>
+ <item>"mms,mobile,1"</item>
+ <item>"supl,mobile,1"</item>
+ <item>"dun,mobile,1"</item>
+ <item>"hipri,mobile,2"</item>
+ </string-array>
+
+ <!-- This string array should be overridden by the device to present a list of radio attributes. This is used by the connectivity manager to decide which networks can coexist based on the hardware -->
+ <!-- An Array of "[radio-name],[priority] -->
+ <!-- [# simultaneous connection types]" -->
+ <string-array translatable="false" name="radioAttributes">
+ <item>"wifi,1,1"</item>
+ <item>"mobile,0,1"</item>
+ </string-array>
+
+ <!-- The number of degrees to rotate the display when the keyboard is open. -->
+ <integer name="config_lidOpenRotation">90</integer>
+
+ <!-- The number of degrees to rotate the display when the device is in a dock. -->
+ <integer name="config_dockedRotation">90</integer>
+
+ <!-- Flag indicating whether the keyguard should be bypassed when
+ the slider is open. This can be set or unset depending how easily
+ the slider can be opened (for example, in a pocket or purse). -->
+ <bool name="config_bypass_keyguard_if_slider_open">true</bool>
+
+ <!-- Vibrator pattern for feedback about a long screen/key press -->
+ <integer-array name="config_longPressVibePattern">
+ <item>0</item>
+ <item>1</item>
+ <item>20</item>
+ <item>21</item>
+ </integer-array>
+
+ <!-- Vibrator pattern for feedback about touching a virtual key -->
+ <integer-array name="config_virtualKeyVibePattern">
+ <item>0</item>
+ <item>10</item>
+ <item>20</item>
+ <item>30</item>
+ </integer-array>
+
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6461460..6a3538d 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -28,4 +28,10 @@
<dimen name="toast_y_offset">64dip</dimen>
<!-- Height of the status bar -->
<dimen name="status_bar_height">25dip</dimen>
+ <!-- Size of the fastscroll hint letter -->
+ <dimen name="fastscroll_overlay_size">104dp</dimen>
+ <!-- Width of the fastscroll thumb -->
+ <dimen name="fastscroll_thumb_width">64dp</dimen>
+ <!-- Height of the fastscroll thumb -->
+ <dimen name="fastscroll_thumb_height">52dp</dimen>
</resources>
diff --git a/core/res/res/values/donottranslate-names.xml b/core/res/res/values/donottranslate-names.xml
new file mode 100644
index 0000000..56ae47a
--- /dev/null
+++ b/core/res/res/values/donottranslate-names.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Various locale-specific string resources for Contacts -->
+ <string-array name="common_nicknames"></string-array>
+ <string name="common_name_prefixes"></string>
+ <string name="common_name_suffixes"></string>
+ <string name="common_last_name_prefixes"></string>
+ <string name="common_name_conjunctions"></string>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index c92cf51..8541c73 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -5,16 +5,16 @@
***************************************************************
IMPORTANT NOTE FOR ANYONE MODIFYING THIS FILE
READ THIS BEFORE YOU MAKE ANY CHANGES
-
+
This file defines the binary compatibility for resources. As such,
you must be very careful when making changes here, or you will
completely break backwards compatibility with old applications.
-
+
To avoid breaking compatibility, all new resources must be placed
at the end of the list of resources of the same type. Placing a resource
in the middle of type will cause all following resources to be
assigned new resource numbers, breaking compatibility.
-
+
***************************************************************
*************************************************************** -->
<resources>
@@ -22,7 +22,7 @@
<!-- We don't want to publish private symbols in android.R as part of the
SDK. Instead, put them here. -->
<private-symbols package="com.android.internal" />
-
+
<!-- AndroidManifest.xml attributes. -->
<eat-comment />
@@ -569,10 +569,10 @@
<public type="attr" name="lineSpacingExtra" id="0x01010217" />
<public type="attr" name="lineSpacingMultiplier" id="0x01010218" />
<public type="attr" name="listChoiceIndicatorSingle" id="0x01010219" />
- <public type="attr" name="listChoiceIndicatorMultiple" id="0x0101021a" />
+ <public type="attr" name="listChoiceIndicatorMultiple" id="0x0101021a" />
<public type="attr" name="versionCode" id="0x0101021b" />
<public type="attr" name="versionName" id="0x0101021c" />
-
+
<public type="id" name="background" id="0x01020000" />
<public type="id" name="checkbox" id="0x01020001" />
<public type="id" name="content" id="0x01020002" />
@@ -601,7 +601,7 @@
<public type="id" name="button1" id="0x01020019" />
<public type="id" name="button2" id="0x0102001a" />
<public type="id" name="button3" id="0x0102001b" />
-
+
<public type="style" name="Animation" id="0x01030000" />
<public type="style" name="Animation.Activity" id="0x01030001" />
<public type="style" name="Animation.Dialog" id="0x01030002" />
@@ -748,7 +748,7 @@
<public type="drawable" name="btn_plus" id="0x01080008" />
<public type="drawable" name="btn_radio" id="0x01080009" />
<public type="drawable" name="btn_star" id="0x0108000a" />
- <public type="drawable" name="btn_star_big_off" id="0x0108000b" />
+ <public type="drawable" name="btn_star_big_off" id="0x0108000b" />
<public type="drawable" name="btn_star_big_on" id="0x0108000c" />
<public type="drawable" name="button_onoff_indicator_on" id="0x0108000d" />
<public type="drawable" name="button_onoff_indicator_off" id="0x0108000e" />
@@ -916,7 +916,7 @@
<public type="layout" name="select_dialog_item" id="0x01090011" />
<public type="layout" name="select_dialog_singlechoice" id="0x01090012" />
<public type="layout" name="select_dialog_multichoice" id="0x01090013" />
-
+
<public type="anim" name="fade_in" id="0x010a0000" />
<public type="anim" name="fade_out" id="0x010a0001" />
<public type="anim" name="slide_in_left" id="0x010a0002" />
@@ -929,9 +929,9 @@
Resources added in version 2 of the platform.
=============================================================== -->
<eat-comment />
-
+
<public type="attr" name="marqueeRepeatLimit" id="0x0101021d" />
-
+
<!-- ===============================================================
Resources added in version 3 of the platform.
=============================================================== -->
@@ -1062,7 +1062,7 @@
<public type="id" name="stopSelectingText" id="0x01020029" />
<!-- Menu ID to perform a "add to dictionary" operation. -->
<public type="id" name="addToDictionary" id="0x0102002a" />
-
+
<public type="style" name="Theme.InputMethod" id="0x01030054" />
<public type="style" name="Theme.NoDisplay" id="0x01030055" />
<public type="style" name="Animation.InputMethod" id="0x01030056" />
@@ -1070,7 +1070,7 @@
<public type="style" name="ButtonBar" id="0x01030058" />
<public type="style" name="Theme.Panel" id="0x01030059" />
<public type="style" name="Theme.Light.Panel" id="0x0103005a" />
-
+
<public type="string" name="dialog_alert_title" id="0x01040014" />
<public type="string" name="VideoView_error_text_invalid_progressive_playback" id="0x01040015" />
@@ -1081,7 +1081,7 @@
<!-- Drawable to use as a background for a taller version of the titlebar -->
<public type="drawable" name="title_bar_tall" id="0x010800a6" />
-
+
<public type="integer" name="config_shortAnimTime" id="0x010e0000" />
<public type="integer" name="config_mediumAnimTime" id="0x010e0001" />
<public type="integer" name="config_longAnimTime" id="0x010e0002" />
@@ -1130,14 +1130,46 @@
<public type="style" name="Widget.ProgressBar.Inverse" id="0x0103005b" />
<public type="style" name="Widget.ProgressBar.Large.Inverse" id="0x0103005c" />
- <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" />
+ <public type="style" name="Widget.ProgressBar.Small.Inverse" id="0x0103005d" />
<public type="drawable" name="stat_sys_vp_phone_call" id="0x010800a7" />
<public type="drawable" name="stat_sys_vp_phone_call_on_hold" id="0x010800a8" />
-
+
<public type="anim" name="anticipate_interpolator" id="0x010a0007" />
<public type="anim" name="overshoot_interpolator" id="0x010a0008" />
<public type="anim" name="anticipate_overshoot_interpolator" id="0x010a0009" />
<public type="anim" name="bounce_interpolator" id="0x010a000a" />
<public type="anim" name="linear_interpolator" id="0x010a000b" />
+
+<!-- ===============================================================
+ Resources added in Eclair.
+ =============================================================== -->
+ <eat-comment />
+
+ <public type="attr" name="required" id="0x0101028e" />
+ <public type="attr" name="accountType" />
+ <public type="attr" name="contentAuthority" />
+ <public type="attr" name="userVisible" />
+ <public type="attr" name="windowShowWallpaper" />
+ <public type="attr" name="wallpaperOpenEnterAnimation" />
+ <public type="attr" name="wallpaperOpenExitAnimation" />
+ <public type="attr" name="wallpaperCloseEnterAnimation" />
+ <public type="attr" name="wallpaperCloseExitAnimation" />
+ <public type="attr" name="wallpaperIntraOpenEnterAnimation" />
+ <public type="attr" name="wallpaperIntraOpenExitAnimation" />
+ <public type="attr" name="wallpaperIntraCloseEnterAnimation" />
+ <public type="attr" name="wallpaperIntraCloseExitAnimation" />
+ <public type="attr" name="supportsUploading" />
+ <public type="attr" name="killAfterRestore" />
+ <public type="attr" name="restoreNeedsApplication" />
+
+ <public type="style" name="Theme.Wallpaper" />
+ <public type="style" name="Theme.Wallpaper.NoTitleBar" />
+ <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" />
+
+ <!-- Semi-transparent background that can be used when placing a dark
+ themed UI on top of some arbitrary background (such as the
+ wallpaper). This darkens the background sufficiently that the UI
+ can be seen. -->
+ <public type="drawable" name="screen_background_dark_transparent" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index aaaebbb..0902c21 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -134,7 +134,7 @@
<string name="RestrictedOnNormal">Voice/SMS service is blocked.</string>
<!-- Displayed to tell the user that all voice service is blocked by access control. -->
<string name="RestrictedOnAll">All voice/SMS services are blocked.</string>
-
+
<!-- Mappings between TS 27.007 +CFCC/+CLCK "service classes" and human-readable strings--> <skip />
<!-- Example: Service was enabled for: Voice, Data -->
<string name="serviceClassVoice">Voice</string>
@@ -229,6 +229,13 @@
<!-- Displayed when a request failed because there are too many requests right now. -->
<string name="httpErrorTooManyRequests">Too many requests are being processed. Try again later.</string>
+ <!-- Account notifications --> <skip />
+ <!-- A notification is shown when the AccountManager is unable to
+ supply an auth token without prompting the user to re-enter the
+ password. This is the text that will scroll through the
+ notification bar (will be seen by the user as he uses another application). -->
+ <string name="notification_title">Sign-in error</string>
+
<!-- Sync notifications --> <skip />
<!-- A notification is shown when there is a sync error. This is the text that will scroll through the notification bar (will be seen by the user as he uses another application). -->
<string name="contentServiceSync">Sync</string>
@@ -299,7 +306,7 @@
<!-- Label for the Android system components when they are shown to the user. -->
<string name="android_system_label">Android System</string>
-
+
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_costMoney">Services that cost you money</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
@@ -542,7 +549,12 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_backup">control system backup and restore</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_backup">Allows the application to control the system's backup and restore mechanism. Not for use by normal applications.</string>
+ <string name="permdesc_backup">Allows the application to control the system\'s backup and restore mechanism. Not for use by normal applications.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_backup_data">back up and restore the application\'s data</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_backup_data">Allows the application to participate in the system\'s backup and restore mechanism.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_internalSystemWindow">display unauthorized windows</string>
@@ -592,6 +604,12 @@
interface of an input method. Should never be needed for normal applications.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_bindWallpaper">bind to a wallpaper</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_bindWallpaper">Allows the holder to bind to the top-level
+ interface of a wallpaper. Should never be needed for normal applications.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_setOrientation">change screen orientation</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_setOrientation">Allows an application to change
@@ -689,7 +707,7 @@
<string name="permlab_writeSecureSettings">modify secure system settings</string>
<string name="permdesc_writeSecureSettings">Allows an application to modify the
- system's secure settings data. Not for use by normal applications.</string>
+ system\'s secure settings data. Not for use by normal applications.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_writeGservices">modify the Google services map</string>
@@ -800,7 +818,7 @@
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_readFrameBuffer">read frame buffer</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_readFrameBuffer">Allows application to use
+ <string name="permdesc_readFrameBuffer">Allows application to
read the content of the frame buffer.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -880,6 +898,12 @@
services.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_performCdmaProvisioning">directly start CDMA phone setup</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_performCdmaProvisioning">Allows the application to start CDMA provisioning.
+ Malicious applications may unnecessarily start CDMA provisioning</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_locationUpdates">control location update notifications</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_locationUpdates">Allows enabling/disabling location
@@ -961,12 +985,40 @@
the phone\'s time zone.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_accountManagerService">act as the AccountManagerService</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_accountManagerService">Allows an
+ application to make calls to AccountAuthenticators</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_getAccounts">discover known accounts</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_getAccounts">Allows an application to get
the list of accounts known by the phone.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_authenticateAccounts">act as an account authenticator</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_authenticateAccounts">Allows an application
+ to use the account authenticator capabilities of the
+ AccountManager, including creating accounts and getting and
+ setting their passwords.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_manageAccounts">manage the accounts list</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_manageAccounts">Allows an application to
+ perform operations like adding, and removing accounts and deleting
+ their password.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_useCredentials">use the authentication
+ credentials of an account</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_useCredentials">Allows an application to
+ request authentication tokens.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessNetworkState">view network state</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessNetworkState">Allows an application to view
@@ -1109,9 +1161,6 @@
<item>Custom</item>
</string-array>
- <!-- String which means the type "mobile phone". -->
- <string name="mobileEmailTypeName">Mobile</string>
-
<!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
<!-- Postal address types from android.provider.Contacts. This could be used when adding a new address for a contact, for example. -->
<string-array name="postalAddressTypes">
@@ -1251,7 +1300,11 @@
their lock gesture -->
<string name="lockscreen_forgot_pattern_button_text">Forgot pattern?</string>
- <!-- Title of the unlock screen that uses your Google login and password -->
+ <!-- Title of the unlock screen that uses your Google login and password when the user hit
+ the 'forgot pattern' button.-->
+ <string name="lockscreen_glogin_forgot_pattern">Account unlock</string>
+ <!-- Title of the unlock screen that uses your Google login and password when the user attempted
+ too many patterns and we are forcing them to use their account instead. -->
<string name="lockscreen_glogin_too_many_attempts">Too many pattern attempts!</string>
<!-- In the unlock screen, message telling the user that they need to use their Google login and password to unlock the phone -->
<string name="lockscreen_glogin_instructions">To unlock, sign in with your Google account</string>
@@ -1264,6 +1317,9 @@
<!-- Displayed to the user when unlocking the phone with a username and password fails. -->
<string name="lockscreen_glogin_invalid_input">Invalid username or password.</string>
+ <!-- Displayed in a progress dialog while a username and password are being checked. -->
+ <string name="lockscreen_glogin_checking_password">Checking...</string>
+
<!-- A format string for 12-hour time of day, just the hour, not the minute, with lower-case "am" or "pm" (example: "3pm"). -->
<string name="hour_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="pm">%P</xliff:g>"</string>
@@ -1307,7 +1363,7 @@
<!-- When the battery is low, this is the label of the button to go to the
power usage activity to find out what drained the battery. -->
- <string name="battery_low_why">Why?</string>
+ <string name="battery_low_why">Battery use</string>
<!-- Title of the alert when something went wrong in the factory test. -->
<string name="factorytest_failed">Factory test failed</string>
@@ -1322,7 +1378,7 @@
<!-- Do not translate. WebView User Agent string -->
<string name="web_user_agent"><xliff:g id="x">Mozilla/5.0 (Linux; U; Android %s)
- AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1</xliff:g></string>
+ AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17</xliff:g></string>
<!-- Title for a JavaScript dialog. "The page at <url of current page> says:" -->
<string name="js_dialog_title">The page at \'<xliff:g id="title">%s</xliff:g>\' says:</string>
@@ -1378,8 +1434,8 @@
<string name="menu_delete_shortcut_label">delete</string>
<!-- Strings used for search bar --><skip />
-
- <!-- This is the default button label in the system-wide search UI.
+
+ <!-- This is the default button label in the system-wide search UI.
It is also used by the home screen's search "widget". It should be short -->
<string name="search_go">Search</string>
@@ -1435,7 +1491,7 @@
<item quantity="one">tomorrow</item>
<item quantity="other">in <xliff:g id="count">%d</xliff:g> days</item>
</plurals>
-
+
<!-- This is used to express that something occurred some number of abbreviated seconds in the past (e.g., 5 secs ago). -->
<plurals name="abbrev_num_seconds_ago">
<item quantity="one">1 sec ago</item>
@@ -1485,11 +1541,11 @@
</plurals>
<!-- String used to display the date. Preposition for date display ("on May 29") -->
- <string name="preposition_for_date">on %s</string>
+ <string name="preposition_for_date">on <xliff:g id="date" example="May 29">%s</xliff:g></string>
<!-- String used to display the date. Preposition for time display ("at 2:33am") -->
- <string name="preposition_for_time">at %s</string>
+ <string name="preposition_for_time">at <xliff:g id="time" example="2:33 am">%s</xliff:g></string>
<!-- String used to display the date. Preposition for year display ("in 2008") -->
- <string name="preposition_for_year">in %s</string>
+ <string name="preposition_for_year">in <xliff:g id="year" example="2003">%s</xliff:g></string>
<!-- Appened to express the value is this unit of time: singular day -->
<string name="day">day</string>
@@ -1598,11 +1654,11 @@
<string name="copyUrl">Copy URL</string>
<!-- EditText context menu -->
- <string name="inputMethod">Input Method</string>
+ <string name="inputMethod">Input method</string>
<!-- Item on EditText context menu, used to add a word to the
input method dictionary. -->
- <string name="addToDictionary">"Add \"%s\" to dictionary</string>
+ <string name="addToDictionary">"Add \"<xliff:g id="word" example="rickroll">%s</xliff:g>\" to dictionary</string>
<!-- Title for EditText context menu -->
<string name="editTextMenuTitle">Edit text</string>
@@ -1677,6 +1733,8 @@
<string name="volume_music">Media volume</string>
<!-- Hint shown in the volume toast to inform the user that the media audio is playing through Bluetooth. -->
<string name="volume_music_hint_playing_through_bluetooth">Playing through Bluetooth</string>
+ <!-- Hint shown in the volume toast to inform the user that the current ringtone is the silent ringtone. -->
+ <string name="volume_music_hint_silent_ringtone_selected">Silent ringtone selected</string>
<!-- Title of the dialog where the user is adjusting the phone call volume -->
<string name="volume_call">In-call volume</string>
<!-- Title of the dialog where the user is adjusting the phone call volume when connected on bluetooth-->
@@ -1754,7 +1812,7 @@
<string name="usb_storage_button_mount">Mount</string>
<!-- See USB_STORAGE. This is the button text to ignore the plugging in of the phone.. -->
<string name="usb_storage_button_unmount">Don\'t mount</string>
- <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. -->
+ <!-- See USB_STORAGE_DIALOG. If there was an error mounting, this is the text. -->
<string name="usb_storage_error_message">There is a problem using your SD card for USB storage.</string>
<!-- USB_STORAGE: When the user connects the phone to a computer via USB, we show a notification asking if he wants to share files across. This is the title -->
<string name="usb_storage_notification_title">USB connected</string>
@@ -1775,8 +1833,8 @@
<string name="usb_storage_stop_button_mount">Turn Off</string>
<!-- See USB_STORAGE_STOP. This is the button text to cancel stoping usb storage. -->
<string name="usb_storage_stop_button_unmount">Cancel</string>
- <!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. -->
- <string name="usb_storage_stop_error_message">We've encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
+ <!-- See USB_STORAGE_STOP_DIALOG. If there was an error stopping, this is the text. -->
+ <string name="usb_storage_stop_error_message">We\'ve encountered a problem turning off USB storage. Check to make sure you have unmounted the USB host, then try again.</string>
<!-- External media format dialog strings -->
<!-- This is the label for the activity, and should never be visible to the user. -->
@@ -1790,8 +1848,8 @@
<!-- Title of notification shown when ADB is actively connected to the phone. -->
<string name="adb_active_notification_title">USB debugging connected</string>
<!-- Message of notification shown when ADB is actively connected to the phone. -->
- <string name="adb_active_notification_message">A computer is connected to your phone.</string>
-
+ <string name="adb_active_notification_message">Select to disable USB debugging.</string>
+
<!-- Used to replace %s in urls retreived from the signin server with locales. For Some -->
<!-- devices we don't support all the locales we ship to and need to replace the '%s' with a -->
<!-- locale string based on mcc values. By default (0-length string) we don't replace the %s -->
@@ -1800,11 +1858,11 @@
<string name="locale_replacement">""</string>
<!-- Title of the pop-up dialog in which the user switches input method components. -->
- <string name="select_input_method">Select Input Method</string>
-
+ <string name="select_input_method">Select input method</string>
+
<string name="fast_scroll_alphabet">\u0020ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
<string name="fast_scroll_numeric_alphabet">\u00200123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ</string>
-
+
<string name="candidates_style"><u>candidates</u></string>
<!-- External media notification strings -->
@@ -1851,23 +1909,23 @@
<!-- Long label for a button on a full-screen input method for the "Go" action. -->
<string name="ime_action_go">Go</string>
-
+
<!-- Long label for a button on a full-screen input method for the "Search" action. -->
<string name="ime_action_search">Search</string>
-
+
<!-- Long label for a button on a full-screen input method for the "Send" action. -->
<string name="ime_action_send">Send</string>
-
+
<!-- Long label for a button on a full-screen input method for the "Next" action. -->
<string name="ime_action_next">Next</string>
-
+
<!-- Long label for a button on a full-screen input method for the "Done" action. -->
<string name="ime_action_done">Done</string>
-
+
<!-- Long label for a button on a full-screen input method for an unknown action. -->
<string name="ime_action_default">Execute</string>
-
- <!-- Strings for search suggestions. These are going here because they are referenced by both
+
+ <!-- Strings for search suggestions. These are going here because they are referenced by both
ContactsProvider and GoogleContactsProvider -->
<skip />
@@ -1878,14 +1936,46 @@
<!-- This string appears (on two lines) when you type a number into contacts search, to let you create a contact whose phone number is the number you typed. The first line will be in bigger type than the second. -->
<string name="create_contact_using">Create contact\nusing <xliff:g id="number" example="555">%s</xliff:g></string>
- <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs. This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array -->
- <string-array translatable="false" name="carrier_locales">
+ <!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale,wifi-channel sets. This is used at startup to set system defaults by checking the system property ro.carrier for the carrier-id and searching through this array -->
+ <!-- An Array of [[Carrier-ID] -->
+ <!-- [default-locale] -->
+ <!-- [default-wifi-allowed-channels]] -->
+ <string-array translatable="false" name="carrier_properties">
</string-array>
- <!-- Title for the selected state of a CompoundButton. -->
- <string name="accessibility_compound_button_selected">checked</string>
-
- <!-- Title for the unselected state of a CompoundButton. -->
- <string name="accessibility_compound_button_unselected">not checked</string>
+ <!-- Title for the selected state of a CompoundButton. -->
+ <string name="accessibility_compound_button_selected">checked</string>
+
+ <!-- Title for the unselected state of a CompoundButton. -->
+ <string name="accessibility_compound_button_unselected">not checked</string>
+
+ <string name="grant_credentials_permission_message_desc">The
+ listed applications are requesting permission to access the login credentials for account <xliff:g id="account" example="foo@gmail.com">%1$s</xliff:g> from
+ <xliff:g id="application" example="Google Apps">%2$s</xliff:g>. Do you wish to grant this permission? If so, your answer will be remembered and you will not be prompted
+ again.</string>
+
+ <string name="grant_credentials_permission_message_with_authtokenlabel_desc">The
+ listed applications are requesting permission to access the <xliff:g id="type" example="Contacts">%1$s</xliff:g> login credentials for account <xliff:g id="account" example="foo@gmail.com">%2$s</xliff:g> from
+ <xliff:g id="application" example="Google Apps">%3$s</xliff:g>. Do you wish to grant this permission? If so, your answer will be remembered and you will not be prompted
+ again.</string>
+
+ <string name="allow">Allow</string>
+ <string name="deny">Deny</string>
+ <string name="permission_request_notification_title">Permission Requested</string>
+ <string name="permission_request_notification_subtitle">for account <xliff:g id="account" example="foo@gmail.com">%s</xliff:g></string>
+
+ <!-- Label to show for a service that is running because it is an input method. -->
+ <string name="input_method_binding_label">Input method</string>
+ <!-- Label to show for a service that is running because it is a sync adapter. -->
+ <string name="sync_binding_label">Sync</string>
+ <!-- Label to show for a service that is running because it is an accessibility module. -->
+ <string name="accessibility_binding_label">Accessibility</string>
+ <!-- Label to show for a service that is running because it is a wallpaper. -->
+ <string name="wallpaper_binding_label">Wallpaper</string>
+ <!-- Dialog title for user to select a different wallpaper from service list -->
+ <string name="chooser_wallpaper">Change wallpaper</string>
+
+ <!-- Do Not Translate: Alternate eri.xml -->
+ <string name="alternate_eri_file">/data/eri.xml</string>
</resources>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8eda12e..a2ceb8f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -49,15 +49,15 @@
<item name="centerMedium">@android:drawable/popup_center_medium</item>
</style>
- <!-- Animations -->
+ <!-- Base style for animations. This style specifies no animations. -->
<style name="Animation" />
<!-- Standard animations for a full-screen window or activity. -->
<style name="Animation.Activity">
- <item name="activityOpenEnterAnimation">@anim/task_open_enter</item>
- <item name="activityOpenExitAnimation">@anim/task_open_exit</item>
- <item name="activityCloseEnterAnimation">@anim/task_close_enter</item>
- <item name="activityCloseExitAnimation">@anim/task_close_exit</item>
+ <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
+ <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
+ <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
+ <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
<item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
<item name="taskOpenExitAnimation">@anim/task_open_exit</item>
<item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
@@ -66,6 +66,14 @@
<item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
<item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
<item name="taskToBackExitAnimation">@anim/task_close_exit</item>
+ <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
+ <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
+ <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
+ <item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
+ <item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
+ <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
+ <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
+ <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
</style>
<!-- Standard animations for a non-full-screen window or activity. -->
@@ -80,8 +88,15 @@
<item name="windowExitAnimation">@anim/status_bar_exit</item>
</style>
- <!-- Standard animations for a translucent window or activity. -->
+ <!-- Standard animations for a translucent window or activity. This
+ style is <em>not<em> used by default for the translucent theme
+ (since translucent activities are a special case that have no
+ clear UI paradigm), but you can make your own specialized theme
+ with this animation style if you would like to have the standard
+ platform transition animation. -->
<style name="Animation.Translucent">
+ <item name="windowEnterAnimation">@anim/translucent_enter</item>
+ <item name="windowExitAnimation">@anim/translucent_exit</item>
</style>
<!-- Standard animations for a non-full-screen window or activity. -->
@@ -130,8 +145,7 @@
<item name="windowExitAnimation">@anim/slide_out_down</item>
</style>
- <!-- Window animations that are applied to input method overlay windows.
- {@hide Pending API council approval} -->
+ <!-- Window animations that are applied to input method overlay windows. -->
<style name="Animation.InputMethod">
<item name="windowEnterAnimation">@anim/input_method_enter</item>
<item name="windowExitAnimation">@anim/input_method_exit</item>
@@ -151,13 +165,18 @@
<item name="windowExitAnimation">@anim/search_bar_exit</item>
</style>
- <!-- Window animations that are applied to the zoom buttons overlay window.
- {@hide Pending API council approval} -->
+ <!-- Window animations that are applied to the zoom buttons overlay window. -->
<style name="Animation.ZoomButtons">
<item name="windowEnterAnimation">@anim/fade_in</item>
<item name="windowExitAnimation">@anim/fade_out</item>
</style>
+ <!-- Standard animations for wallpapers. -->
+ <style name="Animation.Wallpaper">
+ <item name="windowEnterAnimation">@anim/wallpaper_enter</item>
+ <item name="windowExitAnimation">@anim/wallpaper_exit</item>
+ </style>
+
<!-- Status Bar Styles -->
<style name="TextAppearance.StatusBarTitle">
@@ -337,7 +356,7 @@
</style>
<style name="Widget.TextView.ListSeparator">
- <item name="android:background">@android:drawable/dark_header</item>
+ <item name="android:background">@android:drawable/dark_header_dither</item>
<item name="android:layout_width">fill_parent</item>
<item name="android:layout_height">25dip</item>
<item name="android:textStyle">bold</item>
@@ -349,7 +368,7 @@
<style name="Widget.TextView.ListSeparator.White">
<item name="android:textColor">?textColorPrimaryInverse</item>
- <item name="android:background">@android:drawable/light_header</item>
+ <item name="android:background">@android:drawable/light_header_dither</item>
</style>
<style name="Widget.EditText">
@@ -659,12 +678,12 @@
</style>
<style name="Preference.PreferenceScreen">
- <item name="android:widgetLayout">@android:layout/preferences</item>
</style>
<style name="Preference.DialogPreference">
<item name="android:positiveButtonText">@android:string/ok</item>
<item name="android:negativeButtonText">@android:string/cancel</item>
+ <item name="android:widgetLayout">@android:layout/preference_dialog</item>
</style>
<style name="Preference.DialogPreference.YesNoPreference">
@@ -680,6 +699,7 @@
<item name="android:ringtoneType">ringtone</item>
<item name="android:showSilent">true</item>
<item name="android:showDefault">true</item>
+ <item name="android:widgetLayout">@android:layout/preference_dialog</item>
</style>
<!-- Other Misc Styles -->
@@ -687,8 +707,8 @@
<style name="MediaButton">
<item name="android:background">@android:drawable/media_button_background</item>
- <item name="android:layout_width">71px</item>
- <item name="android:layout_height">52px</item>
+ <item name="android:layout_width">71dip</item>
+ <item name="android:layout_height">52dip</item>
</style>
<style name="MediaButton.Previous">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index e3fffb7..3b9590d 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -112,6 +112,7 @@
<item name="windowFullscreen">false</item>
<item name="windowIsFloating">false</item>
<item name="windowContentOverlay">@android:drawable/title_bar_shadow</item>
+ <item name="windowShowWallpaper">false</item>
<item name="windowTitleStyle">@android:style/WindowTitle</item>
<item name="windowTitleSize">25dip</item>
<item name="windowTitleBackgroundStyle">@android:style/WindowTitleBackground</item>
@@ -280,25 +281,47 @@
<item name="android:windowContentOverlay">@null</item>
</style>
+ <!-- Default theme for windows that want to have the user's selected
+ wallpaper appear behind them. -->
+ <style name="Theme.Wallpaper">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowShowWallpaper">true</item>
+ </style>
+
+ <!-- Variant of the translucent theme with no title bar -->
+ <style name="Theme.Wallpaper.NoTitleBar">
+ <item name="android:windowNoTitle">true</item>
+ </style>
+
+ <!-- Variant of the translucent theme that has no title bar and
+ fills the entire screen -->
+ <style name="Theme.Wallpaper.NoTitleBar.Fullscreen">
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ </style>
+
<!-- Default theme for translucent activities, that is windows that allow you
to see through them to the windows behind. This sets up the translucent
flag and appropriate animations for your windows. -->
<style name="Theme.Translucent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
- <item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
+ <!-- Note that we use the base animation style here (that is no
+ animations) because we really have no idea how this kind of
+ activity will be used. -->
+ <item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>
<!-- Variant of the translucent theme with no title bar -->
<style name="Theme.Translucent.NoTitleBar">
<item name="android:windowNoTitle">true</item>
+ <item name="android:windowContentOverlay">@null</item>
</style>
<!-- Variant of the translucent theme that has no title bar and
fills the entire screen -->
<style name="Theme.Translucent.NoTitleBar.Fullscreen">
<item name="android:windowFullscreen">true</item>
- <item name="android:windowContentOverlay">@null</item>
</style>
<!-- Default theme for activities that don't actually display a UI; that
diff --git a/data/etc/android.hardware.camera.autofocus.xml b/data/etc/android.hardware.camera.autofocus.xml
new file mode 100644
index 0000000..d6e2b90
--- /dev/null
+++ b/data/etc/android.hardware.camera.autofocus.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- This is the standard set of features for an auto-focus camera. -->
+<permissions>
+ <feature name="android.hardware.camera" />
+ <feature name="android.hardware.camera.autofocus" />
+</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index a3579c7..7322e6c 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -133,6 +133,7 @@
<assign-permission name="android.permission.READ_FRAME_BUFFER" uid="shell" />
<assign-permission name="android.permission.DEVICE_POWER" uid="shell" />
<assign-permission name="android.permission.INSTALL_LOCATION_PROVIDER" uid="shell" />
+ <assign-permission name="android.permission.BACKUP" uid="shell" />
<assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />
<assign-permission name="android.permission.ACCESS_DRM" uid="media" />
@@ -145,7 +146,7 @@
<library name="android.test.runner"
file="/system/framework/android.test.runner.jar" />
- <library name="com.android.im.plugin"
- file="/system/framework/com.android.im.plugin.jar"/>
+ <library name="javax.obex"
+ file="/system/framework/javax.obex.jar"/>
</permissions>
diff --git a/docs/html/guide/appendix/faq/commontasks.jd b/docs/html/guide/appendix/faq/commontasks.jd
index 0f89e75..e88a867 100644
--- a/docs/html/guide/appendix/faq/commontasks.jd
+++ b/docs/html/guide/appendix/faq/commontasks.jd
@@ -56,7 +56,7 @@ href="{@docRoot}guide/topics/fundamentals.html">Application Fundamentals</a>
to understand the basics of how an Android application works.</p>
<p>You should also take a look at the ApiDemos application and the other sample
-applications included in the SDK, in the <code>&lt;sdk&gt;/samples/
+applications included in the SDK, in the <code>&lt;sdk&gt;/samples/</code>
folder in the SDK.</p>
<p>Finally, a great way to started with Android development in Eclipse is to
@@ -281,6 +281,15 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data){
<pre>//Hide the title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
</pre>
+<p>A better way to achieve the same end is to specify a theme in your Android
+Manifest file:</p>
+<pre>&lt;application android:icon="@drawable/icon" android:theme="@android:style/Theme.NoTitleBar"&gt;
+</pre>
+<p>This is preferable because it tells the system not to show a title bar while
+your application is starting up. With the explicit method call, your application
+will have a title bar visible to the user until <code>onCreate</code> runs.</p>
+<p>(Note that this can be applied to either the <code>&lt;application&gt;</code>
+tag or to individual <code>&lt;activity&gt;</code> tags.)</p>
<a name="localhostalias" id="localhostalias"></a><h2>Referring to localhost from the emulated environment</h2>
<p>
If you need to refer to your host computer's <em>localhost</em>, such as when you
@@ -427,7 +436,7 @@ user receiving new e-mail.</p>
<td>Activity</td>
<td>By setting the theme of an activity to
{@link android.R.style#Theme_Dialog
- android:theme=&quot;android:style/Theme.Dialog&quot;},
+ android:theme=&quot;&#064;android:style/Theme.Dialog&quot;},
your activity will take on
the appearance of a normal dialog, floating on top of whatever was
underneath it. You usually set the theme through the
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 5a2a751..9dea053 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -134,7 +134,7 @@ would on the emulator. There are just a few things to do before you can start.</
<code>SUBSYSTEM=="usb_device", SYSFS{idVendor}=="0bb4", MODE="0666"</code></p>
</li>
<li>Now execute:<br/>
- <code>chmod a+rx /etc/udev/rules.d/51-android.rules</code>
+ <code>chmod a+r /etc/udev/rules.d/51-android.rules</code>
</li>
</ol>
diff --git a/docs/html/guide/developing/tools/adb.jd b/docs/html/guide/developing/tools/adb.jd
index b111047..e8c726f 100644
--- a/docs/html/guide/developing/tools/adb.jd
+++ b/docs/html/guide/developing/tools/adb.jd
@@ -313,7 +313,7 @@ emulator-5558&nbsp;&nbsp;device</pre>
<li><code>&lt;tty&gt;</code> &mdash; the tty for PPP stream. For example <code>dev:/dev/omap_csmi_ttyl</code>. </li>
<li><code>[parm]... </code> &mdash zero or more PPP/PPPD options, such as <code>defaultroute</code>, <code>local</code>, <code>notty</code>, etc.</li></ul>
-<p>Note that you should not automatically start a PDP connection. </p></td>
+<p>Note that you should not automatically start a PPP connection. </p></td>
<td></td>
</tr>
diff --git a/docs/html/guide/developing/tools/aidl.jd b/docs/html/guide/developing/tools/aidl.jd
index f370a80..abfa8b1 100644
--- a/docs/html/guide/developing/tools/aidl.jd
+++ b/docs/html/guide/developing/tools/aidl.jd
@@ -194,7 +194,6 @@ started.</p>
<li>Make your class implement the {@link android.os.Parcelable} interface.</li>
<li>Implement the method <code>public void writeToParcel(Parcel out)</code> that takes the
current state of the object and writes it to a parcel.</li>
-<li>Implement the method <code>public void readFromParcel(Parcel in)</code> that reads the
value in a parcel into your object.</li>
<li>Add a static field called <code>CREATOR</code> to your class which is an object implementing
the {@link android.os.Parcelable.Creator Parcelable.Creator} interface.</li>
diff --git a/docs/html/guide/developing/tools/ddms.jd b/docs/html/guide/developing/tools/ddms.jd
index fa04216..f55940d 100644
--- a/docs/html/guide/developing/tools/ddms.jd
+++ b/docs/html/guide/developing/tools/ddms.jd
@@ -1,7 +1,7 @@
-page.title=Using Dalvik Debug Monitor Service (DDMS)
+page.title=Using the Dalvik Debug Monitor
@jd:body
-<p>Android ships with a debugging tool called the Dalvik Debug Monitor Service (DDMS),
+<p>Android ships with a debugging tool called the Dalvik Debug Monitor Server (DDMS),
which provides port-forwarding services, screen capture on the device, thread
and heap information on the device, logcat, process, and radio state information,
incoming call and SMS spoofing, location data spoofing, and more. This page
@@ -106,7 +106,7 @@ and some pretty cool tools.</p>
</ul>
</li>
<li> <strong>utime</strong> - cumulative time spent executing user code, in &quot;jiffies&quot; (usually
- 10ms). Only available under Linux. </li>
+ 10ms). </li>
<li> <strong>stime</strong> - cumulative time spent executing system code, in &quot;jiffies&quot; (usually
10ms). </li>
<li> <strong>Name</strong> - the name of the thread</li>
@@ -214,14 +214,15 @@ the emulator from command line, be sure to mount the sdcard again.)</p>
<h2 id="screen-capture">Screen Capture</h2>
<p>You can capture screen images on the device or emulator by selecting <strong>Device</strong>
- &gt; <strong>Screen capture...</strong> in the menu bar, or press CTRL-S.</p>
+ &gt; <strong>Screen capture...</strong> in the menu bar, or press CTRL-S.
+ Be sure to select a device first.</p>
<h2 id="exploring-processes">Exploring Processes</h2>
<p>You can see the output of <code>ps -x</code> for a specific VM by selecting <strong>Device</strong>
&gt; <strong>Show process status</strong>... in the menu bar.</p>
<h2 id="cause-a-gc-to-occur">Cause a GC to Occur</h2>
-<p>Cause garbage collection to occury by pressing the trash can button on the toolbar. </p>
+<p>Cause garbage collection to occur in the selected application by pressing the trash can button on the toolbar. </p>
<h2 id="running-dumpsys-and-dumpstate">Running Dumpsys and Dumpstate on the Device (logcat)<a name="logcat" id="logcat"></a> </h2>
<ul>
@@ -239,7 +240,7 @@ the emulator from command line, be sure to mount the sdcard again.)</p>
<h2 id="stop-a-vitrual-machine">Stop a Virtual Machine </h2>
<p>You can stop a virtual machine by selecting <strong>Actions</strong> &gt; <strong>Halt
-VM</strong>. Pressing this button causes the VM to call <code>System.exit(1)</code>.</p>
+VM</strong>. Pressing this button causes the VM to call <code>Runtime.halt(1)</code>.</p>
<h2 id="known-issues" style="color:#FF0000">Known issues with DDMS </h2>
<p>DDMS has the following known limitations:</p>
diff --git a/docs/html/guide/topics/manifest/manifest-element.jd b/docs/html/guide/topics/manifest/manifest-element.jd
index a9d1090..48e598a 100644
--- a/docs/html/guide/topics/manifest/manifest-element.jd
+++ b/docs/html/guide/topics/manifest/manifest-element.jd
@@ -44,8 +44,11 @@ to "{@code http://schemas.android.com/apk/res/android}".</dd>
<dt><a name="package"></a>{@code package}</dt>
<dd>A full Java package name for the application. The name should
-be unique. For example, applications published by Google could have
-names in the form <code>com.google.app.<i>application_name</i></code>.
+be unique. The name may contain uppercase or lowercase letters ('A'
+through 'Z'), numbers, and underscores ('_'). However, individual
+package name parts may only start with letters. For example, applications
+published by Google could have names in the form
+<code>com.google.app.<i>application_name</i></code>.
<p>
The package name serves as a unique identifier for the application.
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index c8bed24..f60a7be 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,6 +18,7 @@ package android.graphics;
import android.content.res.AssetManager;
import android.content.res.Resources;
+import android.os.MemoryFile;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -72,7 +73,7 @@ public class BitmapFactory {
public Bitmap.Config inPreferredConfig;
/**
- * If dither is true, the decoder will atttempt to dither the decoded
+ * If dither is true, the decoder will attempt to dither the decoded
* image.
*/
public boolean inDither;
@@ -336,19 +337,26 @@ public class BitmapFactory {
*/
public static Bitmap decodeResource(Resources res, int id, Options opts) {
Bitmap bm = null;
-
+ InputStream is = null;
+
try {
final TypedValue value = new TypedValue();
- final InputStream is = res.openRawResource(id, value);
+ is = res.openRawResource(id, value);
bm = decodeResourceStream(res, value, is, null, opts);
- is.close();
- } catch (java.io.IOException e) {
+ } catch (Exception e) {
/* do nothing.
If the exception happened on open, bm will be null.
If it happened on close, bm is still valid.
*/
+ } finally {
+ try {
+ if (is != null) is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
}
+
return bm;
}
@@ -451,6 +459,10 @@ public class BitmapFactory {
bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
}
+ return finishDecode(bm, outPadding, opts);
+ }
+
+ private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
if (bm == null || opts == null) {
return bm;
}
@@ -486,7 +498,7 @@ public class BitmapFactory {
return bm;
}
-
+
/**
* Decode an input stream into a bitmap. If the input stream is null, or
* cannot be used to decode a bitmap, the function returns null.
@@ -506,7 +518,7 @@ public class BitmapFactory {
/**
* Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
* return null. The position within the descriptor will not be changed when
- * this returns, so the descriptor can be used again as is.
+ * this returns, so the descriptor can be used again as-is.
*
* @param fd The file descriptor containing the bitmap data to decode
* @param outPadding If not null, return the padding rect for the bitmap if
@@ -518,7 +530,20 @@ public class BitmapFactory {
* @return the decoded bitmap, or null
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
- return nativeDecodeFileDescriptor(fd, outPadding, opts);
+ try {
+ if (MemoryFile.isMemoryFile(fd)) {
+ int mappedlength = MemoryFile.getMappedSize(fd);
+ MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+ InputStream is = file.getInputStream();
+ Bitmap bm = decodeStream(is, outPadding, opts);
+ return finishDecode(bm, outPadding, opts);
+ }
+ } catch (IOException ex) {
+ // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+ return null;
+ }
+ Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+ return finishDecode(bm, outPadding, opts);
}
/**
@@ -530,7 +555,7 @@ public class BitmapFactory {
* @return the decoded bitmap, or null
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
- return nativeDecodeFileDescriptor(fd, null, null);
+ return decodeFileDescriptor(fd, null, null);
}
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java
index 3fc391c..5cefaa3 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -16,6 +16,8 @@
package android.graphics;
+import android.util.MathUtils;
+
import java.util.HashMap;
import java.util.Locale;
@@ -105,6 +107,92 @@ public class Color {
}
/**
+ * Returns the hue component of a color int.
+ *
+ * @return A value between 0.0f and 1.0f
+ *
+ * @hide Pending API council
+ */
+ public static float hue(int color) {
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+ int temp = Math.min(b, Math.min(r, g));
+
+ float H;
+
+ if (V == temp) {
+ H = 0;
+ } else {
+ final float vtemp = (float) (V - temp);
+ final float cr = (V - r) / vtemp;
+ final float cg = (V - g) / vtemp;
+ final float cb = (V - b) / vtemp;
+
+ if (r == V) {
+ H = cb - cg;
+ } else if (g == V) {
+ H = 2 + cr - cb;
+ } else {
+ H = 4 + cg - cr;
+ }
+
+ H /= 6.f;
+ if (H < 0) {
+ H++;
+ }
+ }
+
+ return H;
+ }
+
+ /**
+ * Returns the saturation component of a color int.
+ *
+ * @return A value between 0.0f and 1.0f
+ *
+ * @hide Pending API council
+ */
+ public static float saturation(int color) {
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
+
+
+ int V = Math.max(b, Math.max(r, g));
+ int temp = Math.min(b, Math.min(r, g));
+
+ float S;
+
+ if (V == temp) {
+ S = 0;
+ } else {
+ S = (V - temp) / (float) V;
+ }
+
+ return S;
+ }
+
+ /**
+ * Returns the brightness component of a color int.
+ *
+ * @return A value between 0.0f and 1.0f
+ *
+ * @hide Pending API council
+ */
+ public static float brightness(int color) {
+ int r = (color >> 16) & 0xFF;
+ int g = (color >> 8) & 0xFF;
+ int b = color & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+
+ return (V / 255.f);
+ }
+
+ /**
* Parse the color string, and return the corresponding color-int.
* If the string cannot be parsed, throws an IllegalArgumentException
* exception. Supported formats are:
@@ -134,6 +222,87 @@ public class Color {
}
/**
+ * Convert HSB components to an ARGB color. Alpha set to 0xFF.
+ * hsv[0] is Hue [0 .. 1)
+ * hsv[1] is Saturation [0...1]
+ * hsv[2] is Value [0...1]
+ * If hsv values are out of range, they are pinned.
+ * @param hsb 3 element array which holds the input HSB components.
+ * @return the resulting argb color
+ *
+ * @hide Pending API council
+ */
+ public static int HSBtoColor(float[] hsb) {
+ return HSBtoColor(hsb[0], hsb[1], hsb[2]);
+ }
+
+ /**
+ * Convert HSB components to an ARGB color. Alpha set to 0xFF.
+ * hsv[0] is Hue [0 .. 1)
+ * hsv[1] is Saturation [0...1]
+ * hsv[2] is Value [0...1]
+ * If hsv values are out of range, they are pinned.
+ * @param h Hue component
+ * @param s Saturation component
+ * @param b Brightness component
+ * @return the resulting argb color
+ *
+ * @hide Pending API council
+ */
+ public static int HSBtoColor(float h, float s, float b) {
+ h = MathUtils.constrain(h, 0.0f, 1.0f);
+ s = MathUtils.constrain(s, 0.0f, 1.0f);
+ b = MathUtils.constrain(b, 0.0f, 1.0f);
+
+ float red = 0.0f;
+ float green = 0.0f;
+ float blue = 0.0f;
+
+ final float hf = (h - (int) h) * 6.0f;
+ final int ihf = (int) hf;
+ final float f = hf - ihf;
+ final float pv = b * (1.0f - s);
+ final float qv = b * (1.0f - s * f);
+ final float tv = b * (1.0f - s * (1.0f - f));
+
+ switch (ihf) {
+ case 0: // Red is the dominant color
+ red = b;
+ green = tv;
+ blue = pv;
+ break;
+ case 1: // Green is the dominant color
+ red = qv;
+ green = b;
+ blue = pv;
+ break;
+ case 2:
+ red = pv;
+ green = b;
+ blue = tv;
+ break;
+ case 3: // Blue is the dominant color
+ red = pv;
+ green = qv;
+ blue = b;
+ break;
+ case 4:
+ red = tv;
+ green = pv;
+ blue = b;
+ break;
+ case 5: // Red is the dominant color
+ red = b;
+ green = pv;
+ blue = qv;
+ break;
+ }
+
+ return 0xFF000000 | (((int) (red * 255.0f)) << 16) |
+ (((int) (green * 255.0f)) << 8) | ((int) (blue * 255.0f));
+ }
+
+ /**
* Convert RGB components to HSV.
* hsv[0] is Hue [0 .. 360)
* hsv[1] is Saturation [0...1]
@@ -193,25 +362,24 @@ public class Color {
return nativeHSVToColor(alpha, hsv);
}
- private static native void nativeRGBToHSV(int red, int greed, int blue,
- float hsv[]);
+ private static native void nativeRGBToHSV(int red, int greed, int blue, float hsv[]);
private static native int nativeHSVToColor(int alpha, float hsv[]);
private static final HashMap<String, Integer> sColorNameMap;
static {
- sColorNameMap = new HashMap();
- sColorNameMap.put("black", Integer.valueOf(BLACK));
- sColorNameMap.put("darkgray", Integer.valueOf(DKGRAY));
- sColorNameMap.put("gray", Integer.valueOf(GRAY));
- sColorNameMap.put("lightgray", Integer.valueOf(LTGRAY));
- sColorNameMap.put("white", Integer.valueOf(WHITE));
- sColorNameMap.put("red", Integer.valueOf(RED));
- sColorNameMap.put("green", Integer.valueOf(GREEN));
- sColorNameMap.put("blue", Integer.valueOf(BLUE));
- sColorNameMap.put("yellow", Integer.valueOf(YELLOW));
- sColorNameMap.put("cyan", Integer.valueOf(CYAN));
- sColorNameMap.put("magenta", Integer.valueOf(MAGENTA));
+ sColorNameMap = new HashMap<String, Integer>();
+ sColorNameMap.put("black", BLACK);
+ sColorNameMap.put("darkgray", DKGRAY);
+ sColorNameMap.put("gray", GRAY);
+ sColorNameMap.put("lightgray", LTGRAY);
+ sColorNameMap.put("white", WHITE);
+ sColorNameMap.put("red", RED);
+ sColorNameMap.put("green", GREEN);
+ sColorNameMap.put("blue", BLUE);
+ sColorNameMap.put("yellow", YELLOW);
+ sColorNameMap.put("cyan", CYAN);
+ sColorNameMap.put("magenta", MAGENTA);
}
}
diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java
index 3deca4a..4f16dc4 100644
--- a/graphics/java/android/graphics/DashPathEffect.java
+++ b/graphics/java/android/graphics/DashPathEffect.java
@@ -23,13 +23,13 @@ public class DashPathEffect extends PathEffect {
* the even indices specifying the "on" intervals, and the odd indices
* specifying the "off" intervals. phase is an offset into the intervals
* array (mod the sum of all of the intervals). The intervals array
- * controlls the width of the dashes. The paint's strokeWidth controlls the
- * height of the dashes.
+ * controls the length of the dashes. The paint's strokeWidth controls the
+ * thickness of the dashes.
* Note: this patheffect only affects drawing with the paint's style is set
* to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
* style == FILL.
* @param intervals array of ON and OFF distances
- * @param phase offset before the first ON interval is drawn
+ * @param phase offset into the intervals array
*/
public DashPathEffect(float intervals[], float phase) {
if (intervals.length < 2) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index e40e84a..9bcab72 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -172,4 +172,14 @@ public class Typeface {
private static native int nativeGetStyle(int native_instance);
private static native int nativeCreateFromAsset(AssetManager mgr, String path);
private static native int nativeCreateFromFile(String path);
+
+ /**
+ * Set the global gamma coefficients for black and white text. This call is
+ * usually a no-op in shipping products, and only exists for testing during
+ * development.
+ *
+ * @param blackGamma gamma coefficient for black text
+ * @param whiteGamma gamma coefficient for white text
+ */
+ public static native void setGammaForText(float blackGamma, float whiteGamma);
}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index eade73a..1755d4f 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -76,6 +76,7 @@ public class BitmapDrawable extends Drawable {
* @deprecated Use {@link #BitmapDrawable(Resources)} to ensure
* that the drawable has correctly set its target density.
*/
+ @Deprecated
public BitmapDrawable() {
mBitmapState = new BitmapState((Bitmap) null);
}
@@ -97,6 +98,7 @@ public class BitmapDrawable extends Drawable {
* @deprecated Use {@link #BitmapDrawable(Resources, Bitmap)} to ensure
* that the drawable has correctly set its target density.
*/
+ @Deprecated
public BitmapDrawable(Bitmap bitmap) {
this(new BitmapState(bitmap));
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 0a0e4eb..21b5e39 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -102,7 +102,7 @@ public abstract class Drawable {
private int[] mStateSet = StateSet.WILD_CARD;
private int mLevel = 0;
private int mChangingConfigurations = 0;
- private Rect mBounds = ZERO_BOUNDS_RECT;
+ private Rect mBounds = ZERO_BOUNDS_RECT; // lazily becomes a new Rect()
/*package*/ Callback mCallback = null;
private boolean mVisible = true;
@@ -654,7 +654,7 @@ public abstract class Drawable {
* Create a drawable from an inputstream
*/
public static Drawable createFromStream(InputStream is, String srcName) {
- return createFromResourceStream(null, null, is, srcName);
+ return createFromResourceStream(null, null, is, srcName, null);
}
/**
@@ -663,6 +663,15 @@ public abstract class Drawable {
*/
public static Drawable createFromResourceStream(Resources res, TypedValue value,
InputStream is, String srcName) {
+ return createFromResourceStream(res, value, is, srcName, null);
+ }
+
+ /**
+ * Create a drawable from an inputstream, using the given resources and
+ * value to determine density information.
+ */
+ public static Drawable createFromResourceStream(Resources res, TypedValue value,
+ InputStream is, String srcName, BitmapFactory.Options opts) {
if (is == null) {
return null;
@@ -683,7 +692,7 @@ public abstract class Drawable {
// an application in compatibility mode, without scaling those down
// to the compatibility density only to have them scaled back up when
// drawn to the screen.
- BitmapFactory.Options opts = new BitmapFactory.Options();
+ if (opts == null) opts = new BitmapFactory.Options();
opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResourceStream(res, value, is, pad, opts);
if (bm != null) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index dc80cf5..6b50406 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -237,7 +237,7 @@ public class DrawableContainer extends Drawable implements Drawable.Callback {
final int N = mDrawableContainerState.getChildCount();
final Drawable[] drawables = mDrawableContainerState.getChildren();
for (int i = 0; i < N; i++) {
- drawables[i].mutate();
+ if (drawables[i] != null) drawables[i].mutate();
}
mMutated = true;
}
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index d5c8a08..1b1ea94 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -56,6 +56,7 @@ public class NinePatchDrawable extends Drawable {
* @deprecated Use {@link #NinePatchDrawable(Resources, Bitmap, byte[], Rect, String)}
* to ensure that the drawable has correctly set its target density.
*/
+ @Deprecated
public NinePatchDrawable(Bitmap bitmap, byte[] chunk, Rect padding, String srcName) {
this(new NinePatchState(new NinePatch(bitmap, chunk, srcName), padding));
}
@@ -78,6 +79,7 @@ public class NinePatchDrawable extends Drawable {
* @deprecated Use {@link #NinePatchDrawable(Resources, NinePatch)}
* to ensure that the drawable has correctly set its target density.
*/
+ @Deprecated
public NinePatchDrawable(NinePatch patch) {
this(new NinePatchState(patch, null));
}
@@ -192,6 +194,14 @@ public class NinePatchDrawable extends Drawable {
}
@Override
+ public void setFilterBitmap(boolean filter) {
+ // at the moment, we see no quality improvement, but a big slowdown
+ // with filtering, so ignore this call for now
+ //
+ //getPaint().setFilterBitmap(filter);
+ }
+
+ @Override
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
super.inflate(r, parser, attrs);
@@ -245,6 +255,8 @@ public class NinePatchDrawable extends Drawable {
public Paint getPaint() {
if (mPaint == null) {
mPaint = new Paint();
+ // dithering helps a lot, and is pretty cheap, so default on
+ mPaint.setDither(true);
}
return mPaint;
}
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index d22a4ba..a8274b1 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -105,6 +105,8 @@ public class StateListDrawable extends DrawableContainer {
mStateListState.setConstantSize(a.getBoolean(
com.android.internal.R.styleable.StateListDrawable_constantSize, false));
+ setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither, false));
+
a.recycle();
int type;
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
new file mode 100644
index 0000000..7749ad3
--- /dev/null
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.res.Resources;
+import android.content.res.AssetManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+import android.util.TypedValue;
+
+/**
+ * @hide
+ *
+ **/
+public class Allocation extends BaseObj {
+ Type mType;
+
+ Allocation(int id, RenderScript rs, Type t) {
+ super(rs);
+ mID = id;
+ mType = t;
+ }
+
+ public void uploadToTexture(int baseMipLevel) {
+ mRS.nAllocationUploadToTexture(mID, baseMipLevel);
+ }
+
+ public void uploadToBufferObject() {
+ mRS.nAllocationUploadToBufferObject(mID);
+ }
+
+ public void data(int[] d) {
+ int size;
+ if(mType != null && mType.mElement != null) {
+ size = mType.mElement.mSize;
+ for(int ct=0; ct < mType.mValues.length; ct++) {
+ if(mType.mValues[ct] != 0) {
+ size *= mType.mValues[ct];
+ }
+ }
+ if((d.length * 4) < size) {
+ throw new IllegalArgumentException("Array too small for allocation type.");
+ }
+ Log.e("rs", "Alloc data size=" + size);
+ mRS.nAllocationData(mID, d, size);
+ return;
+ }
+ mRS.nAllocationData(mID, d, d.length * 4);
+ }
+
+ public void data(float[] d) {
+ int size;
+ if(mType != null && mType.mElement != null) {
+ size = mType.mElement.mSize;
+ for(int ct=0; ct < mType.mValues.length; ct++) {
+ if(mType.mValues[ct] != 0) {
+ size *= mType.mValues[ct];
+ }
+ }
+ if((d.length * 4) < size) {
+ throw new IllegalArgumentException("Array too small for allocation type.");
+ }
+ Log.e("rs", "Alloc data size=" + size);
+ mRS.nAllocationData(mID, d, size);
+ return;
+ }
+ mRS.nAllocationData(mID, d, d.length * 4);
+ }
+
+ public void subData1D(int off, int count, int[] d) {
+ mRS.nAllocationSubData1D(mID, off, count, d, count * 4);
+ }
+
+ public void subData1D(int off, int count, float[] d) {
+ mRS.nAllocationSubData1D(mID, off, count, d, d.length * 4);
+ }
+
+ public void subData2D(int xoff, int yoff, int w, int h, int[] d) {
+ mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4);
+ }
+
+ public void subData2D(int xoff, int yoff, int w, int h, float[] d) {
+ mRS.nAllocationSubData2D(mID, xoff, yoff, w, h, d, d.length * 4);
+ }
+
+ public void readData(int[] d) {
+ mRS.nAllocationRead(mID, d);
+ }
+
+ public void readData(float[] d) {
+ mRS.nAllocationRead(mID, d);
+ }
+
+ public void data(Object o) {
+ mRS.nAllocationSubDataFromObject(mID, mType, 0, o);
+ }
+
+ public void subData(int offset, Object o) {
+ mRS.nAllocationSubDataFromObject(mID, mType, offset, o);
+ }
+
+ public class Adapter1D extends BaseObj {
+ Adapter1D(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void setConstraint(Dimension dim, int value) {
+ mRS.nAdapter1DSetConstraint(mID, dim.mID, value);
+ }
+
+ public void data(int[] d) {
+ mRS.nAdapter1DData(mID, d);
+ }
+
+ public void data(float[] d) {
+ mRS.nAdapter1DData(mID, d);
+ }
+
+ public void subData(int off, int count, int[] d) {
+ mRS.nAdapter1DSubData(mID, off, count, d);
+ }
+
+ public void subData(int off, int count, float[] d) {
+ mRS.nAdapter1DSubData(mID, off, count, d);
+ }
+ }
+
+ public Adapter1D createAdapter1D() {
+ int id = mRS.nAdapter1DCreate();
+ if (id != 0) {
+ mRS.nAdapter1DBindAllocation(id, mID);
+ }
+ return new Adapter1D(id, mRS);
+ }
+
+
+ public class Adapter2D extends BaseObj {
+ Adapter2D(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void setConstraint(Dimension dim, int value) {
+ mRS.nAdapter2DSetConstraint(mID, dim.mID, value);
+ }
+
+ public void data(int[] d) {
+ mRS.nAdapter2DData(mID, d);
+ }
+
+ public void data(float[] d) {
+ mRS.nAdapter2DData(mID, d);
+ }
+
+ public void subData(int xoff, int yoff, int w, int h, int[] d) {
+ mRS.nAdapter2DSubData(mID, xoff, yoff, w, h, d);
+ }
+
+ public void subData(int xoff, int yoff, int w, int h, float[] d) {
+ mRS.nAdapter2DSubData(mID, xoff, yoff, w, h, d);
+ }
+ }
+
+ public Adapter2D createAdapter2D() {
+ int id = mRS.nAdapter2DCreate();
+ if (id != 0) {
+ mRS.nAdapter2DBindAllocation(id, mID);
+ }
+ return new Adapter2D(id, mRS);
+ }
+
+
+ // creation
+
+ private static BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
+ static {
+ mBitmapOptions.inScaled = false;
+ }
+
+ static public Allocation createTyped(RenderScript rs, Type type)
+ throws IllegalArgumentException {
+
+ if(type.mID == 0) {
+ throw new IllegalStateException("Bad Type");
+ }
+ int id = rs.nAllocationCreateTyped(type.mID);
+ return new Allocation(id, rs, type);
+ }
+
+ static public Allocation createSized(RenderScript rs, Element e, int count)
+ throws IllegalArgumentException {
+
+ int id = rs.nAllocationCreateSized(e.mID, count);
+ if(id == 0) {
+ throw new IllegalStateException("Bad element.");
+ }
+ return new Allocation(id, rs, null);
+ }
+
+ static public Allocation createFromBitmap(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips)
+ throws IllegalArgumentException {
+
+ int id = rs.nAllocationCreateFromBitmap(dstFmt.mID, genMips, b);
+ return new Allocation(id, rs, null);
+ }
+
+ static public Allocation createFromBitmapBoxed(RenderScript rs, Bitmap b, Element dstFmt, boolean genMips)
+ throws IllegalArgumentException {
+
+ int id = rs.nAllocationCreateFromBitmapBoxed(dstFmt.mID, genMips, b);
+ return new Allocation(id, rs, null);
+ }
+
+ static public Allocation createFromBitmapResource(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
+ throws IllegalArgumentException {
+
+ InputStream is = null;
+ try {
+ final TypedValue value = new TypedValue();
+ is = res.openRawResource(id, value);
+
+ int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
+ int allocationId = rs.nAllocationCreateFromAssetStream(dstFmt.mID, genMips,
+ asset);
+
+ return new Allocation(allocationId, rs, null);
+ } catch (Exception e) {
+ // Ignore
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static public Allocation createFromBitmapResourceBoxed(RenderScript rs, Resources res, int id, Element dstFmt, boolean genMips)
+ throws IllegalArgumentException {
+
+ Bitmap b = BitmapFactory.decodeResource(res, id, mBitmapOptions);
+ return createFromBitmapBoxed(rs, b, dstFmt, genMips);
+ }
+}
+
+
diff --git a/graphics/java/android/renderscript/BaseObj.java b/graphics/java/android/renderscript/BaseObj.java
new file mode 100644
index 0000000..c25f16a
--- /dev/null
+++ b/graphics/java/android/renderscript/BaseObj.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ **/
+class BaseObj {
+
+ BaseObj(RenderScript rs) {
+ mRS = rs;
+ mID = 0;
+ mDestroyed = false;
+ }
+
+ public int getID() {
+ return mID;
+ }
+
+ int mID;
+ boolean mDestroyed;
+ String mName;
+ RenderScript mRS;
+
+ public void setName(String s) throws IllegalStateException, IllegalArgumentException
+ {
+ if(s.length() < 1) {
+ throw new IllegalArgumentException("setName does not accept a zero length string.");
+ }
+ if(mName != null) {
+ throw new IllegalArgumentException("setName object already has a name.");
+ }
+
+ try {
+ byte[] bytes = s.getBytes("UTF-8");
+ mRS.nAssignName(mID, bytes);
+ mName = s;
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected void finalize() throws Throwable
+ {
+ if (!mDestroyed) {
+ if(mID != 0) {
+ mRS.nObjDestroyOOB(mID);
+ }
+ mID = 0;
+ mDestroyed = true;
+ Log.v(RenderScript.LOG_TAG,
+ getClass() + " auto finalizing object without having released the RS reference.");
+ }
+ super.finalize();
+ }
+
+ public void destroy() {
+ if(mDestroyed) {
+ throw new IllegalStateException("Object already destroyed.");
+ }
+ mDestroyed = true;
+ mRS.nObjDestroy(mID);
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/Dimension.java b/graphics/java/android/renderscript/Dimension.java
new file mode 100644
index 0000000..f29057d
--- /dev/null
+++ b/graphics/java/android/renderscript/Dimension.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+/**
+ * @hide
+ **/
+public enum Dimension {
+ X (0),
+ Y (1),
+ Z (2),
+ LOD (3),
+ FACE (4),
+ ARRAY_0 (100);
+
+ int mID;
+ Dimension(int id) {
+ mID = id;
+ }
+}
+
diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java
new file mode 100644
index 0000000..04c36fd
--- /dev/null
+++ b/graphics/java/android/renderscript/Element.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ *
+ **/
+public class Element extends BaseObj {
+ int mSize;
+ Entry[] mEntries;
+
+ static class Entry {
+ Element mElement;
+ Element.DataType mType;
+ Element.DataKind mKind;
+ boolean mIsNormalized;
+ int mBits;
+ String mName;
+
+ Entry(Element e, int bits) {
+ mElement = e;
+ int mBits = bits;
+ }
+
+ Entry(DataType dt, DataKind dk, boolean isNorm, int bits, String name) {
+ mType = dt;
+ mKind = dk;
+ mIsNormalized = isNorm;
+ mBits = bits;
+ mName = name;
+ }
+ }
+
+ public static final Element USER_U8 = new Element();
+ public static final Element USER_I8 = new Element();
+ public static final Element USER_U16 = new Element();
+ public static final Element USER_I16 = new Element();
+ public static final Element USER_U32 = new Element();
+ public static final Element USER_I32 = new Element();
+ public static final Element USER_FLOAT = new Element();
+
+ public static final Element A_8 = new Element();
+ public static final Element RGB_565 = new Element();
+ public static final Element RGB_888 = new Element();
+ public static final Element RGBA_5551 = new Element();
+ public static final Element RGBA_4444 = new Element();
+ public static final Element RGBA_8888 = new Element();
+
+ public static final Element INDEX_16 = new Element();
+ public static final Element XY_F32 = new Element();
+ public static final Element XYZ_F32 = new Element();
+ public static final Element ST_XY_F32 = new Element();
+ public static final Element ST_XYZ_F32 = new Element();
+ public static final Element NORM_XYZ_F32 = new Element();
+ public static final Element NORM_ST_XYZ_F32 = new Element();
+
+ static void initPredefined(RenderScript rs) {
+ USER_U8.mEntries = new Entry[1];
+ USER_U8.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.USER, false, 8, null);
+ USER_U8.init(rs);
+
+ USER_I8.mEntries = new Entry[1];
+ USER_I8.mEntries[0] = new Entry(DataType.SIGNED, DataKind.USER, false, 8, null);
+ USER_I8.init(rs);
+
+ USER_U16.mEntries = new Entry[1];
+ USER_U16.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.USER, false, 16, null);
+ USER_U16.init(rs);
+
+ USER_I16.mEntries = new Entry[1];
+ USER_I16.mEntries[0] = new Entry(DataType.SIGNED, DataKind.USER, false, 16, null);
+ USER_I16.init(rs);
+
+ USER_U32.mEntries = new Entry[1];
+ USER_U32.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.USER, false, 32, null);
+ USER_U32.init(rs);
+
+ USER_I32.mEntries = new Entry[1];
+ USER_I32.mEntries[0] = new Entry(DataType.SIGNED, DataKind.USER, false, 32, null);
+ USER_I32.init(rs);
+
+ USER_FLOAT.mEntries = new Entry[1];
+ USER_FLOAT.mEntries[0] = new Entry(DataType.FLOAT, DataKind.USER, false, 32, null);
+ USER_FLOAT.init(rs);
+
+ A_8.mEntries = new Entry[1];
+ A_8.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.ALPHA, true, 8, "a");
+ A_8.init(rs);
+
+ RGB_565.mEntries = new Entry[3];
+ RGB_565.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.RED, true, 5, "r");
+ RGB_565.mEntries[1] = new Entry(DataType.UNSIGNED, DataKind.GREEN, true, 6, "g");
+ RGB_565.mEntries[2] = new Entry(DataType.UNSIGNED, DataKind.BLUE, true, 5, "b");
+ RGB_565.init(rs);
+
+ RGB_888.mEntries = new Entry[3];
+ RGB_888.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.RED, true, 8, "r");
+ RGB_888.mEntries[1] = new Entry(DataType.UNSIGNED, DataKind.GREEN, true, 8, "g");
+ RGB_888.mEntries[2] = new Entry(DataType.UNSIGNED, DataKind.BLUE, true, 8, "b");
+ RGB_888.init(rs);
+
+ RGBA_5551.mEntries = new Entry[4];
+ RGBA_5551.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.RED, true, 5, "r");
+ RGBA_5551.mEntries[1] = new Entry(DataType.UNSIGNED, DataKind.GREEN, true, 5, "g");
+ RGBA_5551.mEntries[2] = new Entry(DataType.UNSIGNED, DataKind.BLUE, true, 5, "b");
+ RGBA_5551.mEntries[3] = new Entry(DataType.UNSIGNED, DataKind.ALPHA, true, 1, "a");
+ RGBA_5551.init(rs);
+
+ RGBA_4444.mEntries = new Entry[4];
+ RGBA_4444.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.RED, true, 4, "r");
+ RGBA_4444.mEntries[1] = new Entry(DataType.UNSIGNED, DataKind.GREEN, true, 4, "g");
+ RGBA_4444.mEntries[2] = new Entry(DataType.UNSIGNED, DataKind.BLUE, true, 4, "b");
+ RGBA_4444.mEntries[3] = new Entry(DataType.UNSIGNED, DataKind.ALPHA, true, 4, "a");
+ RGBA_4444.init(rs);
+
+ RGBA_8888.mEntries = new Entry[4];
+ RGBA_8888.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.RED, true, 8, "r");
+ RGBA_8888.mEntries[1] = new Entry(DataType.UNSIGNED, DataKind.GREEN, true, 8, "g");
+ RGBA_8888.mEntries[2] = new Entry(DataType.UNSIGNED, DataKind.BLUE, true, 8, "b");
+ RGBA_8888.mEntries[3] = new Entry(DataType.UNSIGNED, DataKind.ALPHA, true, 8, "a");
+ RGBA_8888.init(rs);
+
+ INDEX_16.mEntries = new Entry[1];
+ INDEX_16.mEntries[0] = new Entry(DataType.UNSIGNED, DataKind.INDEX, false, 16, "index");
+ INDEX_16.init(rs);
+
+ XY_F32.mEntries = new Entry[2];
+ XY_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ XY_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ XY_F32.init(rs);
+
+ XYZ_F32.mEntries = new Entry[3];
+ XYZ_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ XYZ_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ XYZ_F32.mEntries[2] = new Entry(DataType.FLOAT, DataKind.Z, false, 32, "z");
+ XYZ_F32.init(rs);
+
+ ST_XY_F32.mEntries = new Entry[4];
+ ST_XY_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.S, false, 32, "s");
+ ST_XY_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.T, false, 32, "t");
+ ST_XY_F32.mEntries[2] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ ST_XY_F32.mEntries[3] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ ST_XY_F32.init(rs);
+
+ ST_XYZ_F32.mEntries = new Entry[5];
+ ST_XYZ_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.S, false, 32, "s");
+ ST_XYZ_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.T, false, 32, "t");
+ ST_XYZ_F32.mEntries[2] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ ST_XYZ_F32.mEntries[3] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ ST_XYZ_F32.mEntries[4] = new Entry(DataType.FLOAT, DataKind.Z, false, 32, "z");
+ ST_XYZ_F32.init(rs);
+
+ NORM_XYZ_F32.mEntries = new Entry[6];
+ NORM_XYZ_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.NX, false, 32, "nx");
+ NORM_XYZ_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.NY, false, 32, "ny");
+ NORM_XYZ_F32.mEntries[2] = new Entry(DataType.FLOAT, DataKind.NZ, false, 32, "nz");
+ NORM_XYZ_F32.mEntries[3] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ NORM_XYZ_F32.mEntries[4] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ NORM_XYZ_F32.mEntries[5] = new Entry(DataType.FLOAT, DataKind.Z, false, 32, "z");
+ NORM_XYZ_F32.init(rs);
+
+ NORM_ST_XYZ_F32.mEntries = new Entry[8];
+ NORM_ST_XYZ_F32.mEntries[0] = new Entry(DataType.FLOAT, DataKind.NX, false, 32, "nx");
+ NORM_ST_XYZ_F32.mEntries[1] = new Entry(DataType.FLOAT, DataKind.NY, false, 32, "ny");
+ NORM_ST_XYZ_F32.mEntries[2] = new Entry(DataType.FLOAT, DataKind.NZ, false, 32, "nz");
+ NORM_ST_XYZ_F32.mEntries[3] = new Entry(DataType.FLOAT, DataKind.S, false, 32, "s");
+ NORM_ST_XYZ_F32.mEntries[4] = new Entry(DataType.FLOAT, DataKind.T, false, 32, "t");
+ NORM_ST_XYZ_F32.mEntries[5] = new Entry(DataType.FLOAT, DataKind.X, false, 32, "x");
+ NORM_ST_XYZ_F32.mEntries[6] = new Entry(DataType.FLOAT, DataKind.Y, false, 32, "y");
+ NORM_ST_XYZ_F32.mEntries[7] = new Entry(DataType.FLOAT, DataKind.Z, false, 32, "z");
+ NORM_ST_XYZ_F32.init(rs);
+
+ rs.nInitElements(A_8.mID, RGBA_4444.mID, RGBA_8888.mID, RGB_565.mID);
+ }
+
+
+ public enum DataType {
+ FLOAT (0),
+ UNSIGNED (1),
+ SIGNED (2);
+
+ int mID;
+ DataType(int id) {
+ mID = id;
+ }
+ }
+
+ public enum DataKind {
+ USER (0),
+ RED (1),
+ GREEN (2),
+ BLUE (3),
+ ALPHA (4),
+ LUMINANCE (5),
+ INTENSITY (6),
+ X (7),
+ Y (8),
+ Z (9),
+ W (10),
+ S (11),
+ T (12),
+ Q (13),
+ R (14),
+ NX (15),
+ NY (16),
+ NZ (17),
+ INDEX (18),
+ POINT_SIZE(19);
+
+ int mID;
+ DataKind(int id) {
+ mID = id;
+ }
+ }
+
+ Element() {
+ super(null);
+ mID = 0;
+ mSize = 0;
+ }
+
+ public void destroy() throws IllegalStateException {
+ super.destroy();
+ }
+
+ public static Element createFromClass(RenderScript rs, Class c) {
+ Field[] fields = c.getFields();
+ Builder b = new Builder(rs);
+
+ for(Field f: fields) {
+ Class fc = f.getType();
+ if(fc == int.class) {
+ b.add(Element.DataType.SIGNED, Element.DataKind.USER, false, 32, f.getName());
+ } else if(fc == short.class) {
+ b.add(Element.DataType.SIGNED, Element.DataKind.USER, false, 16, f.getName());
+ } else if(fc == byte.class) {
+ b.add(Element.DataType.SIGNED, Element.DataKind.USER, false, 8, f.getName());
+ } else if(fc == float.class) {
+ b.add(Element.DataType.FLOAT, Element.DataKind.USER, false, 32, f.getName());
+ } else {
+ throw new IllegalArgumentException("Unkown field type");
+ }
+ }
+ return b.create();
+ }
+
+ static synchronized void internalCreate(RenderScript rs, Element e) {
+ rs.nElementBegin();
+ int bits = 0;
+ for (int ct=0; ct < e.mEntries.length; ct++) {
+ Entry en = e.mEntries[ct];
+ if(en.mElement != null) {
+ //rs.nElementAdd(en.mElement.mID);
+ } else {
+ int norm = 0;
+ if (en.mIsNormalized) {
+ norm = 1;
+ }
+ rs.nElementAdd(en.mKind.mID, en.mType.mID, norm, en.mBits, en.mName);
+ bits += en.mBits;
+ }
+ }
+ e.mID = rs.nElementCreate();
+ e.mSize = (bits + 7) >> 3;
+ }
+
+ void init(RenderScript rs) {
+ mRS = rs;
+ internalCreate(mRS, this);
+ }
+
+
+ public static class Builder {
+ RenderScript mRS;
+ Entry[] mEntries;
+ int mEntryCount;
+
+ public Builder(RenderScript rs) {
+ mRS = rs;
+ mEntryCount = 0;
+ mEntries = new Entry[8];
+ }
+
+ void addEntry(Entry e) {
+ if(mEntries.length >= mEntryCount) {
+ Entry[] en = new Entry[mEntryCount + 8];
+ System.arraycopy(mEntries, 0, en, 0, mEntries.length);
+ mEntries = en;
+ }
+ mEntries[mEntryCount] = e;
+ mEntryCount++;
+ }
+
+ public Builder add(Element e) throws IllegalArgumentException {
+ Entry en = new Entry(e, e.mSize * 8);
+ addEntry(en);
+ return this;
+ }
+
+ public Builder add(Element.DataType dt, Element.DataKind dk, boolean isNormalized, int bits, String name) {
+ Entry en = new Entry(dt, dk, isNormalized, bits, name);
+ addEntry(en);
+ return this;
+ }
+
+ public Builder add(Element.DataType dt, Element.DataKind dk, boolean isNormalized, int bits) {
+ add(dt, dk, isNormalized, bits, null);
+ return this;
+ }
+
+ public Builder addFloat(Element.DataKind dk) {
+ add(DataType.FLOAT, dk, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloat(Element.DataKind dk, String name) {
+ add(DataType.FLOAT, dk, false, 32, name);
+ return this;
+ }
+
+ public Builder addFloatXY() {
+ add(DataType.FLOAT, DataKind.X, false, 32, null);
+ add(DataType.FLOAT, DataKind.Y, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatXY(String prefix) {
+ add(DataType.FLOAT, DataKind.X, false, 32, prefix + "x");
+ add(DataType.FLOAT, DataKind.Y, false, 32, prefix + "y");
+ return this;
+ }
+
+ public Builder addFloatXYZ() {
+ add(DataType.FLOAT, DataKind.X, false, 32, null);
+ add(DataType.FLOAT, DataKind.Y, false, 32, null);
+ add(DataType.FLOAT, DataKind.Z, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatXYZ(String prefix) {
+ add(DataType.FLOAT, DataKind.X, false, 32, prefix + "x");
+ add(DataType.FLOAT, DataKind.Y, false, 32, prefix + "y");
+ add(DataType.FLOAT, DataKind.Z, false, 32, prefix + "z");
+ return this;
+ }
+
+ public Builder addFloatST() {
+ add(DataType.FLOAT, DataKind.S, false, 32, null);
+ add(DataType.FLOAT, DataKind.T, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatST(String prefix) {
+ add(DataType.FLOAT, DataKind.S, false, 32, prefix + "s");
+ add(DataType.FLOAT, DataKind.T, false, 32, prefix + "t");
+ return this;
+ }
+
+ public Builder addFloatNorm() {
+ add(DataType.FLOAT, DataKind.NX, false, 32, null);
+ add(DataType.FLOAT, DataKind.NY, false, 32, null);
+ add(DataType.FLOAT, DataKind.NZ, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatNorm(String prefix) {
+ add(DataType.FLOAT, DataKind.NX, false, 32, prefix + "nx");
+ add(DataType.FLOAT, DataKind.NY, false, 32, prefix + "ny");
+ add(DataType.FLOAT, DataKind.NZ, false, 32, prefix + "nz");
+ return this;
+ }
+
+ public Builder addFloatPointSize() {
+ add(DataType.FLOAT, DataKind.POINT_SIZE, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatPointSize(String prefix) {
+ add(DataType.FLOAT, DataKind.POINT_SIZE, false, 32, prefix + "pointSize");
+ return this;
+ }
+
+ public Builder addFloatRGB() {
+ add(DataType.FLOAT, DataKind.RED, false, 32, null);
+ add(DataType.FLOAT, DataKind.GREEN, false, 32, null);
+ add(DataType.FLOAT, DataKind.BLUE, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatRGB(String prefix) {
+ add(DataType.FLOAT, DataKind.RED, false, 32, prefix + "r");
+ add(DataType.FLOAT, DataKind.GREEN, false, 32, prefix + "g");
+ add(DataType.FLOAT, DataKind.BLUE, false, 32, prefix + "b");
+ return this;
+ }
+
+ public Builder addFloatRGBA() {
+ add(DataType.FLOAT, DataKind.RED, false, 32, null);
+ add(DataType.FLOAT, DataKind.GREEN, false, 32, null);
+ add(DataType.FLOAT, DataKind.BLUE, false, 32, null);
+ add(DataType.FLOAT, DataKind.ALPHA, false, 32, null);
+ return this;
+ }
+
+ public Builder addFloatRGBA(String prefix) {
+ add(DataType.FLOAT, DataKind.RED, false, 32, prefix + "r");
+ add(DataType.FLOAT, DataKind.GREEN, false, 32, prefix + "g");
+ add(DataType.FLOAT, DataKind.BLUE, false, 32, prefix + "b");
+ add(DataType.FLOAT, DataKind.ALPHA, false, 32, prefix + "a");
+ return this;
+ }
+
+ public Builder addUNorm8RGBA() {
+ add(DataType.UNSIGNED, DataKind.RED, true, 8, null);
+ add(DataType.UNSIGNED, DataKind.GREEN, true, 8, null);
+ add(DataType.UNSIGNED, DataKind.BLUE, true, 8, null);
+ add(DataType.UNSIGNED, DataKind.ALPHA, true, 8, null);
+ return this;
+ }
+
+ public Builder addUNorm8RGBA(String prefix) {
+ add(DataType.UNSIGNED, DataKind.RED, true, 8, prefix + "r");
+ add(DataType.UNSIGNED, DataKind.GREEN, true, 8, prefix + "g");
+ add(DataType.UNSIGNED, DataKind.BLUE, true, 8, prefix + "b");
+ add(DataType.UNSIGNED, DataKind.ALPHA, true, 8, prefix + "a");
+ return this;
+ }
+
+ public Element create() {
+ Element e = new Element();
+ e.mEntries = new Entry[mEntryCount];
+ java.lang.System.arraycopy(mEntries, 0, e.mEntries, 0, mEntryCount);
+ e.init(mRS);
+ return e;
+ }
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/Light.java b/graphics/java/android/renderscript/Light.java
new file mode 100644
index 0000000..115ae03
--- /dev/null
+++ b/graphics/java/android/renderscript/Light.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ **/
+public class Light extends BaseObj {
+ Light(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void setColor(float r, float g, float b) {
+ mRS.nLightSetColor(mID, r, g, b);
+ }
+
+ public void setPosition(float x, float y, float z) {
+ mRS.nLightSetPosition(mID, x, y, z);
+ }
+
+ public static class Builder {
+ RenderScript mRS;
+ boolean mIsMono;
+ boolean mIsLocal;
+
+ public Builder(RenderScript rs) {
+ mRS = rs;
+ mIsMono = false;
+ mIsLocal = false;
+ }
+
+ public void lightSetIsMono(boolean isMono) {
+ mIsMono = isMono;
+ }
+
+ public void lightSetIsLocal(boolean isLocal) {
+ mIsLocal = isLocal;
+ }
+
+ static synchronized Light internalCreate(RenderScript rs, Builder b) {
+ rs.nSamplerBegin();
+ rs.nLightSetIsMono(b.mIsMono);
+ rs.nLightSetIsLocal(b.mIsLocal);
+ int id = rs.nLightCreate();
+ return new Light(id, rs);
+ }
+
+ public Light create() {
+ return internalCreate(mRS, this);
+ }
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/Matrix.java b/graphics/java/android/renderscript/Matrix.java
new file mode 100644
index 0000000..a266d6b
--- /dev/null
+++ b/graphics/java/android/renderscript/Matrix.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.Math;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class Matrix {
+
+ public Matrix() {
+ mMat = new float[16];
+ loadIdentity();
+ }
+
+ public float get(int i, int j) {
+ return mMat[i*4 + j];
+ }
+
+ public void set(int i, int j, float v) {
+ mMat[i*4 + j] = v;
+ }
+
+ public void loadIdentity() {
+ mMat[0] = 1;
+ mMat[1] = 0;
+ mMat[2] = 0;
+ mMat[3] = 0;
+
+ mMat[4] = 0;
+ mMat[5] = 1;
+ mMat[6] = 0;
+ mMat[7] = 0;
+
+ mMat[8] = 0;
+ mMat[9] = 0;
+ mMat[10] = 1;
+ mMat[11] = 0;
+
+ mMat[12] = 0;
+ mMat[13] = 0;
+ mMat[14] = 0;
+ mMat[15] = 1;
+ }
+
+ public void load(Matrix src) {
+ mMat = src.mMat;
+ }
+
+ public void loadRotate(float rot, float x, float y, float z) {
+ float c, s;
+ mMat[3] = 0;
+ mMat[7] = 0;
+ mMat[11]= 0;
+ mMat[12]= 0;
+ mMat[13]= 0;
+ mMat[14]= 0;
+ mMat[15]= 1;
+ rot *= (float)(java.lang.Math.PI / 180.0f);
+ c = (float)java.lang.Math.cos(rot);
+ s = (float)java.lang.Math.sin(rot);
+
+ float len = (float)java.lang.Math.sqrt(x*x + y*y + z*z);
+ if (!(len != 1)) {
+ float recipLen = 1.f / len;
+ x *= recipLen;
+ y *= recipLen;
+ z *= recipLen;
+ }
+ float nc = 1.0f - c;
+ float xy = x * y;
+ float yz = y * z;
+ float zx = z * x;
+ float xs = x * s;
+ float ys = y * s;
+ float zs = z * s;
+ mMat[ 0] = x*x*nc + c;
+ mMat[ 4] = xy*nc - zs;
+ mMat[ 8] = zx*nc + ys;
+ mMat[ 1] = xy*nc + zs;
+ mMat[ 5] = y*y*nc + c;
+ mMat[ 9] = yz*nc - xs;
+ mMat[ 2] = zx*nc - ys;
+ mMat[ 6] = yz*nc + xs;
+ mMat[10] = z*z*nc + c;
+ }
+
+ public void loadScale(float x, float y, float z) {
+ loadIdentity();
+ mMat[0] = x;
+ mMat[5] = y;
+ mMat[10] = z;
+ }
+
+ public void loadTranslate(float x, float y, float z) {
+ loadIdentity();
+ mMat[12] = x;
+ mMat[13] = y;
+ mMat[14] = z;
+ }
+
+ public void loadMultiply(Matrix lhs, Matrix rhs) {
+ for (int i=0 ; i<4 ; i++) {
+ float ri0 = 0;
+ float ri1 = 0;
+ float ri2 = 0;
+ float ri3 = 0;
+ for (int j=0 ; j<4 ; j++) {
+ float rhs_ij = rhs.get(i,j);
+ ri0 += lhs.get(j,0) * rhs_ij;
+ ri1 += lhs.get(j,1) * rhs_ij;
+ ri2 += lhs.get(j,2) * rhs_ij;
+ ri3 += lhs.get(j,3) * rhs_ij;
+ }
+ set(i,0, ri0);
+ set(i,1, ri1);
+ set(i,2, ri2);
+ set(i,3, ri3);
+ }
+ }
+
+ public void loadOrtho(float l, float r, float b, float t, float n, float f) {
+ loadIdentity();
+ mMat[0] = 2 / (r - l);
+ mMat[5] = 2 / (t - b);
+ mMat[10]= -2 / (f - n);
+ mMat[12]= -(r + l) / (r - l);
+ mMat[13]= -(t + b) / (t - b);
+ mMat[14]= -(f + n) / (f - n);
+ }
+
+ public void loadFrustum(float l, float r, float b, float t, float n, float f) {
+ loadIdentity();
+ mMat[0] = 2 * n / (r - l);
+ mMat[5] = 2 * n / (t - b);
+ mMat[8] = (r + l) / (r - l);
+ mMat[9] = (t + b) / (t - b);
+ mMat[10]= -(f + n) / (f - n);
+ mMat[11]= -1;
+ mMat[14]= -2*f*n / (f - n);
+ mMat[15]= 0;
+ }
+
+ public void multiply(Matrix rhs) {
+ Matrix tmp = new Matrix();
+ tmp.loadMultiply(this, rhs);
+ load(tmp);
+ }
+ public void rotate(float rot, float x, float y, float z) {
+ Matrix tmp = new Matrix();
+ tmp.loadRotate(rot, x, y, z);
+ multiply(tmp);
+ }
+ public void scale(float x, float y, float z) {
+ Matrix tmp = new Matrix();
+ tmp.loadScale(x, y, z);
+ multiply(tmp);
+ }
+ public void translate(float x, float y, float z) {
+ Matrix tmp = new Matrix();
+ tmp.loadTranslate(x, y, z);
+ multiply(tmp);
+ }
+
+
+
+ float[] mMat;
+
+}
+
+
+
+
+
diff --git a/graphics/java/android/renderscript/Primitive.java b/graphics/java/android/renderscript/Primitive.java
new file mode 100644
index 0000000..7925cac
--- /dev/null
+++ b/graphics/java/android/renderscript/Primitive.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+/**
+ * @hide
+ **/
+public enum Primitive {
+ POINT (0),
+ LINE (1),
+ LINE_STRIP (2),
+ TRIANGLE (3),
+ TRIANGLE_STRIP (4),
+ TRIANGLE_FAN (5);
+
+ int mID;
+ Primitive(int id) {
+ mID = id;
+ }
+}
+
+
+
diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java
new file mode 100644
index 0000000..392d93d
--- /dev/null
+++ b/graphics/java/android/renderscript/ProgramFragment.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+import android.util.Config;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class ProgramFragment extends BaseObj {
+ public static final int MAX_SLOT = 2;
+
+ public enum EnvMode {
+ REPLACE (0),
+ MODULATE (1),
+ DECAL (2);
+
+ int mID;
+ EnvMode(int id) {
+ mID = id;
+ }
+ }
+
+
+ ProgramFragment(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void bindTexture(Allocation va, int slot)
+ throws IllegalArgumentException {
+ if((slot < 0) || (slot >= MAX_SLOT)) {
+ throw new IllegalArgumentException("Slot ID out of range.");
+ }
+
+ mRS.nProgramFragmentBindTexture(mID, slot, va.mID);
+ }
+
+ public void bindSampler(Sampler vs, int slot)
+ throws IllegalArgumentException {
+ if((slot < 0) || (slot >= MAX_SLOT)) {
+ throw new IllegalArgumentException("Slot ID out of range.");
+ }
+
+ mRS.nProgramFragmentBindSampler(mID, slot, vs.mID);
+ }
+
+
+ public static class Builder {
+ RenderScript mRS;
+ Element mIn;
+ Element mOut;
+ boolean mPointSpriteEnable;
+
+ private class Slot {
+ Type mType;
+ EnvMode mEnv;
+ boolean mTexEnable;
+
+ Slot() {
+ mTexEnable = false;
+ }
+ }
+ Slot[] mSlots;
+
+ public Builder(RenderScript rs, Element in, Element out) {
+ mRS = rs;
+ mIn = in;
+ mOut = out;
+ mSlots = new Slot[MAX_SLOT];
+ mPointSpriteEnable = false;
+ for(int ct=0; ct < MAX_SLOT; ct++) {
+ mSlots[ct] = new Slot();
+ }
+ }
+
+ public void setType(int slot, Type t)
+ throws IllegalArgumentException {
+ if((slot < 0) || (slot >= MAX_SLOT)) {
+ throw new IllegalArgumentException("Slot ID out of range.");
+ }
+
+ mSlots[slot].mType = t;
+ }
+
+ public void setTexEnable(boolean enable, int slot)
+ throws IllegalArgumentException {
+ if((slot < 0) || (slot >= MAX_SLOT)) {
+ throw new IllegalArgumentException("Slot ID out of range.");
+ }
+
+ mSlots[slot].mTexEnable = enable;
+ }
+
+ public void setTexEnvMode(EnvMode env, int slot)
+ throws IllegalArgumentException {
+ if((slot < 0) || (slot >= MAX_SLOT)) {
+ throw new IllegalArgumentException("Slot ID out of range.");
+ }
+
+ mSlots[slot].mEnv = env;
+ }
+
+ public void setPointSpriteTexCoordinateReplacement(boolean enable) {
+ mPointSpriteEnable = enable;
+ }
+
+ static synchronized ProgramFragment internalCreate(RenderScript rs, Builder b) {
+ int inID = 0;
+ int outID = 0;
+ if (b.mIn != null) {
+ inID = b.mIn.mID;
+ }
+ if (b.mOut != null) {
+ outID = b.mOut.mID;
+ }
+ rs.nProgramFragmentBegin(inID, outID, b.mPointSpriteEnable);
+ for(int ct=0; ct < MAX_SLOT; ct++) {
+ if(b.mSlots[ct].mTexEnable) {
+ Slot s = b.mSlots[ct];
+ int typeID = 0;
+ if(s.mType != null) {
+ typeID = s.mType.mID;
+ }
+ rs.nProgramFragmentSetSlot(ct, true, s.mEnv.mID, typeID);
+ }
+ }
+
+ int id = rs.nProgramFragmentCreate();
+ return new ProgramFragment(id, rs);
+ }
+
+ public ProgramFragment create() {
+ return internalCreate(mRS, this);
+ }
+ }
+}
+
+
+
diff --git a/graphics/java/android/renderscript/ProgramStore.java b/graphics/java/android/renderscript/ProgramStore.java
new file mode 100644
index 0000000..b7d987e
--- /dev/null
+++ b/graphics/java/android/renderscript/ProgramStore.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+import android.util.Config;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class ProgramStore extends BaseObj {
+ public enum DepthFunc {
+ ALWAYS (0),
+ LESS (1),
+ LEQUAL (2),
+ GREATER (3),
+ GEQUAL (4),
+ EQUAL (5),
+ NOTEQUAL (6);
+
+ int mID;
+ DepthFunc(int id) {
+ mID = id;
+ }
+ }
+
+ public enum BlendSrcFunc {
+ ZERO (0),
+ ONE (1),
+ DST_COLOR (2),
+ ONE_MINUS_DST_COLOR (3),
+ SRC_ALPHA (4),
+ ONE_MINUS_SRC_ALPHA (5),
+ DST_ALPHA (6),
+ ONE_MINUS_DST_ALPA (7),
+ SRC_ALPHA_SATURATE (8);
+
+ int mID;
+ BlendSrcFunc(int id) {
+ mID = id;
+ }
+ }
+
+ public enum BlendDstFunc {
+ ZERO (0),
+ ONE (1),
+ SRC_COLOR (2),
+ ONE_MINUS_SRC_COLOR (3),
+ SRC_ALPHA (4),
+ ONE_MINUS_SRC_ALPHA (5),
+ DST_ALPHA (6),
+ ONE_MINUS_DST_ALPA (7);
+
+ int mID;
+ BlendDstFunc(int id) {
+ mID = id;
+ }
+ }
+
+
+ ProgramStore(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+
+
+ public static class Builder {
+ RenderScript mRS;
+ Element mIn;
+ Element mOut;
+ DepthFunc mDepthFunc;
+ boolean mDepthMask;
+ boolean mColorMaskR;
+ boolean mColorMaskG;
+ boolean mColorMaskB;
+ boolean mColorMaskA;
+ BlendSrcFunc mBlendSrc;
+ BlendDstFunc mBlendDst;
+ boolean mDither;
+
+
+
+ public Builder(RenderScript rs, Element in, Element out) {
+ mRS = rs;
+ mIn = in;
+ mOut = out;
+ mDepthFunc = DepthFunc.ALWAYS;
+ mDepthMask = false;
+ mColorMaskR = true;
+ mColorMaskG = true;
+ mColorMaskB = true;
+ mColorMaskA = true;
+ mBlendSrc = BlendSrcFunc.ONE;
+ mBlendDst = BlendDstFunc.ZERO;
+
+
+ }
+
+ public void setDepthFunc(DepthFunc func) {
+ mDepthFunc = func;
+ }
+
+ public void setDepthMask(boolean enable) {
+ mDepthMask = enable;
+ }
+
+ public void setColorMask(boolean r, boolean g, boolean b, boolean a) {
+ mColorMaskR = r;
+ mColorMaskG = g;
+ mColorMaskB = b;
+ mColorMaskA = a;
+ }
+
+ public void setBlendFunc(BlendSrcFunc src, BlendDstFunc dst) {
+ mBlendSrc = src;
+ mBlendDst = dst;
+ }
+
+ public void setDitherEnable(boolean enable) {
+ mDither = enable;
+ }
+
+ static synchronized ProgramStore internalCreate(RenderScript rs, Builder b) {
+ int inID = 0;
+ int outID = 0;
+ if (b.mIn != null) {
+ inID = b.mIn.mID;
+ }
+ if (b.mOut != null) {
+ outID = b.mOut.mID;
+ }
+ rs.nProgramFragmentStoreBegin(inID, outID);
+ rs.nProgramFragmentStoreDepthFunc(b.mDepthFunc.mID);
+ rs.nProgramFragmentStoreDepthMask(b.mDepthMask);
+ rs.nProgramFragmentStoreColorMask(b.mColorMaskR,
+ b.mColorMaskG,
+ b.mColorMaskB,
+ b.mColorMaskA);
+ rs.nProgramFragmentStoreBlendFunc(b.mBlendSrc.mID, b.mBlendDst.mID);
+ rs.nProgramFragmentStoreDither(b.mDither);
+
+ int id = rs.nProgramFragmentStoreCreate();
+ return new ProgramStore(id, rs);
+ }
+
+ public ProgramStore create() {
+ return internalCreate(mRS, this);
+ }
+ }
+
+}
+
+
+
+
diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java
new file mode 100644
index 0000000..2a11bfb
--- /dev/null
+++ b/graphics/java/android/renderscript/ProgramVertex.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+import android.util.Config;
+import android.util.Log;
+
+
+/**
+ * @hide
+ *
+ **/
+public class ProgramVertex extends BaseObj {
+ public static final int MAX_LIGHT = 8;
+
+ ProgramVertex(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void bindAllocation(MatrixAllocation va) {
+ mRS.nProgramVertexBindAllocation(mID, va.mAlloc.mID);
+ }
+
+
+ public static class Builder {
+ RenderScript mRS;
+ Element mIn;
+ Element mOut;
+ Light[] mLights;
+ int mLightCount;
+ boolean mTextureMatrixEnable;
+
+
+ public Builder(RenderScript rs, Element in, Element out) {
+ mRS = rs;
+ mIn = in;
+ mOut = out;
+ mLights = new Light[MAX_LIGHT];
+ mLightCount = 0;
+ }
+
+ public void setTextureMatrixEnable(boolean enable) {
+ mTextureMatrixEnable = enable;
+ }
+
+ public void addLight(Light l) throws IllegalStateException {
+ if(mLightCount >= MAX_LIGHT) {
+ throw new IllegalArgumentException("Max light count exceeded.");
+ }
+ mLights[mLightCount] = l;
+ mLightCount++;
+ }
+
+
+
+ static synchronized ProgramVertex internalCreate(RenderScript rs, Builder b) {
+ int inID = 0;
+ int outID = 0;
+ if (b.mIn != null) {
+ inID = b.mIn.mID;
+ }
+ if (b.mOut != null) {
+ outID = b.mOut.mID;
+ }
+ rs.nProgramVertexBegin(inID, outID);
+ for(int ct=0; ct < b.mLightCount; ct++) {
+ rs.nProgramVertexAddLight(b.mLights[ct].mID);
+ }
+ rs.nProgramVertexSetTextureMatrixEnable(b.mTextureMatrixEnable);
+ int id = rs.nProgramVertexCreate();
+ return new ProgramVertex(id, rs);
+ }
+
+ public ProgramVertex create() {
+ return internalCreate(mRS, this);
+ }
+ }
+
+
+
+ public static class MatrixAllocation {
+ static final int MODELVIEW_OFFSET = 0;
+ static final int PROJECTION_OFFSET = 16;
+ static final int TEXTURE_OFFSET = 32;
+
+ Matrix mModel;
+ Matrix mProjection;
+ Matrix mTexture;
+
+ public Allocation mAlloc;
+
+ public MatrixAllocation(RenderScript rs) {
+ mModel = new Matrix();
+ mProjection = new Matrix();
+ mTexture = new Matrix();
+
+ mAlloc = Allocation.createSized(rs, Element.USER_FLOAT, 48);
+ mAlloc.subData1D(MODELVIEW_OFFSET, 16, mModel.mMat);
+ mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ mAlloc.subData1D(TEXTURE_OFFSET, 16, mTexture.mMat);
+ }
+
+ public void destroy() {
+ mAlloc.destroy();
+ mAlloc = null;
+ }
+
+ public void loadModelview(Matrix m) {
+ mModel = m;
+ mAlloc.subData1D(MODELVIEW_OFFSET, 16, m.mMat);
+ }
+
+ public void loadProjection(Matrix m) {
+ mProjection = m;
+ mAlloc.subData1D(PROJECTION_OFFSET, 16, m.mMat);
+ }
+
+ public void loadTexture(Matrix m) {
+ mTexture = m;
+ mAlloc.subData1D(TEXTURE_OFFSET, 16, m.mMat);
+ }
+
+ public void setupOrthoWindow(int w, int h) {
+ mProjection.loadOrtho(0,w, h,0, -1,1);
+ mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ }
+
+ public void setupOrthoNormalized(int w, int h) {
+ // range -1,1 in the narrow axis.
+ if(w > h) {
+ float aspect = ((float)w) / h;
+ mProjection.loadOrtho(-aspect,aspect, -1,1, -1,1);
+ } else {
+ float aspect = ((float)h) / w;
+ mProjection.loadOrtho(-1,1, -aspect,aspect, -1,1);
+ }
+ mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ }
+
+ public void setupProjectionNormalized(int w, int h) {
+ // range -1,1 in the narrow axis at z = 0.
+ Matrix m1 = new Matrix();
+ Matrix m2 = new Matrix();
+
+ if(w > h) {
+ float aspect = ((float)w) / h;
+ m1.loadFrustum(-aspect,aspect, -1,1, 1,100);
+ } else {
+ float aspect = ((float)h) / w;
+ m1.loadFrustum(-1,1, -aspect,aspect, 1,100);
+ }
+
+ m2.loadRotate(180, 0, 1, 0);
+ m1.loadMultiply(m1, m2);
+
+ m2.loadScale(-2, 2, 1);
+ m1.loadMultiply(m1, m2);
+
+ m2.loadTranslate(0, 0, 2);
+ m1.loadMultiply(m1, m2);
+
+ mProjection = m1;
+ mAlloc.subData1D(PROJECTION_OFFSET, 16, mProjection.mMat);
+ }
+
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java
new file mode 100644
index 0000000..a3f1ded
--- /dev/null
+++ b/graphics/java/android/renderscript/RSSurfaceView.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * @hide
+ *
+ **/
+public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private SurfaceHolder mSurfaceHolder;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public RSSurfaceView(Context context) {
+ super(context);
+ init();
+ Log.v(RenderScript.LOG_TAG, "RSSurfaceView");
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public RSSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ Log.v(RenderScript.LOG_TAG, "RSSurfaceView");
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of RSSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ Log.v(RenderScript.LOG_TAG, "surfaceCreated");
+ mSurfaceHolder = holder;
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of RSSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ Log.v(RenderScript.LOG_TAG, "surfaceDestroyed");
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of RSSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ Log.v(RenderScript.LOG_TAG, "surfaceChanged");
+ }
+
+ /**
+ * Inform the view that the activity is paused. The owner of this view must
+ * call this method when the activity is paused. Calling this method will
+ * pause the rendering thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ Log.v(RenderScript.LOG_TAG, "onPause");
+ }
+
+ /**
+ * Inform the view that the activity is resumed. The owner of this view must
+ * call this method when the activity is resumed. Calling this method will
+ * recreate the OpenGL display and resume the rendering
+ * thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ Log.v(RenderScript.LOG_TAG, "onResume");
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ Log.v(RenderScript.LOG_TAG, "queueEvent");
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of RSSurfaceView.
+ * Must not be called before a renderer has been set.
+ */
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ }
+
+ // ----------------------------------------------------------------------
+
+ public RenderScript createRenderScript(boolean useDepth) {
+ Surface sur = null;
+ while ((sur == null) || (mSurfaceHolder == null)) {
+ sur = getHolder().getSurface();
+ }
+ RenderScript rs = new RenderScript(sur, useDepth);
+ return rs;
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
new file mode 100644
index 0000000..6f5b67e
--- /dev/null
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.reflect.Field;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Config;
+import android.util.Log;
+import android.view.Surface;
+
+
+/**
+ * @hide
+ *
+ **/
+public class RenderScript {
+ static final String LOG_TAG = "libRS_jni";
+ private static final boolean DEBUG = false;
+ @SuppressWarnings({"UnusedDeclaration", "deprecation"})
+ private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+
+
+ /*
+ * We use a class initializer to allow the native code to cache some
+ * field offsets.
+ */
+ @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+ private static boolean sInitialized;
+ native private static void _nInit();
+
+
+ static {
+ sInitialized = false;
+ try {
+ System.loadLibrary("rs_jni");
+ _nInit();
+ sInitialized = true;
+ } catch (UnsatisfiedLinkError e) {
+ Log.d(LOG_TAG, "RenderScript JNI library not found!");
+ }
+ }
+
+ native void nInitElements(int a8, int rgba4444, int rgba8888, int rgb565);
+
+ native int nDeviceCreate();
+ native void nDeviceDestroy(int dev);
+ native int nContextCreate(int dev, Surface sur, int ver, boolean useDepth);
+ native void nContextDestroy(int con);
+
+ //void rsContextBindSampler (uint32_t slot, RsSampler sampler);
+ //void rsContextBindRootScript (RsScript sampler);
+ native void nContextBindRootScript(int script);
+ native void nContextBindSampler(int sampler, int slot);
+ native void nContextBindProgramFragmentStore(int pfs);
+ native void nContextBindProgramFragment(int pf);
+ native void nContextBindProgramVertex(int pf);
+ native void nContextAddDefineI32(String name, int value);
+ native void nContextAddDefineF(String name, float value);
+
+ native void nAssignName(int obj, byte[] name);
+ native void nObjDestroy(int id);
+ native void nObjDestroyOOB(int id);
+ native int nFileOpen(byte[] name);
+
+ native void nElementBegin();
+ native void nElementAdd(int kind, int type, int norm, int bits, String s);
+ native int nElementCreate();
+
+ native void nTypeBegin(int elementID);
+ native void nTypeAdd(int dim, int val);
+ native int nTypeCreate();
+ native void nTypeFinalDestroy(Type t);
+ native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
+
+ native int nAllocationCreateTyped(int type);
+ native int nAllocationCreateSized(int elem, int count);
+ native int nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
+ native int nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp);
+ native int nAllocationCreateFromAssetStream(int dstFmt, boolean genMips, int assetStream);
+
+ native void nAllocationUploadToTexture(int alloc, int baseMioLevel);
+ native void nAllocationUploadToBufferObject(int alloc);
+ native void nAllocationData(int id, int[] d, int sizeBytes);
+ native void nAllocationData(int id, float[] d, int sizeBytes);
+ native void nAllocationSubData1D(int id, int off, int count, int[] d, int sizeBytes);
+ native void nAllocationSubData1D(int id, int off, int count, float[] d, int sizeBytes);
+ native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, int[] d, int sizeBytes);
+ native void nAllocationSubData2D(int id, int xoff, int yoff, int w, int h, float[] d, int sizeBytes);
+ native void nAllocationRead(int id, int[] d);
+ native void nAllocationRead(int id, float[] d);
+ native void nAllocationSubDataFromObject(int id, Type t, int offset, Object o);
+
+ native void nTriangleMeshBegin(int vertex, int index);
+ native void nTriangleMeshAddVertex_XY (float x, float y);
+ native void nTriangleMeshAddVertex_XYZ (float x, float y, float z);
+ native void nTriangleMeshAddVertex_XY_ST (float x, float y, float s, float t);
+ native void nTriangleMeshAddVertex_XYZ_ST (float x, float y, float z, float s, float t);
+ native void nTriangleMeshAddVertex_XYZ_ST_NORM (float x, float y, float z, float s, float t, float nx, float ny, float nz);
+ native void nTriangleMeshAddTriangle(int i1, int i2, int i3);
+ native int nTriangleMeshCreate();
+
+ native void nAdapter1DBindAllocation(int ad, int alloc);
+ native void nAdapter1DSetConstraint(int ad, int dim, int value);
+ native void nAdapter1DData(int ad, int[] d);
+ native void nAdapter1DData(int ad, float[] d);
+ native void nAdapter1DSubData(int ad, int off, int count, int[] d);
+ native void nAdapter1DSubData(int ad, int off, int count, float[] d);
+ native int nAdapter1DCreate();
+
+ native void nAdapter2DBindAllocation(int ad, int alloc);
+ native void nAdapter2DSetConstraint(int ad, int dim, int value);
+ native void nAdapter2DData(int ad, int[] d);
+ native void nAdapter2DData(int ad, float[] d);
+ native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, int[] d);
+ native void nAdapter2DSubData(int ad, int xoff, int yoff, int w, int h, float[] d);
+ native int nAdapter2DCreate();
+
+ native void nScriptBindAllocation(int script, int alloc, int slot);
+ native void nScriptSetClearColor(int script, float r, float g, float b, float a);
+ native void nScriptSetClearDepth(int script, float depth);
+ native void nScriptSetClearStencil(int script, int stencil);
+ native void nScriptSetTimeZone(int script, byte[] timeZone);
+ native void nScriptSetType(int type, boolean writable, String name, int slot);
+ native void nScriptSetRoot(boolean isRoot);
+
+ native void nScriptCBegin();
+ native void nScriptCSetScript(byte[] script, int offset, int length);
+ native int nScriptCCreate();
+ native void nScriptCAddDefineI32(String name, int value);
+ native void nScriptCAddDefineF(String name, float value);
+
+ native void nSamplerBegin();
+ native void nSamplerSet(int param, int value);
+ native int nSamplerCreate();
+
+ native void nProgramFragmentStoreBegin(int in, int out);
+ native void nProgramFragmentStoreDepthFunc(int func);
+ native void nProgramFragmentStoreDepthMask(boolean enable);
+ native void nProgramFragmentStoreColorMask(boolean r, boolean g, boolean b, boolean a);
+ native void nProgramFragmentStoreBlendFunc(int src, int dst);
+ native void nProgramFragmentStoreDither(boolean enable);
+ native int nProgramFragmentStoreCreate();
+
+ native void nProgramFragmentBegin(int in, int out, boolean pointSpriteEnable);
+ native void nProgramFragmentBindTexture(int vpf, int slot, int a);
+ native void nProgramFragmentBindSampler(int vpf, int slot, int s);
+ native void nProgramFragmentSetSlot(int slot, boolean enable, int env, int vt);
+ native int nProgramFragmentCreate();
+
+ native void nProgramVertexBindAllocation(int pv, int mID);
+ native void nProgramVertexBegin(int inID, int outID);
+ native void nProgramVertexSetTextureMatrixEnable(boolean enable);
+ native void nProgramVertexAddLight(int id);
+ native int nProgramVertexCreate();
+
+ native void nLightBegin();
+ native void nLightSetIsMono(boolean isMono);
+ native void nLightSetIsLocal(boolean isLocal);
+ native int nLightCreate();
+ native void nLightSetColor(int l, float r, float g, float b);
+ native void nLightSetPosition(int l, float x, float y, float z);
+
+ native int nSimpleMeshCreate(int batchID, int idxID, int[] vtxID, int prim);
+ native void nSimpleMeshBindVertex(int id, int alloc, int slot);
+ native void nSimpleMeshBindIndex(int id, int alloc);
+
+ native void nAnimationBegin(int attribCount, int keyframeCount);
+ native void nAnimationAdd(float time, float[] attribs);
+ native int nAnimationCreate();
+
+ private int mDev;
+ private int mContext;
+ @SuppressWarnings({"FieldCanBeLocal"})
+ private Surface mSurface;
+
+ private static boolean mElementsInitialized = false;
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ //
+
+ public RenderScript(Surface sur, boolean useDepth) {
+ mSurface = sur;
+ mDev = nDeviceCreate();
+ mContext = nContextCreate(mDev, mSurface, 0, useDepth);
+
+ // TODO: This should be protected by a lock
+ if(!mElementsInitialized) {
+ Element.initPredefined(this);
+ mElementsInitialized = true;
+ }
+ }
+
+ public void destroy() {
+ nContextDestroy(mContext);
+ mContext = 0;
+
+ nDeviceDestroy(mDev);
+ mDev = 0;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // Triangle Mesh
+
+ public class TriangleMesh extends BaseObj {
+ TriangleMesh(int id) {
+ super(RenderScript.this);
+ mID = id;
+ }
+ }
+
+ public void triangleMeshBegin(Element vertex, Element index) {
+ nTriangleMeshBegin(vertex.mID, index.mID);
+ }
+
+ public void triangleMeshAddVertex_XY(float x, float y) {
+ nTriangleMeshAddVertex_XY(x, y);
+ }
+
+ public void triangleMeshAddVertex_XYZ(float x, float y, float z) {
+ nTriangleMeshAddVertex_XYZ(x, y, z);
+ }
+
+ public void triangleMeshAddVertex_XY_ST(float x, float y, float s, float t) {
+ nTriangleMeshAddVertex_XY_ST(x, y, s, t);
+ }
+
+ public void triangleMeshAddVertex_XYZ_ST(float x, float y, float z, float s, float t) {
+ nTriangleMeshAddVertex_XYZ_ST(x, y, z, s, t);
+ }
+
+ public void triangleMeshAddVertex_XYZ_ST_NORM(float x, float y, float z, float s, float t, float nx, float ny, float nz) {
+ nTriangleMeshAddVertex_XYZ_ST_NORM(x, y, z, s, t, nx, ny, nz);
+ }
+
+ public void triangleMeshAddTriangle(int i1, int i2, int i3) {
+ nTriangleMeshAddTriangle(i1, i2, i3);
+ }
+
+ public TriangleMesh triangleMeshCreate() {
+ int id = nTriangleMeshCreate();
+ return new TriangleMesh(id);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // File
+
+ public class File extends BaseObj {
+ File(int id) {
+ super(RenderScript.this);
+ mID = id;
+ }
+ }
+
+ public File fileOpen(String s) throws IllegalStateException, IllegalArgumentException
+ {
+ if(s.length() < 1) {
+ throw new IllegalArgumentException("fileOpen does not accept a zero length string.");
+ }
+
+ try {
+ byte[] bytes = s.getBytes("UTF-8");
+ int id = nFileOpen(bytes);
+ return new File(id);
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ ///////////////////////////////////////////////////////////////////////////////////
+ // Root state
+
+ public void contextBindRootScript(Script s) {
+ int id = 0;
+ if(s != null) {
+ id = s.mID;
+ }
+ nContextBindRootScript(id);
+ }
+
+ //public void contextBindSampler(Sampler s, int slot) {
+ //nContextBindSampler(s.mID);
+ //}
+
+ public void contextBindProgramFragmentStore(ProgramStore pfs) {
+ nContextBindProgramFragmentStore(pfs.mID);
+ }
+
+ public void contextBindProgramFragment(ProgramFragment pf) {
+ nContextBindProgramFragment(pf.mID);
+ }
+
+ public void contextBindProgramVertex(ProgramVertex pf) {
+ nContextBindProgramVertex(pf.mID);
+ }
+
+}
+
+
diff --git a/graphics/java/android/renderscript/Sampler.java b/graphics/java/android/renderscript/Sampler.java
new file mode 100644
index 0000000..5e0b110
--- /dev/null
+++ b/graphics/java/android/renderscript/Sampler.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Config;
+import android.util.Log;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+/**
+ * @hide
+ *
+ **/
+public class Sampler extends BaseObj {
+ public enum Value {
+ NEAREST (0),
+ LINEAR (1),
+ LINEAR_MIP_LINEAR (2),
+ WRAP (3),
+ CLAMP (4);
+
+ int mID;
+ Value(int id) {
+ mID = id;
+ }
+ }
+
+ Sampler(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public static class Builder {
+ RenderScript mRS;
+ Value mMin;
+ Value mMag;
+ Value mWrapS;
+ Value mWrapT;
+ Value mWrapR;
+
+ public Builder(RenderScript rs) {
+ mRS = rs;
+ mMin = Value.NEAREST;
+ mMag = Value.NEAREST;
+ mWrapS = Value.WRAP;
+ mWrapT = Value.WRAP;
+ mWrapR = Value.WRAP;
+ }
+
+ public void setMin(Value v) {
+ mMin = v;
+ }
+
+ public void setMag(Value v) {
+ mMag = v;
+ }
+
+ public void setWrapS(Value v) {
+ mWrapS = v;
+ }
+
+ public void setWrapT(Value v) {
+ mWrapT = v;
+ }
+
+ public void setWrapR(Value v) {
+ mWrapR = v;
+ }
+
+ static synchronized Sampler internalCreate(RenderScript rs, Builder b) {
+ rs.nSamplerBegin();
+ rs.nSamplerSet(0, b.mMin.mID);
+ rs.nSamplerSet(1, b.mMag.mID);
+ rs.nSamplerSet(2, b.mWrapS.mID);
+ rs.nSamplerSet(3, b.mWrapT.mID);
+ rs.nSamplerSet(4, b.mWrapR.mID);
+ int id = rs.nSamplerCreate();
+ return new Sampler(id, rs);
+ }
+
+ public Sampler create() {
+ return internalCreate(mRS, this);
+ }
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/Script.java b/graphics/java/android/renderscript/Script.java
new file mode 100644
index 0000000..a402471
--- /dev/null
+++ b/graphics/java/android/renderscript/Script.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+/**
+ * @hide
+ **/
+public class Script extends BaseObj {
+ public static final int MAX_SLOT = 16;
+
+ boolean mIsRoot;
+ Type[] mTypes;
+ boolean[] mWritable;
+
+ Script(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void bindAllocation(Allocation va, int slot) {
+ mRS.nScriptBindAllocation(mID, va.mID, slot);
+ }
+
+ public void setClearColor(float r, float g, float b, float a) {
+ mRS.nScriptSetClearColor(mID, r, g, b, a);
+ }
+
+ public void setClearDepth(float d) {
+ mRS.nScriptSetClearDepth(mID, d);
+ }
+
+ public void setClearStencil(int stencil) {
+ mRS.nScriptSetClearStencil(mID, stencil);
+ }
+
+ public void setTimeZone(String timeZone) {
+ try {
+ mRS.nScriptSetTimeZone(mID, timeZone.getBytes("UTF-8"));
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static class Builder {
+ RenderScript mRS;
+ boolean mIsRoot = false;
+ Type[] mTypes;
+ String[] mNames;
+ boolean[] mWritable;
+
+ Builder(RenderScript rs) {
+ mRS = rs;
+ mTypes = new Type[MAX_SLOT];
+ mNames = new String[MAX_SLOT];
+ mWritable = new boolean[MAX_SLOT];
+ }
+
+ public void setType(Type t, int slot) {
+ mTypes[slot] = t;
+ mNames[slot] = null;
+ }
+
+ public void setType(Type t, String name, int slot) {
+ mTypes[slot] = t;
+ mNames[slot] = name;
+ }
+
+ public void setType(boolean writable, int slot) {
+ mWritable[slot] = writable;
+ }
+
+ void transferCreate() {
+ mRS.nScriptSetRoot(mIsRoot);
+ for(int ct=0; ct < mTypes.length; ct++) {
+ if(mTypes[ct] != null) {
+ mRS.nScriptSetType(mTypes[ct].mID, mWritable[ct], mNames[ct], ct);
+ }
+ }
+ }
+
+ void transferObject(Script s) {
+ s.mIsRoot = mIsRoot;
+ s.mTypes = mTypes;
+ }
+
+ public void setRoot(boolean r) {
+ mIsRoot = r;
+ }
+
+ }
+
+}
+
diff --git a/graphics/java/android/renderscript/ScriptC.java b/graphics/java/android/renderscript/ScriptC.java
new file mode 100644
index 0000000..bb99e23
--- /dev/null
+++ b/graphics/java/android/renderscript/ScriptC.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map.Entry;
+import java.util.HashMap;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * @hide
+ **/
+public class ScriptC extends Script {
+ private static final String TAG = "ScriptC";
+
+ ScriptC(int id, RenderScript rs) {
+ super(id, rs);
+ }
+
+ public static class Builder extends Script.Builder {
+ byte[] mProgram;
+ int mProgramLength;
+ HashMap<String,Integer> mIntDefines = new HashMap();
+ HashMap<String,Float> mFloatDefines = new HashMap();
+
+ public Builder(RenderScript rs) {
+ super(rs);
+ }
+
+ public void setScript(String s) {
+ try {
+ mProgram = s.getBytes("UTF-8");
+ mProgramLength = mProgram.length;
+ } catch (java.io.UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setScript(Resources resources, int id) {
+ InputStream is = resources.openRawResource(id);
+ try {
+ try {
+ setScript(is);
+ } finally {
+ is.close();
+ }
+ } catch(IOException e) {
+ throw new Resources.NotFoundException();
+ }
+ }
+
+ public void setScript(InputStream is) throws IOException {
+ byte[] buf = new byte[1024];
+ int currentPos = 0;
+ while(true) {
+ int bytesLeft = buf.length - currentPos;
+ if (bytesLeft == 0) {
+ byte[] buf2 = new byte[buf.length * 2];
+ System.arraycopy(buf, 0, buf2, 0, buf.length);
+ buf = buf2;
+ bytesLeft = buf.length - currentPos;
+ }
+ int bytesRead = is.read(buf, currentPos, bytesLeft);
+ if (bytesRead <= 0) {
+ break;
+ }
+ currentPos += bytesRead;
+ }
+ mProgram = buf;
+ mProgramLength = currentPos;
+ }
+
+ static synchronized ScriptC internalCreate(Builder b) {
+ b.mRS.nScriptCBegin();
+ b.transferCreate();
+
+ for (Entry<String,Integer> e: b.mIntDefines.entrySet()) {
+ b.mRS.nScriptCAddDefineI32(e.getKey(), e.getValue().intValue());
+ }
+ for (Entry<String,Float> e: b.mFloatDefines.entrySet()) {
+ b.mRS.nScriptCAddDefineF(e.getKey(), e.getValue().floatValue());
+ }
+
+ b.mRS.nScriptCSetScript(b.mProgram, 0, b.mProgramLength);
+
+ int id = b.mRS.nScriptCCreate();
+ ScriptC obj = new ScriptC(id, b.mRS);
+ b.transferObject(obj);
+
+ return obj;
+ }
+
+ public void addDefine(String name, int value) {
+ mIntDefines.put(name, value);
+ }
+
+ public void addDefine(String name, float value) {
+ mFloatDefines.put(name, value);
+ }
+
+ /**
+ * Takes the all public static final fields for a class, and adds defines
+ * for them, using the name of the field as the name of the define.
+ */
+ public void addDefines(Class cl) {
+ addDefines(cl.getFields(), (Modifier.STATIC | Modifier.FINAL | Modifier.PUBLIC), null);
+ }
+
+ /**
+ * Takes the all public fields for an object, and adds defines
+ * for them, using the name of the field as the name of the define.
+ */
+ public void addDefines(Object o) {
+ addDefines(o.getClass().getFields(), Modifier.PUBLIC, o);
+ }
+
+ void addDefines(Field[] fields, int mask, Object o) {
+ for (Field f: fields) {
+ try {
+ if ((f.getModifiers() & mask) == mask) {
+ Class t = f.getType();
+ if (t == int.class) {
+ mIntDefines.put(f.getName(), f.getInt(o));
+ }
+ else if (t == float.class) {
+ mFloatDefines.put(f.getName(), f.getFloat(o));
+ }
+ }
+ } catch (IllegalAccessException ex) {
+ // TODO: Do we want this log?
+ Log.d(TAG, "addDefines skipping field " + f.getName());
+ }
+ }
+ }
+
+ public ScriptC create() {
+ return internalCreate(this);
+ }
+ }
+}
+
diff --git a/graphics/java/android/renderscript/SimpleMesh.java b/graphics/java/android/renderscript/SimpleMesh.java
new file mode 100644
index 0000000..5d87654
--- /dev/null
+++ b/graphics/java/android/renderscript/SimpleMesh.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * @hide
+ *
+ **/
+public class SimpleMesh extends BaseObj {
+ Type[] mVertexTypes;
+ Type mIndexType;
+ //Type mBatcheType;
+ Primitive mPrimitive;
+
+ SimpleMesh(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ }
+
+ public void bindVertexAllocation(Allocation a, int slot) {
+ mRS.nSimpleMeshBindVertex(mID, a.mID, slot);
+ }
+
+ public void bindIndexAllocation(Allocation a) {
+ mRS.nSimpleMeshBindIndex(mID, a.mID);
+ }
+
+ public Allocation createVertexAllocation(int slot) {
+ return Allocation.createTyped(mRS, mVertexTypes[slot]);
+ }
+
+ public Allocation createIndexAllocation() {
+ return Allocation.createTyped(mRS, mIndexType);
+ }
+
+ public Type getVertexType(int slot) {
+ return mVertexTypes[slot];
+ }
+
+ public Type getIndexType() {
+ return mIndexType;
+ }
+
+ public static class Builder {
+ RenderScript mRS;
+
+ class Entry {
+ Type t;
+ Element e;
+ int size;
+ }
+
+ int mVertexTypeCount;
+ Entry[] mVertexTypes;
+ Entry mIndexType;
+ //Entry mBatchType;
+ Primitive mPrimitive;
+
+
+ public Builder(RenderScript rs) {
+ mRS = rs;
+ mVertexTypeCount = 0;
+ mVertexTypes = new Entry[16];
+ mIndexType = new Entry();
+ }
+
+ public int addVertexType(Type t) throws IllegalStateException {
+ if(mVertexTypeCount >= mVertexTypes.length) {
+ throw new IllegalStateException("Max vertex types exceeded.");
+ }
+
+ int addedIndex = mVertexTypeCount;
+ mVertexTypes[mVertexTypeCount] = new Entry();
+ mVertexTypes[mVertexTypeCount].t = t;
+ mVertexTypeCount++;
+ return addedIndex;
+ }
+
+ public int addVertexType(Element e, int size) throws IllegalStateException {
+ if(mVertexTypeCount >= mVertexTypes.length) {
+ throw new IllegalStateException("Max vertex types exceeded.");
+ }
+
+ int addedIndex = mVertexTypeCount;
+ mVertexTypes[mVertexTypeCount] = new Entry();
+ mVertexTypes[mVertexTypeCount].e = e;
+ mVertexTypes[mVertexTypeCount].size = size;
+ mVertexTypeCount++;
+ return addedIndex;
+ }
+
+ public void setIndexType(Type t) {
+ mIndexType.t = t;
+ mIndexType.e = null;
+ mIndexType.size = 0;
+ }
+
+ public void setIndexType(Element e, int size) {
+ mIndexType.t = null;
+ mIndexType.e = e;
+ mIndexType.size = size;
+ }
+
+ public void setPrimitive(Primitive p) {
+ mPrimitive = p;
+ }
+
+
+ Type newType(Element e, int size) {
+ Type.Builder tb = new Type.Builder(mRS, e);
+ tb.add(Dimension.X, size);
+ return tb.create();
+ }
+
+ static synchronized SimpleMesh internalCreate(RenderScript rs, Builder b) {
+ Type[] toDestroy = new Type[18];
+ int toDestroyCount = 0;
+
+ int indexID = 0;
+ if(b.mIndexType.t != null) {
+ indexID = b.mIndexType.t.mID;
+ } else if(b.mIndexType.size != 0) {
+ b.mIndexType.t = b.newType(b.mIndexType.e, b.mIndexType.size);
+ indexID = b.mIndexType.t.mID;
+ toDestroy[toDestroyCount++] = b.mIndexType.t;
+ }
+
+ int[] IDs = new int[b.mVertexTypeCount];
+ for(int ct=0; ct < b.mVertexTypeCount; ct++) {
+ if(b.mVertexTypes[ct].t != null) {
+ IDs[ct] = b.mVertexTypes[ct].t.mID;
+ } else {
+ b.mVertexTypes[ct].t = b.newType(b.mVertexTypes[ct].e, b.mVertexTypes[ct].size);
+ IDs[ct] = b.mVertexTypes[ct].t.mID;
+ toDestroy[toDestroyCount++] = b.mVertexTypes[ct].t;
+ }
+ }
+
+ int id = rs.nSimpleMeshCreate(0, indexID, IDs, b.mPrimitive.mID);
+ for(int ct=0; ct < toDestroyCount; ct++) {
+ toDestroy[ct].destroy();
+ }
+
+ return new SimpleMesh(id, rs);
+ }
+
+ public SimpleMesh create() {
+ Log.e("rs", "SimpleMesh create");
+ SimpleMesh sm = internalCreate(mRS, this);
+ sm.mVertexTypes = new Type[mVertexTypeCount];
+ for(int ct=0; ct < mVertexTypeCount; ct++) {
+ sm.mVertexTypes[ct] = mVertexTypes[ct].t;
+ }
+ sm.mIndexType = mIndexType.t;
+ sm.mPrimitive = mPrimitive;
+ return sm;
+ }
+ }
+
+ public static class TriangleMeshBuilder {
+ float mVtxData[];
+ int mVtxCount;
+ int mIndexData[];
+ int mIndexCount;
+ RenderScript mRS;
+ Element mElement;
+
+ int mVtxSize;
+ boolean mNorm;
+ boolean mTex;
+
+ public TriangleMeshBuilder(RenderScript rs, int vtxSize, boolean norm, boolean tex) {
+ mRS = rs;
+ mVtxCount = 0;
+ mIndexCount = 0;
+ mVtxData = new float[128];
+ mIndexData = new int[128];
+ mVtxSize = vtxSize;
+ mNorm = norm;
+ mTex = tex;
+
+ if(vtxSize < 2 || vtxSize > 3) {
+ throw new IllegalArgumentException("Vertex size out of range.");
+ }
+ }
+
+ private void makeSpace(int count) {
+ if((mVtxCount + count) >= mVtxData.length) {
+ float t[] = new float[mVtxData.length * 2];
+ System.arraycopy(mVtxData, 0, t, 0, mVtxData.length);
+ mVtxData = t;
+ }
+ }
+
+ public void add_XY(float x, float y) {
+ if((mVtxSize != 2) || mNorm || mTex) {
+ throw new IllegalStateException("add mistmatch with declaired components.");
+ }
+ makeSpace(2);
+ mVtxData[mVtxCount++] = x;
+ mVtxData[mVtxCount++] = y;
+ }
+
+ public void add_XYZ(float x, float y, float z) {
+ if((mVtxSize != 3) || mNorm || mTex) {
+ throw new IllegalStateException("add mistmatch with declaired components.");
+ }
+ makeSpace(3);
+ mVtxData[mVtxCount++] = x;
+ mVtxData[mVtxCount++] = y;
+ mVtxData[mVtxCount++] = z;
+ }
+
+ public void add_XY_ST(float x, float y, float s, float t) {
+ if((mVtxSize != 2) || mNorm || !mTex) {
+ throw new IllegalStateException("add mistmatch with declaired components.");
+ }
+ makeSpace(4);
+ mVtxData[mVtxCount++] = x;
+ mVtxData[mVtxCount++] = y;
+ mVtxData[mVtxCount++] = s;
+ mVtxData[mVtxCount++] = t;
+ }
+
+ public void add_XYZ_ST(float x, float y, float z, float s, float t) {
+ if((mVtxSize != 3) || mNorm || !mTex) {
+ throw new IllegalStateException("add mistmatch with declaired components.");
+ }
+ makeSpace(5);
+ mVtxData[mVtxCount++] = x;
+ mVtxData[mVtxCount++] = y;
+ mVtxData[mVtxCount++] = z;
+ mVtxData[mVtxCount++] = s;
+ mVtxData[mVtxCount++] = t;
+ }
+
+ public void add_XYZ_ST_NORM(float x, float y, float z, float s, float t, float nx, float ny, float nz) {
+ if((mVtxSize != 3) || !mNorm || !mTex) {
+ throw new IllegalStateException("add mistmatch with declaired components.");
+ }
+ makeSpace(8);
+ mVtxData[mVtxCount++] = x;
+ mVtxData[mVtxCount++] = y;
+ mVtxData[mVtxCount++] = z;
+ mVtxData[mVtxCount++] = s;
+ mVtxData[mVtxCount++] = t;
+ mVtxData[mVtxCount++] = nx;
+ mVtxData[mVtxCount++] = ny;
+ mVtxData[mVtxCount++] = nz;
+ }
+
+ public void addTriangle(int idx1, int idx2, int idx3) {
+ if((mIndexCount + 3) >= mIndexData.length) {
+ int t[] = new int[mIndexData.length * 2];
+ System.arraycopy(mIndexData, 0, t, 0, mIndexData.length);
+ mIndexData = t;
+ }
+ mIndexData[mIndexCount++] = idx1;
+ mIndexData[mIndexCount++] = idx2;
+ mIndexData[mIndexCount++] = idx3;
+ }
+
+ public SimpleMesh create() {
+ Element.Builder b = new Element.Builder(mRS);
+ int floatCount = mVtxSize;
+ if(mVtxSize == 2) {
+ b.addFloatXY();
+ } else {
+ b.addFloatXYZ();
+ }
+ if(mTex) {
+ floatCount += 2;
+ b.addFloatST();
+ }
+ if(mNorm) {
+ floatCount += 3;
+ b.addFloatNorm();
+ }
+ mElement = b.create();
+
+ Builder smb = new Builder(mRS);
+ smb.addVertexType(mElement, mVtxCount / floatCount);
+ smb.setIndexType(Element.INDEX_16, mIndexCount);
+ smb.setPrimitive(Primitive.TRIANGLE);
+ SimpleMesh sm = smb.create();
+
+ Allocation vertexAlloc = sm.createVertexAllocation(0);
+ Allocation indexAlloc = sm.createIndexAllocation();
+ sm.bindVertexAllocation(vertexAlloc, 0);
+ sm.bindIndexAllocation(indexAlloc);
+
+ vertexAlloc.data(mVtxData);
+ vertexAlloc.uploadToBufferObject();
+
+ // This is safe because length is a pow2
+ for(int ct=0; ct < (mIndexCount+1); ct += 2) {
+ mIndexData[ct >> 1] = mIndexData[ct] | (mIndexData[ct+1] << 16);
+ }
+ indexAlloc.data(mIndexData);
+ indexAlloc.uploadToBufferObject();
+
+ return sm;
+ }
+ }
+}
+
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
new file mode 100644
index 0000000..b6b7adf
--- /dev/null
+++ b/graphics/java/android/renderscript/Type.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript;
+
+import java.lang.reflect.Field;
+
+/**
+ * @hide
+ *
+ **/
+public class Type extends BaseObj {
+ Dimension[] mDimensions;
+ int[] mValues;
+ Element mElement;
+ private int mNativeCache;
+ Class mJavaClass;
+
+
+ Type(int id, RenderScript rs) {
+ super(rs);
+ mID = id;
+ mNativeCache = 0;
+ }
+
+ protected void finalize() throws Throwable {
+ if(mNativeCache != 0) {
+ mRS.nTypeFinalDestroy(this);
+ mNativeCache = 0;
+ }
+ super.finalize();
+ }
+
+ public static Type createFromClass(RenderScript rs, Class c, int size) {
+ Element e = Element.createFromClass(rs, c);
+ Builder b = new Builder(rs, e);
+ b.add(Dimension.X, size);
+ Type t = b.create();
+ e.destroy();
+
+ // native fields
+ {
+ Field[] fields = c.getFields();
+ int[] arTypes = new int[fields.length];
+ int[] arBits = new int[fields.length];
+
+ for(int ct=0; ct < fields.length; ct++) {
+ Field f = fields[ct];
+ Class fc = f.getType();
+ if(fc == int.class) {
+ arTypes[ct] = Element.DataType.SIGNED.mID;
+ arBits[ct] = 32;
+ } else if(fc == short.class) {
+ arTypes[ct] = Element.DataType.SIGNED.mID;
+ arBits[ct] = 16;
+ } else if(fc == byte.class) {
+ arTypes[ct] = Element.DataType.SIGNED.mID;
+ arBits[ct] = 8;
+ } else if(fc == float.class) {
+ arTypes[ct] = Element.DataType.FLOAT.mID;
+ arBits[ct] = 32;
+ } else {
+ throw new IllegalArgumentException("Unkown field type");
+ }
+ }
+ rs.nTypeSetupFields(t, arTypes, arBits, fields);
+ }
+ t.mJavaClass = c;
+ return t;
+ }
+
+ public static Type createFromClass(RenderScript rs, Class c, int size, String scriptName) {
+ Type t = createFromClass(rs, c, size);
+ t.setName(scriptName);
+ return t;
+ }
+
+
+ public static class Builder {
+ RenderScript mRS;
+ Entry[] mEntries;
+ int mEntryCount;
+ Element mElement;
+
+ class Entry {
+ Dimension mDim;
+ int mValue;
+ }
+
+ public Builder(RenderScript rs, Element e) {
+ mRS = rs;
+ mEntries = new Entry[4];
+ mElement = e;
+ }
+
+ public void add(Dimension d, int value) {
+ if(mEntries.length >= mEntryCount) {
+ Entry[] en = new Entry[mEntryCount + 8];
+ System.arraycopy(mEntries, 0, en, 0, mEntries.length);
+ mEntries = en;
+ }
+ mEntries[mEntryCount] = new Entry();
+ mEntries[mEntryCount].mDim = d;
+ mEntries[mEntryCount].mValue = value;
+ mEntryCount++;
+ }
+
+ static synchronized Type internalCreate(RenderScript rs, Builder b) {
+ rs.nTypeBegin(b.mElement.mID);
+ for (int ct=0; ct < b.mEntryCount; ct++) {
+ Entry en = b.mEntries[ct];
+ rs.nTypeAdd(en.mDim.mID, en.mValue);
+ }
+ int id = rs.nTypeCreate();
+ return new Type(id, rs);
+ }
+
+ public Type create() {
+ Type t = internalCreate(mRS, this);
+ t.mElement = mElement;
+ t.mDimensions = new Dimension[mEntryCount];
+ t.mValues = new int[mEntryCount];
+ for(int ct=0; ct < mEntryCount; ct++) {
+ t.mDimensions[ct] = mEntries[ct].mDim;
+ t.mValues[ct] = mEntries[ct].mValue;
+ }
+ return t;
+ }
+ }
+
+}
diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk
new file mode 100644
index 0000000..799fcbb
--- /dev/null
+++ b/graphics/jni/Android.mk
@@ -0,0 +1,45 @@
+
+# libRS needs libacc, which isn't 64-bit clean, and so can't be built
+# for the simulator on gHardy, and therefore libRS needs to be excluded
+# from the simulator as well, and so in turn librs_jni needs to be
+# excluded.
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ android_renderscript_RenderScript.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libacc \
+ libnativehelper \
+ libRS \
+ libcutils \
+ libskia \
+ libutils \
+ libui
+
+LOCAL_STATIC_LIBRARIES :=
+
+rs_generated_include_dir := $(call intermediates-dir-for,SHARED_LIBRARIES,libRS,,)
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(LOCAL_PATH)/../../libs/rs \
+ $(rs_generated_include_dir) \
+ $(call include-path-for, corecg graphics)
+
+LOCAL_CFLAGS +=
+
+LOCAL_LDLIBS := -lpthread
+LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h)
+LOCAL_MODULE:= librs_jni
+LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source)
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := libRS
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif #simulator
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
new file mode 100644
index 0000000..a94ccb1
--- /dev/null
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -0,0 +1,1384 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+#define LOG_TAG "libRS_jni"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <math.h>
+#include <utils/misc.h>
+
+#include <ui/Surface.h>
+
+#include <core/SkBitmap.h>
+#include <core/SkPixelRef.h>
+#include <core/SkStream.h>
+#include <core/SkTemplates.h>
+#include <images/SkImageDecoder.h>
+
+#include <utils/Asset.h>
+#include <utils/ResourceTypes.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <RenderScript.h>
+#include <RenderScriptEnv.h>
+
+//#define LOG_API LOGE
+#define LOG_API(...)
+
+using namespace android;
+
+// ---------------------------------------------------------------------------
+
+static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+ jclass npeClazz = env->FindClass(exc);
+ env->ThrowNew(npeClazz, msg);
+}
+
+static jfieldID gContextId = 0;
+static jfieldID gNativeBitmapID = 0;
+static jfieldID gTypeNativeCache = 0;
+
+static RsElement g_A_8 = NULL;
+static RsElement g_RGBA_4444 = NULL;
+static RsElement g_RGBA_8888 = NULL;
+static RsElement g_RGB_565 = NULL;
+
+static void _nInit(JNIEnv *_env, jclass _this)
+{
+ gContextId = _env->GetFieldID(_this, "mContext", "I");
+
+ jclass bitmapClass = _env->FindClass("android/graphics/Bitmap");
+ gNativeBitmapID = _env->GetFieldID(bitmapClass, "mNativeBitmap", "I");
+
+ jclass typeClass = _env->FindClass("android/renderscript/Type");
+ gTypeNativeCache = _env->GetFieldID(typeClass, "mNativeCache", "I");
+}
+
+static void nInitElements(JNIEnv *_env, jobject _this, jint a8, jint rgba4444, jint rgba8888, jint rgb565)
+{
+ g_A_8 = reinterpret_cast<RsElement>(a8);
+ g_RGBA_4444 = reinterpret_cast<RsElement>(rgba4444);
+ g_RGBA_8888 = reinterpret_cast<RsElement>(rgba8888);
+ g_RGB_565 = reinterpret_cast<RsElement>(rgb565);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nAssignName(JNIEnv *_env, jobject _this, jint obj, jbyteArray str)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAssignName, con(%p), obj(%p)", con, (void *)obj);
+
+ jint len = _env->GetArrayLength(str);
+ jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
+ rsAssignName(con, (void *)obj, (const char *)cptr, len);
+ _env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
+}
+
+static void
+nObjDestroy(JNIEnv *_env, jobject _this, jint obj)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nObjDestroy, con(%p) obj(%p)", con, (void *)obj);
+ rsObjDestroy(con, (void *)obj);
+}
+
+static void
+nObjDestroyOOB(JNIEnv *_env, jobject _this, jint obj)
+{
+ // This function only differs from nObjDestroy in that it calls the
+ // special Out Of Band version of ObjDestroy which is thread safe.
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nObjDestroyOOB, con(%p) obj(%p)", con, (void *)obj);
+ rsObjDestroyOOB(con, (void *)obj);
+}
+
+static jint
+nFileOpen(JNIEnv *_env, jobject _this, jbyteArray str)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nFileOpen, con(%p)", con);
+
+ jint len = _env->GetArrayLength(str);
+ jbyte * cptr = (jbyte *) _env->GetPrimitiveArrayCritical(str, 0);
+ jint ret = (jint)rsFileOpen(con, (const char *)cptr, len);
+ _env->ReleasePrimitiveArrayCritical(str, cptr, JNI_ABORT);
+ return ret;
+}
+
+// ---------------------------------------------------------------------------
+
+static jint
+nDeviceCreate(JNIEnv *_env, jobject _this)
+{
+ LOG_API("nDeviceCreate");
+ return (jint)rsDeviceCreate();
+}
+
+static void
+nDeviceDestroy(JNIEnv *_env, jobject _this, jint dev)
+{
+ LOG_API("nDeviceDestroy");
+ return rsDeviceDestroy((RsDevice)dev);
+}
+
+static jint
+nContextCreate(JNIEnv *_env, jobject _this, jint dev, jobject wnd, jint ver, jboolean useDepth)
+{
+ LOG_API("nContextCreate");
+
+ if (wnd == NULL) {
+ not_valid_surface:
+ doThrow(_env, "java/lang/IllegalArgumentException",
+ "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface");
+ return 0;
+ }
+ jclass surface_class = _env->FindClass("android/view/Surface");
+ jfieldID surfaceFieldID = _env->GetFieldID(surface_class, "mSurface", "I");
+ Surface * window = (Surface*)_env->GetIntField(wnd, surfaceFieldID);
+ if (window == NULL)
+ goto not_valid_surface;
+
+ return (jint)rsContextCreate((RsDevice)dev, window, ver, useDepth);
+}
+
+static void
+nContextDestroy(JNIEnv *_env, jobject _this, jint con)
+{
+ LOG_API("nContextDestroy, con(%p)", (RsContext)con);
+ return rsContextDestroy((RsContext)con);
+}
+
+
+static void
+nElementBegin(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nElementBegin, con(%p)", con);
+ rsElementBegin(con);
+}
+
+
+static void
+nElementAdd(JNIEnv *_env, jobject _this, jint kind, jint type, jint norm, jint bits, jstring name)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ const char* n = NULL;
+ if (name) {
+ n = _env->GetStringUTFChars(name, NULL);
+ }
+ LOG_API("nElementAdd, con(%p), kind(%i), type(%i), norm(%i), bits(%i)", con, kind, type, norm, bits);
+ rsElementAdd(con, (RsDataKind)kind, (RsDataType)type, norm != 0, (size_t)bits, n);
+ if (n) {
+ _env->ReleaseStringUTFChars(name, n);
+ }
+}
+
+static jint
+nElementCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nElementCreate, con(%p)", con);
+ return (jint)rsElementCreate(con);
+}
+
+// -----------------------------------
+
+static void
+nTypeBegin(JNIEnv *_env, jobject _this, jint eID)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTypeBegin, con(%p) e(%p)", con, (RsElement)eID);
+ rsTypeBegin(con, (RsElement)eID);
+}
+
+static void
+nTypeAdd(JNIEnv *_env, jobject _this, jint dim, jint val)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTypeAdd, con(%p) dim(%i), val(%i)", con, dim, val);
+ rsTypeAdd(con, (RsDimension)dim, val);
+}
+
+static jint
+nTypeCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTypeCreate, con(%p)", con);
+ return (jint)rsTypeCreate(con);
+}
+
+static void * SF_LoadInt(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
+{
+ ((int32_t *)buffer)[0] = _env->GetIntField(_obj, _field);
+ return ((uint8_t *)buffer) + 4;
+}
+
+static void * SF_LoadShort(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
+{
+ ((int16_t *)buffer)[0] = _env->GetShortField(_obj, _field);
+ return ((uint8_t *)buffer) + 2;
+}
+
+static void * SF_LoadByte(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
+{
+ ((int8_t *)buffer)[0] = _env->GetByteField(_obj, _field);
+ return ((uint8_t *)buffer) + 1;
+}
+
+static void * SF_LoadFloat(JNIEnv *_env, jobject _obj, jfieldID _field, void *buffer)
+{
+ ((float *)buffer)[0] = _env->GetFloatField(_obj, _field);
+ return ((uint8_t *)buffer) + 4;
+}
+
+struct TypeFieldCache {
+ jfieldID field;
+ int bits;
+ void * (*ptr)(JNIEnv *, jobject, jfieldID, void *buffer);
+};
+
+struct TypeCache {
+ int fieldCount;
+ int size;
+ TypeFieldCache fields[1];
+};
+
+//{"nTypeFinalDestroy", "(Landroid/renderscript/Type;)V", (void*)nTypeFinalDestroy },
+static void
+nTypeFinalDestroy(JNIEnv *_env, jobject _this, jobject _type)
+{
+ TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
+ free(tc);
+}
+
+// native void nTypeSetupFields(Type t, int[] types, int[] bits, Field[] IDs);
+static void
+nTypeSetupFields(JNIEnv *_env, jobject _this, jobject _type, jintArray _types, jintArray _bits, jobjectArray _IDs)
+{
+ int fieldCount = _env->GetArrayLength(_types);
+ size_t structSize = sizeof(TypeCache) + (sizeof(TypeFieldCache) * (fieldCount-1));
+ TypeCache *tc = (TypeCache *)malloc(structSize);
+ memset(tc, 0, structSize);
+
+ TypeFieldCache *tfc = &tc->fields[0];
+ tc->fieldCount = fieldCount;
+ _env->SetIntField(_type, gTypeNativeCache, (jint)tc);
+
+ jint *fType = _env->GetIntArrayElements(_types, NULL);
+ jint *fBits = _env->GetIntArrayElements(_bits, NULL);
+ for (int ct=0; ct < fieldCount; ct++) {
+ jobject field = _env->GetObjectArrayElement(_IDs, ct);
+ tfc[ct].field = _env->FromReflectedField(field);
+ tfc[ct].bits = fBits[ct];
+
+ switch(fType[ct]) {
+ case RS_TYPE_FLOAT:
+ tfc[ct].ptr = SF_LoadFloat;
+ break;
+ case RS_TYPE_UNSIGNED:
+ case RS_TYPE_SIGNED:
+ switch(tfc[ct].bits) {
+ case 32: tfc[ct].ptr = SF_LoadInt; break;
+ case 16: tfc[ct].ptr = SF_LoadShort; break;
+ case 8: tfc[ct].ptr = SF_LoadByte; break;
+ }
+ break;
+ }
+ tc->size += 4;
+ }
+
+ _env->ReleaseIntArrayElements(_types, fType, JNI_ABORT);
+ _env->ReleaseIntArrayElements(_bits, fBits, JNI_ABORT);
+}
+
+
+// -----------------------------------
+
+static jint
+nAllocationCreateTyped(JNIEnv *_env, jobject _this, jint e)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAllocationCreateTyped, con(%p), e(%p)", con, (RsElement)e);
+ return (jint) rsAllocationCreateTyped(con, (RsElement)e);
+}
+
+static jint
+nAllocationCreateSized(JNIEnv *_env, jobject _this, jint e, jint count)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAllocationCreateSized, con(%p), e(%p), count(%i)", con, (RsElement)e, count);
+ return (jint) rsAllocationCreateSized(con, (RsElement)e, count);
+}
+
+static void
+nAllocationUploadToTexture(JNIEnv *_env, jobject _this, jint a, jint mip)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAllocationUploadToTexture, con(%p), a(%p), mip(%i)", con, (RsAllocation)a, mip);
+ rsAllocationUploadToTexture(con, (RsAllocation)a, mip);
+}
+
+static void
+nAllocationUploadToBufferObject(JNIEnv *_env, jobject _this, jint a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAllocationUploadToBufferObject, con(%p), a(%p)", con, (RsAllocation)a);
+ rsAllocationUploadToBufferObject(con, (RsAllocation)a);
+}
+
+static RsElement SkBitmapToPredefined(SkBitmap::Config cfg)
+{
+ switch (cfg) {
+ case SkBitmap::kA8_Config:
+ return g_A_8;
+ case SkBitmap::kARGB_4444_Config:
+ return g_RGBA_4444;
+ case SkBitmap::kARGB_8888_Config:
+ return g_RGBA_8888;
+ case SkBitmap::kRGB_565_Config:
+ return g_RGB_565;
+
+ default:
+ break;
+ }
+ // If we don't have a conversion mark it as a user type.
+ LOGE("Unsupported bitmap type");
+ return NULL;
+}
+
+static int
+nAllocationCreateFromBitmap(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ SkBitmap const * nativeBitmap =
+ (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+ const SkBitmap& bitmap(*nativeBitmap);
+ SkBitmap::Config config = bitmap.getConfig();
+
+ RsElement e = SkBitmapToPredefined(config);
+ if (e) {
+ bitmap.lockPixels();
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ const void* ptr = bitmap.getPixels();
+ jint id = (jint)rsAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
+ bitmap.unlockPixels();
+ return id;
+ }
+ return 0;
+}
+
+static int
+nAllocationCreateFromAssetStream(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jint native_asset)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+
+ Asset* asset = reinterpret_cast<Asset*>(native_asset);
+ SkBitmap bitmap;
+ SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(),
+ &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode);
+
+ SkBitmap::Config config = bitmap.getConfig();
+
+ RsElement e = SkBitmapToPredefined(config);
+
+ if (e) {
+ bitmap.lockPixels();
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ const void* ptr = bitmap.getPixels();
+ jint id = (jint)rsAllocationCreateFromBitmap(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
+ bitmap.unlockPixels();
+ return id;
+ }
+ return 0;
+}
+
+static int
+nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ SkBitmap const * nativeBitmap =
+ (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+ const SkBitmap& bitmap(*nativeBitmap);
+ SkBitmap::Config config = bitmap.getConfig();
+
+ RsElement e = SkBitmapToPredefined(config);
+
+ if (e) {
+ bitmap.lockPixels();
+ const int w = bitmap.width();
+ const int h = bitmap.height();
+ const void* ptr = bitmap.getPixels();
+ jint id = (jint)rsAllocationCreateFromBitmapBoxed(con, w, h, (RsElement)dstFmt, e, genMips, ptr);
+ bitmap.unlockPixels();
+ return id;
+ }
+ return 0;
+}
+
+
+static void
+nAllocationData_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocationData(con, (RsAllocation)alloc, ptr, sizeBytes);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationData_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocationData_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocationData(con, (RsAllocation)alloc, ptr, sizeBytes);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData1D_i(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jintArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData1D_f(JNIEnv *_env, jobject _this, jint alloc, jint offset, jint count, jfloatArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAllocation)alloc, offset, count, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocation1DSubData(con, (RsAllocation)alloc, offset, count, ptr, sizeBytes);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData2D_i(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jintArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocation2DSubData(con, (RsAllocation)alloc, xoff, yoff, w, h, ptr, sizeBytes);
+ _env->ReleaseIntArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationSubData2D_f(JNIEnv *_env, jobject _this, jint alloc, jint xoff, jint yoff, jint w, jint h, jfloatArray data, int sizeBytes)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocation2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)", con, (RsAllocation)alloc, xoff, yoff, w, h, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocation2DSubData(con, (RsAllocation)alloc, xoff, yoff, w, h, ptr, sizeBytes);
+ _env->ReleaseFloatArrayElements(data, ptr, JNI_ABORT);
+}
+
+static void
+nAllocationRead_i(JNIEnv *_env, jobject _this, jint alloc, jintArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocationRead_i, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr);
+ _env->ReleaseIntArrayElements(data, ptr, 0);
+}
+
+static void
+nAllocationRead_f(JNIEnv *_env, jobject _this, jint alloc, jfloatArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAllocationRead_f, con(%p), alloc(%p), len(%i)", con, (RsAllocation)alloc, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAllocationRead(con, (RsAllocation)alloc, ptr);
+ _env->ReleaseFloatArrayElements(data, ptr, 0);
+}
+
+
+//{"nAllocationDataFromObject", "(ILandroid/renderscript/Type;Ljava/lang/Object;)V", (void*)nAllocationDataFromObject },
+static void
+nAllocationSubDataFromObject(JNIEnv *_env, jobject _this, jint alloc, jobject _type, jint offset, jobject _o)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAllocationDataFromObject con(%p), alloc(%p)", con, (RsAllocation)alloc);
+
+ const TypeCache *tc = (TypeCache *)_env->GetIntField(_type, gTypeNativeCache);
+
+ void * bufAlloc = malloc(tc->size);
+ void * buf = bufAlloc;
+ for (int ct=0; ct < tc->fieldCount; ct++) {
+ const TypeFieldCache *tfc = &tc->fields[ct];
+ buf = tfc->ptr(_env, _o, tfc->field, buf);
+ }
+ rsAllocation1DSubData(con, (RsAllocation)alloc, offset, 1, bufAlloc, tc->size);
+ const uint32_t * tmp = (const uint32_t *)bufAlloc;
+ free(bufAlloc);
+}
+
+// -----------------------------------
+
+static void
+nTriangleMeshBegin(JNIEnv *_env, jobject _this, jint v, jint i)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshBegin, con(%p), vertex(%p), index(%p)", con, (RsElement)v, (RsElement)i);
+ rsTriangleMeshBegin(con, (RsElement)v, (RsElement)i);
+}
+
+static void
+nTriangleMeshAddVertex_XY(JNIEnv *_env, jobject _this, jfloat x, jfloat y)
+{
+ float v[] = {x, y};
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddVertex_XY, con(%p), x(%f), y(%f)", con, x, y);
+ rsTriangleMeshAddVertex(con, v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z)
+{
+ float v[] = {x, y, z};
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddVertex_XYZ, con(%p), x(%f), y(%f), z(%f)", con, x, y, z);
+ rsTriangleMeshAddVertex(con, v);
+}
+
+static void
+nTriangleMeshAddVertex_XY_ST(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat s, jfloat t)
+{
+ float v[] = {s, t, x, y};
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddVertex_XY_ST, con(%p), x(%f), y(%f), s(%f), t(%f)", con, x, y, s, t);
+ rsTriangleMeshAddVertex(con, v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ_ST(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z, jfloat s, jfloat t)
+{
+ float v[] = {s, t, x, y, z};
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddVertex_XYZ_ST, con(%p), x(%f), y(%f), z(%f), s(%f), t(%f)", con, x, y, z, s, t);
+ rsTriangleMeshAddVertex(con, v);
+}
+
+static void
+nTriangleMeshAddVertex_XYZ_ST_NORM(JNIEnv *_env, jobject _this, jfloat x, jfloat y, jfloat z, jfloat s, jfloat t, jfloat nx, jfloat ny, jfloat nz)
+{
+ float v[] = {nx, ny, nz, s, t, x, y, z};
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddVertex_XYZ_ST, con(%p), x(%f), y(%f), z(%f), s(%f), t(%f)", con, x, y, z, s, t);
+ rsTriangleMeshAddVertex(con, v);
+}
+
+static void
+nTriangleMeshAddTriangle(JNIEnv *_env, jobject _this, jint i1, jint i2, jint i3)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshAddTriangle, con(%p), i1(%i), i2(%i), i3(%i)", con, i1, i2, i3);
+ rsTriangleMeshAddTriangle(con, i1, i2, i3);
+}
+
+static jint
+nTriangleMeshCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nTriangleMeshCreate, con(%p)", con);
+ return (jint) rsTriangleMeshCreate(con);
+}
+
+// -----------------------------------
+
+static void
+nAdapter1DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter1DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter1D)adapter, (RsAllocation)alloc);
+ rsAdapter1DBindAllocation(con, (RsAdapter1D)adapter, (RsAllocation)alloc);
+}
+
+static void
+nAdapter1DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter1DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter1D)adapter, dim, value);
+ rsAdapter1DSetConstraint(con, (RsAdapter1D)adapter, (RsDimension)dim, value);
+}
+
+static void
+nAdapter1DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter1DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAdapter1DData(con, (RsAdapter1D)adapter, ptr);
+ _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jintArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter1DSubData_i, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAdapter1DSubData(con, (RsAdapter1D)adapter, offset, count, ptr);
+ _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter1DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter1D)adapter, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAdapter1DData(con, (RsAdapter1D)adapter, ptr);
+ _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter1DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint offset, jint count, jfloatArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter1DSubData_f, con(%p), adapter(%p), offset(%i), count(%i), len(%i)", con, (RsAdapter1D)adapter, offset, count, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAdapter1DSubData(con, (RsAdapter1D)adapter, offset, count, ptr);
+ _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static jint
+nAdapter1DCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter1DCreate, con(%p)", con);
+ return (jint)rsAdapter1DCreate(con);
+}
+
+// -----------------------------------
+
+static void
+nAdapter2DBindAllocation(JNIEnv *_env, jobject _this, jint adapter, jint alloc)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter2DBindAllocation, con(%p), adapter(%p), alloc(%p)", con, (RsAdapter2D)adapter, (RsAllocation)alloc);
+ rsAdapter2DBindAllocation(con, (RsAdapter2D)adapter, (RsAllocation)alloc);
+}
+
+static void
+nAdapter2DSetConstraint(JNIEnv *_env, jobject _this, jint adapter, jint dim, jint value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter2DSetConstraint, con(%p), adapter(%p), dim(%i), value(%i)", con, (RsAdapter2D)adapter, dim, value);
+ rsAdapter2DSetConstraint(con, (RsAdapter2D)adapter, (RsDimension)dim, value);
+}
+
+static void
+nAdapter2DData_i(JNIEnv *_env, jobject _this, jint adapter, jintArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter2DData_i, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAdapter2DData(con, (RsAdapter2D)adapter, ptr);
+ _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter2DData_f(JNIEnv *_env, jobject _this, jint adapter, jfloatArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter2DData_f, con(%p), adapter(%p), len(%i)", con, (RsAdapter2D)adapter, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAdapter2DData(con, (RsAdapter2D)adapter, ptr);
+ _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter2DSubData_i(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jintArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter2DSubData_i, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
+ con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
+ jint *ptr = _env->GetIntArrayElements(data, NULL);
+ rsAdapter2DSubData(con, (RsAdapter2D)adapter, xoff, yoff, w, h, ptr);
+ _env->ReleaseIntArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static void
+nAdapter2DSubData_f(JNIEnv *_env, jobject _this, jint adapter, jint xoff, jint yoff, jint w, jint h, jfloatArray data)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(data);
+ LOG_API("nAdapter2DSubData_f, con(%p), adapter(%p), xoff(%i), yoff(%i), w(%i), h(%i), len(%i)",
+ con, (RsAdapter2D)adapter, xoff, yoff, w, h, len);
+ jfloat *ptr = _env->GetFloatArrayElements(data, NULL);
+ rsAdapter2DSubData(con, (RsAdapter1D)adapter, xoff, yoff, w, h, ptr);
+ _env->ReleaseFloatArrayElements(data, ptr, 0/*JNI_ABORT*/);
+}
+
+static jint
+nAdapter2DCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nAdapter2DCreate, con(%p)", con);
+ return (jint)rsAdapter2DCreate(con);
+}
+
+// -----------------------------------
+
+static void
+nScriptBindAllocation(JNIEnv *_env, jobject _this, jint script, jint alloc, jint slot)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptBindAllocation, con(%p), script(%p), alloc(%p), slot(%i)", con, (RsScript)script, (RsAllocation)alloc, slot);
+ rsScriptBindAllocation(con, (RsScript)script, (RsAllocation)alloc, slot);
+}
+
+static void
+nScriptSetClearColor(JNIEnv *_env, jobject _this, jint script, jfloat r, jfloat g, jfloat b, jfloat a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptSetClearColor, con(%p), s(%p), r(%f), g(%f), b(%f), a(%f)", con, (void *)script, r, g, b, a);
+ rsScriptSetClearColor(con, (RsScript)script, r, g, b, a);
+}
+
+static void
+nScriptSetClearDepth(JNIEnv *_env, jobject _this, jint script, jfloat d)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCSetClearDepth, con(%p), s(%p), depth(%f)", con, (void *)script, d);
+ rsScriptSetClearDepth(con, (RsScript)script, d);
+}
+
+static void
+nScriptSetClearStencil(JNIEnv *_env, jobject _this, jint script, jint stencil)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCSetClearStencil, con(%p), s(%p), stencil(%i)", con, (void *)script, stencil);
+ rsScriptSetClearStencil(con, (RsScript)script, stencil);
+}
+
+static void
+nScriptSetTimeZone(JNIEnv *_env, jobject _this, jint script, jbyteArray timeZone)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCSetTimeZone, con(%p), s(%p), timeZone(%s)", con, (void *)script, (const char *)timeZone);
+
+ jint length = _env->GetArrayLength(timeZone);
+ jbyte* timeZone_ptr;
+ timeZone_ptr = (jbyte *) _env->GetPrimitiveArrayCritical(timeZone, (jboolean *)0);
+
+ rsScriptSetTimeZone(con, (RsScript)script, (const char *)timeZone_ptr, length);
+
+ if (timeZone_ptr) {
+ _env->ReleasePrimitiveArrayCritical(timeZone, timeZone_ptr, 0);
+ }
+}
+
+static void
+nScriptSetType(JNIEnv *_env, jobject _this, jint type, jboolean writable, jstring _str, jint slot)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCAddType, con(%p), type(%p), writable(%i), slot(%i)", con, (RsType)type, writable, slot);
+ const char* n = NULL;
+ if (_str) {
+ n = _env->GetStringUTFChars(_str, NULL);
+ }
+ rsScriptSetType(con, (RsType)type, slot, writable, n);
+ if (n) {
+ _env->ReleaseStringUTFChars(_str, n);
+ }
+}
+
+static void
+nScriptSetRoot(JNIEnv *_env, jobject _this, jboolean isRoot)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCSetRoot, con(%p), isRoot(%i)", con, isRoot);
+ rsScriptSetRoot(con, isRoot);
+}
+
+// -----------------------------------
+
+static void
+nScriptCBegin(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCBegin, con(%p)", con);
+ rsScriptCBegin(con);
+}
+
+static void
+nScriptCSetScript(JNIEnv *_env, jobject _this, jbyteArray scriptRef,
+ jint offset, jint length)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("!!! nScriptCSetScript, con(%p)", con);
+ jint _exception = 0;
+ jint remaining;
+ jbyte* script_base = 0;
+ jbyte* script_ptr;
+ if (!scriptRef) {
+ _exception = 1;
+ //_env->ThrowNew(IAEClass, "script == null");
+ goto exit;
+ }
+ if (offset < 0) {
+ _exception = 1;
+ //_env->ThrowNew(IAEClass, "offset < 0");
+ goto exit;
+ }
+ if (length < 0) {
+ _exception = 1;
+ //_env->ThrowNew(IAEClass, "length < 0");
+ goto exit;
+ }
+ remaining = _env->GetArrayLength(scriptRef) - offset;
+ if (remaining < length) {
+ _exception = 1;
+ //_env->ThrowNew(IAEClass, "length > script.length - offset");
+ goto exit;
+ }
+ script_base = (jbyte *)
+ _env->GetPrimitiveArrayCritical(scriptRef, (jboolean *)0);
+ script_ptr = script_base + offset;
+
+ rsScriptCSetText(con, (const char *)script_ptr, length);
+
+exit:
+ if (script_base) {
+ _env->ReleasePrimitiveArrayCritical(scriptRef, script_base,
+ _exception ? JNI_ABORT: 0);
+ }
+}
+
+static jint
+nScriptCCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nScriptCCreate, con(%p)", con);
+ return (jint)rsScriptCCreate(con);
+}
+
+static void
+nScriptCAddDefineI32(JNIEnv *_env, jobject _this, jstring name, jint value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ const char* n = _env->GetStringUTFChars(name, NULL);
+ LOG_API("nScriptCAddDefineI32, con(%p) name(%s) value(%d)", con, n, value);
+ rsScriptCSetDefineI32(con, n, value);
+ _env->ReleaseStringUTFChars(name, n);
+}
+
+static void
+nScriptCAddDefineF(JNIEnv *_env, jobject _this, jstring name, jfloat value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ const char* n = _env->GetStringUTFChars(name, NULL);
+ LOG_API("nScriptCAddDefineF, con(%p) name(%s) value(%f)", con, n, value);
+ rsScriptCSetDefineF(con, n, value);
+ _env->ReleaseStringUTFChars(name, n);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramFragmentStoreBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+ rsProgramFragmentStoreBegin(con, (RsElement)in, (RsElement)out);
+}
+
+static void
+nProgramFragmentStoreDepthFunc(JNIEnv *_env, jobject _this, jint func)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreDepthFunc, con(%p), func(%i)", con, func);
+ rsProgramFragmentStoreDepthFunc(con, (RsDepthFunc)func);
+}
+
+static void
+nProgramFragmentStoreDepthMask(JNIEnv *_env, jobject _this, jboolean enable)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreDepthMask, con(%p), enable(%i)", con, enable);
+ rsProgramFragmentStoreDepthMask(con, enable);
+}
+
+static void
+nProgramFragmentStoreColorMask(JNIEnv *_env, jobject _this, jboolean r, jboolean g, jboolean b, jboolean a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreColorMask, con(%p), r(%i), g(%i), b(%i), a(%i)", con, r, g, b, a);
+ rsProgramFragmentStoreColorMask(con, r, g, b, a);
+}
+
+static void
+nProgramFragmentStoreBlendFunc(JNIEnv *_env, jobject _this, int src, int dst)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreBlendFunc, con(%p), src(%i), dst(%i)", con, src, dst);
+ rsProgramFragmentStoreBlendFunc(con, (RsBlendSrcFunc)src, (RsBlendDstFunc)dst);
+}
+
+static void
+nProgramFragmentStoreDither(JNIEnv *_env, jobject _this, jboolean enable)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreDither, con(%p), enable(%i)", con, enable);
+ rsProgramFragmentStoreDither(con, enable);
+}
+
+static jint
+nProgramFragmentStoreCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentStoreCreate, con(%p)", con);
+
+ return (jint)rsProgramFragmentStoreCreate(con);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramFragmentBegin(JNIEnv *_env, jobject _this, jint in, jint out, jboolean pointSpriteEnable)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentBegin, con(%p), in(%p), out(%p) PointSprite(%i)", con, (RsElement)in, (RsElement)out, pointSpriteEnable);
+ rsProgramFragmentBegin(con, (RsElement)in, (RsElement)out, pointSpriteEnable);
+}
+
+static void
+nProgramFragmentBindTexture(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentBindTexture, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
+ rsProgramFragmentBindTexture(con, (RsProgramFragment)vpf, slot, (RsAllocation)a);
+}
+
+static void
+nProgramFragmentBindSampler(JNIEnv *_env, jobject _this, jint vpf, jint slot, jint a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentBindSampler, con(%p), vpf(%p), slot(%i), a(%p)", con, (RsProgramFragment)vpf, slot, (RsSampler)a);
+ rsProgramFragmentBindSampler(con, (RsProgramFragment)vpf, slot, (RsSampler)a);
+}
+
+static void
+nProgramFragmentSetSlot(JNIEnv *_env, jobject _this, jint slot, jboolean enable, jint env, jint vt)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentSetType, con(%p), slot(%i), enable(%i), env(%i), vt(%p)", con, slot, enable, env, (RsType)vt);
+ rsProgramFragmentSetSlot(con, slot, enable, (RsTexEnvMode)env, (RsType)vt);
+}
+
+static jint
+nProgramFragmentCreate(JNIEnv *_env, jobject _this, jint slot, jboolean enable)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramFragmentCreate, con(%p)", con);
+ return (jint)rsProgramFragmentCreate(con);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nProgramVertexBegin(JNIEnv *_env, jobject _this, jint in, jint out)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexBegin, con(%p), in(%p), out(%p)", con, (RsElement)in, (RsElement)out);
+ rsProgramVertexBegin(con, (RsElement)in, (RsElement)out);
+}
+
+static void
+nProgramVertexBindAllocation(JNIEnv *_env, jobject _this, jint vpv, jint a)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexBindAllocation, con(%p), vpf(%p), a(%p)", con, (RsProgramVertex)vpv, (RsAllocation)a);
+ rsProgramVertexBindAllocation(con, (RsProgramFragment)vpv, (RsAllocation)a);
+}
+
+static void
+nProgramVertexSetTextureMatrixEnable(JNIEnv *_env, jobject _this, jboolean enable)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexSetTextureMatrixEnable, con(%p), enable(%i)", con, enable);
+ rsProgramVertexSetTextureMatrixEnable(con, enable);
+}
+
+static void
+nProgramVertexAddLight(JNIEnv *_env, jobject _this, jint light)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexAddLight, con(%p), light(%p)", con, (RsLight)light);
+ rsProgramVertexAddLight(con, (RsLight)light);
+}
+
+static jint
+nProgramVertexCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nProgramVertexCreate, con(%p)", con);
+ return (jint)rsProgramVertexCreate(con);
+}
+
+
+
+// ---------------------------------------------------------------------------
+
+static void
+nContextBindRootScript(JNIEnv *_env, jobject _this, jint script)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nContextBindRootScript, con(%p), script(%p)", con, (RsScript)script);
+ rsContextBindRootScript(con, (RsScript)script);
+}
+
+static void
+nContextBindProgramFragmentStore(JNIEnv *_env, jobject _this, jint pfs)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nContextBindProgramFragmentStore, con(%p), pfs(%p)", con, (RsProgramFragmentStore)pfs);
+ rsContextBindProgramFragmentStore(con, (RsProgramFragmentStore)pfs);
+}
+
+static void
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, jint pf)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", con, (RsProgramFragment)pf);
+ rsContextBindProgramFragment(con, (RsProgramFragment)pf);
+}
+
+static void
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, jint pf)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", con, (RsProgramVertex)pf);
+ rsContextBindProgramVertex(con, (RsProgramVertex)pf);
+}
+
+static void
+nContextAddDefineI32(JNIEnv *_env, jobject _this, jstring name, jint value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ const char* n = _env->GetStringUTFChars(name, NULL);
+ LOG_API("nScriptCAddDefineI32, con(%p) name(%s) value(%d)", con, n, value);
+ rsContextSetDefineI32(con, n, value);
+ _env->ReleaseStringUTFChars(name, n);
+}
+
+static void
+nContextAddDefineF(JNIEnv *_env, jobject _this, jstring name, jfloat value)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ const char* n = _env->GetStringUTFChars(name, NULL);
+ LOG_API("nScriptCAddDefineF, con(%p) name(%s) value(%f)", con, n, value);
+ rsContextSetDefineF(con, n, value);
+ _env->ReleaseStringUTFChars(name, n);
+}
+
+
+// ---------------------------------------------------------------------------
+
+static void
+nSamplerBegin(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nSamplerBegin, con(%p)", con);
+ rsSamplerBegin(con);
+}
+
+static void
+nSamplerSet(JNIEnv *_env, jobject _this, jint p, jint v)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nSamplerSet, con(%p), param(%i), value(%i)", con, p, v);
+ rsSamplerSet(con, (RsSamplerParam)p, (RsSamplerValue)v);
+}
+
+static jint
+nSamplerCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nSamplerCreate, con(%p)", con);
+ return (jint)rsSamplerCreate(con);
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+nLightBegin(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightBegin, con(%p)", con);
+ rsLightBegin(con);
+}
+
+static void
+nLightSetIsMono(JNIEnv *_env, jobject _this, jboolean isMono)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightSetIsMono, con(%p), isMono(%i)", con, isMono);
+ rsLightSetMonochromatic(con, isMono);
+}
+
+static void
+nLightSetIsLocal(JNIEnv *_env, jobject _this, jboolean isLocal)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightSetIsLocal, con(%p), isLocal(%i)", con, isLocal);
+ rsLightSetLocal(con, isLocal);
+}
+
+static jint
+nLightCreate(JNIEnv *_env, jobject _this)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightCreate, con(%p)", con);
+ return (jint)rsLightCreate(con);
+}
+
+static void
+nLightSetColor(JNIEnv *_env, jobject _this, jint light, float r, float g, float b)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightSetColor, con(%p), light(%p), r(%f), g(%f), b(%f)", con, (RsLight)light, r, g, b);
+ rsLightSetColor(con, (RsLight)light, r, g, b);
+}
+
+static void
+nLightSetPosition(JNIEnv *_env, jobject _this, jint light, float x, float y, float z)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nLightSetPosition, con(%p), light(%p), x(%f), y(%f), z(%f)", con, (RsLight)light, x, y, z);
+ rsLightSetPosition(con, (RsLight)light, x, y, z);
+}
+
+// ---------------------------------------------------------------------------
+
+static jint
+nSimpleMeshCreate(JNIEnv *_env, jobject _this, jint batchID, jint indexID, jintArray vtxIDs, jint primID)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ jint len = _env->GetArrayLength(vtxIDs);
+ LOG_API("nSimpleMeshCreate, con(%p), batchID(%i), indexID(%i), vtxIDs.len(%i), primID(%i)",
+ con, batchID, indexID, len, primID);
+ jint *ptr = _env->GetIntArrayElements(vtxIDs, NULL);
+ int id = (int)rsSimpleMeshCreate(con, (void *)batchID, (void *)indexID, (void **)ptr, len, primID);
+ _env->ReleaseIntArrayElements(vtxIDs, ptr, 0/*JNI_ABORT*/);
+ return id;
+}
+
+static void
+nSimpleMeshBindVertex(JNIEnv *_env, jobject _this, jint s, jint alloc, jint slot)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nSimpleMeshBindVertex, con(%p), SimpleMesh(%p), Alloc(%p), slot(%i)", con, (RsSimpleMesh)s, (RsAllocation)alloc, slot);
+ rsSimpleMeshBindVertex(con, (RsSimpleMesh)s, (RsAllocation)alloc, slot);
+}
+
+static void
+nSimpleMeshBindIndex(JNIEnv *_env, jobject _this, jint s, jint alloc)
+{
+ RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+ LOG_API("nSimpleMeshBindIndex, con(%p), SimpleMesh(%p), Alloc(%p)", con, (RsSimpleMesh)s, (RsAllocation)alloc);
+ rsSimpleMeshBindIndex(con, (RsSimpleMesh)s, (RsAllocation)alloc);
+}
+
+// ---------------------------------------------------------------------------
+
+
+static const char *classPathName = "android/renderscript/RenderScript";
+
+static JNINativeMethod methods[] = {
+{"_nInit", "()V", (void*)_nInit },
+{"nInitElements", "(IIII)V", (void*)nInitElements },
+
+{"nDeviceCreate", "()I", (void*)nDeviceCreate },
+{"nDeviceDestroy", "(I)V", (void*)nDeviceDestroy },
+{"nContextCreate", "(ILandroid/view/Surface;IZ)I", (void*)nContextCreate },
+{"nContextDestroy", "(I)V", (void*)nContextDestroy },
+{"nAssignName", "(I[B)V", (void*)nAssignName },
+{"nObjDestroy", "(I)V", (void*)nObjDestroy },
+{"nObjDestroyOOB", "(I)V", (void*)nObjDestroyOOB },
+
+{"nFileOpen", "([B)I", (void*)nFileOpen },
+
+{"nElementBegin", "()V", (void*)nElementBegin },
+{"nElementAdd", "(IIIILjava/lang/String;)V", (void*)nElementAdd },
+{"nElementCreate", "()I", (void*)nElementCreate },
+
+{"nTypeBegin", "(I)V", (void*)nTypeBegin },
+{"nTypeAdd", "(II)V", (void*)nTypeAdd },
+{"nTypeCreate", "()I", (void*)nTypeCreate },
+{"nTypeFinalDestroy", "(Landroid/renderscript/Type;)V", (void*)nTypeFinalDestroy },
+{"nTypeSetupFields", "(Landroid/renderscript/Type;[I[I[Ljava/lang/reflect/Field;)V", (void*)nTypeSetupFields },
+
+{"nAllocationCreateTyped", "(I)I", (void*)nAllocationCreateTyped },
+{"nAllocationCreateSized", "(II)I", (void*)nAllocationCreateSized },
+{"nAllocationCreateFromBitmap", "(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap },
+{"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmapBoxed },
+{"nAllocationCreateFromAssetStream","(IZI)I", (void*)nAllocationCreateFromAssetStream },
+{"nAllocationUploadToTexture", "(II)V", (void*)nAllocationUploadToTexture },
+{"nAllocationUploadToBufferObject","(I)V", (void*)nAllocationUploadToBufferObject },
+{"nAllocationData", "(I[II)V", (void*)nAllocationData_i },
+{"nAllocationData", "(I[FI)V", (void*)nAllocationData_f },
+{"nAllocationSubData1D", "(III[II)V", (void*)nAllocationSubData1D_i },
+{"nAllocationSubData1D", "(III[FI)V", (void*)nAllocationSubData1D_f },
+{"nAllocationSubData2D", "(IIIII[II)V", (void*)nAllocationSubData2D_i },
+{"nAllocationSubData2D", "(IIIII[FI)V", (void*)nAllocationSubData2D_f },
+{"nAllocationRead", "(I[I)V", (void*)nAllocationRead_i },
+{"nAllocationRead", "(I[F)V", (void*)nAllocationRead_f },
+{"nAllocationSubDataFromObject", "(ILandroid/renderscript/Type;ILjava/lang/Object;)V", (void*)nAllocationSubDataFromObject },
+
+{"nTriangleMeshBegin", "(II)V", (void*)nTriangleMeshBegin },
+{"nTriangleMeshAddVertex_XY", "(FF)V", (void*)nTriangleMeshAddVertex_XY },
+{"nTriangleMeshAddVertex_XYZ", "(FFF)V", (void*)nTriangleMeshAddVertex_XYZ },
+{"nTriangleMeshAddVertex_XY_ST", "(FFFF)V", (void*)nTriangleMeshAddVertex_XY_ST },
+{"nTriangleMeshAddVertex_XYZ_ST", "(FFFFF)V", (void*)nTriangleMeshAddVertex_XYZ_ST },
+{"nTriangleMeshAddVertex_XYZ_ST_NORM", "(FFFFFFFF)V", (void*)nTriangleMeshAddVertex_XYZ_ST_NORM },
+{"nTriangleMeshAddTriangle", "(III)V", (void*)nTriangleMeshAddTriangle },
+{"nTriangleMeshCreate", "()I", (void*)nTriangleMeshCreate },
+
+{"nAdapter1DBindAllocation", "(II)V", (void*)nAdapter1DBindAllocation },
+{"nAdapter1DSetConstraint", "(III)V", (void*)nAdapter1DSetConstraint },
+{"nAdapter1DData", "(I[I)V", (void*)nAdapter1DData_i },
+{"nAdapter1DData", "(I[F)V", (void*)nAdapter1DData_f },
+{"nAdapter1DSubData", "(III[I)V", (void*)nAdapter1DSubData_i },
+{"nAdapter1DSubData", "(III[F)V", (void*)nAdapter1DSubData_f },
+{"nAdapter1DCreate", "()I", (void*)nAdapter1DCreate },
+
+{"nAdapter2DBindAllocation", "(II)V", (void*)nAdapter2DBindAllocation },
+{"nAdapter2DSetConstraint", "(III)V", (void*)nAdapter2DSetConstraint },
+{"nAdapter2DData", "(I[I)V", (void*)nAdapter2DData_i },
+{"nAdapter2DData", "(I[F)V", (void*)nAdapter2DData_f },
+{"nAdapter2DSubData", "(IIIII[I)V", (void*)nAdapter2DSubData_i },
+{"nAdapter2DSubData", "(IIIII[F)V", (void*)nAdapter2DSubData_f },
+{"nAdapter2DCreate", "()I", (void*)nAdapter2DCreate },
+
+{"nScriptBindAllocation", "(III)V", (void*)nScriptBindAllocation },
+{"nScriptSetClearColor", "(IFFFF)V", (void*)nScriptSetClearColor },
+{"nScriptSetClearDepth", "(IF)V", (void*)nScriptSetClearDepth },
+{"nScriptSetClearStencil", "(II)V", (void*)nScriptSetClearStencil },
+{"nScriptSetTimeZone", "(I[B)V", (void*)nScriptSetTimeZone },
+{"nScriptSetType", "(IZLjava/lang/String;I)V", (void*)nScriptSetType },
+{"nScriptSetRoot", "(Z)V", (void*)nScriptSetRoot },
+
+{"nScriptCBegin", "()V", (void*)nScriptCBegin },
+{"nScriptCSetScript", "([BII)V", (void*)nScriptCSetScript },
+{"nScriptCCreate", "()I", (void*)nScriptCCreate },
+{"nScriptCAddDefineI32", "(Ljava/lang/String;I)V", (void*)nScriptCAddDefineI32 },
+{"nScriptCAddDefineF", "(Ljava/lang/String;F)V", (void*)nScriptCAddDefineF },
+
+{"nProgramFragmentStoreBegin", "(II)V", (void*)nProgramFragmentStoreBegin },
+{"nProgramFragmentStoreDepthFunc", "(I)V", (void*)nProgramFragmentStoreDepthFunc },
+{"nProgramFragmentStoreDepthMask", "(Z)V", (void*)nProgramFragmentStoreDepthMask },
+{"nProgramFragmentStoreColorMask", "(ZZZZ)V", (void*)nProgramFragmentStoreColorMask },
+{"nProgramFragmentStoreBlendFunc", "(II)V", (void*)nProgramFragmentStoreBlendFunc },
+{"nProgramFragmentStoreDither", "(Z)V", (void*)nProgramFragmentStoreDither },
+{"nProgramFragmentStoreCreate", "()I", (void*)nProgramFragmentStoreCreate },
+
+{"nProgramFragmentBegin", "(IIZ)V", (void*)nProgramFragmentBegin },
+{"nProgramFragmentBindTexture", "(III)V", (void*)nProgramFragmentBindTexture },
+{"nProgramFragmentBindSampler", "(III)V", (void*)nProgramFragmentBindSampler },
+{"nProgramFragmentSetSlot", "(IZII)V", (void*)nProgramFragmentSetSlot },
+{"nProgramFragmentCreate", "()I", (void*)nProgramFragmentCreate },
+
+{"nProgramVertexBindAllocation", "(II)V", (void*)nProgramVertexBindAllocation },
+{"nProgramVertexBegin", "(II)V", (void*)nProgramVertexBegin },
+{"nProgramVertexSetTextureMatrixEnable", "(Z)V", (void*)nProgramVertexSetTextureMatrixEnable },
+{"nProgramVertexAddLight", "(I)V", (void*)nProgramVertexAddLight },
+{"nProgramVertexCreate", "()I", (void*)nProgramVertexCreate },
+
+{"nLightBegin", "()V", (void*)nLightBegin },
+{"nLightSetIsMono", "(Z)V", (void*)nLightSetIsMono },
+{"nLightSetIsLocal", "(Z)V", (void*)nLightSetIsLocal },
+{"nLightCreate", "()I", (void*)nLightCreate },
+{"nLightSetColor", "(IFFF)V", (void*)nLightSetColor },
+{"nLightSetPosition", "(IFFF)V", (void*)nLightSetPosition },
+
+{"nContextBindRootScript", "(I)V", (void*)nContextBindRootScript },
+{"nContextBindProgramFragmentStore","(I)V", (void*)nContextBindProgramFragmentStore },
+{"nContextBindProgramFragment", "(I)V", (void*)nContextBindProgramFragment },
+{"nContextBindProgramVertex", "(I)V", (void*)nContextBindProgramVertex },
+
+{"nSamplerBegin", "()V", (void*)nSamplerBegin },
+{"nSamplerSet", "(II)V", (void*)nSamplerSet },
+{"nSamplerCreate", "()I", (void*)nSamplerCreate },
+
+{"nSimpleMeshCreate", "(II[II)I", (void*)nSimpleMeshCreate },
+{"nSimpleMeshBindVertex", "(III)V", (void*)nSimpleMeshBindVertex },
+{"nSimpleMeshBindIndex", "(II)V", (void*)nSimpleMeshBindIndex },
+
+};
+
+static int registerFuncs(JNIEnv *_env)
+{
+ return android::AndroidRuntime::registerNativeMethods(
+ _env, classPathName, methods, NELEM(methods));
+}
+
+// ---------------------------------------------------------------------------
+
+jint JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ JNIEnv* env = NULL;
+ jint result = -1;
+
+ if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+ LOGE("ERROR: GetEnv failed\n");
+ goto bail;
+ }
+ assert(env != NULL);
+
+ if (registerFuncs(env) < 0) {
+ LOGE("ERROR: MediaPlayer native registration failed\n");
+ goto bail;
+ }
+
+ /* success -- return valid version number */
+ result = JNI_VERSION_1_4;
+
+bail:
+ return result;
+}
diff --git a/im/java/android/im/BrandingResourceIDs.java b/im/java/android/im/BrandingResourceIDs.java
deleted file mode 100644
index 9960722..0000000
--- a/im/java/android/im/BrandingResourceIDs.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.im;
-
-/**
- * @hide
- * Defines the IDs of branding resources.
- */
-public interface BrandingResourceIDs {
- /**
- * The logo icon of the provider which is displayed in the landing page.
- */
- public static final int DRAWABLE_LOGO = 100;
- /**
- * The icon of online presence status.
- */
- public static final int DRAWABLE_PRESENCE_ONLINE = 102;
- /**
- * The icon of busy presence status.
- */
- public static final int DRAWABLE_PRESENCE_BUSY = 103;
- /**
- * The icon of away presence status.
- */
- public static final int DRAWABLE_PRESENCE_AWAY = 104;
- /**
- * The icon of invisible presence status.
- */
- public static final int DRAWABLE_PRESENCE_INVISIBLE = 105;
- /**
- * The icon of offline presence status.
- */
- public static final int DRAWABLE_PRESENCE_OFFLINE = 106;
- /**
- * The label of the menu to go to the contact list screen.
- */
- public static final int STRING_MENU_CONTACT_LIST = 107;
-
-}
diff --git a/im/java/android/im/IImPlugin.aidl b/im/java/android/im/IImPlugin.aidl
deleted file mode 100644
index 229cd0e..0000000
--- a/im/java/android/im/IImPlugin.aidl
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.im;
-
-/**
- * @hide
- */
-interface IImPlugin {
- /**
- * Notify the plugin the front door activity is created. This gives the plugin a chance to
- * start its own servics, etc.
- */
- void onStart();
-
- /**
- * Notify the plugin the front door activity is stopping.
- */
- void onStop();
-
- /**
- * Sign in to the service for the account passed in.
- *
- * @param account the account id for the accont to be signed into.
- */
- void signIn(long account);
-
- /**
- * Sign out of the service for the account passed in.
- *
- * @param account the account id for the accont to be signed out of.
- */
- void signOut(long account);
-
- /**
- * Returns the package name used to load the resources for the given provider name.
- *
- * @return The package name to load the resourcs for the given provider.
- */
- String getResourcePackageNameForProvider(String providerName);
-
- /**
- * Returns a map of branding resources for the given provider. The keys are defined
- * in {@link android.im.BrandingResourceIDs}. The values are the resource identifiers generated
- * by the aapt tool.
- *
- * @return The map of branding resources for the given provider.
- */
- Map getResourceMapForProvider(String providerName);
-
- /*
- * Returns a list of supported IM providers.
- *
- * @return a List of supported providers.
- */
- List getSupportedProviders();
-}
diff --git a/im/java/android/im/ImPluginConsts.java b/im/java/android/im/ImPluginConsts.java
deleted file mode 100644
index 416493ff..0000000
--- a/im/java/android/im/ImPluginConsts.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.im;
-
-/**
- * @hide
- */
-public class ImPluginConsts {
- /**
- * The intent action name for the plugin service.
- */
- public static final String PLUGIN_ACTION_NAME = "android.im.plugin";
-} \ No newline at end of file
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 78bef91..99ab2f0 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -20,7 +20,7 @@
#define _RUNTIME_ANDROID_RUNTIME_H
#include <utils/Errors.h>
-#include <utils/IBinder.h>
+#include <binder/IBinder.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/Vector.h>
@@ -98,6 +98,7 @@ public:
private:
static int startReg(JNIEnv* env);
+ int startVm(JavaVM** pJavaVM, JNIEnv** pEnv);
Vector<JavaVMOption> mOptions;
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 100644
index 0000000..47b2bb9
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BINDER_H
+#define ANDROID_BINDER_H
+
+#include <binder/IBinder.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder : public IBinder
+{
+public:
+ BBinder();
+
+ virtual const String16& getInterfaceDescriptor() const;
+ virtual bool isBinderAlive() const;
+ virtual status_t pingBinder();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ virtual status_t transact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0);
+
+ virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0,
+ wp<DeathRecipient>* outRecipient = NULL);
+
+ virtual void attachObject( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ object_cleanup_func func);
+ virtual void* findObject(const void* objectID) const;
+ virtual void detachObject(const void* objectID);
+
+ virtual BBinder* localBinder();
+
+protected:
+ virtual ~BBinder();
+
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+private:
+ BBinder(const BBinder& o);
+ BBinder& operator=(const BBinder& o);
+
+ class Extras;
+
+ Extras* mExtras;
+ void* mReserved0;
+ static String16 sEmptyDescriptor;
+};
+
+// ---------------------------------------------------------------------------
+
+class BpRefBase : public virtual RefBase
+{
+protected:
+ BpRefBase(const sp<IBinder>& o);
+ virtual ~BpRefBase();
+ virtual void onFirstRef();
+ virtual void onLastStrongRef(const void* id);
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+
+ inline IBinder* remote() { return mRemote; }
+ inline IBinder* remote() const { return mRemote; }
+
+private:
+ BpRefBase(const BpRefBase& o);
+ BpRefBase& operator=(const BpRefBase& o);
+
+ IBinder* const mRemote;
+ RefBase::weakref_type* mRefs;
+ volatile int32_t mState;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_BINDER_H
diff --git a/include/binder/BpBinder.h b/include/binder/BpBinder.h
new file mode 100644
index 0000000..7ef93aa
--- /dev/null
+++ b/include/binder/BpBinder.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#ifndef ANDROID_BPBINDER_H
+#define ANDROID_BPBINDER_H
+
+#include <binder/IBinder.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BpBinder : public IBinder
+{
+public:
+ BpBinder(int32_t handle);
+
+ inline int32_t handle() const { return mHandle; }
+
+ virtual const String16& getInterfaceDescriptor() const;
+ virtual bool isBinderAlive() const;
+ virtual status_t pingBinder();
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ virtual status_t transact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0);
+ virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0,
+ wp<DeathRecipient>* outRecipient = NULL);
+
+ virtual void attachObject( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ object_cleanup_func func);
+ virtual void* findObject(const void* objectID) const;
+ virtual void detachObject(const void* objectID);
+
+ virtual BpBinder* remoteBinder();
+
+ status_t setConstantData(const void* data, size_t size);
+ void sendObituary();
+
+ class ObjectManager
+ {
+ public:
+ ObjectManager();
+ ~ObjectManager();
+
+ void attach( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ IBinder::object_cleanup_func func);
+ void* find(const void* objectID) const;
+ void detach(const void* objectID);
+
+ void kill();
+
+ private:
+ ObjectManager(const ObjectManager&);
+ ObjectManager& operator=(const ObjectManager&);
+
+ struct entry_t
+ {
+ void* object;
+ void* cleanupCookie;
+ IBinder::object_cleanup_func func;
+ };
+
+ KeyedVector<const void*, entry_t> mObjects;
+ };
+
+protected:
+ virtual ~BpBinder();
+ virtual void onFirstRef();
+ virtual void onLastStrongRef(const void* id);
+ virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
+
+private:
+ const int32_t mHandle;
+
+ struct Obituary {
+ wp<DeathRecipient> recipient;
+ void* cookie;
+ uint32_t flags;
+ };
+
+ void reportOneDeath(const Obituary& obit);
+ bool isDescriptorCached() const;
+
+ mutable Mutex mLock;
+ volatile int32_t mAlive;
+ volatile int32_t mObitsSent;
+ Vector<Obituary>* mObituaries;
+ ObjectManager mObjects;
+ Parcel* mConstantData;
+ mutable String16 mDescriptorCache;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_BPBINDER_H
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 100644
index 0000000..884b5c1
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IBINDER_H
+#define ANDROID_IBINDER_H
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+
+#define B_PACK_CHARS(c1, c2, c3, c4) \
+ ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class BBinder;
+class BpBinder;
+class IInterface;
+class Parcel;
+
+/**
+ * Base class and low-level protocol for a remotable object.
+ * You can derive from this class to create an object for which other
+ * processes can hold references to it. Communication between processes
+ * (method calls, property get and set) is down through a low-level
+ * protocol implemented on top of the transact() API.
+ */
+class IBinder : public virtual RefBase
+{
+public:
+ enum {
+ FIRST_CALL_TRANSACTION = 0x00000001,
+ LAST_CALL_TRANSACTION = 0x00ffffff,
+
+ PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
+ DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
+ INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
+
+ // Corresponds to tfOneWay -- an asynchronous call.
+ FLAG_ONEWAY = 0x00000001
+ };
+
+ IBinder();
+
+ /**
+ * Check if this IBinder implements the interface named by
+ * @a descriptor. If it does, the base pointer to it is returned,
+ * which you can safely static_cast<> to the concrete C++ interface.
+ */
+ virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
+
+ /**
+ * Return the canonical name of the interface provided by this IBinder
+ * object.
+ */
+ virtual const String16& getInterfaceDescriptor() const = 0;
+
+ virtual bool isBinderAlive() const = 0;
+ virtual status_t pingBinder() = 0;
+ virtual status_t dump(int fd, const Vector<String16>& args) = 0;
+
+ virtual status_t transact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0) = 0;
+
+ /**
+ * This method allows you to add data that is transported through
+ * IPC along with your IBinder pointer. When implementing a Binder
+ * object, override it to write your desired data in to @a outData.
+ * You can then call getConstantData() on your IBinder to retrieve
+ * that data, from any process. You MUST return the number of bytes
+ * written in to the parcel (including padding).
+ */
+ class DeathRecipient : public virtual RefBase
+ {
+ public:
+ virtual void binderDied(const wp<IBinder>& who) = 0;
+ };
+
+ /**
+ * Register the @a recipient for a notification if this binder
+ * goes away. If this binder object unexpectedly goes away
+ * (typically because its hosting process has been killed),
+ * then DeathRecipient::binderDied() will be called with a referene
+ * to this.
+ *
+ * The @a cookie is optional -- if non-NULL, it should be a
+ * memory address that you own (that is, you know it is unique).
+ *
+ * @note You will only receive death notifications for remote binders,
+ * as local binders by definition can't die without you dying as well.
+ * Trying to use this function on a local binder will result in an
+ * INVALID_OPERATION code being returned and nothing happening.
+ *
+ * @note This link always holds a weak reference to its recipient.
+ *
+ * @note You will only receive a weak reference to the dead
+ * binder. You should not try to promote this to a strong reference.
+ * (Nor should you need to, as there is nothing useful you can
+ * directly do with it now that it has passed on.)
+ */
+ virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0) = 0;
+
+ /**
+ * Remove a previously registered death notification.
+ * The @a recipient will no longer be called if this object
+ * dies. The @a cookie is optional. If non-NULL, you can
+ * supply a NULL @a recipient, and the recipient previously
+ * added with that cookie will be unlinked.
+ */
+ virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
+ void* cookie = NULL,
+ uint32_t flags = 0,
+ wp<DeathRecipient>* outRecipient = NULL) = 0;
+
+ virtual bool checkSubclass(const void* subclassID) const;
+
+ typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
+
+ virtual void attachObject( const void* objectID,
+ void* object,
+ void* cleanupCookie,
+ object_cleanup_func func) = 0;
+ virtual void* findObject(const void* objectID) const = 0;
+ virtual void detachObject(const void* objectID) = 0;
+
+ virtual BBinder* localBinder();
+ virtual BpBinder* remoteBinder();
+
+protected:
+ virtual ~IBinder();
+
+private:
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IBINDER_H
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 100644
index 0000000..273d922
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+#ifndef ANDROID_IINTERFACE_H
+#define ANDROID_IINTERFACE_H
+
+#include <binder/Binder.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IInterface : public virtual RefBase
+{
+public:
+ IInterface();
+ sp<IBinder> asBinder();
+ sp<const IBinder> asBinder() const;
+
+protected:
+ virtual ~IInterface();
+ virtual IBinder* onAsBinder() = 0;
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
+{
+ return INTERFACE::asInterface(obj);
+}
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BnInterface : public INTERFACE, public BBinder
+{
+public:
+ virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
+ virtual const String16& getInterfaceDescriptor() const;
+
+protected:
+ virtual IBinder* onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+template<typename INTERFACE>
+class BpInterface : public INTERFACE, public BpRefBase
+{
+public:
+ BpInterface(const sp<IBinder>& remote);
+
+protected:
+ virtual IBinder* onAsBinder();
+};
+
+// ----------------------------------------------------------------------
+
+#define DECLARE_META_INTERFACE(INTERFACE) \
+ static const String16 descriptor; \
+ static sp<I##INTERFACE> asInterface(const sp<IBinder>& obj); \
+ virtual const String16& getInterfaceDescriptor() const; \
+ I##INTERFACE(); \
+ virtual ~I##INTERFACE(); \
+
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ const String16 I##INTERFACE::descriptor(NAME); \
+ const String16& I##INTERFACE::getInterfaceDescriptor() const { \
+ return I##INTERFACE::descriptor; \
+ } \
+ sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj) \
+ { \
+ sp<I##INTERFACE> intr; \
+ if (obj != NULL) { \
+ intr = static_cast<I##INTERFACE*>( \
+ obj->queryLocalInterface( \
+ I##INTERFACE::descriptor).get()); \
+ if (intr == NULL) { \
+ intr = new Bp##INTERFACE(obj); \
+ } \
+ } \
+ return intr; \
+ } \
+ I##INTERFACE::I##INTERFACE() { } \
+ I##INTERFACE::~I##INTERFACE() { } \
+
+
+#define CHECK_INTERFACE(interface, data, reply) \
+ if (!data.checkInterface(this)) { return PERMISSION_DENIED; } \
+
+
+// ----------------------------------------------------------------------
+// No user-serviceable parts after this...
+
+template<typename INTERFACE>
+inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
+ const String16& _descriptor)
+{
+ if (_descriptor == INTERFACE::descriptor) return this;
+ return NULL;
+}
+
+template<typename INTERFACE>
+inline const String16& BnInterface<INTERFACE>::getInterfaceDescriptor() const
+{
+ return INTERFACE::getInterfaceDescriptor();
+}
+
+template<typename INTERFACE>
+IBinder* BnInterface<INTERFACE>::onAsBinder()
+{
+ return this;
+}
+
+template<typename INTERFACE>
+inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
+ : BpRefBase(remote)
+{
+}
+
+template<typename INTERFACE>
+inline IBinder* BpInterface<INTERFACE>::onAsBinder()
+{
+ return remote();
+}
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IINTERFACE_H
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 100644
index 0000000..ae042cb
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IMEMORY_H
+#define ANDROID_IMEMORY_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IMemoryHeap : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(MemoryHeap);
+
+ // flags returned by getFlags()
+ enum {
+ READ_ONLY = 0x00000001,
+ MAP_ONCE = 0x00000002
+ };
+
+ virtual int getHeapID() const = 0;
+ virtual void* getBase() const = 0;
+ virtual size_t getSize() const = 0;
+ virtual uint32_t getFlags() const = 0;
+
+ // these are there just for backward source compatibility
+ int32_t heapID() const { return getHeapID(); }
+ void* base() const { return getBase(); }
+ size_t virtualSize() const { return getSize(); }
+};
+
+class BnMemoryHeap : public BnInterface<IMemoryHeap>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ BnMemoryHeap();
+protected:
+ virtual ~BnMemoryHeap();
+};
+
+// ----------------------------------------------------------------------------
+
+class IMemory : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(Memory);
+
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
+
+ // helpers
+ void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
+ void* pointer() const;
+ size_t size() const;
+ ssize_t offset() const;
+};
+
+class BnMemory : public BnInterface<IMemory>
+{
+public:
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+
+ BnMemory();
+protected:
+ virtual ~BnMemory();
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IMEMORY_H
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 100644
index 0000000..78306b2
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#ifndef ANDROID_IPC_THREAD_STATE_H
+#define ANDROID_IPC_THREAD_STATE_H
+
+#include <utils/Errors.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+#include <utils/Vector.h>
+
+#ifdef HAVE_WIN32_PROC
+typedef int uid_t;
+#endif
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IPCThreadState
+{
+public:
+ static IPCThreadState* self();
+
+ sp<ProcessState> process();
+
+ status_t clearLastError();
+
+ int getCallingPid();
+ int getCallingUid();
+
+ int64_t clearCallingIdentity();
+ void restoreCallingIdentity(int64_t token);
+
+ void flushCommands();
+
+ void joinThreadPool(bool isMain = true);
+
+ // Stop the local process.
+ void stopProcess(bool immediate = true);
+
+ status_t transact(int32_t handle,
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+ void incStrongHandle(int32_t handle);
+ void decStrongHandle(int32_t handle);
+ void incWeakHandle(int32_t handle);
+ void decWeakHandle(int32_t handle);
+ status_t attemptIncStrongHandle(int32_t handle);
+ static void expungeHandle(int32_t handle, IBinder* binder);
+ status_t requestDeathNotification( int32_t handle,
+ BpBinder* proxy);
+ status_t clearDeathNotification( int32_t handle,
+ BpBinder* proxy);
+
+ static void shutdown();
+
+private:
+ IPCThreadState();
+ ~IPCThreadState();
+
+ status_t sendReply(const Parcel& reply, uint32_t flags);
+ status_t waitForResponse(Parcel *reply,
+ status_t *acquireResult=NULL);
+ status_t talkWithDriver(bool doReceive=true);
+ status_t writeTransactionData(int32_t cmd,
+ uint32_t binderFlags,
+ int32_t handle,
+ uint32_t code,
+ const Parcel& data,
+ status_t* statusBuffer);
+ status_t executeCommand(int32_t command);
+
+ void clearCaller();
+
+ static void threadDestructor(void *st);
+ static void freeBuffer(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const size_t* objects, size_t objectsSize,
+ void* cookie);
+
+ const sp<ProcessState> mProcess;
+ Vector<BBinder*> mPendingStrongDerefs;
+ Vector<RefBase::weakref_type*> mPendingWeakDerefs;
+
+ Parcel mIn;
+ Parcel mOut;
+ status_t mLastError;
+ pid_t mCallingPid;
+ uid_t mCallingUid;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/include/binder/IPermissionController.h b/include/binder/IPermissionController.h
new file mode 100644
index 0000000..f9d371b
--- /dev/null
+++ b/include/binder/IPermissionController.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+#ifndef ANDROID_IPERMISSION_CONTROLLER_H
+#define ANDROID_IPERMISSION_CONTROLLER_H
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IPermissionController : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(PermissionController);
+
+ virtual bool checkPermission(const String16& permission,
+ int32_t pid, int32_t uid) = 0;
+
+ enum {
+ CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
+ };
+};
+
+// ----------------------------------------------------------------------
+
+class BnPermissionController : public BnInterface<IPermissionController>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IPERMISSION_CONTROLLER_H
+
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 100644
index 0000000..24e9e99
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+#ifndef ANDROID_ISERVICE_MANAGER_H
+#define ANDROID_ISERVICE_MANAGER_H
+
+#include <binder/IInterface.h>
+#include <binder/IPermissionController.h>
+#include <utils/Vector.h>
+#include <utils/String16.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class IServiceManager : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(ServiceManager);
+
+ /**
+ * Retrieve an existing service, blocking for a few seconds
+ * if it doesn't yet exist.
+ */
+ virtual sp<IBinder> getService( const String16& name) const = 0;
+
+ /**
+ * Retrieve an existing service, non-blocking.
+ */
+ virtual sp<IBinder> checkService( const String16& name) const = 0;
+
+ /**
+ * Register a service.
+ */
+ virtual status_t addService( const String16& name,
+ const sp<IBinder>& service) = 0;
+
+ /**
+ * Return list of all existing services.
+ */
+ virtual Vector<String16> listServices() = 0;
+
+ enum {
+ GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
+ CHECK_SERVICE_TRANSACTION,
+ ADD_SERVICE_TRANSACTION,
+ LIST_SERVICES_TRANSACTION,
+ };
+};
+
+sp<IServiceManager> defaultServiceManager();
+
+template<typename INTERFACE>
+status_t getService(const String16& name, sp<INTERFACE>* outService)
+{
+ const sp<IServiceManager> sm = defaultServiceManager();
+ if (sm != NULL) {
+ *outService = interface_cast<INTERFACE>(sm->getService(name));
+ if ((*outService) != NULL) return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
+}
+
+bool checkCallingPermission(const String16& permission);
+bool checkCallingPermission(const String16& permission,
+ int32_t* outPid, int32_t* outUid);
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid);
+
+
+// ----------------------------------------------------------------------
+
+class BnServiceManager : public BnInterface<IServiceManager>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_ISERVICE_MANAGER_H
+
diff --git a/include/binder/MemoryBase.h b/include/binder/MemoryBase.h
new file mode 100644
index 0000000..463e26d
--- /dev/null
+++ b/include/binder/MemoryBase.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEMORY_BASE_H
+#define ANDROID_MEMORY_BASE_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <binder/IMemory.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class MemoryBase : public BnMemory
+{
+public:
+ MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
+ virtual ~MemoryBase();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
+
+protected:
+ size_t getSize() const { return mSize; }
+ ssize_t getOffset() const { return mOffset; }
+ const sp<IMemoryHeap>& getHeap() const { return mHeap; }
+
+private:
+ size_t mSize;
+ ssize_t mOffset;
+ sp<IMemoryHeap> mHeap;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_MEMORY_BASE_H
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 100644
index 0000000..03ac70a
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEMORY_DEALER_H
+#define ANDROID_MEMORY_DEALER_H
+
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/IMemory.h>
+#include <utils/threads.h>
+#include <binder/MemoryHeapBase.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+class String8;
+
+/*
+ * interface for implementing a "heap". A heap basically provides
+ * the IMemoryHeap interface for cross-process sharing and the
+ * ability to map/unmap pages within the heap.
+ */
+class HeapInterface : public virtual BnMemoryHeap
+{
+public:
+ // all values must be page-aligned
+ virtual sp<IMemory> mapMemory(size_t offset, size_t size) = 0;
+
+ HeapInterface();
+protected:
+ virtual ~HeapInterface();
+};
+
+// ----------------------------------------------------------------------------
+
+/*
+ * interface for implementing an allocator. An allocator provides
+ * methods for allocating and freeing memory blocks and dumping
+ * its state.
+ */
+class AllocatorInterface : public RefBase
+{
+public:
+ enum {
+ PAGE_ALIGNED = 0x00000001
+ };
+
+ virtual size_t allocate(size_t size, uint32_t flags = 0) = 0;
+ virtual status_t deallocate(size_t offset) = 0;
+ virtual size_t size() const = 0;
+ virtual void dump(const char* what, uint32_t flags = 0) const = 0;
+ virtual void dump(String8& res,
+ const char* what, uint32_t flags = 0) const = 0;
+
+ AllocatorInterface();
+protected:
+ virtual ~AllocatorInterface();
+};
+
+// ----------------------------------------------------------------------------
+
+/*
+ * concrete implementation of HeapInterface on top of mmap()
+ */
+class SharedHeap : public HeapInterface, public MemoryHeapBase
+{
+public:
+ SharedHeap();
+ SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL);
+ virtual ~SharedHeap();
+ virtual sp<IMemory> mapMemory(size_t offset, size_t size);
+};
+
+// ----------------------------------------------------------------------------
+
+/*
+ * A simple templatized doubly linked-list implementation
+ */
+
+template <typename NODE>
+class LinkedList
+{
+ NODE* mFirst;
+ NODE* mLast;
+
+public:
+ LinkedList() : mFirst(0), mLast(0) { }
+ bool isEmpty() const { return mFirst == 0; }
+ NODE const* head() const { return mFirst; }
+ NODE* head() { return mFirst; }
+ NODE const* tail() const { return mLast; }
+ NODE* tail() { return mLast; }
+
+ void insertAfter(NODE* node, NODE* newNode) {
+ newNode->prev = node;
+ newNode->next = node->next;
+ if (node->next == 0) mLast = newNode;
+ else node->next->prev = newNode;
+ node->next = newNode;
+ }
+
+ void insertBefore(NODE* node, NODE* newNode) {
+ newNode->prev = node->prev;
+ newNode->next = node;
+ if (node->prev == 0) mFirst = newNode;
+ else node->prev->next = newNode;
+ node->prev = newNode;
+ }
+
+ void insertHead(NODE* newNode) {
+ if (mFirst == 0) {
+ mFirst = mLast = newNode;
+ newNode->prev = newNode->next = 0;
+ } else {
+ newNode->prev = 0;
+ newNode->next = mFirst;
+ mFirst->prev = newNode;
+ mFirst = newNode;
+ }
+ }
+
+ void insertTail(NODE* newNode) {
+ if (mLast == 0) {
+ insertHead(newNode);
+ } else {
+ newNode->prev = mLast;
+ newNode->next = 0;
+ mLast->next = newNode;
+ mLast = newNode;
+ }
+ }
+
+ NODE* remove(NODE* node) {
+ if (node->prev == 0) mFirst = node->next;
+ else node->prev->next = node->next;
+ if (node->next == 0) mLast = node->prev;
+ else node->next->prev = node->prev;
+ return node;
+ }
+};
+
+
+/*
+ * concrete implementation of AllocatorInterface using a simple
+ * best-fit allocation scheme
+ */
+class SimpleBestFitAllocator : public AllocatorInterface
+{
+public:
+
+ SimpleBestFitAllocator(size_t size);
+ virtual ~SimpleBestFitAllocator();
+
+ virtual size_t allocate(size_t size, uint32_t flags = 0);
+ virtual status_t deallocate(size_t offset);
+ virtual size_t size() const;
+ virtual void dump(const char* what, uint32_t flags = 0) const;
+ virtual void dump(String8& res,
+ const char* what, uint32_t flags = 0) const;
+
+private:
+
+ struct chunk_t {
+ chunk_t(size_t start, size_t size)
+ : start(start), size(size), free(1), prev(0), next(0) {
+ }
+ size_t start;
+ size_t size : 28;
+ int free : 4;
+ mutable chunk_t* prev;
+ mutable chunk_t* next;
+ };
+
+ ssize_t alloc(size_t size, uint32_t flags);
+ chunk_t* dealloc(size_t start);
+ void dump_l(const char* what, uint32_t flags = 0) const;
+ void dump_l(String8& res, const char* what, uint32_t flags = 0) const;
+
+ static const int kMemoryAlign;
+ mutable Mutex mLock;
+ LinkedList<chunk_t> mList;
+ size_t mHeapSize;
+};
+
+// ----------------------------------------------------------------------------
+
+class MemoryDealer : public RefBase
+{
+public:
+
+ enum {
+ READ_ONLY = MemoryHeapBase::READ_ONLY,
+ PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED
+ };
+
+ // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator
+ MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0);
+
+ // provide a custom heap but use the SimpleBestFitAllocator
+ MemoryDealer(const sp<HeapInterface>& heap);
+
+ // provide both custom heap and allocotar
+ MemoryDealer(
+ const sp<HeapInterface>& heap,
+ const sp<AllocatorInterface>& allocator);
+
+ virtual sp<IMemory> allocate(size_t size, uint32_t flags = 0);
+ virtual void deallocate(size_t offset);
+ virtual void dump(const char* what, uint32_t flags = 0) const;
+
+
+ sp<IMemoryHeap> getMemoryHeap() const { return heap(); }
+ sp<AllocatorInterface> getAllocator() const { return allocator(); }
+
+protected:
+ virtual ~MemoryDealer();
+
+private:
+ const sp<HeapInterface>& heap() const;
+ const sp<AllocatorInterface>& allocator() const;
+
+ class Allocation : public BnMemory {
+ public:
+ Allocation(const sp<MemoryDealer>& dealer,
+ ssize_t offset, size_t size, const sp<IMemory>& memory);
+ virtual ~Allocation();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
+ private:
+ sp<MemoryDealer> mDealer;
+ ssize_t mOffset;
+ size_t mSize;
+ sp<IMemory> mMemory;
+ };
+
+ sp<HeapInterface> mHeap;
+ sp<AllocatorInterface> mAllocator;
+};
+
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_MEMORY_DEALER_H
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 100644
index 0000000..435540e
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEMORY_HEAP_BASE_H
+#define ANDROID_MEMORY_HEAP_BASE_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <binder/IMemory.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class MemoryHeapBase : public virtual BnMemoryHeap
+{
+public:
+ enum {
+ READ_ONLY = IMemoryHeap::READ_ONLY,
+ MAP_ONCE = IMemoryHeap::MAP_ONCE,
+ // memory won't be mapped locally, but will be mapped in the remote
+ // process.
+ DONT_MAP_LOCALLY = 0x00000100
+ };
+
+ /*
+ * maps the memory referenced by fd. but DOESN'T take ownership
+ * of the filedescriptor (it makes a copy with dup()
+ */
+ MemoryHeapBase(int fd, size_t size, uint32_t flags = 0, uint32_t offset = 0);
+
+ /*
+ * maps memory from the given device
+ */
+ MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);
+
+ /*
+ * maps memory from ashmem, with the given name for debugging
+ */
+ MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);
+
+ virtual ~MemoryHeapBase();
+
+ /* implement IMemoryHeap interface */
+ virtual int getHeapID() const;
+ virtual void* getBase() const;
+ virtual size_t getSize() const;
+ virtual uint32_t getFlags() const;
+
+ const char* getDevice() const;
+
+ /* this closes this heap -- use carefully */
+ void dispose();
+
+ /* this is only needed as a workaround, use only if you know
+ * what you are doing */
+ status_t setDevice(const char* device) {
+ if (mDevice == 0)
+ mDevice = device;
+ return mDevice ? NO_ERROR : ALREADY_EXISTS;
+ }
+
+protected:
+ MemoryHeapBase();
+ // init() takes ownership of fd
+ status_t init(int fd, void *base, int size,
+ int flags = 0, const char* device = NULL);
+
+private:
+ status_t mapfd(int fd, size_t size, uint32_t offset = 0);
+
+ int mFD;
+ size_t mSize;
+ void* mBase;
+ uint32_t mFlags;
+ const char* mDevice;
+ bool mNeedUnmap;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_MEMORY_HEAP_BASE_H
diff --git a/include/binder/MemoryHeapPmem.h b/include/binder/MemoryHeapPmem.h
new file mode 100644
index 0000000..dbf26ff
--- /dev/null
+++ b/include/binder/MemoryHeapPmem.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEMORY_HEAP_PMEM_H
+#define ANDROID_MEMORY_HEAP_PMEM_H
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <binder/MemoryDealer.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/IMemory.h>
+#include <utils/SortedVector.h>
+
+namespace android {
+
+class MemoryHeapBase;
+
+// ---------------------------------------------------------------------------
+
+class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase
+{
+public:
+ class MemoryPmem : public BnMemory {
+ public:
+ MemoryPmem(const sp<MemoryHeapPmem>& heap);
+ ~MemoryPmem();
+ protected:
+ const sp<MemoryHeapPmem>& getHeap() const { return mClientHeap; }
+ private:
+ friend class MemoryHeapPmem;
+ virtual void revoke() = 0;
+ sp<MemoryHeapPmem> mClientHeap;
+ };
+
+ MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
+ uint32_t flags = IMemoryHeap::MAP_ONCE);
+ ~MemoryHeapPmem();
+
+ /* HeapInterface additions */
+ virtual sp<IMemory> mapMemory(size_t offset, size_t size);
+
+ /* make the whole heap visible (you know who you are) */
+ virtual status_t slap();
+
+ /* hide (revoke) the whole heap (the client will see the garbage page) */
+ virtual status_t unslap();
+
+ /* revoke all allocations made by this heap */
+ virtual void revoke();
+
+private:
+ /* use this to create your own IMemory for mapMemory */
+ virtual sp<MemoryPmem> createMemory(size_t offset, size_t size);
+ void remove(const wp<MemoryPmem>& memory);
+
+private:
+ sp<MemoryHeapBase> mParentHeap;
+ mutable Mutex mLock;
+ SortedVector< wp<MemoryPmem> > mAllocations;
+};
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_MEMORY_HEAP_PMEM_H
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 100644
index 0000000..ba6c711
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#ifndef ANDROID_PARCEL_H
+#define ANDROID_PARCEL_H
+
+#include <cutils/native_handle.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+class IBinder;
+class ProcessState;
+class String8;
+class TextOutput;
+
+struct flat_binder_object; // defined in support_p/binder_module.h
+
+class Parcel
+{
+public:
+ Parcel();
+ ~Parcel();
+
+ const uint8_t* data() const;
+ size_t dataSize() const;
+ size_t dataAvail() const;
+ size_t dataPosition() const;
+ size_t dataCapacity() const;
+
+ status_t setDataSize(size_t size);
+ void setDataPosition(size_t pos) const;
+ status_t setDataCapacity(size_t size);
+
+ status_t setData(const uint8_t* buffer, size_t len);
+
+ status_t appendFrom(Parcel *parcel, size_t start, size_t len);
+
+ bool hasFileDescriptors() const;
+
+ status_t writeInterfaceToken(const String16& interface);
+ bool enforceInterface(const String16& interface) const;
+ bool checkInterface(IBinder*) const;
+
+ void freeData();
+
+ const size_t* objects() const;
+ size_t objectsCount() const;
+
+ status_t errorCheck() const;
+ void setError(status_t err);
+
+ status_t write(const void* data, size_t len);
+ void* writeInplace(size_t len);
+ status_t writeUnpadded(const void* data, size_t len);
+ status_t writeInt32(int32_t val);
+ status_t writeInt64(int64_t val);
+ status_t writeFloat(float val);
+ status_t writeDouble(double val);
+ status_t writeIntPtr(intptr_t val);
+ status_t writeCString(const char* str);
+ status_t writeString8(const String8& str);
+ status_t writeString16(const String16& str);
+ status_t writeString16(const char16_t* str, size_t len);
+ status_t writeStrongBinder(const sp<IBinder>& val);
+ status_t writeWeakBinder(const wp<IBinder>& val);
+
+ // Place a native_handle into the parcel (the native_handle's file-
+ // descriptors are dup'ed, so it is safe to delete the native_handle
+ // when this function returns).
+ // Doesn't take ownership of the native_handle.
+ status_t writeNativeHandle(const native_handle* handle);
+
+ // Place a file descriptor into the parcel. The given fd must remain
+ // valid for the lifetime of the parcel.
+ status_t writeFileDescriptor(int fd);
+
+ // Place a file descriptor into the parcel. A dup of the fd is made, which
+ // will be closed once the parcel is destroyed.
+ status_t writeDupFileDescriptor(int fd);
+
+ status_t writeObject(const flat_binder_object& val, bool nullMetaData);
+
+ void remove(size_t start, size_t amt);
+
+ status_t read(void* outData, size_t len) const;
+ const void* readInplace(size_t len) const;
+ int32_t readInt32() const;
+ status_t readInt32(int32_t *pArg) const;
+ int64_t readInt64() const;
+ status_t readInt64(int64_t *pArg) const;
+ float readFloat() const;
+ status_t readFloat(float *pArg) const;
+ double readDouble() const;
+ status_t readDouble(double *pArg) const;
+ intptr_t readIntPtr() const;
+ status_t readIntPtr(intptr_t *pArg) const;
+
+ const char* readCString() const;
+ String8 readString8() const;
+ String16 readString16() const;
+ const char16_t* readString16Inplace(size_t* outLen) const;
+ sp<IBinder> readStrongBinder() const;
+ wp<IBinder> readWeakBinder() const;
+
+
+ // Retrieve native_handle from the parcel. This returns a copy of the
+ // parcel's native_handle (the caller takes ownership). The caller
+ // must free the native_handle with native_handle_close() and
+ // native_handle_delete().
+ native_handle* readNativeHandle() const;
+
+
+ // Retrieve a file descriptor from the parcel. This returns the raw fd
+ // in the parcel, which you do not own -- use dup() to get your own copy.
+ int readFileDescriptor() const;
+
+ const flat_binder_object* readObject(bool nullMetaData) const;
+
+ // Explicitly close all file descriptors in the parcel.
+ void closeFileDescriptors();
+
+ typedef void (*release_func)(Parcel* parcel,
+ const uint8_t* data, size_t dataSize,
+ const size_t* objects, size_t objectsSize,
+ void* cookie);
+
+ const uint8_t* ipcData() const;
+ size_t ipcDataSize() const;
+ const size_t* ipcObjects() const;
+ size_t ipcObjectsCount() const;
+ void ipcSetDataReference(const uint8_t* data, size_t dataSize,
+ const size_t* objects, size_t objectsCount,
+ release_func relFunc, void* relCookie);
+
+ void print(TextOutput& to, uint32_t flags = 0) const;
+
+private:
+ Parcel(const Parcel& o);
+ Parcel& operator=(const Parcel& o);
+
+ status_t finishWrite(size_t len);
+ void releaseObjects();
+ void acquireObjects();
+ status_t growData(size_t len);
+ status_t restartWrite(size_t desired);
+ status_t continueWrite(size_t desired);
+ void freeDataNoInit();
+ void initState();
+ void scanForFds() const;
+
+ template<class T>
+ status_t readAligned(T *pArg) const;
+
+ template<class T> T readAligned() const;
+
+ template<class T>
+ status_t writeAligned(T val);
+
+ status_t mError;
+ uint8_t* mData;
+ size_t mDataSize;
+ size_t mDataCapacity;
+ mutable size_t mDataPos;
+ size_t* mObjects;
+ size_t mObjectsSize;
+ size_t mObjectsCapacity;
+ mutable size_t mNextObjectHint;
+
+ mutable bool mFdsKnown;
+ mutable bool mHasFds;
+
+ release_func mOwner;
+ void* mOwnerCookie;
+};
+
+// ---------------------------------------------------------------------------
+
+inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
+{
+ parcel.print(to);
+ return to;
+}
+
+// ---------------------------------------------------------------------------
+
+// Generic acquire and release of objects.
+void acquire_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who);
+void release_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who);
+
+void flatten_binder(const sp<ProcessState>& proc,
+ const sp<IBinder>& binder, flat_binder_object* out);
+void flatten_binder(const sp<ProcessState>& proc,
+ const wp<IBinder>& binder, flat_binder_object* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const flat_binder_object& flat, sp<IBinder>* out);
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const flat_binder_object& flat, wp<IBinder>* out);
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PARCEL_H
diff --git a/include/binder/Permission.h b/include/binder/Permission.h
new file mode 100644
index 0000000..9542d50
--- /dev/null
+++ b/include/binder/Permission.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BINDER_PERMISSION_H
+#define BINDER_PERMISSION_H
+
+#include <stdint.h>
+#include <unistd.h>
+
+#include <utils/SortedVector.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+/*
+ * Permission caches the result of the permission check for the given
+ * permission name and the provided uid/pid. It also handles a few
+ * known cases efficiently (caller is in the same process or is root).
+ * The package manager does something similar but lives in dalvik world
+ * and is therefore extremely slow to access.
+ */
+
+class Permission
+{
+public:
+ Permission(char const* name);
+ Permission(const String16& name);
+ Permission(const Permission& rhs);
+ virtual ~Permission();
+
+ bool operator < (const Permission& rhs) const;
+
+ // checks the current binder call's caller has access to this permission
+ bool checkCalling() const;
+
+ // checks the specified pid/uid has access to this permission
+ bool check(pid_t pid, uid_t uid) const;
+
+protected:
+ virtual bool doCheckPermission(pid_t pid, uid_t uid) const;
+
+private:
+ Permission& operator = (const Permission& rhs) const;
+ const String16 mPermissionName;
+ mutable SortedVector<uid_t> mGranted;
+ const pid_t mPid;
+ mutable Mutex mLock;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* BINDER_PERMISSION_H */
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 100644
index 0000000..feeb3c3
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#ifndef ANDROID_PROCESS_STATE_H
+#define ANDROID_PROCESS_STATE_H
+
+#include <binder/IBinder.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+#include <utils/threads.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+
+// Global variables
+extern int mArgC;
+extern const char* const* mArgV;
+extern int mArgLen;
+
+class IPCThreadState;
+
+class ProcessState : public virtual RefBase
+{
+public:
+ static sp<ProcessState> self();
+
+ static void setSingleProcess(bool singleProcess);
+
+ void setContextObject(const sp<IBinder>& object);
+ sp<IBinder> getContextObject(const sp<IBinder>& caller);
+
+ void setContextObject(const sp<IBinder>& object,
+ const String16& name);
+ sp<IBinder> getContextObject(const String16& name,
+ const sp<IBinder>& caller);
+
+ bool supportsProcesses() const;
+
+ void startThreadPool();
+
+ typedef bool (*context_check_func)(const String16& name,
+ const sp<IBinder>& caller,
+ void* userData);
+
+ bool isContextManager(void) const;
+ bool becomeContextManager(
+ context_check_func checkFunc,
+ void* userData);
+
+ sp<IBinder> getStrongProxyForHandle(int32_t handle);
+ wp<IBinder> getWeakProxyForHandle(int32_t handle);
+ void expungeHandle(int32_t handle, IBinder* binder);
+
+ void setArgs(int argc, const char* const argv[]);
+ int getArgC() const;
+ const char* const* getArgV() const;
+
+ void setArgV0(const char* txt);
+
+ void spawnPooledThread(bool isMain);
+
+private:
+ friend class IPCThreadState;
+
+ ProcessState();
+ ~ProcessState();
+
+ ProcessState(const ProcessState& o);
+ ProcessState& operator=(const ProcessState& o);
+
+ struct handle_entry {
+ IBinder* binder;
+ RefBase::weakref_type* refs;
+ };
+
+ handle_entry* lookupHandleLocked(int32_t handle);
+
+ int mDriverFD;
+ void* mVMStart;
+
+ mutable Mutex mLock; // protects everything below.
+
+ Vector<handle_entry>mHandleToObject;
+
+ bool mManagesContexts;
+ context_check_func mBinderContextCheckFunc;
+ void* mBinderContextUserData;
+
+ KeyedVector<String16, sp<IBinder> >
+ mContexts;
+
+
+ String8 mRootDir;
+ bool mThreadPoolStarted;
+ volatile int32_t mThreadPoolSeq;
+};
+
+}; // namespace android
+
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_PROCESS_STATE_H
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 13e51ee..503cb31 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -26,8 +26,8 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
#include <utils/threads.h>
@@ -39,21 +39,10 @@ class AudioRecord
{
public:
- // input sources values must always be defined in the range
- // [AudioRecord::DEFAULT_INPUT, AudioRecord::NUM_INPUT_SOURCES[
- enum input_source {
- DEFAULT_INPUT =-1,
- MIC_INPUT = 0,
- VOICE_UPLINK_INPUT = 1,
- VOICE_DOWNLINK_INPUT = 2,
- VOICE_CALL_INPUT = 3,
- NUM_INPUT_SOURCES
- };
-
static const int DEFAULT_SAMPLE_RATE = 8000;
/* Events used by AudioRecord callback function (callback_t).
- *
+ *
* to keep in sync with frameworks/base/media/java/android/media/AudioRecord.java
*/
enum event_type {
@@ -61,7 +50,7 @@ public:
EVENT_OVERRUN = 1, // PCM buffer overrun occured.
EVENT_MARKER = 2, // Record head is at the specified marker position
// (See setMarkerPosition()).
- EVENT_NEW_POS = 3, // Record head is at a new position
+ EVENT_NEW_POS = 3, // Record head is at a new position
// (See setPositionUpdatePeriod()).
};
@@ -123,11 +112,11 @@ public:
*
* Parameters:
*
- * inputSource: Select the audio input to record to (e.g. AudioRecord::MIC_INPUT).
+ * inputSource: Select the audio input to record to (e.g. AUDIO_SOURCE_DEFAULT).
* sampleRate: Track sampling rate in Hz.
- * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed
+ * format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
* 16 bits per sample).
- * channelCount: Number of PCM channels (e.g 2 for stereo).
+ * channels: Channel mask: see AudioSystem::audio_channels.
* frameCount: Total size of track PCM buffer in frames. This defines the
* latency of the track.
* flags: A bitmask of acoustic values from enum record_flags. It enables
@@ -148,7 +137,7 @@ public:
AudioRecord(int inputSource,
uint32_t sampleRate = 0,
int format = 0,
- int channelCount = 0,
+ uint32_t channels = AudioSystem::CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -166,14 +155,14 @@ public:
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful intialization
* - INVALID_OPERATION: AudioRecord is already intitialized or record device is already in use
- * - BAD_VALUE: invalid parameter (channelCount, format, sampleRate...)
+ * - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
* - NO_INIT: audio server or audio hardware not initialized
* - PERMISSION_DENIED: recording is not allowed for the requesting process
* */
status_t set(int inputSource = 0,
uint32_t sampleRate = 0,
int format = 0,
- int channelCount = 0,
+ uint32_t channels = AudioSystem::CHANNEL_IN_MONO,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -199,6 +188,7 @@ public:
int format() const;
int channelCount() const;
+ int channels() const;
uint32_t frameCount() const;
int frameSize() const;
int inputSource() const;
@@ -222,8 +212,8 @@ public:
/* Sets marker position. When record reaches the number of frames specified,
* a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
- * with marker == 0 cancels marker notification callback.
- * If the AudioRecord has been opened with no callback function associated,
+ * with marker == 0 cancels marker notification callback.
+ * If the AudioRecord has been opened with no callback function associated,
* the operation will fail.
*
* Parameters:
@@ -238,10 +228,10 @@ public:
status_t getMarkerPosition(uint32_t *marker);
- /* Sets position update period. Every time the number of frames specified has been recorded,
- * a callback with event type EVENT_NEW_POS is called.
- * Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification
- * callback.
+ /* Sets position update period. Every time the number of frames specified has been recorded,
+ * a callback with event type EVENT_NEW_POS is called.
+ * Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification
+ * callback.
* If the AudioRecord has been opened with no callback function associated,
* the operation will fail.
*
@@ -257,8 +247,8 @@ public:
status_t getPositionUpdatePeriod(uint32_t *updatePeriod);
- /* Gets record head position. The position is the total number of frames
- * recorded since record start.
+ /* Gets record head position. The position is the total number of frames
+ * recorded since record start.
*
* Parameters:
*
@@ -270,8 +260,16 @@ public:
*/
status_t getPosition(uint32_t *position);
-
-
+ /* returns a handle on the audio input used by this AudioRecord.
+ *
+ * Parameters:
+ * none.
+ *
+ * Returned value:
+ * handle on audio hardware input
+ */
+ audio_io_handle_t getInput() { return mInput; }
+
/* obtains a buffer of "frameCount" frames. The buffer must be
* filled entirely. If the track is stopped, obtainBuffer() returns
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -342,6 +340,7 @@ private:
bool mMarkerReached;
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
+ audio_io_handle_t mInput;
};
}; // namespace android
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 3a3a714..57f8102 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -24,36 +24,130 @@
namespace android {
typedef void (*audio_error_callback)(status_t err);
+typedef int audio_io_handle_t;
+
+class IAudioPolicyService;
+class String8;
class AudioSystem
{
public:
enum stream_type {
- DEFAULT =-1,
- VOICE_CALL = 0,
- SYSTEM = 1,
- RING = 2,
- MUSIC = 3,
- ALARM = 4,
- NOTIFICATION = 5,
- BLUETOOTH_SCO = 6,
+ DEFAULT =-1,
+ VOICE_CALL = 0,
+ SYSTEM = 1,
+ RING = 2,
+ MUSIC = 3,
+ ALARM = 4,
+ NOTIFICATION = 5,
+ BLUETOOTH_SCO = 6,
ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker
+ DTMF = 8,
+ TTS = 9,
NUM_STREAM_TYPES
};
- enum audio_output_type {
- AUDIO_OUTPUT_DEFAULT =-1,
- AUDIO_OUTPUT_HARDWARE = 0,
- AUDIO_OUTPUT_A2DP = 1,
- NUM_AUDIO_OUTPUT_TYPES
+ // Audio sub formats (see AudioSystem::audio_format).
+ enum pcm_sub_format {
+ PCM_SUB_16_BIT = 0x1, // must be 1 for backward compatibility
+ PCM_SUB_8_BIT = 0x2, // must be 2 for backward compatibility
+ };
+
+ // MP3 sub format field definition : can use 11 LSBs in the same way as MP3 frame header to specify
+ // bit rate, stereo mode, version...
+ enum mp3_sub_format {
+ //TODO
+ };
+
+ // AMR NB/WB sub format field definition: specify frame block interleaving, bandwidth efficient or octet aligned,
+ // encoding mode for recording...
+ enum amr_sub_format {
+ //TODO
+ };
+
+ // AAC sub format field definition: specify profile or bitrate for recording...
+ enum aac_sub_format {
+ //TODO
};
+ // VORBIS sub format field definition: specify quality for recording...
+ enum vorbis_sub_format {
+ //TODO
+ };
+
+ // Audio format consists in a main format field (upper 8 bits) and a sub format field (lower 24 bits).
+ // The main format indicates the main codec type. The sub format field indicates options and parameters
+ // for each format. The sub format is mainly used for record to indicate for instance the requested bitrate
+ // or profile. It can also be used for certain formats to give informations not present in the encoded
+ // audio stream (e.g. octet alignement for AMR).
enum audio_format {
- FORMAT_DEFAULT = 0,
- PCM_16_BIT,
- PCM_8_BIT,
- INVALID_FORMAT
+ INVALID_FORMAT = -1,
+ FORMAT_DEFAULT = 0,
+ PCM = 0x00000000, // must be 0 for backward compatibility
+ MP3 = 0x01000000,
+ AMR_NB = 0x02000000,
+ AMR_WB = 0x03000000,
+ AAC = 0x04000000,
+ HE_AAC_V1 = 0x05000000,
+ HE_AAC_V2 = 0x06000000,
+ VORBIS = 0x07000000,
+ MAIN_FORMAT_MASK = 0xFF000000,
+ SUB_FORMAT_MASK = 0x00FFFFFF,
+ // Aliases
+ PCM_16_BIT = (PCM|PCM_SUB_16_BIT),
+ PCM_8_BIT = (PCM|PCM_SUB_8_BIT)
+ };
+
+
+ // Channel mask definitions must be kept in sync with JAVA values in /media/java/android/media/AudioFormat.java
+ enum audio_channels {
+ // output channels
+ CHANNEL_OUT_FRONT_LEFT = 0x4,
+ CHANNEL_OUT_FRONT_RIGHT = 0x8,
+ CHANNEL_OUT_FRONT_CENTER = 0x10,
+ CHANNEL_OUT_LOW_FREQUENCY = 0x20,
+ CHANNEL_OUT_BACK_LEFT = 0x40,
+ CHANNEL_OUT_BACK_RIGHT = 0x80,
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100,
+ CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200,
+ CHANNEL_OUT_BACK_CENTER = 0x400,
+ CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT,
+ CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT),
+ CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+ CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER),
+ CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT),
+ CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER),
+ CHANNEL_OUT_ALL = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER | CHANNEL_OUT_BACK_CENTER),
+
+ // input channels
+ CHANNEL_IN_LEFT = 0x4,
+ CHANNEL_IN_RIGHT = 0x8,
+ CHANNEL_IN_FRONT = 0x10,
+ CHANNEL_IN_BACK = 0x20,
+ CHANNEL_IN_LEFT_PROCESSED = 0x40,
+ CHANNEL_IN_RIGHT_PROCESSED = 0x80,
+ CHANNEL_IN_FRONT_PROCESSED = 0x100,
+ CHANNEL_IN_BACK_PROCESSED = 0x200,
+ CHANNEL_IN_PRESSURE = 0x400,
+ CHANNEL_IN_X_AXIS = 0x800,
+ CHANNEL_IN_Y_AXIS = 0x1000,
+ CHANNEL_IN_Z_AXIS = 0x2000,
+ CHANNEL_IN_VOICE_UPLINK = 0x4000,
+ CHANNEL_IN_VOICE_DNLINK = 0x8000,
+ CHANNEL_IN_MONO = CHANNEL_IN_FRONT,
+ CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT),
+ CHANNEL_IN_ALL = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK|
+ CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED | CHANNEL_IN_FRONT_PROCESSED | CHANNEL_IN_BACK_PROCESSED|
+ CHANNEL_IN_PRESSURE | CHANNEL_IN_X_AXIS | CHANNEL_IN_Y_AXIS | CHANNEL_IN_Z_AXIS |
+ CHANNEL_IN_VOICE_UPLINK | CHANNEL_IN_VOICE_DNLINK)
};
enum audio_mode {
@@ -65,15 +159,6 @@ public:
NUM_MODES // not a valid entry, denotes end-of-list
};
- enum audio_routes {
- ROUTE_EARPIECE = (1 << 0),
- ROUTE_SPEAKER = (1 << 1),
- ROUTE_BLUETOOTH_SCO = (1 << 2),
- ROUTE_HEADSET = (1 << 3),
- ROUTE_BLUETOOTH_A2DP = (1 << 4),
- ROUTE_ALL = -1UL,
- };
-
enum audio_in_acoustics {
AGC_ENABLE = 0x0001,
AGC_DISABLE = 0,
@@ -87,36 +172,37 @@ public:
* only privileged processes can have access to them
*/
- // routing helper functions
- static status_t speakerphone(bool state);
- static status_t isSpeakerphoneOn(bool* state);
- static status_t bluetoothSco(bool state);
- static status_t isBluetoothScoOn(bool* state);
+ // mute/unmute microphone
static status_t muteMicrophone(bool state);
static status_t isMicrophoneMuted(bool *state);
+ // set/get master volume
static status_t setMasterVolume(float value);
- static status_t setMasterMute(bool mute);
static status_t getMasterVolume(float* volume);
+ // mute/unmute audio outputs
+ static status_t setMasterMute(bool mute);
static status_t getMasterMute(bool* mute);
- static status_t setStreamVolume(int stream, float value);
+ // set/get stream volume on specified output
+ static status_t setStreamVolume(int stream, float value, int output);
+ static status_t getStreamVolume(int stream, float* volume, int output);
+
+ // mute/unmute stream
static status_t setStreamMute(int stream, bool mute);
- static status_t getStreamVolume(int stream, float* volume);
static status_t getStreamMute(int stream, bool* mute);
+ // set audio mode in audio hardware (see AudioSystem::audio_mode)
static status_t setMode(int mode);
- static status_t getMode(int* mode);
-
- static status_t setRouting(int mode, uint32_t routes, uint32_t mask);
- static status_t getRouting(int mode, uint32_t* routes);
+ // returns true if tracks are active on AudioSystem::MUSIC stream
static status_t isMusicActive(bool *state);
- // Temporary interface, do not use
- // TODO: Replace with a more generic key:value get/set mechanism
- static status_t setParameter(const char* key, const char* value);
-
+ // set/get audio hardware parameters. The function accepts a list of parameters
+ // key value pairs in the form: key1=value1;key2=value2;...
+ // Some keys are reserved for standard parameters (See AudioParameter class).
+ static status_t setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs);
+ static String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
+
static void setErrorCallback(audio_error_callback cb);
// helper function to obtain AudioFlinger service handle
@@ -130,47 +216,248 @@ public:
static status_t getOutputLatency(uint32_t* latency, int stream = DEFAULT);
static bool routedToA2dpOutput(int streamType);
-
- static status_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
+
+ static status_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
size_t* buffSize);
+
+ //
+ // AudioPolicyService interface
+ //
+
+ enum audio_devices {
+ // output devices
+ DEVICE_OUT_EARPIECE = 0x1,
+ DEVICE_OUT_SPEAKER = 0x2,
+ DEVICE_OUT_WIRED_HEADSET = 0x4,
+ DEVICE_OUT_WIRED_HEADPHONE = 0x8,
+ DEVICE_OUT_BLUETOOTH_SCO = 0x10,
+ DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20,
+ DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40,
+ DEVICE_OUT_BLUETOOTH_A2DP = 0x80,
+ DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100,
+ DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200,
+ DEVICE_OUT_AUX_DIGITAL = 0x400,
+ DEVICE_OUT_FM_HEADPHONE = 0x800,
+ DEVICE_OUT_FM_SPEAKER = 0x1000,
+ DEVICE_OUT_TTY = 0x2000,
+ DEVICE_OUT_DEFAULT = 0x8000,
+ DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE | DEVICE_OUT_SPEAKER | DEVICE_OUT_WIRED_HEADSET |
+ DEVICE_OUT_WIRED_HEADPHONE | DEVICE_OUT_BLUETOOTH_SCO | DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ DEVICE_OUT_BLUETOOTH_SCO_CARKIT | DEVICE_OUT_BLUETOOTH_A2DP | DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER | DEVICE_OUT_AUX_DIGITAL | DEVICE_OUT_FM_HEADPHONE |
+ DEVICE_OUT_FM_SPEAKER | DEVICE_OUT_TTY | DEVICE_OUT_DEFAULT),
+
+ // input devices
+ DEVICE_IN_COMMUNICATION = 0x10000,
+ DEVICE_IN_AMBIENT = 0x20000,
+ DEVICE_IN_BUILTIN_MIC = 0x40000,
+ DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x80000,
+ DEVICE_IN_WIRED_HEADSET = 0x100000,
+ DEVICE_IN_AUX_DIGITAL = 0x200000,
+ DEVICE_IN_VOICE_CALL = 0x400000,
+ DEVICE_IN_DEFAULT = 0x80000000,
+
+ DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION | DEVICE_IN_AMBIENT | DEVICE_IN_BUILTIN_MIC |
+ DEVICE_IN_BLUETOOTH_SCO_HEADSET | DEVICE_IN_WIRED_HEADSET | DEVICE_IN_AUX_DIGITAL |
+ DEVICE_IN_VOICE_CALL| DEVICE_IN_DEFAULT)
+ };
+
+ // device connection states used for setDeviceConnectionState()
+ enum device_connection_state {
+ DEVICE_STATE_UNAVAILABLE,
+ DEVICE_STATE_AVAILABLE,
+ NUM_DEVICE_STATES
+ };
+
+ // request to open a direct output with getOutput() (by opposition to sharing an output with other AudioTracks)
+ enum output_flags {
+ OUTPUT_FLAG_INDIRECT = 0x0,
+ OUTPUT_FLAG_DIRECT = 0x1
+ };
+
+ // device categories used for setForceUse()
+ enum forced_config {
+ FORCE_NONE,
+ FORCE_SPEAKER,
+ FORCE_HEADPHONES,
+ FORCE_BT_SCO,
+ FORCE_BT_A2DP,
+ FORCE_WIRED_ACCESSORY,
+ NUM_FORCE_CONFIG,
+ FORCE_DEFAULT = FORCE_NONE
+ };
+
+ // usages used for setForceUse()
+ enum force_use {
+ FOR_COMMUNICATION,
+ FOR_MEDIA,
+ FOR_RECORD,
+ NUM_FORCE_USE
+ };
+
+ // types of io configuration change events received with ioConfigChanged()
+ enum io_config_event {
+ OUTPUT_OPENED,
+ OUTPUT_CLOSED,
+ OUTPUT_CONFIG_CHANGED,
+ INPUT_OPENED,
+ INPUT_CLOSED,
+ INPUT_CONFIG_CHANGED,
+ STREAM_CONFIG_CHANGED,
+ NUM_CONFIG_EVENTS
+ };
+
+ // audio output descritor used to cache output configurations in client process to avoid frequent calls
+ // through IAudioFlinger
+ class OutputDescriptor {
+ public:
+ OutputDescriptor()
+ : samplingRate(0), format(0), channels(0), frameCount(0), latency(0) {}
+
+ uint32_t samplingRate;
+ int32_t format;
+ int32_t channels;
+ size_t frameCount;
+ uint32_t latency;
+ };
+
+ //
+ // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
+ //
+ static status_t setDeviceConnectionState(audio_devices device, device_connection_state state, const char *device_address);
+ static device_connection_state getDeviceConnectionState(audio_devices device, const char *device_address);
+ static status_t setPhoneState(int state);
+ static status_t setRingerMode(uint32_t mode, uint32_t mask);
+ static status_t setForceUse(force_use usage, forced_config config);
+ static forced_config getForceUse(force_use usage);
+ static audio_io_handle_t getOutput(stream_type stream,
+ uint32_t samplingRate = 0,
+ uint32_t format = FORMAT_DEFAULT,
+ uint32_t channels = CHANNEL_OUT_STEREO,
+ output_flags flags = OUTPUT_FLAG_INDIRECT);
+ static status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ static status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ static void releaseOutput(audio_io_handle_t output);
+ static audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate = 0,
+ uint32_t format = FORMAT_DEFAULT,
+ uint32_t channels = CHANNEL_IN_MONO,
+ audio_in_acoustics acoustics = (audio_in_acoustics)0);
+ static status_t startInput(audio_io_handle_t input);
+ static status_t stopInput(audio_io_handle_t input);
+ static void releaseInput(audio_io_handle_t input);
+ static status_t initStreamVolume(stream_type stream,
+ int indexMin,
+ int indexMax);
+ static status_t setStreamVolumeIndex(stream_type stream, int index);
+ static status_t getStreamVolumeIndex(stream_type stream, int *index);
+
+ static const sp<IAudioPolicyService>& get_audio_policy_service();
+
// ----------------------------------------------------------------------------
+ static uint32_t popCount(uint32_t u);
+ static bool isOutputDevice(audio_devices device);
+ static bool isInputDevice(audio_devices device);
+ static bool isA2dpDevice(audio_devices device);
+ static bool isBluetoothScoDevice(audio_devices device);
+ static bool isLowVisibility(stream_type stream);
+ static bool isOutputChannel(uint32_t channel);
+ static bool isInputChannel(uint32_t channel);
+ static bool isValidFormat(uint32_t format);
+ static bool isLinearPCM(uint32_t format);
+
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
{
public:
- AudioFlingerClient() {
+ AudioFlingerClient() {
}
-
+
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
-
+
// IAudioFlingerClient
- virtual void a2dpEnabledChanged(bool enabled);
-
+
+ // indicate a change in the configuration of an output or input: keeps the cached
+ // values for output/input parameters upto date in client process
+ virtual void ioConfigChanged(int event, int ioHandle, void *param2);
};
- static int getOutput(int streamType);
- static sp<AudioFlingerClient> gAudioFlingerClient;
+ class AudioPolicyServiceClient: public IBinder::DeathRecipient
+ {
+ public:
+ AudioPolicyServiceClient() {
+ }
+ // DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+ };
+
+ static sp<AudioFlingerClient> gAudioFlingerClient;
+ static sp<AudioPolicyServiceClient> gAudioPolicyServiceClient;
friend class AudioFlingerClient;
+ friend class AudioPolicyServiceClient;
static Mutex gLock;
static sp<IAudioFlinger> gAudioFlinger;
static audio_error_callback gAudioErrorCallback;
- static int gOutSamplingRate[NUM_AUDIO_OUTPUT_TYPES];
- static int gOutFrameCount[NUM_AUDIO_OUTPUT_TYPES];
- static uint32_t gOutLatency[NUM_AUDIO_OUTPUT_TYPES];
- static bool gA2dpEnabled;
-
+
static size_t gInBuffSize;
// previous parameters for recording buffer size queries
static uint32_t gPrevInSamplingRate;
static int gPrevInFormat;
static int gPrevInChannelCount;
+ static sp<IAudioPolicyService> gAudioPolicyService;
+
+ // mapping between stream types and outputs
+ static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap;
+ // list of output descritor containing cached parameters (sampling rate, framecount, channel count...)
+ static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+};
+
+class AudioParameter {
+
+public:
+ AudioParameter() {}
+ AudioParameter(const String8& keyValuePairs);
+ virtual ~AudioParameter();
+
+ // reserved parameter keys for changeing standard parameters with setParameters() function.
+ // Using these keys is mandatory for AudioFlinger to properly monitor audio output/input
+ // configuration changes and act accordingly.
+ // keyRouting: to change audio routing, value is an int in AudioSystem::audio_devices
+ // keySamplingRate: to change sampling rate routing, value is an int
+ // keyFormat: to change audio format, value is an int in AudioSystem::audio_format
+ // keyChannels: to change audio channel configuration, value is an int in AudioSystem::audio_channels
+ // keyFrameCount: to change audio output frame count, value is an int
+ static const char *keyRouting;
+ static const char *keySamplingRate;
+ static const char *keyFormat;
+ static const char *keyChannels;
+ static const char *keyFrameCount;
+
+ String8 toString();
+
+ status_t add(const String8& key, const String8& value);
+ status_t addInt(const String8& key, const int value);
+ status_t addFloat(const String8& key, const float value);
+
+ status_t remove(const String8& key);
+
+ status_t get(const String8& key, String8& value);
+ status_t getInt(const String8& key, int& value);
+ status_t getFloat(const String8& key, float& value);
+ status_t getAt(size_t index, String8& key, String8& value);
+
+ size_t size() { return mParameters.size(); }
+
+private:
+ String8 mKeyValuePairs;
+ KeyedVector <String8, String8> mParameters;
};
}; // namespace android
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index 7c86a65..981c2f6 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -26,8 +26,8 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
#include <utils/threads.h>
@@ -117,9 +117,9 @@ public:
* streamType: Select the type of audio stream this track is attached to
* (e.g. AudioSystem::MUSIC).
* sampleRate: Track sampling rate in Hz.
- * format: PCM sample format (e.g AudioSystem::PCM_16_BIT for signed
+ * format: Audio format (e.g AudioSystem::PCM_16_BIT for signed
* 16 bits per sample).
- * channelCount: Number of PCM channels (e.g 2 for stereo).
+ * channels: Channel mask: see AudioSystem::audio_channels.
* frameCount: Total size of track PCM buffer in frames. This defines the
* latency of the track.
* flags: Reserved for future use.
@@ -133,7 +133,7 @@ public:
AudioTrack( int streamType,
uint32_t sampleRate = 0,
int format = 0,
- int channelCount = 0,
+ int channels = 0,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -152,7 +152,7 @@ public:
AudioTrack( int streamType,
uint32_t sampleRate = 0,
int format = 0,
- int channelCount = 0,
+ int channels = 0,
const sp<IMemory>& sharedBuffer = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -169,13 +169,13 @@ public:
* Returned status (from utils/Errors.h) can be:
* - NO_ERROR: successful intialization
* - INVALID_OPERATION: AudioTrack is already intitialized
- * - BAD_VALUE: invalid parameter (channelCount, format, sampleRate...)
+ * - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
* - NO_INIT: audio server or audio hardware not initialized
* */
status_t set(int streamType =-1,
uint32_t sampleRate = 0,
int format = 0,
- int channelCount = 0,
+ int channels = 0,
int frameCount = 0,
uint32_t flags = 0,
callback_t cbf = 0,
@@ -330,6 +330,16 @@ public:
*/
status_t reload();
+ /* returns a handle on the audio output used by this AudioTrack.
+ *
+ * Parameters:
+ * none.
+ *
+ * Returned value:
+ * handle on audio hardware output
+ */
+ audio_io_handle_t getOutput();
+
/* obtains a buffer of "frameCount" frames. The buffer must be
* filled entirely. If the track is stopped, obtainBuffer() returns
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
@@ -387,7 +397,6 @@ private:
sp<AudioTrackThread> mAudioTrackThread;
float mVolume[2];
- uint32_t mSampleRate;
uint32_t mFrameCount;
audio_track_cblk_t* mCblk;
@@ -395,6 +404,7 @@ private:
uint8_t mFormat;
uint8_t mChannelCount;
uint8_t mMuted;
+ uint32_t mChannels;
status_t mStatus;
uint32_t mLatency;
@@ -410,6 +420,7 @@ private:
bool mMarkerReached;
uint32_t mNewPosition;
uint32_t mUpdatePeriod;
+ uint32_t mFlags;
};
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 3e59d85..8018568 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -23,11 +23,11 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
#include <media/IAudioFlingerClient.h>
-
+#include <utils/String8.h>
namespace android {
@@ -50,11 +50,12 @@ public:
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
+ int output,
status_t *status) = 0;
virtual sp<IAudioRecord> openRecord(
pid_t pid,
- int inputSource,
+ int input,
uint32_t sampleRate,
int format,
int channelCount,
@@ -83,19 +84,14 @@ public:
/* set/get stream type state. This will probably be used by
* the preference panel, mostly.
*/
- virtual status_t setStreamVolume(int stream, float value) = 0;
+ virtual status_t setStreamVolume(int stream, float value, int output) = 0;
virtual status_t setStreamMute(int stream, bool muted) = 0;
- virtual float streamVolume(int stream) const = 0;
+ virtual float streamVolume(int stream, int output) const = 0;
virtual bool streamMute(int stream) const = 0;
- // set/get audio routing
- virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask) = 0;
- virtual uint32_t getRouting(int mode) const = 0;
-
- // set/get audio mode
+ // set audio mode
virtual status_t setMode(int mode) = 0;
- virtual int getMode() const = 0;
// mic mute/state
virtual status_t setMicMute(bool state) = 0;
@@ -104,22 +100,34 @@ public:
// is a music stream active?
virtual bool isMusicActive() const = 0;
- // pass a generic configuration parameter to libaudio
- // Temporary interface, do not use
- // TODO: Replace with a more generic key:value get/set mechanism
- virtual status_t setParameter(const char* key, const char* value) = 0;
+ virtual status_t setParameters(int ioHandle, const String8& keyValuePairs) = 0;
+ virtual String8 getParameters(int ioHandle, const String8& keys) = 0;
// register a current process for audio output change notifications
virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0;
// retrieve the audio recording buffer size
virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount) = 0;
-
- // force AudioFlinger thread out of standby
- virtual void wakeUp() = 0;
- // is A2DP output enabled
- virtual bool isA2dpEnabled() const = 0;
+ virtual int openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags) = 0;
+ virtual int openDuplicateOutput(int output1, int output2) = 0;
+ virtual status_t closeOutput(int output) = 0;
+ virtual status_t suspendOutput(int output) = 0;
+ virtual status_t restoreOutput(int output) = 0;
+
+ virtual int openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics) = 0;
+ virtual status_t closeInput(int input) = 0;
+
+ virtual status_t setStreamOutput(uint32_t stream, int output) = 0;
};
diff --git a/include/media/IAudioFlingerClient.h b/include/media/IAudioFlingerClient.h
index c3deb0b..aa0cdcf 100644
--- a/include/media/IAudioFlingerClient.h
+++ b/include/media/IAudioFlingerClient.h
@@ -19,8 +19,8 @@
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-
+#include <binder/IInterface.h>
+#include <utils/KeyedVector.h>
namespace android {
@@ -31,8 +31,8 @@ class IAudioFlingerClient : public IInterface
public:
DECLARE_META_INTERFACE(AudioFlingerClient);
- // Notifies a change of audio output from/to hardware to/from A2DP.
- virtual void a2dpEnabledChanged(bool enabled) = 0;
+ // Notifies a change of audio input/output configuration.
+ virtual void ioConfigChanged(int event, int ioHandle, void *param2) = 0;
};
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
new file mode 100644
index 0000000..4804bbd
--- /dev/null
+++ b/include/media/IAudioPolicyService.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IAUDIOPOLICYSERVICE_H
+#define ANDROID_IAUDIOPOLICYSERVICE_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <media/AudioSystem.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyService : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AudioPolicyService);
+
+ //
+ // IAudioPolicyService interface (see AudioPolicyInterface for method descriptions)
+ //
+ virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address) = 0;
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address) = 0;
+ virtual status_t setPhoneState(int state) = 0;
+ virtual status_t setRingerMode(uint32_t mode, uint32_t mask) = 0;
+ virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) = 0;
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage) = 0;
+ virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT) = 0;
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) = 0;
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) = 0;
+ virtual void releaseOutput(audio_io_handle_t output) = 0;
+ virtual audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0) = 0;
+ virtual status_t startInput(audio_io_handle_t input) = 0;
+ virtual status_t stopInput(audio_io_handle_t input) = 0;
+ virtual void releaseInput(audio_io_handle_t input) = 0;
+ virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax) = 0;
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index) = 0;
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyService : public BnInterface<IAudioPolicyService>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICE_H
diff --git a/include/media/IAudioRecord.h b/include/media/IAudioRecord.h
index 9d45d2d..46735de 100644
--- a/include/media/IAudioRecord.h
+++ b/include/media/IAudioRecord.h
@@ -22,8 +22,8 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
namespace android {
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index 12f2111..de6426a 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -22,8 +22,8 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
namespace android {
diff --git a/include/media/IMediaMetadataRetriever.h b/include/media/IMediaMetadataRetriever.h
index c677e83..9baba8e 100644
--- a/include/media/IMediaMetadataRetriever.h
+++ b/include/media/IMediaMetadataRetriever.h
@@ -19,9 +19,9 @@
#define ANDROID_IMEDIAMETADATARETRIEVER_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
namespace android {
diff --git a/include/media/IMediaPlayer.h b/include/media/IMediaPlayer.h
index a683e74..b6f654f 100644
--- a/include/media/IMediaPlayer.h
+++ b/include/media/IMediaPlayer.h
@@ -18,11 +18,12 @@
#define ANDROID_IMEDIAPLAYER_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
namespace android {
+class Parcel;
class ISurface;
class IMediaPlayer: public IInterface
@@ -45,6 +46,36 @@ public:
virtual status_t setAudioStreamType(int type) = 0;
virtual status_t setLooping(int loop) = 0;
virtual status_t setVolume(float leftVolume, float rightVolume) = 0;
+
+ // Invoke a generic method on the player by using opaque parcels
+ // for the request and reply.
+ // @param request Parcel that must start with the media player
+ // interface token.
+ // @param[out] reply Parcel to hold the reply data. Cannot be null.
+ // @return OK if the invocation was made successfully.
+ virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+
+ // Set a new metadata filter.
+ // @param filter A set of allow and drop rules serialized in a Parcel.
+ // @return OK if the invocation was made successfully.
+ virtual status_t setMetadataFilter(const Parcel& filter) = 0;
+
+ // Retrieve a set of metadata.
+ // @param update_only Include only the metadata that have changed
+ // since the last invocation of getMetadata.
+ // The set is built using the unfiltered
+ // notifications the native player sent to the
+ // MediaPlayerService during that period of
+ // time. If false, all the metadatas are considered.
+ // @param apply_filter If true, once the metadata set has been built based
+ // on the value update_only, the current filter is
+ // applied.
+ // @param[out] metadata On exit contains a set (possibly empty) of metadata.
+ // Valid only if the call returned OK.
+ // @return OK if the invocation was made successfully.
+ virtual status_t getMetadata(bool update_only,
+ bool apply_filter,
+ Parcel *metadata) = 0;
};
// ----------------------------------------------------------------------------
@@ -61,4 +92,3 @@ public:
}; // namespace android
#endif // ANDROID_IMEDIAPLAYER_H
-
diff --git a/include/media/IMediaPlayerClient.h b/include/media/IMediaPlayerClient.h
index 5d32811..eee6c97 100644
--- a/include/media/IMediaPlayerClient.h
+++ b/include/media/IMediaPlayerClient.h
@@ -18,8 +18,8 @@
#define ANDROID_IMEDIAPLAYERCLIENT_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
namespace android {
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index d1d96b1..39b5e57 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -17,9 +17,10 @@
#ifndef ANDROID_IMEDIAPLAYERSERVICE_H
#define ANDROID_IMEDIAPLAYERSERVICE_H
+#include <utils/Errors.h> // for status_t
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
#include <media/IMediaPlayerClient.h>
#include <media/IMediaPlayer.h>
@@ -28,6 +29,7 @@
namespace android {
class IMediaRecorder;
+class IOMX;
class IMediaPlayerService: public IInterface
{
@@ -36,11 +38,11 @@ public:
virtual sp<IMediaRecorder> createMediaRecorder(pid_t pid) = 0;
virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid) = 0;
-
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url) = 0;
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
+ virtual sp<IOMX> createOMX() = 0;
};
// ----------------------------------------------------------------------------
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 64d3a40..24ac82b 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -18,7 +18,7 @@
#ifndef ANDROID_IMEDIARECORDER_H
#define ANDROID_IMEDIARECORDER_H
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
namespace android {
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
new file mode 100644
index 0000000..0014d5c
--- /dev/null
+++ b/include/media/IOMX.h
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_IOMX_H_
+
+#define ANDROID_IOMX_H_
+
+#include <binder/IInterface.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+
+#include <OMX_Core.h>
+#include <OMX_Video.h>
+
+namespace android {
+
+class IMemory;
+class IOMXObserver;
+class IOMXRenderer;
+class ISurface;
+class Surface;
+
+class IOMX : public IInterface {
+public:
+ DECLARE_META_INTERFACE(OMX);
+
+ typedef void *buffer_id;
+ typedef void *node_id;
+
+ virtual status_t list_nodes(List<String8> *list) = 0;
+
+ virtual status_t allocate_node(const char *name, node_id *node) = 0;
+ virtual status_t free_node(node_id node) = 0;
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) = 0;
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) = 0;
+
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) = 0;
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) = 0;
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) = 0;
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) = 0;
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) = 0;
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
+
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer) = 0;
+
+ virtual void fill_buffer(node_id node, buffer_id buffer) = 0;
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) = 0;
+
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) = 0;
+
+ virtual sp<IOMXRenderer> createRenderer(
+ const sp<ISurface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight) = 0;
+
+ // Note: This method is _not_ virtual, it exists as a wrapper around
+ // the virtual "createRenderer" method above facilitating extraction
+ // of the ISurface from a regular Surface.
+ sp<IOMXRenderer> createRenderer(
+ const sp<Surface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight);
+};
+
+struct omx_message {
+ enum {
+ EVENT,
+ EMPTY_BUFFER_DONE,
+ FILL_BUFFER_DONE,
+
+ } type;
+
+ IOMX::node_id node;
+
+ union {
+ // if type == EVENT
+ struct {
+ OMX_EVENTTYPE event;
+ OMX_U32 data1;
+ OMX_U32 data2;
+ } event_data;
+
+ // if type == EMPTY_BUFFER_DONE
+ struct {
+ IOMX::buffer_id buffer;
+ } buffer_data;
+
+ // if type == FILL_BUFFER_DONE
+ struct {
+ IOMX::buffer_id buffer;
+ OMX_U32 range_offset;
+ OMX_U32 range_length;
+ OMX_U32 flags;
+ OMX_TICKS timestamp;
+ OMX_PTR platform_private;
+ } extended_buffer_data;
+
+ } u;
+};
+
+class IOMXObserver : public IInterface {
+public:
+ DECLARE_META_INTERFACE(OMXObserver);
+
+ virtual void on_message(const omx_message &msg) = 0;
+};
+
+class IOMXRenderer : public IInterface {
+public:
+ DECLARE_META_INTERFACE(OMXRenderer);
+
+ virtual void render(IOMX::buffer_id buffer) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BnOMX : public BnInterface<IOMX> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+class BnOMXObserver : public BnInterface<IOMXObserver> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+class BnOMXRenderer : public BnInterface<IOMXRenderer> {
+public:
+ virtual status_t onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply,
+ uint32_t flags = 0);
+};
+
+} // namespace android
+
+#endif // ANDROID_IOMX_H_
diff --git a/include/media/MediaMetadataRetrieverInterface.h b/include/media/MediaMetadataRetrieverInterface.h
index b178836..e228357 100644
--- a/include/media/MediaMetadataRetrieverInterface.h
+++ b/include/media/MediaMetadataRetrieverInterface.h
@@ -44,6 +44,28 @@ class MediaMetadataRetrieverInterface : public MediaMetadataRetrieverBase
{
public:
virtual ~MediaMetadataRetrieverInterface() {}
+
+ // @param mode The intended mode of operations:
+ // can be any of the following:
+ // METADATA_MODE_NOOP: Experimental - just add and remove data source.
+ // METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.
+ // METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.
+ // METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame
+ // capture and meta data retrieval.
+ virtual status_t setMode(int mode) {
+ if (mode < METADATA_MODE_NOOP ||
+ mode > METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL) {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
+
+ virtual status_t getMode(int* mode) const { *mode = mMode; return NO_ERROR; }
+ virtual VideoFrame* captureFrame() { return NULL; }
+ virtual MediaAlbumArt* extractAlbumArt() { return NULL; }
+ virtual const char* extractMetadata(int keyCode) { return NULL; }
+
+ uint32_t mMode;
};
}; // namespace android
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 7bf555a..f723cfd 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -19,20 +19,32 @@
#ifdef __cplusplus
+#include <sys/types.h>
#include <ui/ISurface.h>
#include <utils/RefBase.h>
+#include <utils/Errors.h>
#include <media/mediaplayer.h>
#include <media/AudioSystem.h>
+#include <media/Metadata.h>
namespace android {
+class Parcel;
+template<typename T> class SortedVector;
+
enum player_type {
PV_PLAYER = 1,
SONIVOX_PLAYER = 2,
- VORBIS_PLAYER = 3
+ VORBIS_PLAYER = 3,
+ STAGEFRIGHT_PLAYER = 4,
+ // Test players are available only in the 'test' and 'eng' builds.
+ // The shared library with the test player is passed passed as an
+ // argument to the 'test:' url in the setDataSource call.
+ TEST_PLAYER = 5,
};
+
#define DEFAULT_AUDIOSINK_BUFFERCOUNT 4
#define DEFAULT_AUDIOSINK_BUFFERSIZE 1200
#define DEFAULT_AUDIOSINK_SAMPLERATE 44100
@@ -45,10 +57,12 @@ typedef void (*notify_callback_f)(void* cookie, int msg, int ext1, int ext2);
class MediaPlayerBase : public RefBase
{
public:
-
// AudioSink: abstraction layer for audio output
class AudioSink : public RefBase {
public:
+ typedef void (*AudioCallback)(
+ AudioSink *audioSink, void *buffer, size_t size, void *cookie);
+
virtual ~AudioSink() {}
virtual bool ready() const = 0; // audio output is open and ready
virtual bool realtime() const = 0; // audio output is real-time output
@@ -58,7 +72,17 @@ public:
virtual ssize_t frameSize() const = 0;
virtual uint32_t latency() const = 0;
virtual float msecsPerFrame() const = 0;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format=AudioSystem::PCM_16_BIT, int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT) = 0;
+
+ // If no callback is specified, use the "write" API below to submit
+ // audio data. Otherwise return a full buffer of audio data on each
+ // callback.
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount,
+ int format=AudioSystem::PCM_16_BIT,
+ int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ AudioCallback cb = NULL,
+ void *cookie = NULL) = 0;
+
virtual void start() = 0;
virtual ssize_t write(const void* buffer, size_t size) = 0;
virtual void stop() = 0;
@@ -88,6 +112,26 @@ public:
virtual player_type playerType() = 0;
virtual void setNotifyCallback(void* cookie, notify_callback_f notifyFunc) {
mCookie = cookie; mNotify = notifyFunc; }
+ // Invoke a generic method on the player by using opaque parcels
+ // for the request and reply.
+ //
+ // @param request Parcel that is positioned at the start of the
+ // data sent by the java layer.
+ // @param[out] reply Parcel to hold the reply data. Cannot be null.
+ // @return OK if the call was successful.
+ virtual status_t invoke(const Parcel& request, Parcel *reply) = 0;
+
+ // The Client in the MetadataPlayerService calls this method on
+ // the native player to retrieve all or a subset of metadata.
+ //
+ // @param ids SortedList of metadata ID to be fetch. If empty, all
+ // the known metadata should be returned.
+ // @param[inout] records Parcel where the player appends its metadata.
+ // @return OK if the call was successful.
+ virtual status_t getMetadata(const media::Metadata::Filter& ids,
+ Parcel *records) {
+ return INVALID_OPERATION;
+ };
protected:
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
diff --git a/include/media/Metadata.h b/include/media/Metadata.h
new file mode 100644
index 0000000..241868a
--- /dev/null
+++ b/include/media/Metadata.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MEDIA_METADATA_H__
+#define ANDROID_MEDIA_METADATA_H__
+
+#include <sys/types.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+
+namespace android {
+class Parcel;
+
+namespace media {
+
+// Metadata is a class to build/serialize a set of metadata in a Parcel.
+//
+// This class should be kept in sync with android/media/Metadata.java.
+// It provides all the metadata ids available and methods to build the
+// header, add records and adjust the set size header field.
+//
+// Typical Usage:
+// ==============
+// Parcel p;
+// media::Metadata data(&p);
+//
+// data.appendHeader();
+// data.appendBool(Metadata::kPauseAvailable, true);
+// ... more append ...
+// data.updateLength();
+//
+
+class Metadata {
+ public:
+ typedef int32_t Type;
+ typedef SortedVector<Type> Filter;
+
+ static const Type kAny = 0;
+
+ // Keep in sync with android/media/Metadata.java
+ static const Type kTitle = 1; // String
+ static const Type kComment = 2; // String
+ static const Type kCopyright = 3; // String
+ static const Type kAlbum = 4; // String
+ static const Type kArtist = 5; // String
+ static const Type kAuthor = 6; // String
+ static const Type kComposer = 7; // String
+ static const Type kGenre = 8; // String
+ static const Type kDate = 9; // Date
+ static const Type kDuration = 10; // Integer(millisec)
+ static const Type kCdTrackNum = 11; // Integer 1-based
+ static const Type kCdTrackMax = 12; // Integer
+ static const Type kRating = 13; // String
+ static const Type kAlbumArt = 14; // byte[]
+ static const Type kVideoFrame = 15; // Bitmap
+ static const Type kCaption = 16; // TimedText
+
+ static const Type kBitRate = 17; // Integer, Aggregate rate of
+ // all the streams in bps.
+
+ static const Type kAudioBitRate = 18; // Integer, bps
+ static const Type kVideoBitRate = 19; // Integer, bps
+ static const Type kAudioSampleRate = 20; // Integer, Hz
+ static const Type kVideoframeRate = 21; // Integer, Hz
+
+ // See RFC2046 and RFC4281.
+ static const Type kMimeType = 22; // String
+ static const Type kAudioCodec = 23; // String
+ static const Type kVideoCodec = 24; // String
+
+ static const Type kVideoHeight = 25; // Integer
+ static const Type kVideoWidth = 26; // Integer
+ static const Type kNumTracks = 27; // Integer
+ static const Type kDrmCrippled = 28; // Boolean
+
+ // Playback capabilities.
+ static const Type kPauseAvailable = 29; // Boolean
+ static const Type kSeekBackwardAvailable = 30; // Boolean
+ static const Type kSeekForwardAvailable = 31; // Boolean
+
+ // @param p[inout] The parcel to append the metadata records
+ // to. The global metadata header should have been set already.
+ explicit Metadata(Parcel *p);
+ ~Metadata();
+
+ // Rewind the underlying parcel, undoing all the changes.
+ void resetParcel();
+
+ // Append the size and 'META' marker.
+ bool appendHeader();
+
+ // Once all the records have been added, call this to update the
+ // lenght field in the header.
+ void updateLength();
+
+ // append* are methods to append metadata.
+ // @param key Is the metadata Id.
+ // @param val Is the value of the metadata.
+ // @return true if successful, false otherwise.
+ // TODO: add more as needed to handle other types.
+ bool appendBool(Type key, bool val);
+ bool appendInt32(Type key, int32_t val);
+
+ private:
+ Metadata(const Metadata&);
+ Metadata& operator=(const Metadata&);
+
+
+ // Checks the key is valid and not already present.
+ bool checkKey(Type key);
+
+ Parcel *mData;
+ size_t mBegin;
+};
+
+} // namespace android::media
+} // namespace android
+
+#endif // ANDROID_MEDIA_METADATA_H__
diff --git a/include/media/PVPlayer.h b/include/media/PVPlayer.h
index 8122df6..8a66152 100644
--- a/include/media/PVPlayer.h
+++ b/include/media/PVPlayer.h
@@ -19,6 +19,7 @@
#include <utils/Errors.h>
#include <media/MediaPlayerInterface.h>
+#include <media/Metadata.h>
#define MAX_OPENCORE_INSTANCES 25
@@ -52,6 +53,10 @@ public:
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return PV_PLAYER; }
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+ virtual status_t getMetadata(
+ const SortedVector<media::Metadata::Type>& ids,
+ Parcel *records);
// make available to PlayerDriver
void sendEvent(int msg, int ext1=0, int ext2=0) { MediaPlayerBase::sendEvent(msg, ext1, ext2); }
@@ -62,6 +67,7 @@ private:
static void run_set_video_surface(status_t s, void *cookie, bool cancelled);
static void run_set_audio_output(status_t s, void *cookie, bool cancelled);
static void run_prepare(status_t s, void *cookie, bool cancelled);
+ static void check_for_live_streaming(status_t s, void *cookie, bool cancelled);
PlayerDriver* mPlayerDriver;
char * mDataSourcePath;
diff --git a/include/media/ToneGenerator.h b/include/media/ToneGenerator.h
index eafa661..ea6bf5d 100644
--- a/include/media/ToneGenerator.h
+++ b/include/media/ToneGenerator.h
@@ -154,7 +154,7 @@ public:
ToneGenerator(int streamType, float volume);
~ToneGenerator();
- bool startTone(int toneType);
+ bool startTone(int toneType, int durationMs = -1);
void stopTone();
bool isInited() { return (mState == TONE_IDLE)?false:true;}
@@ -246,6 +246,7 @@ private:
// NOTE: because mTotalSmp, mNextSegSmp are stored on 32 bit, current design will operate properly
// only if tone duration is less than about 27 Hours(@44100Hz sampling rate). If this time is exceeded,
// no crash will occur but tone sequence will show a glitch.
+ unsigned int mMaxSmp; // Maximum number of audio samples played (maximun tone duration)
unsigned short mCurSegment; // Current segment index in ToneDescriptor segments[]
unsigned short mCurCount; // Current sequence repeat count
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index f2719d3..cfc205c 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -20,7 +20,7 @@
#include <utils/Errors.h> // for status_t
#include <utils/threads.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <media/IMediaMetadataRetriever.h>
namespace android {
@@ -52,9 +52,22 @@ enum {
METADATA_KEY_VIDEO_FORMAT = 18,
METADATA_KEY_VIDEO_HEIGHT = 19,
METADATA_KEY_VIDEO_WIDTH = 20,
+ METADATA_KEY_WRITER = 21,
// Add more here...
};
+// The intended mode of operations:$
+// METADATA_MODE_NOOP: Experimental - just add and remove data source.$
+// METADATA_MODE_FRAME_CAPTURE_ONLY: For capture frame/thumbnail only.$
+// METADATA_MODE_METADATA_RETRIEVAL_ONLY: For meta data retrieval only.$
+// METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL: For both frame capture
+// and meta data retrieval.$
+enum {
+ METADATA_MODE_NOOP = 0x00,
+ METADATA_MODE_FRAME_CAPTURE_ONLY = 0x01,
+ METADATA_MODE_METADATA_RETRIEVAL_ONLY = 0x02,
+ METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL = 0x03
+};
class MediaMetadataRetriever: public RefBase
{
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 513ffe1..26b054bd 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -17,7 +17,7 @@
#ifndef ANDROID_MEDIAPLAYER_H
#define ANDROID_MEDIAPLAYER_H
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <ui/Surface.h>
#include <media/IMediaPlayerClient.h>
#include <media/IMediaPlayer.h>
@@ -97,6 +97,8 @@ enum media_info_type {
MEDIA_INFO_BAD_INTERLEAVING = 800,
// The media is not seekable (e.g live stream).
MEDIA_INFO_NOT_SEEKABLE = 801,
+ // New media metadata is available.
+ MEDIA_INFO_METADATA_UPDATE = 802,
};
@@ -151,7 +153,9 @@ public:
void notify(int msg, int ext1, int ext2);
static sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
static sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
-
+ status_t invoke(const Parcel& request, Parcel *reply);
+ status_t setMetadataFilter(const Parcel& filter);
+ status_t getMetadata(bool update_only, bool apply_filter, Parcel *metadata);
private:
void clear_l();
status_t seekTo_l(int msec);
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 9b54ca9..13316a9 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -18,7 +18,10 @@
#ifndef ANDROID_MEDIARECORDER_H
#define ANDROID_MEDIARECORDER_H
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <media/IMediaPlayerClient.h>
namespace android {
@@ -90,10 +93,6 @@ enum video_encoder {
VIDEO_ENCODER_LIST_END // must be the last - used to validate the video encoder type
};
-
-// Maximum frames per second is 24
-#define MEDIA_RECORDER_MAX_FRAME_RATE 24
-
/*
* The state machine of the media_recorder uses a set of different state names.
* The mapping between the media_recorder and the pvauthorengine is shown below:
diff --git a/include/media/mediascanner.h b/include/media/mediascanner.h
index fbef1db..cd0b86e 100644
--- a/include/media/mediascanner.h
+++ b/include/media/mediascanner.h
@@ -17,7 +17,10 @@
#ifndef MEDIASCANNER_H
#define MEDIASCANNER_H
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <pthread.h>
namespace android {
@@ -56,16 +59,17 @@ private:
class MediaScannerClient
{
public:
- MediaScannerClient();
- virtual ~MediaScannerClient();
- void setLocale(const char* locale);
- void beginFile();
- bool addStringTag(const char* name, const char* value);
- void endFile();
-
- virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
- virtual bool handleStringTag(const char* name, const char* value) = 0;
- virtual bool setMimeType(const char* mimeType) = 0;
+ MediaScannerClient();
+ virtual ~MediaScannerClient();
+ void setLocale(const char* locale);
+ void beginFile();
+ bool addStringTag(const char* name, const char* value);
+ void endFile();
+
+ virtual bool scanFile(const char* path, long long lastModified, long long fileSize) = 0;
+ virtual bool handleStringTag(const char* name, const char* value) = 0;
+ virtual bool setMimeType(const char* mimeType) = 0;
+ virtual bool addNoMediaFolder(const char* path) = 0;
protected:
void convertValues(uint32_t encoding);
diff --git a/include/media/stagefright/AMRExtractor.h b/include/media/stagefright/AMRExtractor.h
new file mode 100644
index 0000000..c8710d3
--- /dev/null
+++ b/include/media/stagefright/AMRExtractor.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMR_EXTRACTOR_H_
+
+#define AMR_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class String8;
+
+class AMRExtractor : public MediaExtractor {
+public:
+ AMRExtractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index);
+
+ static sp<MetaData> makeAMRFormat(bool isWide);
+
+protected:
+ virtual ~AMRExtractor();
+
+private:
+ sp<DataSource> mDataSource;
+ status_t mInitCheck;
+ bool mIsWide;
+
+ AMRExtractor(const AMRExtractor &);
+ AMRExtractor &operator=(const AMRExtractor &);
+};
+
+bool SniffAMR(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // AMR_EXTRACTOR_H_
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
new file mode 100644
index 0000000..960eda3
--- /dev/null
+++ b/include/media/stagefright/AudioPlayer.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_PLAYER_H_
+
+#define AUDIO_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/TimeSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaSource;
+class AudioTrack;
+
+class AudioPlayer : public TimeSource {
+public:
+ AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
+ virtual ~AudioPlayer();
+
+ // Caller retains ownership of "source".
+ void setSource(const sp<MediaSource> &source);
+
+ // Return time in us.
+ virtual int64_t getRealTimeUs();
+
+ void start();
+
+ void pause();
+ void resume();
+
+ void stop();
+
+ // Returns the timestamp of the last buffer played (in us).
+ int64_t getMediaTimeUs();
+
+ // Returns true iff a mapping is established, i.e. the AudioPlayer
+ // has played at least one frame of audio.
+ bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+ status_t seekTo(int64_t time_us);
+
+private:
+ sp<MediaSource> mSource;
+ AudioTrack *mAudioTrack;
+
+ MediaBuffer *mInputBuffer;
+
+ int mSampleRate;
+ int64_t mLatencyUs;
+ size_t mFrameSize;
+
+ Mutex mLock;
+ int64_t mNumFramesPlayed;
+
+ int64_t mPositionTimeMediaUs;
+ int64_t mPositionTimeRealUs;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ bool mStarted;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+ static void AudioCallback(int event, void *user, void *info);
+ void AudioCallback(int event, void *info);
+
+ static void AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *data, size_t size, void *me);
+
+ void fillBuffer(void *data, size_t size);
+
+ int64_t getRealTimeUsLocked() const;
+
+ AudioPlayer(const AudioPlayer &);
+ AudioPlayer &operator=(const AudioPlayer &);
+};
+
+} // namespace android
+
+#endif // AUDIO_PLAYER_H_
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
new file mode 100644
index 0000000..e129958
--- /dev/null
+++ b/include/media/stagefright/AudioSource.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AUDIO_SOURCE_H_
+
+#define AUDIO_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class AudioRecord;
+
+class AudioSource {
+public:
+ AudioSource(int inputSource);
+ virtual ~AudioSource();
+
+ status_t initCheck() const;
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ AudioRecord *mRecord;
+ status_t mInitCheck;
+
+ AudioSource(const AudioSource &);
+ AudioSource &operator=(const AudioSource &);
+};
+
+} // namespace android
+
+#endif // AUDIO_SOURCE_H_
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
new file mode 100644
index 0000000..e35e19e
--- /dev/null
+++ b/include/media/stagefright/CachingDataSource.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CACHING_DATASOURCE_H_
+
+#define CACHING_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CachingDataSource : public DataSource {
+public:
+ CachingDataSource(
+ const sp<DataSource> &source, size_t pageSize, int numPages);
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+protected:
+ virtual ~CachingDataSource();
+
+private:
+ struct Page {
+ Page *mPrev, *mNext;
+ off_t mOffset;
+ size_t mLength;
+ void *mData;
+ };
+
+ sp<DataSource> mSource;
+ void *mData;
+ size_t mPageSize;
+ Page *mFirst, *mLast;
+
+ Page *allocate_page();
+
+ Mutex mLock;
+
+ CachingDataSource(const CachingDataSource &);
+ CachingDataSource &operator=(const CachingDataSource &);
+};
+
+} // namespace android
+
+#endif // CACHING_DATASOURCE_H_
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
new file mode 100644
index 0000000..7042e1a
--- /dev/null
+++ b/include/media/stagefright/CameraSource.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAMERA_SOURCE_H_
+
+#define CAMERA_SOURCE_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ICamera;
+class ICameraClient;
+class IMemory;
+
+class CameraSource : public MediaSource,
+ public MediaBufferObserver {
+public:
+ static CameraSource *Create();
+
+ virtual ~CameraSource();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
+ virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
+
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ CameraSource(const sp<ICamera> &camera, const sp<ICameraClient> &client);
+
+ sp<ICamera> mCamera;
+ sp<ICameraClient> mCameraClient;
+
+ Mutex mLock;
+ Condition mFrameAvailableCondition;
+ List<sp<IMemory> > mFrames;
+
+ int mNumFrames;
+ bool mStarted;
+
+ CameraSource(const CameraSource &);
+ CameraSource &operator=(const CameraSource &);
+};
+
+} // namespace android
+
+#endif // CAMERA_SOURCE_H_
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
new file mode 100644
index 0000000..f46f0af
--- /dev/null
+++ b/include/media/stagefright/DataSource.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DATA_SOURCE_H_
+
+#define DATA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class String8;
+
+class DataSource : public RefBase {
+public:
+ DataSource() {}
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0;
+
+ // Convenience methods:
+ bool getUInt16(off_t offset, uint16_t *x);
+
+ // May return ERROR_UNSUPPORTED.
+ virtual status_t getSize(off_t *size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ bool sniff(String8 *mimeType, float *confidence);
+
+ typedef bool (*SnifferFunc)(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+ static void RegisterSniffer(SnifferFunc func);
+ static void RegisterDefaultSniffers();
+
+protected:
+ virtual ~DataSource() {}
+
+private:
+ static Mutex gSnifferMutex;
+ static List<SnifferFunc> gSniffers;
+
+ DataSource(const DataSource &);
+ DataSource &operator=(const DataSource &);
+};
+
+} // namespace android
+
+#endif // DATA_SOURCE_H_
diff --git a/include/media/stagefright/ESDS.h b/include/media/stagefright/ESDS.h
new file mode 100644
index 0000000..01bcd18
--- /dev/null
+++ b/include/media/stagefright/ESDS.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ESDS_H_
+
+#define ESDS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class ESDS {
+public:
+ ESDS(const void *data, size_t size);
+ ~ESDS();
+
+ status_t InitCheck() const;
+
+ status_t getCodecSpecificInfo(const void **data, size_t *size) const;
+
+private:
+ enum {
+ kTag_ESDescriptor = 0x03,
+ kTag_DecoderConfigDescriptor = 0x04,
+ kTag_DecoderSpecificInfo = 0x05
+ };
+
+ uint8_t *mData;
+ size_t mSize;
+
+ status_t mInitCheck;
+
+ size_t mDecoderSpecificOffset;
+ size_t mDecoderSpecificLength;
+
+ status_t skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const;
+
+ status_t parse();
+ status_t parseESDescriptor(size_t offset, size_t size);
+ status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
+
+ ESDS(const ESDS &);
+ ESDS &operator=(const ESDS &);
+};
+
+} // namespace android
+#endif // ESDS_H_
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
new file mode 100644
index 0000000..ccbe0ef
--- /dev/null
+++ b/include/media/stagefright/FileSource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FILE_SOURCE_H_
+
+#define FILE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FileSource : public DataSource {
+public:
+ FileSource(const char *filename);
+ virtual ~FileSource();
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+ FILE *mFile;
+ Mutex mLock;
+
+ FileSource(const FileSource &);
+ FileSource &operator=(const FileSource &);
+};
+
+} // namespace android
+
+#endif // FILE_SOURCE_H_
+
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
new file mode 100644
index 0000000..0587c7c
--- /dev/null
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_DATASOURCE_H_
+
+#define HTTP_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPStream.h>
+
+namespace android {
+
+class HTTPDataSource : public DataSource {
+public:
+ HTTPDataSource(const char *host, int port, const char *path);
+ HTTPDataSource(const char *uri);
+
+ virtual ~HTTPDataSource();
+
+ // XXXandih
+ status_t InitCheck() const { return OK; }
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+ enum {
+ kBufferSize = 64 * 1024
+ };
+
+ HTTPStream mHttp;
+ char *mHost;
+ int mPort;
+ char *mPath;
+
+ void *mBuffer;
+ size_t mBufferLength;
+ off_t mBufferOffset;
+
+ HTTPDataSource(const HTTPDataSource &);
+ HTTPDataSource &operator=(const HTTPDataSource &);
+};
+
+} // namespace android
+
+#endif // HTTP_DATASOURCE_H_
+
diff --git a/include/media/stagefright/HTTPStream.h b/include/media/stagefright/HTTPStream.h
new file mode 100644
index 0000000..3d0d67a
--- /dev/null
+++ b/include/media/stagefright/HTTPStream.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef HTTP_STREAM_H_
+
+#define HTTP_STREAM_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/string.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class HTTPStream {
+public:
+ HTTPStream();
+ ~HTTPStream();
+
+ status_t connect(const char *server, int port = 80);
+ status_t disconnect();
+
+ status_t send(const char *data, size_t size);
+
+ // Assumes data is a '\0' terminated string.
+ status_t send(const char *data);
+
+ // Receive up to "size" bytes of data.
+ ssize_t receive(void *data, size_t size);
+
+ status_t receive_header(int *http_status);
+
+ // The header key used to retrieve the status line.
+ static const char *kStatusKey;
+
+ bool find_header_value(
+ const string &key, string *value) const;
+
+private:
+ enum State {
+ READY,
+ CONNECTED
+ };
+
+ State mState;
+ int mSocket;
+
+ KeyedVector<string, string> mHeaders;
+
+ // Receive a line of data terminated by CRLF, line will be '\0' terminated
+ // _excluding_ the termianting CRLF.
+ status_t receive_line(char *line, size_t size);
+
+ HTTPStream(const HTTPStream &);
+ HTTPStream &operator=(const HTTPStream &);
+};
+
+} // namespace android
+
+#endif // HTTP_STREAM_H_
diff --git a/include/media/stagefright/MP3Extractor.h b/include/media/stagefright/MP3Extractor.h
new file mode 100644
index 0000000..11ba01d
--- /dev/null
+++ b/include/media/stagefright/MP3Extractor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MP3_EXTRACTOR_H_
+
+#define MP3_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class String8;
+
+class MP3Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MP3Extractor(const sp<DataSource> &source);
+
+ virtual size_t countTracks();
+ virtual sp<MediaSource> getTrack(size_t index);
+ virtual sp<MetaData> getTrackMetaData(size_t index);
+
+protected:
+ virtual ~MP3Extractor();
+
+private:
+ sp<DataSource> mDataSource;
+ off_t mFirstFramePos;
+ sp<MetaData> mMeta;
+ uint32_t mFixedHeader;
+
+ MP3Extractor(const MP3Extractor &);
+ MP3Extractor &operator=(const MP3Extractor &);
+};
+
+bool SniffMP3(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MP3_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Extractor.h b/include/media/stagefright/MPEG4Extractor.h
new file mode 100644
index 0000000..932e30f
--- /dev/null
+++ b/include/media/stagefright/MPEG4Extractor.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPEG4_EXTRACTOR_H_
+
+#define MPEG4_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class SampleTable;
+class String8;
+
+class MPEG4Extractor : public MediaExtractor {
+public:
+ // Extractor assumes ownership of "source".
+ MPEG4Extractor(const sp<DataSource> &source);
+
+ size_t countTracks();
+ sp<MediaSource> getTrack(size_t index);
+ sp<MetaData> getTrackMetaData(size_t index);
+
+protected:
+ virtual ~MPEG4Extractor();
+
+private:
+ struct Track {
+ Track *next;
+ sp<MetaData> meta;
+ uint32_t timescale;
+ sp<SampleTable> sampleTable;
+ };
+
+ sp<DataSource> mDataSource;
+ bool mHaveMetadata;
+
+ Track *mFirstTrack, *mLastTrack;
+
+ uint32_t mHandlerType;
+
+ status_t readMetaData();
+ status_t parseChunk(off_t *offset, int depth);
+
+ MPEG4Extractor(const MPEG4Extractor &);
+ MPEG4Extractor &operator=(const MPEG4Extractor &);
+};
+
+bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence);
+
+} // namespace android
+
+#endif // MPEG4_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
new file mode 100644
index 0000000..6b0399f
--- /dev/null
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MPEG4_WRITER_H_
+
+#define MPEG4_WRITER_H_
+
+#include <stdio.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaSource;
+class MetaData;
+
+class MPEG4Writer : public RefBase {
+public:
+ MPEG4Writer(const char *filename);
+
+ void addSource(const sp<MediaSource> &source);
+ status_t start();
+ bool reachedEOS();
+ void stop();
+
+ void beginBox(const char *fourcc);
+ void writeInt8(int8_t x);
+ void writeInt16(int16_t x);
+ void writeInt32(int32_t x);
+ void writeInt64(int64_t x);
+ void writeCString(const char *s);
+ void writeFourcc(const char *fourcc);
+ void write(const void *data, size_t size);
+ void endBox();
+
+protected:
+ virtual ~MPEG4Writer();
+
+private:
+ class Track;
+
+ FILE *mFile;
+ off_t mOffset;
+ off_t mMdatOffset;
+ Mutex mLock;
+
+ List<Track *> mTracks;
+
+ List<off_t> mBoxes;
+
+ off_t addSample(MediaBuffer *buffer);
+
+ MPEG4Writer(const MPEG4Writer &);
+ MPEG4Writer &operator=(const MPEG4Writer &);
+};
+
+} // namespace android
+
+#endif // MPEG4_WRITER_H_
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
new file mode 100644
index 0000000..339e6fb
--- /dev/null
+++ b/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_H_
+
+#define MEDIA_BUFFER_H_
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaBufferObserver;
+class MetaData;
+
+class MediaBufferObserver {
+public:
+ MediaBufferObserver() {}
+ virtual ~MediaBufferObserver() {}
+
+ virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
+
+private:
+ MediaBufferObserver(const MediaBufferObserver &);
+ MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBuffer {
+public:
+ // The underlying data remains the responsibility of the caller!
+ MediaBuffer(void *data, size_t size);
+
+ MediaBuffer(size_t size);
+
+ // Decrements the reference count and returns the buffer to its
+ // associated MediaBufferGroup if the reference count drops to 0.
+ void release();
+
+ // Increments the reference count.
+ void add_ref();
+
+ void *data() const;
+ size_t size() const;
+
+ size_t range_offset() const;
+ size_t range_length() const;
+
+ void set_range(size_t offset, size_t length);
+
+ sp<MetaData> meta_data();
+
+ // Clears meta data and resets the range to the full extent.
+ void reset();
+
+ void setObserver(MediaBufferObserver *group);
+
+ // Returns a clone of this MediaBuffer increasing its reference count.
+ // The clone references the same data but has its own range and
+ // MetaData.
+ MediaBuffer *clone();
+
+ int refcount() const;
+
+protected:
+ virtual ~MediaBuffer();
+
+private:
+ friend class MediaBufferGroup;
+ friend class OMXDecoder;
+
+ // For use by OMXDecoder, reference count must be 1, drop reference
+ // count to 0 without signalling the observer.
+ void claim();
+
+ MediaBufferObserver *mObserver;
+ MediaBuffer *mNextBuffer;
+ int mRefCount;
+
+ void *mData;
+ size_t mSize, mRangeOffset, mRangeLength;
+
+ bool mOwnsData;
+
+ sp<MetaData> mMetaData;
+
+ MediaBuffer *mOriginal;
+
+ void setNextBuffer(MediaBuffer *buffer);
+ MediaBuffer *nextBuffer();
+
+ MediaBuffer(const MediaBuffer &);
+ MediaBuffer &operator=(const MediaBuffer &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_H_
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
new file mode 100644
index 0000000..0488292
--- /dev/null
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_GROUP_H_
+
+#define MEDIA_BUFFER_GROUP_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaBufferGroup : public MediaBufferObserver {
+public:
+ MediaBufferGroup();
+ ~MediaBufferGroup();
+
+ void add_buffer(MediaBuffer *buffer);
+
+ // Blocks until a buffer is available and returns it to the caller,
+ // the returned buffer will have a reference count of 1.
+ status_t acquire_buffer(MediaBuffer **buffer);
+
+protected:
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+ friend class MediaBuffer;
+
+ Mutex mLock;
+ Condition mCondition;
+
+ MediaBuffer *mFirstBuffer, *mLastBuffer;
+
+ MediaBufferGroup(const MediaBufferGroup &);
+ MediaBufferGroup &operator=(const MediaBufferGroup &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_GROUP_H_
diff --git a/include/media/stagefright/MediaDebug.h b/include/media/stagefright/MediaDebug.h
new file mode 100644
index 0000000..c8a8f00
--- /dev/null
+++ b/include/media/stagefright/MediaDebug.h
@@ -0,0 +1,20 @@
+#ifndef MEDIA_DEBUG_H_
+
+#define MEDIA_DEBUG_H_
+
+#include <cutils/log.h>
+
+#define LITERAL_TO_STRING_INTERNAL(x) #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK_EQ(x,y) \
+ LOG_ALWAYS_FATAL_IF( \
+ (x) != (y), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y)
+
+#define CHECK(x) \
+ LOG_ALWAYS_FATAL_IF( \
+ !(x), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x)
+
+#endif // MEDIA_DEBUG_H_
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
new file mode 100644
index 0000000..feb66e3
--- /dev/null
+++ b/include/media/stagefright/MediaDefs.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_DEFS_H_
+
+#define MEDIA_DEFS_H_
+
+namespace android {
+
+extern const char *MEDIA_MIMETYPE_IMAGE_JPEG;
+
+extern const char *MEDIA_MIMETYPE_VIDEO_AVC;
+extern const char *MEDIA_MIMETYPE_VIDEO_MPEG4;
+extern const char *MEDIA_MIMETYPE_VIDEO_H263;
+extern const char *MEDIA_MIMETYPE_VIDEO_RAW;
+
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB;
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
+extern const char *MEDIA_MIMETYPE_AUDIO_MPEG;
+extern const char *MEDIA_MIMETYPE_AUDIO_AAC;
+extern const char *MEDIA_MIMETYPE_AUDIO_RAW;
+
+extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
+
+} // namespace android
+
+#endif // MEDIA_DEFS_H_
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
new file mode 100644
index 0000000..2bb0ed6
--- /dev/null
+++ b/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_ERRORS_H_
+
+#define MEDIA_ERRORS_H_
+
+#include <utils/Errors.h>
+
+namespace android {
+
+enum {
+ MEDIA_ERROR_BASE = -1000,
+
+ ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
+ ERROR_NOT_CONNECTED = MEDIA_ERROR_BASE - 1,
+ ERROR_UNKNOWN_HOST = MEDIA_ERROR_BASE - 2,
+ ERROR_CANNOT_CONNECT = MEDIA_ERROR_BASE - 3,
+ ERROR_IO = MEDIA_ERROR_BASE - 4,
+ ERROR_CONNECTION_LOST = MEDIA_ERROR_BASE - 5,
+ ERROR_MALFORMED = MEDIA_ERROR_BASE - 7,
+ ERROR_OUT_OF_RANGE = MEDIA_ERROR_BASE - 8,
+ ERROR_BUFFER_TOO_SMALL = MEDIA_ERROR_BASE - 9,
+ ERROR_UNSUPPORTED = MEDIA_ERROR_BASE - 10,
+ ERROR_END_OF_STREAM = MEDIA_ERROR_BASE - 11,
+};
+
+} // namespace android
+
+#endif // MEDIA_ERRORS_H_
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
new file mode 100644
index 0000000..67e45bd
--- /dev/null
+++ b/include/media/stagefright/MediaExtractor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_EXTRACTOR_H_
+
+#define MEDIA_EXTRACTOR_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MediaSource;
+class MetaData;
+
+class MediaExtractor : public RefBase {
+public:
+ static sp<MediaExtractor> Create(
+ const sp<DataSource> &source, const char *mime = NULL);
+
+ virtual size_t countTracks() = 0;
+ virtual sp<MediaSource> getTrack(size_t index) = 0;
+ virtual sp<MetaData> getTrackMetaData(size_t index) = 0;
+
+protected:
+ MediaExtractor() {}
+ virtual ~MediaExtractor() {}
+
+private:
+ MediaExtractor(const MediaExtractor &);
+ MediaExtractor &operator=(const MediaExtractor &);
+};
+
+} // namespace android
+
+#endif // MEDIA_EXTRACTOR_H_
diff --git a/include/media/stagefright/MediaPlayerImpl.h b/include/media/stagefright/MediaPlayerImpl.h
new file mode 100644
index 0000000..53a2088
--- /dev/null
+++ b/include/media/stagefright/MediaPlayerImpl.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_PLAYER_IMPL_H_
+
+#define MEDIA_PLAYER_IMPL_H_
+
+#include <pthread.h>
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class AudioPlayer;
+class IOMXRenderer;
+class ISurface;
+class MediaExtractor;
+class MediaBuffer;
+class MediaSource;
+class MemoryHeapPmem;
+class MetaData;
+class Surface;
+class TimeSource;
+
+class MediaPlayerImpl {
+public:
+ MediaPlayerImpl(const char *uri);
+
+ status_t initCheck() const;
+
+ // Assumes ownership of "fd".
+ MediaPlayerImpl(int fd, int64_t offset, int64_t length);
+
+ ~MediaPlayerImpl();
+
+ void play();
+ void pause();
+ bool isPlaying() const;
+
+ void setSurface(const sp<Surface> &surface);
+ void setISurface(const sp<ISurface> &isurface);
+
+ void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
+
+ int32_t getWidth() const { return mVideoWidth; }
+ int32_t getHeight() const { return mVideoHeight; }
+
+ int64_t getDuration();
+ int64_t getPosition();
+ status_t seekTo(int64_t time);
+
+private:
+ status_t mInitCheck;
+
+ OMXClient mClient;
+
+ sp<MediaExtractor> mExtractor;
+
+ TimeSource *mTimeSource;
+
+ sp<MediaSource> mAudioSource;
+ sp<MediaSource> mAudioDecoder;
+ AudioPlayer *mAudioPlayer;
+
+ sp<MediaSource> mVideoSource;
+ sp<MediaSource> mVideoDecoder;
+ int32_t mVideoWidth, mVideoHeight;
+ int64_t mVideoPosition;
+
+ int64_t mDuration;
+
+ bool mPlaying;
+ bool mPaused;
+
+ int64_t mTimeSourceDeltaUs;
+
+ sp<Surface> mSurface;
+ sp<ISurface> mISurface;
+ sp<IOMXRenderer> mVideoRenderer;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+ Mutex mLock;
+ pthread_t mVideoThread;
+
+ bool mSeeking;
+ int64_t mSeekTimeUs;
+
+ void init();
+
+ static void *VideoWrapper(void *me);
+ void videoEntry();
+
+ void setAudioSource(const sp<MediaSource> &source);
+ void setVideoSource(const sp<MediaSource> &source);
+
+ MediaSource *makeShoutcastSource(const char *path);
+
+ void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
+ void populateISurface();
+ void depopulateISurface();
+ void sendFrameToISurface(MediaBuffer *buffer);
+
+ void stop();
+
+ MediaPlayerImpl(const MediaPlayerImpl &);
+ MediaPlayerImpl &operator=(const MediaPlayerImpl &);
+};
+
+} // namespace android
+
+#endif // MEDIA_PLAYER_IMPL_H_
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
new file mode 100644
index 0000000..d1fa114
--- /dev/null
+++ b/include/media/stagefright/MediaSource.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_SOURCE_H_
+
+#define MEDIA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+struct MediaSource : public RefBase {
+ MediaSource();
+
+ // To be called before any other methods on this object, except
+ // getFormat().
+ virtual status_t start(MetaData *params = NULL) = 0;
+
+ // Any blocking read call returns immediately with a result of NO_INIT.
+ // It is an error to call any methods other than start after this call
+ // returns. Any buffers the object may be holding onto at the time of
+ // the stop() call are released.
+ // Also, it is imperative that any buffers output by this object and
+ // held onto by callers be released before a call to stop() !!!
+ virtual status_t stop() = 0;
+
+ // Returns the format of the data output by this media source.
+ virtual sp<MetaData> getFormat() = 0;
+
+ struct ReadOptions;
+
+ // Returns a new buffer of data. Call blocks until a
+ // buffer is available, an error is encountered of the end of the stream
+ // is reached.
+ // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+
+ // Options that modify read() behaviour. The default is to
+ // a) not request a seek
+ // b) not be late, i.e. lateness_us = 0
+ struct ReadOptions {
+ ReadOptions();
+
+ // Reset everything back to defaults.
+ void reset();
+
+ void setSeekTo(int64_t time_us);
+ void clearSeekTo();
+ bool getSeekTo(int64_t *time_us) const;
+
+ void setLateBy(int64_t lateness_us);
+ int64_t getLateBy() const;
+
+ private:
+ enum Options {
+ kSeekTo_Option = 1,
+ };
+
+ uint32_t mOptions;
+ int64_t mSeekTimeUs;
+ int64_t mLatenessUs;
+ };
+
+protected:
+ virtual ~MediaSource();
+
+private:
+ MediaSource(const MediaSource &);
+ MediaSource &operator=(const MediaSource &);
+};
+
+} // namespace android
+
+#endif // MEDIA_SOURCE_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
new file mode 100644
index 0000000..abb45a9
--- /dev/null
+++ b/include/media/stagefright/MetaData.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef META_DATA_H_
+
+#define META_DATA_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+enum {
+ kKeyMIMEType = 'mime',
+ kKeyWidth = 'widt',
+ kKeyHeight = 'heig',
+ kKeyChannelCount = '#chn',
+ kKeySampleRate = 'srte',
+ kKeyBitRate = 'brte',
+ kKeyESDS = 'esds',
+ kKeyAVCC = 'avcc',
+ kKeyTimeUnits = '#tim',
+ kKeyTimeScale = 'scal',
+ kKeyWantsNALFragments = 'NALf',
+ kKeyIsSyncFrame = 'sync',
+ kKeyDuration = 'dura',
+ kKeyColorFormat = 'colf',
+ kKeyPlatformPrivate = 'priv',
+ kKeyDecoderComponent = 'decC',
+ kKeyBufferID = 'bfID',
+ kKeyMaxInputSize = 'inpS',
+};
+
+enum {
+ kTypeESDS = 'esds',
+ kTypeAVCC = 'avcc',
+};
+
+class MetaData : public RefBase {
+public:
+ MetaData();
+ MetaData(const MetaData &from);
+
+ enum Type {
+ TYPE_NONE = 'none',
+ TYPE_C_STRING = 'cstr',
+ TYPE_INT32 = 'in32',
+ TYPE_FLOAT = 'floa',
+ TYPE_POINTER = 'ptr ',
+ };
+
+ void clear();
+ bool remove(uint32_t key);
+
+ bool setCString(uint32_t key, const char *value);
+ bool setInt32(uint32_t key, int32_t value);
+ bool setFloat(uint32_t key, float value);
+ bool setPointer(uint32_t key, void *value);
+
+ bool findCString(uint32_t key, const char **value);
+ bool findInt32(uint32_t key, int32_t *value);
+ bool findFloat(uint32_t key, float *value);
+ bool findPointer(uint32_t key, void **value);
+
+ bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+ bool findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const;
+
+protected:
+ virtual ~MetaData();
+
+private:
+ struct typed_data {
+ typed_data();
+ ~typed_data();
+
+ typed_data(const MetaData::typed_data &);
+ typed_data &operator=(const MetaData::typed_data &);
+
+ void clear();
+ void setData(uint32_t type, const void *data, size_t size);
+ void getData(uint32_t *type, const void **data, size_t *size) const;
+
+ private:
+ uint32_t mType;
+ size_t mSize;
+
+ union {
+ void *ext_data;
+ float reservoir;
+ } u;
+
+ bool usesReservoir() const {
+ return mSize <= sizeof(u.reservoir);
+ }
+
+ void allocateStorage(size_t size);
+ void freeStorage();
+
+ void *storage() {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+
+ const void *storage() const {
+ return usesReservoir() ? &u.reservoir : u.ext_data;
+ }
+ };
+
+ KeyedVector<uint32_t, typed_data> mItems;
+
+ // MetaData &operator=(const MetaData &);
+};
+
+} // namespace android
+
+#endif // META_DATA_H_
diff --git a/include/media/stagefright/MmapSource.h b/include/media/stagefright/MmapSource.h
new file mode 100644
index 0000000..a8bd57f
--- /dev/null
+++ b/include/media/stagefright/MmapSource.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MMAP_SOURCE_H_
+
+#define MMAP_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class MmapSource : public DataSource {
+public:
+ MmapSource(const char *filename);
+
+ // Assumes ownership of "fd".
+ MmapSource(int fd, int64_t offset, int64_t length);
+
+ virtual ~MmapSource();
+
+ status_t InitCheck() const;
+
+ virtual ssize_t read_at(off_t offset, void *data, size_t size);
+ virtual status_t getSize(off_t *size);
+
+private:
+ int mFd;
+ void *mBase;
+ size_t mSize;
+
+ MmapSource(const MmapSource &);
+ MmapSource &operator=(const MmapSource &);
+};
+
+} // namespace android
+
+#endif // MMAP_SOURCE_H_
+
diff --git a/include/media/stagefright/OMXClient.h b/include/media/stagefright/OMXClient.h
new file mode 100644
index 0000000..2f14d06
--- /dev/null
+++ b/include/media/stagefright/OMXClient.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_CLIENT_H_
+
+#define OMX_CLIENT_H_
+
+#include <media/IOMX.h>
+
+namespace android {
+
+class OMXClient {
+public:
+ OMXClient();
+
+ status_t connect();
+ void disconnect();
+
+ sp<IOMX> interface() {
+ return mOMX;
+ }
+
+private:
+ sp<IOMX> mOMX;
+
+ OMXClient(const OMXClient &);
+ OMXClient &operator=(const OMXClient &);
+};
+
+} // namespace android
+
+#endif // OMX_CLIENT_H_
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
new file mode 100644
index 0000000..ff7e34a
--- /dev/null
+++ b/include/media/stagefright/OMXCodec.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_CODEC_H_
+
+#define OMX_CODEC_H_
+
+#include <media/IOMX.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MemoryDealer;
+struct OMXCodecObserver;
+
+struct OMXCodec : public MediaSource,
+ public MediaBufferObserver {
+ static sp<OMXCodec> Create(
+ const sp<IOMX> &omx,
+ const sp<MetaData> &meta, bool createEncoder,
+ const sp<MediaSource> &source,
+ const char *matchComponentName = NULL);
+
+ static void setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
+ const char *mime);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+ void on_message(const omx_message &msg);
+
+ // from MediaBufferObserver
+ virtual void signalBufferReturned(MediaBuffer *buffer);
+
+protected:
+ virtual ~OMXCodec();
+
+private:
+ enum State {
+ DEAD,
+ LOADED,
+ LOADED_TO_IDLE,
+ IDLE_TO_EXECUTING,
+ EXECUTING,
+ EXECUTING_TO_IDLE,
+ IDLE_TO_LOADED,
+ RECONFIGURING,
+ ERROR
+ };
+
+ enum {
+ kPortIndexInput = 0,
+ kPortIndexOutput = 1
+ };
+
+ enum PortStatus {
+ ENABLED,
+ DISABLING,
+ DISABLED,
+ ENABLING,
+ SHUTTING_DOWN,
+ };
+
+ enum Quirks {
+ kNeedsFlushBeforeDisable = 1,
+ kWantsNALFragments = 2,
+ kRequiresLoadedToIdleAfterAllocation = 4,
+ kRequiresAllocateBufferOnInputPorts = 8,
+ kRequiresFlushCompleteEmulation = 16,
+ kRequiresAllocateBufferOnOutputPorts = 32,
+ kRequiresFlushBeforeShutdown = 64,
+ };
+
+ struct BufferInfo {
+ IOMX::buffer_id mBuffer;
+ bool mOwnedByComponent;
+ sp<IMemory> mMem;
+ MediaBuffer *mMediaBuffer;
+ };
+
+ struct CodecSpecificData {
+ size_t mSize;
+ uint8_t mData[1];
+ };
+
+ sp<IOMX> mOMX;
+ IOMX::node_id mNode;
+ sp<OMXCodecObserver> mObserver;
+ uint32_t mQuirks;
+ bool mIsEncoder;
+ char *mMIME;
+ char *mComponentName;
+ sp<MetaData> mOutputFormat;
+ sp<MediaSource> mSource;
+ Vector<CodecSpecificData *> mCodecSpecificData;
+ size_t mCodecSpecificDataIndex;
+
+ sp<MemoryDealer> mDealer[2];
+
+ State mState;
+ Vector<BufferInfo> mPortBuffers[2];
+ PortStatus mPortStatus[2];
+ bool mInitialBufferSubmit;
+ bool mSignalledEOS;
+ bool mNoMoreOutputData;
+ int64_t mSeekTimeUs;
+
+ Mutex mLock;
+ Condition mAsyncCompletion;
+
+ // A list of indices into mPortStatus[kPortIndexOutput] filled with data.
+ List<size_t> mFilledBuffers;
+ Condition mBufferFilled;
+
+ OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+ bool isEncoder, const char *mime, const char *componentName,
+ const sp<MediaSource> &source);
+
+ void addCodecSpecificData(const void *data, size_t size);
+ void clearCodecSpecificData();
+
+ void setComponentRole();
+
+ void setAMRFormat();
+ void setAMRWBFormat();
+ void setAACFormat(int32_t numChannels, int32_t sampleRate);
+
+ status_t setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat);
+
+ void setVideoInputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height);
+
+ void setVideoOutputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height);
+
+ void setImageOutputFormat(
+ OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height);
+
+ void setJPEGInputFormat(
+ OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize);
+
+ void setMinBufferSize(OMX_U32 portIndex, OMX_U32 size);
+
+ void setRawAudioFormat(
+ OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
+
+ status_t allocateBuffers();
+ status_t allocateBuffersOnPort(OMX_U32 portIndex);
+
+ status_t freeBuffersOnPort(
+ OMX_U32 portIndex, bool onlyThoseWeOwn = false);
+
+ void drainInputBuffer(IOMX::buffer_id buffer);
+ void fillOutputBuffer(IOMX::buffer_id buffer);
+ void drainInputBuffer(BufferInfo *info);
+ void fillOutputBuffer(BufferInfo *info);
+
+ void drainInputBuffers();
+ void fillOutputBuffers();
+
+ // Returns true iff a flush was initiated and a completion event is
+ // upcoming, false otherwise (A flush was not necessary as we own all
+ // the buffers on that port).
+ // This method will ONLY ever return false for a component with quirk
+ // "kRequiresFlushCompleteEmulation".
+ bool flushPortAsync(OMX_U32 portIndex);
+
+ void disablePortAsync(OMX_U32 portIndex);
+ void enablePortAsync(OMX_U32 portIndex);
+
+ static size_t countBuffersWeOwn(const Vector<BufferInfo> &buffers);
+ static bool isIntermediateState(State state);
+
+ void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+ void onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data);
+ void onStateChange(OMX_STATETYPE newState);
+ void onPortSettingsChanged(OMX_U32 portIndex);
+
+ void setState(State newState);
+
+ status_t init();
+ void initOutputFormat(const sp<MetaData> &inputFormat);
+
+ void dumpPortStatus(OMX_U32 portIndex);
+
+ OMXCodec(const OMXCodec &);
+ OMXCodec &operator=(const OMXCodec &);
+};
+
+struct CodecProfileLevel {
+ OMX_U32 mProfile;
+ OMX_U32 mLevel;
+};
+
+struct CodecCapabilities {
+ String8 mComponentName;
+ Vector<CodecProfileLevel> mProfileLevels;
+};
+
+// Return a vector of componentNames with supported profile/level pairs
+// supporting the given mime type, if queryDecoders==true, returns components
+// that decode content of the given type, otherwise returns components
+// that encode content of the given type.
+// profile and level indications only make sense for h.263, mpeg4 and avc
+// video.
+// The profile/level values correspond to
+// OMX_VIDEO_H263PROFILETYPE, OMX_VIDEO_MPEG4PROFILETYPE,
+// OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263LEVELTYPE, OMX_VIDEO_MPEG4LEVELTYPE
+// and OMX_VIDEO_AVCLEVELTYPE respectively.
+
+status_t QueryCodecs(
+ const sp<IOMX> &omx,
+ const char *mimeType, bool queryDecoders,
+ Vector<CodecCapabilities> *results);
+
+} // namespace android
+
+#endif // OMX_CODEC_H_
diff --git a/include/media/stagefright/QComHardwareRenderer.h b/include/media/stagefright/QComHardwareRenderer.h
new file mode 100644
index 0000000..8292dd5
--- /dev/null
+++ b/include/media/stagefright/QComHardwareRenderer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef QCOM_HARDWARE_RENDERER_H_
+
+#define QCOM_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapPmem;
+
+class QComHardwareRenderer : public VideoRenderer {
+public:
+ QComHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~QComHardwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<MemoryHeapPmem> mMemoryHeap;
+
+ bool getOffset(void *platformPrivate, size_t *offset);
+ void publishBuffers(uint32_t pmem_fd);
+
+ QComHardwareRenderer(const QComHardwareRenderer &);
+ QComHardwareRenderer &operator=(const QComHardwareRenderer &);
+};
+
+} // namespace android
+
+#endif // QCOM_HARDWARE_RENDERER_H_
diff --git a/include/media/stagefright/SampleTable.h b/include/media/stagefright/SampleTable.h
new file mode 100644
index 0000000..808d142
--- /dev/null
+++ b/include/media/stagefright/SampleTable.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_TABLE_H_
+
+#define SAMPLE_TABLE_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class DataSource;
+
+class SampleTable : public RefBase {
+public:
+ SampleTable(const sp<DataSource> &source);
+
+ // type can be 'stco' or 'co64'.
+ status_t setChunkOffsetParams(
+ uint32_t type, off_t data_offset, off_t data_size);
+
+ status_t setSampleToChunkParams(off_t data_offset, off_t data_size);
+
+ // type can be 'stsz' or 'stz2'.
+ status_t setSampleSizeParams(
+ uint32_t type, off_t data_offset, off_t data_size);
+
+ status_t setTimeToSampleParams(off_t data_offset, off_t data_size);
+
+ status_t setSyncSampleParams(off_t data_offset, off_t data_size);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ uint32_t countChunkOffsets() const;
+ status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
+
+ status_t getChunkForSample(
+ uint32_t sample_index, uint32_t *chunk_index,
+ uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
+
+ uint32_t countSamples() const;
+ status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
+
+ status_t getSampleOffsetAndSize(
+ uint32_t sample_index, off_t *offset, size_t *size);
+
+ status_t getMaxSampleSize(size_t *size);
+
+ status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+
+ enum {
+ kSyncSample_Flag = 1
+ };
+ status_t findClosestSample(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+
+ status_t findClosestSyncSample(
+ uint32_t start_sample_index, uint32_t *sample_index);
+
+protected:
+ ~SampleTable();
+
+private:
+ sp<DataSource> mDataSource;
+ Mutex mLock;
+
+ off_t mChunkOffsetOffset;
+ uint32_t mChunkOffsetType;
+ uint32_t mNumChunkOffsets;
+
+ off_t mSampleToChunkOffset;
+ uint32_t mNumSampleToChunkOffsets;
+
+ off_t mSampleSizeOffset;
+ uint32_t mSampleSizeFieldSize;
+ uint32_t mDefaultSampleSize;
+ uint32_t mNumSampleSizes;
+
+ uint32_t mTimeToSampleCount;
+ uint32_t *mTimeToSample;
+
+ off_t mSyncSampleOffset;
+ uint32_t mNumSyncSamples;
+
+ SampleTable(const SampleTable &);
+ SampleTable &operator=(const SampleTable &);
+};
+
+} // namespace android
+
+#endif // SAMPLE_TABLE_H_
diff --git a/include/media/stagefright/ShoutcastSource.h b/include/media/stagefright/ShoutcastSource.h
new file mode 100644
index 0000000..352857a
--- /dev/null
+++ b/include/media/stagefright/ShoutcastSource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SHOUTCAST_SOURCE_H_
+
+#define SHOUTCAST_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class HTTPStream;
+class MediaBufferGroup;
+
+class ShoutcastSource : public MediaSource {
+public:
+ // Assumes ownership of "http".
+ ShoutcastSource(HTTPStream *http);
+ virtual ~ShoutcastSource();
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+ HTTPStream *mHttp;
+ size_t mMetaDataOffset;
+ size_t mBytesUntilMetaData;
+
+ MediaBufferGroup *mGroup;
+ bool mStarted;
+
+ ShoutcastSource(const ShoutcastSource &);
+ ShoutcastSource &operator= (const ShoutcastSource &);
+};
+
+} // namespace android
+
+#endif // SHOUTCAST_SOURCE_H_
+
diff --git a/include/media/stagefright/SoftwareRenderer.h b/include/media/stagefright/SoftwareRenderer.h
new file mode 100644
index 0000000..705b914
--- /dev/null
+++ b/include/media/stagefright/SoftwareRenderer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SOFTWARE_RENDERER_H_
+
+#define SOFTWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapBase;
+
+class SoftwareRenderer : public VideoRenderer {
+public:
+ SoftwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~SoftwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<MemoryHeapBase> mMemoryHeap;
+ int mIndex;
+
+ SoftwareRenderer(const SoftwareRenderer &);
+ SoftwareRenderer &operator=(const SoftwareRenderer &);
+};
+
+} // namespace android
+
+#endif // SOFTWARE_RENDERER_H_
diff --git a/include/media/stagefright/TIHardwareRenderer.h b/include/media/stagefright/TIHardwareRenderer.h
new file mode 100644
index 0000000..ef42648
--- /dev/null
+++ b/include/media/stagefright/TIHardwareRenderer.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TI_HARDWARE_RENDERER_H_
+
+#define TI_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class ISurface;
+class Overlay;
+
+class TIHardwareRenderer : public VideoRenderer {
+public:
+ TIHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight);
+
+ virtual ~TIHardwareRenderer();
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate);
+
+private:
+ sp<ISurface> mISurface;
+ size_t mDisplayWidth, mDisplayHeight;
+ size_t mDecodedWidth, mDecodedHeight;
+ size_t mFrameSize;
+ sp<Overlay> mOverlay;
+ Vector<void *> mOverlayAddresses;
+ bool mIsFirstFrame;
+ size_t mIndex;
+
+ TIHardwareRenderer(const TIHardwareRenderer &);
+ TIHardwareRenderer &operator=(const TIHardwareRenderer &);
+};
+
+} // namespace android
+
+#endif // TI_HARDWARE_RENDERER_H_
+
diff --git a/include/media/stagefright/TimeSource.h b/include/media/stagefright/TimeSource.h
new file mode 100644
index 0000000..443673d
--- /dev/null
+++ b/include/media/stagefright/TimeSource.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIME_SOURCE_H_
+
+#define TIME_SOURCE_H_
+
+#include <stdint.h>
+
+namespace android {
+
+class TimeSource {
+public:
+ TimeSource() {}
+ virtual ~TimeSource() {}
+
+ virtual int64_t getRealTimeUs() = 0;
+
+private:
+ TimeSource(const TimeSource &);
+ TimeSource &operator=(const TimeSource &);
+};
+
+class SystemTimeSource : public TimeSource {
+public:
+ SystemTimeSource();
+
+ virtual int64_t getRealTimeUs();
+
+private:
+ static int64_t GetSystemTimeUs();
+
+ int64_t mStartTimeUs;
+};
+
+} // namespace android
+
+#endif // TIME_SOURCE_H_
diff --git a/include/media/stagefright/TimedEventQueue.h b/include/media/stagefright/TimedEventQueue.h
new file mode 100644
index 0000000..a264421
--- /dev/null
+++ b/include/media/stagefright/TimedEventQueue.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TIMED_EVENT_QUEUE_H_
+
+#define TIMED_EVENT_QUEUE_H_
+
+#include <pthread.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct TimedEventQueue {
+
+ struct Event : public RefBase {
+ Event() {}
+ virtual ~Event() {}
+
+ protected:
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
+
+ private:
+ friend class TimedEventQueue;
+
+ Event(const Event &);
+ Event &operator=(const Event &);
+ };
+
+ TimedEventQueue();
+ ~TimedEventQueue();
+
+ // Start executing the event loop.
+ void start();
+
+ // Stop executing the event loop, if flush is false, any pending
+ // events are discarded, otherwise the queue will stop (and this call
+ // return) once all pending events have been handled.
+ void stop(bool flush = false);
+
+ // Posts an event to the front of the queue (after all events that
+ // have previously been posted to the front but before timed events).
+ void postEvent(const sp<Event> &event);
+
+ void postEventToBack(const sp<Event> &event);
+
+ // It is an error to post an event with a negative delay.
+ void postEventWithDelay(const sp<Event> &event, int64_t delay_us);
+
+ // If the event is to be posted at a time that has already passed,
+ // it will fire as soon as possible.
+ void postTimedEvent(const sp<Event> &event, int64_t realtime_us);
+
+ // Returns true iff event is currently in the queue and has been
+ // successfully cancelled. In this case the event will have been
+ // removed from the queue and won't fire.
+ bool cancelEvent(const sp<Event> &event);
+
+ static int64_t getRealTimeUs();
+
+private:
+ struct QueueItem {
+ sp<Event> event;
+ int64_t realtime_us;
+ };
+
+ struct StopEvent : public TimedEventQueue::Event {
+ virtual void fire(TimedEventQueue *queue, int64_t now_us) {
+ queue->mStopped = true;
+ }
+ };
+
+ pthread_t mThread;
+ List<QueueItem> mQueue;
+ Mutex mLock;
+ Condition mQueueNotEmptyCondition;
+ Condition mQueueHeadChangedCondition;
+
+ bool mRunning;
+ bool mStopped;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ TimedEventQueue(const TimedEventQueue &);
+ TimedEventQueue &operator=(const TimedEventQueue &);
+};
+
+} // namespace android
+
+#endif // TIMED_EVENT_QUEUE_H_
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
new file mode 100644
index 0000000..30c7f11
--- /dev/null
+++ b/include/media/stagefright/Utils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UTILS_H_
+
+#define UTILS_H_
+
+#include <stdint.h>
+
+namespace android {
+
+#define FOURCC(c1, c2, c3, c4) \
+ (c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+uint16_t U16_AT(const uint8_t *ptr);
+uint32_t U32_AT(const uint8_t *ptr);
+uint64_t U64_AT(const uint8_t *ptr);
+
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+} // namespace android
+
+#endif // UTILS_H_
diff --git a/include/media/stagefright/VideoRenderer.h b/include/media/stagefright/VideoRenderer.h
new file mode 100644
index 0000000..f80b277
--- /dev/null
+++ b/include/media/stagefright/VideoRenderer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEO_RENDERER_H_
+
+#define VIDEO_RENDERER_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+class VideoRenderer {
+public:
+ virtual ~VideoRenderer() {}
+
+ virtual void render(
+ const void *data, size_t size, void *platformPrivate) = 0;
+
+protected:
+ VideoRenderer() {}
+
+ VideoRenderer(const VideoRenderer &);
+ VideoRenderer &operator=(const VideoRenderer &);
+};
+
+} // namespace android
+
+#endif // VIDEO_RENDERER_H_
diff --git a/include/media/stagefright/string.h b/include/media/stagefright/string.h
new file mode 100644
index 0000000..5dc7116
--- /dev/null
+++ b/include/media/stagefright/string.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef STRING_H_
+
+#define STRING_H_
+
+#include <utils/String8.h>
+
+namespace android {
+
+class string {
+public:
+ typedef size_t size_type;
+ static size_type npos;
+
+ string();
+ string(const char *s);
+ string(const char *s, size_t length);
+ string(const string &from, size_type start, size_type length = npos);
+
+ const char *c_str() const;
+ size_type size() const;
+
+ void clear();
+ void erase(size_type from, size_type length);
+
+ size_type find(char c) const;
+
+ bool operator<(const string &other) const;
+ bool operator==(const string &other) const;
+
+ string &operator+=(char c);
+
+private:
+ String8 mString;
+};
+
+} // namespace android
+
+#endif // STRING_H_
diff --git a/include/private/binder/Static.h b/include/private/binder/Static.h
new file mode 100644
index 0000000..5b0f9fc
--- /dev/null
+++ b/include/private/binder/Static.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <utils/threads.h>
+
+#include <binder/IBinder.h>
+#include <binder/IMemory.h>
+#include <binder/ProcessState.h>
+#include <binder/IPermissionController.h>
+#include <binder/IServiceManager.h>
+
+namespace android {
+
+// For ProcessState.cpp
+extern Mutex gProcessMutex;
+extern sp<ProcessState> gProcess;
+
+// For ServiceManager.cpp
+extern Mutex gDefaultServiceManagerLock;
+extern sp<IServiceManager> gDefaultServiceManager;
+extern sp<IPermissionController> gPermissionController;
+
+} // namespace android
diff --git a/include/private/utils/binder_module.h b/include/private/binder/binder_module.h
index fdf327e..fdf327e 100644
--- a/include/private/utils/binder_module.h
+++ b/include/private/binder/binder_module.h
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 496a739..8e2db20 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -55,17 +55,18 @@ struct audio_track_cblk_t
uint32_t volumeLR;
};
uint32_t sampleRate;
+ // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
+ // 8 bit PCM data: in this case, mCblk->frameSize is based on a sample size of
+ // 16 bit because data is converted to 16 bit before being stored in buffer
+ uint32_t frameSize;
uint8_t channels;
uint8_t flowControlFlag; // underrun (out) or overrrun (in) indication
uint8_t out; // out equals 1 for AudioTrack and 0 for AudioRecord
- uint8_t forceReady;
+ uint8_t forceReady;
uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
uint16_t waitTimeMs; // Cumulated wait time
- // Padding ensuring that data buffer starts on a cache line boundary (32 bytes).
- // See AudioFlinger::TrackBase constructor
- int32_t Padding[1];
- // Cache line boundary
-
+ // Cache line boundary (32 bytes)
+
audio_track_cblk_t();
uint32_t stepUser(uint32_t frameCount);
bool stepServer(uint32_t frameCount);
diff --git a/include/private/opengles/gl_context.h b/include/private/opengles/gl_context.h
index a85f275..67c2dd8 100644
--- a/include/private/opengles/gl_context.h
+++ b/include/private/opengles/gl_context.h
@@ -26,6 +26,8 @@
#endif
#include <private/pixelflinger/ggl_context.h>
+#include <hardware/copybit.h>
+#include <hardware/gralloc.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
@@ -39,7 +41,7 @@ class EGLSurfaceManager;
class EGLBufferObjectManager;
namespace gl {
-
+
struct ogles_context_t;
struct matrixx_t;
struct transform_t;
@@ -96,7 +98,7 @@ struct vec4_t {
struct vertex_t {
enum {
- // these constant matter for our clipping
+ // these constant matter for our clipping
CLIP_L = 0x0001, // clipping flags
CLIP_R = 0x0002,
CLIP_B = 0x0004,
@@ -106,7 +108,7 @@ struct vertex_t {
EYE = 0x0040,
RESERVED = 0x0080,
-
+
USER_CLIP_0 = 0x0100, // user clipping flags
USER_CLIP_1 = 0x0200,
USER_CLIP_2 = 0x0400,
@@ -121,7 +123,7 @@ struct vertex_t {
USER_CLIP_ALL = 0x3F00,
CLIP_ALL = 0x3F3F,
};
-
+
// the fields below are arranged to minimize d-cache usage
// we group together, by cache-line, the fields most likely to be used
@@ -130,7 +132,7 @@ struct vertex_t {
vec4_t eye;
};
vec4_t clip;
-
+
uint32_t flags;
size_t index; // cache tag, and vertex index
GLfixed fog;
@@ -142,7 +144,7 @@ struct vertex_t {
vec4_t color;
vec4_t texture[GGL_TEXTURE_UNIT_COUNT];
uint32_t reserved1[4];
-
+
inline void clear() {
flags = index = locked = mru = 0;
}
@@ -199,7 +201,7 @@ struct array_machine_t {
GLenum indicesType;
buffer_t const* array_buffer;
buffer_t const* element_array_buffer;
-
+
void (*compileElements)(ogles_context_t*, vertex_t*, GLint, GLsizei);
void (*compileElement)(ogles_context_t*, vertex_t*, GLint);
@@ -285,6 +287,7 @@ struct light_t {
vec4_t normalizedObjPosition;
vec4_t spotDir;
vec4_t normalizedSpotDir;
+ vec4_t objViewer;
GLfixed spotExp;
GLfixed spotCutoff;
GLfixed spotCutoffCosine;
@@ -410,7 +413,7 @@ struct transform_t {
matrixx_t matrix;
uint32_t flags;
uint32_t ops;
-
+
union {
struct {
void (*point2)(transform_t const* t, vec4_t*, vec4_t const*);
@@ -509,17 +512,17 @@ struct viewport_t {
GLint x;
GLint y;
GLsizei w;
- GLsizei h;
+ GLsizei h;
struct {
GLint x;
GLint y;
- } surfaceport;
+ } surfaceport;
struct {
GLint x;
GLint y;
GLsizei w;
- GLsizei h;
- } scissor;
+ GLsizei h;
+ } scissor;
};
// ----------------------------------------------------------------------------
@@ -594,6 +597,14 @@ struct prims_t {
void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*);
};
+struct copybits_context_t {
+ // A handle to the blit engine, if it exists, else NULL.
+ copybit_device_t* blitEngine;
+ int32_t minScale;
+ int32_t maxScale;
+ buffer_handle_t drawSurfaceBuffer;
+};
+
struct ogles_context_t {
context_t rasterizer;
array_machine_t arrays __attribute__((aligned(32)));
@@ -617,6 +628,14 @@ struct ogles_context_t {
uint32_t transformTextures : 1;
EGLSurfaceManager* surfaceManager;
EGLBufferObjectManager* bufferObjectManager;
+
+ // copybits is only used if LIBAGL_USE_GRALLOC_COPYBITS is
+ // defined, but it is always present because ogles_context_t is a public
+ // struct that is used by clients of libagl. We want the size and offsets
+ // to stay the same, whether or not LIBAGL_USE_GRALLOC_COPYBITS is defined.
+
+ copybits_context_t copybits;
+
GLenum error;
static inline ogles_context_t* get() {
diff --git a/include/private/ui/LayerState.h b/include/private/ui/LayerState.h
index b6fcd80..f1a2618 100644
--- a/include/private/ui/LayerState.h
+++ b/include/private/ui/LayerState.h
@@ -25,8 +25,6 @@
#include <ui/ISurfaceFlingerClient.h>
#include <ui/Region.h>
-#include <private/ui/SharedState.h>
-
namespace android {
class Parcel;
diff --git a/include/private/ui/RegionHelper.h b/include/private/ui/RegionHelper.h
new file mode 100644
index 0000000..926fddb
--- /dev/null
+++ b/include/private/ui/RegionHelper.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
+#define ANDROID_UI_PRIVATE_REGION_HELPER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+template<typename RECT>
+class region_operator
+{
+ typedef typename RECT::value_type TYPE;
+ static const TYPE max_value = 0x7FFFFFF;
+
+public:
+ /*
+ * Common boolean operations:
+ * value is computed as 0b101 op 0b110
+ * other boolean operation are possible, simply compute
+ * their corresponding value with the above formulae and use
+ * it when instantiating a region_operator.
+ */
+ static const uint32_t LHS = 0x5; // 0b101
+ static const uint32_t RHS = 0x6; // 0b110
+ enum {
+ op_nand = LHS & ~RHS,
+ op_and = LHS & RHS,
+ op_or = LHS | RHS,
+ op_xor = LHS ^ RHS
+ };
+
+ struct region {
+ RECT const* rects;
+ size_t count;
+ TYPE dx;
+ TYPE dy;
+ inline region(const region& rhs)
+ : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
+ inline region(RECT const* r, size_t c)
+ : rects(r), count(c), dx(), dy() { }
+ inline region(RECT const* r, size_t c, TYPE dx, TYPE dy)
+ : rects(r), count(c), dx(dx), dy(dy) { }
+ };
+
+ class region_rasterizer {
+ friend class region_operator;
+ virtual void operator()(const RECT& rect) = 0;
+ public:
+ virtual ~region_rasterizer() { };
+ };
+
+ inline region_operator(int op, const region& lhs, const region& rhs)
+ : op_mask(op), spanner(lhs, rhs)
+ {
+ }
+
+ void operator()(region_rasterizer& rasterizer) {
+ RECT current;
+ do {
+ SpannerInner spannerInner(spanner.lhs, spanner.rhs);
+ int inside = spanner.next(current.top, current.bottom);
+ spannerInner.prepare(inside);
+ do {
+ TYPE left, right;
+ int inside = spannerInner.next(current.left, current.right);
+ if ((op_mask >> inside) & 1) {
+ if (current.left < current.right &&
+ current.top < current.bottom) {
+ rasterizer(current);
+ }
+ }
+ } while(!spannerInner.isDone());
+ } while(!spanner.isDone());
+ }
+
+private:
+ uint32_t op_mask;
+
+ class SpannerBase
+ {
+ public:
+ enum {
+ lhs_before_rhs = 0,
+ lhs_after_rhs = 1,
+ lhs_coincide_rhs = 2
+ };
+
+ protected:
+ TYPE lhs_head;
+ TYPE lhs_tail;
+ TYPE rhs_head;
+ TYPE rhs_tail;
+
+ inline int next(TYPE& head, TYPE& tail,
+ bool& more_lhs, bool& more_rhs)
+ {
+ int inside;
+ more_lhs = false;
+ more_rhs = false;
+ if (lhs_head < rhs_head) {
+ inside = lhs_before_rhs;
+ head = lhs_head;
+ if (lhs_tail <= rhs_head) {
+ tail = lhs_tail;
+ more_lhs = true;
+ } else {
+ lhs_head = rhs_head;
+ tail = rhs_head;
+ }
+ } else if (rhs_head < lhs_head) {
+ inside = lhs_after_rhs;
+ head = rhs_head;
+ if (rhs_tail <= lhs_head) {
+ tail = rhs_tail;
+ more_rhs = true;
+ } else {
+ rhs_head = lhs_head;
+ tail = lhs_head;
+ }
+ } else {
+ inside = lhs_coincide_rhs;
+ head = lhs_head;
+ if (lhs_tail <= rhs_tail) {
+ tail = rhs_head = lhs_tail;
+ more_lhs = true;
+ }
+ if (rhs_tail <= lhs_tail) {
+ tail = lhs_head = rhs_tail;
+ more_rhs = true;
+ }
+ }
+ return inside;
+ }
+ };
+
+ class Spanner : protected SpannerBase
+ {
+ friend class region_operator;
+ region lhs;
+ region rhs;
+
+ public:
+ inline Spanner(const region& lhs, const region& rhs)
+ : lhs(lhs), rhs(rhs)
+ {
+ SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+ SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
+ SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+ SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
+ }
+
+ inline bool isDone() const {
+ return !rhs.count && !lhs.count;
+ }
+
+ inline int next(TYPE& top, TYPE& bottom)
+ {
+ bool more_lhs = false;
+ bool more_rhs = false;
+ int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
+ if (more_lhs) {
+ advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail);
+ }
+ if (more_rhs) {
+ advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail);
+ }
+ return inside;
+ }
+
+ private:
+ static inline
+ void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+ // got to next span
+ size_t count = reg.count;
+ RECT const * rects = reg.rects;
+ RECT const * const end = rects + count;
+ const int top = rects->top;
+ while (rects != end && rects->top == top) {
+ rects++;
+ count--;
+ }
+ if (rects != end) {
+ aTop = rects->top + reg.dy;
+ aBottom = rects->bottom + reg.dy;
+ } else {
+ aTop = max_value;
+ aBottom = max_value;
+ }
+ reg.rects = rects;
+ reg.count = count;
+ }
+ };
+
+ class SpannerInner : protected SpannerBase
+ {
+ region lhs;
+ region rhs;
+
+ public:
+ inline SpannerInner(const region& lhs, const region& rhs)
+ : lhs(lhs), rhs(rhs)
+ {
+ }
+
+ inline void prepare(int inside) {
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
+ if (inside == SpannerBase::lhs_before_rhs) {
+ SpannerBase::rhs_head = max_value;
+ SpannerBase::rhs_tail = max_value;
+ } else if (inside == SpannerBase::lhs_after_rhs) {
+ SpannerBase::lhs_head = max_value;
+ SpannerBase::lhs_tail = max_value;
+ } else {
+ // use both spans
+ }
+ }
+
+ inline bool isDone() const {
+ return SpannerBase::lhs_head == max_value &&
+ SpannerBase::rhs_head == max_value;
+ }
+
+ inline int next(TYPE& left, TYPE& right)
+ {
+ bool more_lhs = false;
+ bool more_rhs = false;
+ int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
+ if (more_lhs) {
+ advance(lhs, SpannerBase::lhs_head, SpannerBase::lhs_tail);
+ }
+ if (more_rhs) {
+ advance(rhs, SpannerBase::rhs_head, SpannerBase::rhs_tail);
+ }
+ return inside;
+ }
+
+ private:
+ static inline
+ void advance(region& reg, TYPE& left, TYPE& right) {
+ if (reg.rects && reg.count) {
+ const int cur_span_top = reg.rects->top;
+ reg.rects++;
+ reg.count--;
+ if (!reg.count || reg.rects->top != cur_span_top) {
+ left = max_value;
+ right = max_value;
+ } else {
+ left = reg.rects->left + reg.dx;
+ right = reg.rects->right + reg.dx;
+ }
+ }
+ }
+ };
+
+ Spanner spanner;
+};
+
+// ----------------------------------------------------------------------------
+};
+
+#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/include/private/ui/SharedBufferStack.h b/include/private/ui/SharedBufferStack.h
new file mode 100644
index 0000000..2bd5344
--- /dev/null
+++ b/include/private/ui/SharedBufferStack.h
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_SHARED_BUFFER_STACK_H
+#define ANDROID_UI_SHARED_BUFFER_STACK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <cutils/compiler.h>
+
+#include <utils/Debug.h>
+#include <utils/threads.h>
+#include <utils/String8.h>
+
+#include <ui/Rect.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+/*
+ * These classes manage a stack of buffers in shared memory.
+ *
+ * SharedClient: represents a client with several stacks
+ * SharedBufferStack: represents a stack of buffers
+ * SharedBufferClient: manipulates the SharedBufferStack from the client side
+ * SharedBufferServer: manipulates the SharedBufferStack from the server side
+ *
+ * Buffers can be dequeued until there are none available, they can be locked
+ * unless they are in use by the server, which is only the case for the last
+ * dequeue-able buffer. When these various conditions are not met, the caller
+ * waits until the condition is met.
+ *
+ *
+ * CAVEATS:
+ *
+ * In the current implementation there are several limitations:
+ * - buffers must be locked in the same order they've been dequeued
+ * - buffers must be enqueued in the same order they've been locked
+ * - dequeue() is not reentrant
+ * - no error checks are done on the condition above
+ *
+ */
+
+// When changing these values, the COMPILE_TIME_ASSERT at the end of this
+// file need to be updated.
+const unsigned int NUM_LAYERS_MAX = 31;
+const unsigned int NUM_BUFFER_MAX = 4;
+const unsigned int NUM_DISPLAY_MAX = 4;
+
+// ----------------------------------------------------------------------------
+
+class Region;
+class SharedBufferStack;
+class SharedClient;
+
+// ----------------------------------------------------------------------------
+
+struct FlatRegion { // 12 bytes
+ static const unsigned int NUM_RECT_MAX = 1;
+ uint32_t count;
+ uint16_t rects[4*NUM_RECT_MAX];
+};
+
+// should be 128 bytes (32 longs)
+class SharedBufferStack
+{
+ friend class SharedClient;
+ friend class SharedBufferBase;
+ friend class SharedBufferClient;
+ friend class SharedBufferServer;
+
+public:
+ SharedBufferStack();
+ status_t setDirtyRegion(int buffer, const Region& reg);
+ Region getDirtyRegion(int buffer) const;
+
+ // these attributes are part of the conditions/updates
+ volatile int32_t head; // server's current front buffer
+ volatile int32_t available; // number of dequeue-able buffers
+ volatile int32_t queued; // number of buffers waiting for post
+ volatile int32_t inUse; // buffer currently in use by SF
+
+ // not part of the conditions
+ volatile int32_t reallocMask;
+
+ int32_t identity; // surface's identity (const)
+ status_t status; // surface's status code
+ int32_t reserved32[13];
+ FlatRegion dirtyRegion[NUM_BUFFER_MAX]; // 12*4=48 bytes
+};
+
+// ----------------------------------------------------------------------------
+
+// 4 KB max
+class SharedClient
+{
+public:
+ SharedClient();
+ ~SharedClient();
+
+ status_t validate(size_t token) const;
+ uint32_t getIdentity(size_t token) const;
+ status_t setIdentity(size_t token, uint32_t identity);
+
+private:
+ friend class SharedBufferBase;
+ friend class SharedBufferClient;
+ friend class SharedBufferServer;
+
+ // FIXME: this should be replaced by a lock-less primitive
+ Mutex lock;
+ Condition cv;
+ SharedBufferStack surfaces[ NUM_LAYERS_MAX ];
+};
+
+// ============================================================================
+
+class SharedBufferBase
+{
+public:
+ SharedBufferBase(SharedClient* sharedClient, int surface, int num);
+ ~SharedBufferBase();
+ uint32_t getIdentity();
+ size_t getFrontBuffer() const;
+ String8 dump(char const* prefix) const;
+
+protected:
+ SharedClient* const mSharedClient;
+ SharedBufferStack* const mSharedStack;
+ const int mNumBuffers;
+
+ friend struct Update;
+ friend struct QueueUpdate;
+
+ struct ConditionBase {
+ SharedBufferStack& stack;
+ inline ConditionBase(SharedBufferBase* sbc)
+ : stack(*sbc->mSharedStack) { }
+ };
+
+ struct UpdateBase {
+ SharedBufferStack& stack;
+ inline UpdateBase(SharedBufferBase* sbb)
+ : stack(*sbb->mSharedStack) { }
+ };
+
+ template <typename T>
+ status_t waitForCondition(T condition);
+
+ template <typename T>
+ status_t updateCondition(T update);
+};
+
+template <typename T>
+status_t SharedBufferBase::waitForCondition(T condition)
+{
+ SharedClient& client( *mSharedClient );
+ const nsecs_t TIMEOUT = s2ns(1);
+ Mutex::Autolock _l(client.lock);
+ while (!condition()) {
+ status_t err = client.cv.waitRelative(client.lock, TIMEOUT);
+
+ // handle errors and timeouts
+ if (CC_UNLIKELY(err != NO_ERROR)) {
+ if (err == TIMED_OUT) {
+ if (condition()) {
+ LOGE("waitForCondition(%s) timed out (identity=%d), "
+ "but condition is true! We recovered but it "
+ "shouldn't happen." ,
+ T::name(), mSharedStack->identity);
+ break;
+ } else {
+ LOGW("waitForCondition(%s) timed out (identity=%d). "
+ "CPU may be pegged. trying again.",
+ T::name(), mSharedStack->identity);
+ }
+ } else {
+ LOGE("waitForCondition(%s) error (%s) ",
+ T::name(), strerror(-err));
+ return err;
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+
+template <typename T>
+status_t SharedBufferBase::updateCondition(T update) {
+ SharedClient& client( *mSharedClient );
+ Mutex::Autolock _l(client.lock);
+ ssize_t result = update();
+ client.cv.broadcast();
+ return result;
+}
+
+// ----------------------------------------------------------------------------
+
+class SharedBufferClient : public SharedBufferBase
+{
+public:
+ SharedBufferClient(SharedClient* sharedClient, int surface, int num);
+
+ ssize_t dequeue();
+ status_t undoDequeue(int buf);
+
+ status_t lock(int buf);
+ status_t queue(int buf);
+ bool needNewBuffer(int buffer) const;
+ status_t setDirtyRegion(int buffer, const Region& reg);
+
+private:
+ friend struct Condition;
+ friend struct DequeueCondition;
+ friend struct LockCondition;
+
+ struct QueueUpdate : public UpdateBase {
+ inline QueueUpdate(SharedBufferBase* sbb);
+ inline ssize_t operator()();
+ };
+
+ struct UndoDequeueUpdate : public UpdateBase {
+ inline UndoDequeueUpdate(SharedBufferBase* sbb);
+ inline ssize_t operator()();
+ };
+
+ // --
+
+ struct DequeueCondition : public ConditionBase {
+ inline DequeueCondition(SharedBufferClient* sbc);
+ inline bool operator()();
+ static inline const char* name() { return "DequeueCondition"; }
+ };
+
+ struct LockCondition : public ConditionBase {
+ int buf;
+ inline LockCondition(SharedBufferClient* sbc, int buf);
+ inline bool operator()();
+ static inline const char* name() { return "LockCondition"; }
+ };
+
+ int32_t tail;
+};
+
+// ----------------------------------------------------------------------------
+
+class SharedBufferServer : public SharedBufferBase
+{
+public:
+ SharedBufferServer(SharedClient* sharedClient, int surface, int num);
+
+ ssize_t retireAndLock();
+ status_t unlock(int buffer);
+ status_t reallocate();
+ status_t assertReallocate(int buffer);
+
+ Region getDirtyRegion(int buffer) const;
+
+private:
+ struct UnlockUpdate : public UpdateBase {
+ const int lockedBuffer;
+ inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer);
+ inline ssize_t operator()();
+ };
+
+ struct RetireUpdate : public UpdateBase {
+ const int numBuffers;
+ inline RetireUpdate(SharedBufferBase* sbb, int numBuffers);
+ inline ssize_t operator()();
+ };
+
+ struct ReallocateCondition : public ConditionBase {
+ int buf;
+ inline ReallocateCondition(SharedBufferBase* sbb, int buf);
+ inline bool operator()();
+ static inline const char* name() { return "ReallocateCondition"; }
+ };
+};
+
+// ===========================================================================
+
+struct display_cblk_t
+{
+ uint16_t w;
+ uint16_t h;
+ uint8_t format;
+ uint8_t orientation;
+ uint8_t reserved[2];
+ float fps;
+ float density;
+ float xdpi;
+ float ydpi;
+ uint32_t pad[2];
+};
+
+struct surface_flinger_cblk_t // 4KB max
+{
+ uint8_t connected;
+ uint8_t reserved[3];
+ uint32_t pad[7];
+ display_cblk_t displays[NUM_DISPLAY_MAX];
+};
+
+// ---------------------------------------------------------------------------
+
+COMPILE_TIME_ASSERT(sizeof(SharedClient) <= 4096)
+COMPILE_TIME_ASSERT(sizeof(SharedBufferStack) == 128)
+COMPILE_TIME_ASSERT(sizeof(surface_flinger_cblk_t) <= 4096)
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif /* ANDROID_UI_SHARED_BUFFER_STACK_H */
diff --git a/include/private/ui/SharedState.h b/include/private/ui/SharedState.h
deleted file mode 100644
index 546d0ad..0000000
--- a/include/private/ui/SharedState.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_UI_SHARED_STATE_H
-#define ANDROID_UI_SHARED_STATE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/threads.h>
-
-namespace android {
-
-/*
- * These structures are shared between the composer process and its clients
- */
-
-// ---------------------------------------------------------------------------
-
-struct surface_info_t { // 4 longs, 16 bytes
- enum {
- eBufferDirty = 0x01
- };
- uint16_t w;
- uint16_t h;
- uint16_t stride;
- uint16_t bpr;
- uint16_t reserved;
- uint8_t format;
- uint8_t flags;
- ssize_t bits_offset;
-};
-
-// ---------------------------------------------------------------------------
-
-const uint32_t NUM_LAYERS_MAX = 31;
-
-enum { // layer_cblk_t swapState
- eIndex = 0x00000001,
- eFlipRequested = 0x00000002,
-
- eResizeBuffer0 = 0x00000004,
- eResizeBuffer1 = 0x00000008,
- eResizeRequested = eResizeBuffer0 | eResizeBuffer1,
-
- eBusy = 0x00000010,
- eLocked = 0x00000020,
- eNextFlipPending = 0x00000040,
- eInvalidSurface = 0x00000080
-};
-
-enum { // layer_cblk_t flags
- eLayerNotPosted = 0x00000001,
- eNoCopyBack = 0x00000002,
- eReserved = 0x0000007C,
- eBufferIndexShift = 7,
- eBufferIndex = 1<<eBufferIndexShift,
-};
-
-struct flat_region_t // 40 bytes
-{
- int32_t count;
- int16_t l;
- int16_t t;
- int16_t r;
- int16_t b;
- uint16_t runs[14];
-};
-
-struct layer_cblk_t // (128 bytes)
-{
- volatile int32_t swapState; // 4
- volatile int32_t flags; // 4
- volatile int32_t identity; // 4
- int32_t reserved; // 4
- surface_info_t surface[2]; // 32
- flat_region_t region[2]; // 80
-
- static inline int backBuffer(uint32_t state) {
- return ((state & eIndex) ^ ((state & eFlipRequested)>>1));
- }
- static inline int frontBuffer(uint32_t state) {
- return 1 - backBuffer(state);
- }
-};
-
-// ---------------------------------------------------------------------------
-
-struct per_client_cblk_t // 4KB max
-{
- Mutex lock;
- Condition cv;
- layer_cblk_t layers[NUM_LAYERS_MAX] __attribute__((aligned(32)));
-
- enum {
- BLOCKING = 0x00000001,
- INSPECT = 0x00000002
- };
-
- per_client_cblk_t();
-
- // these functions are used by the clients
- status_t validate(size_t i) const;
- int32_t lock_layer(size_t i, uint32_t flags);
- uint32_t unlock_layer_and_post(size_t i);
- void unlock_layer(size_t i);
-};
-// ---------------------------------------------------------------------------
-
-const uint32_t NUM_DISPLAY_MAX = 4;
-
-struct display_cblk_t
-{
- uint16_t w;
- uint16_t h;
- uint8_t format;
- uint8_t orientation;
- uint8_t reserved[2];
- float fps;
- float density;
- float xdpi;
- float ydpi;
- uint32_t pad[2];
-};
-
-struct surface_flinger_cblk_t // 4KB max
-{
- surface_flinger_cblk_t();
-
- uint8_t connected;
- uint8_t reserved[3];
- uint32_t pad[7];
-
- display_cblk_t displays[NUM_DISPLAY_MAX];
-};
-
-// ---------------------------------------------------------------------------
-
-template<bool> struct CTA;
-template<> struct CTA<true> { };
-
-// compile-time assertions. just to avoid catastrophes.
-inline void compile_time_asserts() {
- CTA<sizeof(layer_cblk_t) == 128> sizeof__layer_cblk_t__eq_128;
- (void)sizeof__layer_cblk_t__eq_128; // we don't want a warning
- CTA<sizeof(per_client_cblk_t) <= 4096> sizeof__per_client_cblk_t__le_4096;
- (void)sizeof__per_client_cblk_t__le_4096; // we don't want a warning
- CTA<sizeof(surface_flinger_cblk_t) <= 4096> sizeof__surface_flinger_cblk_t__le_4096;
- (void)sizeof__surface_flinger_cblk_t__le_4096; // we don't want a warning
-}
-
-}; // namespace android
-
-#endif // ANDROID_UI_SHARED_STATE_H
-
diff --git a/include/private/ui/SurfaceBuffer.h b/include/private/ui/SurfaceBuffer.h
new file mode 100644
index 0000000..73e517b
--- /dev/null
+++ b/include/private/ui/SurfaceBuffer.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_PRIVATE_SURFACE_BUFFER_H
+#define ANDROID_UI_PRIVATE_SURFACE_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+
+#include <private/ui/android_natives_priv.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class BufferMapper;
+class Parcel;
+class Rect;
+class Surface;
+class SurfaceBuffer;
+
+// ---------------------------------------------------------------------------
+
+class SurfaceBuffer
+ : public EGLNativeBase<
+ android_native_buffer_t,
+ SurfaceBuffer,
+ LightRefBase<SurfaceBuffer> >
+{
+public:
+ status_t lock(uint32_t usage, void** vaddr);
+ status_t lock(uint32_t usage, const Rect& rect, void** vaddr);
+ status_t unlock();
+
+ void setIndex(int index);
+ int getIndex() const;
+
+protected:
+ SurfaceBuffer();
+ SurfaceBuffer(const Parcel& reply);
+ virtual ~SurfaceBuffer();
+ bool mOwner;
+
+ inline const BufferMapper& getBufferMapper() const { return mBufferMapper; }
+ inline BufferMapper& getBufferMapper() { return mBufferMapper; }
+
+private:
+ friend class Surface;
+ friend class BpSurface;
+ friend class BnSurface;
+ friend class LightRefBase<SurfaceBuffer>;
+
+ SurfaceBuffer& operator = (const SurfaceBuffer& rhs);
+ const SurfaceBuffer& operator = (const SurfaceBuffer& rhs) const;
+
+ static status_t writeToParcel(Parcel* reply,
+ android_native_buffer_t const* buffer);
+
+ BufferMapper& mBufferMapper;
+ int mIndex;
+};
+
+}; // namespace android
+
+#endif // ANDROID_UI_PRIVATE_SURFACE_BUFFER_H
+
diff --git a/include/private/ui/SurfaceFlingerSynchro.h b/include/private/ui/SurfaceFlingerSynchro.h
index ff91b61..7386d33 100644
--- a/include/private/ui/SurfaceFlingerSynchro.h
+++ b/include/private/ui/SurfaceFlingerSynchro.h
@@ -21,7 +21,6 @@
#include <stdint.h>
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/threads.h>
#include <ui/ISurfaceComposer.h>
namespace android {
@@ -31,7 +30,6 @@ class SurfaceFlinger;
class SurfaceFlingerSynchro
{
public:
-
// client constructor
SurfaceFlingerSynchro(const sp<ISurfaceComposer>& flinger);
~SurfaceFlingerSynchro();
@@ -40,34 +38,8 @@ public:
status_t signal();
private:
- class Barrier {
- public:
- Barrier();
- ~Barrier();
- void open();
- void close();
- void waitAndClose();
- status_t waitAndClose(nsecs_t timeout);
- private:
- enum { OPENED, CLOSED };
- mutable Mutex lock;
- mutable Condition cv;
- volatile int state;
- };
-
friend class SurfaceFlinger;
-
- // server constructor
- SurfaceFlingerSynchro();
-
- void open();
-
- // wait until there is some work to do
- status_t wait();
- status_t wait(nsecs_t timeout);
-
sp<ISurfaceComposer> mSurfaceComposer;
- Barrier mBarrier;
};
}; // namespace android
diff --git a/include/private/ui/android_natives_priv.h b/include/private/ui/android_natives_priv.h
new file mode 100644
index 0000000..9c92af8
--- /dev/null
+++ b/include/private/ui/android_natives_priv.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ANDROID_NATIVES_PRIV_H
+#define ANDROID_ANDROID_NATIVES_PRIV_H
+
+#include <ui/egl/android_natives.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+typedef struct android_native_buffer_t
+{
+#ifdef __cplusplus
+ android_native_buffer_t() {
+ common.magic = ANDROID_NATIVE_BUFFER_MAGIC;
+ common.version = sizeof(android_native_buffer_t);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ int width;
+ int height;
+ int stride;
+ int format;
+ int usage;
+
+ void* reserved[2];
+
+ buffer_handle_t handle;
+
+ void* reserved_proc[8];
+} android_native_buffer_t;
+
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+/*****************************************************************************/
+
+#endif /* ANDROID_ANDROID_NATIVES_PRIV_H */
diff --git a/include/private/utils/Static.h b/include/private/utils/Static.h
index f1439b7..d95ae0d 100644
--- a/include/private/utils/Static.h
+++ b/include/private/utils/Static.h
@@ -20,14 +20,6 @@
#include <utils/threads.h>
#include <utils/KeyedVector.h>
-#ifndef LIBUTILS_NATIVE
-#include <utils/IBinder.h>
-#include <utils/IMemory.h>
-#include <utils/ProcessState.h>
-#include <utils/IPermissionController.h>
-#include <utils/IServiceManager.h>
-#endif
-
namespace android {
// For TextStream.cpp
extern Vector<int32_t> gTextBuffers;
@@ -40,19 +32,4 @@ extern void terminate_string8();
extern void initialize_string16();
extern void terminate_string16();
-
-
-#ifndef LIBUTILS_NATIVE
-
-// For ProcessState.cpp
-extern Mutex gProcessMutex;
-extern sp<ProcessState> gProcess;
-
-// For ServiceManager.cpp
-extern Mutex gDefaultServiceManagerLock;
-extern sp<IServiceManager> gDefaultServiceManager;
-extern sp<IPermissionController> gPermissionController;
-
-#endif
-
} // namespace android
diff --git a/include/private/utils/futex_synchro.h b/include/private/utils/futex_synchro.h
deleted file mode 100644
index ac2ab19..0000000
--- a/include/private/utils/futex_synchro.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _FUTEX_SYNCHRO_H
-#define _FUTEX_SYNCHRO_H
-
-#ifndef HAVE_FUTEX
-#error "HAVE_FUTEX not defined"
-#endif
-
-#define FUTEX_WAIT_INFINITE (0)
-
-typedef struct futex_mutex_t futex_mutex_t;
-
-struct futex_mutex_t
-{
- volatile int value;
-};
-
-typedef struct futex_cond_t futex_cond_t;
-
-struct futex_cond_t
-{
- volatile int value;
-};
-
-
-#if __cplusplus
-extern "C" {
-#endif
-
-void futex_mutex_init(futex_mutex_t *m);
-int futex_mutex_lock(futex_mutex_t *m, unsigned msec);
-void futex_mutex_unlock(futex_mutex_t *m);
-int futex_mutex_trylock(futex_mutex_t *m);
-
-void futex_cond_init(futex_cond_t *c);
-int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec);
-void futex_cond_signal(futex_cond_t *c);
-void futex_cond_broadcast(futex_cond_t *c);
-
-#if __cplusplus
-} // extern "C"
-#endif
-
-#endif // _FUTEX_SYNCHRO_H
-
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
index 21cb73b..28b0d2f 100644
--- a/include/tts/TtsEngine.h
+++ b/include/tts/TtsEngine.h
@@ -43,7 +43,7 @@ enum tts_callback_status {
// @param [inout] void *& - The userdata pointer set in the original
// synth call
// @param [in] uint32_t - Track sampling rate in Hz
-// @param [in] audio_format - The AudioSystem::audio_format enum
+// @param [in] uint32_t - The audio format
// @param [in] int - The number of channels
// @param [inout] int8_t *& - A buffer of audio data only valid during the
// execution of the callback
@@ -54,7 +54,7 @@ enum tts_callback_status {
// TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
// there is more data to produce.
typedef tts_callback_status (synthDoneCB_t)(void *&, uint32_t,
- AudioSystem::audio_format, int, int8_t *&, size_t&, tts_synth_status);
+ uint32_t, int, int8_t *&, size_t&, tts_synth_status);
class TtsEngine;
extern "C" TtsEngine* getTtsEngine();
@@ -80,6 +80,8 @@ enum tts_support_result {
class TtsEngine
{
public:
+ virtual ~TtsEngine() {}
+
// Initialize the TTS engine and returns whether initialization succeeded.
// @param synthDoneCBPtr synthesis callback function pointer
// @return TTS_SUCCESS, or TTS_FAILURE
diff --git a/include/ui/BufferMapper.h b/include/ui/BufferMapper.h
new file mode 100644
index 0000000..5f084be
--- /dev/null
+++ b/include/ui/BufferMapper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UI_BUFFER_MAPPER_H
+#define ANDROID_UI_BUFFER_MAPPER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Singleton.h>
+
+#include <hardware/gralloc.h>
+
+
+struct gralloc_module_t;
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class Rect;
+
+class BufferMapper : public Singleton<BufferMapper>
+{
+public:
+ static inline BufferMapper& get() { return getInstance(); }
+
+ status_t registerBuffer(buffer_handle_t handle);
+
+ status_t unregisterBuffer(buffer_handle_t handle);
+
+ status_t lock(buffer_handle_t handle,
+ int usage, const Rect& bounds, void** vaddr);
+
+ status_t unlock(buffer_handle_t handle);
+
+ // dumps information about the mapping of this handle
+ void dump(buffer_handle_t handle);
+
+private:
+ friend class Singleton<BufferMapper>;
+ BufferMapper();
+ gralloc_module_t const *mAllocMod;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_UI_BUFFER_MAPPER_H
+
diff --git a/include/ui/Camera.h b/include/ui/Camera.h
index afb07b5..ae6e255 100644
--- a/include/ui/Camera.h
+++ b/include/ui/Camera.h
@@ -66,15 +66,16 @@ namespace android {
// msgType in notifyCallback and dataCallback functions
enum {
- CAMERA_MSG_ERROR = 0,
- CAMERA_MSG_SHUTTER,
- CAMERA_MSG_FOCUS,
- CAMERA_MSG_ZOOM,
- CAMERA_MSG_PREVIEW_FRAME,
- CAMERA_MSG_VIDEO_FRAME,
- CAMERA_MSG_POSTVIEW_FRAME,
- CAMERA_MSG_RAW_IMAGE,
- CAMERA_MSG_COMPRESSED_IMAGE
+ CAMERA_MSG_ERROR = 0x001,
+ CAMERA_MSG_SHUTTER = 0x002,
+ CAMERA_MSG_FOCUS = 0x004,
+ CAMERA_MSG_ZOOM = 0x008,
+ CAMERA_MSG_PREVIEW_FRAME = 0x010,
+ CAMERA_MSG_VIDEO_FRAME = 0x020,
+ CAMERA_MSG_POSTVIEW_FRAME = 0x040,
+ CAMERA_MSG_RAW_IMAGE = 0x080,
+ CAMERA_MSG_COMPRESSED_IMAGE = 0x100,
+ CAMERA_MSG_ALL_MSGS = 0x1FF
};
// camera fatal errors
diff --git a/include/ui/CameraHardwareInterface.h b/include/ui/CameraHardwareInterface.h
index 822b4a8..535f70e 100644
--- a/include/ui/CameraHardwareInterface.h
+++ b/include/ui/CameraHardwareInterface.h
@@ -17,30 +17,28 @@
#ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
#define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <utils/RefBase.h>
+#include <ui/ISurface.h>
+#include <ui/Camera.h>
#include <ui/CameraParameters.h>
#include <ui/Overlay.h>
namespace android {
-/** Callback for startPreview() */
-typedef void (*preview_callback)(const sp<IMemory>& mem, void* user);
+typedef void (*notify_callback)(int32_t msgType,
+ int32_t ext1,
+ int32_t ext2,
+ void* user);
-/** Callback for startRecord() */
-typedef void (*recording_callback)(nsecs_t timestamp, const sp<IMemory>& mem, void* user);
+typedef void (*data_callback)(int32_t msgType,
+ const sp<IMemory>& dataPtr,
+ void* user);
-/** Callback for takePicture() */
-typedef void (*shutter_callback)(void* user);
-
-/** Callback for takePicture() */
-typedef void (*raw_callback)(const sp<IMemory>& mem, void* user);
-
-/** Callback for takePicture() */
-typedef void (*jpeg_callback)(const sp<IMemory>& mem, void* user);
-
-/** Callback for autoFocus() */
-typedef void (*autofocus_callback)(bool focused, void* user);
+typedef void (*data_callback_timestamp)(nsecs_t timestamp,
+ int32_t msgType,
+ const sp<IMemory>& dataPtr,
+ void* user);
/**
* CameraHardwareInterface.h defines the interface to the
@@ -57,28 +55,21 @@ typedef void (*autofocus_callback)(bool focused, void* user);
* CameraService calls getPreviewHeap() to establish access to the
* preview heap so it can be registered with SurfaceFlinger for
* efficient display updating while in preview mode.
- * -# startPreview() is called, which is passed a preview_callback()
- * function and a user parameter. The camera instance then periodically
- * calls preview_callback() each time a new preview frame is available.
- * The callback routine has two parameters: the first is a pointer to
- * the IMemory containing the frame and the second a user parameter. If
- * the preview_callback code needs to use this memory after returning,
- * it must copy the data.
+ * -# startPreview() is called. The camera instance then periodically
+ * sends the message CAMERA_MSG_PREVIEW_FRAME (if enabled) each time
+ * a new preview frame is available. If data callback code needs to use
+ * this memory after returning, it must copy the data.
*
- * Prior to taking a picture, CameraService calls autofocus() with
- * autofocus_callback() and a user parameter. When auto focusing has
- * completed, the camera instance calls autofocus_callback(), which informs
- * the application whether focusing was successful. The camera instance
- * only calls autofocus_callback() once and it is up to the application to
- * call autoFocus() again if refocusing is desired.
+ * Prior to taking a picture, CameraService calls autofocus(). When auto
+ * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification,
+ * which informs the application whether focusing was successful. The camera instance
+ * only sends this message once and it is up to the application to call autoFocus()
+ * again if refocusing is desired.
*
* CameraService calls takePicture() to request the camera instance take a
- * picture. This method has two callbacks: raw_callback() and jpeg_callback().
- * When the raw image is available, raw_callback() is called with a pointer
- * to the IMemory containing the raw image. When the jpeg image is available,
- * jpeg_callback() is called with a pointer to the IMemory containing the
- * jpeg image. As with preview_callback(), the memory must be copied if it's
- * needed after returning.
+ * picture. At this point, if a shutter, postview, raw, and/or compressed callback
+ * is desired, the corresponding message must be enabled. As with CAMERA_MSG_PREVIEW_FRAME,
+ * any memory provided in a data callback must be copied if it's needed after returning.
*/
class CameraHardwareInterface : public virtual RefBase {
public:
@@ -90,17 +81,45 @@ public:
/** Return the IMemoryHeap for the raw image heap */
virtual sp<IMemoryHeap> getRawHeap() const = 0;
+ /** Set the notification and data callbacks */
+ virtual void setCallbacks(notify_callback notify_cb,
+ data_callback data_cb,
+ data_callback_timestamp data_cb_timestamp,
+ void* user) = 0;
+
/**
- * Start preview mode. When a preview image is available
- * preview_callback is called with the user parameter. The
- * call back parameter may be null.
+ * The following three functions all take a msgtype,
+ * which is a bitmask of the messages defined in
+ * include/ui/Camera.h
*/
- virtual status_t startPreview(preview_callback cb, void* user) = 0;
+
+ /**
+ * Enable a message, or set of messages.
+ */
+ virtual void enableMsgType(int32_t msgType) = 0;
+
+ /**
+ * Disable a message, or a set of messages.
+ */
+ virtual void disableMsgType(int32_t msgType) = 0;
+
+ /**
+ * Query whether a message, or a set of messages, is enabled.
+ * Note that this is operates as an AND, if any of the messages
+ * queried are off, this will return false.
+ */
+ virtual bool msgTypeEnabled(int32_t msgType) = 0;
+
+ /**
+ * Start preview mode.
+ */
+ virtual status_t startPreview() = 0;
+
/**
* Only used if overlays are used for camera preview.
*/
- virtual bool useOverlay() {return false;}
- virtual status_t setOverlay(const sp<Overlay> &overlay) {return BAD_VALUE;}
+ virtual bool useOverlay() {return false;}
+ virtual status_t setOverlay(const sp<Overlay> &overlay) {return BAD_VALUE;}
/**
* Stop a previously started preview.
@@ -113,11 +132,11 @@ public:
virtual bool previewEnabled() = 0;
/**
- * Start record mode. When a record image is available recording_callback()
- * is called with the user parameter. Every record frame must be released
+ * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME
+ * message is sent with the corresponding frame. Every record frame must be released
* by calling releaseRecordingFrame().
*/
- virtual status_t startRecording(recording_callback cb, void* user) = 0;
+ virtual status_t startRecording() = 0;
/**
* Stop a previously started recording.
@@ -130,39 +149,27 @@ public:
virtual bool recordingEnabled() = 0;
/**
- * Release a record frame previously returned by the recording_callback()
- * passed to startRecord().
+ * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME.
*/
virtual void releaseRecordingFrame(const sp<IMemory>& mem) = 0;
/**
- * Start auto focus, the callback routine is called
- * once when focusing is complete. autoFocus() will
- * be called again if another auto focus is needed.
+ * Start auto focus, the notification callback routine is called
+ * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
+ * will be called again if another auto focus is needed.
*/
- virtual status_t autoFocus(autofocus_callback,
- void* user) = 0;
+ virtual status_t autoFocus() = 0;
/**
- * Take a picture. The raw_callback is called when
- * the uncompressed image is available. The jpeg_callback
- * is called when the compressed image is available. These
- * call backs may be null. The user parameter is passed
- * to each of the call back routines.
+ * Take a picture.
*/
- virtual status_t takePicture(shutter_callback,
- raw_callback,
- jpeg_callback,
- void* user) = 0;
+ virtual status_t takePicture() = 0;
/**
- * Cancel a picture that was started with takePicture. You may cancel any
- * of the shutter, raw, or jpeg callbacks. Calling this method when no
- * picture is being taken is a no-op.
+ * Cancel a picture that was started with takePicture. Calling this
+ * method when no picture is being taken is a no-op.
*/
- virtual status_t cancelPicture(bool cancel_shutter,
- bool cancel_raw,
- bool cancel_jpeg) = 0;
+ virtual status_t cancelPicture() = 0;
/** Set the camera parameters. */
virtual status_t setParameters(const CameraParameters& params) = 0;
diff --git a/include/ui/EGLDisplaySurface.h b/include/ui/EGLDisplaySurface.h
deleted file mode 100644
index a8b5853..0000000
--- a/include/ui/EGLDisplaySurface.h
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_EGL_DISPLAY_SURFACE_H
-#define ANDROID_EGL_DISPLAY_SURFACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Timers.h>
-
-#include <ui/EGLNativeSurface.h>
-
-#include <pixelflinger/pixelflinger.h>
-#include <linux/fb.h>
-
-#include <EGL/egl.h>
-
-struct copybit_device_t;
-struct copybit_image_t;
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Region;
-class Rect;
-
-class EGLDisplaySurface : public EGLNativeSurface<EGLDisplaySurface>
-{
-public:
- EGLDisplaySurface();
- ~EGLDisplaySurface();
-
- int32_t getPageFlipCount() const;
- void copyFrontToBack(const Region& copyback);
- void copyFrontToImage(const copybit_image_t& dst);
- void copyBackToImage(const copybit_image_t& dst);
-
- void setSwapRectangle(int l, int t, int w, int h);
-
-private:
- static void hook_incRef(NativeWindowType window);
- static void hook_decRef(NativeWindowType window);
- static uint32_t hook_swapBuffers(NativeWindowType window);
-
- uint32_t swapBuffers();
-
- status_t mapFrameBuffer();
-
- enum {
- PAGE_FLIP = 0x00000001
- };
- GGLSurface mFb[2];
- int mIndex;
- uint32_t mFlags;
- size_t mSize;
- fb_var_screeninfo mInfo;
- fb_fix_screeninfo mFinfo;
- int32_t mPageFlipCount;
- nsecs_t mTime;
- int32_t mSwapCount;
- nsecs_t mSleep;
- uint32_t mFeatureFlags;
- copybit_device_t* mBlitEngine;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_EGL_DISPLAY_SURFACE_H
-
diff --git a/include/ui/EGLNativeWindowSurface.h b/include/ui/EGLNativeWindowSurface.h
deleted file mode 100644
index 3494234..0000000
--- a/include/ui/EGLNativeWindowSurface.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_EGL_NATIVE_WINDOW_SURFACE_H
-#define ANDROID_EGL_NATIVE_WINDOW_SURFACE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <ui/EGLNativeSurface.h>
-#include <EGL/egl.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
-class Surface;
-
-class EGLNativeWindowSurface : public EGLNativeSurface<EGLNativeWindowSurface>
-{
-public:
- EGLNativeWindowSurface(const sp<Surface>& surface);
- ~EGLNativeWindowSurface();
-
- void setSwapRectangle(int l, int t, int w, int h);
-
-private:
- static void hook_incRef(NativeWindowType window);
- static void hook_decRef(NativeWindowType window);
- static uint32_t hook_swapBuffers(NativeWindowType window);
- static void hook_connect(NativeWindowType window);
- static void hook_disconnect(NativeWindowType window);
-
- uint32_t swapBuffers();
- void connect();
- void disconnect();
-
- sp<Surface> mSurface;
- bool mConnected;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_EGL_NATIVE_WINDOW_SURFACE_H
-
diff --git a/include/ui/EGLUtils.h b/include/ui/EGLUtils.h
new file mode 100644
index 0000000..a5bff81
--- /dev/null
+++ b/include/ui/EGLUtils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_UI_EGLUTILS_H
+#define ANDROID_UI_EGLUTILS_H
+
+#include <utils/Errors.h>
+#include <ui/PixelFormat.h>
+#include <EGL/egl.h>
+
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class EGLUtils
+{
+public:
+
+ static const char *strerror(EGLint err);
+
+ static status_t selectConfigForPixelFormat(
+ EGLDisplay dpy,
+ EGLint const* attrs,
+ PixelFormat format,
+ EGLConfig* outConfig);
+
+ static status_t selectConfigForNativeWindow(
+ EGLDisplay dpy,
+ EGLint const* attrs,
+ EGLNativeWindowType window,
+ EGLConfig* outConfig);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+#endif /* ANDROID_UI_EGLUTILS_H */
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 3848d8c..3b18c77 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -20,7 +20,10 @@
#include <utils/String8.h>
#include <utils/threads.h>
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <linux/input.h>
@@ -52,7 +55,9 @@ public:
CLASS_KEYBOARD = 0x00000001,
CLASS_ALPHAKEY = 0x00000002,
CLASS_TOUCHSCREEN = 0x00000004,
- CLASS_TRACKBALL = 0x00000008
+ CLASS_TRACKBALL = 0x00000008,
+ CLASS_TOUCHSCREEN_MT= 0x00000010,
+ CLASS_DPAD = 0x00000020
};
uint32_t getDeviceClasses(int32_t deviceId) const;
@@ -70,6 +75,13 @@ public:
int getKeycodeState(int key) const;
int getKeycodeState(int32_t deviceId, int key) const;
+ status_t scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const;
+
+ // exclude a particular device from opening
+ // this can be used to ignore input devices for sensors
+ void addExcludedDevice(const char* deviceName);
+
// special type codes when devices are added/removed.
enum {
DEVICE_ADDED = 0x10000000,
@@ -82,10 +94,9 @@ public:
virtual bool getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen);
-
+
protected:
virtual ~EventHub();
- virtual void onFirstRef();
private:
bool openPlatformInput(void);
@@ -108,17 +119,18 @@ private:
String8 keylayoutFilename;
device_t* next;
- device_t(int32_t _id, const char* _path);
+ device_t(int32_t _id, const char* _path, const char* name);
~device_t();
};
device_t* getDevice(int32_t deviceId) const;
+ bool hasKeycode(device_t* device, int keycode) const;
// Protect all internal state.
mutable Mutex mLock;
bool mHaveFirstKeyboard;
- int32_t mFirstKeyboardId; // the API is that the build in keyboard is id 0, so map it
+ int32_t mFirstKeyboardId; // the API is that the built-in keyboard is id 0, so map it
struct device_ent {
device_t* device;
@@ -133,7 +145,10 @@ private:
device_t **mDevices;
struct pollfd *mFDs;
int mFDCount;
-
+
+ bool mOpened;
+ List<String8> mExcludedDevices;
+
// device ids that report particular switches.
#ifdef EV_SW
int32_t mSwitches[SW_MAX+1];
diff --git a/include/ui/FramebufferNativeWindow.h b/include/ui/FramebufferNativeWindow.h
new file mode 100644
index 0000000..68144b5
--- /dev/null
+++ b/include/ui/FramebufferNativeWindow.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEBUFFER_NATIVE_WINDOW_H
+#define ANDROID_FRAMEBUFFER_NATIVE_WINDOW_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+
+#include <utils/threads.h>
+#include <ui/Rect.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include <ui/egl/android_natives.h>
+
+
+extern "C" EGLNativeWindowType android_createDisplaySurface(void);
+
+// ---------------------------------------------------------------------------
+namespace android {
+// ---------------------------------------------------------------------------
+
+class Surface;
+class NativeBuffer;
+
+// ---------------------------------------------------------------------------
+
+class FramebufferNativeWindow
+ : public EGLNativeBase<
+ android_native_window_t,
+ FramebufferNativeWindow,
+ LightRefBase<FramebufferNativeWindow> >
+{
+public:
+ FramebufferNativeWindow();
+
+ framebuffer_device_t const * getDevice() const { return fbDev; }
+
+ bool isUpdateOnDemand() const { return mUpdateOnDemand; }
+ status_t setUpdateRectangle(const Rect& updateRect);
+
+private:
+ friend class LightRefBase<FramebufferNativeWindow>;
+ ~FramebufferNativeWindow(); // this class cannot be overloaded
+ static int setSwapInterval(android_native_window_t* window, int interval);
+ static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
+ static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
+ static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
+ static int query(android_native_window_t* window, int what, int* value);
+ static int perform(android_native_window_t* window, int operation, ...);
+
+ framebuffer_device_t* fbDev;
+ alloc_device_t* grDev;
+
+ sp<NativeBuffer> buffers[2];
+ sp<NativeBuffer> front;
+
+ mutable Mutex mutex;
+ Condition mCondition;
+ int32_t mNumBuffers;
+ int32_t mNumFreeBuffers;
+ int32_t mBufferHead;
+ bool mUpdateOnDemand;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+// ---------------------------------------------------------------------------
+
+#endif // ANDROID_FRAMEBUFFER_NATIVE_WINDOW_H
+
diff --git a/include/ui/ICamera.h b/include/ui/ICamera.h
index 241fb63..1df7914 100644
--- a/include/ui/ICamera.h
+++ b/include/ui/ICamera.h
@@ -18,10 +18,10 @@
#define ANDROID_HARDWARE_ICAMERA_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
#include <ui/ISurface.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <utils/String8.h>
#include <ui/Camera.h>
diff --git a/include/ui/ICameraClient.h b/include/ui/ICameraClient.h
index 1001c71..236d0f6 100644
--- a/include/ui/ICameraClient.h
+++ b/include/ui/ICameraClient.h
@@ -18,9 +18,9 @@
#define ANDROID_HARDWARE_ICAMERA_APP_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
#include <utils/Timers.h>
namespace android {
diff --git a/include/ui/ICameraService.h b/include/ui/ICameraService.h
index c652c51..061681a 100644
--- a/include/ui/ICameraService.h
+++ b/include/ui/ICameraService.h
@@ -18,8 +18,8 @@
#define ANDROID_HARDWARE_ICAMERASERVICE_H
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
#include <ui/ICameraClient.h>
#include <ui/ICamera.h>
diff --git a/include/ui/IOverlay.h b/include/ui/IOverlay.h
index 699b1b0..af3add1 100644
--- a/include/ui/IOverlay.h
+++ b/include/ui/IOverlay.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <utils/RefBase.h>
#include <ui/PixelFormat.h>
diff --git a/include/ui/ISurface.h b/include/ui/ISurface.h
index 87b320f..1283033 100644
--- a/include/ui/ISurface.h
+++ b/include/ui/ISurface.h
@@ -21,11 +21,12 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <utils/RefBase.h>
#include <ui/PixelFormat.h>
#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
namespace android {
@@ -33,6 +34,7 @@ typedef int32_t SurfaceID;
class IMemoryHeap;
class OverlayRef;
+class SurfaceBuffer;
class ISurface : public IInterface
{
@@ -42,11 +44,13 @@ protected:
UNREGISTER_BUFFERS,
POST_BUFFER, // one-way transaction
CREATE_OVERLAY,
+ REQUEST_BUFFER,
};
public:
DECLARE_META_INTERFACE(Surface);
+ virtual sp<SurfaceBuffer> requestBuffer(int bufferIdx, int usage) = 0;
class BufferHeap {
public:
@@ -78,9 +82,7 @@ public:
};
virtual status_t registerBuffers(const BufferHeap& buffers) = 0;
-
virtual void postBuffer(ssize_t offset) = 0; // one-way
-
virtual void unregisterBuffers() = 0;
virtual sp<OverlayRef> createOverlay(
diff --git a/include/ui/ISurfaceComposer.h b/include/ui/ISurfaceComposer.h
index 5c64b22..25d954c 100644
--- a/include/ui/ISurfaceComposer.h
+++ b/include/ui/ISurfaceComposer.h
@@ -22,7 +22,7 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <ui/PixelFormat.h>
#include <ui/ISurfaceFlingerClient.h>
@@ -32,7 +32,6 @@ namespace android {
// ----------------------------------------------------------------------------
class DisplayInfo;
-class IGPUCallback;
class ISurfaceComposer : public IInterface
{
@@ -41,8 +40,6 @@ public:
enum { // (keep in sync with Surface.java)
eHidden = 0x00000004,
- eGPU = 0x00000008,
- eHardware = 0x00000010,
eDestroyBackbuffer = 0x00000020,
eSecure = 0x00000080,
eNonPremultiplied = 0x00000100,
@@ -63,7 +60,6 @@ public:
eTransparentRegionChanged = 0x00000020,
eVisibilityChanged = 0x00000040,
eFreezeTintChanged = 0x00000080,
- eDestroyed = 0x00000100
};
enum {
@@ -94,7 +90,7 @@ public:
virtual sp<ISurfaceFlingerClient> createConnection() = 0;
/* retrieve the control block */
- virtual sp<IMemory> getCblk() const = 0;
+ virtual sp<IMemoryHeap> getCblk() const = 0;
/* open/close transactions. recquires ACCESS_SURFACE_FLINGER permission */
virtual void openGlobalTransaction() = 0;
@@ -112,37 +108,12 @@ public:
*/
virtual void bootFinished() = 0;
- /* get access to the GPU. Access is relinquished when releasing regs */
- struct gpu_info_t {
- struct gpu_region_t {
- sp<IMemory> region;
- size_t reserved;
- };
- sp<IMemory> regs;
- size_t count;
- gpu_region_t regions[2];
- };
- virtual status_t requestGPU(
- const sp<IGPUCallback>& callback,
- gpu_info_t* gpu) = 0;
-
- /* take the gpu back from any apps using it. They'll get a
- * EGL_CONTEXT_LOST error */
- virtual status_t revokeGPU() = 0;
-
/* Signal surfaceflinger that there might be some work to do
* This is an ASYNCHRONOUS call.
*/
virtual void signal() const = 0;
};
-class IGPUCallback : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(GPUCallback);
- virtual void gpuLost() = 0; //one-way
-};
-
// ----------------------------------------------------------------------------
class BnSurfaceComposer : public BnInterface<ISurfaceComposer>
@@ -159,8 +130,6 @@ public:
SET_ORIENTATION,
FREEZE_DISPLAY,
UNFREEZE_DISPLAY,
- REQUEST_GPU,
- REVOKE_GPU,
SIGNAL
};
@@ -170,15 +139,6 @@ public:
uint32_t flags = 0);
};
-class BnGPUCallback : public BnInterface<IGPUCallback>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/include/ui/ISurfaceFlingerClient.h b/include/ui/ISurfaceFlingerClient.h
index 5b9361d..5d231e6 100644
--- a/include/ui/ISurfaceFlingerClient.h
+++ b/include/ui/ISurfaceFlingerClient.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <utils/RefBase.h>
#include <ui/ISurface.h>
@@ -52,12 +52,14 @@ public:
struct surface_data_t {
int32_t token;
int32_t identity;
- sp<IMemoryHeap> heap[2];
+ uint32_t width;
+ uint32_t height;
+ uint32_t format;
status_t readFromParcel(const Parcel& parcel);
status_t writeToParcel(Parcel* parcel) const;
};
- virtual void getControlBlocks(sp<IMemory>* ctl) const = 0;
+ virtual sp<IMemoryHeap> getControlBlock() const = 0;
virtual sp<ISurface> createSurface( surface_data_t* data,
int pid,
diff --git a/include/ui/Overlay.h b/include/ui/Overlay.h
index 66514b4..a9ae1c4 100644
--- a/include/ui/Overlay.h
+++ b/include/ui/Overlay.h
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <utils/Errors.h>
-#include <utils/IInterface.h>
+#include <binder/IInterface.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -82,6 +82,16 @@ public:
/* release the overlay buffer and post it */
status_t queueBuffer(overlay_buffer_t buffer);
+ /* change the width and height of the overlay */
+ status_t resizeInput(uint32_t width, uint32_t height);
+
+ status_t setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) ;
+
+ status_t getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) ;
+
+ /* set the buffer attributes */
+ status_t setParameter(int param, int value);
+
/* returns the address of a given buffer if supported, NULL otherwise. */
void* getBufferAddress(overlay_buffer_t buffer);
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
index 14af823..6d87321 100644
--- a/include/ui/PixelFormat.h
+++ b/include/ui/PixelFormat.h
@@ -84,6 +84,13 @@ typedef int32_t PixelFormat;
struct PixelFormatInfo
{
+ enum {
+ INDEX_ALPHA = 0,
+ INDEX_RED = 1,
+ INDEX_GREEN = 2,
+ INDEX_BLUE = 3
+ };
+
enum { // components
ALPHA = 1,
RGB = 2,
@@ -95,20 +102,33 @@ struct PixelFormatInfo
Y_CB_CR_I = 8,
};
+ struct szinfo {
+ uint8_t h;
+ uint8_t l;
+ };
+
inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { }
size_t getScanlineSize(unsigned int width) const;
+ size_t getSize(size_t ci) const {
+ return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0;
+ }
size_t version;
PixelFormat format;
size_t bytesPerPixel;
size_t bitsPerPixel;
- uint8_t h_alpha;
- uint8_t l_alpha;
- uint8_t h_red;
- uint8_t l_red;
- uint8_t h_green;
- uint8_t l_green;
- uint8_t h_blue;
- uint8_t l_blue;
+ union {
+ szinfo cinfo[4];
+ struct {
+ uint8_t h_alpha;
+ uint8_t l_alpha;
+ uint8_t h_red;
+ uint8_t l_red;
+ uint8_t h_green;
+ uint8_t l_green;
+ uint8_t h_blue;
+ uint8_t l_blue;
+ };
+ };
uint8_t components;
uint8_t reserved0[3];
uint32_t reserved1;
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
index da72944..a213c09 100644
--- a/include/ui/Rect.h
+++ b/include/ui/Rect.h
@@ -30,6 +30,8 @@ public:
int right;
int bottom;
+ typedef int value_type;
+
// we don't provide copy-ctor and operator= on purpose
// because we want the compiler generated versions
@@ -46,7 +48,11 @@ public:
}
void makeInvalid();
-
+
+ inline void clear() {
+ left = top = right = bottom = 0;
+ }
+
// a valid rectangle has a non negative width and height
inline bool isValid() const {
return (width()>=0) && (height()>=0);
diff --git a/include/ui/Region.h b/include/ui/Region.h
index 7689673..2bcad5b 100644
--- a/include/ui/Region.h
+++ b/include/ui/Region.h
@@ -21,14 +21,12 @@
#include <sys/types.h>
#include <utils/Vector.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <ui/Rect.h>
#include <hardware/copybit.h>
-#include <core/SkRegion.h>
-
namespace android {
// ---------------------------------------------------------------------------
@@ -40,7 +38,6 @@ class Region
public:
Region();
Region(const Region& rhs);
- explicit Region(const SkRegion& rhs);
explicit Region(const Rect& rhs);
explicit Region(const Parcel& parcel);
explicit Region(const void* buffer);
@@ -48,65 +45,78 @@ public:
Region& operator = (const Region& rhs);
- inline bool isEmpty() const { return mRegion.isEmpty(); }
- inline bool isRect() const { return mRegion.isRect(); }
-
- Rect bounds() const;
+ inline bool isEmpty() const { return mBounds.isEmpty(); }
+ inline bool isRect() const { return mStorage.isEmpty(); }
- const SkRegion& toSkRegion() const;
+ inline Rect getBounds() const { return mBounds; }
+ inline Rect bounds() const { return getBounds(); }
+ // the region becomes its bounds
+ Region& makeBoundsSelf();
+
void clear();
void set(const Rect& r);
+ void set(uint32_t w, uint32_t h);
Region& orSelf(const Rect& rhs);
Region& andSelf(const Rect& rhs);
+ Region& subtractSelf(const Rect& rhs);
// boolean operators, applied on this
Region& orSelf(const Region& rhs);
Region& andSelf(const Region& rhs);
Region& subtractSelf(const Region& rhs);
+ // boolean operators
+ const Region merge(const Rect& rhs) const;
+ const Region intersect(const Rect& rhs) const;
+ const Region subtract(const Rect& rhs) const;
+
+ // boolean operators
+ const Region merge(const Region& rhs) const;
+ const Region intersect(const Region& rhs) const;
+ const Region subtract(const Region& rhs) const;
+
// these translate rhs first
Region& translateSelf(int dx, int dy);
Region& orSelf(const Region& rhs, int dx, int dy);
Region& andSelf(const Region& rhs, int dx, int dy);
Region& subtractSelf(const Region& rhs, int dx, int dy);
- // boolean operators
- Region merge(const Region& rhs) const;
- Region intersect(const Region& rhs) const;
- Region subtract(const Region& rhs) const;
-
// these translate rhs first
- Region translate(int dx, int dy) const;
- Region merge(const Region& rhs, int dx, int dy) const;
- Region intersect(const Region& rhs, int dx, int dy) const;
- Region subtract(const Region& rhs, int dx, int dy) const;
+ const Region translate(int dx, int dy) const;
+ const Region merge(const Region& rhs, int dx, int dy) const;
+ const Region intersect(const Region& rhs, int dx, int dy) const;
+ const Region subtract(const Region& rhs, int dx, int dy) const;
// convenience operators overloads
- inline Region operator | (const Region& rhs) const;
- inline Region operator & (const Region& rhs) const;
- inline Region operator - (const Region& rhs) const;
- inline Region operator + (const Point& pt) const;
+ inline const Region operator | (const Region& rhs) const;
+ inline const Region operator & (const Region& rhs) const;
+ inline const Region operator - (const Region& rhs) const;
+ inline const Region operator + (const Point& pt) const;
inline Region& operator |= (const Region& rhs);
inline Region& operator &= (const Region& rhs);
inline Region& operator -= (const Region& rhs);
inline Region& operator += (const Point& pt);
- class iterator {
- SkRegion::Iterator mIt;
- public:
- iterator(const Region& r);
- inline operator bool () const { return !done(); }
- int iterate(Rect* rect);
- private:
- inline bool done() const {
- return const_cast<SkRegion::Iterator&>(mIt).done();
- }
- };
+
+ /* various ways to access the rectangle list */
+
+ typedef Rect const* const_iterator;
+
+ const_iterator begin() const;
+ const_iterator end() const;
- size_t rects(Vector<Rect>& rectList) const;
+ /* no user serviceable parts here... */
+
+ size_t getRects(Vector<Rect>& rectList) const;
+ Rect const* getArray(size_t* count) const;
+
+
+ // add a rectangle to the internal list. This rectangle must
+ // be sorted in Y and X and must not make the region invalid.
+ void addRectUnchecked(int l, int t, int r, int b);
// flatten/unflatten a region to/from a Parcel
status_t write(Parcel& parcel) const;
@@ -123,20 +133,46 @@ public:
void dump(const char* what, uint32_t flags=0) const;
private:
- SkRegion mRegion;
+ class rasterizer;
+ friend class rasterizer;
+
+ Region& operationSelf(const Rect& r, int op);
+ Region& operationSelf(const Region& r, int op);
+ Region& operationSelf(const Region& r, int dx, int dy, int op);
+ const Region operation(const Rect& rhs, int op) const;
+ const Region operation(const Region& rhs, int op) const;
+ const Region operation(const Region& rhs, int dx, int dy, int op) const;
+
+ static void boolean_operation(int op, Region& dst,
+ const Region& lhs, const Region& rhs, int dx, int dy);
+ static void boolean_operation(int op, Region& dst,
+ const Region& lhs, const Rect& rhs, int dx, int dy);
+
+ static void boolean_operation(int op, Region& dst,
+ const Region& lhs, const Region& rhs);
+ static void boolean_operation(int op, Region& dst,
+ const Region& lhs, const Rect& rhs);
+
+ static void translate(Region& reg, int dx, int dy);
+ static void translate(Region& dst, const Region& reg, int dx, int dy);
+
+ static bool validate(const Region& reg, const char* name);
+
+ Rect mBounds;
+ Vector<Rect> mStorage;
};
-Region Region::operator | (const Region& rhs) const {
+const Region Region::operator | (const Region& rhs) const {
return merge(rhs);
}
-Region Region::operator & (const Region& rhs) const {
+const Region Region::operator & (const Region& rhs) const {
return intersect(rhs);
}
-Region Region::operator - (const Region& rhs) const {
+const Region Region::operator - (const Region& rhs) const {
return subtract(rhs);
}
-Region Region::operator + (const Point& pt) const {
+const Region Region::operator + (const Point& pt) const {
return translate(pt.x, pt.y);
}
@@ -157,16 +193,23 @@ Region& Region::operator += (const Point& pt) {
// ---------------------------------------------------------------------------
struct region_iterator : public copybit_region_t {
- region_iterator(const Region& region) : i(region) {
+ region_iterator(const Region& region)
+ : b(region.begin()), e(region.end()) {
this->next = iterate;
}
private:
static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
- return static_cast<const region_iterator*>(self)
- ->i.iterate(reinterpret_cast<Rect*>(rect));
+ region_iterator const* me = static_cast<region_iterator const*>(self);
+ if (me->b != me->e) {
+ *reinterpret_cast<Rect*>(rect) = *me->b++;
+ return 1;
+ }
+ return 0;
}
- mutable Region::iterator i;
+ mutable Region::const_iterator b;
+ Region::const_iterator const e;
};
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/include/ui/Surface.h b/include/ui/Surface.h
index 33953a9..118fb83 100644
--- a/include/ui/Surface.h
+++ b/include/ui/Surface.h
@@ -28,48 +28,41 @@
#include <ui/Region.h>
#include <ui/ISurfaceFlingerClient.h>
+#include <ui/egl/android_natives.h>
+
namespace android {
// ---------------------------------------------------------------------------
+class BufferMapper;
+class IOMX;
class Rect;
+class Surface;
class SurfaceComposerClient;
+class SharedClient;
+class SharedBufferClient;
-class Surface : public RefBase
-{
+// ---------------------------------------------------------------------------
+class SurfaceControl : public RefBase
+{
public:
- struct SurfaceInfo {
- uint32_t w;
- uint32_t h;
- uint32_t bpr;
- PixelFormat format;
- void* bits;
- void* base;
- uint32_t reserved[2];
- };
-
- bool isValid() const { return this && mToken>=0 && mClient!=0; }
+ static bool isValid(const sp<SurfaceControl>& surface) {
+ return (surface != 0) && surface->isValid();
+ }
+ bool isValid() {
+ return mToken>=0 && mClient!=0;
+ }
+ static bool isSameSurface(
+ const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs);
+
SurfaceID ID() const { return mToken; }
-
- status_t lock(SurfaceInfo* info, bool blocking = true);
- status_t lock(SurfaceInfo* info, Region* dirty, bool blocking = true);
- status_t unlockAndPost();
- status_t unlock();
-
- void* heapBase(int i) const;
uint32_t getFlags() const { return mFlags; }
+ uint32_t getIdentity() const { return mIdentity; }
- // setSwapRectangle() is mainly used by EGL
- void setSwapRectangle(const Rect& r);
- const Rect& swapRectangle() const;
- status_t nextBuffer(SurfaceInfo* info);
-
- sp<Surface> dup() const;
- static sp<Surface> readFromParcel(Parcel* parcel);
- static status_t writeToParcel(const sp<Surface>& surface, Parcel* parcel);
- static bool isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs);
-
+ // release surface data from java
+ void clear();
+
status_t setLayer(int32_t layer);
status_t setPosition(int32_t x, int32_t y);
status_t setSize(uint32_t w, uint32_t h);
@@ -83,8 +76,17 @@ public:
status_t setMatrix(float dsdx, float dtdx, float dsdy, float dtdy);
status_t setFreezeTint(uint32_t tint);
- uint32_t getIdentity() const { return mIdentity; }
+ static status_t writeSurfaceToParcel(
+ const sp<SurfaceControl>& control, Parcel* parcel);
+
+ sp<Surface> getSurface() const;
+
private:
+ // can't be copied
+ SurfaceControl& operator = (SurfaceControl& rhs);
+ SurfaceControl(const SurfaceControl& rhs);
+
+
friend class SurfaceComposerClient;
// camera and camcorder need access to the ISurface binder interface for preview
@@ -92,43 +94,159 @@ private:
friend class MediaRecorder;
// mediaplayer needs access to ISurface for display
friend class MediaPlayer;
+ // for testing
friend class Test;
const sp<ISurface>& getISurface() const { return mSurface; }
+
- // can't be copied
- Surface& operator = (Surface& rhs);
- Surface(const Surface& rhs);
+ friend class Surface;
- Surface(const sp<SurfaceComposerClient>& client,
+ SurfaceControl(
+ const sp<SurfaceComposerClient>& client,
const sp<ISurface>& surface,
const ISurfaceFlingerClient::surface_data_t& data,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- bool owner = true);
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
- Surface(Surface const* rhs);
+ ~SurfaceControl();
- ~Surface();
+ status_t validate(SharedClient const* cblk) const;
+ void destroy();
+
+ sp<SurfaceComposerClient> mClient;
+ sp<ISurface> mSurface;
+ SurfaceID mToken;
+ uint32_t mIdentity;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ PixelFormat mFormat;
+ uint32_t mFlags;
+ mutable Mutex mLock;
+
+ mutable sp<Surface> mSurfaceData;
+};
+
+// ---------------------------------------------------------------------------
- Region dirtyRegion() const;
- void setDirtyRegion(const Region& region) const;
+class Surface
+ : public EGLNativeBase<android_native_window_t, Surface, RefBase>
+{
+public:
+ struct SurfaceInfo {
+ uint32_t w;
+ uint32_t h;
+ uint32_t s;
+ uint32_t usage;
+ PixelFormat format;
+ void* bits;
+ uint32_t reserved[2];
+ };
+
+ Surface(const Parcel& data);
+
+ static bool isValid(const sp<Surface>& surface) {
+ return (surface != 0) && surface->isValid();
+ }
+
+ static bool isSameSurface(
+ const sp<Surface>& lhs, const sp<Surface>& rhs);
+
+ bool isValid();
+ SurfaceID ID() const { return mToken; }
+ uint32_t getFlags() const { return mFlags; }
+ uint32_t getIdentity() const { return mIdentity; }
+
+ // the lock/unlock APIs must be used from the same thread
+ status_t lock(SurfaceInfo* info, bool blocking = true);
+ status_t lock(SurfaceInfo* info, Region* dirty, bool blocking = true);
+ status_t unlockAndPost();
- // this locks protects calls to lockSurface() / unlockSurface()
- // and is called by SurfaceComposerClient.
- Mutex& getLock() const { return mSurfaceLock; }
+ // setSwapRectangle() is intended to be used by GL ES clients
+ void setSwapRectangle(const Rect& r);
+
+private:
+ // can't be copied
+ Surface& operator = (Surface& rhs);
+ Surface(const Surface& rhs);
+
+ Surface(const sp<SurfaceControl>& control);
+ void init();
+ ~Surface();
+
+ friend class SurfaceComposerClient;
+ friend class SurfaceControl;
+
+
+ // camera and camcorder need access to the ISurface binder interface for preview
+ friend class Camera;
+ friend class MediaRecorder;
+ // mediaplayer needs access to ISurface for display
+ friend class MediaPlayer;
+ friend class IOMX;
+ // this is just to be able to write some unit tests
+ friend class Test;
+ sp<SurfaceComposerClient> getClient() const;
+ sp<ISurface> getISurface() const;
+
+ status_t getBufferLocked(int index, int usage);
+
+ status_t validate(SharedClient const* cblk) const;
+
+ inline const BufferMapper& getBufferMapper() const { return mBufferMapper; }
+ inline BufferMapper& getBufferMapper() { return mBufferMapper; }
+
+ static int setSwapInterval(android_native_window_t* window, int interval);
+ static int dequeueBuffer(android_native_window_t* window, android_native_buffer_t** buffer);
+ static int lockBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
+ static int queueBuffer(android_native_window_t* window, android_native_buffer_t* buffer);
+ static int query(android_native_window_t* window, int what, int* value);
+ static int perform(android_native_window_t* window, int operation, ...);
+
+ int dequeueBuffer(android_native_buffer_t** buffer);
+ int lockBuffer(android_native_buffer_t* buffer);
+ int queueBuffer(android_native_buffer_t* buffer);
+ int query(int what, int* value);
+ int perform(int operation, va_list args);
+
+ status_t dequeueBuffer(sp<SurfaceBuffer>* buffer);
+
+
+ void setUsage(uint32_t reqUsage);
+ bool getUsage(uint32_t* usage);
+
+ // constants
sp<SurfaceComposerClient> mClient;
sp<ISurface> mSurface;
- sp<IMemoryHeap> mHeap[2];
SurfaceID mToken;
uint32_t mIdentity;
PixelFormat mFormat;
uint32_t mFlags;
- const bool mOwner;
- mutable void* mSurfaceHeapBase[2];
+ BufferMapper& mBufferMapper;
+ SharedBufferClient* mSharedBufferClient;
+
+ // protected by mSurfaceLock
+ Rect mSwapRectangle;
+ uint32_t mUsage;
+ int32_t mUsageChanged;
+
+ // protected by mSurfaceLock. These are also used from lock/unlock
+ // but in that case, they must be called form the same thread.
+ sp<SurfaceBuffer> mBuffers[2];
mutable Region mDirtyRegion;
- mutable Rect mSwapRectangle;
- mutable uint8_t mBackbufferIndex;
+
+ // must be used from the lock/unlock thread
+ sp<SurfaceBuffer> mLockedBuffer;
+ sp<SurfaceBuffer> mPostedBuffer;
+ mutable Region mOldDirtyRegion;
+ bool mNeedFullUpdate;
+
+ // query() must be called from dequeueBuffer() thread
+ uint32_t mWidth;
+ uint32_t mHeight;
+
+ // Inherently thread-safe
mutable Mutex mSurfaceLock;
+ mutable Mutex mApiLock;
};
}; // namespace android
diff --git a/include/ui/SurfaceComposerClient.h b/include/ui/SurfaceComposerClient.h
index 76a3b55..269959c 100644
--- a/include/ui/SurfaceComposerClient.h
+++ b/include/ui/SurfaceComposerClient.h
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <utils/SortedVector.h>
-#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
#include <utils/threads.h>
@@ -36,8 +35,7 @@ namespace android {
class Region;
class SurfaceFlingerSynchro;
-struct per_client_cblk_t;
-struct layer_cblk_t;
+class SharedClient;
class SurfaceComposerClient : virtual public RefBase
{
@@ -62,13 +60,13 @@ public:
// surface creation / destruction
//! Create a surface
- sp<Surface> createSurface(
- int pid, //!< pid of the process the surfacec is for
- DisplayID display, //!< Display to create this surface on
- uint32_t w, //!< width in pixel
- uint32_t h, //!< height in pixel
- PixelFormat format, //!< pixel-format desired
- uint32_t flags = 0 //!< usage flags
+ sp<SurfaceControl> createSurface(
+ int pid, // pid of the process the surface is for
+ DisplayID display, // Display to create this surface on
+ uint32_t w, // width in pixel
+ uint32_t h, // height in pixel
+ PixelFormat format, // pixel-format desired
+ uint32_t flags = 0 // usage flags
);
// ------------------------------------------------------------------------
@@ -111,51 +109,35 @@ public:
private:
friend class Surface;
+ friend class SurfaceControl;
SurfaceComposerClient(const sp<ISurfaceComposer>& sm,
const sp<IBinder>& conn);
- status_t hide(Surface* surface);
- status_t show(Surface* surface, int32_t layer = -1);
- status_t freeze(Surface* surface);
- status_t unfreeze(Surface* surface);
- status_t setFlags(Surface* surface, uint32_t flags, uint32_t mask);
- status_t setTransparentRegionHint(Surface* surface, const Region& transparent);
- status_t setLayer(Surface* surface, int32_t layer);
- status_t setAlpha(Surface* surface, float alpha=1.0f);
- status_t setFreezeTint(Surface* surface, uint32_t tint);
- status_t setMatrix(Surface* surface, float dsdx, float dtdx, float dsdy, float dtdy);
- status_t setPosition(Surface* surface, int32_t x, int32_t y);
- status_t setSize(Surface* surface, uint32_t w, uint32_t h);
+ status_t hide(SurfaceID id);
+ status_t show(SurfaceID id, int32_t layer = -1);
+ status_t freeze(SurfaceID id);
+ status_t unfreeze(SurfaceID id);
+ status_t setFlags(SurfaceID id, uint32_t flags, uint32_t mask);
+ status_t setTransparentRegionHint(SurfaceID id, const Region& transparent);
+ status_t setLayer(SurfaceID id, int32_t layer);
+ status_t setAlpha(SurfaceID id, float alpha=1.0f);
+ status_t setFreezeTint(SurfaceID id, uint32_t tint);
+ status_t setMatrix(SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy);
+ status_t setPosition(SurfaceID id, int32_t x, int32_t y);
+ status_t setSize(SurfaceID id, uint32_t w, uint32_t h);
- //! Unlock the surface, and specify the dirty region if any
- status_t unlockAndPostSurface(Surface* surface);
- status_t unlockSurface(Surface* surface);
-
- status_t lockSurface(Surface* surface,
- Surface::SurfaceInfo* info,
- Region* dirty,
- bool blocking = true);
-
- status_t nextBuffer(Surface* surface,
- Surface::SurfaceInfo* info);
+ void signalServer();
status_t destroySurface(SurfaceID sid);
void _init(const sp<ISurfaceComposer>& sm,
const sp<ISurfaceFlingerClient>& conn);
- void _signal_server();
- static void _send_dirty_region(layer_cblk_t* lcblk, const Region& dirty);
- inline layer_state_t* _get_state_l(const sp<Surface>& surface);
- layer_state_t* _lockLayerState(const sp<Surface>& surface);
+ inline layer_state_t* _get_state_l(SurfaceID id);
+ layer_state_t* _lockLayerState(SurfaceID id);
inline void _unlockLayerState();
- status_t validateSurface(
- per_client_cblk_t const* cblk, Surface const * surface);
-
- void pinHeap(const sp<IMemoryHeap>& heap);
-
mutable Mutex mLock;
layer_state_t* mPrebuiltLayerState;
SortedVector<layer_state_t> mStates;
@@ -164,12 +146,9 @@ private:
// these don't need to be protected because they never change
// after assignment
status_t mStatus;
- per_client_cblk_t* mControl;
- sp<IMemory> mControlMemory;
+ SharedClient* mControl;
+ sp<IMemoryHeap> mControlMemory;
sp<ISurfaceFlingerClient> mClient;
- sp<IMemoryHeap> mSurfaceHeap;
- uint8_t* mSurfaceHeapBase;
- void* mGL;
SurfaceFlingerSynchro* mSignalServer;
};
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
new file mode 100644
index 0000000..3740db5
--- /dev/null
+++ b/include/ui/egl/android_natives.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ANDROID_NATIVES_H
+#define ANDROID_ANDROID_NATIVES_H
+
+#include <sys/types.h>
+#include <string.h>
+
+#include <hardware/gralloc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+#define ANDROID_NATIVE_MAKE_CONSTANT(a,b,c,d) \
+ (((unsigned)(a)<<24)|((unsigned)(b)<<16)|((unsigned)(c)<<8)|(unsigned)(d))
+
+#define ANDROID_NATIVE_WINDOW_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','w','n','d')
+
+#define ANDROID_NATIVE_BUFFER_MAGIC \
+ ANDROID_NATIVE_MAKE_CONSTANT('_','b','f','r')
+
+// ---------------------------------------------------------------------------
+
+struct android_native_buffer_t;
+
+// ---------------------------------------------------------------------------
+
+typedef struct android_native_base_t
+{
+ /* a magic value defined by the actual EGL native type */
+ int magic;
+
+ /* the sizeof() of the actual EGL native type */
+ int version;
+
+ void* reserved[4];
+
+ /* reference-counting interface */
+ void (*incRef)(struct android_native_base_t* base);
+ void (*decRef)(struct android_native_base_t* base);
+} android_native_base_t;
+
+// ---------------------------------------------------------------------------
+
+/* attributes queriable with query() */
+enum {
+ NATIVE_WINDOW_WIDTH = 0,
+ NATIVE_WINDOW_HEIGHT = 1,
+ NATIVE_WINDOW_FORMAT = 2,
+};
+
+/* valid operations for the (*perform)() hook */
+enum {
+ NATIVE_WINDOW_SET_USAGE = 0
+};
+
+typedef struct android_native_window_t
+{
+#ifdef __cplusplus
+ android_native_window_t()
+ : flags(0), minSwapInterval(0), maxSwapInterval(0), xdpi(0), ydpi(0)
+ {
+ common.magic = ANDROID_NATIVE_WINDOW_MAGIC;
+ common.version = sizeof(android_native_window_t);
+ memset(common.reserved, 0, sizeof(common.reserved));
+ }
+#endif
+
+ struct android_native_base_t common;
+
+ /* flags describing some attributes of this surface or its updater */
+ const uint32_t flags;
+
+ /* min swap interval supported by this updated */
+ const int minSwapInterval;
+
+ /* max swap interval supported by this updated */
+ const int maxSwapInterval;
+
+ /* horizontal and vertical resolution in DPI */
+ const float xdpi;
+ const float ydpi;
+
+ /* Some storage reserved for the OEM's driver. */
+ intptr_t oem[4];
+
+
+ /*
+ * Set the swap interval for this surface.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*setSwapInterval)(struct android_native_window_t* window,
+ int interval);
+
+ /*
+ * hook called by EGL to acquire a buffer. After this call, the buffer
+ * is not locked, so its content cannot be modified.
+ * this call may block if no buffers are available.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*dequeueBuffer)(struct android_native_window_t* window,
+ struct android_native_buffer_t** buffer);
+
+ /*
+ * hook called by EGL to lock a buffer. This MUST be called before modifying
+ * the content of a buffer. The buffer must have been acquired with
+ * dequeueBuffer first.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*lockBuffer)(struct android_native_window_t* window,
+ struct android_native_buffer_t* buffer);
+ /*
+ * hook called by EGL when modifications to the render buffer are done.
+ * This unlocks and post the buffer.
+ *
+ * Buffers MUST be queued in the same order than they were dequeued.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*queueBuffer)(struct android_native_window_t* window,
+ struct android_native_buffer_t* buffer);
+
+ /*
+ * hook used to retrieve information about the native window.
+ *
+ * Returns 0 on success or -errno on error.
+ */
+ int (*query)(struct android_native_window_t* window,
+ int what, int* value);
+
+ /*
+ * hook used to perform various operations on the surface.
+ * (*perform)() is a generic mechanism to add functionality to
+ * android_native_window_t while keeping backward binary compatibility.
+ *
+ * This hook should not be called directly, instead use the helper functions
+ * defined below.
+ *
+ * The valid operations are:
+ * NATIVE_WINDOW_SET_USAGE
+ *
+ */
+
+ int (*perform)(struct android_native_window_t* window,
+ int operation, ... );
+
+ void* reserved_proc[3];
+} android_native_window_t;
+
+
+/*
+ * native_window_set_usage() sets the intended usage flags for the next
+ * buffers acquired with (*lockBuffer)() and on.
+ * By default (if this function is never called), a usage of
+ * GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE
+ * is assumed.
+ * Calling this function will usually cause following buffers to be
+ * reallocated.
+ */
+
+static inline int native_window_set_usage(
+ android_native_window_t* window, int usage)
+{
+ return window->perform(window, NATIVE_WINDOW_SET_USAGE, usage);
+}
+
+
+// ---------------------------------------------------------------------------
+
+/* FIXME: this is legacy for pixmaps */
+typedef struct egl_native_pixmap_t
+{
+ int32_t version; /* must be 32 */
+ int32_t width;
+ int32_t height;
+ int32_t stride;
+ uint8_t* data;
+ uint8_t format;
+ uint8_t rfu[3];
+ union {
+ uint32_t compressedFormat;
+ int32_t vstride;
+ };
+ int32_t reserved;
+} egl_native_pixmap_t;
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+/*
+ * This helper class turns an EGL android_native_xxx type into a C++
+ * reference-counted object; with proper type conversions.
+ */
+template <typename NATIVE_TYPE, typename TYPE, typename REF>
+class EGLNativeBase : public NATIVE_TYPE, public REF
+{
+protected:
+ typedef EGLNativeBase<NATIVE_TYPE, TYPE, REF> BASE;
+ EGLNativeBase() : NATIVE_TYPE(), REF() {
+ NATIVE_TYPE::common.incRef = incRef;
+ NATIVE_TYPE::common.decRef = decRef;
+ }
+ static inline TYPE* getSelf(NATIVE_TYPE* self) {
+ return static_cast<TYPE*>(self);
+ }
+ static inline TYPE const* getSelf(NATIVE_TYPE const* self) {
+ return static_cast<TYPE const *>(self);
+ }
+ static inline TYPE* getSelf(android_native_base_t* base) {
+ return getSelf(reinterpret_cast<NATIVE_TYPE*>(base));
+ }
+ static inline TYPE const * getSelf(android_native_base_t const* base) {
+ return getSelf(reinterpret_cast<NATIVE_TYPE const*>(base));
+ }
+ static void incRef(android_native_base_t* base) {
+ EGLNativeBase* self = getSelf(base);
+ self->incStrong(self);
+ }
+ static void decRef(android_native_base_t* base) {
+ EGLNativeBase* self = getSelf(base);
+ self->decStrong(self);
+ }
+};
+
+} // namespace android
+#endif // __cplusplus
+
+/*****************************************************************************/
+
+#endif /* ANDROID_ANDROID_NATIVES_H */
diff --git a/include/utils.h b/include/utils.h
deleted file mode 100644
index 30648b1..0000000
--- a/include/utils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Handy utility functions and portability code. This file includes all
-// of the generally-useful headers in the "utils" directory.
-//
-#ifndef _LIBS_UTILS_H
-#define _LIBS_UTILS_H
-
-#include <utils/ported.h>
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <utils/Timers.h>
-#include <utils/List.h>
-#include <utils/string_array.h>
-#include <utils/misc.h>
-#include <utils/Errors.h>
-
-#endif // _LIBS_UTILS_H
diff --git a/include/utils/Binder.h b/include/utils/Binder.h
deleted file mode 100644
index b5b8d98..0000000
--- a/include/utils/Binder.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_BINDER_H
-#define ANDROID_BINDER_H
-
-#include <utils/IBinder.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder : public IBinder
-{
-public:
- BBinder();
-
- virtual String16 getInterfaceDescriptor() const;
- virtual bool isBinderAlive() const;
- virtual status_t pingBinder();
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- virtual status_t transact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
- virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0);
-
- virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0,
- wp<DeathRecipient>* outRecipient = NULL);
-
- virtual void attachObject( const void* objectID,
- void* object,
- void* cleanupCookie,
- object_cleanup_func func);
- virtual void* findObject(const void* objectID) const;
- virtual void detachObject(const void* objectID);
-
- virtual BBinder* localBinder();
-
-protected:
- virtual ~BBinder();
-
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
-private:
- BBinder(const BBinder& o);
- BBinder& operator=(const BBinder& o);
-
- class Extras;
-
- Extras* mExtras;
- void* mReserved0;
-};
-
-// ---------------------------------------------------------------------------
-
-class BpRefBase : public virtual RefBase
-{
-protected:
- BpRefBase(const sp<IBinder>& o);
- virtual ~BpRefBase();
- virtual void onFirstRef();
- virtual void onLastStrongRef(const void* id);
- virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
-
- inline IBinder* remote() { return mRemote; }
- inline IBinder* remote() const { return mRemote; }
-
-private:
- BpRefBase(const BpRefBase& o);
- BpRefBase& operator=(const BpRefBase& o);
-
- IBinder* const mRemote;
- RefBase::weakref_type* mRefs;
- volatile int32_t mState;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BINDER_H
diff --git a/include/utils/BpBinder.h b/include/utils/BpBinder.h
deleted file mode 100644
index 7b96e29..0000000
--- a/include/utils/BpBinder.h
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_BPBINDER_H
-#define ANDROID_BPBINDER_H
-
-#include <utils/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BpBinder : public IBinder
-{
-public:
- BpBinder(int32_t handle);
-
- inline int32_t handle() const { return mHandle; }
-
- virtual String16 getInterfaceDescriptor() const;
- virtual bool isBinderAlive() const;
- virtual status_t pingBinder();
- virtual status_t dump(int fd, const Vector<String16>& args);
-
- virtual status_t transact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-
- virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0);
- virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0,
- wp<DeathRecipient>* outRecipient = NULL);
-
- virtual void attachObject( const void* objectID,
- void* object,
- void* cleanupCookie,
- object_cleanup_func func);
- virtual void* findObject(const void* objectID) const;
- virtual void detachObject(const void* objectID);
-
- virtual BpBinder* remoteBinder();
-
- status_t setConstantData(const void* data, size_t size);
- void sendObituary();
-
- class ObjectManager
- {
- public:
- ObjectManager();
- ~ObjectManager();
-
- void attach( const void* objectID,
- void* object,
- void* cleanupCookie,
- IBinder::object_cleanup_func func);
- void* find(const void* objectID) const;
- void detach(const void* objectID);
-
- void kill();
-
- private:
- ObjectManager(const ObjectManager&);
- ObjectManager& operator=(const ObjectManager&);
-
- struct entry_t
- {
- void* object;
- void* cleanupCookie;
- IBinder::object_cleanup_func func;
- };
-
- KeyedVector<const void*, entry_t> mObjects;
- };
-
-protected:
- virtual ~BpBinder();
- virtual void onFirstRef();
- virtual void onLastStrongRef(const void* id);
- virtual bool onIncStrongAttempted(uint32_t flags, const void* id);
-
-private:
- const int32_t mHandle;
-
- struct Obituary {
- wp<DeathRecipient> recipient;
- void* cookie;
- uint32_t flags;
- };
-
- void reportOneDeath(const Obituary& obit);
-
- mutable Mutex mLock;
- volatile int32_t mAlive;
- volatile int32_t mObitsSent;
- Vector<Obituary>* mObituaries;
- ObjectManager mObjects;
- Parcel* mConstantData;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_BPBINDER_H
diff --git a/include/utils/Debug.h b/include/utils/Debug.h
index a662b9c..d9ed32d 100644
--- a/include/utils/Debug.h
+++ b/include/utils/Debug.h
@@ -14,10 +14,6 @@
* limitations under the License.
*/
-//
-// Debugging tools. These should be able to be stripped
-// in release builds.
-//
#ifndef ANDROID_DEBUG_H
#define ANDROID_DEBUG_H
@@ -25,9 +21,32 @@
#include <sys/types.h>
namespace android {
+// ---------------------------------------------------------------------------
+#ifdef __cplusplus
template<bool> struct CompileTimeAssert;
template<> struct CompileTimeAssert<true> {};
+#define COMPILE_TIME_ASSERT(_exp) \
+ template class CompileTimeAssert< (_exp) >;
+#endif
+#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE(_exp) \
+ CompileTimeAssert<( _exp )>();
+
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+template<bool C, typename LSH, typename RHS> struct CompileTimeIfElse;
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<true, LHS, RHS> { typedef LHS TYPE; };
+template<typename LHS, typename RHS>
+struct CompileTimeIfElse<false, LHS, RHS> { typedef RHS TYPE; };
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef __cplusplus
+extern "C" {
+#endif
const char* stringForIndent(int32_t indentLevel);
@@ -35,11 +54,17 @@ typedef void (*debugPrintFunc)(void* cookie, const char* txt);
void printTypeCode(uint32_t typeCode,
debugPrintFunc func = 0, void* cookie = 0);
+
void printHexData(int32_t indent, const void *buf, size_t length,
size_t bytesPerLine=16, int32_t singleLineBytesCutoff=16,
size_t alignment=0, bool cArrayStyle=false,
debugPrintFunc func = 0, void* cookie = 0);
+#ifdef __cplusplus
+}
+#endif
+
+// ---------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_DEBUG_H
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 1bf9e6f..81f818b 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -63,7 +63,7 @@ enum {
BAD_INDEX = -EOVERFLOW,
NOT_ENOUGH_DATA = -ENODATA,
WOULD_BLOCK = -EWOULDBLOCK,
- TIMED_OUT = -ETIME,
+ TIMED_OUT = -ETIMEDOUT,
UNKNOWN_TRANSACTION = -EBADMSG,
#else
BAD_INDEX = -E2BIG,
diff --git a/include/utils/IBinder.h b/include/utils/IBinder.h
deleted file mode 100644
index 7370330..0000000
--- a/include/utils/IBinder.h
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IBINDER_H
-#define ANDROID_IBINDER_H
-
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-
-#define B_PACK_CHARS(c1, c2, c3, c4) \
- ((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class BBinder;
-class BpBinder;
-class IInterface;
-class Parcel;
-
-/**
- * Base class and low-level protocol for a remotable object.
- * You can derive from this class to create an object for which other
- * processes can hold references to it. Communication between processes
- * (method calls, property get and set) is down through a low-level
- * protocol implemented on top of the transact() API.
- */
-class IBinder : public virtual RefBase
-{
-public:
- enum {
- FIRST_CALL_TRANSACTION = 0x00000001,
- LAST_CALL_TRANSACTION = 0x00ffffff,
-
- PING_TRANSACTION = B_PACK_CHARS('_','P','N','G'),
- DUMP_TRANSACTION = B_PACK_CHARS('_','D','M','P'),
- INTERFACE_TRANSACTION = B_PACK_CHARS('_', 'N', 'T', 'F'),
-
- // Corresponds to tfOneWay -- an asynchronous call.
- FLAG_ONEWAY = 0x00000001
- };
-
- inline IBinder() { }
-
- /**
- * Check if this IBinder implements the interface named by
- * @a descriptor. If it does, the base pointer to it is returned,
- * which you can safely static_cast<> to the concrete C++ interface.
- */
- virtual sp<IInterface> queryLocalInterface(const String16& descriptor);
-
- /**
- * Return the canonical name of the interface provided by this IBinder
- * object.
- */
- virtual String16 getInterfaceDescriptor() const = 0;
-
- virtual bool isBinderAlive() const = 0;
- virtual status_t pingBinder() = 0;
- virtual status_t dump(int fd, const Vector<String16>& args) = 0;
-
- virtual status_t transact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0) = 0;
-
- /**
- * This method allows you to add data that is transported through
- * IPC along with your IBinder pointer. When implementing a Binder
- * object, override it to write your desired data in to @a outData.
- * You can then call getConstantData() on your IBinder to retrieve
- * that data, from any process. You MUST return the number of bytes
- * written in to the parcel (including padding).
- */
- class DeathRecipient : public virtual RefBase
- {
- public:
- virtual void binderDied(const wp<IBinder>& who) = 0;
- };
-
- /**
- * Register the @a recipient for a notification if this binder
- * goes away. If this binder object unexpectedly goes away
- * (typically because its hosting process has been killed),
- * then DeathRecipient::binderDied() will be called with a referene
- * to this.
- *
- * The @a cookie is optional -- if non-NULL, it should be a
- * memory address that you own (that is, you know it is unique).
- *
- * @note You will only receive death notifications for remote binders,
- * as local binders by definition can't die without you dying as well.
- * Trying to use this function on a local binder will result in an
- * INVALID_OPERATION code being returned and nothing happening.
- *
- * @note This link always holds a weak reference to its recipient.
- *
- * @note You will only receive a weak reference to the dead
- * binder. You should not try to promote this to a strong reference.
- * (Nor should you need to, as there is nothing useful you can
- * directly do with it now that it has passed on.)
- */
- virtual status_t linkToDeath(const sp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0) = 0;
-
- /**
- * Remove a previously registered death notification.
- * The @a recipient will no longer be called if this object
- * dies. The @a cookie is optional. If non-NULL, you can
- * supply a NULL @a recipient, and the recipient previously
- * added with that cookie will be unlinked.
- */
- virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
- void* cookie = NULL,
- uint32_t flags = 0,
- wp<DeathRecipient>* outRecipient = NULL) = 0;
-
- virtual bool checkSubclass(const void* subclassID) const;
-
- typedef void (*object_cleanup_func)(const void* id, void* obj, void* cleanupCookie);
-
- virtual void attachObject( const void* objectID,
- void* object,
- void* cleanupCookie,
- object_cleanup_func func) = 0;
- virtual void* findObject(const void* objectID) const = 0;
- virtual void detachObject(const void* objectID) = 0;
-
- virtual BBinder* localBinder();
- virtual BpBinder* remoteBinder();
-
-protected:
- inline virtual ~IBinder() { }
-
-private:
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IBINDER_H
diff --git a/include/utils/IInterface.h b/include/utils/IInterface.h
deleted file mode 100644
index 959722a..0000000
--- a/include/utils/IInterface.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-#ifndef ANDROID_IINTERFACE_H
-#define ANDROID_IINTERFACE_H
-
-#include <utils/Binder.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IInterface : public virtual RefBase
-{
-public:
- sp<IBinder> asBinder();
- sp<const IBinder> asBinder() const;
-
-protected:
- virtual IBinder* onAsBinder() = 0;
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
-{
- return INTERFACE::asInterface(obj);
-}
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BnInterface : public INTERFACE, public BBinder
-{
-public:
- virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
- virtual String16 getInterfaceDescriptor() const;
-
-protected:
- virtual IBinder* onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-template<typename INTERFACE>
-class BpInterface : public INTERFACE, public BpRefBase
-{
-public:
- BpInterface(const sp<IBinder>& remote);
-
-protected:
- virtual IBinder* onAsBinder();
-};
-
-// ----------------------------------------------------------------------
-
-#define DECLARE_META_INTERFACE(INTERFACE) \
- static const String16 descriptor; \
- static sp<I##INTERFACE> asInterface(const sp<IBinder>& obj); \
- virtual String16 getInterfaceDescriptor() const; \
-
-#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
- const String16 I##INTERFACE::descriptor(NAME); \
- String16 I##INTERFACE::getInterfaceDescriptor() const { \
- return I##INTERFACE::descriptor; \
- } \
- sp<I##INTERFACE> I##INTERFACE::asInterface(const sp<IBinder>& obj) \
- { \
- sp<I##INTERFACE> intr; \
- if (obj != NULL) { \
- intr = static_cast<I##INTERFACE*>( \
- obj->queryLocalInterface( \
- I##INTERFACE::descriptor).get()); \
- if (intr == NULL) { \
- intr = new Bp##INTERFACE(obj); \
- } \
- } \
- return intr; \
- } \
-
-// ----------------------------------------------------------------------
-// No user-servicable parts after this...
-
-template<typename INTERFACE>
-inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
- const String16& _descriptor)
-{
- if (_descriptor == INTERFACE::descriptor) return this;
- return NULL;
-}
-
-template<typename INTERFACE>
-inline String16 BnInterface<INTERFACE>::getInterfaceDescriptor() const
-{
- return INTERFACE::getInterfaceDescriptor();
-}
-
-template<typename INTERFACE>
-IBinder* BnInterface<INTERFACE>::onAsBinder()
-{
- return this;
-}
-
-template<typename INTERFACE>
-inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
- : BpRefBase(remote)
-{
-}
-
-template<typename INTERFACE>
-inline IBinder* BpInterface<INTERFACE>::onAsBinder()
-{
- return remote();
-}
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IINTERFACE_H
diff --git a/include/utils/IMemory.h b/include/utils/IMemory.h
deleted file mode 100644
index 35a3fd7..0000000
--- a/include/utils/IMemory.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_IMEMORY_H
-#define ANDROID_IMEMORY_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <utils/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------------
-
-class IMemoryHeap : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(MemoryHeap);
-
- // flags returned by getFlags()
- enum {
- READ_ONLY = 0x00000001,
- MAP_ONCE = 0x00000002
- };
-
- virtual int getHeapID() const = 0;
- virtual void* getBase() const = 0;
- virtual size_t getSize() const = 0;
- virtual uint32_t getFlags() const = 0;
-
- // these are there just for backward source compatibility
- int32_t heapID() const { return getHeapID(); }
- void* base() const { return getBase(); }
- size_t virtualSize() const { return getSize(); }
-};
-
-class BnMemoryHeap : public BnInterface<IMemoryHeap>
-{
-public:
- virtual status_t onTransact(
- uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-class IMemory : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(Memory);
-
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const = 0;
-
- // helpers
- void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const;
- void* pointer() const;
- size_t size() const;
- ssize_t offset() const;
-};
-
-class BnMemory : public BnInterface<IMemory>
-{
-public:
- virtual status_t onTransact(
- uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IMEMORY_H
diff --git a/include/utils/IPCThreadState.h b/include/utils/IPCThreadState.h
deleted file mode 100644
index 0490fd3..0000000
--- a/include/utils/IPCThreadState.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_IPC_THREAD_STATE_H
-#define ANDROID_IPC_THREAD_STATE_H
-
-#include <utils/Errors.h>
-#include <utils/Parcel.h>
-#include <utils/ProcessState.h>
-#include <utils/Vector.h>
-
-#ifdef HAVE_WIN32_PROC
-typedef int uid_t;
-#endif
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IPCThreadState
-{
-public:
- static IPCThreadState* self();
-
- sp<ProcessState> process();
-
- status_t clearLastError();
-
- int getCallingPid();
- int getCallingUid();
-
- int64_t clearCallingIdentity();
- void restoreCallingIdentity(int64_t token);
-
- void flushCommands();
-
- void joinThreadPool(bool isMain = true);
-
- // Stop the local process.
- void stopProcess(bool immediate = true);
-
- status_t transact(int32_t handle,
- uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
-
- void incStrongHandle(int32_t handle);
- void decStrongHandle(int32_t handle);
- void incWeakHandle(int32_t handle);
- void decWeakHandle(int32_t handle);
- status_t attemptIncStrongHandle(int32_t handle);
- static void expungeHandle(int32_t handle, IBinder* binder);
- status_t requestDeathNotification( int32_t handle,
- BpBinder* proxy);
- status_t clearDeathNotification( int32_t handle,
- BpBinder* proxy);
-
- static void shutdown();
-
-private:
- IPCThreadState();
- ~IPCThreadState();
-
- status_t sendReply(const Parcel& reply, uint32_t flags);
- status_t waitForResponse(Parcel *reply,
- status_t *acquireResult=NULL);
- status_t talkWithDriver(bool doReceive=true);
- status_t writeTransactionData(int32_t cmd,
- uint32_t binderFlags,
- int32_t handle,
- uint32_t code,
- const Parcel& data,
- status_t* statusBuffer);
- status_t executeCommand(int32_t command);
-
- void clearCaller();
-
- static void threadDestructor(void *st);
- static void freeBuffer(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const size_t* objects, size_t objectsSize,
- void* cookie);
-
- const sp<ProcessState> mProcess;
- Vector<BBinder*> mPendingStrongDerefs;
- Vector<RefBase::weakref_type*> mPendingWeakDerefs;
-
- Parcel mIn;
- Parcel mOut;
- status_t mLastError;
- pid_t mCallingPid;
- uid_t mCallingUid;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_IPC_THREAD_STATE_H
diff --git a/include/utils/IPermissionController.h b/include/utils/IPermissionController.h
deleted file mode 100644
index cb1dd34..0000000
--- a/include/utils/IPermissionController.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-#ifndef ANDROID_IPERMISSION_CONTROLLER_H
-#define ANDROID_IPERMISSION_CONTROLLER_H
-
-#include <utils/IInterface.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IPermissionController : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(PermissionController);
-
- virtual bool checkPermission(const String16& permission,
- int32_t pid, int32_t uid) = 0;
-
- enum {
- CHECK_PERMISSION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION
- };
-};
-
-// ----------------------------------------------------------------------
-
-class BnPermissionController : public BnInterface<IPermissionController>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_IPERMISSION_CONTROLLER_H
-
diff --git a/include/utils/IServiceManager.h b/include/utils/IServiceManager.h
deleted file mode 100644
index e3d99fe..0000000
--- a/include/utils/IServiceManager.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-#ifndef ANDROID_ISERVICE_MANAGER_H
-#define ANDROID_ISERVICE_MANAGER_H
-
-#include <utils/IInterface.h>
-#include <utils/IPermissionController.h>
-#include <utils/Vector.h>
-#include <utils/String16.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class IServiceManager : public IInterface
-{
-public:
- DECLARE_META_INTERFACE(ServiceManager);
-
- /**
- * Retrieve an existing service, blocking for a few seconds
- * if it doesn't yet exist.
- */
- virtual sp<IBinder> getService( const String16& name) const = 0;
-
- /**
- * Retrieve an existing service, non-blocking.
- */
- virtual sp<IBinder> checkService( const String16& name) const = 0;
-
- /**
- * Register a service.
- */
- virtual status_t addService( const String16& name,
- const sp<IBinder>& service) = 0;
-
- /**
- * Return list of all existing services.
- */
- virtual Vector<String16> listServices() = 0;
-
- enum {
- GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- CHECK_SERVICE_TRANSACTION,
- ADD_SERVICE_TRANSACTION,
- LIST_SERVICES_TRANSACTION,
- };
-};
-
-sp<IServiceManager> defaultServiceManager();
-
-template<typename INTERFACE>
-status_t getService(const String16& name, sp<INTERFACE>* outService)
-{
- const sp<IServiceManager> sm = defaultServiceManager();
- if (sm != NULL) {
- *outService = interface_cast<INTERFACE>(sm->getService(name));
- if ((*outService) != NULL) return NO_ERROR;
- }
- return NAME_NOT_FOUND;
-}
-
-bool checkCallingPermission(const String16& permission);
-bool checkCallingPermission(const String16& permission,
- int32_t* outPid, int32_t* outUid);
-
-// ----------------------------------------------------------------------
-
-class BnServiceManager : public BnInterface<IServiceManager>
-{
-public:
- virtual status_t onTransact( uint32_t code,
- const Parcel& data,
- Parcel* reply,
- uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_ISERVICE_MANAGER_H
-
diff --git a/include/utils/KeyedVector.h b/include/utils/KeyedVector.h
index f4513ee..6bcdea4 100644
--- a/include/utils/KeyedVector.h
+++ b/include/utils/KeyedVector.h
@@ -164,7 +164,7 @@ ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& val
template<typename KEY, typename VALUE> inline
ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
if (index<size()) {
- mVector.editValueAt(index).value = item;
+ mVector.editItemAt(index).value = item;
return index;
}
return BAD_INDEX;
diff --git a/include/utils/List.h b/include/utils/List.h
index 1a6be9a..403cd7f 100644
--- a/include/utils/List.h
+++ b/include/utils/List.h
@@ -22,147 +22,200 @@
// construction, so if the compiler's auto-generated versions won't work for
// you, define your own.
//
-// The only class you want to use from here is "List". Do not use classes
-// starting with "_" directly.
+// The only class you want to use from here is "List".
//
#ifndef _LIBS_UTILS_LIST_H
#define _LIBS_UTILS_LIST_H
-namespace android {
-
-/*
- * One element in the list.
- */
-template<class T> class _ListNode {
-public:
- typedef _ListNode<T> _Node;
-
- _ListNode(const T& val) : mVal(val) {}
- ~_ListNode(void) {}
-
- T& getRef(void) { return mVal; }
- void setVal(const T& val) { mVal = val; }
+#include <stddef.h>
+#include <stdint.h>
- _Node* getPrev(void) const { return mpPrev; }
- void setPrev(_Node* ptr) { mpPrev = ptr; }
- _Node* getNext(void) const { return mpNext; }
- void setNext(_Node* ptr) { mpNext = ptr; }
-
-private:
- T mVal;
- _Node* mpPrev;
- _Node* mpNext;
-};
+namespace android {
/*
- * Iterator for walking through the list.
+ * Doubly-linked list. Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
*/
-template<class T, class Tref> class _ListIterator {
-public:
- typedef _ListIterator<T,Tref> _Iter;
- typedef _ListNode<T> _Node;
-
- _ListIterator(void) {}
- _ListIterator(_Node* ptr) : mpNode(ptr) {}
- ~_ListIterator(void) {}
-
- /*
- * Dereference operator. Used to get at the juicy insides.
- */
- Tref operator*() const { return mpNode->getRef(); }
-
+template<typename T>
+class List
+{
+protected:
/*
- * Iterator comparison.
+ * One element in the list.
*/
- bool operator==(const _Iter& right) const { return mpNode == right.mpNode; }
- bool operator!=(const _Iter& right) const { return mpNode != right.mpNode; }
+ class _Node {
+ public:
+ explicit _Node(const T& val) : mVal(val) {}
+ ~_Node() {}
+ inline T& getRef() { return mVal; }
+ inline const T& getRef() const { return mVal; }
+ inline _Node* getPrev() const { return mpPrev; }
+ inline _Node* getNext() const { return mpNext; }
+ inline void setVal(const T& val) { mVal = val; }
+ inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+ inline void setNext(_Node* ptr) { mpNext = ptr; }
+ private:
+ friend class List;
+ friend class _ListIterator;
+ T mVal;
+ _Node* mpPrev;
+ _Node* mpNext;
+ };
/*
- * Incr/decr, used to move through the list.
+ * Iterator for walking through the list.
*/
- _Iter& operator++(void) { // pre-increment
- mpNode = mpNode->getNext();
- return *this;
- }
- _Iter operator++(int) { // post-increment
- _Iter tmp = *this;
- ++*this;
- return tmp;
- }
- _Iter& operator--(void) { // pre-increment
- mpNode = mpNode->getPrev();
- return *this;
- }
- _Iter operator--(int) { // post-increment
- _Iter tmp = *this;
- --*this;
- return tmp;
- }
+
+ template <typename TYPE>
+ struct CONST_ITERATOR {
+ typedef _Node const * NodePtr;
+ typedef const TYPE Type;
+ };
+
+ template <typename TYPE>
+ struct NON_CONST_ITERATOR {
+ typedef _Node* NodePtr;
+ typedef TYPE Type;
+ };
+
+ template<
+ typename U,
+ template <class> class Constness
+ >
+ class _ListIterator {
+ typedef _ListIterator<U, Constness> _Iter;
+ typedef typename Constness<U>::NodePtr _NodePtr;
+ typedef typename Constness<U>::Type _Type;
+
+ explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+ public:
+ _ListIterator() {}
+ _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+ ~_ListIterator() {}
+
+ // this will handle conversions from iterator to const_iterator
+ // (and also all convertible iterators)
+ // Here, in this implementation, the iterators can be converted
+ // if the nodes can be converted
+ template<typename V> explicit
+ _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+
+
+ /*
+ * Dereference operator. Used to get at the juicy insides.
+ */
+ _Type& operator*() const { return mpNode->getRef(); }
+ _Type* operator->() const { return &(mpNode->getRef()); }
+
+ /*
+ * Iterator comparison.
+ */
+ inline bool operator==(const _Iter& right) const {
+ return mpNode == right.mpNode; }
+
+ inline bool operator!=(const _Iter& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * handle comparisons between iterator and const_iterator
+ */
+ template<typename OTHER>
+ inline bool operator==(const OTHER& right) const {
+ return mpNode == right.mpNode; }
+
+ template<typename OTHER>
+ inline bool operator!=(const OTHER& right) const {
+ return mpNode != right.mpNode; }
+
+ /*
+ * Incr/decr, used to move through the list.
+ */
+ inline _Iter& operator++() { // pre-increment
+ mpNode = mpNode->getNext();
+ return *this;
+ }
+ const _Iter operator++(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getNext();
+ return tmp;
+ }
+ inline _Iter& operator--() { // pre-increment
+ mpNode = mpNode->getPrev();
+ return *this;
+ }
+ const _Iter operator--(int) { // post-increment
+ _Iter tmp(*this);
+ mpNode = mpNode->getPrev();
+ return tmp;
+ }
- _Node* getNode(void) const { return mpNode; }
+ inline _NodePtr getNode() const { return mpNode; }
-private:
- _Node* mpNode;
-};
+ _NodePtr mpNode; /* should be private, but older gcc fails */
+ private:
+ friend class List;
+ };
-
-/*
- * Doubly-linked list. Instantiate with "List<MyClass> myList".
- *
- * Objects added to the list are copied using the assignment operator,
- * so this must be defined.
- */
-template<class T> class List {
public:
- typedef _ListNode<T> _Node;
-
- List(void) {
+ List() {
prep();
}
List(const List<T>& src) { // copy-constructor
prep();
insert(begin(), src.begin(), src.end());
}
- virtual ~List(void) {
+ virtual ~List() {
clear();
delete[] (unsigned char*) mpMiddle;
}
- typedef _ListIterator<T,T&> iterator;
- typedef _ListIterator<T, const T&> const_iterator;
+ typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+ typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
List<T>& operator=(const List<T>& right);
/* returns true if the list is empty */
- bool empty(void) const { return mpMiddle->getNext() == mpMiddle; }
+ inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
/* return #of elements in list */
- unsigned int size(void) const {
- return distance(begin(), end());
+ size_t size() const {
+ return size_t(distance(begin(), end()));
}
/*
* Return the first element or one past the last element. The
- * _ListNode* we're returning is converted to an "iterator" by a
+ * _Node* we're returning is converted to an "iterator" by a
* constructor in _ListIterator.
*/
- iterator begin() { return mpMiddle->getNext(); }
- const_iterator begin() const { return mpMiddle->getNext(); }
- iterator end() { return mpMiddle; }
- const_iterator end() const { return mpMiddle; }
+ inline iterator begin() {
+ return iterator(mpMiddle->getNext());
+ }
+ inline const_iterator begin() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle->getNext()));
+ }
+ inline iterator end() {
+ return iterator(mpMiddle);
+ }
+ inline const_iterator end() const {
+ return const_iterator(const_cast<_Node const*>(mpMiddle));
+ }
/* add the object to the head or tail of the list */
void push_front(const T& val) { insert(begin(), val); }
void push_back(const T& val) { insert(end(), val); }
/* insert before the current node; returns iterator at new node */
- iterator insert(iterator posn, const T& val) {
+ iterator insert(iterator posn, const T& val)
+ {
_Node* newNode = new _Node(val); // alloc & copy-construct
newNode->setNext(posn.getNode());
newNode->setPrev(posn.getNode()->getPrev());
posn.getNode()->getPrev()->setNext(newNode);
posn.getNode()->setPrev(newNode);
- return newNode;
+ return iterator(newNode);
}
/* insert a range of elements before the current node */
@@ -178,18 +231,18 @@ public:
pPrev->setNext(pNext);
pNext->setPrev(pPrev);
delete posn.getNode();
- return pNext;
+ return iterator(pNext);
}
/* remove a range of elements */
iterator erase(iterator first, iterator last) {
while (first != last)
erase(first++); // don't erase than incr later!
- return last;
+ return iterator(last);
}
/* remove all contents of the list */
- void clear(void) {
+ void clear() {
_Node* pCurrent = mpMiddle->getNext();
_Node* pNext;
@@ -207,21 +260,20 @@ public:
* will be equal to "last". The iterators must refer to the same
* list.
*
- * (This is actually a generic iterator function. It should be part
- * of some other class, possibly an iterator base class. It needs to
- * know the difference between a list, which has to march through,
- * and a vector, which can just do pointer math.)
+ * FIXME: This is actually a generic iterator function. It should be a
+ * template function at the top-level with specializations for things like
+ * vector<>, which can just do pointer math). Here we limit it to
+ * _ListIterator of the same type but different constness.
*/
- unsigned int distance(iterator first, iterator last) {
- unsigned int count = 0;
- while (first != last) {
- ++first;
- ++count;
- }
- return count;
- }
- unsigned int distance(const_iterator first, const_iterator last) const {
- unsigned int count = 0;
+ template<
+ typename U,
+ template <class> class CL,
+ template <class> class CR
+ >
+ ptrdiff_t distance(
+ _ListIterator<U, CL> first, _ListIterator<U, CR> last) const
+ {
+ ptrdiff_t count = 0;
while (first != last) {
++first;
++count;
@@ -231,12 +283,12 @@ public:
private:
/*
- * I want a _ListNode but don't need it to hold valid data. More
+ * I want a _Node but don't need it to hold valid data. More
* to the point, I don't want T's constructor to fire, since it
* might have side-effects or require arguments. So, we do this
* slightly uncouth storage alloc.
*/
- void prep(void) {
+ void prep() {
mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
mpMiddle->setPrev(mpMiddle);
mpMiddle->setNext(mpMiddle);
diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h
deleted file mode 100644
index 01fbfb5..0000000
--- a/include/utils/LogSocket.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* utils/LogSocket.h
-**
-** Copyright 2008, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _UTILS_LOGSOCKET_H
-#define _UTILS_LOGSOCKET_H
-
-#define SOCKET_CLOSE_LOCAL 0
-
-void add_send_stats(int fd, int send);
-void add_recv_stats(int fd, int recv);
-void log_socket_close(int fd, short reason);
-void log_socket_connect(int fd, unsigned int ip, unsigned short port);
-
-#endif /* _UTILS_LOGSOCKET_H */
diff --git a/include/utils/MemoryBase.h b/include/utils/MemoryBase.h
deleted file mode 100644
index eb5a9d2..0000000
--- a/include/utils/MemoryBase.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEMORY_BASE_H
-#define ANDROID_MEMORY_BASE_H
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <utils/IMemory.h>
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class MemoryBase : public BnMemory
-{
-public:
- MemoryBase(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
- virtual ~MemoryBase();
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
-
-protected:
- size_t getSize() const { return mSize; }
- ssize_t getOffset() const { return mOffset; }
- const sp<IMemoryHeap>& getHeap() const { return mHeap; }
-
-private:
- size_t mSize;
- ssize_t mOffset;
- sp<IMemoryHeap> mHeap;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_MEMORY_BASE_H
diff --git a/include/utils/MemoryDealer.h b/include/utils/MemoryDealer.h
deleted file mode 100644
index 454b627..0000000
--- a/include/utils/MemoryDealer.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEMORY_DEALER_H
-#define ANDROID_MEMORY_DEALER_H
-
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/IMemory.h>
-#include <utils/threads.h>
-#include <utils/MemoryHeapBase.h>
-
-namespace android {
-// ----------------------------------------------------------------------------
-class String8;
-
-/*
- * interface for implementing a "heap". A heap basically provides
- * the IMemoryHeap interface for cross-process sharing and the
- * ability to map/unmap pages within the heap.
- */
-class HeapInterface : public virtual BnMemoryHeap
-{
-public:
- // all values must be page-aligned
- virtual sp<IMemory> mapMemory(size_t offset, size_t size) = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-/*
- * interface for implementing an allocator. An allocator provides
- * methods for allocating and freeing memory blocks and dumping
- * its state.
- */
-class AllocatorInterface : public RefBase
-{
-public:
- enum {
- PAGE_ALIGNED = 0x00000001
- };
-
- virtual size_t allocate(size_t size, uint32_t flags = 0) = 0;
- virtual status_t deallocate(size_t offset) = 0;
- virtual size_t size() const = 0;
- virtual void dump(const char* what, uint32_t flags = 0) const = 0;
- virtual void dump(String8& res,
- const char* what, uint32_t flags = 0) const = 0;
-};
-
-// ----------------------------------------------------------------------------
-
-/*
- * concrete implementation of HeapInterface on top of mmap()
- */
-class SharedHeap : public HeapInterface, public MemoryHeapBase
-{
-public:
- SharedHeap(size_t size, uint32_t flags = 0, char const * name = NULL);
- virtual ~SharedHeap();
- virtual sp<IMemory> mapMemory(size_t offset, size_t size);
-};
-
-// ----------------------------------------------------------------------------
-
-/*
- * A simple templatized doubly linked-list implementation
- */
-
-template <typename NODE>
-class LinkedList
-{
- NODE* mFirst;
- NODE* mLast;
-
-public:
- LinkedList() : mFirst(0), mLast(0) { }
- bool isEmpty() const { return mFirst == 0; }
- NODE const* head() const { return mFirst; }
- NODE* head() { return mFirst; }
- NODE const* tail() const { return mLast; }
- NODE* tail() { return mLast; }
-
- void insertAfter(NODE* node, NODE* newNode) {
- newNode->prev = node;
- newNode->next = node->next;
- if (node->next == 0) mLast = newNode;
- else node->next->prev = newNode;
- node->next = newNode;
- }
-
- void insertBefore(NODE* node, NODE* newNode) {
- newNode->prev = node->prev;
- newNode->next = node;
- if (node->prev == 0) mFirst = newNode;
- else node->prev->next = newNode;
- node->prev = newNode;
- }
-
- void insertHead(NODE* newNode) {
- if (mFirst == 0) {
- mFirst = mLast = newNode;
- newNode->prev = newNode->next = 0;
- } else {
- insertBefore(mFirst, newNode);
- }
- }
-
- void insertTail(NODE* newNode) {
- if (mLast == 0) insertBeginning(newNode);
- else insertAfter(mLast, newNode);
- }
-
- NODE* remove(NODE* node) {
- if (node->prev == 0) mFirst = node->next;
- else node->prev->next = node->next;
- if (node->next == 0) mLast = node->prev;
- else node->next->prev = node->prev;
- return node;
- }
-};
-
-
-/*
- * concrete implementation of AllocatorInterface using a simple
- * best-fit allocation scheme
- */
-class SimpleBestFitAllocator : public AllocatorInterface
-{
-public:
-
- SimpleBestFitAllocator(size_t size);
- virtual ~SimpleBestFitAllocator();
-
- virtual size_t allocate(size_t size, uint32_t flags = 0);
- virtual status_t deallocate(size_t offset);
- virtual size_t size() const;
- virtual void dump(const char* what, uint32_t flags = 0) const;
- virtual void dump(String8& res,
- const char* what, uint32_t flags = 0) const;
-
-private:
-
- struct chunk_t {
- chunk_t(size_t start, size_t size)
- : start(start), size(size), free(1), prev(0), next(0) {
- }
- size_t start;
- size_t size : 28;
- int free : 4;
- mutable chunk_t* prev;
- mutable chunk_t* next;
- };
-
- ssize_t alloc(size_t size, uint32_t flags);
- chunk_t* dealloc(size_t start);
- void dump_l(const char* what, uint32_t flags = 0) const;
- void dump_l(String8& res, const char* what, uint32_t flags = 0) const;
-
- static const int kMemoryAlign;
- mutable Mutex mLock;
- LinkedList<chunk_t> mList;
- size_t mHeapSize;
-};
-
-// ----------------------------------------------------------------------------
-
-class MemoryDealer : public RefBase
-{
-public:
-
- enum {
- READ_ONLY = MemoryHeapBase::READ_ONLY,
- PAGE_ALIGNED = AllocatorInterface::PAGE_ALIGNED
- };
-
- // creates a memory dealer with the SharedHeap and SimpleBestFitAllocator
- MemoryDealer(size_t size, uint32_t flags = 0, const char* name = 0);
-
- // provide a custom heap but use the SimpleBestFitAllocator
- MemoryDealer(const sp<HeapInterface>& heap);
-
- // provide both custom heap and allocotar
- MemoryDealer(
- const sp<HeapInterface>& heap,
- const sp<AllocatorInterface>& allocator);
-
- virtual ~MemoryDealer();
-
- virtual sp<IMemory> allocate(size_t size, uint32_t flags = 0);
- virtual void deallocate(size_t offset);
- virtual void dump(const char* what, uint32_t flags = 0) const;
-
-
- sp<IMemoryHeap> getMemoryHeap() const { return heap(); }
- sp<AllocatorInterface> getAllocator() const { return allocator(); }
-
-private:
- const sp<HeapInterface>& heap() const;
- const sp<AllocatorInterface>& allocator() const;
-
- class Allocation : public BnMemory {
- public:
- Allocation(const sp<MemoryDealer>& dealer,
- ssize_t offset, size_t size, const sp<IMemory>& memory);
- virtual ~Allocation();
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
- private:
- sp<MemoryDealer> mDealer;
- ssize_t mOffset;
- size_t mSize;
- sp<IMemory> mMemory;
- };
-
- sp<HeapInterface> mHeap;
- sp<AllocatorInterface> mAllocator;
-};
-
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_MEMORY_DEALER_H
diff --git a/include/utils/MemoryHeapBase.h b/include/utils/MemoryHeapBase.h
deleted file mode 100644
index 574acf4..0000000
--- a/include/utils/MemoryHeapBase.h
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEMORY_HEAP_BASE_H
-#define ANDROID_MEMORY_HEAP_BASE_H
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <utils/IMemory.h>
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class MemoryHeapBase : public virtual BnMemoryHeap
-{
-public:
- enum {
- READ_ONLY = IMemoryHeap::READ_ONLY,
- MAP_ONCE = IMemoryHeap::MAP_ONCE,
- // memory won't be mapped locally, but will be mapped in the remote
- // process.
- DONT_MAP_LOCALLY = 0x00000100
- };
-
- /*
- * maps the memory referenced by fd. but DOESN'T take ownership
- * of the filedescriptor (it makes a copy with dup()
- */
- MemoryHeapBase(int fd, size_t size, uint32_t flags = 0);
-
- /*
- * maps memory from the given device
- */
- MemoryHeapBase(const char* device, size_t size = 0, uint32_t flags = 0);
-
- /*
- * maps memory from ashmem, with the given name for debugging
- */
- MemoryHeapBase(size_t size, uint32_t flags = 0, char const* name = NULL);
-
- virtual ~MemoryHeapBase();
-
- /* implement IMemoryHeap interface */
- virtual int getHeapID() const;
- virtual void* getBase() const;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
-
- const char* getDevice() const;
-
- /* this closes this heap -- use carefully */
- void dispose();
-
- /* this is only needed as a workaround, use only if you know
- * what you are doing */
- status_t setDevice(const char* device) {
- if (mDevice == 0)
- mDevice = device;
- return mDevice ? NO_ERROR : ALREADY_EXISTS;
- }
-
-protected:
- MemoryHeapBase();
- // init() takes ownership of fd
- status_t init(int fd, void *base, int size,
- int flags = 0, const char* device = NULL);
-
-private:
- status_t mapfd(int fd, size_t size);
-
- int mFD;
- size_t mSize;
- void* mBase;
- uint32_t mFlags;
- const char* mDevice;
- bool mNeedUnmap;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_MEMORY_HEAP_BASE_H
diff --git a/include/utils/MemoryHeapPmem.h b/include/utils/MemoryHeapPmem.h
deleted file mode 100644
index 60335ad..0000000
--- a/include/utils/MemoryHeapPmem.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_MEMORY_HEAP_PMEM_H
-#define ANDROID_MEMORY_HEAP_PMEM_H
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/IMemory.h>
-#include <utils/SortedVector.h>
-
-namespace android {
-
-class MemoryHeapBase;
-
-// ---------------------------------------------------------------------------
-
-class MemoryHeapPmem : public HeapInterface, public MemoryHeapBase
-{
-public:
- class MemoryPmem : public BnMemory {
- public:
- MemoryPmem(const sp<MemoryHeapPmem>& heap);
- ~MemoryPmem();
- protected:
- const sp<MemoryHeapPmem>& getHeap() const { return mClientHeap; }
- private:
- friend class MemoryHeapPmem;
- virtual void revoke() = 0;
- sp<MemoryHeapPmem> mClientHeap;
- };
-
- MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
- uint32_t flags = IMemoryHeap::MAP_ONCE);
- ~MemoryHeapPmem();
-
- /* HeapInterface additions */
- virtual sp<IMemory> mapMemory(size_t offset, size_t size);
-
- /* make the whole heap visible (you know who you are) */
- virtual status_t slap();
-
- /* hide (revoke) the whole heap (the client will see the garbage page) */
- virtual status_t unslap();
-
- /* revoke all allocations made by this heap */
- virtual void revoke();
-
-private:
- /* use this to create your own IMemory for mapMemory */
- virtual sp<MemoryPmem> createMemory(size_t offset, size_t size);
- void remove(const wp<MemoryPmem>& memory);
-
-private:
- sp<MemoryHeapBase> mParentHeap;
- mutable Mutex mLock;
- SortedVector< wp<MemoryPmem> > mAllocations;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_MEMORY_HEAP_PMEM_H
diff --git a/include/utils/Parcel.h b/include/utils/Parcel.h
deleted file mode 100644
index af1490a..0000000
--- a/include/utils/Parcel.h
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_PARCEL_H
-#define ANDROID_PARCEL_H
-
-#include <cutils/native_handle.h>
-#include <utils/Errors.h>
-#include <utils/RefBase.h>
-#include <utils/String16.h>
-#include <utils/Vector.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-class IBinder;
-class ProcessState;
-class String8;
-class TextOutput;
-
-struct flat_binder_object; // defined in support_p/binder_module.h
-
-class Parcel
-{
-public:
- Parcel();
- ~Parcel();
-
- const uint8_t* data() const;
- size_t dataSize() const;
- size_t dataAvail() const;
- size_t dataPosition() const;
- size_t dataCapacity() const;
-
- status_t setDataSize(size_t size);
- void setDataPosition(size_t pos) const;
- status_t setDataCapacity(size_t size);
-
- status_t setData(const uint8_t* buffer, size_t len);
-
- status_t appendFrom(Parcel *parcel, size_t start, size_t len);
-
- bool hasFileDescriptors() const;
-
- status_t writeInterfaceToken(const String16& interface);
- bool enforceInterface(const String16& interface) const;
-
- void freeData();
-
- const size_t* objects() const;
- size_t objectsCount() const;
-
- status_t errorCheck() const;
- void setError(status_t err);
-
- status_t write(const void* data, size_t len);
- void* writeInplace(size_t len);
- status_t writeUnpadded(const void* data, size_t len);
- status_t writeInt32(int32_t val);
- status_t writeInt64(int64_t val);
- status_t writeFloat(float val);
- status_t writeDouble(double val);
- status_t writeCString(const char* str);
- status_t writeString8(const String8& str);
- status_t writeString16(const String16& str);
- status_t writeString16(const char16_t* str, size_t len);
- status_t writeStrongBinder(const sp<IBinder>& val);
- status_t writeWeakBinder(const wp<IBinder>& val);
-
- // Place a native_handle into the parcel (the native_handle's file-
- // descriptors are dup'ed, so it is safe to delete the native_handle
- // when this function returns).
- // Doesn't take ownership of the native_handle.
- status_t writeNativeHandle(const native_handle* handle);
-
- // Place a file descriptor into the parcel. The given fd must remain
- // valid for the lifetime of the parcel.
- status_t writeFileDescriptor(int fd);
-
- // Place a file descriptor into the parcel. A dup of the fd is made, which
- // will be closed once the parcel is destroyed.
- status_t writeDupFileDescriptor(int fd);
-
- status_t writeObject(const flat_binder_object& val, bool nullMetaData);
-
- void remove(size_t start, size_t amt);
-
- status_t read(void* outData, size_t len) const;
- const void* readInplace(size_t len) const;
- int32_t readInt32() const;
- status_t readInt32(int32_t *pArg) const;
- int64_t readInt64() const;
- status_t readInt64(int64_t *pArg) const;
- float readFloat() const;
- status_t readFloat(float *pArg) const;
- double readDouble() const;
- status_t readDouble(double *pArg) const;
-
- const char* readCString() const;
- String8 readString8() const;
- String16 readString16() const;
- const char16_t* readString16Inplace(size_t* outLen) const;
- sp<IBinder> readStrongBinder() const;
- wp<IBinder> readWeakBinder() const;
-
-
- // Retrieve native_handle from the parcel. This returns a copy of the
- // parcel's native_handle (the caller takes ownership). The caller
- // must free the native_handle with native_handle_close() and
- // native_handle_delete().
- native_handle* readNativeHandle() const;
-
-
- // Retrieve a file descriptor from the parcel. This returns the raw fd
- // in the parcel, which you do not own -- use dup() to get your own copy.
- int readFileDescriptor() const;
-
- const flat_binder_object* readObject(bool nullMetaData) const;
-
- // Explicitly close all file descriptors in the parcel.
- void closeFileDescriptors();
-
- typedef void (*release_func)(Parcel* parcel,
- const uint8_t* data, size_t dataSize,
- const size_t* objects, size_t objectsSize,
- void* cookie);
-
- const uint8_t* ipcData() const;
- size_t ipcDataSize() const;
- const size_t* ipcObjects() const;
- size_t ipcObjectsCount() const;
- void ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const size_t* objects, size_t objectsCount,
- release_func relFunc, void* relCookie);
-
- void print(TextOutput& to, uint32_t flags = 0) const;
-
-private:
- Parcel(const Parcel& o);
- Parcel& operator=(const Parcel& o);
-
- status_t finishWrite(size_t len);
- void releaseObjects();
- void acquireObjects();
- status_t growData(size_t len);
- status_t restartWrite(size_t desired);
- status_t continueWrite(size_t desired);
- void freeDataNoInit();
- void initState();
- void scanForFds() const;
-
- status_t mError;
- uint8_t* mData;
- size_t mDataSize;
- size_t mDataCapacity;
- mutable size_t mDataPos;
- size_t* mObjects;
- size_t mObjectsSize;
- size_t mObjectsCapacity;
- mutable size_t mNextObjectHint;
-
- mutable bool mFdsKnown;
- mutable bool mHasFds;
-
- release_func mOwner;
- void* mOwnerCookie;
-};
-
-// ---------------------------------------------------------------------------
-
-inline TextOutput& operator<<(TextOutput& to, const Parcel& parcel)
-{
- parcel.print(to);
- return to;
-}
-
-// ---------------------------------------------------------------------------
-
-// Generic acquire and release of objects.
-void acquire_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who);
-void release_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who);
-
-void flatten_binder(const sp<ProcessState>& proc,
- const sp<IBinder>& binder, flat_binder_object* out);
-void flatten_binder(const sp<ProcessState>& proc,
- const wp<IBinder>& binder, flat_binder_object* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const flat_binder_object& flat, sp<IBinder>* out);
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const flat_binder_object& flat, wp<IBinder>* out);
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PARCEL_H
diff --git a/include/utils/Pipe.h b/include/utils/Pipe.h
deleted file mode 100644
index 6404168..0000000
--- a/include/utils/Pipe.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// FIFO I/O.
-//
-#ifndef _LIBS_UTILS_PIPE_H
-#define _LIBS_UTILS_PIPE_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-namespace android {
-
-/*
- * Simple anonymous unidirectional pipe.
- *
- * The primary goal is to create an implementation with minimal overhead
- * under Linux. Making Windows, Mac OS X, and Linux all work the same way
- * is a secondary goal. Part of this goal is to have something that can
- * be fed to a select() call, so that the application can sleep in the
- * kernel until something interesting happens.
- */
-class Pipe {
-public:
- Pipe(void);
- virtual ~Pipe(void);
-
- /* Create the pipe */
- bool create(void);
-
- /* Create a read-only pipe, using the supplied handle as read handle */
- bool createReader(unsigned long handle);
- /* Create a write-only pipe, using the supplied handle as write handle */
- bool createWriter(unsigned long handle);
-
- /* Is this object ready to go? */
- bool isCreated(void);
-
- /*
- * Read "count" bytes from the pipe. Returns the amount of data read,
- * or 0 if no data available and we're non-blocking.
- * Returns -1 on error.
- */
- int read(void* buf, int count);
-
- /*
- * Write "count" bytes into the pipe. Returns number of bytes written,
- * or 0 if there's no room for more data and we're non-blocking.
- * Returns -1 on error.
- */
- int write(const void* buf, int count);
-
- /* Returns "true" if data is available to read */
- bool readReady(void);
-
- /* Enable or disable non-blocking I/O for reads */
- bool setReadNonBlocking(bool val);
- /* Enable or disable non-blocking I/O for writes. Only works on Linux. */
- bool setWriteNonBlocking(bool val);
-
- /*
- * Get the handle. Only useful in some platform-specific situations.
- */
- unsigned long getReadHandle(void);
- unsigned long getWriteHandle(void);
-
- /*
- * Modify inheritance, i.e. whether or not a child process will get
- * copies of the descriptors. Systems with fork+exec allow us to close
- * the descriptors before launching the child process, but Win32
- * doesn't allow it.
- */
- bool disallowReadInherit(void);
- bool disallowWriteInherit(void);
-
- /*
- * Close one side or the other. Useful in the parent after launching
- * a child process.
- */
- bool closeRead(void);
- bool closeWrite(void);
-
-private:
- bool mReadNonBlocking;
- bool mWriteNonBlocking;
-
- unsigned long mReadHandle;
- unsigned long mWriteHandle;
-};
-
-}; // android
-
-#endif // _LIBS_UTILS_PIPE_H
diff --git a/include/utils/ProcessState.h b/include/utils/ProcessState.h
deleted file mode 100644
index 39584f4..0000000
--- a/include/utils/ProcessState.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#ifndef ANDROID_PROCESS_STATE_H
-#define ANDROID_PROCESS_STATE_H
-
-#include <utils/IBinder.h>
-#include <utils/KeyedVector.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-
-#include <utils/threads.h>
-
-// ---------------------------------------------------------------------------
-namespace android {
-
-// Global variables
-extern int mArgC;
-extern const char* const* mArgV;
-extern int mArgLen;
-
-class IPCThreadState;
-
-class ProcessState : public virtual RefBase
-{
-public:
- static sp<ProcessState> self();
-
- static void setSingleProcess(bool singleProcess);
-
- void setContextObject(const sp<IBinder>& object);
- sp<IBinder> getContextObject(const sp<IBinder>& caller);
-
- void setContextObject(const sp<IBinder>& object,
- const String16& name);
- sp<IBinder> getContextObject(const String16& name,
- const sp<IBinder>& caller);
-
- bool supportsProcesses() const;
-
- void startThreadPool();
-
- typedef bool (*context_check_func)(const String16& name,
- const sp<IBinder>& caller,
- void* userData);
-
- bool isContextManager(void) const;
- bool becomeContextManager(
- context_check_func checkFunc,
- void* userData);
-
- sp<IBinder> getStrongProxyForHandle(int32_t handle);
- wp<IBinder> getWeakProxyForHandle(int32_t handle);
- void expungeHandle(int32_t handle, IBinder* binder);
-
- void setArgs(int argc, const char* const argv[]);
- int getArgC() const;
- const char* const* getArgV() const;
-
- void setArgV0(const char* txt);
-
- void spawnPooledThread(bool isMain);
-
-private:
- friend class IPCThreadState;
-
- ProcessState();
- ~ProcessState();
-
- ProcessState(const ProcessState& o);
- ProcessState& operator=(const ProcessState& o);
-
- struct handle_entry {
- IBinder* binder;
- RefBase::weakref_type* refs;
- };
-
- handle_entry* lookupHandleLocked(int32_t handle);
-
- int mDriverFD;
- void* mVMStart;
-
- mutable Mutex mLock; // protects everything below.
-
- Vector<handle_entry>mHandleToObject;
-
- bool mManagesContexts;
- context_check_func mBinderContextCheckFunc;
- void* mBinderContextUserData;
-
- KeyedVector<String16, sp<IBinder> >
- mContexts;
-
-
- String8 mRootDir;
- bool mThreadPoolStarted;
- volatile int32_t mThreadPoolSeq;
-};
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PROCESS_STATE_H
diff --git a/include/utils/RefBase.h b/include/utils/RefBase.h
index cbda0fd..bd7f28c 100644
--- a/include/utils/RefBase.h
+++ b/include/utils/RefBase.h
@@ -156,6 +156,10 @@ public:
delete static_cast<const T*>(this);
}
}
+ //! DEBUGGING ONLY: Get current strong ref count.
+ inline int32_t getStrongCount() const {
+ return mCount;
+ }
protected:
inline ~LightRefBase() { }
diff --git a/include/utils/Singleton.h b/include/utils/Singleton.h
new file mode 100644
index 0000000..bc7626a8
--- /dev/null
+++ b/include/utils/Singleton.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_SINGLETON_H
+#define ANDROID_UTILS_SINGLETON_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/threads.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+template <typename TYPE>
+class Singleton
+{
+public:
+ static TYPE& getInstance() {
+ Mutex::Autolock _l(sLock);
+ TYPE* instance = sInstance;
+ if (instance == 0) {
+ instance = new TYPE();
+ sInstance = instance;
+ }
+ return *instance;
+ }
+
+protected:
+ ~Singleton() { };
+ Singleton() { };
+
+private:
+ Singleton(const Singleton&);
+ Singleton& operator = (const Singleton&);
+ static Mutex sLock;
+ static TYPE* sInstance;
+};
+
+/*
+ * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file
+ * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,
+ * and avoid to have a copy of them in each compilation units Singleton<TYPE>
+ * is used.
+ */
+
+#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) \
+ template class Singleton< TYPE >; \
+ template< class TYPE > Mutex Singleton< TYPE >::sLock; \
+ template<> TYPE* Singleton< TYPE >::sInstance(0);
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_UTILS_SINGLETON_H
+
diff --git a/include/utils/Socket.h b/include/utils/Socket.h
deleted file mode 100644
index 8b7f406..0000000
--- a/include/utils/Socket.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Socket class. Modeled after Java classes.
-//
-#ifndef _RUNTIME_SOCKET_H
-#define _RUNTIME_SOCKET_H
-
-#include <utils/inet_address.h>
-#include <sys/types.h>
-
-namespace android {
-
-/*
- * Basic socket class, needed to abstract away the differences between
- * BSD sockets and WinSock. This establishes a streaming network
- * connection (TCP/IP) to somebody.
- */
-class Socket {
-public:
- Socket(void);
- ~Socket(void);
-
- // Create a connection to somewhere.
- // Return 0 on success.
- int connect(const char* host, int port);
- int connect(const InetAddress* addr, int port);
-
-
- // Close the socket. Don't try to use this object again after
- // calling this. Returns false on failure.
- bool close(void);
-
- // If we created the socket without an address, we can use these
- // to finish the connection. Returns 0 on success.
- int bind(const SocketAddress& bindPoint);
- int connect(const SocketAddress& endPoint);
-
- // Here we deviate from the traditional object-oriented fanciness
- // and just provide read/write operators instead of getters for
- // objects that abstract a stream.
- //
- // Standard read/write semantics.
- int read(void* buf, ssize_t len) const;
- int write(const void* buf, ssize_t len) const;
-
- // This must be called once, at program startup.
- static bool bootInit(void);
- static void finalShutdown(void);
-
-private:
- // Internal function that establishes a connection.
- int doConnect(const InetSocketAddress& addr);
-
- unsigned long mSock; // holds SOCKET or int
-
- static bool mBootInitialized;
-};
-
-
-// debug -- unit tests
-void TestSockets(void);
-
-}; // namespace android
-
-#endif // _RUNTIME_SOCKET_H
diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h
index c8a6153..8beec57 100644
--- a/include/utils/SortedVector.h
+++ b/include/utils/SortedVector.h
@@ -141,8 +141,7 @@ SortedVector<TYPE>::SortedVector()
: SortedVectorImpl(sizeof(TYPE),
((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
|(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
)
{
}
diff --git a/include/utils/StringArray.h b/include/utils/StringArray.h
new file mode 100644
index 0000000..c244587
--- /dev/null
+++ b/include/utils/StringArray.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings. STL-ish, but STL-free.
+//
+#ifndef _LIBS_UTILS_STRING_ARRAY_H
+#define _LIBS_UTILS_STRING_ARRAY_H
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+//
+// An expanding array of strings. Add, get, sort, delete.
+//
+class StringArray {
+public:
+ StringArray();
+ virtual ~StringArray();
+
+ //
+ // Add a string. A copy of the string is made.
+ //
+ bool push_back(const char* str);
+
+ //
+ // Delete an entry.
+ //
+ void erase(int idx);
+
+ //
+ // Sort the array.
+ //
+ void sort(int (*compare)(const void*, const void*));
+
+ //
+ // Pass this to the sort routine to do an ascending alphabetical sort.
+ //
+ static int cmpAscendingAlpha(const void* pstr1, const void* pstr2);
+
+ //
+ // Get the #of items in the array.
+ //
+ inline int size(void) const { return mCurrent; }
+
+ //
+ // Return entry N.
+ // [should use operator[] here]
+ //
+ const char* getEntry(int idx) const {
+ return (unsigned(idx) >= unsigned(mCurrent)) ? NULL : mArray[idx];
+ }
+
+ //
+ // Set entry N to specified string.
+ // [should use operator[] here]
+ //
+ void setEntry(int idx, const char* str);
+
+private:
+ int mMax;
+ int mCurrent;
+ char** mArray;
+};
+
+}; // namespace android
+
+#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/TextOutput.h b/include/utils/TextOutput.h
index d8d86ba..de2fbbe 100644
--- a/include/utils/TextOutput.h
+++ b/include/utils/TextOutput.h
@@ -28,8 +28,8 @@ namespace android {
class TextOutput
{
public:
- TextOutput() { }
- virtual ~TextOutput() { }
+ TextOutput();
+ virtual ~TextOutput();
virtual status_t print(const char* txt, size_t len) = 0;
virtual void moveIndent(int delta) = 0;
diff --git a/include/utils/TimerProbe.h b/include/utils/TimerProbe.h
deleted file mode 100644
index f2e32b2..0000000
--- a/include/utils/TimerProbe.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TIMER_PROBE_H
-#define ANDROID_TIMER_PROBE_H
-
-#if 0 && defined(HAVE_POSIX_CLOCKS)
-#define ENABLE_TIMER_PROBE 1
-#else
-#define ENABLE_TIMER_PROBE 0
-#endif
-
-#if ENABLE_TIMER_PROBE
-
-#include <time.h>
-#include <sys/time.h>
-#include <utils/Vector.h>
-
-#define TIMER_PROBE(tag) \
- static int _timer_slot_; \
- android::TimerProbe probe(tag, &_timer_slot_)
-#define TIMER_PROBE_END() probe.end()
-#else
-#define TIMER_PROBE(tag)
-#define TIMER_PROBE_END()
-#endif
-
-#if ENABLE_TIMER_PROBE
-namespace android {
-
-class TimerProbe {
-public:
- TimerProbe(const char tag[], int* slot);
- void end();
- ~TimerProbe();
-private:
- struct Bucket {
- int mStart, mReal, mProcess, mThread, mCount;
- const char* mTag;
- int* mSlotPtr;
- int mIndent;
- };
- static Vector<Bucket> gBuckets;
- static TimerProbe* gExecuteChain;
- static int gIndent;
- static timespec gRealBase;
- TimerProbe* mNext;
- static uint32_t ElapsedTime(const timespec& start, const timespec& end);
- void print(const timespec& r, const timespec& p, const timespec& t) const;
- timespec mRealStart, mPStart, mTStart;
- const char* mTag;
- int mIndent;
- int mBucket;
-};
-
-}; // namespace android
-
-#endif
-#endif
diff --git a/include/utils/Timers.h b/include/utils/Timers.h
index 9610399..9a9e07c 100644
--- a/include/utils/Timers.h
+++ b/include/utils/Timers.h
@@ -88,9 +88,6 @@ nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);
nsecs_t systemTime(int clock);
#endif // def __cplusplus
-// return the system-time according to the specified clock
-int sleepForInterval(long interval, struct timeval* pNextTick);
-
#ifdef __cplusplus
} // extern "C"
#endif
@@ -108,15 +105,15 @@ namespace android {
*/
class DurationTimer {
public:
- DurationTimer(void) {}
- ~DurationTimer(void) {}
+ DurationTimer() {}
+ ~DurationTimer() {}
// Start the timer.
- void start(void);
+ void start();
// Stop the timer.
- void stop(void);
+ void stop();
// Get the duration in microseconds.
- long long durationUsecs(void) const;
+ long long durationUsecs() const;
// Subtract two timevals. Returns the difference (ptv1-ptv2) in
// microseconds.
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
index c04c37f..2ff2749 100644
--- a/include/utils/TypeHelpers.h
+++ b/include/utils/TypeHelpers.h
@@ -29,35 +29,39 @@ namespace android {
/*
* Types traits
*/
-
-template <typename T> struct trait_trivial_ctor { enum { value = false }; };
-template <typename T> struct trait_trivial_dtor { enum { value = false }; };
-template <typename T> struct trait_trivial_copy { enum { value = false }; };
-template <typename T> struct trait_trivial_assign{ enum { value = false }; };
-
-template <typename T> struct trait_pointer { enum { value = false }; };
-template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-
-#define ANDROID_BASIC_TYPES_TRAITS( T ) \
- template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = true }; };
-
-#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign ) \
- template<> struct trait_trivial_ctor< T > { enum { value = ctor }; }; \
- template<> struct trait_trivial_dtor< T > { enum { value = dtor }; }; \
- template<> struct trait_trivial_copy< T > { enum { value = copy }; }; \
- template<> struct trait_trivial_assign< T >{ enum { value = assign }; };
+
+template <typename T> struct trait_trivial_ctor { enum { value = false }; };
+template <typename T> struct trait_trivial_dtor { enum { value = false }; };
+template <typename T> struct trait_trivial_copy { enum { value = false }; };
+template <typename T> struct trait_trivial_move { enum { value = false }; };
+template <typename T> struct trait_pointer { enum { value = false }; };
+template <typename T> struct trait_pointer<T*> { enum { value = true }; };
+
+// sp<> can be trivially moved
+template <typename T> class sp;
+template <typename T> struct trait_trivial_move< sp<T> >{
+ enum { value = true };
+};
+
+// wp<> can be trivially moved
+template <typename T> class wp;
+template <typename T> struct trait_trivial_move< wp<T> >{
+ enum { value = true };
+};
template <typename TYPE>
struct traits {
enum {
+ // whether this type is a pointer
is_pointer = trait_pointer<TYPE>::value,
+ // whether this type's constructor is a no-op
has_trivial_ctor = is_pointer || trait_trivial_ctor<TYPE>::value,
+ // whether this type's destructor is a no-op
has_trivial_dtor = is_pointer || trait_trivial_dtor<TYPE>::value,
+ // whether this type type can be copy-constructed with memcpy
has_trivial_copy = is_pointer || trait_trivial_copy<TYPE>::value,
- has_trivial_assign = is_pointer || trait_trivial_assign<TYPE>::value
+ // whether this type can be moved with memmove
+ has_trivial_move = is_pointer || trait_trivial_move<TYPE>::value
};
};
@@ -65,37 +69,47 @@ template <typename T, typename U>
struct aggregate_traits {
enum {
is_pointer = false,
- has_trivial_ctor = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
- has_trivial_dtor = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
- has_trivial_copy = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
- has_trivial_assign = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
+ has_trivial_ctor =
+ traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
+ has_trivial_dtor =
+ traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
+ has_trivial_copy =
+ traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
+ has_trivial_move =
+ traits<T>::has_trivial_move && traits<U>::has_trivial_move
};
};
+#define ANDROID_BASIC_TYPES_TRAITS( T ) \
+ template<> struct trait_trivial_ctor< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_dtor< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_copy< T > { enum { value = true }; }; \
+ template<> struct trait_trivial_move< T > { enum { value = true }; };
+
// ---------------------------------------------------------------------------
/*
* basic types traits
*/
-
-ANDROID_BASIC_TYPES_TRAITS( void );
-ANDROID_BASIC_TYPES_TRAITS( bool );
-ANDROID_BASIC_TYPES_TRAITS( char );
-ANDROID_BASIC_TYPES_TRAITS( unsigned char );
-ANDROID_BASIC_TYPES_TRAITS( short );
-ANDROID_BASIC_TYPES_TRAITS( unsigned short );
-ANDROID_BASIC_TYPES_TRAITS( int );
-ANDROID_BASIC_TYPES_TRAITS( unsigned int );
-ANDROID_BASIC_TYPES_TRAITS( long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long );
-ANDROID_BASIC_TYPES_TRAITS( long long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
-ANDROID_BASIC_TYPES_TRAITS( float );
-ANDROID_BASIC_TYPES_TRAITS( double );
+
+ANDROID_BASIC_TYPES_TRAITS( void )
+ANDROID_BASIC_TYPES_TRAITS( bool )
+ANDROID_BASIC_TYPES_TRAITS( char )
+ANDROID_BASIC_TYPES_TRAITS( unsigned char )
+ANDROID_BASIC_TYPES_TRAITS( short )
+ANDROID_BASIC_TYPES_TRAITS( unsigned short )
+ANDROID_BASIC_TYPES_TRAITS( int )
+ANDROID_BASIC_TYPES_TRAITS( unsigned int )
+ANDROID_BASIC_TYPES_TRAITS( long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long )
+ANDROID_BASIC_TYPES_TRAITS( long long )
+ANDROID_BASIC_TYPES_TRAITS( unsigned long long )
+ANDROID_BASIC_TYPES_TRAITS( float )
+ANDROID_BASIC_TYPES_TRAITS( double )
// ---------------------------------------------------------------------------
-
+
/*
* compare and order types
*/
@@ -111,9 +125,9 @@ int compare_type(const TYPE& lhs, const TYPE& rhs) {
}
/*
- * create, destroy, copy and assign types...
+ * create, destroy, copy and move types...
*/
-
+
template<typename TYPE> inline
void construct_type(TYPE* p, size_t n) {
if (!traits<TYPE>::has_trivial_ctor) {
@@ -146,17 +160,6 @@ void copy_type(TYPE* d, const TYPE* s, size_t n) {
}
template<typename TYPE> inline
-void assign_type(TYPE* d, const TYPE* s, size_t n) {
- if (!traits<TYPE>::has_trivial_assign) {
- while (n--) {
- *d++ = *s++;
- }
- } else {
- memcpy(d,s,n*sizeof(TYPE));
- }
-}
-
-template<typename TYPE> inline
void splat_type(TYPE* where, const TYPE* what, size_t n) {
if (!traits<TYPE>::has_trivial_copy) {
while (n--) {
@@ -164,15 +167,19 @@ void splat_type(TYPE* where, const TYPE* what, size_t n) {
where++;
}
} else {
- while (n--) {
- *where++ = *what;
+ while (n--) {
+ *where++ = *what;
}
}
}
template<typename TYPE> inline
void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
d += n;
s += n;
while (n--) {
@@ -180,35 +187,37 @@ void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
if (!traits<TYPE>::has_trivial_copy) {
new(d) TYPE(*s);
} else {
- *d = *s;
+ *d = *s;
}
if (!traits<TYPE>::has_trivial_dtor) {
s->~TYPE();
}
}
- } else {
- memmove(d,s,n*sizeof(TYPE));
}
}
template<typename TYPE> inline
void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
- if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
+ if ((traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)
+ || traits<TYPE>::has_trivial_move)
+ {
+ memmove(d,s,n*sizeof(TYPE));
+ } else {
while (n--) {
if (!traits<TYPE>::has_trivial_copy) {
new(d) TYPE(*s);
} else {
- *d = *s;
+ *d = *s;
}
if (!traits<TYPE>::has_trivial_dtor) {
s->~TYPE();
}
d++, s++;
}
- } else {
- memmove(d,s,n*sizeof(TYPE));
}
}
+
+
// ---------------------------------------------------------------------------
/*
@@ -242,8 +251,8 @@ struct trait_trivial_copy< key_value_pair_t<K, V> >
{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
template<>
template <typename K, typename V>
-struct trait_trivial_assign< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
+struct trait_trivial_move< key_value_pair_t<K, V> >
+{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };
// ---------------------------------------------------------------------------
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index be365d8..ad59fd6 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -175,8 +175,7 @@ Vector<TYPE>::Vector()
: VectorImpl(sizeof(TYPE),
((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0)
|(traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0)
- |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0)
- |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
+ |(traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))
)
{
}
diff --git a/include/utils/VectorImpl.h b/include/utils/VectorImpl.h
index 2525229..49b03f1 100644
--- a/include/utils/VectorImpl.h
+++ b/include/utils/VectorImpl.h
@@ -44,7 +44,6 @@ public:
HAS_TRIVIAL_CTOR = 0x00000001,
HAS_TRIVIAL_DTOR = 0x00000002,
HAS_TRIVIAL_COPY = 0x00000004,
- HAS_TRIVIAL_ASSIGN = 0x00000008
};
VectorImpl(size_t itemSize, uint32_t flags);
diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h
deleted file mode 100644
index e4698df..0000000
--- a/include/utils/ZipEntry.h
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-//
-// Zip archive entries.
-//
-// The ZipEntry class is tightly meshed with the ZipFile class.
-//
-#ifndef __LIBS_ZIPENTRY_H
-#define __LIBS_ZIPENTRY_H
-
-#include "Errors.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-
-namespace android {
-
-class ZipFile;
-
-/*
- * ZipEntry objects represent a single entry in a Zip archive.
- *
- * You can use one of these to get or set information about an entry, but
- * there are no functions here for accessing the data itself. (We could
- * tuck a pointer to the ZipFile in here for convenience, but that raises
- * the likelihood of using ZipEntry objects after discarding the ZipFile.)
- *
- * File information is stored in two places: next to the file data (the Local
- * File Header, and possibly a Data Descriptor), and at the end of the file
- * (the Central Directory Entry). The two must be kept in sync.
- */
-class ZipEntry {
-public:
- friend class ZipFile;
-
- ZipEntry(void)
- : mDeleted(false), mMarked(false)
- {}
- ~ZipEntry(void) {}
-
- /*
- * Returns "true" if the data is compressed.
- */
- bool isCompressed(void) const {
- return mCDE.mCompressionMethod != kCompressStored;
- }
- int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
-
- /*
- * Return the uncompressed length.
- */
- off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
-
- /*
- * Return the compressed length. For uncompressed data, this returns
- * the same thing as getUncompresesdLen().
- */
- off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
-
- /*
- * Return the absolute file offset of the start of the compressed or
- * uncompressed data.
- */
- off_t getFileOffset(void) const {
- return mCDE.mLocalHeaderRelOffset +
- LocalFileHeader::kLFHLen +
- mLFH.mFileNameLength +
- mLFH.mExtraFieldLength;
- }
-
- /*
- * Return the data CRC.
- */
- unsigned long getCRC32(void) const { return mCDE.mCRC32; }
-
- /*
- * Return file modification time in UNIX seconds-since-epoch.
- */
- time_t getModWhen(void) const;
-
- /*
- * Return the archived file name.
- */
- const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
-
- /*
- * Application-defined "mark". Can be useful when synchronizing the
- * contents of an archive with contents on disk.
- */
- bool getMarked(void) const { return mMarked; }
- void setMarked(bool val) { mMarked = val; }
-
- /*
- * Some basic functions for raw data manipulation. "LE" means
- * Little Endian.
- */
- static inline unsigned short getShortLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8);
- }
- static inline unsigned long getLongLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
- }
- static inline void putShortLE(unsigned char* buf, short val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- }
- static inline void putLongLE(unsigned char* buf, long val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- buf[2] = (unsigned char) (val >> 16);
- buf[3] = (unsigned char) (val >> 24);
- }
-
- /* defined for Zip archives */
- enum {
- kCompressStored = 0, // no compression
- // shrunk = 1,
- // reduced 1 = 2,
- // reduced 2 = 3,
- // reduced 3 = 4,
- // reduced 4 = 5,
- // imploded = 6,
- // tokenized = 7,
- kCompressDeflated = 8, // standard deflate
- // Deflate64 = 9,
- // lib imploded = 10,
- // reserved = 11,
- // bzip2 = 12,
- };
-
- /*
- * Deletion flag. If set, the entry will be removed on the next
- * call to "flush".
- */
- bool getDeleted(void) const { return mDeleted; }
-
-protected:
- /*
- * Initialize the structure from the file, which is pointing at
- * our Central Directory entry.
- */
- status_t initFromCDE(FILE* fp);
-
- /*
- * Initialize the structure for a new file. We need the filename
- * and comment so that we can properly size the LFH area. The
- * filename is mandatory, the comment is optional.
- */
- void initNew(const char* fileName, const char* comment);
-
- /*
- * Initialize the structure with the contents of a ZipEntry from
- * another file.
- */
- status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
-
- /*
- * Add some pad bytes to the LFH. We do this by adding or resizing
- * the "extra" field.
- */
- status_t addPadding(int padding);
-
- /*
- * Set information about the data for this entry.
- */
- void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod);
-
- /*
- * Set the modification date.
- */
- void setModWhen(time_t when);
-
- /*
- * Return the offset of the local file header.
- */
- off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
- /*
- * Set the offset of the local file header, relative to the start of
- * the current file.
- */
- void setLFHOffset(off_t offset) {
- mCDE.mLocalHeaderRelOffset = (long) offset;
- }
-
- /* mark for deletion; used by ZipFile::remove() */
- void setDeleted(void) { mDeleted = true; }
-
-private:
- /* these are private and not defined */
- ZipEntry(const ZipEntry& src);
- ZipEntry& operator=(const ZipEntry& src);
-
- /* returns "true" if the CDE and the LFH agree */
- bool compareHeaders(void) const;
- void copyCDEtoLFH(void);
-
- bool mDeleted; // set if entry is pending deletion
- bool mMarked; // app-defined marker
-
- /*
- * Every entry in the Zip archive starts off with one of these.
- */
- class LocalFileHeader {
- public:
- LocalFileHeader(void) :
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileName(NULL),
- mExtraField(NULL)
- {}
- virtual ~LocalFileHeader(void) {
- delete[] mFileName;
- delete[] mExtraField;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- // unsigned long mSignature;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned char* mFileName;
- unsigned char* mExtraField;
-
- enum {
- kSignature = 0x04034b50,
- kLFHLen = 30, // LocalFileHdr len, excl. var fields
- };
-
- void dump(void) const;
- };
-
- /*
- * Every entry in the Zip archive has one of these in the "central
- * directory" at the end of the file.
- */
- class CentralDirEntry {
- public:
- CentralDirEntry(void) :
- mVersionMadeBy(0),
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileCommentLength(0),
- mDiskNumberStart(0),
- mInternalAttrs(0),
- mExternalAttrs(0),
- mLocalHeaderRelOffset(0),
- mFileName(NULL),
- mExtraField(NULL),
- mFileComment(NULL)
- {}
- virtual ~CentralDirEntry(void) {
- delete[] mFileName;
- delete[] mExtraField;
- delete[] mFileComment;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- // unsigned long mSignature;
- unsigned short mVersionMadeBy;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned short mFileCommentLength;
- unsigned short mDiskNumberStart;
- unsigned short mInternalAttrs;
- unsigned long mExternalAttrs;
- unsigned long mLocalHeaderRelOffset;
- unsigned char* mFileName;
- unsigned char* mExtraField;
- unsigned char* mFileComment;
-
- void dump(void) const;
-
- enum {
- kSignature = 0x02014b50,
- kCDELen = 46, // CentralDirEnt len, excl. var fields
- };
- };
-
- enum {
- //kDataDescriptorSignature = 0x08074b50, // currently unused
- kDataDescriptorLen = 16, // four 32-bit fields
-
- kDefaultVersion = 20, // need deflate, nothing much else
- kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3
- kUsesDataDescr = 0x0008, // GPBitFlag bit 3
- };
-
- LocalFileHeader mLFH;
- CentralDirEntry mCDE;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPENTRY_H
diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h
deleted file mode 100644
index 44df5bb..0000000
--- a/include/utils/ZipFile.h
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-//
-// General-purpose Zip archive access. This class allows both reading and
-// writing to Zip archives, including deletion of existing entries.
-//
-#ifndef __LIBS_ZIPFILE_H
-#define __LIBS_ZIPFILE_H
-
-#include "ZipEntry.h"
-#include "Vector.h"
-#include "Errors.h"
-#include <stdio.h>
-
-namespace android {
-
-/*
- * Manipulate a Zip archive.
- *
- * Some changes will not be visible in the until until "flush" is called.
- *
- * The correct way to update a file archive is to make all changes to a
- * copy of the archive in a temporary file, and then unlink/rename over
- * the original after everything completes. Because we're only interested
- * in using this for packaging, we don't worry about such things. Crashing
- * after making changes and before flush() completes could leave us with
- * an unusable Zip archive.
- */
-class ZipFile {
-public:
- ZipFile(void)
- : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
- {}
- ~ZipFile(void) {
- if (!mReadOnly)
- flush();
- if (mZipFp != NULL)
- fclose(mZipFp);
- discardEntries();
- }
-
- /*
- * Open a new or existing archive.
- */
- typedef enum {
- kOpenReadOnly = 0x01,
- kOpenReadWrite = 0x02,
- kOpenCreate = 0x04, // create if it doesn't exist
- kOpenTruncate = 0x08, // if it exists, empty it
- };
- status_t open(const char* zipFileName, int flags);
-
- /*
- * Add a file to the end of the archive. Specify whether you want the
- * library to try to store it compressed.
- *
- * If "storageName" is specified, the archive will use that instead
- * of "fileName".
- *
- * If there is already an entry with the same name, the call fails.
- * Existing entries with the same name must be removed first.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const char* fileName, int compressionMethod,
- ZipEntry** ppEntry)
- {
- return add(fileName, fileName, compressionMethod, ppEntry);
- }
- status_t add(const char* fileName, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- /*
- * Add a file that is already compressed with gzip.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t addGzip(const char* fileName, const char* storageName,
- ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressDeflated,
- ZipEntry::kCompressDeflated, ppEntry);
- }
-
- /*
- * Add a file from an in-memory data buffer.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const void* data, size_t size, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(NULL, data, size, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- /*
- * Add an entry by copying it from another zip file. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- int padding, ZipEntry** ppEntry);
-
- /*
- * Mark an entry as having been removed. It is not actually deleted
- * from the archive or our internal data structures until flush() is
- * called.
- */
- status_t remove(ZipEntry* pEntry);
-
- /*
- * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
- */
- status_t flush(void);
-
- /*
- * Expand the data into the buffer provided. The buffer must hold
- * at least <uncompressed len> bytes. Variation expands directly
- * to a file.
- *
- * Returns "false" if an error was encountered in the compressed data.
- */
- //bool uncompress(const ZipEntry* pEntry, void* buf) const;
- //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
- void* uncompress(const ZipEntry* pEntry);
-
- /*
- * Get an entry, by name. Returns NULL if not found.
- *
- * Does not return entries pending deletion.
- */
- ZipEntry* getEntryByName(const char* fileName) const;
-
- /*
- * Get the Nth entry in the archive.
- *
- * This will return an entry that is pending deletion.
- */
- int getNumEntries(void) const { return mEntries.size(); }
- ZipEntry* getEntryByIndex(int idx) const;
-
-private:
- /* these are private and not defined */
- ZipFile(const ZipFile& src);
- ZipFile& operator=(const ZipFile& src);
-
- class EndOfCentralDir {
- public:
- EndOfCentralDir(void) :
- mDiskNumber(0),
- mDiskWithCentralDir(0),
- mNumEntries(0),
- mTotalNumEntries(0),
- mCentralDirSize(0),
- mCentralDirOffset(0),
- mCommentLen(0),
- mComment(NULL)
- {}
- virtual ~EndOfCentralDir(void) {
- delete[] mComment;
- }
-
- status_t readBuf(const unsigned char* buf, int len);
- status_t write(FILE* fp);
-
- //unsigned long mSignature;
- unsigned short mDiskNumber;
- unsigned short mDiskWithCentralDir;
- unsigned short mNumEntries;
- unsigned short mTotalNumEntries;
- unsigned long mCentralDirSize;
- unsigned long mCentralDirOffset; // offset from first disk
- unsigned short mCommentLen;
- unsigned char* mComment;
-
- enum {
- kSignature = 0x06054b50,
- kEOCDLen = 22, // EndOfCentralDir len, excl. comment
-
- kMaxCommentLen = 65535, // longest possible in ushort
- kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
-
- };
-
- void dump(void) const;
- };
-
-
- /* read all entries in the central dir */
- status_t readCentralDir(void);
-
- /* crunch deleted entries out */
- status_t crunchArchive(void);
-
- /* clean up mEntries */
- void discardEntries(void);
-
- /* common handler for all "add" functions */
- status_t addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry);
-
- /* copy all of "srcFp" into "dstFp" */
- status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
- /* copy all of "data" into "dstFp" */
- status_t copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32);
- /* copy some of "srcFp" into "dstFp" */
- status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32);
- /* like memmove(), but on parts of a single file */
- status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
- /* compress all of "srcFp" into "dstFp", using Deflate */
- status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32);
-
- /* get modification date from a file descriptor */
- time_t getModTime(int fd);
-
- /*
- * We use stdio FILE*, which gives us buffering but makes dealing
- * with files >2GB awkward. Until we support Zip64, we're fine.
- */
- FILE* mZipFp; // Zip file pointer
-
- /* one of these per file */
- EndOfCentralDir mEOCD;
-
- /* did we open this read-only? */
- bool mReadOnly;
-
- /* set this when we trash the central dir */
- bool mNeedCDRewrite;
-
- /*
- * One ZipEntry per entry in the zip file. I'm using pointers instead
- * of objects because it's easier than making operator= work for the
- * classes and sub-classes.
- */
- Vector<ZipEntry*> mEntries;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPFILE_H
diff --git a/include/utils/executablepath.h b/include/utils/executablepath.h
deleted file mode 100644
index c979432..0000000
--- a/include/utils/executablepath.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _UTILS_EXECUTABLEPATH_H
-#define _UTILS_EXECUTABLEPATH_H
-
-#include <limits.h>
-
-// returns the path to this executable
-#if __cplusplus
-extern "C"
-#endif
-void executablepath(char s[PATH_MAX]);
-
-#endif // _UTILS_EXECUTABLEPATH_H
diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h
deleted file mode 100644
index dbd8672..0000000
--- a/include/utils/inet_address.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address classes. Modeled after Java classes.
-//
-#ifndef _RUNTIME_INET_ADDRESS_H
-#define _RUNTIME_INET_ADDRESS_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-
-namespace android {
-
-/*
- * This class holds Internet addresses. Perhaps more useful is its
- * ability to look up addresses by name.
- *
- * Invoke one of the static factory methods to create a new object.
- */
-class InetAddress {
-public:
- virtual ~InetAddress(void);
-
- // create from w.x.y.z or foo.bar.com notation
- static InetAddress* getByName(const char* host);
-
- // copy-construction
- InetAddress(const InetAddress& orig);
-
- const void* getAddress(void) const { return mAddress; }
- int getAddressLength(void) const { return mLength; }
- const char* getHostName(void) const { return mName; }
-
-private:
- InetAddress(void);
- // assignment (private)
- InetAddress& operator=(const InetAddress& addr);
-
- // use a void* here so we don't have to expose actual socket headers
- void* mAddress; // this is really a ptr to sockaddr_in
- int mLength;
- char* mName;
-};
-
-
-/*
- * Base class for socket addresses.
- */
-class SocketAddress {
-public:
- SocketAddress() {}
- virtual ~SocketAddress() {}
-};
-
-
-/*
- * Internet address class. This combines an InetAddress with a port.
- */
-class InetSocketAddress : public SocketAddress {
-public:
- InetSocketAddress() :
- mAddress(0), mPort(-1)
- {}
- ~InetSocketAddress(void) {
- delete mAddress;
- }
-
- // Create an address with a host wildcard (useful for servers).
- bool create(int port);
- // Create an address with the specified host and port.
- bool create(const InetAddress* addr, int port);
- // Create an address with the specified host and port. Does the
- // hostname lookup.
- bool create(const char* host, int port);
-
- const InetAddress* getAddress(void) const { return mAddress; }
- const int getPort(void) const { return mPort; }
- const char* getHostName(void) const { return mAddress->getHostName(); }
-
-private:
- InetAddress* mAddress;
- int mPort;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_INET_ADDRESS_H
diff --git a/include/utils/misc.h b/include/utils/misc.h
index 62e84b4..23f2a4c 100644
--- a/include/utils/misc.h
+++ b/include/utils/misc.h
@@ -21,7 +21,7 @@
#define _LIBS_UTILS_MISC_H
#include <sys/time.h>
-#include "utils/Endian.h"
+#include <utils/Endian.h>
namespace android {
diff --git a/include/utils/ported.h b/include/utils/ported.h
deleted file mode 100644
index eb3be01..0000000
--- a/include/utils/ported.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Standard functions ported to the current platform. Note these are NOT
-// in the "android" namespace.
-//
-#ifndef _LIBS_UTILS_PORTED_H
-#define _LIBS_UTILS_PORTED_H
-
-#include <sys/time.h> // for timeval
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* library replacement functions */
-#if defined(NEED_GETTIMEOFDAY)
-int gettimeofday(struct timeval* tv, struct timezone* tz);
-#endif
-#if defined(NEED_USLEEP)
-void usleep(unsigned long usec);
-#endif
-#if defined(NEED_PIPE)
-int pipe(int filedes[2]);
-#endif
-#if defined(NEED_SETENV)
-int setenv(const char* name, const char* value, int overwrite);
-void unsetenv(const char* name);
-char* getenv(const char* name);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // _LIBS_UTILS_PORTED_H
diff --git a/include/utils/string_array.h b/include/utils/string_array.h
deleted file mode 100644
index 064dda2..0000000
--- a/include/utils/string_array.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Sortable array of strings. STL-ish, but STL-free.
-//
-#ifndef _LIBS_UTILS_STRING_ARRAY_H
-#define _LIBS_UTILS_STRING_ARRAY_H
-
-#include <stdlib.h>
-#include <string.h>
-
-namespace android {
-
-//
-// An expanding array of strings. Add, get, sort, delete.
-//
-class StringArray {
-public:
- StringArray()
- : mMax(0), mCurrent(0), mArray(NULL)
- {}
- virtual ~StringArray() {
- for (int i = 0; i < mCurrent; i++)
- delete[] mArray[i];
- delete[] mArray;
- }
-
- //
- // Add a string. A copy of the string is made.
- //
- bool push_back(const char* str) {
- if (mCurrent >= mMax) {
- char** tmp;
-
- if (mMax == 0)
- mMax = 16; // initial storage
- else
- mMax *= 2;
-
- tmp = new char*[mMax];
- if (tmp == NULL)
- return false;
-
- memcpy(tmp, mArray, mCurrent * sizeof(char*));
- delete[] mArray;
- mArray = tmp;
- }
-
- int len = strlen(str);
- mArray[mCurrent] = new char[len+1];
- memcpy(mArray[mCurrent], str, len+1);
- mCurrent++;
-
- return true;
- }
-
- //
- // Delete an entry.
- //
- void erase(int idx) {
- if (idx < 0 || idx >= mCurrent)
- return;
- delete[] mArray[idx];
- if (idx < mCurrent-1) {
- memmove(&mArray[idx], &mArray[idx+1],
- (mCurrent-1 - idx) * sizeof(char*));
- }
- mCurrent--;
- }
-
- //
- // Sort the array.
- //
- void sort(int (*compare)(const void*, const void*)) {
- qsort(mArray, mCurrent, sizeof(char*), compare);
- }
-
- //
- // Pass this to the sort routine to do an ascending alphabetical sort.
- //
- static int cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
- return strcmp(*(const char**)pstr1, *(const char**)pstr2);
- }
-
- //
- // Get the #of items in the array.
- //
- inline int size(void) const { return mCurrent; }
-
- //
- // Return entry N.
- // [should use operator[] here]
- //
- const char* getEntry(int idx) const {
- if (idx < 0 || idx >= mCurrent)
- return NULL;
- return mArray[idx];
- }
-
- //
- // Set entry N to specified string.
- // [should use operator[] here]
- //
- void setEntry(int idx, const char* str) {
- if (idx < 0 || idx >= mCurrent)
- return;
- delete[] mArray[idx];
- int len = strlen(str);
- mArray[idx] = new char[len+1];
- memcpy(mArray[idx], str, len+1);
- }
-
-private:
- int mMax;
- int mCurrent;
- char** mArray;
-};
-
-}; // namespace android
-
-#endif // _LIBS_UTILS_STRING_ARRAY_H
diff --git a/include/utils/threads.h b/include/utils/threads.h
index b320915..f5304f7 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -21,6 +21,10 @@
#include <sys/types.h>
#include <time.h>
+#if defined(HAVE_PTHREADS)
+# include <pthread.h>
+#endif
+
// ------------------------------------------------------------------
// C API
@@ -86,6 +90,11 @@ enum {
ANDROID_TGROUP_MAX = ANDROID_TGROUP_FG_BOOST,
};
+typedef enum {
+ SP_BACKGROUND = 0,
+ SP_FOREGROUND = 1,
+} SchedPolicy;
+
// Create and run a new thread.
extern int androidCreateThread(android_thread_func_t, void *);
@@ -176,6 +185,8 @@ inline thread_id_t getThreadId() {
return androidGetThreadId();
}
+/*****************************************************************************/
+
/*
* Simple mutex class. The implementation is system-dependent.
*
@@ -184,8 +195,14 @@ inline thread_id_t getThreadId() {
*/
class Mutex {
public:
+ enum {
+ NORMAL = 0,
+ SHARED = 1
+ };
+
Mutex();
Mutex(const char* name);
+ Mutex(int type, const char* name = NULL);
~Mutex();
// lock or unlock the mutex
@@ -199,11 +216,11 @@ public:
// constructed and released when Autolock goes out of scope.
class Autolock {
public:
- inline Autolock(Mutex& mutex) : mpMutex(&mutex) { mutex.lock(); }
- inline Autolock(Mutex* mutex) : mpMutex(mutex) { mutex->lock(); }
- inline ~Autolock() { mpMutex->unlock(); }
+ inline Autolock(Mutex& mutex) : mLock(mutex) { mLock.lock(); }
+ inline Autolock(Mutex* mutex) : mLock(*mutex) { mLock.lock(); }
+ inline ~Autolock() { mLock.unlock(); }
private:
- Mutex* mpMutex;
+ Mutex& mLock;
};
private:
@@ -212,11 +229,49 @@ private:
// A mutex cannot be copied
Mutex(const Mutex&);
Mutex& operator = (const Mutex&);
- void _init();
+#if defined(HAVE_PTHREADS)
+ pthread_mutex_t mMutex;
+#else
+ void _init();
void* mState;
+#endif
};
+#if defined(HAVE_PTHREADS)
+
+inline Mutex::Mutex() {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(const char* name) {
+ pthread_mutex_init(&mMutex, NULL);
+}
+inline Mutex::Mutex(int type, const char* name) {
+ if (type == SHARED) {
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&mMutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+ } else {
+ pthread_mutex_init(&mMutex, NULL);
+ }
+}
+inline Mutex::~Mutex() {
+ pthread_mutex_destroy(&mMutex);
+}
+inline status_t Mutex::lock() {
+ return -pthread_mutex_lock(&mMutex);
+}
+inline void Mutex::unlock() {
+ pthread_mutex_unlock(&mMutex);
+}
+inline status_t Mutex::tryLock() {
+ return -pthread_mutex_trylock(&mMutex);
+}
+
+#endif // HAVE_PTHREADS
+
/*
* Automatic mutex. Declare one of these at the top of a function.
* When the function returns, it will go out of scope, and release the
@@ -225,6 +280,7 @@ private:
typedef Mutex::Autolock AutoMutex;
+/*****************************************************************************/
/*
* Condition variable class. The implementation is system-dependent.
@@ -240,9 +296,6 @@ public:
~Condition();
// Wait on the condition variable. Lock the mutex before calling.
status_t wait(Mutex& mutex);
- // Wait on the condition variable until the given time. Lock the mutex
- // before calling.
- status_t wait(Mutex& mutex, nsecs_t abstime);
// same with relative timeout
status_t waitRelative(Mutex& mutex, nsecs_t reltime);
// Signal the condition variable, allowing one thread to continue.
@@ -251,9 +304,60 @@ public:
void broadcast();
private:
+#if defined(HAVE_PTHREADS)
+ pthread_cond_t mCond;
+#else
void* mState;
+#endif
};
+#if defined(HAVE_PTHREADS)
+
+inline Condition::Condition() {
+ pthread_cond_init(&mCond, NULL);
+}
+inline Condition::~Condition() {
+ pthread_cond_destroy(&mCond);
+}
+inline status_t Condition::wait(Mutex& mutex) {
+ return -pthread_cond_wait(&mCond, &mutex.mMutex);
+}
+inline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {
+#if defined(HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE)
+ struct timespec ts;
+ ts.tv_sec = reltime/1000000000;
+ ts.tv_nsec = reltime%1000000000;
+ return -pthread_cond_timedwait_relative_np(&mCond, &mutex.mMutex, &ts);
+#else // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+ struct timespec ts;
+#if defined(HAVE_POSIX_CLOCKS)
+ clock_gettime(CLOCK_REALTIME, &ts);
+#else // HAVE_POSIX_CLOCKS
+ // we don't support the clocks here.
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ ts.tv_sec = t.tv_sec;
+ ts.tv_nsec= t.tv_usec*1000;
+#endif // HAVE_POSIX_CLOCKS
+ ts.tv_sec += reltime/1000000000;
+ ts.tv_nsec+= reltime%1000000000;
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec += 1;
+ }
+ return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);
+#endif // HAVE_PTHREAD_COND_TIMEDWAIT_RELATIVE
+}
+inline void Condition::signal() {
+ pthread_cond_signal(&mCond);
+}
+inline void Condition::broadcast() {
+ pthread_cond_broadcast(&mCond);
+}
+
+#endif // HAVE_PTHREADS
+
+/*****************************************************************************/
/*
* This is our spiffy thread object!
@@ -291,7 +395,7 @@ protected:
bool exitPending() const;
private:
- // Derived class must implemtent threadLoop(). The thread starts its life
+ // Derived class must implement threadLoop(). The thread starts its life
// here. There are two ways of using the Thread object:
// 1) loop: if threadLoop() returns true, it will be called again if
// requestExit() wasn't called.
@@ -309,6 +413,9 @@ private:
volatile bool mExitPending;
volatile bool mRunning;
sp<Thread> mHoldSelf;
+#if HAVE_ANDROID_OS
+ int mTid;
+#endif
};
diff --git a/keystore/java/android/security/CertTool.java b/keystore/java/android/security/CertTool.java
index 989432a..88d6e3d 100644
--- a/keystore/java/android/security/CertTool.java
+++ b/keystore/java/android/security/CertTool.java
@@ -16,6 +16,12 @@
package android.security;
+import android.content.Context;
+import android.content.Intent;
+import android.security.Keystore;
+import android.text.TextUtils;
+import android.util.Log;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.cert.Certificate;
@@ -23,16 +29,11 @@ import java.security.cert.CertificateException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
-
-import android.content.Context;
-import android.content.Intent;
-import android.security.Keystore;
-import android.text.TextUtils;
-import android.util.Log;
+import java.util.ArrayList;
/**
* The CertTool class provides the functions to list the certs/keys,
- * generate the certificate request(csr) and store the certificate into
+ * generate the certificate request(csr) and store certificates into
* keystore.
*
* {@hide}
@@ -42,28 +43,143 @@ public class CertTool {
System.loadLibrary("certtool_jni");
}
+ /** Keystore namespace for CA certificates. */
+ public static final String CA_CERTIFICATE = "CACERT";
+
+ /** Keystore namespace for user certificates. */
+ public static final String USER_CERTIFICATE = "USRCERT";
+
+ /** Keystore namespace for user private keys. */
+ public static final String USER_KEY = "USRKEY";
+
+ /** Action string for adding certificates to keystore. */
public static final String ACTION_ADD_CREDENTIAL =
"android.security.ADD_CREDENTIAL";
- public static final String KEY_TYPE_NAME = "typeName";
- public static final String KEY_ITEM = "item";
- public static final String KEY_NAMESPACE = "namespace";
- public static final String KEY_DESCRIPTION = "description";
+ /** Action string for installing certificates to keystore from sdcard. */
+ public static final String ACTION_INSTALL_CERT_FROM_SDCARD =
+ "android.security.INSTALL_CERT_FROM_SDCARD";
+
+ /** Dialog title for adding a CA certificate. */
public static final String TITLE_CA_CERT = "CA Certificate";
+
+ /** Dialog title for adding a user certificate. */
public static final String TITLE_USER_CERT = "User Certificate";
- public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
+
+ /** Dialog title for adding a user private key. */
public static final String TITLE_PRIVATE_KEY = "Private Key";
+
+ /** Dialog title for adding a PKCS12 keystore. */
+ public static final String TITLE_PKCS12_KEYSTORE = "PKCS12 Keystore";
+
public static final int INCORRECT_PKCS12_PASSPHRASE = -100;
+ /**
+ * The builder class for building an add-credential-to-keystore intent.
+ */
+ public static class AddCredentialIntentBuilder {
+ private Intent mIntent;
+ private int mCount;
+
+ /**
+ * Creates a builder to build a add-credential-to-keystore intent.
+ *
+ * @param title title of the dialog for adding this credential
+ * @param descriptions description strings to show on the dialog
+ */
+ public AddCredentialIntentBuilder(String title,
+ String... descriptions) {
+ Intent intent = new Intent(ACTION_ADD_CREDENTIAL);
+ intent.putExtra(KEY_TITLE, title);
+
+ int i = 0;
+ for (String description : descriptions) {
+ intent.putExtra(KEY_DESCRIPTION + (i++), description);
+ }
+ mIntent = intent;
+ }
+
+ /**
+ * Adds credential data to the intent.
+ *
+ * @param namespace the namespace of the keystore to add the credential
+ * data to
+ * @param data the credential data
+ * @return this builder
+ */
+ public AddCredentialIntentBuilder addCredential(String namespace,
+ byte[] data) {
+ mIntent.putExtra(KEY_NAMESPACE + mCount, namespace);
+ mIntent.putExtra(KEY_ITEM + mCount, data);
+ mCount++;
+ return this;
+ }
+
+ /** Returns the intent. */
+ public Intent build() {
+ return mIntent;
+ }
+ }
+
+ /**
+ * Request for adding credential data to keystore.
+ */
+ public static class AddCredentialRequest {
+ private Intent mIntent;
+
+ /**
+ * Creates an add-credential-data-to-keystore request.
+ *
+ * @param intent an add-credential-data-to-keystore intent
+ * @see AddCredentialIntentBuilder
+ */
+ public AddCredentialRequest(Intent intent) {
+ mIntent = intent;
+ }
+
+ /** Returns the dialog title. */
+ public String getTitle() {
+ return mIntent.getStringExtra(KEY_TITLE);
+ }
+
+ /**
+ * Returns the i'th credential data.
+ * @return the data or null if not exists
+ */
+ public byte[] getDataAt(int i) {
+ return mIntent.getByteArrayExtra(KEY_ITEM + i);
+ }
+
+ /**
+ * Returns the namespace of the i'th credential data.
+ * @return the namespace string or null if missing
+ */
+ public String getNamespaceAt(int i) {
+ return mIntent.getStringExtra(KEY_NAMESPACE + i);
+ }
+
+ /** Returns the descriptions of the credential data. */
+ public String[] getDescriptions() {
+ ArrayList<String> list = new ArrayList<String>();
+ for (int i = 0; ; i++) {
+ String s = mIntent.getStringExtra(KEY_DESCRIPTION + i);
+ if (s == null) break;
+ list.add(s);
+ }
+ return list.toArray(new String[list.size()]);
+ }
+ }
+
+ private static final String KEY_TITLE = "typeName";
+ private static final String KEY_ITEM = "item";
+ private static final String KEY_NAMESPACE = "namespace";
+ private static final String KEY_DESCRIPTION = "description";
+
private static final String TAG = "CertTool";
private static final String UNKNOWN = "Unknown";
private static final String ISSUER_NAME = "Issuer Name:";
private static final String DISTINCT_NAME = "Distinct Name:";
- private static final String CA_CERTIFICATE = "CACERT";
- private static final String USER_CERTIFICATE = "USRCERT";
- private static final String USER_KEY = "USRKEY";
-
private static final String KEYNAME_DELIMITER = "_";
private static final Keystore sKeystore = Keystore.getInstance();
@@ -81,33 +197,55 @@ public class CertTool {
private native String getPrivateKeyPEM(int handle);
private native void freeX509Certificate(int handle);
- private static CertTool singleton = null;
+ private static CertTool sSingleton = null;
private CertTool() { }
public static final CertTool getInstance() {
- if (singleton == null) {
- singleton = new CertTool();
+ if (sSingleton == null) {
+ sSingleton = new CertTool();
}
- return singleton;
+ return sSingleton;
}
+ /**
+ * Gets the full key to retrieve the user private key from the keystore.
+ * @see #getAllUserCertificateKeys()
+ */
public String getUserPrivateKey(String key) {
return USER_KEY + KEYNAME_DELIMITER + key;
}
+ /**
+ * Gets the full key to retrieve the user certificate from the keystore.
+ * @see #getAllUserCertificateKeys()
+ */
public String getUserCertificate(String key) {
return USER_CERTIFICATE + KEYNAME_DELIMITER + key;
}
+ /**
+ * Gets the full key to retrieve the CA certificate from the keystore.
+ * @see #getAllCaCertificateKeys()
+ */
public String getCaCertificate(String key) {
return CA_CERTIFICATE + KEYNAME_DELIMITER + key;
}
+ /**
+ * Gets all the keys to the user certificates/private keys stored in the
+ * keystore.
+ * @see #getUserCertificate(String)
+ * @see #getUserPrivateKey(String)
+ */
public String[] getAllUserCertificateKeys() {
return sKeystore.listKeys(USER_KEY);
}
+ /**
+ * Gets all the keys to the CA certificates stored in the keystore.
+ * @see #getCaCertificate(String)
+ */
public String[] getAllCaCertificateKeys() {
return sKeystore.listKeys(CA_CERTIFICATE);
}
@@ -121,29 +259,21 @@ public class CertTool {
return 1024;
}
+ /**
+ * Generates a key pair.
+ *
+ * @param keyStrengthIndex index to the array of supported key strengths;
+ * see {@link #getSupportedKeyStrenghs()}
+ * @param challenge the challenge string for generating the pair
+ * @param dirName (not used)
+ * @return a certificate request from the resulted public key
+ */
public String generateKeyPair(int keyStrengthIndex, String challenge,
String dirName) {
return generateCertificateRequest(getKeyLength(keyStrengthIndex),
challenge);
}
- private Intent prepareIntent(String title, byte[] data, String namespace,
- String issuer, String distinctName) {
- Intent intent = new Intent(ACTION_ADD_CREDENTIAL);
- intent.putExtra(KEY_TYPE_NAME, title);
- intent.putExtra(KEY_ITEM + "0", data);
- intent.putExtra(KEY_NAMESPACE + "0", namespace);
- intent.putExtra(KEY_DESCRIPTION + "0", ISSUER_NAME + issuer);
- intent.putExtra(KEY_DESCRIPTION + "1", DISTINCT_NAME + distinctName);
- return intent;
- }
-
- private void addExtraIntentInfo(Intent intent, String namespace,
- String data) {
- intent.putExtra(KEY_ITEM + "1", data.getBytes());
- intent.putExtra(KEY_NAMESPACE + "1", namespace);
- }
-
private int extractAndStoreKeysFromPkcs12(int handle, String keyname) {
int ret, i = 0;
String pemData;
@@ -158,20 +288,15 @@ public class CertTool {
return ret;
}
}
- while ((pemData = this.popPkcs12CertificateStack(handle)) != null) {
- if (i++ > 0) {
- if ((ret = sKeystore.put(CA_CERTIFICATE, keyname + i, pemData)) != 0) {
- return ret;
- }
- } else {
- if ((ret = sKeystore.put(CA_CERTIFICATE, keyname, pemData)) != 0) {
- return ret;
- }
+ if ((pemData = this.popPkcs12CertificateStack(handle)) != null) {
+ if ((ret = sKeystore.put(CA_CERTIFICATE, keyname, pemData)) != 0) {
+ return ret;
}
}
return 0;
}
+ /** Adds a PKCS12 keystore to the keystore. */
public int addPkcs12Keystore(byte[] p12Data, String password,
String keyname) {
int handle, ret;
@@ -185,27 +310,35 @@ public class CertTool {
return ret;
}
+ /**
+ * Adds a certificate to the keystore.
+ *
+ * @param data the certificate data
+ * @param context the context to send an add-credential-to-keystore intent
+ */
public synchronized void addCertificate(byte[] data, Context context) {
int handle;
Intent intent = null;
Log.i("CertTool", "addCertificate()");
if (isPkcs12Keystore(data)) {
- intent = prepareIntent(TITLE_PKCS12_KEYSTORE, data, USER_KEY,
- UNKNOWN, UNKNOWN);
+ intent = prepareIntent(TITLE_PKCS12_KEYSTORE, UNKNOWN, UNKNOWN)
+ .addCredential(USER_KEY, data).build();
} else if ((handle = generateX509Certificate(data)) != 0) {
String issuer = getIssuerDN(handle);
String distinctName = getCertificateDN(handle);
String privateKeyPEM = getPrivateKeyPEM(handle);
if (isCaCertificate(handle)) {
- intent = prepareIntent(TITLE_CA_CERT, data, CA_CERTIFICATE,
- issuer, distinctName);
+ intent = prepareIntent(TITLE_CA_CERT, issuer, distinctName)
+ .addCredential(CA_CERTIFICATE, data).build();
} else {
- intent = prepareIntent(TITLE_USER_CERT, data, USER_CERTIFICATE,
- issuer, distinctName);
+ AddCredentialIntentBuilder builder =
+ prepareIntent(TITLE_USER_CERT, issuer, distinctName)
+ .addCredential(USER_CERTIFICATE, data);
if (!TextUtils.isEmpty(privateKeyPEM)) {
- addExtraIntentInfo(intent, USER_KEY, privateKeyPEM);
+ builder.addCredential(USER_KEY, privateKeyPEM.getBytes());
}
+ intent = builder.build();
}
freeX509Certificate(handle);
}
@@ -215,4 +348,10 @@ public class CertTool {
Log.w("CertTool", "incorrect data for addCertificate()");
}
}
+
+ private AddCredentialIntentBuilder prepareIntent(
+ String title, String issuer, String distinctName) {
+ return new AddCredentialIntentBuilder(title, ISSUER_NAME + issuer,
+ DISTINCT_NAME + distinctName);
+ }
}
diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java
index a6cfbca..a706c89 100644
--- a/keystore/java/android/security/Keystore.java
+++ b/keystore/java/android/security/Keystore.java
@@ -22,8 +22,9 @@ package android.security;
*/
public abstract class Keystore {
- private static final String TAG = "Keystore";
- private static final String[] NOTFOUND = new String[0];
+ /** Action to unlock (or initialize) the keystore. */
+ public static final String ACTION_UNLOCK_CREDENTIAL_STORAGE =
+ "android.security.UNLOCK_CREDENTIAL_STORAGE";
// Keystore States
public static final int BOOTUP = 0;
@@ -31,8 +32,9 @@ public abstract class Keystore {
public static final int LOCKED = 2;
public static final int UNLOCKED = 3;
- /**
- */
+ private static final String TAG = "Keystore";
+ private static final String[] NOTFOUND = new String[0];
+
public static Keystore getInstance() {
return new FileKeystore();
}
@@ -53,7 +55,6 @@ public abstract class Keystore {
private static final String CA_CERTIFICATE = "CaCertificate";
private static final String USER_CERTIFICATE = "UserCertificate";
private static final String USER_KEY = "UserPrivateKey";
- private static final String COMMAND_DELIMITER = " ";
private static final ServiceCommand mServiceCommand =
new ServiceCommand(SERVICE_NAME);
@@ -80,7 +81,7 @@ public abstract class Keystore {
@Override
public int changePassword(String oldPassword, String newPassword) {
Reply result = mServiceCommand.execute(ServiceCommand.PASSWD,
- oldPassword + " " + newPassword);
+ oldPassword + "\0" + newPassword + "\0");
return (result != null) ? result.returnCode : -1;
}
@@ -105,14 +106,14 @@ public abstract class Keystore {
@Override
public int put(String namespace, String keyname, String value) {
Reply result = mServiceCommand.execute(ServiceCommand.PUT_KEY,
- namespace + " " + keyname + " " + value);
+ namespace + "\0" + keyname + "\0" + value);
return (result != null) ? result.returnCode : -1;
}
@Override
public String get(String namespace, String keyname) {
Reply result = mServiceCommand.execute(ServiceCommand.GET_KEY,
- namespace + " " + keyname);
+ namespace + "\0" + keyname + "\0");
return (result != null) ? ((result.returnCode != 0) ? null :
new String(result.data, 0, result.len)) : null;
}
@@ -120,7 +121,7 @@ public abstract class Keystore {
@Override
public int remove(String namespace, String keyname) {
Reply result = mServiceCommand.execute(ServiceCommand.REMOVE_KEY,
- namespace + " " + keyname);
+ namespace + "\0" + keyname + "\0");
return (result != null) ? result.returnCode : -1;
}
diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java
index dddf654..cefae40 100644
--- a/keystore/java/android/security/ServiceCommand.java
+++ b/keystore/java/android/security/ServiceCommand.java
@@ -121,15 +121,16 @@ public class ServiceCommand {
Reply reply = new Reply();
if (!readBytes(buf, 4)) return null;
- reply.len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8) |
- ((((int) buf[2]) & 0xff) << 16) |
- ((((int) buf[3]) & 0xff) << 24);
+ reply.len = ((((int) buf[0]) & 0xff) << 24) |
+ ((((int) buf[1]) & 0xff) << 16) |
+ ((((int) buf[2]) & 0xff) << 8) |
+ (((int) buf[3]) & 0xff);
if (!readBytes(buf, 4)) return null;
- reply.returnCode = (((int) buf[0]) & 0xff) |
- ((((int) buf[1]) & 0xff) << 8) |
- ((((int) buf[2]) & 0xff) << 16) |
- ((((int) buf[3]) & 0xff) << 24);
+ reply.returnCode = ((((int) buf[0]) & 0xff) << 24) |
+ ((((int) buf[1]) & 0xff) << 16) |
+ ((((int) buf[2]) & 0xff) << 8) |
+ (((int) buf[3]) & 0xff);
if (reply.len > BUFFER_LENGTH) {
Log.e(mTag,"invalid reply length (" + reply.len + ")");
@@ -145,15 +146,15 @@ public class ServiceCommand {
byte[] data = (_data == null) ? new byte[0] : _data.getBytes();
int len = data.length;
// the length of data
- buf[0] = (byte) (len & 0xff);
- buf[1] = (byte) ((len >> 8) & 0xff);
- buf[2] = (byte) ((len >> 16) & 0xff);
- buf[3] = (byte) ((len >> 24) & 0xff);
+ buf[0] = (byte) ((len >> 24) & 0xff);
+ buf[1] = (byte) ((len >> 16) & 0xff);
+ buf[2] = (byte) ((len >> 8) & 0xff);
+ buf[3] = (byte) (len & 0xff);
// the opcode of the command
- buf[4] = (byte) (cmd & 0xff);
- buf[5] = (byte) ((cmd >> 8) & 0xff);
- buf[6] = (byte) ((cmd >> 16) & 0xff);
- buf[7] = (byte) ((cmd >> 24) & 0xff);
+ buf[4] = (byte) ((cmd >> 24) & 0xff);
+ buf[5] = (byte) ((cmd >> 16) & 0xff);
+ buf[6] = (byte) ((cmd >> 8) & 0xff);
+ buf[7] = (byte) (cmd & 0xff);
try {
mOut.write(buf, 0, 8);
mOut.write(data, 0, len);
diff --git a/keystore/jni/cert.c b/keystore/jni/cert.c
index 006a0a3..90f872e 100644
--- a/keystore/jni/cert.c
+++ b/keystore/jni/cert.c
@@ -18,7 +18,6 @@
#define LOG_TAG "CertTool"
#include <stdio.h>
-#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/pkcs12.h>
#include <openssl/rsa.h>
@@ -212,13 +211,14 @@ static int convert_to_pem(void *data, int is_cert, char *buf, int size)
}
err:
if (bio) BIO_free(bio);
- return (len == 0) ? -1 : 0;
+ return len;
}
int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size)
{
if ((p12store != NULL) && (p12store->cert != NULL)) {
- return convert_to_pem((void*)p12store->cert, 1, buf, size);
+ int len = convert_to_pem((void*)p12store->cert, 1, buf, size);
+ return (len == 0) ? -1 : 0;
}
return -1;
}
@@ -226,7 +226,8 @@ int get_pkcs12_certificate(PKCS12_KEYSTORE *p12store, char *buf, int size)
int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size)
{
if ((p12store != NULL) && (p12store->pkey != NULL)) {
- return convert_to_pem((void*)p12store->pkey, 0, buf, size);
+ int len = convert_to_pem((void*)p12store->pkey, 0, buf, size);
+ return (len == 0) ? -1 : 0;
}
return -1;
}
@@ -234,12 +235,19 @@ int get_pkcs12_private_key(PKCS12_KEYSTORE *p12store, char *buf, int size)
int pop_pkcs12_certs_stack(PKCS12_KEYSTORE *p12store, char *buf, int size)
{
X509 *cert = NULL;
+ int len = 0;
- if ((p12store != NULL) && (p12store->certs != NULL) &&
- ((cert = sk_X509_pop(p12store->certs)) != NULL)) {
- int ret = convert_to_pem((void*)cert, 1, buf, size);
- X509_free(cert);
- return ret;
+ if ((p12store != NULL) && (p12store->certs != NULL)) {
+ while (((cert = sk_X509_pop(p12store->certs)) != NULL) && (len < size)) {
+ int s = convert_to_pem((void*)cert, 1, buf + len, size - len);
+ if (s == 0) {
+ LOGE("buffer size is too small. len=%d size=%d\n", len, size);
+ return -1;
+ }
+ len += s;
+ X509_free(cert);
+ }
+ return (len == 0) ? -1 : 0;
}
return -1;
}
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp
index 16a4f2d..57a29f2 100644
--- a/libs/audioflinger/A2dpAudioInterface.cpp
+++ b/libs/audioflinger/A2dpAudioInterface.cpp
@@ -29,25 +29,41 @@ namespace android {
// ----------------------------------------------------------------------------
-A2dpAudioInterface::A2dpAudioInterface() :
- mOutput(0)
+//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface()
+//{
+// AudioHardwareInterface* hw = 0;
+//
+// hw = AudioHardwareInterface::create();
+// LOGD("new A2dpAudioInterface(hw: %p)", hw);
+// hw = new A2dpAudioInterface(hw);
+// return hw;
+//}
+
+A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) :
+ mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true)
{
}
A2dpAudioInterface::~A2dpAudioInterface()
{
- delete mOutput;
+ closeOutputStream((AudioStreamOut *)mOutput);
+ delete mHardwareInterface;
}
status_t A2dpAudioInterface::initCheck()
{
- return 0;
+ if (mHardwareInterface == 0) return NO_INIT;
+ return mHardwareInterface->initCheck();
}
AudioStreamOut* A2dpAudioInterface::openOutputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status)
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
{
- LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate);
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
+ LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices);
+ return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status);
+ }
+
status_t err = 0;
// only one output stream allowed
@@ -59,71 +75,127 @@ AudioStreamOut* A2dpAudioInterface::openOutputStream(
// create new output stream
A2dpAudioStreamOut* out = new A2dpAudioStreamOut();
- if ((err = out->set(format, channelCount, sampleRate)) == NO_ERROR) {
+ if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) {
mOutput = out;
+ mOutput->setBluetoothEnabled(mBluetoothEnabled);
} else {
delete out;
}
-
+
if (status)
*status = err;
return mOutput;
}
+void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) {
+ if (mOutput == 0 || mOutput != out) {
+ mHardwareInterface->closeOutputStream(out);
+ }
+ else {
+ delete mOutput;
+ mOutput = 0;
+ }
+}
+
+
AudioStreamIn* A2dpAudioInterface::openInputStream(
- int inputSource, int format, int channelCount, uint32_t sampleRate,
- status_t *status, AudioSystem::audio_in_acoustics acoustics)
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status,
+ AudioSystem::audio_in_acoustics acoustics)
{
- if (status)
- *status = -1;
- return NULL;
+ return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
+}
+
+void A2dpAudioInterface::closeInputStream(AudioStreamIn* in)
+{
+ return mHardwareInterface->closeInputStream(in);
+}
+
+status_t A2dpAudioInterface::setMode(int mode)
+{
+ return mHardwareInterface->setMode(mode);
}
status_t A2dpAudioInterface::setMicMute(bool state)
{
- return 0;
+ return mHardwareInterface->setMicMute(state);
}
status_t A2dpAudioInterface::getMicMute(bool* state)
{
- return 0;
+ return mHardwareInterface->getMicMute(state);
}
-status_t A2dpAudioInterface::setParameter(const char *key, const char *value)
+status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs)
{
- LOGD("setParameter %s,%s\n", key, value);
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ String8 key;
+ status_t status = NO_ERROR;
+
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ key = "bluetooth_enabled";
+ if (param.get(key, value) == NO_ERROR) {
+ mBluetoothEnabled = (value == "true");
+ if (mOutput) {
+ mOutput->setBluetoothEnabled(mBluetoothEnabled);
+ }
+ param.remove(key);
+ }
- if (!key || !value)
- return -EINVAL;
+ if (param.size()) {
+ status_t hwStatus = mHardwareInterface->setParameters(param.toString());
+ if (status == NO_ERROR) {
+ status = hwStatus;
+ }
+ }
- if (strcmp(key, "a2dp_sink_address") == 0) {
- return mOutput->setAddress(value);
+ return status;
+}
+
+String8 A2dpAudioInterface::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ AudioParameter a2dpParam = AudioParameter();
+ String8 value;
+ String8 key;
+
+ key = "bluetooth_enabled";
+ if (param.get(key, value) == NO_ERROR) {
+ value = mBluetoothEnabled ? "true" : "false";
+ a2dpParam.add(key, value);
+ param.remove(key);
}
- if (strcmp(key, "bluetooth_enabled") == 0) {
- mOutput->setBluetoothEnabled(strcmp(value, "true") == 0);
+
+ String8 keyValuePairs = a2dpParam.toString();
+
+ if (param.size()) {
+ keyValuePairs += ";";
+ keyValuePairs += mHardwareInterface->getParameters(param.toString());
}
- return 0;
+ LOGV("getParameters() %s", keyValuePairs.string());
+ return keyValuePairs;
}
-status_t A2dpAudioInterface::setVoiceVolume(float v)
+size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
- return 0;
+ return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount);
}
-status_t A2dpAudioInterface::setMasterVolume(float v)
+status_t A2dpAudioInterface::setVoiceVolume(float v)
{
- return 0;
+ return mHardwareInterface->setVoiceVolume(v);
}
-status_t A2dpAudioInterface::doRouting()
+status_t A2dpAudioInterface::setMasterVolume(float v)
{
- return 0;
+ return mHardwareInterface->setMasterVolume(v);
}
status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args)
{
- return 0;
+ return mHardwareInterface->dumpState(fd, args);
}
// ----------------------------------------------------------------------------
@@ -132,7 +204,7 @@ A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL),
// assume BT enabled to start, this is safe because its only the
// enabled->disabled transition we are worried about
- mBluetoothEnabled(true)
+ mBluetoothEnabled(true), mDevice(0), mClosing(false)
{
// use any address by default
strcpy(mA2dpAddress, "00:00:00:00:00:00");
@@ -140,27 +212,43 @@ A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() :
}
status_t A2dpAudioInterface::A2dpAudioStreamOut::set(
- int format, int channels, uint32_t rate)
+ uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate)
{
- LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate);
+ int lFormat = pFormat ? *pFormat : 0;
+ uint32_t lChannels = pChannels ? *pChannels : 0;
+ uint32_t lRate = pRate ? *pRate : 0;
+
+ LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate);
// fix up defaults
- if (format == 0) format = AudioSystem::PCM_16_BIT;
- if (channels == 0) channels = channelCount();
- if (rate == 0) rate = sampleRate();
+ if (lFormat == 0) lFormat = format();
+ if (lChannels == 0) lChannels = channels();
+ if (lRate == 0) lRate = sampleRate();
// check values
- if ((format != AudioSystem::PCM_16_BIT) ||
- (channels != channelCount()) ||
- (rate != sampleRate()))
+ if ((lFormat != format()) ||
+ (lChannels != channels()) ||
+ (lRate != sampleRate())){
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
return BAD_VALUE;
+ }
+
+ if (pFormat) *pFormat = lFormat;
+ if (pChannels) *pChannels = lChannels;
+ if (pRate) *pRate = lRate;
+ mDevice = device;
return NO_ERROR;
}
A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut()
{
+ LOGV("A2dpAudioStreamOut destructor");
+ standby();
close();
+ LOGV("A2dpAudioStreamOut destructor returning from close()");
}
ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes)
@@ -170,7 +258,7 @@ ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t
size_t remaining = bytes;
status_t status = -1;
- if (!mBluetoothEnabled) {
+ if (!mBluetoothEnabled || mClosing) {
LOGW("A2dpAudioStreamOut::write(), but bluetooth disabled");
goto Error;
}
@@ -219,6 +307,11 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
{
int result = 0;
+ if (mClosing) {
+ LOGV("Ignore standby, closing");
+ return result;
+ }
+
Mutex::Autolock lock(mLock);
if (!mStandby) {
@@ -230,6 +323,64 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::standby()
return result;
}
+status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ String8 key = String8("a2dp_sink_address");
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string());
+
+ if (param.get(key, value) == NO_ERROR) {
+ if (value.length() != strlen("00:00:00:00:00:00")) {
+ status = BAD_VALUE;
+ } else {
+ setAddress(value.string());
+ }
+ param.remove(key);
+ }
+ key = String8("closing");
+ if (param.get(key, value) == NO_ERROR) {
+ mClosing = (value == "true");
+ param.remove(key);
+ }
+ key = AudioParameter::keyRouting;
+ if (param.getInt(key, device) == NO_ERROR) {
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) {
+ mDevice = device;
+ status = NO_ERROR;
+ } else {
+ status = BAD_VALUE;
+ }
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8("a2dp_sink_address");
+
+ if (param.get(key, value) == NO_ERROR) {
+ value = mA2dpAddress;
+ param.add(key, value);
+ }
+ key = AudioParameter::keyRouting;
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address)
{
Mutex::Autolock lock(mLock);
@@ -260,12 +411,14 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enable
status_t A2dpAudioInterface::A2dpAudioStreamOut::close()
{
Mutex::Autolock lock(mLock);
+ LOGV("A2dpAudioStreamOut::close() calling close_l()");
return close_l();
}
status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l()
{
if (mData) {
+ LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)");
a2dp_cleanup(mData);
mData = NULL;
}
@@ -277,5 +430,4 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<Strin
return NO_ERROR;
}
-
}; // namespace android
diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h
index 091e775..35a6e11 100644
--- a/libs/audioflinger/A2dpAudioInterface.h
+++ b/libs/audioflinger/A2dpAudioInterface.h
@@ -32,38 +32,44 @@ class A2dpAudioInterface : public AudioHardwareBase
class A2dpAudioStreamOut;
public:
- A2dpAudioInterface();
+ A2dpAudioInterface(AudioHardwareInterface* hw);
virtual ~A2dpAudioInterface();
virtual status_t initCheck();
virtual status_t setVoiceVolume(float volume);
virtual status_t setMasterVolume(float volume);
+ virtual status_t setMode(int mode);
+
// mic mute
virtual status_t setMicMute(bool state);
virtual status_t getMicMute(bool* state);
- // Temporary interface, do not use
- // TODO: Replace with a more generic key:value get/set mechanism
- virtual status_t setParameter(const char *key, const char *value);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
// create I/O streams
virtual AudioStreamOut* openOutputStream(
- int format=0,
- int channelCount=0,
- uint32_t sampleRate=0,
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
virtual AudioStreamIn* openInputStream(
- int inputSource,
- int format,
- int channelCount,
- uint32_t sampleRate,
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
status_t *status,
AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
+// static AudioHardwareInterface* createA2dpInterface();
protected:
- virtual status_t doRouting();
virtual status_t dump(int fd, const Vector<String16>& args);
private:
@@ -71,19 +77,22 @@ private:
public:
A2dpAudioStreamOut();
virtual ~A2dpAudioStreamOut();
- status_t set(int format,
- int channelCount,
- uint32_t sampleRate);
+ status_t set(uint32_t device,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate);
virtual uint32_t sampleRate() const { return 44100; }
// SBC codec wants a multiple of 512
virtual size_t bufferSize() const { return 512 * 20; }
- virtual int channelCount() const { return 2; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
virtual int format() const { return AudioSystem::PCM_16_BIT; }
virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; }
- virtual status_t setVolume(float volume) { return INVALID_OPERATION; }
+ virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
virtual ssize_t write(const void* buffer, size_t bytes);
status_t standby();
virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
private:
friend class A2dpAudioInterface;
@@ -102,11 +111,19 @@ private:
void* mData;
Mutex mLock;
bool mBluetoothEnabled;
+ uint32_t mDevice;
+ bool mClosing;
};
+ friend class A2dpAudioStreamOut;
+
A2dpAudioStreamOut* mOutput;
+ AudioHardwareInterface *mHardwareInterface;
+ char mA2dpAddress[20];
+ bool mBluetoothEnabled;
};
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk
index 50d516b..f5c03bb 100644
--- a/libs/audioflinger/Android.mk
+++ b/libs/audioflinger/Android.mk
@@ -1,16 +1,30 @@
LOCAL_PATH:= $(call my-dir)
+#AUDIO_POLICY_TEST := true
+#ENABLE_AUDIO_DUMP := true
+
include $(CLEAR_VARS)
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ ENABLE_AUDIO_DUMP := true
+endif
+
+
LOCAL_SRC_FILES:= \
AudioHardwareGeneric.cpp \
AudioHardwareStub.cpp \
- AudioDumpInterface.cpp \
AudioHardwareInterface.cpp
+ifeq ($(ENABLE_AUDIO_DUMP),true)
+ LOCAL_SRC_FILES += AudioDumpInterface.cpp
+ LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP
+endif
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ libbinder \
libmedia \
libhardware_legacy
@@ -20,8 +34,44 @@ endif
LOCAL_MODULE:= libaudiointerface
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_SRC_FILES += A2dpAudioInterface.cpp
+ LOCAL_SHARED_LIBRARIES += liba2dp
+ LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
+ LOCAL_C_INCLUDES += $(call include-path-for, bluez)
+endif
+
include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AudioPolicyManagerGeneric.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libmedia
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_MODULE:= libaudiopolicygeneric
+
+ifeq ($(BOARD_HAVE_BLUETOOTH),true)
+ LOCAL_CFLAGS += -DWITH_A2DP
+endif
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
@@ -29,28 +79,45 @@ LOCAL_SRC_FILES:= \
AudioMixer.cpp.arm \
AudioResampler.cpp.arm \
AudioResamplerSinc.cpp.arm \
- AudioResamplerCubic.cpp.arm
+ AudioResamplerCubic.cpp.arm \
+ AudioPolicyService.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ libbinder \
libmedia \
- libhardware_legacy
+ libhardware_legacy \
+ libaudiopolicygeneric
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
LOCAL_STATIC_LIBRARIES += libaudiointerface
+ LOCAL_CFLAGS += -DGENERIC_AUDIO
+else
+ LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy
+endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
else
- LOCAL_SHARED_LIBRARIES += libaudio
+ LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_MODULE:= libaudioflinger
ifeq ($(BOARD_HAVE_BLUETOOTH),true)
- LOCAL_SRC_FILES += A2dpAudioInterface.cpp
- LOCAL_SHARED_LIBRARIES += liba2dp
LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP
- LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs)
- LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils)
+ LOCAL_SHARED_LIBRARIES += liba2dp
+endif
+
+ifeq ($(AUDIO_POLICY_TEST),true)
+ LOCAL_CFLAGS += -DAUDIO_POLICY_TEST
+endif
+
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
endif
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp
index b4940cb..858e5aa 100644
--- a/libs/audioflinger/AudioDumpInterface.cpp
+++ b/libs/audioflinger/AudioDumpInterface.cpp
@@ -16,6 +16,7 @@
*/
#define LOG_TAG "AudioFlingerDump"
+//#define LOG_NDEBUG 0
#include <stdint.h>
#include <sys/types.h>
@@ -28,68 +29,240 @@
namespace android {
-bool gFirst = true; // true if first write after a standby
-
// ----------------------------------------------------------------------------
AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw)
+ : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8(""))
{
if(hw == 0) {
LOGE("Dump construct hw = 0");
}
mFinalInterface = hw;
- mStreamOut = 0;
+ LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface);
}
AudioDumpInterface::~AudioDumpInterface()
{
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ closeOutputStream((AudioStreamOut *)mOutputs[i]);
+ }
if(mFinalInterface) delete mFinalInterface;
- if(mStreamOut) delete mStreamOut;
}
AudioStreamOut* AudioDumpInterface::openOutputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status)
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
+{
+ AudioStreamOut* outFinal = NULL;
+ int lFormat = AudioSystem::PCM_16_BIT;
+ uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ uint32_t lRate = 44100;
+
+
+ if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) {
+ outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status);
+ if (outFinal != 0) {
+ lFormat = outFinal->format();
+ lChannels = outFinal->channels();
+ lRate = outFinal->sampleRate();
+ if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) {
+ mFirstHwOutput = false;
+ }
+ }
+ } else {
+ if (format != 0 && *format != 0) {
+ lFormat = *format;
+ } else {
+ lFormat = AudioSystem::PCM_16_BIT;
+ }
+ if (channels != 0 && *channels != 0) {
+ lChannels = *channels;
+ } else {
+ lChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ }
+ if (sampleRate != 0 && *sampleRate != 0) {
+ lRate = *sampleRate;
+ } else {
+ lRate = 44100;
+ }
+ if (status) *status = NO_ERROR;
+ }
+ LOGV("openOutputStream(), outFinal %p", outFinal);
+
+ AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal,
+ devices, lFormat, lChannels, lRate);
+ mOutputs.add(dumOutput);
+
+ return dumOutput;
+}
+
+void AudioDumpInterface::closeOutputStream(AudioStreamOut* out)
+{
+ AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out;
+
+ if (mOutputs.indexOf(dumpOut) < 0) {
+ LOGW("Attempt to close invalid output stream");
+ return;
+ }
+
+ LOGV("closeOutputStream() output %p", out);
+
+ dumpOut->standby();
+ if (dumpOut->finalStream() != NULL) {
+ mFinalInterface->closeOutputStream(dumpOut->finalStream());
+ mFirstHwOutput = true;
+ }
+
+ mOutputs.remove(dumpOut);
+ delete dumpOut;
+}
+
+AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels,
+ uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
- AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status);
+ AudioStreamIn* inFinal = NULL;
+ int lFormat = AudioSystem::PCM_16_BIT;
+ uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO;
+ uint32_t lRate = 8000;
- if(outFinal) {
- mStreamOut = new AudioStreamOutDump(outFinal);
- return mStreamOut;
+
+ if (mInputs.size() == 0) {
+ inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics);
+ if (inFinal == 0) return 0;
+
+ lFormat = inFinal->format();
+ lChannels = inFinal->channels();
+ lRate = inFinal->sampleRate();
} else {
- LOGE("Dump outFinal=0");
- return 0;
+ if (format != 0 && *format != 0) lFormat = *format;
+ if (channels != 0 && *channels != 0) lChannels = *channels;
+ if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate;
+ if (status) *status = NO_ERROR;
}
+ LOGV("openInputStream(), inFinal %p", inFinal);
+
+ AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal,
+ devices, lFormat, lChannels, lRate);
+ mInputs.add(dumInput);
+
+ return dumInput;
}
+void AudioDumpInterface::closeInputStream(AudioStreamIn* in)
+{
+ AudioStreamInDump *dumpIn = (AudioStreamInDump *)in;
+
+ if (mInputs.indexOf(dumpIn) < 0) {
+ LOGW("Attempt to close invalid input stream");
+ return;
+ }
+ dumpIn->standby();
+ if (dumpIn->finalStream() != NULL) {
+ mFinalInterface->closeInputStream(dumpIn->finalStream());
+ }
+
+ mInputs.remove(dumpIn);
+ delete dumpIn;
+}
+
+
+status_t AudioDumpInterface::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ int valueInt;
+ LOGV("setParameters %s", keyValuePairs.string());
+
+ if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
+ mFileName = value;
+ param.remove(String8("test_cmd_file_name"));
+ }
+ if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
+ Mutex::Autolock _l(mLock);
+ param.remove(String8("test_cmd_policy"));
+ mPolicyCommands = param.toString();
+ LOGV("test_cmd_policy command %s written", mPolicyCommands.string());
+ return NO_ERROR;
+ }
+
+ if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs);
+ return NO_ERROR;
+}
+
+String8 AudioDumpInterface::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ AudioParameter response;
+ String8 value;
+
+// LOGV("getParameters %s", keys.string());
+ if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) {
+ Mutex::Autolock _l(mLock);
+ if (mPolicyCommands.length() != 0) {
+ response = AudioParameter(mPolicyCommands);
+ response.addInt(String8("test_cmd_policy"), 1);
+ } else {
+ response.addInt(String8("test_cmd_policy"), 0);
+ }
+ param.remove(String8("test_cmd_policy"));
+// LOGV("test_cmd_policy command %s read", mPolicyCommands.string());
+ }
+
+ if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) {
+ response.add(String8("test_cmd_file_name"), mFileName);
+ param.remove(String8("test_cmd_file_name"));
+ }
+
+ String8 keyValuePairs = response.toString();
+
+ if (param.size() && mFinalInterface != 0 ) {
+ keyValuePairs += ";";
+ keyValuePairs += mFinalInterface->getParameters(param.toString());
+ }
+
+ return keyValuePairs;
+}
+
// ----------------------------------------------------------------------------
-AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream)
+AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamOut* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate)
+ : mInterface(interface), mId(id),
+ mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices),
+ mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0)
{
- mFinalStream = finalStream;
- mOutFile = 0;
+ LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
}
AudioStreamOutDump::~AudioStreamOutDump()
{
+ LOGV("AudioStreamOutDump destructor");
Close();
- delete mFinalStream;
}
ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
{
ssize_t ret;
- ret = mFinalStream->write(buffer, bytes);
- if(!mOutFile && gFirst) {
- gFirst = false;
- // check if dump file exist
- mOutFile = fopen(FLINGER_DUMP_NAME, "r");
- if(mOutFile) {
- fclose(mOutFile);
- mOutFile = fopen(FLINGER_DUMP_NAME, "ab");
+ if (mFinalStream) {
+ ret = mFinalStream->write(buffer, bytes);
+ } else {
+ usleep((bytes * 1000000) / frameSize() / sampleRate());
+ ret = bytes;
+ }
+ if(!mOutFile) {
+ if (mInterface->fileName() != "") {
+ char name[255];
+ sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount);
+ mOutFile = fopen(name, "wb");
+ LOGV("Opening dump file %s, fh %p", name, mOutFile);
}
}
if (mOutFile) {
@@ -100,13 +273,105 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes)
status_t AudioStreamOutDump::standby()
{
+ LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream);
+
Close();
- gFirst = true;
- return mFinalStream->standby();
+ if (mFinalStream != 0 ) return mFinalStream->standby();
+ return NO_ERROR;
+}
+
+uint32_t AudioStreamOutDump::sampleRate() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->sampleRate();
+ return mSampleRate;
+}
+
+size_t AudioStreamOutDump::bufferSize() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->bufferSize();
+ return mBufferSize;
+}
+
+uint32_t AudioStreamOutDump::channels() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->channels();
+ return mChannels;
+}
+int AudioStreamOutDump::format() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->format();
+ return mFormat;
+}
+uint32_t AudioStreamOutDump::latency() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->latency();
+ return 0;
+}
+status_t AudioStreamOutDump::setVolume(float left, float right)
+{
+ if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right);
+ return NO_ERROR;
+}
+status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs)
+{
+ LOGV("AudioStreamOutDump::setParameters %s", keyValuePairs.string());
+
+ if (mFinalStream != 0 ) {
+ return mFinalStream->setParameters(keyValuePairs);
+ }
+
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 value;
+ int valueInt;
+ status_t status = NO_ERROR;
+
+ if (param.getInt(String8("set_id"), valueInt) == NO_ERROR) {
+ mId = valueInt;
+ }
+
+ if (param.getInt(String8("format"), valueInt) == NO_ERROR) {
+ if (mOutFile == 0) {
+ mFormat = valueInt;
+ } else {
+ status = INVALID_OPERATION;
+ }
+ }
+ if (param.getInt(String8("channels"), valueInt) == NO_ERROR) {
+ if (valueInt == AudioSystem::CHANNEL_OUT_STEREO || valueInt == AudioSystem::CHANNEL_OUT_MONO) {
+ mChannels = valueInt;
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) {
+ if (valueInt > 0 && valueInt <= 48000) {
+ if (mOutFile == 0) {
+ mSampleRate = valueInt;
+ } else {
+ status = INVALID_OPERATION;
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ }
+ return status;
}
+String8 AudioStreamOutDump::getParameters(const String8& keys)
+{
+ if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
+
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
-void AudioStreamOutDump::Close(void)
+status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args)
+{
+ if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
+ return NO_ERROR;
+}
+
+void AudioStreamOutDump::Close()
{
if(mOutFile) {
fclose(mOutFile);
@@ -114,4 +379,141 @@ void AudioStreamOutDump::Close(void)
}
}
+// ----------------------------------------------------------------------------
+
+AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamIn* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate)
+ : mInterface(interface), mId(id),
+ mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices),
+ mBufferSize(1024), mFinalStream(finalStream), mInFile(0)
+{
+ LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream);
+}
+
+
+AudioStreamInDump::~AudioStreamInDump()
+{
+ Close();
+}
+
+ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes)
+{
+ if (mFinalStream) {
+ return mFinalStream->read(buffer, bytes);
+ }
+
+ usleep((bytes * 1000000) / frameSize() / sampleRate());
+
+ if(!mInFile) {
+ char name[255];
+ strcpy(name, "/sdcard/music/sine440");
+ if (channels() == AudioSystem::CHANNEL_IN_MONO) {
+ strcat(name, "_mo");
+ } else {
+ strcat(name, "_st");
+ }
+ if (format() == AudioSystem::PCM_16_BIT) {
+ strcat(name, "_16b");
+ } else {
+ strcat(name, "_8b");
+ }
+ if (sampleRate() < 16000) {
+ strcat(name, "_8k");
+ } else if (sampleRate() < 32000) {
+ strcat(name, "_22k");
+ } else if (sampleRate() < 48000) {
+ strcat(name, "_44k");
+ } else {
+ strcat(name, "_48k");
+ }
+ strcat(name, ".wav");
+ mInFile = fopen(name, "rb");
+ LOGV("Opening dump file %s, fh %p", name, mInFile);
+ if (mInFile) {
+ fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ }
+
+ }
+ if (mInFile) {
+ ssize_t bytesRead = fread(buffer, bytes, 1, mInFile);
+ if (bytesRead != bytes) {
+ fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET);
+ fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile);
+ }
+ }
+ return bytes;
+}
+
+status_t AudioStreamInDump::standby()
+{
+ LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream);
+
+ Close();
+ if (mFinalStream != 0 ) return mFinalStream->standby();
+ return NO_ERROR;
+}
+
+status_t AudioStreamInDump::setGain(float gain)
+{
+ if (mFinalStream != 0 ) return mFinalStream->setGain(gain);
+ return NO_ERROR;
+}
+
+uint32_t AudioStreamInDump::sampleRate() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->sampleRate();
+ return mSampleRate;
+}
+
+size_t AudioStreamInDump::bufferSize() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->bufferSize();
+ return mBufferSize;
+}
+
+uint32_t AudioStreamInDump::channels() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->channels();
+ return mChannels;
+}
+
+int AudioStreamInDump::format() const
+{
+ if (mFinalStream != 0 ) return mFinalStream->format();
+ return mFormat;
+}
+
+status_t AudioStreamInDump::setParameters(const String8& keyValuePairs)
+{
+ LOGV("AudioStreamInDump::setParameters()");
+ if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs);
+ return NO_ERROR;
+}
+
+String8 AudioStreamInDump::getParameters(const String8& keys)
+{
+ if (mFinalStream != 0 ) return mFinalStream->getParameters(keys);
+
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
+status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args)
+{
+ if (mFinalStream != 0 ) return mFinalStream->dump(fd, args);
+ return NO_ERROR;
+}
+
+void AudioStreamInDump::Close()
+{
+ if(mInFile) {
+ fclose(mInFile);
+ mInFile = 0;
+ }
+}
}; // namespace android
diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h
index b72c94e..1136ce1 100644
--- a/libs/audioflinger/AudioDumpInterface.h
+++ b/libs/audioflinger/AudioDumpInterface.h
@@ -20,35 +20,95 @@
#include <stdint.h>
#include <sys/types.h>
+#include <utils/String8.h>
+#include <utils/SortedVector.h>
#include <hardware_legacy/AudioHardwareBase.h>
namespace android {
-#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump
+#define AUDIO_DUMP_WAVE_HDR_SIZE 44
+
+class AudioDumpInterface;
class AudioStreamOutDump : public AudioStreamOut {
public:
- AudioStreamOutDump( AudioStreamOut* FinalStream);
+ AudioStreamOutDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamOut* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate);
~AudioStreamOutDump();
- virtual ssize_t write(const void* buffer, size_t bytes);
-
- virtual uint32_t sampleRate() const { return mFinalStream->sampleRate(); }
- virtual size_t bufferSize() const { return mFinalStream->bufferSize(); }
- virtual int channelCount() const { return mFinalStream->channelCount(); }
- virtual int format() const { return mFinalStream->format(); }
- virtual uint32_t latency() const { return mFinalStream->latency(); }
- virtual status_t setVolume(float volume)
- { return mFinalStream->setVolume(volume); }
+
+ virtual ssize_t write(const void* buffer, size_t bytes);
+ virtual uint32_t sampleRate() const;
+ virtual size_t bufferSize() const;
+ virtual uint32_t channels() const;
+ virtual int format() const;
+ virtual uint32_t latency() const;
+ virtual status_t setVolume(float left, float right);
virtual status_t standby();
- virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t dump(int fd, const Vector<String16>& args);
void Close(void);
+ AudioStreamOut* finalStream() { return mFinalStream; }
+ uint32_t device() { return mDevice; }
+ int getId() { return mId; }
private:
+ AudioDumpInterface *mInterface;
+ int mId;
+ uint32_t mSampleRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mLatency; //
+ uint32_t mDevice; // current device this output is routed to
+ size_t mBufferSize;
AudioStreamOut *mFinalStream;
- FILE *mOutFile; // output file
+ FILE *mOutFile; // output file
+ int mFileCount;
};
+class AudioStreamInDump : public AudioStreamIn {
+public:
+ AudioStreamInDump(AudioDumpInterface *interface,
+ int id,
+ AudioStreamIn* finalStream,
+ uint32_t devices,
+ int format,
+ uint32_t channels,
+ uint32_t sampleRate);
+ ~AudioStreamInDump();
+
+ virtual uint32_t sampleRate() const;
+ virtual size_t bufferSize() const;
+ virtual uint32_t channels() const;
+ virtual int format() const;
+
+ virtual status_t setGain(float gain);
+ virtual ssize_t read(void* buffer, ssize_t bytes);
+ virtual status_t standby();
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
+ virtual status_t dump(int fd, const Vector<String16>& args);
+ void Close(void);
+ AudioStreamIn* finalStream() { return mFinalStream; }
+ uint32_t device() { return mDevice; }
+
+private:
+ AudioDumpInterface *mInterface;
+ int mId;
+ uint32_t mSampleRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mDevice; // current device this output is routed to
+ size_t mBufferSize;
+ AudioStreamIn *mFinalStream;
+ FILE *mInFile; // output file
+};
class AudioDumpInterface : public AudioHardwareBase
{
@@ -56,10 +116,13 @@ class AudioDumpInterface : public AudioHardwareBase
public:
AudioDumpInterface(AudioHardwareInterface* hw);
virtual AudioStreamOut* openOutputStream(
- int format=0,
- int channelCount=0,
- uint32_t sampleRate=0,
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
+
virtual ~AudioDumpInterface();
virtual status_t initCheck()
@@ -75,21 +138,25 @@ public:
virtual status_t getMicMute(bool* state)
{return mFinalInterface->getMicMute(state);}
- virtual status_t setParameter(const char* key, const char* value)
- {return mFinalInterface->setParameter(key, value);}
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
- virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount,
- uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics)
- { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); }
+ virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels,
+ uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); }
+ String8 fileName() const { return mFileName; }
protected:
- virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);}
-
- AudioHardwareInterface *mFinalInterface;
- AudioStreamOutDump *mStreamOut;
+ AudioHardwareInterface *mFinalInterface;
+ SortedVector<AudioStreamOutDump *> mOutputs;
+ bool mFirstHwOutput;
+ SortedVector<AudioStreamInDump *> mInputs;
+ Mutex mLock;
+ String8 mPolicyCommands;
+ String8 mFileName;
};
}; // namespace android
diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp
index da7cc8a..e2b6b51 100644
--- a/libs/audioflinger/AudioFlinger.cpp
+++ b/libs/audioflinger/AudioFlinger.cpp
@@ -24,10 +24,10 @@
#include <sys/time.h>
#include <sys/resource.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/threads.h>
@@ -71,15 +71,9 @@ static const float MAX_GAIN = 4096.0f;
static const int8_t kMaxTrackRetries = 50;
static const int8_t kMaxTrackStartupRetries = 50;
-static const int kStartSleepTime = 30000;
-static const int kStopSleepTime = 30000;
-
static const int kDumpLockRetries = 50;
static const int kDumpLockSleep = 20000;
-// Maximum number of pending buffers allocated by OutputTrack::write()
-static const uint8_t kMaxOutputTrackBuffers = 5;
-
#define AUDIOFLINGER_SECURITY_ENABLED 1
@@ -121,132 +115,41 @@ static bool settingsAllowed() {
AudioFlinger::AudioFlinger()
: BnAudioFlinger(),
- mAudioHardware(0), mA2dpAudioInterface(0), mA2dpEnabled(false), mNotifyA2dpChange(false),
- mForcedSpeakerCount(0), mA2dpDisableCount(0), mA2dpSuppressed(false), mForcedRoute(0),
- mRouteRestoreTime(0), mMusicMuteSaved(false)
+ mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0)
{
mHardwareStatus = AUDIO_HW_IDLE;
+
mAudioHardware = AudioHardwareInterface::create();
+
mHardwareStatus = AUDIO_HW_INIT;
if (mAudioHardware->initCheck() == NO_ERROR) {
// open 16-bit output stream for s/w mixer
- mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status_t status;
- AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);
- mHardwareStatus = AUDIO_HW_IDLE;
- if (hwOutput) {
- mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE);
- } else {
- LOGE("Failed to initialize hardware output stream, status: %d", status);
- }
-
-#ifdef WITH_A2DP
- // Create A2DP interface
- mA2dpAudioInterface = new A2dpAudioInterface();
- AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status);
- if (a2dpOutput) {
- mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP);
- if (hwOutput) {
- uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate();
- MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread,
- hwOutput->sampleRate(),
- AudioSystem::PCM_16_BIT,
- hwOutput->channelCount(),
- frameCount);
- mHardwareMixerThread->setOuputTrack(a2dpOutTrack);
- }
- } else {
- LOGE("Failed to initialize A2DP output stream, status: %d", status);
- }
-#endif
-
- // FIXME - this should come from settings
- setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL);
- setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL);
- setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL);
+
setMode(AudioSystem::MODE_NORMAL);
setMasterVolume(1.0f);
setMasterMute(false);
-
- // Start record thread
- mAudioRecordThread = new AudioRecordThread(mAudioHardware, this);
- if (mAudioRecordThread != 0) {
- mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO);
- }
- } else {
+ } else {
LOGE("Couldn't even initialize the stubbed audio hardware!");
}
}
AudioFlinger::~AudioFlinger()
{
- if (mAudioRecordThread != 0) {
- mAudioRecordThread->exit();
- mAudioRecordThread.clear();
+ while (!mRecordThreads.isEmpty()) {
+ // closeInput() will remove first entry from mRecordThreads
+ closeInput(mRecordThreads.keyAt(0));
}
- mHardwareMixerThread.clear();
- delete mAudioHardware;
- // deleting mA2dpAudioInterface also deletes mA2dpOutput;
-#ifdef WITH_A2DP
- mA2dpMixerThread.clear();
- delete mA2dpAudioInterface;
-#endif
-}
-
-
-#ifdef WITH_A2DP
-// setA2dpEnabled_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::setA2dpEnabled_l(bool enable)
-{
- SortedVector < sp<MixerThread::Track> > tracks;
- SortedVector < wp<MixerThread::Track> > activeTracks;
-
- LOGD_IF(enable, "set output to A2DP\n");
- LOGD_IF(!enable, "set output to hardware audio\n");
-
- // Transfer tracks playing on MUSIC stream from one mixer to the other
- if (enable) {
- mHardwareMixerThread->getTracks_l(tracks, activeTracks);
- mA2dpMixerThread->putTracks_l(tracks, activeTracks);
- } else {
- mA2dpMixerThread->getTracks_l(tracks, activeTracks);
- mHardwareMixerThread->putTracks_l(tracks, activeTracks);
- mA2dpMixerThread->mOutput->standby();
+ while (!mPlaybackThreads.isEmpty()) {
+ // closeOutput() will remove first entry from mPlaybackThreads
+ closeOutput(mPlaybackThreads.keyAt(0));
}
- mA2dpEnabled = enable;
- mNotifyA2dpChange = true;
- mWaitWorkCV.broadcast();
-}
-
-// checkA2dpEnabledChange_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::checkA2dpEnabledChange_l()
-{
- if (mNotifyA2dpChange) {
- // Notify AudioSystem of the A2DP activation/deactivation
- size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- sp<IBinder> binder = mNotificationClients.itemAt(i).promote();
- if (binder != NULL) {
- LOGV("Notifying output change to client %p", binder.get());
- sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
- client->a2dpEnabledChanged(mA2dpEnabled);
- }
- }
- mNotifyA2dpChange = false;
+ if (mAudioHardware) {
+ delete mAudioHardware;
}
}
-#endif // WITH_A2DP
-bool AudioFlinger::streamForcedToSpeaker(int streamType)
-{
- // NOTE that streams listed here must not be routed to A2DP by default:
- // AudioSystem::routedToA2dpOutput(streamType) == false
- return (streamType == AudioSystem::RING ||
- streamType == AudioSystem::ALARM ||
- streamType == AudioSystem::NOTIFICATION ||
- streamType == AudioSystem::ENFORCED_AUDIBLE);
-}
+
status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
{
@@ -276,10 +179,7 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
char buffer[SIZE];
String8 result;
int hardwareStatus = mHardwareStatus;
-
- if (hardwareStatus == AUDIO_HW_IDLE && mHardwareMixerThread->mStandby) {
- hardwareStatus = AUDIO_HW_STANDBY;
- }
+
snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus);
result.append(buffer);
write(fd, result.string(), result.size());
@@ -337,13 +237,16 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
dumpClients(fd, args);
dumpInternals(fd, args);
- mHardwareMixerThread->dump(fd, args);
-#ifdef WITH_A2DP
- mA2dpMixerThread->dump(fd, args);
-#endif
- // dump record client
- if (mAudioRecordThread != 0) mAudioRecordThread->dump(fd, args);
+ // dump playback threads
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->dump(fd, args);
+ }
+
+ // dump record threads
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->dump(fd, args);
+ }
if (mAudioHardware) {
mAudioHardware->dumpState(fd, args);
@@ -353,6 +256,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
return NO_ERROR;
}
+
// IAudioFlinger interface
@@ -365,9 +269,10 @@ sp<IAudioTrack> AudioFlinger::createTrack(
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
+ int output,
status_t *status)
{
- sp<MixerThread::Track> track;
+ sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
sp<Client> client;
wp<Client> wclient;
@@ -381,6 +286,12 @@ sp<IAudioTrack> AudioFlinger::createTrack(
{
Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGE("unknown output thread");
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
wclient = mClients.valueFor(pid);
@@ -390,16 +301,8 @@ sp<IAudioTrack> AudioFlinger::createTrack(
client = new Client(this, pid);
mClients.add(pid, client);
}
-#ifdef WITH_A2DP
- if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) {
- track = mA2dpMixerThread->createTrack_l(client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer, &lStatus);
- } else
-#endif
- {
- track = mHardwareMixerThread->createTrack_l(client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer, &lStatus);
- }
+ track = thread->createTrack_l(client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer, &lStatus);
}
if (lStatus == NO_ERROR) {
trackHandle = new TrackHandle(track);
@@ -416,52 +319,57 @@ Exit:
uint32_t AudioFlinger::sampleRate(int output) const
{
-#ifdef WITH_A2DP
- if (output == AudioSystem::AUDIO_OUTPUT_A2DP) {
- return mA2dpMixerThread->sampleRate();
- }
-#endif
- return mHardwareMixerThread->sampleRate();
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("sampleRate() unknown thread %d", output);
+ return 0;
+ }
+ return thread->sampleRate();
}
int AudioFlinger::channelCount(int output) const
{
-#ifdef WITH_A2DP
- if (output == AudioSystem::AUDIO_OUTPUT_A2DP) {
- return mA2dpMixerThread->channelCount();
- }
-#endif
- return mHardwareMixerThread->channelCount();
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("channelCount() unknown thread %d", output);
+ return 0;
+ }
+ return thread->channelCount();
}
int AudioFlinger::format(int output) const
{
-#ifdef WITH_A2DP
- if (output == AudioSystem::AUDIO_OUTPUT_A2DP) {
- return mA2dpMixerThread->format();
- }
-#endif
- return mHardwareMixerThread->format();
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("format() unknown thread %d", output);
+ return 0;
+ }
+ return thread->format();
}
size_t AudioFlinger::frameCount(int output) const
{
-#ifdef WITH_A2DP
- if (output == AudioSystem::AUDIO_OUTPUT_A2DP) {
- return mA2dpMixerThread->frameCount();
- }
-#endif
- return mHardwareMixerThread->frameCount();
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("frameCount() unknown thread %d", output);
+ return 0;
+ }
+ return thread->frameCount();
}
uint32_t AudioFlinger::latency(int output) const
{
-#ifdef WITH_A2DP
- if (output == AudioSystem::AUDIO_OUTPUT_A2DP) {
- return mA2dpMixerThread->latency();
- }
-#endif
- return mHardwareMixerThread->latency();
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ LOGW("latency() unknown thread %d", output);
+ return 0;
+ }
+ return thread->latency();
}
status_t AudioFlinger::setMasterVolume(float value)
@@ -478,94 +386,12 @@ status_t AudioFlinger::setMasterVolume(float value)
value = 1.0f;
}
mHardwareStatus = AUDIO_HW_IDLE;
- mHardwareMixerThread->setMasterVolume(value);
-#ifdef WITH_A2DP
- mA2dpMixerThread->setMasterVolume(value);
-#endif
-
- return NO_ERROR;
-}
-
-status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask)
-{
- status_t err = NO_ERROR;
-
- // check calling permissions
- if (!settingsAllowed()) {
- return PERMISSION_DENIED;
- }
- if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) {
- LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask);
- return BAD_VALUE;
- }
-
-#ifdef WITH_A2DP
- LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(),
- IPCThreadState::self()->getCallingPid());
- if (mode == AudioSystem::MODE_NORMAL &&
- (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) {
- AutoMutex lock(&mLock);
-
- bool enableA2dp = false;
- if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) {
- enableA2dp = true;
- }
- if (mA2dpDisableCount > 0) {
- mA2dpSuppressed = enableA2dp;
- } else {
- setA2dpEnabled_l(enableA2dp);
- }
- LOGV("setOutput done\n");
- }
- // setRouting() is always called at least for mode == AudioSystem::MODE_IN_CALL when
- // SCO is enabled, whatever current mode is so we can safely handle A2DP disabling only
- // in this case to avoid doing it several times.
- if (mode == AudioSystem::MODE_IN_CALL &&
- (mask & AudioSystem::ROUTE_BLUETOOTH_SCO)) {
- AutoMutex lock(&mLock);
- handleRouteDisablesA2dp_l(routes);
- }
-#endif
- // do nothing if only A2DP routing is affected
- mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP;
- if (mask) {
- AutoMutex lock(mHardwareLock);
- mHardwareStatus = AUDIO_HW_GET_ROUTING;
- uint32_t r;
- err = mAudioHardware->getRouting(mode, &r);
- if (err == NO_ERROR) {
- r = (r & ~mask) | (routes & mask);
- if (mode == AudioSystem::MODE_NORMAL ||
- (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) {
- mSavedRoute = r;
- r |= mForcedRoute;
- LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute);
- }
- mHardwareStatus = AUDIO_HW_SET_ROUTING;
- err = mAudioHardware->setRouting(mode, r);
- }
- mHardwareStatus = AUDIO_HW_IDLE;
- }
- return err;
-}
+ mMasterVolume = value;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMasterVolume(value);
-uint32_t AudioFlinger::getRouting(int mode) const
-{
- uint32_t routes = 0;
- if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) {
- if (mode == AudioSystem::MODE_NORMAL ||
- (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) {
- routes = mSavedRoute;
- } else {
- mHardwareStatus = AUDIO_HW_GET_ROUTING;
- mAudioHardware->getRouting(mode, &routes);
- mHardwareStatus = AUDIO_HW_IDLE;
- }
- } else {
- LOGW("Illegal value: getRouting(%d)", mode);
- }
- return routes;
+ return NO_ERROR;
}
status_t AudioFlinger::setMode(int mode)
@@ -586,15 +412,6 @@ status_t AudioFlinger::setMode(int mode)
return ret;
}
-int AudioFlinger::getMode() const
-{
- int mode = AudioSystem::MODE_INVALID;
- mHardwareStatus = AUDIO_HW_SET_MODE;
- mAudioHardware->getMode(&mode);
- mHardwareStatus = AUDIO_HW_IDLE;
- return mode;
-}
-
status_t AudioFlinger::setMicMute(bool state)
{
// check calling permissions
@@ -624,36 +441,46 @@ status_t AudioFlinger::setMasterMute(bool muted)
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- mHardwareMixerThread->setMasterMute(muted);
-#ifdef WITH_A2DP
- mA2dpMixerThread->setMasterMute(muted);
-#endif
+
+ mMasterMute = muted;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setMasterMute(muted);
+
return NO_ERROR;
}
float AudioFlinger::masterVolume() const
{
- return mHardwareMixerThread->masterVolume();
+ return mMasterVolume;
}
bool AudioFlinger::masterMute() const
{
- return mHardwareMixerThread->masterMute();
+ return mMasterMute;
}
-status_t AudioFlinger::setStreamVolume(int stream, float value)
+status_t AudioFlinger::setStreamVolume(int stream, float value, int output)
{
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
}
- if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
- uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
return BAD_VALUE;
}
+ AutoMutex lock(mLock);
+ PlaybackThread *thread = NULL;
+ if (output) {
+ thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+ }
+
status_t ret = NO_ERROR;
+
if (stream == AudioSystem::VOICE_CALL ||
stream == AudioSystem::BLUETOOTH_SCO) {
float hwValue;
@@ -670,12 +497,18 @@ status_t AudioFlinger::setStreamVolume(int stream, float value)
mHardwareStatus = AUDIO_SET_VOICE_VOLUME;
ret = mAudioHardware->setVoiceVolume(hwValue);
mHardwareStatus = AUDIO_HW_IDLE;
+
}
- mHardwareMixerThread->setStreamVolume(stream, value);
-#ifdef WITH_A2DP
- mA2dpMixerThread->setStreamVolume(stream, value);
-#endif
+ mStreamTypes[stream].volume = value;
+
+ if (thread == NULL) {
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value);
+
+ } else {
+ thread->setStreamVolume(stream, value);
+ }
return ret;
}
@@ -687,82 +520,116 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted)
return PERMISSION_DENIED;
}
- if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES ||
uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) {
return BAD_VALUE;
}
-#ifdef WITH_A2DP
- mA2dpMixerThread->setStreamMute(stream, muted);
-#endif
- if (stream == AudioSystem::MUSIC)
- {
- AutoMutex lock(&mHardwareLock);
- if (mForcedRoute != 0)
- mMusicMuteSaved = muted;
- else
- mHardwareMixerThread->setStreamMute(stream, muted);
- } else {
- mHardwareMixerThread->setStreamMute(stream, muted);
- }
+ mStreamTypes[stream].mute = muted;
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++)
+ mPlaybackThreads.valueAt(i)->setStreamMute(stream, muted);
return NO_ERROR;
}
-float AudioFlinger::streamVolume(int stream) const
+float AudioFlinger::streamVolume(int stream, int output) const
{
- if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
return 0.0f;
}
-
- float volume = mHardwareMixerThread->streamVolume(stream);
+
+ AutoMutex lock(mLock);
+ float volume;
+ if (output) {
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return 0.0f;
+ }
+ volume = thread->streamVolume(stream);
+ } else {
+ volume = mStreamTypes[stream].volume;
+ }
+
// remove correction applied by setStreamVolume()
if (stream == AudioSystem::VOICE_CALL) {
volume = (volume - 0.01) / 0.99 ;
}
-
+
return volume;
}
bool AudioFlinger::streamMute(int stream) const
{
- if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) {
+ if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) {
return true;
}
-
- if (stream == AudioSystem::MUSIC && mForcedRoute != 0)
- {
- return mMusicMuteSaved;
- }
- return mHardwareMixerThread->streamMute(stream);
+
+ return mStreamTypes[stream].mute;
}
bool AudioFlinger::isMusicActive() const
{
Mutex::Autolock _l(mLock);
- #ifdef WITH_A2DP
- if (isA2dpEnabled()) {
- return mA2dpMixerThread->isMusicActive_l();
- }
- #endif
- return mHardwareMixerThread->isMusicActive_l();
+ for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->isMusicActive()) {
+ return true;
+ }
+ }
+ return false;
}
-status_t AudioFlinger::setParameter(const char* key, const char* value)
+status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
{
- status_t result, result2;
- AutoMutex lock(mHardwareLock);
- mHardwareStatus = AUDIO_SET_PARAMETER;
-
- LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid());
- result = mAudioHardware->setParameter(key, value);
- if (mA2dpAudioInterface) {
- result2 = mA2dpAudioInterface->setParameter(key, value);
- if (result2)
- result = result2;
+ status_t result;
+
+ LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d",
+ ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
+ // check calling permissions
+ if (!settingsAllowed()) {
+ return PERMISSION_DENIED;
}
- mHardwareStatus = AUDIO_HW_IDLE;
- return result;
+
+ // ioHandle == 0 means the parameters are global to the audio hardware interface
+ if (ioHandle == 0) {
+ AutoMutex lock(mHardwareLock);
+ mHardwareStatus = AUDIO_SET_PARAMETER;
+ result = mAudioHardware->setParameters(keyValuePairs);
+ mHardwareStatus = AUDIO_HW_IDLE;
+ return result;
+ }
+
+ // Check if parameters are for an output
+ PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle);
+ if (playbackThread != NULL) {
+ return playbackThread->setParameters(keyValuePairs);
+ }
+
+ // Check if parameters are for an input
+ RecordThread *recordThread = checkRecordThread_l(ioHandle);
+ if (recordThread != NULL) {
+ return recordThread->setParameters(keyValuePairs);
+ }
+
+ return BAD_VALUE;
+}
+
+String8 AudioFlinger::getParameters(int ioHandle, const String8& keys)
+{
+// LOGV("getParameters() io %d, keys %s, tid %d, calling tid %d",
+// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
+
+ if (ioHandle == 0) {
+ return mAudioHardware->getParameters(keys);
+ }
+ PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle);
+ if (playbackThread != NULL) {
+ return playbackThread->getParameters(keys);
+ }
+ RecordThread *recordThread = checkRecordThread_l(ioHandle);
+ if (recordThread != NULL) {
+ return recordThread->getParameters(keys);
+ }
+ return String8("");
}
size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
@@ -772,7 +639,7 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int cha
void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
{
-
+
LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid());
Mutex::Autolock _l(mLock);
@@ -781,12 +648,21 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client)
LOGV("Adding notification client %p", binder.get());
binder->linkToDeath(this);
mNotificationClients.add(binder);
- client->a2dpEnabledChanged(isA2dpEnabled());
+ }
+
+ // the config change is always sent from playback or record threads to avoid deadlock
+ // with AudioSystem::gLock
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED);
+ }
+
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED);
}
}
void AudioFlinger::binderDied(const wp<IBinder>& who) {
-
+
LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
Mutex::Autolock _l(mLock);
@@ -801,6 +677,36 @@ void AudioFlinger::binderDied(const wp<IBinder>& who) {
}
}
+void AudioFlinger::audioConfigChanged(int event, const sp<ThreadBase>& thread, void *param2) {
+ Mutex::Autolock _l(mLock);
+ int ioHandle = 0;
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i) == thread) {
+ ioHandle = mPlaybackThreads.keyAt(i);
+ break;
+ }
+ }
+ if (ioHandle == 0) {
+ for (size_t i = 0; i < mRecordThreads.size(); i++) {
+ if (mRecordThreads.valueAt(i) == thread) {
+ ioHandle = mRecordThreads.keyAt(i);
+ break;
+ }
+ }
+ }
+
+ if (ioHandle != 0) {
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<IBinder> binder = mNotificationClients.itemAt(i);
+ LOGV("audioConfigChanged() Notifying change to client %p", binder.get());
+ sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
+ client->ioConfigChanged(event, ioHandle, param2);
+ }
+ }
+}
+
void AudioFlinger::removeClient(pid_t pid)
{
LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
@@ -808,147 +714,146 @@ void AudioFlinger::removeClient(pid_t pid)
mClients.removeItem(pid);
}
-bool AudioFlinger::isA2dpEnabled() const
+// ----------------------------------------------------------------------------
+
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger)
+ : Thread(false),
+ mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
+ mFormat(0), mFrameSize(1), mStandby(false)
{
- return mA2dpEnabled;
}
-void AudioFlinger::handleForcedSpeakerRoute(int command)
+AudioFlinger::ThreadBase::~ThreadBase()
{
- switch(command) {
- case ACTIVE_TRACK_ADDED:
- {
- AutoMutex lock(mHardwareLock);
- if (mForcedSpeakerCount++ == 0) {
- if (mForcedRoute == 0) {
- mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC);
- LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime);
- if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
- LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER);
- mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true);
- usleep(mHardwareMixerThread->latency()*1000);
- mHardwareStatus = AUDIO_HW_SET_ROUTING;
- mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER);
- mHardwareStatus = AUDIO_HW_IDLE;
- // delay track start so that audio hardware has time to siwtch routes
- usleep(kStartSleepTime);
- }
- }
- mForcedRoute = AudioSystem::ROUTE_SPEAKER;
- mRouteRestoreTime = 0;
- }
- LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount);
- }
- break;
- case ACTIVE_TRACK_REMOVED:
- {
- AutoMutex lock(mHardwareLock);
- if (mForcedSpeakerCount > 0){
- if (--mForcedSpeakerCount == 0) {
- mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000);
- }
- LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount);
- } else {
- LOGE("mForcedSpeakerCount is already zero");
- }
- }
- break;
- case CHECK_ROUTE_RESTORE_TIME:
- case FORCE_ROUTE_RESTORE:
- if (mRouteRestoreTime) {
- AutoMutex lock(mHardwareLock);
- if (mRouteRestoreTime &&
- (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) {
- mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved);
- mForcedRoute = 0;
- if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) {
- mHardwareStatus = AUDIO_HW_SET_ROUTING;
- mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute);
- mHardwareStatus = AUDIO_HW_IDLE;
- LOGV("Route forced to Speaker OFF %08x", mSavedRoute);
- }
- mRouteRestoreTime = 0;
- }
- }
- break;
- }
+ mParamCond.broadcast();
+ mNewParameters.clear();
}
-#ifdef WITH_A2DP
-// handleRouteDisablesA2dp_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::handleRouteDisablesA2dp_l(int routes)
-{
- if (routes & AudioSystem::ROUTE_BLUETOOTH_SCO) {
- if (mA2dpDisableCount++ == 0) {
- if (mA2dpEnabled) {
- setA2dpEnabled_l(false);
- mA2dpSuppressed = true;
- }
- }
- LOGV("mA2dpDisableCount incremented to %d", mA2dpDisableCount);
- } else {
- if (mA2dpDisableCount > 0) {
- if (--mA2dpDisableCount == 0) {
- if (mA2dpSuppressed) {
- setA2dpEnabled_l(true);
- mA2dpSuppressed = false;
- }
- }
- LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount);
- } else {
- LOGV("mA2dpDisableCount is already zero");
- }
+void AudioFlinger::ThreadBase::exit()
+{
+ // keep a strong ref on ourself so that we want get
+ // destroyed in the middle of requestExitAndWait()
+ sp <ThreadBase> strongMe = this;
+
+ LOGV("ThreadBase::exit");
+ {
+ AutoMutex lock(&mLock);
+ requestExit();
+ mWaitWorkCV.signal();
}
+ requestExitAndWait();
}
-#endif
-// ----------------------------------------------------------------------------
+uint32_t AudioFlinger::ThreadBase::sampleRate() const
+{
+ return mSampleRate;
+}
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType)
- : Thread(false),
- mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType),
- mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0),
- mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false),
- mInWrite(false)
+int AudioFlinger::ThreadBase::channelCount() const
{
- mSampleRate = output->sampleRate();
- mChannelCount = output->channelCount();
+ return mChannelCount;
+}
- // FIXME - Current mixer implementation only supports stereo output
- if (mChannelCount == 1) {
- LOGE("Invalid audio hardware channel count");
+int AudioFlinger::ThreadBase::format() const
+{
+ return mFormat;
+}
+
+size_t AudioFlinger::ThreadBase::frameCount() const
+{
+ return mFrameCount;
+}
+
+status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
+{
+ status_t status;
+
+ LOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
+ Mutex::Autolock _l(mLock);
+
+ mNewParameters.add(keyValuePairs);
+ mWaitWorkCV.signal();
+ mParamCond.wait(mLock);
+ status = mParamStatus;
+ mWaitWorkCV.signal();
+ return status;
+}
+
+void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param)
+{
+ Mutex::Autolock _l(mLock);
+ sendConfigEvent_l(event, param);
+}
+
+// sendConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param)
+{
+ ConfigEvent *configEvent = new ConfigEvent();
+ configEvent->mEvent = event;
+ configEvent->mParam = param;
+ mConfigEvents.add(configEvent);
+ LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
+ mWaitWorkCV.signal();
+}
+
+void AudioFlinger::ThreadBase::processConfigEvents()
+{
+ mLock.lock();
+ while(!mConfigEvents.isEmpty()) {
+ LOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
+ ConfigEvent *configEvent = mConfigEvents[0];
+ mConfigEvents.removeAt(0);
+ // release mLock because audioConfigChanged() will call
+ // Audioflinger::audioConfigChanged() which locks AudioFlinger mLock thus creating
+ // potential cross deadlock between AudioFlinger::mLock and mLock
+ mLock.unlock();
+ audioConfigChanged(configEvent->mEvent, configEvent->mParam);
+ delete configEvent;
+ mLock.lock();
}
+ mLock.unlock();
+}
- mFormat = output->format();
- mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t);
- mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate());
- // FIXME - Current mixer implementation only supports stereo output: Always
- // Allocate a stereo buffer even if HW output is mono.
- mMixBuffer = new int16_t[mFrameCount * 2];
- memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
+ : ThreadBase(audioFlinger),
+ mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
+ mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
+{
+ readOutputParameters();
+
+ mMasterVolume = mAudioFlinger->masterVolume();
+ mMasterMute = mAudioFlinger->masterMute();
+
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
+ mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
+ }
+ // notify client processes that a new input has been opened
+ sendConfigEvent(AudioSystem::OUTPUT_OPENED);
}
-AudioFlinger::MixerThread::~MixerThread()
+AudioFlinger::PlaybackThread::~PlaybackThread()
{
delete [] mMixBuffer;
- delete mAudioMixer;
}
-status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args)
+status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
{
dumpInternals(fd, args);
dumpTracks(fd, args);
return NO_ERROR;
}
-status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args)
+status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType);
+ snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
result.append(buffer);
result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
for (size_t i = 0; i < mTracks.size(); ++i) {
@@ -959,7 +864,7 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a
}
}
- snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType);
+ snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
result.append(buffer);
result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n");
for (size_t i = 0; i < mActiveTracks.size(); ++i) {
@@ -976,15 +881,13 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a
return NO_ERROR;
}
-status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType);
- result.append(buffer);
- snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames());
+ snprintf(buffer, SIZE, "Output thread %p internals\n", this);
result.append(buffer);
snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
result.append(buffer);
@@ -1001,275 +904,28 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>
}
// Thread virtuals
-bool AudioFlinger::MixerThread::threadLoop()
-{
- unsigned long sleepTime = kBufferRecoveryInUsecs;
- int16_t* curBuf = mMixBuffer;
- Vector< sp<Track> > tracksToRemove;
- size_t enabledTracks = 0;
- nsecs_t standbyTime = systemTime();
- size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t);
- nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2;
-
-#ifdef WITH_A2DP
- bool outputTrackActive = false;
-#endif
-
- do {
- enabledTracks = 0;
- { // scope for the AudioFlinger::mLock
-
- Mutex::Autolock _l(mAudioFlinger->mLock);
-
-#ifdef WITH_A2DP
- if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) {
- if (outputTrackActive) {
- mAudioFlinger->mLock.unlock();
- mOutputTrack->stop();
- mAudioFlinger->mLock.lock();
- outputTrackActive = false;
- }
- }
- mAudioFlinger->checkA2dpEnabledChange_l();
-#endif
-
- const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
-
- // put audio hardware into standby after short delay
- if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) {
- // wait until we have something to do...
- LOGV("Audio hardware entering standby, output %d\n", mOutputType);
- if (!mStandby) {
- mOutput->standby();
- mStandby = true;
- }
-
-#ifdef WITH_A2DP
- if (outputTrackActive) {
- mAudioFlinger->mLock.unlock();
- mOutputTrack->stop();
- mAudioFlinger->mLock.lock();
- outputTrackActive = false;
- }
-#endif
- if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
- mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE);
- }
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- mAudioFlinger->mWaitWorkCV.wait(mAudioFlinger->mLock);
- LOGV("Audio hardware exiting standby, output %d\n", mOutputType);
-
- if (mMasterMute == false) {
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.audio.silent", value, "0");
- if (atoi(value)) {
- LOGD("Silence is golden");
- setMasterMute(true);
- }
- }
-
- standbyTime = systemTime() + kStandbyTimeInNsecs;
- continue;
- }
-
- // Forced route to speaker is handled by hardware mixer thread
- if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
- mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME);
- }
-
- // find out which tracks need to be processed
- size_t count = activeTracks.size();
- for (size_t i=0 ; i<count ; i++) {
- sp<Track> t = activeTracks[i].promote();
- if (t == 0) continue;
-
- Track* const track = t.get();
- audio_track_cblk_t* cblk = track->cblk();
-
- // The first time a track is added we wait
- // for all its buffers to be filled before processing it
- mAudioMixer->setActiveTrack(track->name());
- if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
- !track->isPaused())
- {
- //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
-
- // compute volume for this track
- int16_t left, right;
- if (track->isMuted() || mMasterMute || track->isPausing()) {
- left = right = 0;
- if (track->isPausing()) {
- LOGV("paused(%d)", track->name());
- track->setPaused();
- }
- } else {
- float typeVolume = mStreamTypes[track->type()].volume;
- float v = mMasterVolume * typeVolume;
- float v_clamped = v * cblk->volume[0];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- left = int16_t(v_clamped);
- v_clamped = v * cblk->volume[1];
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- right = int16_t(v_clamped);
- }
-
- // XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(track);
- mAudioMixer->enable(AudioMixer::MIXING);
-
- int param;
- if ( track->mFillingUpStatus == Track::FS_FILLED) {
- // no ramp for the first volume setting
- track->mFillingUpStatus = Track::FS_ACTIVE;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
- param = AudioMixer::RAMP_VOLUME;
- } else {
- param = AudioMixer::VOLUME;
- }
- } else {
- param = AudioMixer::RAMP_VOLUME;
- }
- mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
- mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
- mAudioMixer->setParameter(
- AudioMixer::TRACK,
- AudioMixer::FORMAT, track->format());
- mAudioMixer->setParameter(
- AudioMixer::TRACK,
- AudioMixer::CHANNEL_COUNT, track->channelCount());
- mAudioMixer->setParameter(
- AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE,
- int(cblk->sampleRate));
-
- // reset retry count
- track->mRetryCount = kMaxTrackRetries;
- enabledTracks++;
- } else {
- //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
- if (track->isStopped()) {
- track->reset();
- }
- if (track->isTerminated() || track->isStopped() || track->isPaused()) {
- // We have consumed all the buffers of this track.
- // Remove it from the list of active tracks.
- LOGV("remove(%d) from active list", track->name());
- tracksToRemove.add(track);
- } else {
- // No buffers for this track. Give it a few chances to
- // fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
- tracksToRemove.add(track);
- }
- }
- // LOGV("disable(%d)", track->name());
- mAudioMixer->disable(AudioMixer::MIXING);
- }
- }
-
- // remove all the tracks that need to be...
- count = tracksToRemove.size();
- if (UNLIKELY(count)) {
- for (size_t i=0 ; i<count ; i++) {
- const sp<Track>& track = tracksToRemove[i];
- removeActiveTrack_l(track);
- if (track->isTerminated()) {
- mTracks.remove(track);
- deleteTrackName_l(track->mName);
- }
- }
- }
- }
-
- if (LIKELY(enabledTracks)) {
- // mix buffers...
- mAudioMixer->process(curBuf);
-
-#ifdef WITH_A2DP
- if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) {
- if (!outputTrackActive) {
- LOGV("starting output track in mixer for output %d", mOutputType);
- mOutputTrack->start();
- outputTrackActive = true;
- }
- mOutputTrack->write(curBuf, mFrameCount);
- }
-#endif
-
- // output audio to hardware
- mLastWriteTime = systemTime();
- mInWrite = true;
- mOutput->write(curBuf, mixBufferSize);
- mNumWrites++;
- mInWrite = false;
- mStandby = false;
- nsecs_t temp = systemTime();
- standbyTime = temp + kStandbyTimeInNsecs;
- nsecs_t delta = temp - mLastWriteTime;
- if (delta > maxPeriod) {
- LOGW("write blocked for %llu msecs", ns2ms(delta));
- mNumDelayedWrites++;
- }
- sleepTime = kBufferRecoveryInUsecs;
- } else {
-#ifdef WITH_A2DP
- if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) {
- if (outputTrackActive) {
- mOutputTrack->write(curBuf, 0);
- if (mOutputTrack->bufferQueueEmpty()) {
- mOutputTrack->stop();
- outputTrackActive = false;
- } else {
- standbyTime = systemTime() + kStandbyTimeInNsecs;
- }
- }
- }
-#endif
- // There was nothing to mix this round, which means all
- // active tracks were late. Sleep a little bit to give
- // them another chance. If we're too late, the audio
- // hardware will zero-fill for us.
- //LOGV("no buffers - usleep(%lu)", sleepTime);
- usleep(sleepTime);
- if (sleepTime < kMaxBufferRecoveryInUsecs) {
- sleepTime += kBufferRecoveryInUsecs;
- }
- }
-
- // finally let go of all our tracks, without the lock held
- // since we can't guarantee the destructors won't acquire that
- // same lock.
- tracksToRemove.clear();
- } while (true);
-
- return false;
-}
-
-status_t AudioFlinger::MixerThread::readyToRun()
+status_t AudioFlinger::PlaybackThread::readyToRun()
{
if (mSampleRate == 0) {
LOGE("No working audio driver found.");
return NO_INIT;
}
- LOGI("AudioFlinger's thread ready to run for output %d", mOutputType);
+ LOGI("AudioFlinger's thread %p ready to run", this);
return NO_ERROR;
}
-void AudioFlinger::MixerThread::onFirstRef()
+void AudioFlinger::PlaybackThread::onFirstRef()
{
const size_t SIZE = 256;
char buffer[SIZE];
- snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType);
+ snprintf(buffer, SIZE, "Playback Thread %p", this);
run(buffer, ANDROID_PRIORITY_URGENT_AUDIO);
}
-// MixerThread::createTrack_l() must be called with AudioFlinger::mLock held
-sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack_l(
+// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
int streamType,
uint32_t sampleRate,
@@ -1281,28 +937,39 @@ sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack_l(
{
sp<Track> track;
status_t lStatus;
-
- // Resampler implementation limits input sampling rate to 2 x output sampling rate.
- if (sampleRate > mSampleRate*2) {
- LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
- lStatus = BAD_VALUE;
- goto Exit;
- }
+ if (mType == DIRECT) {
+ if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) {
+ LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p",
+ sampleRate, format, channelCount, mOutput);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ } else {
+ // Resampler implementation limits input sampling rate to 2 x output sampling rate.
+ if (sampleRate > mSampleRate*2) {
+ LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
- if (mSampleRate == 0) {
+ if (mOutput == 0) {
LOGE("Audio driver not initialized.");
lStatus = NO_INIT;
goto Exit;
}
- track = new Track(this, client, streamType, sampleRate, format,
- channelCount, frameCount, sharedBuffer);
- if (track->getCblk() == NULL) {
- lStatus = NO_MEMORY;
- goto Exit;
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ track = new Track(this, client, streamType, sampleRate, format,
+ channelCount, frameCount, sharedBuffer);
+ if (track->getCblk() == NULL) {
+ lStatus = NO_MEMORY;
+ goto Exit;
+ }
+ mTracks.add(track);
}
- mTracks.add(track);
lStatus = NO_ERROR;
Exit:
@@ -1312,87 +979,7 @@ Exit:
return track;
}
-// getTracks_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::MixerThread::getTracks_l(
- SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks)
-{
- size_t size = mTracks.size();
- LOGV ("MixerThread::getTracks_l() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size());
- for (size_t i = 0; i < size; i++) {
- sp<Track> t = mTracks[i];
- if (AudioSystem::routedToA2dpOutput(t->mStreamType)) {
- tracks.add(t);
- int j = mActiveTracks.indexOf(t);
- if (j >= 0) {
- t = mActiveTracks[j].promote();
- if (t != NULL) {
- activeTracks.add(t);
- }
- }
- }
- }
-
- size = activeTracks.size();
- for (size_t i = 0; i < size; i++) {
- removeActiveTrack_l(activeTracks[i]);
- }
-
- size = tracks.size();
- for (size_t i = 0; i < size; i++) {
- sp<Track> t = tracks[i];
- mTracks.remove(t);
- deleteTrackName_l(t->name());
- }
-}
-
-// putTracks_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::MixerThread::putTracks_l(
- SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks)
-{
-
- LOGV ("MixerThread::putTracks_l() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size());
-
- size_t size = tracks.size();
- for (size_t i = 0; i < size ; i++) {
- sp<Track> t = tracks[i];
- int name = getTrackName_l();
-
- if (name < 0) return;
-
- t->mName = name;
- t->mMixerThread = this;
- mTracks.add(t);
-
- int j = activeTracks.indexOf(t);
- if (j >= 0) {
- addActiveTrack_l(t);
- }
- }
-}
-
-uint32_t AudioFlinger::MixerThread::sampleRate() const
-{
- return mSampleRate;
-}
-
-int AudioFlinger::MixerThread::channelCount() const
-{
- return mChannelCount;
-}
-
-int AudioFlinger::MixerThread::format() const
-{
- return mFormat;
-}
-
-size_t AudioFlinger::MixerThread::frameCount() const
-{
- return mFrameCount;
-}
-
-uint32_t AudioFlinger::MixerThread::latency() const
+uint32_t AudioFlinger::PlaybackThread::latency() const
{
if (mOutput) {
return mOutput->latency();
@@ -1402,66 +989,66 @@ uint32_t AudioFlinger::MixerThread::latency() const
}
}
-status_t AudioFlinger::MixerThread::setMasterVolume(float value)
+status_t AudioFlinger::PlaybackThread::setMasterVolume(float value)
{
mMasterVolume = value;
return NO_ERROR;
}
-status_t AudioFlinger::MixerThread::setMasterMute(bool muted)
+status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted)
{
mMasterMute = muted;
return NO_ERROR;
}
-float AudioFlinger::MixerThread::masterVolume() const
+float AudioFlinger::PlaybackThread::masterVolume() const
{
return mMasterVolume;
}
-bool AudioFlinger::MixerThread::masterMute() const
+bool AudioFlinger::PlaybackThread::masterMute() const
{
return mMasterMute;
}
-status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value)
+status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value)
{
mStreamTypes[stream].volume = value;
return NO_ERROR;
}
-status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted)
+status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted)
{
mStreamTypes[stream].mute = muted;
return NO_ERROR;
}
-float AudioFlinger::MixerThread::streamVolume(int stream) const
+float AudioFlinger::PlaybackThread::streamVolume(int stream) const
{
return mStreamTypes[stream].volume;
}
-bool AudioFlinger::MixerThread::streamMute(int stream) const
+bool AudioFlinger::PlaybackThread::streamMute(int stream) const
{
return mStreamTypes[stream].mute;
}
-// isMusicActive_l() must be called with AudioFlinger::mLock held
-bool AudioFlinger::MixerThread::isMusicActive_l() const
+bool AudioFlinger::PlaybackThread::isMusicActive() const
{
+ Mutex::Autolock _l(mLock);
size_t count = mActiveTracks.size();
for (size_t i = 0 ; i < count ; ++i) {
sp<Track> t = mActiveTracks[i].promote();
if (t == 0) continue;
Track* const track = t.get();
- if (t->mStreamType == AudioSystem::MUSIC)
+ if (t->type() == AudioSystem::MUSIC)
return true;
}
return false;
}
-// addTrack_l() must be called with AudioFlinger::mLock held
-status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track)
+// addTrack_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
status_t status = ALREADY_EXISTS;
@@ -1482,18 +1069,18 @@ status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track)
// effectively get the latency it requested.
track->mFillingUpStatus = Track::FS_FILLING;
track->mResetDone = false;
- addActiveTrack_l(track);
+ mActiveTracks.add(track);
status = NO_ERROR;
}
-
+
LOGV("mWaitWorkCV.broadcast");
- mAudioFlinger->mWaitWorkCV.broadcast();
+ mWaitWorkCV.broadcast();
return status;
}
-// destroyTrack_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track)
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
{
track->mState = TrackBase::TERMINATED;
if (mActiveTracks.indexOf(track) < 0) {
@@ -1503,62 +1090,920 @@ void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track)
}
}
-// addActiveTrack_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::MixerThread::addActiveTrack_l(const wp<Track>& t)
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
+ return mOutput->getParameters(keys);
+}
+
+void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = 0;
+
+ LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param);
+
+ switch (event) {
+ case AudioSystem::OUTPUT_OPENED:
+ case AudioSystem::OUTPUT_CONFIG_CHANGED:
+ desc.channels = mChannelCount;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mFrameCount;
+ desc.latency = latency();
+ param2 = &desc;
+ break;
+
+ case AudioSystem::STREAM_CONFIG_CHANGED:
+ param2 = &param;
+ case AudioSystem::OUTPUT_CLOSED:
+ default:
+ break;
+ }
+ mAudioFlinger->audioConfigChanged(event, this, param2);
+}
+
+void AudioFlinger::PlaybackThread::readOutputParameters()
+{
+ mSampleRate = mOutput->sampleRate();
+ mChannelCount = AudioSystem::popCount(mOutput->channels());
+
+ mFormat = mOutput->format();
+ mFrameSize = mOutput->frameSize();
+ mFrameCount = mOutput->bufferSize() / mFrameSize;
+
+ mMinBytesToWrite = (mOutput->latency() * mSampleRate * mFrameSize) / 1000;
+ // FIXME - Current mixer implementation only supports stereo output: Always
+ // Allocate a stereo buffer even if HW output is mono.
+ if (mMixBuffer != NULL) delete mMixBuffer;
+ mMixBuffer = new int16_t[mFrameCount * 2];
+ memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
+ : PlaybackThread(audioFlinger, output),
+ mAudioMixer(0)
+{
+ mType = PlaybackThread::MIXER;
+ mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+
+ // FIXME - Current mixer implementation only supports stereo output
+ if (mChannelCount == 1) {
+ LOGE("Invalid audio hardware channel count");
+ }
+}
+
+AudioFlinger::MixerThread::~MixerThread()
+{
+ delete mAudioMixer;
+}
+
+bool AudioFlinger::MixerThread::threadLoop()
+{
+ unsigned long sleepTime = 0;
+ int16_t* curBuf = mMixBuffer;
+ Vector< sp<Track> > tracksToRemove;
+ size_t enabledTracks = 0;
+ nsecs_t standbyTime = systemTime();
+ size_t mixBufferSize = mFrameCount * mFrameSize;
+ nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2;
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ enabledTracks = 0;
+ { // scope for mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount * mFrameSize;
+ maxPeriod = seconds(mFrameCount) / mSampleRate * 2;
+ }
+
+ const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ if (!mStandby) {
+ LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended);
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+
+ if (exitPending()) break;
+
+ // wait until we have something to do...
+ LOGV("MixerThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("MixerThread %p TID %d waking up\n", this, gettid());
+
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ sleepTime = 0;
+ continue;
+ }
+ }
+
+ enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove);
+ }
+
+
+ // output audio to hardware
+ if (mSuspended) {
+ usleep(kMaxBufferRecoveryInUsecs);
+ } else {
+ if (LIKELY(enabledTracks)) {
+ // mix buffers...
+ mAudioMixer->process(curBuf);
+ sleepTime = 0;
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ } else {
+ sleepTime += kBufferRecoveryInUsecs;
+ // There was nothing to mix this round, which means all
+ // active tracks were late. Sleep a little bit to give
+ // them another chance. If we're too late, write 0s to audio
+ // hardware to avoid underrun.
+ if (sleepTime < kMaxBufferRecoveryInUsecs) {
+ usleep(kBufferRecoveryInUsecs);
+ } else {
+ memset (curBuf, 0, mixBufferSize);
+ sleepTime = 0;
+ }
+ }
+ // sleepTime == 0 means PCM data were written to mMixBuffer[]
+ if (sleepTime == 0) {
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize);
+ if (bytesWritten > 0) mBytesWritten += bytesWritten;
+ mNumWrites++;
+ mInWrite = false;
+ mStandby = false;
+ nsecs_t delta = systemTime() - mLastWriteTime;
+ if (delta > maxPeriod) {
+ LOGW("write blocked for %llu msecs", ns2ms(delta));
+ mNumDelayedWrites++;
+ }
+ }
+ }
+
+ // finally let go of all our tracks, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ tracksToRemove.clear();
+ }
+
+ if (!mStandby) {
+ mOutput->standby();
+ }
+ sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
+ processConfigEvents();
+
+ LOGV("MixerThread %p exiting", this);
+ return false;
+}
+
+// prepareTracks_l() must be called with ThreadBase::mLock held
+size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+{
+
+ size_t enabledTracks = 0;
+ // find out which tracks need to be processed
+ size_t count = activeTracks.size();
+ for (size_t i=0 ; i<count ; i++) {
+ sp<Track> t = activeTracks[i].promote();
+ if (t == 0) continue;
+
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ mAudioMixer->setActiveTrack(track->name());
+ if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
+ !track->isPaused())
+ {
+ //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+
+ // compute volume for this track
+ int16_t left, right;
+ if (track->isMuted() || mMasterMute || track->isPausing() ||
+ mStreamTypes[track->type()].mute) {
+ left = right = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+ float typeVolume = mStreamTypes[track->type()].volume;
+ float v = mMasterVolume * typeVolume;
+ float v_clamped = v * cblk->volume[0];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = int16_t(v_clamped);
+ v_clamped = v * cblk->volume[1];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = int16_t(v_clamped);
+ }
+
+ // XXX: these things DON'T need to be done each time
+ mAudioMixer->setBufferProvider(track);
+ mAudioMixer->enable(AudioMixer::MIXING);
+
+ int param = AudioMixer::VOLUME;
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ // no ramp for the first volume setting
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ param = AudioMixer::RAMP_VOLUME;
+ }
+ } else if (cblk->server != 0) {
+ // If the track is stopped before the first frame was mixed,
+ // do not apply ramp
+ param = AudioMixer::RAMP_VOLUME;
+ }
+
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left);
+ mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right);
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::FORMAT, track->format());
+ mAudioMixer->setParameter(
+ AudioMixer::TRACK,
+ AudioMixer::CHANNEL_COUNT, track->channelCount());
+ mAudioMixer->setParameter(
+ AudioMixer::RESAMPLE,
+ AudioMixer::SAMPLE_RATE,
+ int(cblk->sampleRate));
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetries;
+ enabledTracks++;
+ } else {
+ //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ if (track->isStopped()) {
+ track->reset();
+ }
+ if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ tracksToRemove->add(track);
+ mAudioMixer->disable(AudioMixer::MIXING);
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ tracksToRemove->add(track);
+ }
+ // For tracks using static shared memory buffer, make sure that we have
+ // written enough data to audio hardware before disabling the track
+ // NOTE: this condition with arrive before track->mRetryCount <= 0 so we
+ // don't care about code removing track from active list above.
+ if ((track->mSharedBuffer == 0) || (mBytesWritten >= mMinBytesToWrite)) {
+ mAudioMixer->disable(AudioMixer::MIXING);
+ } else {
+ enabledTracks++;
+ }
+ }
+ }
+ }
+
+ // remove all the tracks that need to be...
+ count = tracksToRemove->size();
+ if (UNLIKELY(count)) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track>& track = tracksToRemove->itemAt(i);
+ mActiveTracks.remove(track);
+ if (track->isTerminated()) {
+ mTracks.remove(track);
+ deleteTrackName_l(track->mName);
+ }
+ }
+ }
+
+ return enabledTracks;
+}
+
+void AudioFlinger::MixerThread::getTracks(
+ SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks,
+ int streamType)
{
- mActiveTracks.add(t);
+ LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size());
+ Mutex::Autolock _l(mLock);
+ size_t size = mTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = mTracks[i];
+ if (t->type() == streamType) {
+ tracks.add(t);
+ int j = mActiveTracks.indexOf(t);
+ if (j >= 0) {
+ t = mActiveTracks[j].promote();
+ if (t != NULL) {
+ activeTracks.add(t);
+ }
+ }
+ }
+ }
+
+ size = activeTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ mActiveTracks.remove(activeTracks[i]);
+ }
- // Force routing to speaker for certain stream types
- // The forced routing to speaker is managed by hardware mixer
- if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
- sp<Track> track = t.promote();
- if (track == NULL) return;
-
- if (streamForcedToSpeaker(track->type())) {
- mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED);
- }
+ size = tracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = tracks[i];
+ mTracks.remove(t);
+ deleteTrackName_l(t->name());
}
}
-// removeActiveTrack_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::MixerThread::removeActiveTrack_l(const wp<Track>& t)
+void AudioFlinger::MixerThread::putTracks(
+ SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks)
{
- mActiveTracks.remove(t);
+ LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size());
+ Mutex::Autolock _l(mLock);
+ size_t size = tracks.size();
+ for (size_t i = 0; i < size ; i++) {
+ sp<Track> t = tracks[i];
+ int name = getTrackName_l();
- // Force routing to speaker for certain stream types
- // The forced routing to speaker is managed by hardware mixer
- if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) {
- sp<Track> track = t.promote();
- if (track == NULL) return;
+ if (name < 0) return;
- if (streamForcedToSpeaker(track->type())) {
- mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED);
+ t->mName = name;
+ t->mThread = this;
+ mTracks.add(t);
+
+ int j = activeTracks.indexOf(t);
+ if (j >= 0) {
+ mActiveTracks.add(t);
+ // force buffer refilling and no ramp volume when the track is mixed for the first time
+ t->mFillingUpStatus = Track::FS_FILLING;
}
}
}
-// getTrackName_l() must be called with AudioFlinger::mLock held
+// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::MixerThread::getTrackName_l()
{
return mAudioMixer->getTrackName();
}
-// deleteTrackName_l() must be called with AudioFlinger::mLock held
+// deleteTrackName_l() must be called with ThreadBase::mLock held
void AudioFlinger::MixerThread::deleteTrackName_l(int name)
{
mAudioMixer->deleteTrackName(name);
}
-size_t AudioFlinger::MixerThread::getOutputFrameCount()
+// checkForNewParameters_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::MixerThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+
+ mNewParameters.removeAt(0);
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if (value != AudioSystem::PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ if (value != AudioSystem::CHANNEL_OUT_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->setParameters(keyValuePair);
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->setParameters(keyValuePair);
+ }
+ if (status == NO_ERROR && reconfig) {
+ delete mAudioMixer;
+ readOutputParameters();
+ mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ int name = getTrackName_l();
+ if (name < 0) break;
+ mTracks[i]->mName = name;
+ // limit track sample rate to 2 x new output sample rate
+ if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
+ mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
+ }
+ }
+ sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ PlaybackThread::dumpInternals(fd, args);
+
+ snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
+ : PlaybackThread(audioFlinger, output),
+ mLeftVolume (1.0), mRightVolume(1.0)
+{
+ mType = PlaybackThread::DIRECT;
+}
+
+AudioFlinger::DirectOutputThread::~DirectOutputThread()
+{
+}
+
+
+bool AudioFlinger::DirectOutputThread::threadLoop()
+{
+ unsigned long sleepTime = 0;
+ sp<Track> trackToRemove;
+ sp<Track> activeTrack;
+ nsecs_t standbyTime = systemTime();
+ int8_t *curBuf;
+ size_t mixBufferSize = mFrameCount*mFrameSize;
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ { // scope for the mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount*mFrameSize;
+ }
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ // wait until we have something to do...
+ if (!mStandby) {
+ LOGV("Audio hardware entering standby, mixer %p\n", this);
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+
+ if (exitPending()) break;
+
+ LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid());
+
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ sleepTime = 0;
+ continue;
+ }
+ }
+
+ // find out which tracks need to be processed
+ if (mActiveTracks.size() != 0) {
+ sp<Track> t = mActiveTracks[0].promote();
+ if (t == 0) continue;
+
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ if (cblk->framesReady() && (track->isReady() || track->isStopped()) &&
+ !track->isPaused())
+ {
+ //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server);
+
+ // compute volume for this track
+ float left, right;
+ if (track->isMuted() || mMasterMute || track->isPausing() ||
+ mStreamTypes[track->type()].mute) {
+ left = right = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+ float typeVolume = mStreamTypes[track->type()].volume;
+ float v = mMasterVolume * typeVolume;
+ float v_clamped = v * cblk->volume[0];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * cblk->volume[1];
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
+
+ if (left != mLeftVolume || right != mRightVolume) {
+ mOutput->setVolume(left, right);
+ left = mLeftVolume;
+ right = mRightVolume;
+ }
+
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ }
+ }
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetries;
+ activeTrack = t;
+ } else {
+ //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
+ if (track->isStopped()) {
+ track->reset();
+ }
+ if (track->isTerminated() || track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ trackToRemove = track;
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ trackToRemove = track;
+ }
+
+ // For tracks using static shared memry buffer, make sure that we have
+ // written enough data to audio hardware before disabling the track
+ // NOTE: this condition with arrive before track->mRetryCount <= 0 so we
+ // don't care about code removing track from active list above.
+ if ((track->mSharedBuffer != 0) && (mBytesWritten < mMinBytesToWrite)) {
+ activeTrack = t;
+ }
+ }
+ }
+ }
+
+ // remove all the tracks that need to be...
+ if (UNLIKELY(trackToRemove != 0)) {
+ mActiveTracks.remove(trackToRemove);
+ if (trackToRemove->isTerminated()) {
+ mTracks.remove(trackToRemove);
+ deleteTrackName_l(trackToRemove->mName);
+ }
+ }
+ }
+
+ // output audio to hardware
+ if (mSuspended) {
+ usleep(kMaxBufferRecoveryInUsecs);
+ } else {
+ if (activeTrack != 0) {
+ AudioBufferProvider::Buffer buffer;
+ size_t frameCount = mFrameCount;
+ curBuf = (int8_t *)mMixBuffer;
+ // output audio to hardware
+ while(frameCount) {
+ buffer.frameCount = frameCount;
+ activeTrack->getNextBuffer(&buffer);
+ if (UNLIKELY(buffer.raw == 0)) {
+ memset(curBuf, 0, frameCount * mFrameSize);
+ break;
+ }
+ memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
+ frameCount -= buffer.frameCount;
+ curBuf += buffer.frameCount * mFrameSize;
+ activeTrack->releaseBuffer(&buffer);
+ }
+ sleepTime = 0;
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ } else {
+ sleepTime += kBufferRecoveryInUsecs;
+ if (sleepTime < kMaxBufferRecoveryInUsecs) {
+ usleep(kBufferRecoveryInUsecs);
+ } else {
+ memset (mMixBuffer, 0, mFrameCount * mFrameSize);
+ sleepTime = 0;
+ }
+ }
+
+ // sleepTime == 0 means PCM data were written to mMixBuffer[]
+ if (sleepTime == 0) {
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);
+ if (bytesWritten) mBytesWritten += bytesWritten;
+ mNumWrites++;
+ mInWrite = false;
+ mStandby = false;
+ }
+ }
+
+ // finally let go of removed track, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ trackToRemove.clear();
+ activeTrack.clear();
+ }
+
+ if (!mStandby) {
+ mOutput->standby();
+ }
+ sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
+ processConfigEvents();
+
+ LOGV("DirectOutputThread %p exiting", this);
+ return false;
+}
+
+// getTrackName_l() must be called with ThreadBase::mLock held
+int AudioFlinger::DirectOutputThread::getTrackName_l()
+{
+ return 0;
+}
+
+// deleteTrackName_l() must be called with ThreadBase::mLock held
+void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name)
+{
+}
+
+// checkForNewParameters_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+
+ mNewParameters.removeAt(0);
+
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->setParameters(keyValuePair);
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->standby();
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->setParameters(keyValuePair);
+ }
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters();
+ sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread)
+ : MixerThread(audioFlinger, mainThread->getOutput())
+{
+ mType = PlaybackThread::DUPLICATING;
+ addOutputTrack(mainThread);
+}
+
+AudioFlinger::DuplicatingThread::~DuplicatingThread()
+{
+ mOutputTracks.clear();
+}
+
+bool AudioFlinger::DuplicatingThread::threadLoop()
+{
+ unsigned long sleepTime = kBufferRecoveryInUsecs;
+ int16_t* curBuf = mMixBuffer;
+ Vector< sp<Track> > tracksToRemove;
+ size_t enabledTracks = 0;
+ nsecs_t standbyTime = systemTime();
+ size_t mixBufferSize = mFrameCount*mFrameSize;
+ SortedVector< sp<OutputTrack> > outputTracks;
+
+ while (!exitPending())
+ {
+ processConfigEvents();
+
+ enabledTracks = 0;
+ { // scope for the mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (checkForNewParameters_l()) {
+ mixBufferSize = mFrameCount*mFrameSize;
+ }
+
+ const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
+
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ outputTracks.add(mOutputTracks[i]);
+ }
+
+ // put audio hardware into standby after short delay
+ if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) ||
+ mSuspended) {
+ if (!mStandby) {
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->stop();
+ }
+ mStandby = true;
+ mBytesWritten = 0;
+ }
+
+ if (!activeTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ outputTracks.clear();
+
+ if (exitPending()) break;
+
+ LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid());
+ mWaitWorkCV.wait(mLock);
+ LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid());
+ if (mMasterMute == false) {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.audio.silent", value, "0");
+ if (atoi(value)) {
+ LOGD("Silence is golden");
+ setMasterMute(true);
+ }
+ }
+
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ sleepTime = kBufferRecoveryInUsecs;
+ continue;
+ }
+ }
+
+ enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove);
+ }
+
+ bool mustSleep = true;
+ if (LIKELY(enabledTracks)) {
+ // mix buffers...
+ mAudioMixer->process(curBuf);
+ if (!mSuspended) {
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->write(curBuf, mFrameCount);
+ mustSleep = false;
+ }
+ mStandby = false;
+ mBytesWritten += mixBufferSize;
+ }
+ } else {
+ // flush remaining overflow buffers in output tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ if (outputTracks[i]->isActive()) {
+ outputTracks[i]->write(curBuf, 0);
+ standbyTime = systemTime() + kStandbyTimeInNsecs;
+ mustSleep = false;
+ }
+ }
+ }
+ if (mustSleep) {
+// LOGV("threadLoop() sleeping %d", sleepTime);
+ usleep(sleepTime);
+ if (sleepTime < kMaxBufferRecoveryInUsecs) {
+ sleepTime += kBufferRecoveryInUsecs;
+ }
+ } else {
+ sleepTime = kBufferRecoveryInUsecs;
+ }
+
+ // finally let go of all our tracks, without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // same lock.
+ tracksToRemove.clear();
+ outputTracks.clear();
+ }
+
+ { // scope for the mLock
+
+ Mutex::Autolock _l(mLock);
+ if (!mStandby) {
+ LOGV("DuplicatingThread() exiting out of standby");
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ mOutputTracks[i]->destroy();
+ }
+ }
+ }
+
+ sendConfigEvent(AudioSystem::OUTPUT_CLOSED);
+ processConfigEvents();
+
+ return false;
+}
+
+void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
+{
+ int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
+ OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread,
+ mSampleRate,
+ mFormat,
+ mChannelCount,
+ frameCount);
+ if (outputTrack->cblk() != NULL) {
+ thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f);
+ mOutputTracks.add(outputTrack);
+ LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
+ }
+}
+
+void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
{
- return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t);
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ if (mOutputTracks[i]->thread() == (ThreadBase *)thread) {
+ mOutputTracks[i]->destroy();
+ mOutputTracks.removeAt(i);
+ return;
+ }
+ }
+ LOGV("removeOutputTrack(): unkonwn thread: %p", thread);
}
+
// ----------------------------------------------------------------------------
// TrackBase constructor must be called with AudioFlinger::mLock held
-AudioFlinger::MixerThread::TrackBase::TrackBase(
- const sp<MixerThread>& mixerThread,
+AudioFlinger::ThreadBase::TrackBase::TrackBase(
+ const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
int format,
@@ -1567,7 +2012,7 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
uint32_t flags,
const sp<IMemory>& sharedBuffer)
: RefBase(),
- mMixerThread(mixerThread),
+ mThread(thread),
mClient(client),
mFrameCount(0),
mState(IDLE),
@@ -1575,13 +2020,6 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
mFormat(format),
mFlags(flags & ~SYSTEM_FLAGS_MASK)
{
- mName = mixerThread->getTrackName_l();
- LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
- if (mName < 0) {
- LOGE("no more track names availlable");
- return;
- }
-
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
// LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
@@ -1635,16 +2073,19 @@ AudioFlinger::MixerThread::TrackBase::TrackBase(
}
}
-AudioFlinger::MixerThread::TrackBase::~TrackBase()
+AudioFlinger::PlaybackThread::TrackBase::~TrackBase()
{
if (mCblk) {
- mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
+ mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
+ if (mClient == NULL) {
+ delete mCblk;
+ }
}
mCblkMemory.clear(); // and free the shared memory
mClient.clear();
}
-void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+void AudioFlinger::PlaybackThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
{
buffer->raw = 0;
mFrameCount = buffer->frameCount;
@@ -1652,7 +2093,7 @@ void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Bu
buffer->frameCount = 0;
}
-bool AudioFlinger::MixerThread::TrackBase::step() {
+bool AudioFlinger::PlaybackThread::TrackBase::step() {
bool result;
audio_track_cblk_t* cblk = this->cblk();
@@ -1664,7 +2105,7 @@ bool AudioFlinger::MixerThread::TrackBase::step() {
return result;
}
-void AudioFlinger::MixerThread::TrackBase::reset() {
+void AudioFlinger::PlaybackThread::TrackBase::reset() {
audio_track_cblk_t* cblk = this->cblk();
cblk->user = 0;
@@ -1675,27 +2116,27 @@ void AudioFlinger::MixerThread::TrackBase::reset() {
LOGV("TrackBase::reset");
}
-sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const
+sp<IMemory> AudioFlinger::PlaybackThread::TrackBase::getCblk() const
{
return mCblkMemory;
}
-int AudioFlinger::MixerThread::TrackBase::sampleRate() const {
+int AudioFlinger::PlaybackThread::TrackBase::sampleRate() const {
return (int)mCblk->sampleRate;
}
-int AudioFlinger::MixerThread::TrackBase::channelCount() const {
+int AudioFlinger::PlaybackThread::TrackBase::channelCount() const {
return (int)mCblk->channels;
}
-void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
+void* AudioFlinger::PlaybackThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
audio_track_cblk_t* cblk = this->cblk();
- int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels;
- int16_t *bufferEnd = bufferStart + frames * cblk->channels;
+ int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize;
+ int8_t *bufferEnd = bufferStart + frames * cblk->frameSize;
// Check validity of returned pointer in case the track control block would have been corrupted.
- if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
- (cblk->channels == 2 && ((unsigned long)bufferStart & 3))) {
+ if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd ||
+ ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) {
LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \
server %d, serverBase %d, user %d, userBase %d, channels %d",
bufferStart, bufferEnd, mBuffer, mBufferEnd,
@@ -1708,9 +2149,9 @@ void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t
// ----------------------------------------------------------------------------
-// Track constructor must be called with AudioFlinger::mLock held
-AudioFlinger::MixerThread::Track::Track(
- const sp<MixerThread>& mixerThread,
+// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
+AudioFlinger::PlaybackThread::Track::Track(
+ const wp<ThreadBase>& thread,
const sp<Client>& client,
int streamType,
uint32_t sampleRate,
@@ -1718,40 +2159,58 @@ AudioFlinger::MixerThread::Track::Track(
int channelCount,
int frameCount,
const sp<IMemory>& sharedBuffer)
- : TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer)
+ : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer),
+ mMute(false), mSharedBuffer(sharedBuffer), mName(-1)
{
+ sp<ThreadBase> baseThread = thread.promote();
+ if (baseThread != 0) {
+ PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get();
+ mName = playbackThread->getTrackName_l();
+ }
+ LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ if (mName < 0) {
+ LOGE("no more track names available");
+ }
mVolume[0] = 1.0f;
mVolume[1] = 1.0f;
- mMute = false;
- mSharedBuffer = sharedBuffer;
mStreamType = streamType;
+ // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
+ // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
+ mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t);
}
-AudioFlinger::MixerThread::Track::~Track()
+AudioFlinger::PlaybackThread::Track::~Track()
{
- wp<Track> weak(this); // never create a strong ref from the dtor
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- mState = TERMINATED;
+ LOGV("PlaybackThread::Track destructor");
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ mState = TERMINATED;
+ }
}
-void AudioFlinger::MixerThread::Track::destroy()
+void AudioFlinger::PlaybackThread::Track::destroy()
{
- // NOTE: destroyTrack_l() can remove a strong reference to this Track
+ // NOTE: destroyTrack_l() can remove a strong reference to this Track
// by removing it from mTracks vector, so there is a risk that this Tracks's
- // desctructor is called. As the destructor needs to lock AudioFlinger::mLock,
- // we must acquire a strong reference on this Track before locking AudioFlinger::mLock
+ // desctructor is called. As the destructor needs to lock mLock,
+ // we must acquire a strong reference on this Track before locking mLock
// here so that the destructor is called only when exiting this function.
- // On the other hand, as long as Track::destroy() is only called by
- // TrackHandle destructor, the TrackHandle still holds a strong ref on
+ // On the other hand, as long as Track::destroy() is only called by
+ // TrackHandle destructor, the TrackHandle still holds a strong ref on
// this Track with its member mTrack.
sp<Track> keep(this);
- { // scope for AudioFlinger::mLock
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- mMixerThread->destroyTrack_l(this);
+ { // scope for mLock
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->destroyTrack_l(this);
+ }
}
}
-void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size)
+void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n",
mName - AudioMixer::TRACK0,
@@ -1770,7 +2229,7 @@ void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size)
mCblk->user);
}
-status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
audio_track_cblk_t* cblk = this->cblk();
uint32_t framesReady;
@@ -1807,76 +2266,90 @@ status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Bu
getNextBuffer_exit:
buffer->raw = 0;
buffer->frameCount = 0;
+ LOGV("getNextBuffer() no more data");
return NOT_ENOUGH_DATA;
}
-bool AudioFlinger::MixerThread::Track::isReady() const {
+bool AudioFlinger::PlaybackThread::Track::isReady() const {
if (mFillingUpStatus != FS_FILLING) return true;
if (mCblk->framesReady() >= mCblk->frameCount ||
mCblk->forceReady) {
mFillingUpStatus = FS_FILLED;
mCblk->forceReady = 0;
- LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType);
return true;
}
return false;
}
-status_t AudioFlinger::MixerThread::Track::start()
+status_t AudioFlinger::PlaybackThread::Track::start()
{
- LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- mMixerThread->addTrack_l(this);
+ LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->addTrack_l(this);
+ }
return NO_ERROR;
}
-void AudioFlinger::MixerThread::Track::stop()
+void AudioFlinger::PlaybackThread::Track::stop()
{
- LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType);
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- if (mState > STOPPED) {
- mState = STOPPED;
- // If the track is not active (PAUSED and buffers full), flush buffers
- if (mMixerThread->mActiveTracks.indexOf(this) < 0) {
- reset();
+ LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ if (mState > STOPPED) {
+ mState = STOPPED;
+ // If the track is not active (PAUSED and buffers full), flush buffers
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
+ LOGV("(> STOPPED) => STOPPED (%d)", mName);
}
- LOGV("(> STOPPED) => STOPPED (%d)", mName);
}
}
-void AudioFlinger::MixerThread::Track::pause()
+void AudioFlinger::PlaybackThread::Track::pause()
{
LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- if (mState == ACTIVE || mState == RESUMING) {
- mState = PAUSING;
- LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ if (mState == ACTIVE || mState == RESUMING) {
+ mState = PAUSING;
+ LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName);
+ }
}
}
-void AudioFlinger::MixerThread::Track::flush()
+void AudioFlinger::PlaybackThread::Track::flush()
{
LOGV("flush(%d)", mName);
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
- return;
- }
- // No point remaining in PAUSED state after a flush => go to
- // STOPPED state
- mState = STOPPED;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
+ return;
+ }
+ // No point remaining in PAUSED state after a flush => go to
+ // STOPPED state
+ mState = STOPPED;
- mCblk->lock.lock();
- // NOTE: reset() will reset cblk->user and cblk->server with
- // the risk that at the same time, the AudioMixer is trying to read
- // data. In this case, getNextBuffer() would return a NULL pointer
- // as audio buffer => the AudioMixer code MUST always test that pointer
- // returned by getNextBuffer() is not NULL!
- reset();
- mCblk->lock.unlock();
+ mCblk->lock.lock();
+ // NOTE: reset() will reset cblk->user and cblk->server with
+ // the risk that at the same time, the AudioMixer is trying to read
+ // data. In this case, getNextBuffer() would return a NULL pointer
+ // as audio buffer => the AudioMixer code MUST always test that pointer
+ // returned by getNextBuffer() is not NULL!
+ reset();
+ mCblk->lock.unlock();
+ }
}
-void AudioFlinger::MixerThread::Track::reset()
+void AudioFlinger::PlaybackThread::Track::reset()
{
// Do not reset twice to avoid discarding data written just after a flush and before
// the audioflinger thread detects the track is stopped.
@@ -1886,17 +2359,17 @@ void AudioFlinger::MixerThread::Track::reset()
// written to buffer
mCblk->flowControlFlag = 1;
mCblk->forceReady = 0;
- mFillingUpStatus = FS_FILLING;
+ mFillingUpStatus = FS_FILLING;
mResetDone = true;
}
}
-void AudioFlinger::MixerThread::Track::mute(bool muted)
+void AudioFlinger::PlaybackThread::Track::mute(bool muted)
{
mMute = muted;
}
-void AudioFlinger::MixerThread::Track::setVolume(float left, float right)
+void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right)
{
mVolume[0] = left;
mVolume[1] = right;
@@ -1905,28 +2378,33 @@ void AudioFlinger::MixerThread::Track::setVolume(float left, float right)
// ----------------------------------------------------------------------------
// RecordTrack constructor must be called with AudioFlinger::mLock held
-AudioFlinger::MixerThread::RecordTrack::RecordTrack(
- const sp<MixerThread>& mixerThread,
+AudioFlinger::RecordThread::RecordTrack::RecordTrack(
+ const wp<ThreadBase>& thread,
const sp<Client>& client,
- int inputSource,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags)
- : TrackBase(mixerThread, client, sampleRate, format,
+ : TrackBase(thread, client, sampleRate, format,
channelCount, frameCount, flags, 0),
- mOverflow(false), mInputSource(inputSource)
+ mOverflow(false)
{
+ LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
+ if (format == AudioSystem::PCM_16_BIT) {
+ mCblk->frameSize = channelCount * sizeof(int16_t);
+ } else if (format == AudioSystem::PCM_8_BIT) {
+ mCblk->frameSize = channelCount * sizeof(int8_t);
+ } else {
+ mCblk->frameSize = sizeof(int8_t);
+ }
}
-AudioFlinger::MixerThread::RecordTrack::~RecordTrack()
+AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
- Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock);
- mMixerThread->deleteTrackName_l(mName);
}
-status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
{
audio_track_cblk_t* cblk = this->cblk();
uint32_t framesAvail;
@@ -1965,180 +2443,234 @@ getNextBuffer_exit:
return NOT_ENOUGH_DATA;
}
-status_t AudioFlinger::MixerThread::RecordTrack::start()
+status_t AudioFlinger::RecordThread::RecordTrack::start()
{
- return mMixerThread->mAudioFlinger->startRecord(this);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ return recordThread->start(this);
+ }
+ return NO_INIT;
}
-void AudioFlinger::MixerThread::RecordTrack::stop()
+void AudioFlinger::RecordThread::RecordTrack::stop()
{
- mMixerThread->mAudioFlinger->stopRecord(this);
- TrackBase::reset();
- // Force overerrun condition to avoid false overrun callback until first data is
- // read from buffer
- mCblk->flowControlFlag = 1;
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ recordThread->stop(this);
+ TrackBase::reset();
+ // Force overerrun condition to avoid false overrun callback until first data is
+ // read from buffer
+ mCblk->flowControlFlag = 1;
+ }
}
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::OutputTrack::OutputTrack(
- const sp<MixerThread>& mixerThread,
+AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
+ const wp<ThreadBase>& thread,
uint32_t sampleRate,
int format,
int channelCount,
int frameCount)
- : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL),
- mOutputMixerThread(mixerThread)
+ : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL),
+ mActive(false)
{
-
- mCblk->out = 1;
- mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mCblk->volume[0] = mCblk->volume[1] = 0x1000;
- mOutBuffer.frameCount = 0;
- mCblk->bufferTimeoutMs = 10;
-
- LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p",
- mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd);
-
+
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get();
+ if (mCblk != NULL) {
+ mCblk->out = 1;
+ mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
+ mCblk->volume[0] = mCblk->volume[1] = 0x1000;
+ mOutBuffer.frameCount = 0;
+ mWaitTimeMs = (playbackThread->frameCount() * 2 * 1000) / playbackThread->sampleRate();
+ playbackThread->mTracks.add(this);
+ LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p mWaitTimeMs %d",
+ mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd, mWaitTimeMs);
+ } else {
+ LOGW("Error creating output track on thread %p", playbackThread);
+ }
}
-AudioFlinger::MixerThread::OutputTrack::~OutputTrack()
+AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
{
- stop();
+ clearBufferQueue();
}
-status_t AudioFlinger::MixerThread::OutputTrack::start()
+status_t AudioFlinger::PlaybackThread::OutputTrack::start()
{
status_t status = Track::start();
-
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ mActive = true;
mRetryCount = 127;
return status;
}
-void AudioFlinger::MixerThread::OutputTrack::stop()
+void AudioFlinger::PlaybackThread::OutputTrack::stop()
{
Track::stop();
clearBufferQueue();
mOutBuffer.frameCount = 0;
+ mActive = false;
}
-void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames)
+bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
{
Buffer *pInBuffer;
Buffer inBuffer;
uint32_t channels = mCblk->channels;
-
+ bool outputBufferFull = false;
inBuffer.frameCount = frames;
inBuffer.i16 = data;
-
- if (mCblk->user == 0) {
- mOutputMixerThread->mAudioFlinger->mLock.lock();
- bool isMusicActive = mOutputMixerThread->isMusicActive_l();
- mOutputMixerThread->mAudioFlinger->mLock.unlock();
- if (isMusicActive) {
- mCblk->forceReady = 1;
- LOGV("OutputTrack::start() force ready");
- } else if (mCblk->frameCount > frames){
- if (mBufferQueue.size() < kMaxOutputTrackBuffers) {
- uint32_t startFrames = (mCblk->frameCount - frames);
- LOGV("OutputTrack::start() write %d frames", startFrames);
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[startFrames * channels];
- pInBuffer->frameCount = startFrames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- } else {
- LOGW ("OutputTrack::write() no more buffers");
+
+ uint32_t waitTimeLeftMs = mWaitTimeMs;
+
+ if (!mActive) {
+ start();
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ MixerThread *mixerThread = (MixerThread *)thread.get();
+ if (mCblk->frameCount > frames){
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ uint32_t startFrames = (mCblk->frameCount - frames);
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[startFrames * channels];
+ pInBuffer->frameCount = startFrames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else {
+ LOGW ("OutputTrack::write() %p no more buffers in queue", this);
+ }
}
- }
+ }
}
- while (1) {
+ while (waitTimeLeftMs) {
// First write pending buffers, then new data
if (mBufferQueue.size()) {
pInBuffer = mBufferQueue.itemAt(0);
} else {
pInBuffer = &inBuffer;
}
-
+
if (pInBuffer->frameCount == 0) {
break;
}
-
+
if (mOutBuffer.frameCount == 0) {
mOutBuffer.frameCount = pInBuffer->frameCount;
- if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) {
+ nsecs_t startTime = systemTime();
+ if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) {
+ LOGV ("OutputTrack::write() %p no more output buffers", this);
+ outputBufferFull = true;
break;
}
+ uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
+// LOGV("OutputTrack::write() waitTimeMs %d waitTimeLeftMs %d", waitTimeMs, waitTimeLeftMs)
+ if (waitTimeLeftMs >= waitTimeMs) {
+ waitTimeLeftMs -= waitTimeMs;
+ } else {
+ waitTimeLeftMs = 0;
+ }
}
-
+
uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount;
memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t));
mCblk->stepUser(outFrames);
pInBuffer->frameCount -= outFrames;
pInBuffer->i16 += outFrames * channels;
mOutBuffer.frameCount -= outFrames;
- mOutBuffer.i16 += outFrames * channels;
-
+ mOutBuffer.i16 += outFrames * channels;
+
if (pInBuffer->frameCount == 0) {
if (mBufferQueue.size()) {
mBufferQueue.removeAt(0);
delete [] pInBuffer->mBuffer;
delete pInBuffer;
+ LOGV("OutputTrack::write() %p released overflow buffer %d", this, mBufferQueue.size());
} else {
break;
}
}
}
-
+
// If we could not write all frames, allocate a buffer and queue it for next time.
if (inBuffer.frameCount) {
- if (mBufferQueue.size() < kMaxOutputTrackBuffers) {
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
pInBuffer = new Buffer;
pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels];
pInBuffer->frameCount = inBuffer.frameCount;
pInBuffer->i16 = pInBuffer->mBuffer;
memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t));
mBufferQueue.add(pInBuffer);
+ LOGV("OutputTrack::write() %p adding overflow buffer %d", this, mBufferQueue.size());
} else {
- LOGW("OutputTrack::write() no more buffers");
+ LOGW("OutputTrack::write() %p no more overflow buffers", this);
}
}
-
+
// Calling write() with a 0 length buffer, means that no more data will be written:
- // If no more buffers are pending, fill output track buffer to make sure it is started
+ // If no more buffers are pending, fill output track buffer to make sure it is started
// by output mixer.
- if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) {
- frames = mCblk->frameCount - mCblk->user;
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[frames * channels];
- pInBuffer->frameCount = frames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
+ if (frames == 0 && mBufferQueue.size() == 0) {
+ if (mCblk->user < mCblk->frameCount) {
+ frames = mCblk->frameCount - mCblk->user;
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[frames * channels];
+ pInBuffer->frameCount = frames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else {
+ stop();
+ }
}
+ return outputBufferFull;
}
-status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer)
+status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
{
int active;
- int timeout = 0;
status_t result;
audio_track_cblk_t* cblk = mCblk;
uint32_t framesReq = buffer->frameCount;
- LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
+// LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
buffer->frameCount = 0;
-
+
uint32_t framesAvail = cblk->framesAvailable();
+
if (framesAvail == 0) {
- return AudioTrack::NO_MORE_BUFFERS;
+ Mutex::Autolock _l(cblk->lock);
+ goto start_loop_here;
+ while (framesAvail == 0) {
+ active = mActive;
+ if (UNLIKELY(!active)) {
+ LOGV("Not active and NO_MORE_BUFFERS");
+ return AudioTrack::NO_MORE_BUFFERS;
+ }
+ result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
+ if (result != NO_ERROR) {
+ return AudioTrack::NO_MORE_BUFFERS;
+ }
+ // read the server count again
+ start_loop_here:
+ framesAvail = cblk->framesAvailable_l();
+ }
}
+// if (framesAvail < framesReq) {
+// return AudioTrack::NO_MORE_BUFFERS;
+// }
+
if (framesReq > framesAvail) {
framesReq = framesAvail;
}
@@ -2156,11 +2688,11 @@ status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvide
}
-void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue()
+void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
{
size_t size = mBufferQueue.size();
Buffer *pBuffer;
-
+
for (size_t i = 0; i < size; i++) {
pBuffer = mBufferQueue.itemAt(i);
delete [] pBuffer->mBuffer;
@@ -2192,7 +2724,7 @@ const sp<MemoryDealer>& AudioFlinger::Client::heap() const
// ----------------------------------------------------------------------------
-AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track)
+AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track)
: BnAudioTrack(),
mTrack(track)
{
@@ -2244,7 +2776,7 @@ status_t AudioFlinger::TrackHandle::onTransact(
sp<IAudioRecord> AudioFlinger::openRecord(
pid_t pid,
- int inputSource,
+ int input,
uint32_t sampleRate,
int format,
int channelCount,
@@ -2252,14 +2784,13 @@ sp<IAudioRecord> AudioFlinger::openRecord(
uint32_t flags,
status_t *status)
{
- sp<MixerThread::RecordTrack> recordTrack;
+ sp<RecordThread::RecordTrack> recordTrack;
sp<RecordHandle> recordHandle;
sp<Client> client;
wp<Client> wclient;
- AudioStreamIn* input = 0;
- int inFrameCount;
- size_t inputBufferSize;
status_t lStatus;
+ RecordThread *thread;
+ size_t inFrameCount;
// check calling permissions
if (!recordingAllowed()) {
@@ -2267,30 +2798,15 @@ sp<IAudioRecord> AudioFlinger::openRecord(
goto Exit;
}
- if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) {
- LOGE("invalid stream type");
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
- if (mAudioRecordThread == 0) {
- LOGE("Audio record thread not started");
- lStatus = NO_INIT;
- goto Exit;
- }
-
-
- // Check that audio input stream accepts requested audio parameters
- inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount);
- if (inputBufferSize == 0) {
- lStatus = BAD_VALUE;
- LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount);
- goto Exit;
- }
-
// add client to list
{ // scope for mLock
Mutex::Autolock _l(mLock);
+ thread = checkRecordThread_l(input);
+ if (thread == NULL) {
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
wclient = mClients.valueFor(pid);
if (wclient != NULL) {
client = wclient.promote();
@@ -2299,12 +2815,8 @@ sp<IAudioRecord> AudioFlinger::openRecord(
mClients.add(pid, client);
}
- // frameCount must be a multiple of input buffer size
- inFrameCount = inputBufferSize/channelCount/sizeof(short);
- frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
-
// create new record track. The record track uses one track in mHardwareMixerThread by convention.
- recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate,
+ recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate,
format, channelCount, frameCount, flags);
}
if (recordTrack->getCblk() == NULL) {
@@ -2324,22 +2836,9 @@ Exit:
return recordHandle;
}
-status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) {
- if (mAudioRecordThread != 0) {
- return mAudioRecordThread->start(recordTrack);
- }
- return NO_INIT;
-}
-
-void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) {
- if (mAudioRecordThread != 0) {
- mAudioRecordThread->stop(recordTrack);
- }
-}
-
// ----------------------------------------------------------------------------
-AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack)
+AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack)
: BnAudioRecord(),
mRecordTrack(recordTrack)
{
@@ -2371,86 +2870,164 @@ status_t AudioFlinger::RecordHandle::onTransact(
// ----------------------------------------------------------------------------
-AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware,
- const sp<AudioFlinger>& audioFlinger) :
- mAudioHardware(audioHardware),
- mAudioFlinger(audioFlinger),
- mActive(false)
+AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels) :
+ ThreadBase(audioFlinger),
+ mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
{
+ mReqChannelCount = AudioSystem::popCount(channels);
+ mReqSampleRate = sampleRate;
+ readInputParameters();
+ sendConfigEvent(AudioSystem::INPUT_OPENED);
}
-AudioFlinger::AudioRecordThread::~AudioRecordThread()
+
+AudioFlinger::RecordThread::~RecordThread()
{
+ delete[] mRsmpInBuffer;
+ if (mResampler != 0) {
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
+ }
}
-bool AudioFlinger::AudioRecordThread::threadLoop()
+void AudioFlinger::RecordThread::onFirstRef()
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "Record Thread %p", this);
+
+ run(buffer, PRIORITY_URGENT_AUDIO);
+}
+bool AudioFlinger::RecordThread::threadLoop()
{
- LOGV("AudioRecordThread: start record loop");
AudioBufferProvider::Buffer buffer;
- int inBufferSize = 0;
- int inFrameCount = 0;
- AudioStreamIn* input = 0;
+ sp<RecordTrack> activeTrack;
- mActive = 0;
-
// start recording
while (!exitPending()) {
- if (!mActive) {
- mLock.lock();
- if (!mActive && !exitPending()) {
- LOGV("AudioRecordThread: loop stopping");
- if (input) {
- delete input;
- input = 0;
+
+ processConfigEvents();
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ checkForNewParameters_l();
+ if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
+ if (!mStandby) {
+ mInput->standby();
+ mStandby = true;
}
- mRecordTrack.clear();
- mStopped.signal();
+ if (exitPending()) break;
+
+ LOGV("RecordThread: loop stopping");
+ // go to sleep
mWaitWorkCV.wait(mLock);
-
- LOGV("AudioRecordThread: loop starting");
- if (mRecordTrack != 0) {
- input = mAudioHardware->openInputStream(
- mRecordTrack->inputSource(),
- mRecordTrack->format(),
- mRecordTrack->channelCount(),
- mRecordTrack->sampleRate(),
- &mStartStatus,
- (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16));
- if (input != 0) {
- inBufferSize = input->bufferSize();
- inFrameCount = inBufferSize/input->frameSize();
+ LOGV("RecordThread: loop starting");
+ continue;
+ }
+ if (mActiveTrack != 0) {
+ if (mActiveTrack->mState == TrackBase::PAUSING) {
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ } else if (mActiveTrack->mState == TrackBase::RESUMING) {
+ mRsmpInIndex = mFrameCount;
+ if (mReqChannelCount != mActiveTrack->channelCount()) {
+ mActiveTrack.clear();
+ } else {
+ mActiveTrack->mState = TrackBase::ACTIVE;
}
- } else {
- mStartStatus = NO_INIT;
- }
- if (mStartStatus !=NO_ERROR) {
- LOGW("record start failed, status %d", mStartStatus);
- mActive = false;
- mRecordTrack.clear();
+ mStartStopCond.broadcast();
}
- mWaitWorkCV.signal();
+ mStandby = false;
}
- mLock.unlock();
- } else if (mRecordTrack != 0) {
-
- buffer.frameCount = inFrameCount;
- if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR &&
- (int)buffer.frameCount == inFrameCount)) {
- LOGV("AudioRecordThread read: %d frames", buffer.frameCount);
- ssize_t bytesRead = input->read(buffer.raw, inBufferSize);
- if (bytesRead < 0) {
- LOGE("Error reading audio input");
- sleep(1);
+ }
+
+ if (mActiveTrack != 0) {
+ buffer.frameCount = mFrameCount;
+ if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+ size_t framesOut = buffer.frameCount;
+ if (mResampler == 0) {
+ // no resampling
+ while (framesOut) {
+ size_t framesIn = mFrameCount - mRsmpInIndex;
+ if (framesIn) {
+ int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
+ int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize;
+ if (framesIn > framesOut)
+ framesIn = framesOut;
+ mRsmpInIndex += framesIn;
+ framesOut -= framesIn;
+ if (mChannelCount == mReqChannelCount ||
+ mFormat != AudioSystem::PCM_16_BIT) {
+ memcpy(dst, src, framesIn * mFrameSize);
+ } else {
+ int16_t *src16 = (int16_t *)src;
+ int16_t *dst16 = (int16_t *)dst;
+ if (mChannelCount == 1) {
+ while (framesIn--) {
+ *dst16++ = *src16;
+ *dst16++ = *src16++;
+ }
+ } else {
+ while (framesIn--) {
+ *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1);
+ src16 += 2;
+ }
+ }
+ }
+ }
+ if (framesOut && mFrameCount == mRsmpInIndex) {
+ ssize_t bytesRead;
+ if (framesOut == mFrameCount &&
+ (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
+ bytesRead = mInput->read(buffer.raw, mInputBytes);
+ framesOut = 0;
+ } else {
+ bytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mRsmpInIndex = 0;
+ }
+ if (bytesRead < 0) {
+ LOGE("Error reading audio input");
+ sleep(1);
+ mRsmpInIndex = mFrameCount;
+ framesOut = 0;
+ buffer.frameCount = 0;
+ }
+ }
+ }
+ } else {
+ // resampling
+
+ memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t));
+ // alter output frame count as if we were expecting stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ framesOut >>= 1;
+ }
+ mResampler->resample(mRsmpOutBuffer, framesOut, this);
+ // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
+ // are 32 bit aligned which should be always true.
+ if (mChannelCount == 2 && mReqChannelCount == 1) {
+ AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
+ // the resampler always outputs stereo samples: do post stereo to mono conversion
+ int16_t *src = (int16_t *)mRsmpOutBuffer;
+ int16_t *dst = buffer.i16;
+ while (framesOut--) {
+ *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1);
+ src += 2;
+ }
+ } else {
+ AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
+ }
+
}
- mRecordTrack->releaseBuffer(&buffer);
- mRecordTrack->overflow();
+ mActiveTrack->releaseBuffer(&buffer);
+ mActiveTrack->overflow();
}
-
// client isn't retrieving buffers fast enough
else {
- if (!mRecordTrack->setOverflow())
- LOGW("AudioRecordThread: buffer overflow");
+ if (!mActiveTrack->setOverflow())
+ LOGW("RecordThread: buffer overflow");
// Release the processor for a while before asking for a new buffer.
// This will give the application more chance to read from the buffer and
// clear the overflow.
@@ -2459,65 +3036,64 @@ bool AudioFlinger::AudioRecordThread::threadLoop()
}
}
-
- if (input) {
- delete input;
+ if (!mStandby) {
+ mInput->standby();
}
- mRecordTrack.clear();
-
+ mActiveTrack.clear();
+
+ sendConfigEvent(AudioSystem::INPUT_CLOSED);
+ processConfigEvents();
+
+ LOGV("RecordThread %p exiting", this);
return false;
}
-status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack)
+status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack)
{
- LOGV("AudioRecordThread::start");
+ LOGV("RecordThread::start");
AutoMutex lock(&mLock);
- mActive = true;
- // If starting the active track, just reset mActive in case a stop
- // was pending and exit
- if (recordTrack == mRecordTrack.get()) return NO_ERROR;
- if (mRecordTrack != 0) return -EBUSY;
+ if (mActiveTrack != 0) {
+ if (recordTrack != mActiveTrack.get()) return -EBUSY;
- mRecordTrack = recordTrack;
+ if (mActiveTrack->mState == TrackBase::PAUSING) mActiveTrack->mState = TrackBase::RESUMING;
+ return NO_ERROR;
+ }
+
+ mActiveTrack = recordTrack;
+ mActiveTrack->mState = TrackBase::RESUMING;
// signal thread to start
LOGV("Signal record thread");
mWaitWorkCV.signal();
- mWaitWorkCV.wait(mLock);
- LOGV("Record started, status %d", mStartStatus);
- return mStartStatus;
-}
-
-void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) {
- LOGV("AudioRecordThread::stop");
- AutoMutex lock(&mLock);
- if (mActive && (recordTrack == mRecordTrack.get())) {
- mActive = false;
- mStopped.wait(mLock);
+ mStartStopCond.wait(mLock);
+ if (mActiveTrack != 0) {
+ LOGV("Record started OK");
+ return NO_ERROR;
+ } else {
+ LOGV("Record failed to start");
+ return BAD_VALUE;
}
}
-void AudioFlinger::AudioRecordThread::exit()
-{
- LOGV("AudioRecordThread::exit");
- {
- AutoMutex lock(&mLock);
- requestExit();
- mWaitWorkCV.signal();
+void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
+ LOGV("RecordThread::stop");
+ AutoMutex lock(&mLock);
+ if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
+ mActiveTrack->mState = TrackBase::PAUSING;
+ mStartStopCond.wait(mLock);
}
- requestExitAndWait();
}
-status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& args)
+status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
String8 result;
pid_t pid = 0;
- if (mRecordTrack != 0 && mRecordTrack->mClient != 0) {
- snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid());
+ if (mActiveTrack != 0 && mActiveTrack->mClient != 0) {
+ snprintf(buffer, SIZE, "Record client pid: %d\n", mActiveTrack->mClient->pid());
result.append(buffer);
} else {
result.append("No record client\n");
@@ -2526,6 +3102,469 @@ status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& a
return NO_ERROR;
}
+status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ size_t framesReq = buffer->frameCount;
+ size_t framesReady = mFrameCount - mRsmpInIndex;
+ int channelCount;
+
+ if (framesReady == 0) {
+ ssize_t bytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ if (bytesRead < 0) {
+ LOGE("RecordThread::getNextBuffer() Error reading audio input");
+ sleep(1);
+ buffer->raw = 0;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+ }
+ mRsmpInIndex = 0;
+ framesReady = mFrameCount;
+ }
+
+ if (framesReq > framesReady) {
+ framesReq = framesReady;
+ }
+
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
+ buffer->frameCount = framesReq;
+ return NO_ERROR;
+}
+
+void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ mRsmpInIndex += buffer->frameCount;
+ buffer->frameCount = 0;
+}
+
+bool AudioFlinger::RecordThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ int reqFormat = mFormat;
+ int reqSamplingRate = mReqSampleRate;
+ int reqChannelCount = mReqChannelCount;
+
+ mNewParameters.removeAt(0);
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reqSamplingRate = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ reqFormat = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ reqChannelCount = AudioSystem::popCount(value);
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (mActiveTrack != 0) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mInput->setParameters(keyValuePair);
+ if (status == INVALID_OPERATION) {
+ mInput->standby();
+ status = mInput->setParameters(keyValuePair);
+ }
+ if (reconfig) {
+ if (status == BAD_VALUE &&
+ reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT &&
+ ((int)mInput->sampleRate() <= 2 * reqSamplingRate) &&
+ (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) {
+ status = NO_ERROR;
+ }
+ if (status == NO_ERROR) {
+ readInputParameters();
+ sendConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+ }
+ }
+ }
+ mParamStatus = status;
+ mParamCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ return reconfig;
+}
+
+String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
+{
+ return mInput->getParameters(keys);
+}
+
+void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = 0;
+
+ switch (event) {
+ case AudioSystem::INPUT_OPENED:
+ case AudioSystem::INPUT_CONFIG_CHANGED:
+ desc.channels = mChannelCount;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mFrameCount;
+ desc.latency = 0;
+ param2 = &desc;
+ break;
+
+ case AudioSystem::INPUT_CLOSED:
+ default:
+ break;
+ }
+ mAudioFlinger->audioConfigChanged(event, this, param2);
+}
+
+void AudioFlinger::RecordThread::readInputParameters()
+{
+ if (mRsmpInBuffer) delete mRsmpInBuffer;
+ if (mRsmpOutBuffer) delete mRsmpOutBuffer;
+ if (mResampler) delete mResampler;
+ mResampler = 0;
+
+ mSampleRate = mInput->sampleRate();
+ mChannelCount = AudioSystem::popCount(mInput->channels());
+ mFormat = mInput->format();
+ mFrameSize = mInput->frameSize();
+ mInputBytes = mInput->bufferSize();
+ mFrameCount = mInputBytes / mFrameSize;
+ mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
+
+ if (mSampleRate != mReqSampleRate && mChannelCount < 3 && mReqChannelCount < 3)
+ {
+ int channelCount;
+ // optmization: if mono to mono, use the resampler in stereo to stereo mode to avoid
+ // stereo to mono post process as the resampler always outputs stereo.
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
+ mResampler->setSampleRate(mSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ mRsmpOutBuffer = new int32_t[mFrameCount * 2];
+
+ // optmization: if mono to mono, alter input frame count as if we were inputing stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ mFrameCount >>= 1;
+ }
+
+ }
+ mRsmpInIndex = mFrameCount;
+}
+
+// ----------------------------------------------------------------------------
+
+int AudioFlinger::openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags)
+{
+ status_t status;
+ PlaybackThread *thread = NULL;
+ mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+ uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
+
+ LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
+ pDevices ? *pDevices : 0,
+ samplingRate,
+ format,
+ channels,
+ flags);
+
+ if (pDevices == NULL || *pDevices == 0) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+
+ AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status);
+ LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
+ output,
+ samplingRate,
+ format,
+ channels,
+ status);
+
+ mHardwareStatus = AUDIO_HW_IDLE;
+ if (output != 0) {
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format != AudioSystem::PCM_16_BIT) ||
+ (channels != AudioSystem::CHANNEL_OUT_STEREO)) {
+ thread = new DirectOutputThread(this, output);
+ LOGV("openOutput() created direct output: ID %d thread %p", (mNextThreadId + 1), thread);
+ } else {
+ thread = new MixerThread(this, output);
+ LOGV("openOutput() created mixer output: ID %d thread %p", (mNextThreadId + 1), thread);
+ }
+ mPlaybackThreads.add(++mNextThreadId, thread);
+
+ if (pSamplingRate) *pSamplingRate = samplingRate;
+ if (pFormat) *pFormat = format;
+ if (pChannels) *pChannels = channels;
+ if (pLatencyMs) *pLatencyMs = thread->latency();
+ }
+
+ return mNextThreadId;
+}
+
+int AudioFlinger::openDuplicateOutput(int output1, int output2)
+{
+ Mutex::Autolock _l(mLock);
+ MixerThread *thread1 = checkMixerThread_l(output1);
+ MixerThread *thread2 = checkMixerThread_l(output2);
+
+ if (thread1 == NULL || thread2 == NULL) {
+ LOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2);
+ return 0;
+ }
+
+
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1);
+ thread->addOutputTrack(thread2);
+ mPlaybackThreads.add(++mNextThreadId, thread);
+ return mNextThreadId;
+}
+
+status_t AudioFlinger::closeOutput(int output)
+{
+ // keep strong reference on the playback thread so that
+ // it is not destroyed while exit() is executed
+ sp <PlaybackThread> thread;
+ {
+ Mutex::Autolock _l(mLock);
+ thread = checkPlaybackThread_l(output);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("closeOutput() %d", output);
+
+ if (thread->type() == PlaybackThread::MIXER) {
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ if (mPlaybackThreads.valueAt(i)->type() == PlaybackThread::DUPLICATING) {
+ DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
+ dupThread->removeOutputTrack((MixerThread *)thread.get());
+ }
+ }
+ }
+ mPlaybackThreads.removeItem(output);
+ }
+ thread->exit();
+
+ if (thread->type() != PlaybackThread::DUPLICATING) {
+ mAudioHardware->closeOutputStream(thread->getOutput());
+ }
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::suspendOutput(int output)
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("suspendOutput() %d", output);
+ thread->suspend();
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::restoreOutput(int output)
+{
+ Mutex::Autolock _l(mLock);
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("restoreOutput() %d", output);
+
+ thread->restore();
+
+ return NO_ERROR;
+}
+
+int AudioFlinger::openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ status_t status;
+ RecordThread *thread = NULL;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+ uint32_t reqSamplingRate = samplingRate;
+ uint32_t reqFormat = format;
+ uint32_t reqChannels = channels;
+
+ if (pDevices == NULL || *pDevices == 0) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+
+ AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status,
+ (AudioSystem::audio_in_acoustics)acoustics);
+ LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d",
+ input,
+ samplingRate,
+ format,
+ channels,
+ acoustics,
+ status);
+
+ // If the input could not be opened with the requested parameters and we can handle the conversion internally,
+ // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
+ // or stereo to mono conversions on 16 bit PCM inputs.
+ if (input == 0 && status == BAD_VALUE &&
+ reqFormat == format && format == AudioSystem::PCM_16_BIT &&
+ (samplingRate <= 2 * reqSamplingRate) &&
+ (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) {
+ LOGV("openInput() reopening with proposed sampling rate and channels");
+ input = mAudioHardware->openInputStream(*pDevices,
+ (int *)&format,
+ &channels,
+ &samplingRate,
+ &status,
+ (AudioSystem::audio_in_acoustics)acoustics);
+ }
+
+ if (input != 0) {
+ // Start record thread
+ thread = new RecordThread(this, input, reqSamplingRate, reqChannels);
+ mRecordThreads.add(++mNextThreadId, thread);
+ LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
+ if (pSamplingRate) *pSamplingRate = reqSamplingRate;
+ if (pFormat) *pFormat = format;
+ if (pChannels) *pChannels = reqChannels;
+
+ input->standby();
+ }
+
+ return mNextThreadId;
+}
+
+status_t AudioFlinger::closeInput(int input)
+{
+ // keep strong reference on the record thread so that
+ // it is not destroyed while exit() is executed
+ sp <RecordThread> thread;
+ {
+ Mutex::Autolock _l(mLock);
+ thread = checkRecordThread_l(input);
+ if (thread == NULL) {
+ return BAD_VALUE;
+ }
+
+ LOGV("closeInput() %d", input);
+ mRecordThreads.removeItem(input);
+ }
+ thread->exit();
+
+ mAudioHardware->closeInputStream(thread->getInput());
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::setStreamOutput(uint32_t stream, int output)
+{
+ Mutex::Autolock _l(mLock);
+ MixerThread *dstThread = checkMixerThread_l(output);
+ if (dstThread == NULL) {
+ LOGW("setStreamOutput() bad output id %d", output);
+ return BAD_VALUE;
+ }
+
+ LOGV("setStreamOutput() stream %d to output %d", stream, output);
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (thread != dstThread &&
+ thread->type() != PlaybackThread::DIRECT) {
+ MixerThread *srcThread = (MixerThread *)thread;
+ SortedVector < sp<MixerThread::Track> > tracks;
+ SortedVector < wp<MixerThread::Track> > activeTracks;
+ srcThread->getTracks(tracks, activeTracks, stream);
+ if (tracks.size()) {
+ dstThread->putTracks(tracks, activeTracks);
+ }
+ }
+ }
+
+ dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream);
+
+ return NO_ERROR;
+}
+
+// checkPlaybackThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const
+{
+ PlaybackThread *thread = NULL;
+ if (mPlaybackThreads.indexOfKey(output) >= 0) {
+ thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get();
+ }
+ return thread;
+}
+
+// checkMixerThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(int output) const
+{
+ PlaybackThread *thread = checkPlaybackThread_l(output);
+ if (thread != NULL) {
+ if (thread->type() == PlaybackThread::DIRECT) {
+ thread = NULL;
+ }
+ }
+ return (MixerThread *)thread;
+}
+
+// checkRecordThread_l() must be called with AudioFlinger::mLock held
+AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const
+{
+ RecordThread *thread = NULL;
+ if (mRecordThreads.indexOfKey(input) >= 0) {
+ thread = (RecordThread *)mRecordThreads.valueFor(input).get();
+ }
+ return thread;
+}
+
+// ----------------------------------------------------------------------------
+
status_t AudioFlinger::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -2533,6 +3572,7 @@ status_t AudioFlinger::onTransact(
}
// ----------------------------------------------------------------------------
+
void AudioFlinger::instantiate() {
defaultServiceManager()->addService(
String16("media.audio_flinger"), new AudioFlinger());
diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h
index 634934e..65c148e 100644
--- a/libs/audioflinger/AudioFlinger.h
+++ b/libs/audioflinger/AudioFlinger.h
@@ -30,8 +30,7 @@
#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
-#include <utils/MemoryDealer.h>
-#include <utils/KeyedVector.h>
+#include <binder/MemoryDealer.h>
#include <utils/SortedVector.h>
#include <utils/Vector.h>
@@ -44,6 +43,7 @@ namespace android {
class audio_track_cblk_t;
class AudioMixer;
class AudioBuffer;
+class AudioResampler;
// ----------------------------------------------------------------------------
@@ -56,7 +56,7 @@ class AudioBuffer;
static const nsecs_t kStandbyTimeInNsecs = seconds(3);
-class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient
+class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient
{
public:
static void instantiate();
@@ -73,6 +73,7 @@ public:
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
+ int output,
status_t *status);
virtual uint32_t sampleRate(int output) const;
@@ -87,33 +88,51 @@ public:
virtual float masterVolume() const;
virtual bool masterMute() const;
- virtual status_t setStreamVolume(int stream, float value);
+ virtual status_t setStreamVolume(int stream, float value, int output);
virtual status_t setStreamMute(int stream, bool muted);
- virtual float streamVolume(int stream) const;
+ virtual float streamVolume(int stream, int output) const;
virtual bool streamMute(int stream) const;
- virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask);
- virtual uint32_t getRouting(int mode) const;
-
virtual status_t setMode(int mode);
- virtual int getMode() const;
virtual status_t setMicMute(bool state);
virtual bool getMicMute() const;
virtual bool isMusicActive() const;
- virtual bool isA2dpEnabled() const;
-
- virtual status_t setParameter(const char* key, const char* value);
+ virtual status_t setParameters(int ioHandle, const String8& keyValuePairs);
+ virtual String8 getParameters(int ioHandle, const String8& keys);
virtual void registerClient(const sp<IAudioFlingerClient>& client);
-
+
virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount);
-
- virtual void wakeUp() { mWaitWorkCV.broadcast(); }
-
+
+ virtual int openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags);
+
+ virtual int openDuplicateOutput(int output1, int output2);
+
+ virtual status_t closeOutput(int output);
+
+ virtual status_t suspendOutput(int output);
+
+ virtual status_t restoreOutput(int output);
+
+ virtual int openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics);
+
+ virtual status_t closeInput(int input);
+
+ virtual status_t setStreamOutput(uint32_t stream, int output);
+
// IBinder::DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
@@ -139,7 +158,7 @@ public:
// record interface
virtual sp<IAudioRecord> openRecord(
pid_t pid,
- int inputSource,
+ int input,
uint32_t sampleRate,
int format,
int channelCount,
@@ -156,27 +175,7 @@ public:
private:
AudioFlinger();
virtual ~AudioFlinger();
-
- void setOutput(int outputType);
- void doSetOutput(int outputType);
-
-#ifdef WITH_A2DP
- void setA2dpEnabled_l(bool enable);
- void checkA2dpEnabledChange_l();
-#endif
- static bool streamForcedToSpeaker(int streamType);
-
- // Management of forced route to speaker for certain track types.
- enum force_speaker_command {
- ACTIVE_TRACK_ADDED = 0,
- ACTIVE_TRACK_REMOVED,
- CHECK_ROUTE_RESTORE_TIME,
- FORCE_ROUTE_RESTORE
- };
- void handleForcedSpeakerRoute(int command);
-#ifdef WITH_A2DP
- void handleRouteDisablesA2dp_l(int routes);
-#endif
+
// Internal dump utilites.
status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
@@ -201,14 +200,17 @@ private:
class TrackHandle;
class RecordHandle;
- class AudioRecordThread;
-
-
- // --- MixerThread ---
- class MixerThread : public Thread {
+ class RecordThread;
+ class PlaybackThread;
+ class MixerThread;
+ class DirectOutputThread;
+ class Track;
+ class RecordTrack;
+
+ class ThreadBase : public Thread {
public:
-
- // --- Track ---
+ ThreadBase (const sp<AudioFlinger>& audioFlinger);
+ virtual ~ThreadBase();
// base for record and playback
class TrackBase : public AudioBufferProvider, public RefBase {
@@ -230,7 +232,7 @@ private:
// The upper 16 bits are used for track-specific flags.
};
- TrackBase(const sp<MixerThread>& mixerThread,
+ TrackBase(const wp<ThreadBase>& thread,
const sp<Client>& client,
uint32_t sampleRate,
int format,
@@ -243,11 +245,15 @@ private:
virtual status_t start() = 0;
virtual void stop() = 0;
sp<IMemory> getCblk() const;
+ audio_track_cblk_t* cblk() const { return mCblk; }
protected:
- friend class MixerThread;
+ friend class ThreadBase;
friend class RecordHandle;
- friend class AudioRecordThread;
+ friend class PlaybackThread;
+ friend class RecordThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
TrackBase(const TrackBase&);
TrackBase& operator = (const TrackBase&);
@@ -255,10 +261,6 @@ private:
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0;
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
- audio_track_cblk_t* cblk() const {
- return mCblk;
- }
-
int format() const {
return mFormat;
}
@@ -269,10 +271,6 @@ private:
void* getBuffer(uint32_t offset, uint32_t frames) const;
- int name() const {
- return mName;
- }
-
bool isStopped() const {
return mState == STOPPED;
}
@@ -284,14 +282,13 @@ private:
bool step();
void reset();
- sp<MixerThread> mMixerThread;
+ wp<ThreadBase> mThread;
sp<Client> mClient;
sp<IMemory> mCblkMemory;
audio_track_cblk_t* mCblk;
void* mBuffer;
void* mBufferEnd;
uint32_t mFrameCount;
- int mName;
// we don't really need a lock for these
int mState;
int mClientTid;
@@ -299,10 +296,69 @@ private:
uint32_t mFlags;
};
+ class ConfigEvent {
+ public:
+ ConfigEvent() : mEvent(0), mParam(0) {}
+
+ int mEvent;
+ int mParam;
+ };
+
+ uint32_t sampleRate() const;
+ int channelCount() const;
+ int format() const;
+ size_t frameCount() const;
+ void wakeUp() { mWaitWorkCV.broadcast(); }
+ void exit();
+ virtual bool checkForNewParameters_l() = 0;
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys) = 0;
+ virtual void audioConfigChanged(int event, int param = 0) = 0;
+ void sendConfigEvent(int event, int param = 0);
+ void sendConfigEvent_l(int event, int param = 0);
+ void processConfigEvents();
+
+ mutable Mutex mLock;
+
+ protected:
+
+ friend class Track;
+ friend class TrackBase;
+ friend class PlaybackThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+ friend class DuplicatingThread;
+ friend class RecordThread;
+ friend class RecordTrack;
+
+ Condition mWaitWorkCV;
+ sp<AudioFlinger> mAudioFlinger;
+ uint32_t mSampleRate;
+ size_t mFrameCount;
+ int mChannelCount;
+ int mFormat;
+ uint32_t mFrameSize;
+ Condition mParamCond;
+ Vector<String8> mNewParameters;
+ status_t mParamStatus;
+ Vector<ConfigEvent *> mConfigEvents;
+ bool mStandby;
+ };
+
+ // --- PlaybackThread ---
+ class PlaybackThread : public ThreadBase {
+ public:
+
+ enum type {
+ MIXER,
+ DIRECT,
+ DUPLICATING
+ };
+
// playback track
class Track : public TrackBase {
public:
- Track( const sp<MixerThread>& mixerThread,
+ Track( const wp<ThreadBase>& thread,
const sp<Client>& client,
int streamType,
uint32_t sampleRate,
@@ -321,6 +377,9 @@ private:
void destroy();
void mute(bool);
void setVolume(float left, float right);
+ int name() const {
+ return mName;
+ }
int type() const {
return mStreamType;
@@ -328,29 +387,25 @@ private:
protected:
- friend class MixerThread;
+ friend class ThreadBase;
friend class AudioFlinger;
- friend class AudioFlinger::TrackHandle;
+ friend class TrackHandle;
+ friend class PlaybackThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
Track(const Track&);
Track& operator = (const Track&);
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
-
- bool isMuted() const {
- return (mMute || mMixerThread->mStreamTypes[mStreamType].mute);
- }
-
+ bool isMuted() { return mMute; }
bool isPausing() const {
return mState == PAUSING;
}
-
bool isPaused() const {
return mState == PAUSED;
}
-
bool isReady() const;
-
void setPaused() { mState = PAUSED; }
void reset();
@@ -364,54 +419,20 @@ private:
sp<IMemory> mSharedBuffer;
bool mResetDone;
int mStreamType;
+ int mName;
}; // end of Track
- // record track
- class RecordTrack : public TrackBase {
- public:
- RecordTrack(const sp<MixerThread>& mixerThread,
- const sp<Client>& client,
- int inputSource,
- uint32_t sampleRate,
- int format,
- int channelCount,
- int frameCount,
- uint32_t flags);
- ~RecordTrack();
-
- virtual status_t start();
- virtual void stop();
-
- bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
- bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
-
- int inputSource() const { return mInputSource; }
-
- private:
- friend class AudioFlinger;
- friend class AudioFlinger::RecordHandle;
- friend class AudioFlinger::AudioRecordThread;
- friend class MixerThread;
-
- RecordTrack(const Track&);
- RecordTrack& operator = (const Track&);
-
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
-
- bool mOverflow;
- int mInputSource;
- };
// playback track
class OutputTrack : public Track {
public:
-
+
class Buffer: public AudioBufferProvider::Buffer {
public:
int16_t *mBuffer;
};
-
- OutputTrack( const sp<MixerThread>& mixerThread,
+
+ OutputTrack( const wp<ThreadBase>& thread,
uint32_t sampleRate,
int format,
int channelCount,
@@ -420,35 +441,35 @@ private:
virtual status_t start();
virtual void stop();
- void write(int16_t* data, uint32_t frames);
+ bool write(int16_t* data, uint32_t frames);
bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; }
+ bool isActive() { return mActive; }
+ wp<ThreadBase>& thread() { return mThread; }
private:
- status_t obtainBuffer(AudioBufferProvider::Buffer* buffer);
+ status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs);
void clearBufferQueue();
-
- sp<MixerThread> mOutputMixerThread;
+
+ // Maximum number of pending buffers allocated by OutputTrack::write()
+ static const uint8_t kMaxOverFlowBuffers = 3;
+
Vector < Buffer* > mBufferQueue;
AudioBufferProvider::Buffer mOutBuffer;
- uint32_t mFramesWritten;
-
- }; // end of OutputTrack
+ uint32_t mWaitTimeMs;
+ bool mActive;
- MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType);
- virtual ~MixerThread();
+ }; // end of OutputTrack
+
+ PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output);
+ virtual ~PlaybackThread();
virtual status_t dump(int fd, const Vector<String16>& args);
// Thread virtuals
- virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
- virtual uint32_t sampleRate() const;
- virtual int channelCount() const;
- virtual int format() const;
- virtual size_t frameCount() const;
virtual uint32_t latency() const;
virtual status_t setMasterVolume(float value);
@@ -463,9 +484,8 @@ private:
virtual float streamVolume(int stream) const;
virtual bool streamMute(int stream) const;
- bool isMusicActive_l() const;
-
-
+ bool isMusicActive() const;
+
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,
int streamType,
@@ -475,13 +495,15 @@ private:
int frameCount,
const sp<IMemory>& sharedBuffer,
status_t *status);
-
- void getTracks_l(SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks);
- void putTracks_l(SortedVector < sp<Track> >& tracks,
- SortedVector < wp<Track> >& activeTracks);
- void setOuputTrack(OutputTrack *track) { mOutputTrack = track; }
-
+
+ AudioStreamOut* getOutput() { return mOutput; }
+
+ virtual int type() const { return mType; }
+ void suspend() { mSuspended++; }
+ void restore() { if (mSuspended) mSuspended--; }
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged(int event, int param = 0);
+
struct stream_type_t {
stream_type_t()
: volume(1.0f),
@@ -492,56 +514,115 @@ private:
bool mute;
};
- private:
+ protected:
+ int mType;
+ int16_t* mMixBuffer;
+ int mSuspended;
+ int mBytesWritten;
+ bool mMasterMute;
+ SortedVector< wp<Track> > mActiveTracks;
+ private:
friend class AudioFlinger;
+ friend class OutputTrack;
friend class Track;
friend class TrackBase;
- friend class RecordTrack;
-
- MixerThread(const Client&);
- MixerThread& operator = (const MixerThread&);
-
+ friend class MixerThread;
+ friend class DirectOutputThread;
+ friend class DuplicatingThread;
+
+ PlaybackThread(const Client&);
+ PlaybackThread& operator = (const PlaybackThread&);
+
status_t addTrack_l(const sp<Track>& track);
void destroyTrack_l(const sp<Track>& track);
- int getTrackName_l();
- void deleteTrackName_l(int name);
- void addActiveTrack_l(const wp<Track>& t);
- void removeActiveTrack_l(const wp<Track>& t);
- size_t getOutputFrameCount();
+ virtual int getTrackName_l() = 0;
+ virtual void deleteTrackName_l(int name) = 0;
+ void readOutputParameters();
- status_t dumpInternals(int fd, const Vector<String16>& args);
+ virtual status_t dumpInternals(int fd, const Vector<String16>& args);
status_t dumpTracks(int fd, const Vector<String16>& args);
-
- sp<AudioFlinger> mAudioFlinger;
- SortedVector< wp<Track> > mActiveTracks;
+
SortedVector< sp<Track> > mTracks;
- stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
- AudioMixer* mAudioMixer;
+ // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
+ stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1];
AudioStreamOut* mOutput;
- int mOutputType;
- uint32_t mSampleRate;
- size_t mFrameCount;
- int mChannelCount;
- int mFormat;
- int16_t* mMixBuffer;
float mMasterVolume;
- bool mMasterMute;
nsecs_t mLastWriteTime;
int mNumWrites;
int mNumDelayedWrites;
- bool mStandby;
bool mInWrite;
- sp <OutputTrack> mOutputTrack;
+ int mMinBytesToWrite;
+ };
+
+ class MixerThread : public PlaybackThread {
+ public:
+ MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output);
+ virtual ~MixerThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ void getTracks(SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks,
+ int streamType);
+ void putTracks(SortedVector < sp<Track> >& tracks,
+ SortedVector < wp<Track> >& activeTracks);
+ virtual int getTrackName_l();
+ virtual void deleteTrackName_l(int name);
+ virtual bool checkForNewParameters_l();
+ virtual status_t dumpInternals(int fd, const Vector<String16>& args);
+
+ protected:
+ size_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove);
+
+ AudioMixer* mAudioMixer;
+ };
+
+ class DirectOutputThread : public PlaybackThread {
+ public:
+
+ DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output);
+ ~DirectOutputThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ virtual int getTrackName_l();
+ virtual void deleteTrackName_l(int name);
+ virtual bool checkForNewParameters_l();
+
+ private:
+ float mLeftVolume;
+ float mRightVolume;
};
-
+ class DuplicatingThread : public MixerThread {
+ public:
+ DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread);
+ ~DuplicatingThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+ void addOutputTrack(MixerThread* thread);
+ void removeOutputTrack(MixerThread* thread);
+
+ private:
+ SortedVector < sp<OutputTrack> > mOutputTracks;
+ };
+
+ PlaybackThread *checkPlaybackThread_l(int output) const;
+ MixerThread *checkMixerThread_l(int output) const;
+ RecordThread *checkRecordThread_l(int input) const;
+ float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
+ void audioConfigChanged(int event, const sp<ThreadBase>& thread, void *param2);
+
friend class AudioBuffer;
class TrackHandle : public android::BnAudioTrack {
public:
- TrackHandle(const sp<MixerThread::Track>& track);
+ TrackHandle(const sp<PlaybackThread::Track>& track);
virtual ~TrackHandle();
virtual status_t start();
virtual void stop();
@@ -553,20 +634,91 @@ private:
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<MixerThread::Track> mTrack;
+ sp<PlaybackThread::Track> mTrack;
};
friend class Client;
- friend class MixerThread::Track;
+ friend class PlaybackThread::Track;
void removeClient(pid_t pid);
+ // record thread
+ class RecordThread : public ThreadBase, public AudioBufferProvider
+ {
+ public:
+
+ // record track
+ class RecordTrack : public TrackBase {
+ public:
+ RecordTrack(const wp<ThreadBase>& thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ int format,
+ int channelCount,
+ int frameCount,
+ uint32_t flags);
+ ~RecordTrack();
+
+ virtual status_t start();
+ virtual void stop();
+
+ bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; }
+ bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
+
+ private:
+ friend class AudioFlinger;
+ friend class RecordThread;
+
+ RecordTrack(const RecordTrack&);
+ RecordTrack& operator = (const RecordTrack&);
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+
+ bool mOverflow;
+ };
+
+
+ RecordThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamIn *input,
+ uint32_t sampleRate,
+ uint32_t channels);
+ ~RecordThread();
+
+ virtual bool threadLoop();
+ virtual status_t readyToRun() { return NO_ERROR; }
+ virtual void onFirstRef();
+
+ status_t start(RecordTrack* recordTrack);
+ void stop(RecordTrack* recordTrack);
+ status_t dump(int fd, const Vector<String16>& args);
+ AudioStreamIn* getInput() { return mInput; }
+
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+ virtual bool checkForNewParameters_l();
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged(int event, int param = 0);
+ void readInputParameters();
+
+ private:
+ RecordThread();
+ AudioStreamIn *mInput;
+ sp<RecordTrack> mActiveTrack;
+ Condition mStartStopCond;
+ AudioResampler *mResampler;
+ int32_t *mRsmpOutBuffer;
+ int16_t *mRsmpInBuffer;
+ size_t mRsmpInIndex;
+ size_t mInputBytes;
+ int mReqChannelCount;
+ uint32_t mReqSampleRate;
+ };
class RecordHandle : public android::BnAudioRecord {
public:
- RecordHandle(const sp<MixerThread::RecordTrack>& recordTrack);
+ RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack);
virtual ~RecordHandle();
virtual status_t start();
virtual void stop();
@@ -574,66 +726,31 @@ private:
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
private:
- sp<MixerThread::RecordTrack> mRecordTrack;
+ sp<RecordThread::RecordTrack> mRecordTrack;
};
- // record thread
- class AudioRecordThread : public Thread
- {
- public:
- AudioRecordThread(AudioHardwareInterface* audioHardware, const sp<AudioFlinger>& audioFlinger);
- virtual ~AudioRecordThread();
- virtual bool threadLoop();
- virtual status_t readyToRun() { return NO_ERROR; }
- virtual void onFirstRef() {}
-
- status_t start(MixerThread::RecordTrack* recordTrack);
- void stop(MixerThread::RecordTrack* recordTrack);
- void exit();
- status_t dump(int fd, const Vector<String16>& args);
-
- private:
- AudioRecordThread();
- AudioHardwareInterface *mAudioHardware;
- sp<AudioFlinger> mAudioFlinger;
- sp<MixerThread::RecordTrack> mRecordTrack;
- Mutex mLock;
- Condition mWaitWorkCV;
- Condition mStopped;
- volatile bool mActive;
- status_t mStartStatus;
- };
+ friend class RecordThread;
+ friend class PlaybackThread;
- friend class AudioRecordThread;
- friend class MixerThread;
- status_t startRecord(MixerThread::RecordTrack* recordTrack);
- void stopRecord(MixerThread::RecordTrack* recordTrack);
-
- mutable Mutex mHardwareLock;
mutable Mutex mLock;
- mutable Condition mWaitWorkCV;
DefaultKeyedVector< pid_t, wp<Client> > mClients;
- sp<MixerThread> mA2dpMixerThread;
- sp<MixerThread> mHardwareMixerThread;
+ mutable Mutex mHardwareLock;
AudioHardwareInterface* mAudioHardware;
- AudioHardwareInterface* mA2dpAudioInterface;
- sp<AudioRecordThread> mAudioRecordThread;
- bool mA2dpEnabled;
- bool mNotifyA2dpChange;
mutable int mHardwareStatus;
- SortedVector< wp<IBinder> > mNotificationClients;
- int mForcedSpeakerCount;
- int mA2dpDisableCount;
-
- // true if A2DP should resume when mA2dpDisableCount returns to zero
- bool mA2dpSuppressed;
- uint32_t mSavedRoute;
- uint32_t mForcedRoute;
- nsecs_t mRouteRestoreTime;
- bool mMusicMuteSaved;
+
+
+ DefaultKeyedVector< int, sp<PlaybackThread> > mPlaybackThreads;
+ PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES];
+ float mMasterVolume;
+ bool mMasterMute;
+
+ DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads;
+
+ SortedVector< sp<IBinder> > mNotificationClients;
+ int mNextThreadId;
};
// ----------------------------------------------------------------------------
diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp
index 1e159b8..57874f3 100644
--- a/libs/audioflinger/AudioHardwareGeneric.cpp
+++ b/libs/audioflinger/AudioHardwareGeneric.cpp
@@ -49,8 +49,8 @@ AudioHardwareGeneric::AudioHardwareGeneric()
AudioHardwareGeneric::~AudioHardwareGeneric()
{
if (mFd >= 0) ::close(mFd);
- delete mOutput;
- delete mInput;
+ closeOutputStream((AudioStreamOut *)mOutput);
+ closeInputStream((AudioStreamIn *)mInput);
}
status_t AudioHardwareGeneric::initCheck()
@@ -63,7 +63,7 @@ status_t AudioHardwareGeneric::initCheck()
}
AudioStreamOut* AudioHardwareGeneric::openOutputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status)
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
{
AutoMutex lock(mLock);
@@ -77,7 +77,7 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream(
// create new output stream
AudioStreamOutGeneric* out = new AudioStreamOutGeneric();
- status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate);
+ status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate);
if (status) {
*status = lStatus;
}
@@ -89,17 +89,19 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream(
return mOutput;
}
-void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) {
- if (out == mOutput) mOutput = 0;
+void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) {
+ if (mOutput && out == mOutput) {
+ delete mOutput;
+ mOutput = 0;
+ }
}
AudioStreamIn* AudioHardwareGeneric::openInputStream(
- int inputSource, int format, int channelCount, uint32_t sampleRate,
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
// check for valid input source
- if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
- (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
+ if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
return 0;
}
@@ -115,7 +117,7 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream(
// create new output stream
AudioStreamInGeneric* in = new AudioStreamInGeneric();
- status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate, acoustics);
+ status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics);
if (status) {
*status = lStatus;
}
@@ -127,8 +129,11 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream(
return mInput;
}
-void AudioHardwareGeneric::closeInputStream(AudioStreamInGeneric* in) {
- if (in == mInput) mInput = 0;
+void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) {
+ if (mInput && in == mInput) {
+ delete mInput;
+ mInput = 0;
+ }
}
status_t AudioHardwareGeneric::setVoiceVolume(float v)
@@ -185,30 +190,42 @@ status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args)
status_t AudioStreamOutGeneric::set(
AudioHardwareGeneric *hw,
int fd,
- int format,
- int channels,
- uint32_t rate)
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate)
{
+ int lFormat = pFormat ? *pFormat : 0;
+ uint32_t lChannels = pChannels ? *pChannels : 0;
+ uint32_t lRate = pRate ? *pRate : 0;
+
// fix up defaults
- if (format == 0) format = AudioSystem::PCM_16_BIT;
- if (channels == 0) channels = channelCount();
- if (rate == 0) rate = sampleRate();
+ if (lFormat == 0) lFormat = format();
+ if (lChannels == 0) lChannels = channels();
+ if (lRate == 0) lRate = sampleRate();
// check values
- if ((format != AudioSystem::PCM_16_BIT) ||
- (channels != channelCount()) ||
- (rate != sampleRate()))
+ if ((lFormat != format()) ||
+ (lChannels != channels()) ||
+ (lRate != sampleRate())) {
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
return BAD_VALUE;
+ }
+
+ if (pFormat) *pFormat = lFormat;
+ if (pChannels) *pChannels = lChannels;
+ if (pRate) *pRate = lRate;
mAudioHardware = hw;
mFd = fd;
+ mDevice = devices;
return NO_ERROR;
}
AudioStreamOutGeneric::~AudioStreamOutGeneric()
{
- if (mAudioHardware)
- mAudioHardware->closeOutputStream(this);
}
ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes)
@@ -234,10 +251,12 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
- snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount());
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
+ snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
+ result.append(buffer);
snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
@@ -246,29 +265,68 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args)
return NO_ERROR;
}
+status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDevice = device;
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 AudioStreamOutGeneric::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
// ----------------------------------------------------------------------------
// record functions
status_t AudioStreamInGeneric::set(
AudioHardwareGeneric *hw,
int fd,
- int format,
- int channels,
- uint32_t rate,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate,
AudioSystem::audio_in_acoustics acoustics)
{
// FIXME: remove logging
- LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, format, channels, rate);
+ if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE;
+ LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate);
// check values
- if ((format != AudioSystem::PCM_16_BIT) ||
- (channels != channelCount()) ||
- (rate != sampleRate())) {
+ if ((*pFormat != format()) ||
+ (*pChannels != channels()) ||
+ (*pRate != sampleRate())) {
LOGE("Error opening input channel");
+ *pFormat = format();
+ *pChannels = channels();
+ *pRate = sampleRate();
return BAD_VALUE;
}
mAudioHardware = hw;
mFd = fd;
+ mDevice = devices;
return NO_ERROR;
}
@@ -276,14 +334,12 @@ AudioStreamInGeneric::~AudioStreamInGeneric()
{
// FIXME: remove logging
LOGD("AudioStreamInGeneric destructor");
- if (mAudioHardware)
- mAudioHardware->closeInputStream(this);
}
ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes)
{
// FIXME: remove logging
- LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, bytes, mFd);
+ LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, (int)bytes, mFd);
AutoMutex lock(mLock);
if (mFd < 0) {
LOGE("Attempt to read from unopened device");
@@ -303,10 +359,12 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
- snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount());
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
+ snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice);
+ result.append(buffer);
snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware);
result.append(buffer);
snprintf(buffer, SIZE, "\tmFd: %d\n", mFd);
@@ -315,6 +373,39 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args)
return NO_ERROR;
}
+status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs)
+{
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ status_t status = NO_ERROR;
+ int device;
+ LOGV("setParameters() %s", keyValuePairs.string());
+
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDevice = device;
+ param.remove(key);
+ }
+
+ if (param.size()) {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+String8 AudioStreamInGeneric::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ String8 value;
+ String8 key = String8(AudioParameter::keyRouting);
+
+ if (param.get(key, value) == NO_ERROR) {
+ param.addInt(key, (int)mDevice);
+ }
+
+ LOGV("getParameters() %s", param.toString().string());
+ return param.toString();
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h
index c89df87..42da413 100644
--- a/libs/audioflinger/AudioHardwareGeneric.h
+++ b/libs/audioflinger/AudioHardwareGeneric.h
@@ -39,24 +39,28 @@ public:
virtual status_t set(
AudioHardwareGeneric *hw,
int mFd,
- int format,
- int channelCount,
- uint32_t sampleRate);
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate);
virtual uint32_t sampleRate() const { return 44100; }
virtual size_t bufferSize() const { return 4096; }
- virtual int channelCount() const { return 2; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
virtual int format() const { return AudioSystem::PCM_16_BIT; }
virtual uint32_t latency() const { return 20; }
- virtual status_t setVolume(float volume) { return INVALID_OPERATION; }
+ virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; }
virtual ssize_t write(const void* buffer, size_t bytes);
virtual status_t standby();
virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
private:
AudioHardwareGeneric *mAudioHardware;
Mutex mLock;
int mFd;
+ uint32_t mDevice;
};
class AudioStreamInGeneric : public AudioStreamIn {
@@ -67,24 +71,28 @@ public:
virtual status_t set(
AudioHardwareGeneric *hw,
int mFd,
- int format,
- int channelCount,
- uint32_t sampleRate,
+ uint32_t devices,
+ int *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pRate,
AudioSystem::audio_in_acoustics acoustics);
- uint32_t sampleRate() const { return 8000; }
+ virtual uint32_t sampleRate() const { return 8000; }
virtual size_t bufferSize() const { return 320; }
- virtual int channelCount() const { return 1; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
virtual int format() const { return AudioSystem::PCM_16_BIT; }
virtual status_t setGain(float gain) { return INVALID_OPERATION; }
virtual ssize_t read(void* buffer, ssize_t bytes);
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t standby() { return NO_ERROR; }
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys);
private:
AudioHardwareGeneric *mAudioHardware;
Mutex mLock;
int mFd;
+ uint32_t mDevice;
};
@@ -101,28 +109,27 @@ public:
virtual status_t setMicMute(bool state);
virtual status_t getMicMute(bool* state);
- virtual status_t setParameter(const char* key, const char* value)
- { return NO_ERROR; }
-
// create I/O streams
virtual AudioStreamOut* openOutputStream(
- int format=0,
- int channelCount=0,
- uint32_t sampleRate=0,
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
virtual AudioStreamIn* openInputStream(
- int inputSource,
- int format,
- int channelCount,
- uint32_t sampleRate,
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
status_t *status,
AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
void closeOutputStream(AudioStreamOutGeneric* out);
void closeInputStream(AudioStreamInGeneric* in);
protected:
- virtual status_t doRouting() { return NO_ERROR; }
virtual status_t dump(int fd, const Vector<String16>& args);
private:
diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp
index cc1bd8f..9a4a7f9 100644
--- a/libs/audioflinger/AudioHardwareInterface.cpp
+++ b/libs/audioflinger/AudioHardwareInterface.cpp
@@ -18,6 +18,7 @@
#include <cutils/properties.h>
#include <string.h>
#include <unistd.h>
+//#define LOG_NDEBUG 0
#define LOG_TAG "AudioHardwareInterface"
#include <utils/Log.h>
@@ -25,15 +26,17 @@
#include "AudioHardwareStub.h"
#include "AudioHardwareGeneric.h"
+#ifdef WITH_A2DP
+#include "A2dpAudioInterface.h"
+#endif
-//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file
-#ifdef DUMP_FLINGER_OUT
+#ifdef ENABLE_AUDIO_DUMP
#include "AudioDumpInterface.h"
#endif
// change to 1 to log routing calls
-#define LOG_ROUTING_CALLS 0
+#define LOG_ROUTING_CALLS 1
namespace android {
@@ -48,14 +51,6 @@ static const char* routingModeStrings[] =
"IN_CALL"
};
-static const char* routeStrings[] =
-{
- "EARPIECE ",
- "SPEAKER ",
- "BLUETOOTH ",
- "HEADSET ",
- "BLUETOOTH_A2DP "
-};
static const char* routeNone = "NONE";
static const char* displayMode(int mode)
@@ -64,22 +59,6 @@ static const char* displayMode(int mode)
return routingModeStrings[0];
return routingModeStrings[mode+3];
}
-
-static const char* displayRoutes(uint32_t routes)
-{
- static char routeStr[80];
- if (routes == 0)
- return routeNone;
- routeStr[0] = 0;
- int bitMask = 1;
- for (int i = 0; i < 4; ++i, bitMask <<= 1) {
- if (routes & bitMask) {
- strcat(routeStr, routeStrings[i]);
- }
- }
- routeStr[strlen(routeStr)-1] = 0;
- return routeStr;
-}
#endif
// ----------------------------------------------------------------------------
@@ -112,13 +91,17 @@ AudioHardwareInterface* AudioHardwareInterface::create()
hw = new AudioHardwareStub();
}
-#ifdef DUMP_FLINGER_OUT
+#ifdef WITH_A2DP
+ hw = new A2dpAudioInterface(hw);
+#endif
+
+#ifdef ENABLE_AUDIO_DUMP
// This code adds a record of buffers in a file to write calls made by AudioFlinger.
// It replaces the current AudioHardwareInterface object by an intermediate one which
// will record buffers in a file (after sending them to hardware) for testing purpose.
- // This feature is enabled by defining symbol DUMP_FLINGER_OUT.
- // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file.
-
+ // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP.
+ // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file.
+ LOGV("opening PCM dump interface");
hw = new AudioDumpInterface(hw); // replace interface
#endif
return hw;
@@ -132,48 +115,9 @@ AudioStreamIn::~AudioStreamIn() {}
AudioHardwareBase::AudioHardwareBase()
{
- // force a routing update on initialization
- memset(&mRoutes, 0, sizeof(mRoutes));
mMode = 0;
}
-// generics for audio routing - the real work is done in doRouting
-status_t AudioHardwareBase::setRouting(int mode, uint32_t routes)
-{
-#if LOG_ROUTING_CALLS
- LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes));
-#endif
- if (mode == AudioSystem::MODE_CURRENT)
- mode = mMode;
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
- return BAD_VALUE;
- uint32_t old = mRoutes[mode];
- mRoutes[mode] = routes;
- if ((mode != mMode) || (old == routes))
- return NO_ERROR;
-#if LOG_ROUTING_CALLS
- const char* oldRouteStr = strdup(displayRoutes(old));
- LOGD("doRouting: mode=%s, old route=[%s], new route=[%s]",
- displayMode(mode), oldRouteStr, displayRoutes(routes));
- delete oldRouteStr;
-#endif
- return doRouting();
-}
-
-status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes)
-{
- if (mode == AudioSystem::MODE_CURRENT)
- mode = mMode;
- if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
- return BAD_VALUE;
- *routes = mRoutes[mode];
-#if LOG_ROUTING_CALLS
- LOGD("getRouting: mode=%s, routes=[%s]",
- displayMode(mode), displayRoutes(*routes));
-#endif
- return NO_ERROR;
-}
-
status_t AudioHardwareBase::setMode(int mode)
{
#if LOG_ROUTING_CALLS
@@ -182,29 +126,24 @@ status_t AudioHardwareBase::setMode(int mode)
if ((mode < 0) || (mode >= AudioSystem::NUM_MODES))
return BAD_VALUE;
if (mMode == mode)
- return NO_ERROR;
-#if LOG_ROUTING_CALLS
- LOGD("doRouting: old mode=%s, new mode=%s route=[%s]",
- displayMode(mMode), displayMode(mode), displayRoutes(mRoutes[mode]));
-#endif
+ return ALREADY_EXISTS;
mMode = mode;
- return doRouting();
+ return NO_ERROR;
}
-status_t AudioHardwareBase::getMode(int* mode)
+// default implementation
+status_t AudioHardwareBase::setParameters(const String8& keyValuePairs)
{
- // Implement: set audio routing
- *mode = mMode;
return NO_ERROR;
}
-status_t AudioHardwareBase::setParameter(const char* key, const char* value)
+// default implementation
+String8 AudioHardwareBase::getParameters(const String8& keys)
{
- // default implementation is to ignore
- return NO_ERROR;
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
}
-
// default implementation
size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
{
@@ -233,10 +172,6 @@ status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "\tmMode: %d\n", mMode);
result.append(buffer);
- for (int i = 0, n = AudioSystem::NUM_MODES; i < n; ++i) {
- snprintf(buffer, SIZE, "\tmRoutes[%d]: %d\n", i, mRoutes[i]);
- result.append(buffer);
- }
::write(fd, result.string(), result.size());
dump(fd, args); // Dump the state of the concrete child.
return NO_ERROR;
diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp
index 0ab4c60..ae391ee 100644
--- a/libs/audioflinger/AudioHardwareStub.cpp
+++ b/libs/audioflinger/AudioHardwareStub.cpp
@@ -43,10 +43,10 @@ status_t AudioHardwareStub::initCheck()
}
AudioStreamOut* AudioHardwareStub::openOutputStream(
- int format, int channelCount, uint32_t sampleRate, status_t *status)
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status)
{
AudioStreamOutStub* out = new AudioStreamOutStub();
- status_t lStatus = out->set(format, channelCount, sampleRate);
+ status_t lStatus = out->set(format, channels, sampleRate);
if (status) {
*status = lStatus;
}
@@ -56,18 +56,22 @@ AudioStreamOut* AudioHardwareStub::openOutputStream(
return 0;
}
+void AudioHardwareStub::closeOutputStream(AudioStreamOut* out)
+{
+ delete out;
+}
+
AudioStreamIn* AudioHardwareStub::openInputStream(
- int inputSource, int format, int channelCount, uint32_t sampleRate,
+ uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate,
status_t *status, AudioSystem::audio_in_acoustics acoustics)
{
// check for valid input source
- if ((inputSource < AudioRecord::DEFAULT_INPUT) ||
- (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) {
+ if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) {
return 0;
}
AudioStreamInStub* in = new AudioStreamInStub();
- status_t lStatus = in->set(format, channelCount, sampleRate, acoustics);
+ status_t lStatus = in->set(format, channels, sampleRate, acoustics);
if (status) {
*status = lStatus;
}
@@ -77,6 +81,11 @@ AudioStreamIn* AudioHardwareStub::openInputStream(
return 0;
}
+void AudioHardwareStub::closeInputStream(AudioStreamIn* in)
+{
+ delete in;
+}
+
status_t AudioHardwareStub::setVoiceVolume(float volume)
{
return NO_ERROR;
@@ -107,24 +116,19 @@ status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args)
// ----------------------------------------------------------------------------
-status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate)
+status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate)
{
- // fix up defaults
- if (format == 0) format = AudioSystem::PCM_16_BIT;
- if (channels == 0) channels = channelCount();
- if (rate == 0) rate = sampleRate();
+ if (pFormat) *pFormat = format();
+ if (pChannels) *pChannels = channels();
+ if (pRate) *pRate = sampleRate();
- if ((format == AudioSystem::PCM_16_BIT) &&
- (channels == channelCount()) &&
- (rate == sampleRate()))
- return NO_ERROR;
- return BAD_VALUE;
+ return NO_ERROR;
}
ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes)
{
// fake timing for audio output
- usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate());
+ usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
return bytes;
}
@@ -141,29 +145,31 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args)
snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n");
snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate());
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
- snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount());
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
}
+String8 AudioStreamOutStub::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
// ----------------------------------------------------------------------------
-status_t AudioStreamInStub::set(int format, int channels, uint32_t rate,
+status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate,
AudioSystem::audio_in_acoustics acoustics)
{
- if ((format == AudioSystem::PCM_16_BIT) &&
- (channels == channelCount()) &&
- (rate == sampleRate()))
- return NO_ERROR;
- return BAD_VALUE;
+ return NO_ERROR;
}
ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes)
{
// fake timing for audio input
- usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate());
+ usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate());
memset(buffer, 0, bytes);
return bytes;
}
@@ -179,7 +185,7 @@ status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args)
result.append(buffer);
snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize());
result.append(buffer);
- snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount());
+ snprintf(buffer, SIZE, "\tchannels: %d\n", channels());
result.append(buffer);
snprintf(buffer, SIZE, "\tformat: %d\n", format());
result.append(buffer);
@@ -187,6 +193,12 @@ status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args)
return NO_ERROR;
}
+String8 AudioStreamInStub::getParameters(const String8& keys)
+{
+ AudioParameter param = AudioParameter(keys);
+ return param.toString();
+}
+
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h
index bf63cc5..583f852 100644
--- a/libs/audioflinger/AudioHardwareStub.h
+++ b/libs/audioflinger/AudioHardwareStub.h
@@ -29,29 +29,33 @@ namespace android {
class AudioStreamOutStub : public AudioStreamOut {
public:
- virtual status_t set(int format, int channelCount, uint32_t sampleRate);
+ virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate);
virtual uint32_t sampleRate() const { return 44100; }
virtual size_t bufferSize() const { return 4096; }
- virtual int channelCount() const { return 2; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; }
virtual int format() const { return AudioSystem::PCM_16_BIT; }
virtual uint32_t latency() const { return 0; }
- virtual status_t setVolume(float volume) { return NO_ERROR; }
+ virtual status_t setVolume(float left, float right) { return NO_ERROR; }
virtual ssize_t write(const void* buffer, size_t bytes);
virtual status_t standby();
virtual status_t dump(int fd, const Vector<String16>& args);
+ virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
+ virtual String8 getParameters(const String8& keys);
};
class AudioStreamInStub : public AudioStreamIn {
public:
- virtual status_t set(int format, int channelCount, uint32_t sampleRate, AudioSystem::audio_in_acoustics acoustics);
+ virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics);
virtual uint32_t sampleRate() const { return 8000; }
virtual size_t bufferSize() const { return 320; }
- virtual int channelCount() const { return 1; }
+ virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; }
virtual int format() const { return AudioSystem::PCM_16_BIT; }
virtual status_t setGain(float gain) { return NO_ERROR; }
virtual ssize_t read(void* buffer, ssize_t bytes);
virtual status_t dump(int fd, const Vector<String16>& args);
virtual status_t standby() { return NO_ERROR; }
+ virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;}
+ virtual String8 getParameters(const String8& keys);
};
class AudioHardwareStub : public AudioHardwareBase
@@ -67,26 +71,25 @@ public:
virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; }
virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; }
- virtual status_t setParameter(const char* key, const char* value)
- { return NO_ERROR; }
-
// create I/O streams
virtual AudioStreamOut* openOutputStream(
- int format=0,
- int channelCount=0,
- uint32_t sampleRate=0,
+ uint32_t devices,
+ int *format=0,
+ uint32_t *channels=0,
+ uint32_t *sampleRate=0,
status_t *status=0);
+ virtual void closeOutputStream(AudioStreamOut* out);
virtual AudioStreamIn* openInputStream(
- int inputSource,
- int format,
- int channelCount,
- uint32_t sampleRate,
+ uint32_t devices,
+ int *format,
+ uint32_t *channels,
+ uint32_t *sampleRate,
status_t *status,
- AudioSystem::audio_in_acoustics acoustics);
+ AudioSystem::audio_in_acoustics acoustics);
+ virtual void closeInputStream(AudioStreamIn* in);
protected:
- virtual status_t doRouting() { return NO_ERROR; }
virtual status_t dump(int fd, const Vector<String16>& args);
bool mMicMute;
diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp
index b02efcc..19a442a 100644
--- a/libs/audioflinger/AudioMixer.cpp
+++ b/libs/audioflinger/AudioMixer.cpp
@@ -610,7 +610,6 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount,
t->in = in;
}
-inline
void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
{
for (size_t i=0 ; i<c ; i++) {
diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h
index 72ca28a..15766cd 100644
--- a/libs/audioflinger/AudioMixer.h
+++ b/libs/audioflinger/AudioMixer.h
@@ -85,6 +85,8 @@ public:
uint32_t trackNames() const { return mTrackNames; }
+ static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c);
+
private:
enum {
@@ -176,7 +178,6 @@ private:
static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
- static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c);
static void process__validate(state_t* state, void* output);
static void process__nop(state_t* state, void* output);
diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.cpp b/libs/audioflinger/AudioPolicyManagerGeneric.cpp
new file mode 100644
index 0000000..6323859
--- /dev/null
+++ b/libs/audioflinger/AudioPolicyManagerGeneric.cpp
@@ -0,0 +1,826 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioPolicyManagerGeneric"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+#include "AudioPolicyManagerGeneric.h"
+#include <media/mediarecorder.h>
+
+namespace android {
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyInterface implementation
+// ----------------------------------------------------------------------------
+
+
+status_t AudioPolicyManagerGeneric::setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+{
+
+ LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address);
+
+ // connect/disconnect only 1 device at a time
+ if (AudioSystem::popCount(device) != 1) return BAD_VALUE;
+
+ if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) {
+ LOGE("setDeviceConnectionState() invalid address: %s", device_address);
+ return BAD_VALUE;
+ }
+
+ // handle output devices
+ if (AudioSystem::isOutputDevice(device)) {
+ switch (state)
+ {
+ // handle output device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE:
+ if (mAvailableOutputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %x", device);
+ return INVALID_OPERATION;
+ }
+ LOGV("setDeviceConnectionState() connecting device %x", device);
+
+ // register new device as available
+ mAvailableOutputDevices |= device;
+ break;
+ // handle output device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE:
+ if (!(mAvailableOutputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %x", device);
+ return INVALID_OPERATION;
+ }
+ LOGV("setDeviceConnectionState() disconnecting device %x", device);
+ // remove device from available output devices
+ mAvailableOutputDevices &= ~device;
+ break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
+ // handle input devices
+ if (AudioSystem::isInputDevice(device)) {
+ switch (state)
+ {
+ // handle input device connection
+ case AudioSystem::DEVICE_STATE_AVAILABLE:
+ if (mAvailableInputDevices & device) {
+ LOGW("setDeviceConnectionState() device already connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices |= device;
+ break;
+
+ // handle input device disconnection
+ case AudioSystem::DEVICE_STATE_UNAVAILABLE:
+ if (!(mAvailableInputDevices & device)) {
+ LOGW("setDeviceConnectionState() device not connected: %d", device);
+ return INVALID_OPERATION;
+ }
+ mAvailableInputDevices &= ~device;
+ break;
+
+ default:
+ LOGE("setDeviceConnectionState() invalid state: %x", state);
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+ }
+
+ LOGW("setDeviceConnectionState() invalid device: %x", device);
+ return BAD_VALUE;
+}
+
+AudioSystem::device_connection_state AudioPolicyManagerGeneric::getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ String8 address = String8(device_address);
+ if (AudioSystem::isOutputDevice(device)) {
+ if (device & mAvailableOutputDevices) {
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ } else if (AudioSystem::isInputDevice(device)) {
+ if (device & mAvailableInputDevices) {
+ state = AudioSystem::DEVICE_STATE_AVAILABLE;
+ }
+ }
+
+ return state;
+}
+
+void AudioPolicyManagerGeneric::setPhoneState(int state)
+{
+ LOGV("setPhoneState() state %d", state);
+ uint32_t newDevice = 0;
+ if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ LOGW("setPhoneState() invalid state %d", state);
+ return;
+ }
+
+ if (state == mPhoneState ) {
+ LOGW("setPhoneState() setting same state %d", state);
+ return;
+ }
+ // store previous phone state for management of sonification strategy below
+ int oldState = mPhoneState;
+ mPhoneState = state;
+
+ // if leaving or entering in call state, handle special case of active streams
+ // pertaining to sonification strategy see handleIncallSonification()
+ if (state == AudioSystem::MODE_IN_CALL ||
+ oldState == AudioSystem::MODE_IN_CALL) {
+ bool starting = (state == AudioSystem::MODE_IN_CALL) ? true : false;
+ LOGV("setPhoneState() in call state management: new state is %d", state);
+ for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) {
+ handleIncallSonification(stream, starting);
+ }
+ }
+}
+
+void AudioPolicyManagerGeneric::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ LOGV("setRingerMode() mode %x, mask %x", mode, mask);
+
+ mRingerMode = mode;
+}
+
+void AudioPolicyManagerGeneric::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ LOGV("setForceUse) usage %d, config %d, mPhoneState %d", usage, config, mPhoneState);
+ mForceUse[usage] = config;
+}
+
+AudioSystem::forced_config AudioPolicyManagerGeneric::getForceUse(AudioSystem::force_use usage)
+{
+ return mForceUse[usage];
+}
+
+void AudioPolicyManagerGeneric::setSystemProperty(const char* property, const char* value)
+{
+ LOGV("setSystemProperty() property %s, value %s", property, value);
+ if (strcmp(property, "ro.camera.sound.forced") == 0) {
+ if (atoi(value)) {
+ LOGV("ENFORCED_AUDIBLE cannot be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false;
+ } else {
+ LOGV("ENFORCED_AUDIBLE can be muted");
+ mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true;
+ }
+ }
+}
+
+audio_io_handle_t AudioPolicyManagerGeneric::getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+{
+ LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mCurOutput != 0) {
+ LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channels %x, mDirectOutput %d",
+ mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput);
+
+ if (mTestOutputs[mCurOutput] == 0) {
+ LOGV("getOutput() opening test output");
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = mTestDevice;
+ outputDesc->mSamplingRate = mTestSamplingRate;
+ outputDesc->mFormat = mTestFormat;
+ outputDesc->mChannels = mTestChannels;
+ outputDesc->mLatency = mTestLatencyMs;
+ outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0);
+ outputDesc->mRefCount[stream] = 0;
+ mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mTestOutputs[mCurOutput]) {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"),mCurOutput);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString());
+ mOutputs.add(mTestOutputs[mCurOutput], outputDesc);
+ }
+ }
+ return mTestOutputs[mCurOutput];
+ }
+#endif //AUDIO_POLICY_TEST
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
+ (format != 0 && !AudioSystem::isLinearPCM(format)) ||
+ (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && channels != AudioSystem::CHANNEL_OUT_STEREO)) {
+ return 0;
+ }
+
+ return mHardwareOutput;
+}
+
+status_t AudioPolicyManagerGeneric::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("startOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("startOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, true);
+ }
+
+ // incremenent usage count for this stream on the requested output:
+ outputDesc->changeRefCount(stream, 1);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerGeneric::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ LOGV("stopOutput() output %d, stream %d", output, stream);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("stopOutput() unknow output %d", output);
+ return BAD_VALUE;
+ }
+
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+
+ // handle special case for sonification while in call
+ if (mPhoneState == AudioSystem::MODE_IN_CALL) {
+ handleIncallSonification(stream, false);
+ }
+
+ if (outputDesc->isUsedByStream(stream)) {
+ // decrement usage count of this stream on the output
+ outputDesc->changeRefCount(stream, -1);
+ return NO_ERROR;
+ } else {
+ LOGW("stopOutput() refcount is already 0 for output %d", output);
+ return INVALID_OPERATION;
+ }
+}
+
+void AudioPolicyManagerGeneric::releaseOutput(audio_io_handle_t output)
+{
+ LOGV("releaseOutput() %d", output);
+ ssize_t index = mOutputs.indexOfKey(output);
+ if (index < 0) {
+ LOGW("releaseOutput() releasing unknown output %d", output);
+ return;
+ }
+
+#ifdef AUDIO_POLICY_TEST
+ int testIndex = testOutputIndex(output);
+ if (testIndex != 0) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
+ if (outputDesc->refCount() == 0) {
+ mpClientInterface->closeOutput(output);
+ delete mOutputs.valueAt(index);
+ mOutputs.removeItem(output);
+ mTestOutputs[testIndex] = 0;
+ }
+ }
+#endif //AUDIO_POLICY_TEST
+}
+
+audio_io_handle_t AudioPolicyManagerGeneric::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ audio_io_handle_t input = 0;
+ uint32_t device;
+
+ LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics);
+
+ AudioInputDescriptor *inputDesc = new AudioInputDescriptor();
+ inputDesc->mDevice = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+ inputDesc->mSamplingRate = samplingRate;
+ inputDesc->mFormat = format;
+ inputDesc->mChannels = channels;
+ inputDesc->mAcoustics = acoustics;
+ inputDesc->mRefCount = 0;
+ input = mpClientInterface->openInput(&inputDesc->mDevice,
+ &inputDesc->mSamplingRate,
+ &inputDesc->mFormat,
+ &inputDesc->mChannels,
+ inputDesc->mAcoustics);
+
+ // only accept input with the exact requested set of parameters
+ if ((samplingRate != inputDesc->mSamplingRate) ||
+ (format != inputDesc->mFormat) ||
+ (channels != inputDesc->mChannels)) {
+ LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d",
+ samplingRate, format, channels);
+ mpClientInterface->closeInput(input);
+ delete inputDesc;
+ return 0;
+ }
+ mInputs.add(input, inputDesc);
+ return input;
+}
+
+status_t AudioPolicyManagerGeneric::startInput(audio_io_handle_t input)
+{
+ LOGV("startInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("startInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+#ifdef AUDIO_POLICY_TEST
+ if (mTestInput == 0)
+#endif //AUDIO_POLICY_TEST
+ {
+ // refuse 2 active AudioRecord clients at the same time
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ if (mInputs.valueAt(i)->mRefCount > 0) {
+ LOGW("startInput() input %d, other input %d already started", input, mInputs.keyAt(i));
+ return INVALID_OPERATION;
+ }
+ }
+ }
+
+ inputDesc->mRefCount = 1;
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerGeneric::stopInput(audio_io_handle_t input)
+{
+ LOGV("stopInput() input %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("stopInput() unknow input %d", input);
+ return BAD_VALUE;
+ }
+ AudioInputDescriptor *inputDesc = mInputs.valueAt(index);
+
+ if (inputDesc->mRefCount == 0) {
+ LOGW("stopInput() input %d already stopped", input);
+ return INVALID_OPERATION;
+ } else {
+ inputDesc->mRefCount = 0;
+ return NO_ERROR;
+ }
+}
+
+void AudioPolicyManagerGeneric::releaseInput(audio_io_handle_t input)
+{
+ LOGV("releaseInput() %d", input);
+ ssize_t index = mInputs.indexOfKey(input);
+ if (index < 0) {
+ LOGW("releaseInput() releasing unknown input %d", input);
+ return;
+ }
+ mpClientInterface->closeInput(input);
+ delete mInputs.valueAt(index);
+ mInputs.removeItem(input);
+}
+
+
+
+void AudioPolicyManagerGeneric::initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax);
+ mStreams[stream].mIndexMin = indexMin;
+ mStreams[stream].mIndexMax = indexMax;
+}
+
+status_t AudioPolicyManagerGeneric::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+
+ if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index);
+ mStreams[stream].mIndexCur = index;
+
+ // do not change actual stream volume if the stream is muted
+ if (mStreams[stream].mMuteCount != 0) {
+ return NO_ERROR;
+ }
+
+ // Do not changed in call volume if bluetooth is connected and vice versa
+ if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) ||
+ (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) {
+ LOGV("setStreamVolumeIndex() cannot set stream %d volume with force use = %d for comm",
+ stream, mForceUse[AudioSystem::FOR_COMMUNICATION]);
+ return INVALID_OPERATION;
+ }
+
+ // compute and apply stream volume on all outputs according to connected device
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i);
+ uint32_t device = outputDesc->device();
+
+ float volume = computeVolume((int)stream, index, device);
+
+ LOGV("setStreamVolume() for output %d stream %d, volume %f", mOutputs.keyAt(i), stream, volume);
+ mpClientInterface->setStreamVolume(stream, volume, mOutputs.keyAt(i));
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManagerGeneric::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+ if (index == 0) {
+ return BAD_VALUE;
+ }
+ LOGV("getStreamVolumeIndex() stream %d", stream);
+ *index = mStreams[stream].mIndexCur;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// AudioPolicyManagerGeneric
+// ----------------------------------------------------------------------------
+
+// --- class factory
+
+AudioPolicyManagerGeneric::AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface)
+ :
+#ifdef AUDIO_POLICY_TEST
+ Thread(false),
+#endif //AUDIO_POLICY_TEST
+ mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0)
+{
+ mpClientInterface = clientInterface;
+
+ for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) {
+ mForceUse[i] = AudioSystem::FORCE_NONE;
+ }
+
+ // devices available by default are speaker, ear piece and microphone
+ mAvailableOutputDevices = AudioSystem::DEVICE_OUT_SPEAKER;
+ mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC;
+
+ // open hardware output
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+ mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+
+ if (mHardwareOutput == 0) {
+ LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ mOutputs.add(mHardwareOutput, outputDesc);
+ }
+
+#ifdef AUDIO_POLICY_TEST
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+
+ mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER;
+ mTestSamplingRate = 44100;
+ mTestFormat = AudioSystem::PCM_16_BIT;
+ mTestChannels = AudioSystem::CHANNEL_OUT_STEREO;
+ mTestLatencyMs = 0;
+ mCurOutput = 0;
+ mDirectOutput = false;
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ mTestOutputs[i] = 0;
+ }
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "AudioPolicyManagerTest");
+ run(buffer, ANDROID_PRIORITY_AUDIO);
+#endif //AUDIO_POLICY_TEST
+}
+
+AudioPolicyManagerGeneric::~AudioPolicyManagerGeneric()
+{
+#ifdef AUDIO_POLICY_TEST
+ exit();
+#endif //AUDIO_POLICY_TEST
+
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ mpClientInterface->closeOutput(mOutputs.keyAt(i));
+ delete mOutputs.valueAt(i);
+ }
+ mOutputs.clear();
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ mpClientInterface->closeInput(mInputs.keyAt(i));
+ delete mInputs.valueAt(i);
+ }
+ mInputs.clear();
+}
+
+#ifdef AUDIO_POLICY_TEST
+bool AudioPolicyManagerGeneric::threadLoop()
+{
+ LOGV("entering threadLoop()");
+ while (!exitPending())
+ {
+ String8 command;
+ int valueInt;
+ String8 value;
+
+ Mutex::Autolock _l(mLock);
+ mWaitWorkCV.waitRelative(mLock, milliseconds(50));
+
+ command = mpClientInterface->getParameters(0, String8("test_cmd_policy"));
+ AudioParameter param = AudioParameter(command);
+
+ if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR &&
+ valueInt != 0) {
+ LOGV("Test command %s received", command.string());
+ String8 target;
+ if (param.get(String8("target"), target) != NO_ERROR) {
+ target = "Manager";
+ }
+ if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_output"));
+ mCurOutput = valueInt;
+ }
+ if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_direct"));
+ if (value == "false") {
+ mDirectOutput = false;
+ } else if (value == "true") {
+ mDirectOutput = true;
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_input"));
+ mTestInput = valueInt;
+ }
+
+ if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_format"));
+ int format = AudioSystem::INVALID_FORMAT;
+ if (value == "PCM 16 bits") {
+ format = AudioSystem::PCM_16_BIT;
+ } else if (value == "PCM 8 bits") {
+ format = AudioSystem::PCM_8_BIT;
+ } else if (value == "Compressed MP3") {
+ format = AudioSystem::MP3;
+ }
+ if (format != AudioSystem::INVALID_FORMAT) {
+ if (target == "Manager") {
+ mTestFormat = format;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("format"), format);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_channels"));
+ int channels = 0;
+
+ if (value == "Channels Stereo") {
+ channels = AudioSystem::CHANNEL_OUT_STEREO;
+ } else if (value == "Channels Mono") {
+ channels = AudioSystem::CHANNEL_OUT_MONO;
+ }
+ if (channels != 0) {
+ if (target == "Manager") {
+ mTestChannels = channels;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("channels"), channels);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+ if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_sampleRate"));
+ if (valueInt >= 0 && valueInt <= 96000) {
+ int samplingRate = valueInt;
+ if (target == "Manager") {
+ mTestSamplingRate = samplingRate;
+ } else if (mTestOutputs[mCurOutput] != 0) {
+ AudioParameter outputParam = AudioParameter();
+ outputParam.addInt(String8("sampling_rate"), samplingRate);
+ mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString());
+ }
+ }
+ }
+
+ if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) {
+ param.remove(String8("test_cmd_policy_reopen"));
+
+ mpClientInterface->closeOutput(mHardwareOutput);
+ delete mOutputs.valueFor(mHardwareOutput);
+ mOutputs.removeItem(mHardwareOutput);
+
+ AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor();
+ outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER;
+ mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice,
+ &outputDesc->mSamplingRate,
+ &outputDesc->mFormat,
+ &outputDesc->mChannels,
+ &outputDesc->mLatency,
+ outputDesc->mFlags);
+ if (mHardwareOutput == 0) {
+ LOGE("Failed to reopen hardware output stream, samplingRate: %d, format %d, channels %d",
+ outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels);
+ } else {
+ AudioParameter outputCmd = AudioParameter();
+ outputCmd.addInt(String8("set_id"), 0);
+ mpClientInterface->setParameters(mHardwareOutput, outputCmd.toString());
+ mOutputs.add(mHardwareOutput, outputDesc);
+ }
+ }
+
+
+ mpClientInterface->setParameters(0, String8("test_cmd_policy="));
+ }
+ }
+ return false;
+}
+
+void AudioPolicyManagerGeneric::exit()
+{
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+int AudioPolicyManagerGeneric::testOutputIndex(audio_io_handle_t output)
+{
+ for (int i = 0; i < NUM_TEST_OUTPUTS; i++) {
+ if (output == mTestOutputs[i]) return i;
+ }
+ return 0;
+}
+#endif //AUDIO_POLICY_TEST
+
+// ---
+
+AudioPolicyManagerGeneric::routing_strategy AudioPolicyManagerGeneric::getStrategy(AudioSystem::stream_type stream)
+{
+ // stream to strategy mapping
+ switch (stream) {
+ case AudioSystem::VOICE_CALL:
+ case AudioSystem::BLUETOOTH_SCO:
+ return STRATEGY_PHONE;
+ case AudioSystem::RING:
+ case AudioSystem::NOTIFICATION:
+ case AudioSystem::ALARM:
+ case AudioSystem::ENFORCED_AUDIBLE:
+ return STRATEGY_SONIFICATION;
+ case AudioSystem::DTMF:
+ return STRATEGY_DTMF;
+ default:
+ LOGE("unknown stream type");
+ case AudioSystem::SYSTEM:
+ // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs
+ // while key clicks are played produces a poor result
+ case AudioSystem::TTS:
+ case AudioSystem::MUSIC:
+ return STRATEGY_MEDIA;
+ }
+}
+
+
+float AudioPolicyManagerGeneric::computeVolume(int stream, int index, uint32_t device)
+{
+ float volume = 1.0;
+
+ StreamDescriptor &streamDesc = mStreams[stream];
+
+ // Force max volume if stream cannot be muted
+ if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax;
+
+ int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin);
+ volume = AudioSystem::linearToLog(volInt);
+
+ return volume;
+}
+
+void AudioPolicyManagerGeneric::setStreamMute(int stream, bool on, audio_io_handle_t output)
+{
+ LOGV("setStreamMute() stream %d, mute %d, output %d", stream, on, output);
+
+ StreamDescriptor &streamDesc = mStreams[stream];
+
+ if (on) {
+ if (streamDesc.mMuteCount++ == 0) {
+ if (streamDesc.mCanBeMuted) {
+ mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, 0, output);
+ }
+ }
+ } else {
+ if (streamDesc.mMuteCount == 0) {
+ LOGW("setStreamMute() unmuting non muted stream!");
+ return;
+ }
+ if (--streamDesc.mMuteCount == 0) {
+ uint32_t device = mOutputs.valueFor(output)->mDevice;
+ float volume = computeVolume(stream, streamDesc.mIndexCur, device);
+ mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output);
+ }
+ }
+}
+
+void AudioPolicyManagerGeneric::handleIncallSonification(int stream, bool starting)
+{
+ // if the stream pertains to sonification strategy and we are in call we must
+ // mute the stream if it is low visibility. If it is high visibility, we must play a tone
+ // in the device used for phone strategy and play the tone if the selected device does not
+ // interfere with the device used for phone strategy
+ if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput);
+ LOGV("handleIncallSonification() stream %d starting %d device %x", stream, starting, outputDesc->mDevice);
+ if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) {
+ if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) {
+ LOGV("handleIncallSonification() low visibility");
+ setStreamMute(stream, starting, mHardwareOutput);
+ } else {
+ if (starting) {
+ mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL);
+ } else {
+ mpClientInterface->stopTone();
+ }
+ }
+ }
+ }
+}
+
+
+// --- AudioOutputDescriptor class implementation
+
+AudioPolicyManagerGeneric::AudioOutputDescriptor::AudioOutputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0),
+ mFlags((AudioSystem::output_flags)0), mDevice(0)
+{
+ // clear usage count for all stream types
+ for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) {
+ mRefCount[i] = 0;
+ }
+}
+
+uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::device()
+{
+ return mDevice;
+}
+
+void AudioPolicyManagerGeneric::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
+{
+ if ((delta + (int)mRefCount[stream]) < 0) {
+ LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
+ mRefCount[stream] = 0;
+ return;
+ }
+ mRefCount[stream] += delta;
+ LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
+}
+
+uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::refCount()
+{
+ uint32_t refcount = 0;
+ for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
+ refcount += mRefCount[i];
+ }
+ return refcount;
+}
+
+// --- AudioInputDescriptor class implementation
+
+AudioPolicyManagerGeneric::AudioInputDescriptor::AudioInputDescriptor()
+ : mSamplingRate(0), mFormat(0), mChannels(0),
+ mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0)
+{
+}
+
+}; // namespace android
diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.h b/libs/audioflinger/AudioPolicyManagerGeneric.h
new file mode 100644
index 0000000..d904520
--- /dev/null
+++ b/libs/audioflinger/AudioPolicyManagerGeneric.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <hardware_legacy/AudioPolicyInterface.h>
+#include <utils/threads.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+#define MAX_DEVICE_ADDRESS_LEN 20
+#define NUM_TEST_OUTPUTS 5
+
+class AudioPolicyManagerGeneric: public AudioPolicyInterface
+#ifdef AUDIO_POLICY_TEST
+ , public Thread
+#endif //AUDIO_POLICY_TEST
+{
+
+public:
+ AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface);
+ virtual ~AudioPolicyManagerGeneric();
+
+ // AudioPolicyInterface
+ virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address);
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address);
+ virtual void setPhoneState(int state);
+ virtual void setRingerMode(uint32_t mode, uint32_t mask);
+ virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
+ virtual void setSystemProperty(const char* property, const char* value);
+ virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags);
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual void releaseOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics);
+ // indicates to the audio policy manager that the input starts being used.
+ virtual status_t startInput(audio_io_handle_t input);
+ // indicates to the audio policy manager that the input stops being used.
+ virtual status_t stopInput(audio_io_handle_t input);
+ virtual void releaseInput(audio_io_handle_t input);
+ virtual void initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax);
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+
+private:
+
+ enum routing_strategy {
+ STRATEGY_MEDIA,
+ STRATEGY_PHONE,
+ STRATEGY_SONIFICATION,
+ STRATEGY_DTMF,
+ NUM_STRATEGIES
+ };
+
+ // descriptor for audio outputs. Used to maintain current configuration of each opened audio output
+ // and keep track of the usage of this output by each audio stream type.
+ class AudioOutputDescriptor
+ {
+ public:
+ AudioOutputDescriptor();
+
+
+ uint32_t device();
+ void changeRefCount(AudioSystem::stream_type, int delta);
+ bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; }
+ uint32_t refCount();
+
+ uint32_t mSamplingRate; //
+ uint32_t mFormat; //
+ uint32_t mChannels; // output configuration
+ uint32_t mLatency; //
+ AudioSystem::output_flags mFlags; //
+ uint32_t mDevice; // current device this output is routed to
+ uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output
+ };
+
+ // descriptor for audio inputs. Used to maintain current configuration of each opened audio input
+ // and keep track of the usage of this input.
+ class AudioInputDescriptor
+ {
+ public:
+ AudioInputDescriptor();
+
+ uint32_t mSamplingRate; //
+ uint32_t mFormat; // input configuration
+ uint32_t mChannels; //
+ AudioSystem::audio_in_acoustics mAcoustics; //
+ uint32_t mDevice; // current device this input is routed to
+ uint32_t mRefCount; // number of AudioRecord clients using this output
+ };
+
+ // stream descriptor used for volume control
+ class StreamDescriptor
+ {
+ public:
+ StreamDescriptor()
+ : mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {}
+
+ int mIndexMin; // min volume index
+ int mIndexMax; // max volume index
+ int mIndexCur; // current volume index
+ int mMuteCount; // mute request counter
+ bool mCanBeMuted; // true is the stream can be muted
+ };
+
+ // return the strategy corresponding to a given stream type
+ static routing_strategy getStrategy(AudioSystem::stream_type stream);
+ // return the output handle of an output routed to the specified device, 0 if no output
+ // is routed to the device
+ float computeVolume(int stream, int index, uint32_t device);
+ // Mute or unmute the stream on the specified output
+ void setStreamMute(int stream, bool on, audio_io_handle_t output);
+ // handle special cases for sonification strategy while in call: mute streams or replace by
+ // a special tone in the device used for communication
+ void handleIncallSonification(int stream, bool starting);
+
+
+#ifdef AUDIO_POLICY_TEST
+ virtual bool threadLoop();
+ void exit();
+ int testOutputIndex(audio_io_handle_t output);
+#endif //AUDIO_POLICY_TEST
+
+
+ AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
+ audio_io_handle_t mHardwareOutput; // hardware output handler
+
+ KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; // list ot output descritors
+ KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors
+ uint32_t mAvailableOutputDevices; // bit field of all available output devices
+ uint32_t mAvailableInputDevices; // bit field of all available input devices
+ int mPhoneState; // current phone state
+ uint32_t mRingerMode; // current ringer mode
+ AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration
+
+ StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control
+
+#ifdef AUDIO_POLICY_TEST
+ Mutex mLock;
+ Condition mWaitWorkCV;
+
+ int mCurOutput;
+ bool mDirectOutput;
+ audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS];
+ int mTestInput;
+ uint32_t mTestDevice;
+ uint32_t mTestSamplingRate;
+ uint32_t mTestFormat;
+ uint32_t mTestChannels;
+ uint32_t mTestLatencyMs;
+#endif //AUDIO_POLICY_TEST
+
+};
+
+};
diff --git a/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp
new file mode 100644
index 0000000..5c3cc8e
--- /dev/null
+++ b/libs/audioflinger/AudioPolicyService.cpp
@@ -0,0 +1,771 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioPolicyService"
+//#define LOG_NDEBUG 0
+
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include <sys/time.h>
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <cutils/properties.h>
+#include <binder/IPCThreadState.h>
+#include <utils/String16.h>
+#include <utils/threads.h>
+#include "AudioPolicyService.h"
+#include "AudioPolicyManagerGeneric.h"
+#include <cutils/properties.h>
+#include <dlfcn.h>
+
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+namespace android {
+
+static bool checkPermission() {
+#ifndef HAVE_ANDROID_OS
+ return true;
+#endif
+ if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
+ bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
+ if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
+ return ok;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioPolicyService::AudioPolicyService()
+ : BnAudioPolicyService() , mpPolicyManager(NULL)
+{
+ char value[PROPERTY_VALUE_MAX];
+
+ // start tone playback thread
+ mTonePlaybackThread = new AudioCommandThread();
+ // start audio commands thread
+ mAudioCommandThread = new AudioCommandThread();
+
+#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST)
+ mpPolicyManager = new AudioPolicyManagerGeneric(this);
+ LOGV("build for GENERIC_AUDIO - using generic audio policy");
+#else
+ // if running in emulation - use the emulator driver
+ if (property_get("ro.kernel.qemu", value, 0)) {
+ LOGV("Running in emulation - using generic audio policy");
+ mpPolicyManager = new AudioPolicyManagerGeneric(this);
+ }
+ else {
+ LOGV("Using hardware specific audio policy");
+ mpPolicyManager = createAudioPolicyManager(this);
+ }
+#endif
+
+ // load properties
+ property_get("ro.camera.sound.forced", value, "0");
+ mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value);
+}
+
+AudioPolicyService::~AudioPolicyService()
+{
+ mTonePlaybackThread->exit();
+ mTonePlaybackThread.clear();
+ mAudioCommandThread->exit();
+ mAudioCommandThread.clear();
+
+ if (mpPolicyManager) {
+ delete mpPolicyManager;
+ }
+}
+
+
+status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) {
+ return BAD_VALUE;
+ }
+ if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setDeviceConnectionState() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->setDeviceConnectionState(device, state, device_address);
+}
+
+AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address)
+{
+ if (mpPolicyManager == NULL) {
+ return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ }
+ if (!checkPermission()) {
+ return AudioSystem::DEVICE_STATE_UNAVAILABLE;
+ }
+ return mpPolicyManager->getDeviceConnectionState(device, device_address);
+}
+
+status_t AudioPolicyService::setPhoneState(int state)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (state < 0 || state >= AudioSystem::NUM_MODES) {
+ return BAD_VALUE;
+ }
+
+ LOGV("setPhoneState() tid %d", gettid());
+
+ // TODO: check if it is more appropriate to do it in platform specific policy manager
+ AudioSystem::setMode(state);
+
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->setPhoneState(state);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+
+ mpPolicyManager->setRingerMode(mode, mask);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ return BAD_VALUE;
+ }
+ if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) {
+ return BAD_VALUE;
+ }
+ LOGV("setForceUse() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->setForceUse(usage, config);
+ return NO_ERROR;
+}
+
+AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage)
+{
+ if (mpPolicyManager == NULL) {
+ return AudioSystem::FORCE_NONE;
+ }
+ if (!checkPermission()) {
+ return AudioSystem::FORCE_NONE;
+ }
+ if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) {
+ return AudioSystem::FORCE_NONE;
+ }
+ return mpPolicyManager->getForceUse(usage);
+}
+
+audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+{
+ if (mpPolicyManager == NULL) {
+ return 0;
+ }
+ LOGV("getOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags);
+}
+
+status_t AudioPolicyService::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ LOGV("startOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->startOutput(output, stream);
+}
+
+status_t AudioPolicyService::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ LOGV("stopOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->stopOutput(output, stream);
+}
+
+void AudioPolicyService::releaseOutput(audio_io_handle_t output)
+{
+ if (mpPolicyManager == NULL) {
+ return;
+ }
+ LOGV("releaseOutput() tid %d", gettid());
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->releaseOutput(output);
+}
+
+audio_io_handle_t AudioPolicyService::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+{
+ if (mpPolicyManager == NULL) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics);
+}
+
+status_t AudioPolicyService::startInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->startInput(input);
+}
+
+status_t AudioPolicyService::stopInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpPolicyManager->stopInput(input);
+}
+
+void AudioPolicyService::releaseInput(audio_io_handle_t input)
+{
+ if (mpPolicyManager == NULL) {
+ return;
+ }
+ Mutex::Autolock _l(mLock);
+ mpPolicyManager->releaseInput(input);
+}
+
+status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+ mpPolicyManager->initStreamVolume(stream, indexMin, indexMax);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+
+ return mpPolicyManager->setStreamVolumeIndex(stream, index);
+}
+
+status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+{
+ if (mpPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ if (!checkPermission()) {
+ return PERMISSION_DENIED;
+ }
+ if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) {
+ return BAD_VALUE;
+ }
+ return mpPolicyManager->getStreamVolumeIndex(stream, index);
+}
+
+void AudioPolicyService::binderDied(const wp<IBinder>& who) {
+ LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid());
+}
+
+status_t AudioPolicyService::dump(int fd, const Vector<String16>& args)
+{
+ if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
+ dumpPermissionDenial(fd, args);
+ } else {
+
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::dumpPermissionDenial(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+ snprintf(buffer, SIZE, "Permission Denial: "
+ "can't dump AudioPolicyService from pid=%d, uid=%d\n",
+ IPCThreadState::self()->getCallingPid(),
+ IPCThreadState::self()->getCallingUid());
+ result.append(buffer);
+ write(fd, result.string(), result.size());
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioPolicyService::onTransact(code, data, reply, flags);
+}
+
+
+// ----------------------------------------------------------------------------
+void AudioPolicyService::instantiate() {
+ defaultServiceManager()->addService(
+ String16("media.audio_policy"), new AudioPolicyService());
+}
+
+
+// ----------------------------------------------------------------------------
+// AudioPolicyClientInterface implementation
+// ----------------------------------------------------------------------------
+
+
+audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ AudioSystem::output_flags flags)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openOutput() could not get AudioFlinger");
+ return 0;
+ }
+
+ return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags);
+}
+
+audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openDuplicateOutput() could not get AudioFlinger");
+ return 0;
+ }
+ return af->openDuplicateOutput(output1, output2);
+}
+
+status_t AudioPolicyService::closeOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->closeOutput(output);
+}
+
+
+status_t AudioPolicyService::suspendOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("suspendOutput() could not get AudioFlinger");
+ return PERMISSION_DENIED;
+ }
+
+ return af->suspendOutput(output);
+}
+
+status_t AudioPolicyService::restoreOutput(audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("restoreOutput() could not get AudioFlinger");
+ return PERMISSION_DENIED;
+ }
+
+ return af->restoreOutput(output);
+}
+
+audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ LOGW("openInput() could not get AudioFlinger");
+ return 0;
+ }
+
+ return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics);
+}
+
+status_t AudioPolicyService::closeInput(audio_io_handle_t input)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->closeInput(input);
+}
+
+status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs)
+{
+ return mAudioCommandThread->volumeCommand((int)stream, volume, (int)output, delayMs);
+}
+
+status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output)
+{
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+
+ return af->setStreamOutput(stream, output);
+}
+
+
+void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs)
+{
+ mAudioCommandThread->parametersCommand((int)ioHandle, keyValuePairs, delayMs);
+}
+
+String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys)
+{
+ String8 result = AudioSystem::getParameters(ioHandle, keys);
+ return result;
+}
+
+status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream)
+{
+ mTonePlaybackThread->startToneCommand(tone, stream);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::stopTone()
+{
+ mTonePlaybackThread->stopToneCommand();
+ return NO_ERROR;
+}
+
+
+// ----------- AudioPolicyService::AudioCommandThread implementation ----------
+
+AudioPolicyService::AudioCommandThread::AudioCommandThread()
+ : Thread(false)
+{
+ mpToneGenerator = NULL;
+}
+
+
+AudioPolicyService::AudioCommandThread::~AudioCommandThread()
+{
+ mAudioCommands.clear();
+ if (mpToneGenerator != NULL) delete mpToneGenerator;
+}
+
+void AudioPolicyService::AudioCommandThread::onFirstRef()
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+
+ snprintf(buffer, SIZE, "AudioCommandThread");
+
+ run(buffer, ANDROID_PRIORITY_AUDIO);
+}
+
+bool AudioPolicyService::AudioCommandThread::threadLoop()
+{
+ nsecs_t waitTime = INT64_MAX;
+
+ mLock.lock();
+ while (!exitPending())
+ {
+ while(!mAudioCommands.isEmpty()) {
+ nsecs_t curTime = systemTime();
+ // commands are sorted by increasing time stamp: execute them from index 0 and up
+ if (mAudioCommands[0]->mTime <= curTime) {
+ AudioCommand *command = mAudioCommands[0];
+ mAudioCommands.removeAt(0);
+ switch (command->mCommand) {
+ case START_TONE: {
+ mLock.unlock();
+ ToneData *data = (ToneData *)command->mParam;
+ LOGV("AudioCommandThread() processing start tone %d on stream %d",
+ data->mType, data->mStream);
+ if (mpToneGenerator != NULL)
+ delete mpToneGenerator;
+ mpToneGenerator = new ToneGenerator(data->mStream, 1.0);
+ mpToneGenerator->startTone(data->mType);
+ delete data;
+ mLock.lock();
+ }break;
+ case STOP_TONE: {
+ mLock.unlock();
+ LOGV("AudioCommandThread() processing stop tone");
+ if (mpToneGenerator != NULL) {
+ mpToneGenerator->stopTone();
+ delete mpToneGenerator;
+ mpToneGenerator = NULL;
+ }
+ mLock.lock();
+ }break;
+ case SET_VOLUME: {
+ VolumeData *data = (VolumeData *)command->mParam;
+ LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %d", data->mStream, data->mVolume, data->mIO);
+ command->mStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO);
+ if (command->mWaitStatus) {
+ command->mCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ delete data;
+ }break;
+ case SET_PARAMETERS: {
+ ParametersData *data = (ParametersData *)command->mParam;
+ LOGV("AudioCommandThread() processing set parameters string %s, io %d", data->mKeyValuePairs.string(), data->mIO);
+ command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
+ if (command->mWaitStatus) {
+ command->mCond.signal();
+ mWaitWorkCV.wait(mLock);
+ }
+ delete data;
+ }break;
+ default:
+ LOGW("AudioCommandThread() unknown command %d", command->mCommand);
+ }
+ delete command;
+ waitTime = INT64_MAX;
+ } else {
+ waitTime = mAudioCommands[0]->mTime - curTime;
+ break;
+ }
+ }
+ LOGV("AudioCommandThread() going to sleep");
+ mWaitWorkCV.waitRelative(mLock, waitTime);
+ LOGV("AudioCommandThread() waking up");
+ }
+ mLock.unlock();
+ return false;
+}
+
+void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stream)
+{
+ Mutex::Autolock _l(mLock);
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = START_TONE;
+ ToneData *data = new ToneData();
+ data->mType = type;
+ data->mStream = stream;
+ command->mParam = (void *)data;
+ command->mWaitStatus = false;
+ insertCommand_l(command);
+ LOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream);
+ mWaitWorkCV.signal();
+}
+
+void AudioPolicyService::AudioCommandThread::stopToneCommand()
+{
+ Mutex::Autolock _l(mLock);
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = STOP_TONE;
+ command->mParam = NULL;
+ command->mWaitStatus = false;
+ insertCommand_l(command);
+ LOGV("AudioCommandThread() adding tone stop");
+ mWaitWorkCV.signal();
+}
+
+status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, int output, int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ Mutex::Autolock _l(mLock);
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = SET_VOLUME;
+ VolumeData *data = new VolumeData();
+ data->mStream = stream;
+ data->mVolume = volume;
+ data->mIO = output;
+ command->mParam = data;
+ if (delayMs == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ insertCommand_l(command, delayMs);
+ LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", stream, volume, output);
+ mWaitWorkCV.signal();
+ if (command->mWaitStatus) {
+ command->mCond.wait(mLock);
+ status = command->mStatus;
+ mWaitWorkCV.signal();
+ }
+ return status;
+}
+
+status_t AudioPolicyService::AudioCommandThread::parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs)
+{
+ status_t status = NO_ERROR;
+
+ Mutex::Autolock _l(mLock);
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = SET_PARAMETERS;
+ ParametersData *data = new ParametersData();
+ data->mIO = ioHandle;
+ data->mKeyValuePairs = keyValuePairs;
+ command->mParam = data;
+ if (delayMs == 0) {
+ command->mWaitStatus = true;
+ } else {
+ command->mWaitStatus = false;
+ }
+ insertCommand_l(command, delayMs);
+ LOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", keyValuePairs.string(), ioHandle, delayMs);
+ mWaitWorkCV.signal();
+ if (command->mWaitStatus) {
+ command->mCond.wait(mLock);
+ status = command->mStatus;
+ mWaitWorkCV.signal();
+ }
+ return status;
+}
+
+// insertCommand_l() must be called with mLock held
+void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
+{
+ ssize_t i;
+ Vector <AudioCommand *> removedCommands;
+
+ command->mTime = systemTime() + milliseconds(delayMs);
+
+ // check same pending commands with later time stamps and eliminate them
+ for (i = mAudioCommands.size()-1; i >= 0; i--) {
+ AudioCommand *command2 = mAudioCommands[i];
+ // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands
+ if (command2->mTime <= command->mTime) break;
+ if (command2->mCommand != command->mCommand) continue;
+
+ switch (command->mCommand) {
+ case SET_PARAMETERS: {
+ ParametersData *data = (ParametersData *)command->mParam;
+ ParametersData *data2 = (ParametersData *)command2->mParam;
+ if (data->mIO != data2->mIO) break;
+ LOGV("Comparing parameter command %s to new command %s", data2->mKeyValuePairs.string(), data->mKeyValuePairs.string());
+ AudioParameter param = AudioParameter(data->mKeyValuePairs);
+ AudioParameter param2 = AudioParameter(data2->mKeyValuePairs);
+ for (size_t j = 0; j < param.size(); j++) {
+ String8 key;
+ String8 value;
+ param.getAt(j, key, value);
+ for (size_t k = 0; k < param2.size(); k++) {
+ String8 key2;
+ String8 value2;
+ param2.getAt(k, key2, value2);
+ if (key2 == key) {
+ param2.remove(key2);
+ LOGV("Filtering out parameter %s", key2.string());
+ break;
+ }
+ }
+ }
+ // if all keys have been filtered out, remove the command.
+ // otherwise, update the key value pairs
+ if (param2.size() == 0) {
+ removedCommands.add(command2);
+ } else {
+ data2->mKeyValuePairs = param2.toString();
+ }
+ } break;
+
+ case SET_VOLUME: {
+ VolumeData *data = (VolumeData *)command->mParam;
+ VolumeData *data2 = (VolumeData *)command2->mParam;
+ if (data->mIO != data2->mIO) break;
+ if (data->mStream != data2->mStream) break;
+ LOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream);
+ removedCommands.add(command2);
+ } break;
+ case START_TONE:
+ case STOP_TONE:
+ default:
+ break;
+ }
+ }
+
+ // remove filtered commands
+ for (size_t j = 0; j < removedCommands.size(); j++) {
+ // removed commands always have time stamps greater than current command
+ for (size_t k = i + 1; k < mAudioCommands.size(); k++) {
+ if (mAudioCommands[k] == removedCommands[j]) {
+ LOGV("suppressing command: %d", mAudioCommands[k]->mCommand);
+ mAudioCommands.removeAt(k);
+ break;
+ }
+ }
+ }
+ removedCommands.clear();
+
+ // insert command at the right place according to its time stamp
+ LOGV("inserting command: %d at index %ld, num commands %d", command->mCommand, i+1, mAudioCommands.size());
+ mAudioCommands.insertAt(command, i + 1);
+}
+
+void AudioPolicyService::AudioCommandThread::exit()
+{
+ LOGV("AudioCommandThread::exit");
+ {
+ AutoMutex _l(mLock);
+ requestExit();
+ mWaitWorkCV.signal();
+ }
+ requestExitAndWait();
+}
+
+}; // namespace android
diff --git a/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h
new file mode 100644
index 0000000..56a85e1
--- /dev/null
+++ b/libs/audioflinger/AudioPolicyService.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIOPOLICYSERVICE_H
+#define ANDROID_AUDIOPOLICYSERVICE_H
+
+#include <media/IAudioPolicyService.h>
+#include <hardware_legacy/AudioPolicyInterface.h>
+#include <media/ToneGenerator.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class String8;
+
+// ----------------------------------------------------------------------------
+
+class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, public IBinder::DeathRecipient
+{
+
+public:
+ static void instantiate();
+
+ virtual status_t dump(int fd, const Vector<String16>& args);
+
+ //
+ // BnAudioPolicyService (see AudioPolicyInterface for method descriptions)
+ //
+
+ virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address);
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device,
+ const char *device_address);
+ virtual status_t setPhoneState(int state);
+ virtual status_t setRingerMode(uint32_t mode, uint32_t mask);
+ virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config);
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage);
+ virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT);
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream);
+ virtual void releaseOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t getInput(int inputSource,
+ uint32_t samplingRate = 0,
+ uint32_t format = AudioSystem::FORMAT_DEFAULT,
+ uint32_t channels = 0,
+ AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0);
+ virtual status_t startInput(audio_io_handle_t input);
+ virtual status_t stopInput(audio_io_handle_t input);
+ virtual void releaseInput(audio_io_handle_t input);
+ virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax);
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index);
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index);
+
+ virtual status_t onTransact(
+ uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags);
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ //
+ // AudioPolicyClientInterface
+ //
+ virtual audio_io_handle_t openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ AudioSystem::output_flags flags);
+ virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2);
+ virtual status_t closeOutput(audio_io_handle_t output);
+ virtual status_t suspendOutput(audio_io_handle_t output);
+ virtual status_t restoreOutput(audio_io_handle_t output);
+ virtual audio_io_handle_t openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics);
+ virtual status_t closeInput(audio_io_handle_t input);
+ virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output, int delayMs = 0);
+ virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output);
+ virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys);
+ virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream);
+ virtual status_t stopTone();
+
+private:
+ AudioPolicyService();
+ virtual ~AudioPolicyService();
+
+ // Thread used for tone playback and to send audio config commands to audio flinger
+ // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because startTone()
+ // and stopTone() are normally called with mLock locked and requesting a tone start or stop will cause
+ // calls to AudioPolicyService and an attempt to lock mLock.
+ // For audio config commands, it is necessary because audio flinger requires that the calling process (user)
+ // has permission to modify audio settings.
+ class AudioCommandThread : public Thread {
+ class AudioCommand;
+ public:
+
+ // commands for tone AudioCommand
+ enum {
+ START_TONE,
+ STOP_TONE,
+ SET_VOLUME,
+ SET_PARAMETERS
+ };
+
+ AudioCommandThread ();
+ virtual ~AudioCommandThread();
+
+ // Thread virtuals
+ virtual void onFirstRef();
+ virtual bool threadLoop();
+
+ void exit();
+ void startToneCommand(int type = 0, int stream = 0);
+ void stopToneCommand();
+ status_t volumeCommand(int stream, float volume, int output, int delayMs = 0);
+ status_t parametersCommand(int ioHandle, const String8& keyValuePairs, int delayMs = 0);
+ void insertCommand_l(AudioCommand *command, int delayMs = 0);
+
+ private:
+ // descriptor for requested tone playback event
+ class AudioCommand {
+ public:
+ int mCommand; // START_TONE, STOP_TONE ...
+ nsecs_t mTime; // time stamp
+ Condition mCond; // condition for status return
+ status_t mStatus; // command status
+ bool mWaitStatus; // true if caller is waiting for status
+ void *mParam; // command parameter (ToneData, VolumeData, ParametersData)
+ };
+
+ class ToneData {
+ public:
+ int mType; // tone type (START_TONE only)
+ int mStream; // stream type (START_TONE only)
+ };
+
+ class VolumeData {
+ public:
+ int mStream;
+ float mVolume;
+ int mIO;
+ };
+ class ParametersData {
+ public:
+ int mIO;
+ String8 mKeyValuePairs;
+ };
+
+
+ Mutex mLock;
+ Condition mWaitWorkCV;
+ Vector <AudioCommand *> mAudioCommands; // list of pending commands
+ ToneGenerator *mpToneGenerator; // the tone generator
+ };
+
+ // Internal dump utilities.
+ status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
+
+
+ Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing device
+ // connection stated our routing
+ AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager
+ sp <AudioCommandThread> mAudioCommandThread; // audio commands thread
+ sp <AudioCommandThread> mTonePlaybackThread; // tone playback thread
+};
+
+}; // namespace android
+
+#endif // ANDROID_AUDIOPOLICYSERVICE_H
+
+
+
+
+
+
+
+
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk
new file mode 100644
index 0000000..2df6775
--- /dev/null
+++ b/libs/binder/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# we have the common sources, plus some device-specific stuff
+LOCAL_SRC_FILES:= \
+ Binder.cpp \
+ BpBinder.cpp \
+ IInterface.cpp \
+ IMemory.cpp \
+ IPCThreadState.cpp \
+ IPermissionController.cpp \
+ IServiceManager.cpp \
+ MemoryDealer.cpp \
+ MemoryBase.cpp \
+ MemoryHeapBase.cpp \
+ MemoryHeapPmem.cpp \
+ Parcel.cpp \
+ Permission.cpp \
+ ProcessState.cpp \
+ Static.cpp
+
+LOCAL_LDLIBS += -lpthread
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils \
+ libutils
+
+LOCAL_MODULE:= libbinder
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
new file mode 100644
index 0000000..0dd7622
--- /dev/null
+++ b/libs/binder/Binder.cpp
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#include <binder/Binder.h>
+
+#include <utils/Atomic.h>
+#include <binder/BpBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <stdio.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+IBinder::IBinder()
+ : RefBase()
+{
+}
+
+IBinder::~IBinder()
+{
+}
+
+// ---------------------------------------------------------------------------
+
+sp<IInterface> IBinder::queryLocalInterface(const String16& descriptor)
+{
+ return NULL;
+}
+
+BBinder* IBinder::localBinder()
+{
+ return NULL;
+}
+
+BpBinder* IBinder::remoteBinder()
+{
+ return NULL;
+}
+
+bool IBinder::checkSubclass(const void* /*subclassID*/) const
+{
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+
+class BBinder::Extras
+{
+public:
+ Mutex mLock;
+ BpBinder::ObjectManager mObjects;
+};
+
+// ---------------------------------------------------------------------------
+
+String16 BBinder::sEmptyDescriptor;
+
+BBinder::BBinder()
+ : mExtras(NULL)
+{
+}
+
+bool BBinder::isBinderAlive() const
+{
+ return true;
+}
+
+status_t BBinder::pingBinder()
+{
+ return NO_ERROR;
+}
+
+const String16& BBinder::getInterfaceDescriptor() const
+{
+ LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this);
+ return sEmptyDescriptor;
+}
+
+status_t BBinder::transact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ data.setDataPosition(0);
+
+ status_t err = NO_ERROR;
+ switch (code) {
+ case PING_TRANSACTION:
+ reply->writeInt32(pingBinder());
+ break;
+ default:
+ err = onTransact(code, data, reply, flags);
+ break;
+ }
+
+ if (reply != NULL) {
+ reply->setDataPosition(0);
+ }
+
+ return err;
+}
+
+status_t BBinder::linkToDeath(
+ const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
+{
+ return INVALID_OPERATION;
+}
+
+status_t BBinder::unlinkToDeath(
+ const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient)
+{
+ return INVALID_OPERATION;
+}
+
+status_t BBinder::dump(int fd, const Vector<String16>& args)
+{
+ return NO_ERROR;
+}
+
+void BBinder::attachObject(
+ const void* objectID, void* object, void* cleanupCookie,
+ object_cleanup_func func)
+{
+ Extras* e = mExtras;
+
+ if (!e) {
+ e = new Extras;
+ if (android_atomic_cmpxchg(0, reinterpret_cast<int32_t>(e),
+ reinterpret_cast<volatile int32_t*>(&mExtras)) != 0) {
+ delete e;
+ e = mExtras;
+ }
+ if (e == 0) return; // out of memory
+ }
+
+ AutoMutex _l(e->mLock);
+ e->mObjects.attach(objectID, object, cleanupCookie, func);
+}
+
+void* BBinder::findObject(const void* objectID) const
+{
+ Extras* e = mExtras;
+ if (!e) return NULL;
+
+ AutoMutex _l(e->mLock);
+ return e->mObjects.find(objectID);
+}
+
+void BBinder::detachObject(const void* objectID)
+{
+ Extras* e = mExtras;
+ if (!e) return;
+
+ AutoMutex _l(e->mLock);
+ e->mObjects.detach(objectID);
+}
+
+BBinder* BBinder::localBinder()
+{
+ return this;
+}
+
+BBinder::~BBinder()
+{
+ if (mExtras) delete mExtras;
+}
+
+
+status_t BBinder::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case INTERFACE_TRANSACTION:
+ reply->writeString16(getInterfaceDescriptor());
+ return NO_ERROR;
+
+ case DUMP_TRANSACTION: {
+ int fd = data.readFileDescriptor();
+ int argc = data.readInt32();
+ Vector<String16> args;
+ for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+ args.add(data.readString16());
+ }
+ return dump(fd, args);
+ }
+ default:
+ return UNKNOWN_TRANSACTION;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+enum {
+ // This is used to transfer ownership of the remote binder from
+ // the BpRefBase object holding it (when it is constructed), to the
+ // owner of the BpRefBase object when it first acquires that BpRefBase.
+ kRemoteAcquired = 0x00000001
+};
+
+BpRefBase::BpRefBase(const sp<IBinder>& o)
+ : mRemote(o.get()), mRefs(NULL), mState(0)
+{
+ extendObjectLifetime(OBJECT_LIFETIME_WEAK);
+
+ if (mRemote) {
+ mRemote->incStrong(this); // Removed on first IncStrong().
+ mRefs = mRemote->createWeak(this); // Held for our entire lifetime.
+ }
+}
+
+BpRefBase::~BpRefBase()
+{
+ if (mRemote) {
+ if (!(mState&kRemoteAcquired)) {
+ mRemote->decStrong(this);
+ }
+ mRefs->decWeak(this);
+ }
+}
+
+void BpRefBase::onFirstRef()
+{
+ android_atomic_or(kRemoteAcquired, &mState);
+}
+
+void BpRefBase::onLastStrongRef(const void* id)
+{
+ if (mRemote) {
+ mRemote->decStrong(this);
+ }
+}
+
+bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id)
+{
+ return mRemote ? mRefs->attemptIncStrong(this) : false;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
new file mode 100644
index 0000000..5de87ec
--- /dev/null
+++ b/libs/binder/BpBinder.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "BpBinder"
+//#define LOG_NDEBUG 0
+
+#include <binder/BpBinder.h>
+
+#include <binder/IPCThreadState.h>
+#include <utils/Log.h>
+
+#include <stdio.h>
+
+//#undef LOGV
+//#define LOGV(...) fprintf(stderr, __VA_ARGS__)
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+BpBinder::ObjectManager::ObjectManager()
+{
+}
+
+BpBinder::ObjectManager::~ObjectManager()
+{
+ kill();
+}
+
+void BpBinder::ObjectManager::attach(
+ const void* objectID, void* object, void* cleanupCookie,
+ IBinder::object_cleanup_func func)
+{
+ entry_t e;
+ e.object = object;
+ e.cleanupCookie = cleanupCookie;
+ e.func = func;
+
+ if (mObjects.indexOfKey(objectID) >= 0) {
+ LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use",
+ objectID, this, object);
+ return;
+ }
+
+ mObjects.add(objectID, e);
+}
+
+void* BpBinder::ObjectManager::find(const void* objectID) const
+{
+ const ssize_t i = mObjects.indexOfKey(objectID);
+ if (i < 0) return NULL;
+ return mObjects.valueAt(i).object;
+}
+
+void BpBinder::ObjectManager::detach(const void* objectID)
+{
+ mObjects.removeItem(objectID);
+}
+
+void BpBinder::ObjectManager::kill()
+{
+ const size_t N = mObjects.size();
+ LOGV("Killing %d objects in manager %p", N, this);
+ for (size_t i=0; i<N; i++) {
+ const entry_t& e = mObjects.valueAt(i);
+ if (e.func != NULL) {
+ e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
+ }
+ }
+
+ mObjects.clear();
+}
+
+// ---------------------------------------------------------------------------
+
+BpBinder::BpBinder(int32_t handle)
+ : mHandle(handle)
+ , mAlive(1)
+ , mObitsSent(0)
+ , mObituaries(NULL)
+{
+ LOGV("Creating BpBinder %p handle %d\n", this, mHandle);
+
+ extendObjectLifetime(OBJECT_LIFETIME_WEAK);
+ IPCThreadState::self()->incWeakHandle(handle);
+}
+
+bool BpBinder::isDescriptorCached() const {
+ Mutex::Autolock _l(mLock);
+ return mDescriptorCache.size() ? true : false;
+}
+
+const String16& BpBinder::getInterfaceDescriptor() const
+{
+ if (isDescriptorCached() == false) {
+ Parcel send, reply;
+ // do the IPC without a lock held.
+ status_t err = const_cast<BpBinder*>(this)->transact(
+ INTERFACE_TRANSACTION, send, &reply);
+ if (err == NO_ERROR) {
+ String16 res(reply.readString16());
+ Mutex::Autolock _l(mLock);
+ // mDescriptorCache could have been assigned while the lock was
+ // released.
+ if (mDescriptorCache.size() == 0)
+ mDescriptorCache = res;
+ }
+ }
+
+ // we're returning a reference to a non-static object here. Usually this
+ // is not something smart to do, however, with binder objects it is
+ // (usually) safe because they are reference-counted.
+
+ return mDescriptorCache;
+}
+
+bool BpBinder::isBinderAlive() const
+{
+ return mAlive != 0;
+}
+
+status_t BpBinder::pingBinder()
+{
+ Parcel send;
+ Parcel reply;
+ status_t err = transact(PING_TRANSACTION, send, &reply);
+ if (err != NO_ERROR) return err;
+ if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA;
+ return (status_t)reply.readInt32();
+}
+
+status_t BpBinder::dump(int fd, const Vector<String16>& args)
+{
+ Parcel send;
+ Parcel reply;
+ send.writeFileDescriptor(fd);
+ const size_t numArgs = args.size();
+ send.writeInt32(numArgs);
+ for (size_t i = 0; i < numArgs; i++) {
+ send.writeString16(args[i]);
+ }
+ status_t err = transact(DUMP_TRANSACTION, send, &reply);
+ return err;
+}
+
+status_t BpBinder::transact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ // Once a binder has died, it will never come back to life.
+ if (mAlive) {
+ status_t status = IPCThreadState::self()->transact(
+ mHandle, code, data, reply, flags);
+ if (status == DEAD_OBJECT) mAlive = 0;
+ return status;
+ }
+
+ return DEAD_OBJECT;
+}
+
+status_t BpBinder::linkToDeath(
+ const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
+{
+ Obituary ob;
+ ob.recipient = recipient;
+ ob.cookie = cookie;
+ ob.flags = flags;
+
+ LOG_ALWAYS_FATAL_IF(recipient == NULL,
+ "linkToDeath(): recipient must be non-NULL");
+
+ {
+ AutoMutex _l(mLock);
+
+ if (!mObitsSent) {
+ if (!mObituaries) {
+ mObituaries = new Vector<Obituary>;
+ if (!mObituaries) {
+ return NO_MEMORY;
+ }
+ LOGV("Requesting death notification: %p handle %d\n", this, mHandle);
+ getWeakRefs()->incWeak(this);
+ IPCThreadState* self = IPCThreadState::self();
+ self->requestDeathNotification(mHandle, this);
+ self->flushCommands();
+ }
+ ssize_t res = mObituaries->add(ob);
+ return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
+ }
+ }
+
+ return DEAD_OBJECT;
+}
+
+status_t BpBinder::unlinkToDeath(
+ const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
+ wp<DeathRecipient>* outRecipient)
+{
+ AutoMutex _l(mLock);
+
+ if (mObitsSent) {
+ return DEAD_OBJECT;
+ }
+
+ const size_t N = mObituaries ? mObituaries->size() : 0;
+ for (size_t i=0; i<N; i++) {
+ const Obituary& obit = mObituaries->itemAt(i);
+ if ((obit.recipient == recipient
+ || (recipient == NULL && obit.cookie == cookie))
+ && obit.flags == flags) {
+ const uint32_t allFlags = obit.flags|flags;
+ if (outRecipient != NULL) {
+ *outRecipient = mObituaries->itemAt(i).recipient;
+ }
+ mObituaries->removeAt(i);
+ if (mObituaries->size() == 0) {
+ LOGV("Clearing death notification: %p handle %d\n", this, mHandle);
+ IPCThreadState* self = IPCThreadState::self();
+ self->clearDeathNotification(mHandle, this);
+ self->flushCommands();
+ delete mObituaries;
+ mObituaries = NULL;
+ }
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+void BpBinder::sendObituary()
+{
+ LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
+ this, mHandle, mObitsSent ? "true" : "false");
+
+ mAlive = 0;
+ if (mObitsSent) return;
+
+ mLock.lock();
+ Vector<Obituary>* obits = mObituaries;
+ if(obits != NULL) {
+ LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
+ IPCThreadState* self = IPCThreadState::self();
+ self->clearDeathNotification(mHandle, this);
+ self->flushCommands();
+ mObituaries = NULL;
+ }
+ mObitsSent = 1;
+ mLock.unlock();
+
+ LOGV("Reporting death of proxy %p for %d recipients\n",
+ this, obits ? obits->size() : 0);
+
+ if (obits != NULL) {
+ const size_t N = obits->size();
+ for (size_t i=0; i<N; i++) {
+ reportOneDeath(obits->itemAt(i));
+ }
+
+ delete obits;
+ }
+}
+
+void BpBinder::reportOneDeath(const Obituary& obit)
+{
+ sp<DeathRecipient> recipient = obit.recipient.promote();
+ LOGV("Reporting death to recipient: %p\n", recipient.get());
+ if (recipient == NULL) return;
+
+ recipient->binderDied(this);
+}
+
+
+void BpBinder::attachObject(
+ const void* objectID, void* object, void* cleanupCookie,
+ object_cleanup_func func)
+{
+ AutoMutex _l(mLock);
+ LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
+ mObjects.attach(objectID, object, cleanupCookie, func);
+}
+
+void* BpBinder::findObject(const void* objectID) const
+{
+ AutoMutex _l(mLock);
+ return mObjects.find(objectID);
+}
+
+void BpBinder::detachObject(const void* objectID)
+{
+ AutoMutex _l(mLock);
+ mObjects.detach(objectID);
+}
+
+BpBinder* BpBinder::remoteBinder()
+{
+ return this;
+}
+
+BpBinder::~BpBinder()
+{
+ LOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
+
+ IPCThreadState* ipc = IPCThreadState::self();
+
+ mLock.lock();
+ Vector<Obituary>* obits = mObituaries;
+ if(obits != NULL) {
+ if (ipc) ipc->clearDeathNotification(mHandle, this);
+ mObituaries = NULL;
+ }
+ mLock.unlock();
+
+ if (obits != NULL) {
+ // XXX Should we tell any remaining DeathRecipient
+ // objects that the last strong ref has gone away, so they
+ // are no longer linked?
+ delete obits;
+ }
+
+ if (ipc) {
+ ipc->expungeHandle(mHandle, this);
+ ipc->decWeakHandle(mHandle);
+ }
+}
+
+void BpBinder::onFirstRef()
+{
+ LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (ipc) ipc->incStrongHandle(mHandle);
+}
+
+void BpBinder::onLastStrongRef(const void* id)
+{
+ LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
+ IF_LOGV() {
+ printRefs();
+ }
+ IPCThreadState* ipc = IPCThreadState::self();
+ if (ipc) ipc->decStrongHandle(mHandle);
+}
+
+bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id)
+{
+ LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
+ IPCThreadState* ipc = IPCThreadState::self();
+ return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
new file mode 100644
index 0000000..29acf5d
--- /dev/null
+++ b/libs/binder/IInterface.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#include <binder/IInterface.h>
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+IInterface::IInterface()
+ : RefBase() {
+}
+
+IInterface::~IInterface() {
+}
+
+sp<IBinder> IInterface::asBinder()
+{
+ return this ? onAsBinder() : NULL;
+}
+
+sp<const IBinder> IInterface::asBinder() const
+{
+ return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp
new file mode 100644
index 0000000..6c1d225
--- /dev/null
+++ b/libs/binder/IMemory.cpp
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "IMemory"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <binder/IMemory.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Atomic.h>
+#include <binder/Parcel.h>
+#include <utils/CallStack.h>
+
+#define VERBOSE 0
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class HeapCache : public IBinder::DeathRecipient
+{
+public:
+ HeapCache();
+ virtual ~HeapCache();
+
+ virtual void binderDied(const wp<IBinder>& who);
+
+ sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);
+ void pin_heap(const sp<IBinder>& binder);
+ void free_heap(const sp<IBinder>& binder);
+ sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);
+ void dump_heaps();
+
+private:
+ // For IMemory.cpp
+ struct heap_info_t {
+ sp<IMemoryHeap> heap;
+ int32_t count;
+ };
+
+ void free_heap(const wp<IBinder>& binder);
+
+ Mutex mHeapCacheLock;
+ KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
+};
+
+static sp<HeapCache> gHeapCache = new HeapCache();
+
+/******************************************************************************/
+
+enum {
+ HEAP_ID = IBinder::FIRST_CALL_TRANSACTION
+};
+
+class BpMemoryHeap : public BpInterface<IMemoryHeap>
+{
+public:
+ BpMemoryHeap(const sp<IBinder>& impl);
+ virtual ~BpMemoryHeap();
+
+ virtual int getHeapID() const;
+ virtual void* getBase() const;
+ virtual size_t getSize() const;
+ virtual uint32_t getFlags() const;
+
+private:
+ friend class IMemory;
+ friend class HeapCache;
+
+ // for debugging in this module
+ static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
+ return gHeapCache->find_heap(binder);
+ }
+ static inline void free_heap(const sp<IBinder>& binder) {
+ gHeapCache->free_heap(binder);
+ }
+ static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) {
+ return gHeapCache->get_heap(binder);
+ }
+ static inline void dump_heaps() {
+ gHeapCache->dump_heaps();
+ }
+ void inline pin_heap() const {
+ gHeapCache->pin_heap(const_cast<BpMemoryHeap*>(this)->asBinder());
+ }
+
+ void assertMapped() const;
+ void assertReallyMapped() const;
+ void pinHeap() const;
+
+ mutable volatile int32_t mHeapId;
+ mutable void* mBase;
+ mutable size_t mSize;
+ mutable uint32_t mFlags;
+ mutable bool mRealHeap;
+ mutable Mutex mLock;
+};
+
+// ----------------------------------------------------------------------------
+
+enum {
+ GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION
+};
+
+class BpMemory : public BpInterface<IMemory>
+{
+public:
+ BpMemory(const sp<IBinder>& impl);
+ virtual ~BpMemory();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;
+
+private:
+ mutable sp<IMemoryHeap> mHeap;
+ mutable ssize_t mOffset;
+ mutable size_t mSize;
+};
+
+/******************************************************************************/
+
+void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const
+{
+ sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder);
+ void* const base = realHeap->base();
+ if (base == MAP_FAILED)
+ return 0;
+ return static_cast<char*>(base) + offset;
+}
+
+void* IMemory::pointer() const {
+ ssize_t offset;
+ sp<IMemoryHeap> heap = getMemory(&offset);
+ void* const base = heap!=0 ? heap->base() : MAP_FAILED;
+ if (base == MAP_FAILED)
+ return 0;
+ return static_cast<char*>(base) + offset;
+}
+
+size_t IMemory::size() const {
+ size_t size;
+ getMemory(NULL, &size);
+ return size;
+}
+
+ssize_t IMemory::offset() const {
+ ssize_t offset;
+ getMemory(&offset);
+ return offset;
+}
+
+/******************************************************************************/
+
+BpMemory::BpMemory(const sp<IBinder>& impl)
+ : BpInterface<IMemory>(impl), mOffset(0), mSize(0)
+{
+}
+
+BpMemory::~BpMemory()
+{
+}
+
+sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
+{
+ if (mHeap == 0) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
+ if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
+ sp<IBinder> heap = reply.readStrongBinder();
+ ssize_t o = reply.readInt32();
+ size_t s = reply.readInt32();
+ if (heap != 0) {
+ mHeap = interface_cast<IMemoryHeap>(heap);
+ if (mHeap != 0) {
+ mOffset = o;
+ mSize = s;
+ }
+ }
+ }
+ }
+ if (offset) *offset = mOffset;
+ if (size) *size = mSize;
+ return mHeap;
+}
+
+// ---------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory");
+
+BnMemory::BnMemory() {
+}
+
+BnMemory::~BnMemory() {
+}
+
+status_t BnMemory::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case GET_MEMORY: {
+ CHECK_INTERFACE(IMemory, data, reply);
+ ssize_t offset;
+ size_t size;
+ reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
+ reply->writeInt32(offset);
+ reply->writeInt32(size);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+
+/******************************************************************************/
+
+BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
+ : BpInterface<IMemoryHeap>(impl),
+ mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false)
+{
+}
+
+BpMemoryHeap::~BpMemoryHeap() {
+ if (mHeapId != -1) {
+ close(mHeapId);
+ if (mRealHeap) {
+ // by construction we're the last one
+ if (mBase != MAP_FAILED) {
+ sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
+
+ if (VERBOSE) {
+ LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d",
+ binder.get(), this, mSize, mHeapId);
+ CallStack stack;
+ stack.update();
+ stack.dump("callstack");
+ }
+
+ munmap(mBase, mSize);
+ }
+ } else {
+ // remove from list only if it was mapped before
+ sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
+ free_heap(binder);
+ }
+ }
+}
+
+void BpMemoryHeap::assertMapped() const
+{
+ if (mHeapId == -1) {
+ sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
+ sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
+ heap->assertReallyMapped();
+ if (heap->mBase != MAP_FAILED) {
+ Mutex::Autolock _l(mLock);
+ if (mHeapId == -1) {
+ mBase = heap->mBase;
+ mSize = heap->mSize;
+ android_atomic_write( dup( heap->mHeapId ), &mHeapId );
+ }
+ } else {
+ // something went wrong
+ free_heap(binder);
+ }
+ }
+}
+
+void BpMemoryHeap::assertReallyMapped() const
+{
+ if (mHeapId == -1) {
+
+ // remote call without mLock held, worse case scenario, we end up
+ // calling transact() from multiple threads, but that's not a problem,
+ // only mmap below must be in the critical section.
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
+ status_t err = remote()->transact(HEAP_ID, data, &reply);
+ int parcel_fd = reply.readFileDescriptor();
+ ssize_t size = reply.readInt32();
+ uint32_t flags = reply.readInt32();
+
+ LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)",
+ asBinder().get(), parcel_fd, size, err, strerror(-err));
+
+ int fd = dup( parcel_fd );
+ LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)",
+ parcel_fd, size, err, strerror(errno));
+
+ int access = PROT_READ;
+ if (!(flags & READ_ONLY)) {
+ access |= PROT_WRITE;
+ }
+
+ Mutex::Autolock _l(mLock);
+ if (mHeapId == -1) {
+ mRealHeap = true;
+ mBase = mmap(0, size, access, MAP_SHARED, fd, 0);
+ if (mBase == MAP_FAILED) {
+ LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)",
+ asBinder().get(), size, fd, strerror(errno));
+ close(fd);
+ } else {
+ if (flags & MAP_ONCE) {
+ //LOGD("pinning heap (binder=%p, size=%d, fd=%d",
+ // asBinder().get(), size, fd);
+ pin_heap();
+ }
+ mSize = size;
+ mFlags = flags;
+ android_atomic_write(fd, &mHeapId);
+ }
+ }
+ }
+}
+
+int BpMemoryHeap::getHeapID() const {
+ assertMapped();
+ return mHeapId;
+}
+
+void* BpMemoryHeap::getBase() const {
+ assertMapped();
+ return mBase;
+}
+
+size_t BpMemoryHeap::getSize() const {
+ assertMapped();
+ return mSize;
+}
+
+uint32_t BpMemoryHeap::getFlags() const {
+ assertMapped();
+ return mFlags;
+}
+
+// ---------------------------------------------------------------------------
+
+IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap");
+
+BnMemoryHeap::BnMemoryHeap() {
+}
+
+BnMemoryHeap::~BnMemoryHeap() {
+}
+
+status_t BnMemoryHeap::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case HEAP_ID: {
+ CHECK_INTERFACE(IMemoryHeap, data, reply);
+ reply->writeFileDescriptor(getHeapID());
+ reply->writeInt32(getSize());
+ reply->writeInt32(getFlags());
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+/*****************************************************************************/
+
+HeapCache::HeapCache()
+ : DeathRecipient()
+{
+}
+
+HeapCache::~HeapCache()
+{
+}
+
+void HeapCache::binderDied(const wp<IBinder>& binder)
+{
+ //LOGD("binderDied binder=%p", binder.unsafe_get());
+ free_heap(binder);
+}
+
+sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
+{
+ Mutex::Autolock _l(mHeapCacheLock);
+ ssize_t i = mHeapCache.indexOfKey(binder);
+ if (i>=0) {
+ heap_info_t& info = mHeapCache.editValueAt(i);
+ LOGD_IF(VERBOSE,
+ "found binder=%p, heap=%p, size=%d, fd=%d, count=%d",
+ binder.get(), info.heap.get(),
+ static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
+ static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+ info.count);
+ android_atomic_inc(&info.count);
+ return info.heap;
+ } else {
+ heap_info_t info;
+ info.heap = interface_cast<IMemoryHeap>(binder);
+ info.count = 1;
+ //LOGD("adding binder=%p, heap=%p, count=%d",
+ // binder.get(), info.heap.get(), info.count);
+ mHeapCache.add(binder, info);
+ return info.heap;
+ }
+}
+
+void HeapCache::pin_heap(const sp<IBinder>& binder)
+{
+ Mutex::Autolock _l(mHeapCacheLock);
+ ssize_t i = mHeapCache.indexOfKey(binder);
+ if (i>=0) {
+ heap_info_t& info(mHeapCache.editValueAt(i));
+ android_atomic_inc(&info.count);
+ binder->linkToDeath(this);
+ } else {
+ LOGE("pin_heap binder=%p not found!!!", binder.get());
+ }
+}
+
+void HeapCache::free_heap(const sp<IBinder>& binder) {
+ free_heap( wp<IBinder>(binder) );
+}
+
+void HeapCache::free_heap(const wp<IBinder>& binder)
+{
+ sp<IMemoryHeap> rel;
+ {
+ Mutex::Autolock _l(mHeapCacheLock);
+ ssize_t i = mHeapCache.indexOfKey(binder);
+ if (i>=0) {
+ heap_info_t& info(mHeapCache.editValueAt(i));
+ int32_t c = android_atomic_dec(&info.count);
+ if (c == 1) {
+ LOGD_IF(VERBOSE,
+ "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d",
+ binder.unsafe_get(), info.heap.get(),
+ static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
+ static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
+ info.count);
+ rel = mHeapCache.valueAt(i).heap;
+ mHeapCache.removeItemsAt(i);
+ }
+ } else {
+ LOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
+ }
+ }
+}
+
+sp<IMemoryHeap> HeapCache::get_heap(const sp<IBinder>& binder)
+{
+ sp<IMemoryHeap> realHeap;
+ Mutex::Autolock _l(mHeapCacheLock);
+ ssize_t i = mHeapCache.indexOfKey(binder);
+ if (i>=0) realHeap = mHeapCache.valueAt(i).heap;
+ else realHeap = interface_cast<IMemoryHeap>(binder);
+ return realHeap;
+}
+
+void HeapCache::dump_heaps()
+{
+ Mutex::Autolock _l(mHeapCacheLock);
+ int c = mHeapCache.size();
+ for (int i=0 ; i<c ; i++) {
+ const heap_info_t& info = mHeapCache.valueAt(i);
+ BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
+ LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)",
+ mHeapCache.keyAt(i).unsafe_get(),
+ info.heap.get(), info.count,
+ h->mHeapId, h->mBase, h->mSize);
+ }
+}
+
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
new file mode 100644
index 0000000..c371a23
--- /dev/null
+++ b/libs/binder/IPCThreadState.cpp
@@ -0,0 +1,1025 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#include <binder/IPCThreadState.h>
+
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/TextOutput.h>
+#include <utils/threads.h>
+
+#include <private/binder/binder_module.h>
+#include <private/binder/Static.h>
+
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#ifdef HAVE_PTHREADS
+#include <pthread.h>
+#include <sched.h>
+#include <sys/resource.h>
+#endif
+#ifdef HAVE_WIN32_THREADS
+#include <windows.h>
+#endif
+
+
+#if LOG_NDEBUG
+
+#define IF_LOG_TRANSACTIONS() if (false)
+#define IF_LOG_COMMANDS() if (false)
+#define LOG_REMOTEREFS(...)
+#define IF_LOG_REMOTEREFS() if (false)
+#define LOG_THREADPOOL(...)
+#define LOG_ONEWAY(...)
+
+#else
+
+#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact")
+#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc")
+#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__)
+#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs")
+#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__)
+#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__)
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+static const char* getReturnString(size_t idx);
+static const char* getCommandString(size_t idx);
+static const void* printReturnCommand(TextOutput& out, const void* _cmd);
+static const void* printCommand(TextOutput& out, const void* _cmd);
+
+// This will result in a missing symbol failure if the IF_LOG_COMMANDS()
+// conditionals don't get stripped... but that is probably what we want.
+#if !LOG_NDEBUG
+static const char *kReturnStrings[] = {
+#if 1 /* TODO: error update strings */
+ "unknown",
+#else
+ "BR_OK",
+ "BR_TIMEOUT",
+ "BR_WAKEUP",
+ "BR_TRANSACTION",
+ "BR_REPLY",
+ "BR_ACQUIRE_RESULT",
+ "BR_DEAD_REPLY",
+ "BR_TRANSACTION_COMPLETE",
+ "BR_INCREFS",
+ "BR_ACQUIRE",
+ "BR_RELEASE",
+ "BR_DECREFS",
+ "BR_ATTEMPT_ACQUIRE",
+ "BR_EVENT_OCCURRED",
+ "BR_NOOP",
+ "BR_SPAWN_LOOPER",
+ "BR_FINISHED",
+ "BR_DEAD_BINDER",
+ "BR_CLEAR_DEATH_NOTIFICATION_DONE"
+#endif
+};
+
+static const char *kCommandStrings[] = {
+#if 1 /* TODO: error update strings */
+ "unknown",
+#else
+ "BC_NOOP",
+ "BC_TRANSACTION",
+ "BC_REPLY",
+ "BC_ACQUIRE_RESULT",
+ "BC_FREE_BUFFER",
+ "BC_TRANSACTION_COMPLETE",
+ "BC_INCREFS",
+ "BC_ACQUIRE",
+ "BC_RELEASE",
+ "BC_DECREFS",
+ "BC_INCREFS_DONE",
+ "BC_ACQUIRE_DONE",
+ "BC_ATTEMPT_ACQUIRE",
+ "BC_RETRIEVE_ROOT_OBJECT",
+ "BC_SET_THREAD_ENTRY",
+ "BC_REGISTER_LOOPER",
+ "BC_ENTER_LOOPER",
+ "BC_EXIT_LOOPER",
+ "BC_SYNC",
+ "BC_STOP_PROCESS",
+ "BC_STOP_SELF",
+ "BC_REQUEST_DEATH_NOTIFICATION",
+ "BC_CLEAR_DEATH_NOTIFICATION",
+ "BC_DEAD_BINDER_DONE"
+#endif
+};
+
+static const char* getReturnString(size_t idx)
+{
+ if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
+ return kReturnStrings[idx];
+ else
+ return "unknown";
+}
+
+static const char* getCommandString(size_t idx)
+{
+ if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0]))
+ return kCommandStrings[idx];
+ else
+ return "unknown";
+}
+
+static const void* printBinderTransactionData(TextOutput& out, const void* data)
+{
+ const binder_transaction_data* btd =
+ (const binder_transaction_data*)data;
+ out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl
+ << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl
+ << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
+ << " bytes)" << endl
+ << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
+ << " bytes)" << endl;
+ return btd+1;
+}
+
+static const void* printReturnCommand(TextOutput& out, const void* _cmd)
+{
+ static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
+
+ const int32_t* cmd = (const int32_t*)_cmd;
+ int32_t code = *cmd++;
+ if (code == BR_ERROR) {
+ out << "BR_ERROR: " << (void*)(*cmd++) << endl;
+ return cmd;
+ } else if (code < 0 || code >= N) {
+ out << "Unknown reply: " << code << endl;
+ return cmd;
+ }
+
+ out << kReturnStrings[code];
+ switch (code) {
+ case BR_TRANSACTION:
+ case BR_REPLY: {
+ out << ": " << indent;
+ cmd = (const int32_t *)printBinderTransactionData(out, cmd);
+ out << dedent;
+ } break;
+
+ case BR_ACQUIRE_RESULT: {
+ const int32_t res = *cmd++;
+ out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)");
+ } break;
+
+ case BR_INCREFS:
+ case BR_ACQUIRE:
+ case BR_RELEASE:
+ case BR_DECREFS: {
+ const int32_t b = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
+ } break;
+
+ case BR_ATTEMPT_ACQUIRE: {
+ const int32_t p = *cmd++;
+ const int32_t b = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": target=" << (void*)b << " (cookie " << (void*)c
+ << "), pri=" << p;
+ } break;
+
+ case BR_DEAD_BINDER:
+ case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
+ const int32_t c = *cmd++;
+ out << ": death cookie " << (void*)c;
+ } break;
+ }
+
+ out << endl;
+ return cmd;
+}
+
+static const void* printCommand(TextOutput& out, const void* _cmd)
+{
+ static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
+
+ const int32_t* cmd = (const int32_t*)_cmd;
+ int32_t code = *cmd++;
+ if (code < 0 || code >= N) {
+ out << "Unknown command: " << code << endl;
+ return cmd;
+ }
+
+ out << kCommandStrings[code];
+ switch (code) {
+ case BC_TRANSACTION:
+ case BC_REPLY: {
+ out << ": " << indent;
+ cmd = (const int32_t *)printBinderTransactionData(out, cmd);
+ out << dedent;
+ } break;
+
+ case BC_ACQUIRE_RESULT: {
+ const int32_t res = *cmd++;
+ out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)");
+ } break;
+
+ case BC_FREE_BUFFER: {
+ const int32_t buf = *cmd++;
+ out << ": buffer=" << (void*)buf;
+ } break;
+
+ case BC_INCREFS:
+ case BC_ACQUIRE:
+ case BC_RELEASE:
+ case BC_DECREFS: {
+ const int32_t d = *cmd++;
+ out << ": descriptor=" << (void*)d;
+ } break;
+
+ case BC_INCREFS_DONE:
+ case BC_ACQUIRE_DONE: {
+ const int32_t b = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
+ } break;
+
+ case BC_ATTEMPT_ACQUIRE: {
+ const int32_t p = *cmd++;
+ const int32_t d = *cmd++;
+ out << ": decriptor=" << (void*)d << ", pri=" << p;
+ } break;
+
+ case BC_REQUEST_DEATH_NOTIFICATION:
+ case BC_CLEAR_DEATH_NOTIFICATION: {
+ const int32_t h = *cmd++;
+ const int32_t c = *cmd++;
+ out << ": handle=" << h << " (death cookie " << (void*)c << ")";
+ } break;
+
+ case BC_DEAD_BINDER_DONE: {
+ const int32_t c = *cmd++;
+ out << ": death cookie " << (void*)c;
+ } break;
+ }
+
+ out << endl;
+ return cmd;
+}
+#endif
+
+static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
+static bool gHaveTLS = false;
+static pthread_key_t gTLS = 0;
+static bool gShutdown = false;
+
+IPCThreadState* IPCThreadState::self()
+{
+ if (gHaveTLS) {
+restart:
+ const pthread_key_t k = gTLS;
+ IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
+ if (st) return st;
+ return new IPCThreadState;
+ }
+
+ if (gShutdown) return NULL;
+
+ pthread_mutex_lock(&gTLSMutex);
+ if (!gHaveTLS) {
+ if (pthread_key_create(&gTLS, threadDestructor) != 0) {
+ pthread_mutex_unlock(&gTLSMutex);
+ return NULL;
+ }
+ gHaveTLS = true;
+ }
+ pthread_mutex_unlock(&gTLSMutex);
+ goto restart;
+}
+
+void IPCThreadState::shutdown()
+{
+ gShutdown = true;
+
+ if (gHaveTLS) {
+ // XXX Need to wait for all thread pool threads to exit!
+ IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS);
+ if (st) {
+ delete st;
+ pthread_setspecific(gTLS, NULL);
+ }
+ gHaveTLS = false;
+ }
+}
+
+sp<ProcessState> IPCThreadState::process()
+{
+ return mProcess;
+}
+
+status_t IPCThreadState::clearLastError()
+{
+ const status_t err = mLastError;
+ mLastError = NO_ERROR;
+ return err;
+}
+
+int IPCThreadState::getCallingPid()
+{
+ return mCallingPid;
+}
+
+int IPCThreadState::getCallingUid()
+{
+ return mCallingUid;
+}
+
+int64_t IPCThreadState::clearCallingIdentity()
+{
+ int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
+ clearCaller();
+ return token;
+}
+
+void IPCThreadState::restoreCallingIdentity(int64_t token)
+{
+ mCallingUid = (int)(token>>32);
+ mCallingPid = (int)token;
+}
+
+void IPCThreadState::clearCaller()
+{
+ mCallingPid = getpid();
+ mCallingUid = getuid();
+}
+
+void IPCThreadState::flushCommands()
+{
+ if (mProcess->mDriverFD <= 0)
+ return;
+ talkWithDriver(false);
+}
+
+void IPCThreadState::joinThreadPool(bool isMain)
+{
+ LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
+
+ mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
+
+ status_t result;
+ do {
+ int32_t cmd;
+
+ // When we've cleared the incoming command queue, process any pending derefs
+ if (mIn.dataPosition() >= mIn.dataSize()) {
+ size_t numPending = mPendingWeakDerefs.size();
+ if (numPending > 0) {
+ for (size_t i = 0; i < numPending; i++) {
+ RefBase::weakref_type* refs = mPendingWeakDerefs[i];
+ refs->decWeak(mProcess.get());
+ }
+ mPendingWeakDerefs.clear();
+ }
+
+ numPending = mPendingStrongDerefs.size();
+ if (numPending > 0) {
+ for (size_t i = 0; i < numPending; i++) {
+ BBinder* obj = mPendingStrongDerefs[i];
+ obj->decStrong(mProcess.get());
+ }
+ mPendingStrongDerefs.clear();
+ }
+ }
+
+ // now get the next command to be processed, waiting if necessary
+ result = talkWithDriver();
+ if (result >= NO_ERROR) {
+ size_t IN = mIn.dataAvail();
+ if (IN < sizeof(int32_t)) continue;
+ cmd = mIn.readInt32();
+ IF_LOG_COMMANDS() {
+ alog << "Processing top-level Command: "
+ << getReturnString(cmd) << endl;
+ }
+ result = executeCommand(cmd);
+ }
+
+ // Let this thread exit the thread pool if it is no longer
+ // needed and it is not the main process thread.
+ if(result == TIMED_OUT && !isMain) {
+ break;
+ }
+ } while (result != -ECONNREFUSED && result != -EBADF);
+
+ LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
+ (void*)pthread_self(), getpid(), (void*)result);
+
+ mOut.writeInt32(BC_EXIT_LOOPER);
+ talkWithDriver(false);
+}
+
+void IPCThreadState::stopProcess(bool immediate)
+{
+ //LOGI("**** STOPPING PROCESS");
+ flushCommands();
+ int fd = mProcess->mDriverFD;
+ mProcess->mDriverFD = -1;
+ close(fd);
+ //kill(getpid(), SIGKILL);
+}
+
+status_t IPCThreadState::transact(int32_t handle,
+ uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags)
+{
+ status_t err = data.errorCheck();
+
+ flags |= TF_ACCEPT_FDS;
+
+ IF_LOG_TRANSACTIONS() {
+ TextOutput::Bundle _b(alog);
+ alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
+ << handle << " / code " << TypeCode(code) << ": "
+ << indent << data << dedent << endl;
+ }
+
+ if (err == NO_ERROR) {
+ LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
+ (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
+ err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
+ }
+
+ if (err != NO_ERROR) {
+ if (reply) reply->setError(err);
+ return (mLastError = err);
+ }
+
+ if ((flags & TF_ONE_WAY) == 0) {
+ if (reply) {
+ err = waitForResponse(reply);
+ } else {
+ Parcel fakeReply;
+ err = waitForResponse(&fakeReply);
+ }
+
+ IF_LOG_TRANSACTIONS() {
+ TextOutput::Bundle _b(alog);
+ alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
+ << handle << ": ";
+ if (reply) alog << indent << *reply << dedent << endl;
+ else alog << "(none requested)" << endl;
+ }
+ } else {
+ err = waitForResponse(NULL, NULL);
+ }
+
+ return err;
+}
+
+void IPCThreadState::incStrongHandle(int32_t handle)
+{
+ LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
+ mOut.writeInt32(BC_ACQUIRE);
+ mOut.writeInt32(handle);
+}
+
+void IPCThreadState::decStrongHandle(int32_t handle)
+{
+ LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
+ mOut.writeInt32(BC_RELEASE);
+ mOut.writeInt32(handle);
+}
+
+void IPCThreadState::incWeakHandle(int32_t handle)
+{
+ LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
+ mOut.writeInt32(BC_INCREFS);
+ mOut.writeInt32(handle);
+}
+
+void IPCThreadState::decWeakHandle(int32_t handle)
+{
+ LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
+ mOut.writeInt32(BC_DECREFS);
+ mOut.writeInt32(handle);
+}
+
+status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
+{
+ mOut.writeInt32(BC_ATTEMPT_ACQUIRE);
+ mOut.writeInt32(0); // xxx was thread priority
+ mOut.writeInt32(handle);
+ status_t result = UNKNOWN_ERROR;
+
+ waitForResponse(NULL, &result);
+
+#if LOG_REFCOUNTS
+ printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
+ handle, result == NO_ERROR ? "SUCCESS" : "FAILURE");
+#endif
+
+ return result;
+}
+
+void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder)
+{
+#if LOG_REFCOUNTS
+ printf("IPCThreadState::expungeHandle(%ld)\n", handle);
+#endif
+ self()->mProcess->expungeHandle(handle, binder);
+}
+
+status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
+{
+ mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writeInt32((int32_t)proxy);
+ return NO_ERROR;
+}
+
+status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
+{
+ mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);
+ mOut.writeInt32((int32_t)handle);
+ mOut.writeInt32((int32_t)proxy);
+ return NO_ERROR;
+}
+
+IPCThreadState::IPCThreadState()
+ : mProcess(ProcessState::self())
+{
+ pthread_setspecific(gTLS, this);
+ clearCaller();
+ mIn.setDataCapacity(256);
+ mOut.setDataCapacity(256);
+}
+
+IPCThreadState::~IPCThreadState()
+{
+}
+
+status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
+{
+ status_t err;
+ status_t statusBuffer;
+ err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
+ if (err < NO_ERROR) return err;
+
+ return waitForResponse(NULL, NULL);
+}
+
+status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
+{
+ int32_t cmd;
+ int32_t err;
+
+ while (1) {
+ if ((err=talkWithDriver()) < NO_ERROR) break;
+ err = mIn.errorCheck();
+ if (err < NO_ERROR) break;
+ if (mIn.dataAvail() == 0) continue;
+
+ cmd = mIn.readInt32();
+
+ IF_LOG_COMMANDS() {
+ alog << "Processing waitForResponse Command: "
+ << getReturnString(cmd) << endl;
+ }
+
+ switch (cmd) {
+ case BR_TRANSACTION_COMPLETE:
+ if (!reply && !acquireResult) goto finish;
+ break;
+
+ case BR_DEAD_REPLY:
+ err = DEAD_OBJECT;
+ goto finish;
+
+ case BR_FAILED_REPLY:
+ err = FAILED_TRANSACTION;
+ goto finish;
+
+ case BR_ACQUIRE_RESULT:
+ {
+ LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
+ const int32_t result = mIn.readInt32();
+ if (!acquireResult) continue;
+ *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
+ }
+ goto finish;
+
+ case BR_REPLY:
+ {
+ binder_transaction_data tr;
+ err = mIn.read(&tr, sizeof(tr));
+ LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
+ if (err != NO_ERROR) goto finish;
+
+ if (reply) {
+ if ((tr.flags & TF_STATUS_CODE) == 0) {
+ reply->ipcSetDataReference(
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t),
+ freeBuffer, this);
+ } else {
+ err = *static_cast<const status_t*>(tr.data.ptr.buffer);
+ freeBuffer(NULL,
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t), this);
+ }
+ } else {
+ freeBuffer(NULL,
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t), this);
+ continue;
+ }
+ }
+ goto finish;
+
+ default:
+ err = executeCommand(cmd);
+ if (err != NO_ERROR) goto finish;
+ break;
+ }
+ }
+
+finish:
+ if (err != NO_ERROR) {
+ if (acquireResult) *acquireResult = err;
+ if (reply) reply->setError(err);
+ mLastError = err;
+ }
+
+ return err;
+}
+
+status_t IPCThreadState::talkWithDriver(bool doReceive)
+{
+ LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened");
+
+ binder_write_read bwr;
+
+ // Is the read buffer empty?
+ const bool needRead = mIn.dataPosition() >= mIn.dataSize();
+
+ // We don't want to write anything if we are still reading
+ // from data left in the input buffer and the caller
+ // has requested to read the next data.
+ const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
+
+ bwr.write_size = outAvail;
+ bwr.write_buffer = (long unsigned int)mOut.data();
+
+ // This is what we'll read.
+ if (doReceive && needRead) {
+ bwr.read_size = mIn.dataCapacity();
+ bwr.read_buffer = (long unsigned int)mIn.data();
+ } else {
+ bwr.read_size = 0;
+ }
+
+ IF_LOG_COMMANDS() {
+ TextOutput::Bundle _b(alog);
+ if (outAvail != 0) {
+ alog << "Sending commands to driver: " << indent;
+ const void* cmds = (const void*)bwr.write_buffer;
+ const void* end = ((const uint8_t*)cmds)+bwr.write_size;
+ alog << HexDump(cmds, bwr.write_size) << endl;
+ while (cmds < end) cmds = printCommand(alog, cmds);
+ alog << dedent;
+ }
+ alog << "Size of receive buffer: " << bwr.read_size
+ << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
+ }
+
+ // Return immediately if there is nothing to do.
+ if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
+
+ bwr.write_consumed = 0;
+ bwr.read_consumed = 0;
+ status_t err;
+ do {
+ IF_LOG_COMMANDS() {
+ alog << "About to read/write, write size = " << mOut.dataSize() << endl;
+ }
+#if defined(HAVE_ANDROID_OS)
+ if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
+ err = NO_ERROR;
+ else
+ err = -errno;
+#else
+ err = INVALID_OPERATION;
+#endif
+ IF_LOG_COMMANDS() {
+ alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
+ }
+ } while (err == -EINTR);
+
+ IF_LOG_COMMANDS() {
+ alog << "Our err: " << (void*)err << ", write consumed: "
+ << bwr.write_consumed << " (of " << mOut.dataSize()
+ << "), read consumed: " << bwr.read_consumed << endl;
+ }
+
+ if (err >= NO_ERROR) {
+ if (bwr.write_consumed > 0) {
+ if (bwr.write_consumed < (ssize_t)mOut.dataSize())
+ mOut.remove(0, bwr.write_consumed);
+ else
+ mOut.setDataSize(0);
+ }
+ if (bwr.read_consumed > 0) {
+ mIn.setDataSize(bwr.read_consumed);
+ mIn.setDataPosition(0);
+ }
+ IF_LOG_COMMANDS() {
+ TextOutput::Bundle _b(alog);
+ alog << "Remaining data size: " << mOut.dataSize() << endl;
+ alog << "Received commands from driver: " << indent;
+ const void* cmds = mIn.data();
+ const void* end = mIn.data() + mIn.dataSize();
+ alog << HexDump(cmds, mIn.dataSize()) << endl;
+ while (cmds < end) cmds = printReturnCommand(alog, cmds);
+ alog << dedent;
+ }
+ return NO_ERROR;
+ }
+
+ return err;
+}
+
+status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
+ int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
+{
+ binder_transaction_data tr;
+
+ tr.target.handle = handle;
+ tr.code = code;
+ tr.flags = binderFlags;
+
+ const status_t err = data.errorCheck();
+ if (err == NO_ERROR) {
+ tr.data_size = data.ipcDataSize();
+ tr.data.ptr.buffer = data.ipcData();
+ tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
+ tr.data.ptr.offsets = data.ipcObjects();
+ } else if (statusBuffer) {
+ tr.flags |= TF_STATUS_CODE;
+ *statusBuffer = err;
+ tr.data_size = sizeof(status_t);
+ tr.data.ptr.buffer = statusBuffer;
+ tr.offsets_size = 0;
+ tr.data.ptr.offsets = NULL;
+ } else {
+ return (mLastError = err);
+ }
+
+ mOut.writeInt32(cmd);
+ mOut.write(&tr, sizeof(tr));
+
+ return NO_ERROR;
+}
+
+sp<BBinder> the_context_object;
+
+void setTheContextObject(sp<BBinder> obj)
+{
+ the_context_object = obj;
+}
+
+status_t IPCThreadState::executeCommand(int32_t cmd)
+{
+ BBinder* obj;
+ RefBase::weakref_type* refs;
+ status_t result = NO_ERROR;
+
+ switch (cmd) {
+ case BR_ERROR:
+ result = mIn.readInt32();
+ break;
+
+ case BR_OK:
+ break;
+
+ case BR_ACQUIRE:
+ refs = (RefBase::weakref_type*)mIn.readInt32();
+ obj = (BBinder*)mIn.readInt32();
+ LOG_ASSERT(refs->refBase() == obj,
+ "BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
+ refs, obj, refs->refBase());
+ obj->incStrong(mProcess.get());
+ IF_LOG_REMOTEREFS() {
+ LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
+ obj->printRefs();
+ }
+ mOut.writeInt32(BC_ACQUIRE_DONE);
+ mOut.writeInt32((int32_t)refs);
+ mOut.writeInt32((int32_t)obj);
+ break;
+
+ case BR_RELEASE:
+ refs = (RefBase::weakref_type*)mIn.readInt32();
+ obj = (BBinder*)mIn.readInt32();
+ LOG_ASSERT(refs->refBase() == obj,
+ "BR_RELEASE: object %p does not match cookie %p (expected %p)",
+ refs, obj, refs->refBase());
+ IF_LOG_REMOTEREFS() {
+ LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj);
+ obj->printRefs();
+ }
+ mPendingStrongDerefs.push(obj);
+ break;
+
+ case BR_INCREFS:
+ refs = (RefBase::weakref_type*)mIn.readInt32();
+ obj = (BBinder*)mIn.readInt32();
+ refs->incWeak(mProcess.get());
+ mOut.writeInt32(BC_INCREFS_DONE);
+ mOut.writeInt32((int32_t)refs);
+ mOut.writeInt32((int32_t)obj);
+ break;
+
+ case BR_DECREFS:
+ refs = (RefBase::weakref_type*)mIn.readInt32();
+ obj = (BBinder*)mIn.readInt32();
+ // NOTE: This assertion is not valid, because the object may no
+ // longer exist (thus the (BBinder*)cast above resulting in a different
+ // memory address).
+ //LOG_ASSERT(refs->refBase() == obj,
+ // "BR_DECREFS: object %p does not match cookie %p (expected %p)",
+ // refs, obj, refs->refBase());
+ mPendingWeakDerefs.push(refs);
+ break;
+
+ case BR_ATTEMPT_ACQUIRE:
+ refs = (RefBase::weakref_type*)mIn.readInt32();
+ obj = (BBinder*)mIn.readInt32();
+
+ {
+ const bool success = refs->attemptIncStrong(mProcess.get());
+ LOG_ASSERT(success && refs->refBase() == obj,
+ "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)",
+ refs, obj, refs->refBase());
+
+ mOut.writeInt32(BC_ACQUIRE_RESULT);
+ mOut.writeInt32((int32_t)success);
+ }
+ break;
+
+ case BR_TRANSACTION:
+ {
+ binder_transaction_data tr;
+ result = mIn.read(&tr, sizeof(tr));
+ LOG_ASSERT(result == NO_ERROR,
+ "Not enough command data for brTRANSACTION");
+ if (result != NO_ERROR) break;
+
+ Parcel buffer;
+ buffer.ipcSetDataReference(
+ reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
+ tr.data_size,
+ reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
+ tr.offsets_size/sizeof(size_t), freeBuffer, this);
+
+ const pid_t origPid = mCallingPid;
+ const uid_t origUid = mCallingUid;
+
+ mCallingPid = tr.sender_pid;
+ mCallingUid = tr.sender_euid;
+
+ //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
+
+ Parcel reply;
+ IF_LOG_TRANSACTIONS() {
+ TextOutput::Bundle _b(alog);
+ alog << "BR_TRANSACTION thr " << (void*)pthread_self()
+ << " / obj " << tr.target.ptr << " / code "
+ << TypeCode(tr.code) << ": " << indent << buffer
+ << dedent << endl
+ << "Data addr = "
+ << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
+ << ", offsets addr="
+ << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
+ }
+ if (tr.target.ptr) {
+ sp<BBinder> b((BBinder*)tr.cookie);
+ const status_t error = b->transact(tr.code, buffer, &reply, 0);
+ if (error < NO_ERROR) reply.setError(error);
+
+ } else {
+ const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);
+ if (error < NO_ERROR) reply.setError(error);
+ }
+
+ //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
+ // mCallingPid, origPid, origUid);
+
+ if ((tr.flags & TF_ONE_WAY) == 0) {
+ LOG_ONEWAY("Sending reply to %d!", mCallingPid);
+ sendReply(reply, 0);
+ } else {
+ LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
+ }
+
+ mCallingPid = origPid;
+ mCallingUid = origUid;
+
+ IF_LOG_TRANSACTIONS() {
+ TextOutput::Bundle _b(alog);
+ alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
+ << tr.target.ptr << ": " << indent << reply << dedent << endl;
+ }
+
+ }
+ break;
+
+ case BR_DEAD_BINDER:
+ {
+ BpBinder *proxy = (BpBinder*)mIn.readInt32();
+ proxy->sendObituary();
+ mOut.writeInt32(BC_DEAD_BINDER_DONE);
+ mOut.writeInt32((int32_t)proxy);
+ } break;
+
+ case BR_CLEAR_DEATH_NOTIFICATION_DONE:
+ {
+ BpBinder *proxy = (BpBinder*)mIn.readInt32();
+ proxy->getWeakRefs()->decWeak(proxy);
+ } break;
+
+ case BR_FINISHED:
+ result = TIMED_OUT;
+ break;
+
+ case BR_NOOP:
+ break;
+
+ case BR_SPAWN_LOOPER:
+ mProcess->spawnPooledThread(false);
+ break;
+
+ default:
+ printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
+ result = UNKNOWN_ERROR;
+ break;
+ }
+
+ if (result != NO_ERROR) {
+ mLastError = result;
+ }
+
+ return result;
+}
+
+void IPCThreadState::threadDestructor(void *st)
+{
+ IPCThreadState* const self = static_cast<IPCThreadState*>(st);
+ if (self) {
+ self->flushCommands();
+#if defined(HAVE_ANDROID_OS)
+ ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
+#endif
+ delete self;
+ }
+}
+
+
+void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize,
+ const size_t* objects, size_t objectsSize,
+ void* cookie)
+{
+ //LOGI("Freeing parcel %p", &parcel);
+ IF_LOG_COMMANDS() {
+ alog << "Writing BC_FREE_BUFFER for " << data << endl;
+ }
+ LOG_ASSERT(data != NULL, "Called with NULL data");
+ if (parcel != NULL) parcel->closeFileDescriptors();
+ IPCThreadState* state = self();
+ state->mOut.writeInt32(BC_FREE_BUFFER);
+ state->mOut.writeInt32((int32_t)data);
+}
+
+}; // namespace android
diff --git a/libs/binder/IPermissionController.cpp b/libs/binder/IPermissionController.cpp
new file mode 100644
index 0000000..bff4c9b
--- /dev/null
+++ b/libs/binder/IPermissionController.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "PermissionController"
+
+#include <binder/IPermissionController.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+#include <private/binder/Static.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------
+
+class BpPermissionController : public BpInterface<IPermissionController>
+{
+public:
+ BpPermissionController(const sp<IBinder>& impl)
+ : BpInterface<IPermissionController>(impl)
+ {
+ }
+
+ virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor());
+ data.writeString16(permission);
+ data.writeInt32(pid);
+ data.writeInt32(uid);
+ remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readInt32() != 0) return 0;
+ return reply.readInt32() != 0;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController");
+
+// ----------------------------------------------------------------------
+
+status_t BnPermissionController::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ //printf("PermissionController received: "); data.print();
+ switch(code) {
+ case CHECK_PERMISSION_TRANSACTION: {
+ CHECK_INTERFACE(IPermissionController, data, reply);
+ String16 permission = data.readString16();
+ int32_t pid = data.readInt32();
+ int32_t uid = data.readInt32();
+ bool res = checkPermission(permission, pid, uid);
+ // write exception
+ reply->writeInt32(0);
+ reply->writeInt32(res ? 1 : 0);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
+
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
new file mode 100644
index 0000000..0cf4158
--- /dev/null
+++ b/libs/binder/IServiceManager.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "ServiceManager"
+
+#include <binder/IServiceManager.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+#include <utils/SystemClock.h>
+
+#include <private/binder/Static.h>
+
+#include <unistd.h>
+
+namespace android {
+
+sp<IServiceManager> defaultServiceManager()
+{
+ if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
+
+ {
+ AutoMutex _l(gDefaultServiceManagerLock);
+ if (gDefaultServiceManager == NULL) {
+ gDefaultServiceManager = interface_cast<IServiceManager>(
+ ProcessState::self()->getContextObject(NULL));
+ }
+ }
+
+ return gDefaultServiceManager;
+}
+
+bool checkCallingPermission(const String16& permission)
+{
+ return checkCallingPermission(permission, NULL, NULL);
+}
+
+static String16 _permission("permission");
+
+
+bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid)
+{
+ IPCThreadState* ipcState = IPCThreadState::self();
+ pid_t pid = ipcState->getCallingPid();
+ uid_t uid = ipcState->getCallingUid();
+ if (outPid) *outPid = pid;
+ if (outUid) *outUid = uid;
+ return checkPermission(permission, pid, uid);
+}
+
+bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
+{
+ sp<IPermissionController> pc;
+ gDefaultServiceManagerLock.lock();
+ pc = gPermissionController;
+ gDefaultServiceManagerLock.unlock();
+
+ int64_t startTime = 0;
+
+ while (true) {
+ if (pc != NULL) {
+ bool res = pc->checkPermission(permission, pid, uid);
+ if (res) {
+ if (startTime != 0) {
+ LOGI("Check passed after %d seconds for %s from uid=%d pid=%d",
+ (int)((uptimeMillis()-startTime)/1000),
+ String8(permission).string(), uid, pid);
+ }
+ return res;
+ }
+
+ // Is this a permission failure, or did the controller go away?
+ if (pc->asBinder()->isBinderAlive()) {
+ LOGW("Permission failure: %s from uid=%d pid=%d",
+ String8(permission).string(), uid, pid);
+ return false;
+ }
+
+ // Object is dead!
+ gDefaultServiceManagerLock.lock();
+ if (gPermissionController == pc) {
+ gPermissionController = NULL;
+ }
+ gDefaultServiceManagerLock.unlock();
+ }
+
+ // Need to retrieve the permission controller.
+ sp<IBinder> binder = defaultServiceManager()->checkService(_permission);
+ if (binder == NULL) {
+ // Wait for the permission controller to come back...
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ LOGI("Waiting to check permission %s from uid=%d pid=%d",
+ String8(permission).string(), uid, pid);
+ }
+ sleep(1);
+ } else {
+ pc = interface_cast<IPermissionController>(binder);
+ // Install the new permission controller, and try again.
+ gDefaultServiceManagerLock.lock();
+ gPermissionController = pc;
+ gDefaultServiceManagerLock.unlock();
+ }
+ }
+}
+
+// ----------------------------------------------------------------------
+
+class BpServiceManager : public BpInterface<IServiceManager>
+{
+public:
+ BpServiceManager(const sp<IBinder>& impl)
+ : BpInterface<IServiceManager>(impl)
+ {
+ }
+
+ virtual sp<IBinder> getService(const String16& name) const
+ {
+ unsigned n;
+ for (n = 0; n < 5; n++){
+ sp<IBinder> svc = checkService(name);
+ if (svc != NULL) return svc;
+ LOGI("Waiting for sevice %s...\n", String8(name).string());
+ sleep(1);
+ }
+ return NULL;
+ }
+
+ virtual sp<IBinder> checkService( const String16& name) const
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
+ data.writeString16(name);
+ remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
+ return reply.readStrongBinder();
+ }
+
+ virtual status_t addService(const String16& name, const sp<IBinder>& service)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
+ data.writeString16(name);
+ data.writeStrongBinder(service);
+ status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
+ return err == NO_ERROR ? reply.readInt32() : err;
+ }
+
+ virtual Vector<String16> listServices()
+ {
+ Vector<String16> res;
+ int n = 0;
+
+ for (;;) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
+ data.writeInt32(n++);
+ status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
+ if (err != NO_ERROR)
+ break;
+ res.add(reply.readString16());
+ }
+ return res;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
+
+// ----------------------------------------------------------------------
+
+status_t BnServiceManager::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ //printf("ServiceManager received: "); data.print();
+ switch(code) {
+ case GET_SERVICE_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ String16 which = data.readString16();
+ sp<IBinder> b = const_cast<BnServiceManager*>(this)->getService(which);
+ reply->writeStrongBinder(b);
+ return NO_ERROR;
+ } break;
+ case CHECK_SERVICE_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ String16 which = data.readString16();
+ sp<IBinder> b = const_cast<BnServiceManager*>(this)->checkService(which);
+ reply->writeStrongBinder(b);
+ return NO_ERROR;
+ } break;
+ case ADD_SERVICE_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ String16 which = data.readString16();
+ sp<IBinder> b = data.readStrongBinder();
+ status_t err = addService(which, b);
+ reply->writeInt32(err);
+ return NO_ERROR;
+ } break;
+ case LIST_SERVICES_TRANSACTION: {
+ CHECK_INTERFACE(IServiceManager, data, reply);
+ Vector<String16> list = listServices();
+ const size_t N = list.size();
+ reply->writeInt32(N);
+ for (size_t i=0; i<N; i++) {
+ reply->writeString16(list[i]);
+ }
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
+
diff --git a/libs/binder/MemoryBase.cpp b/libs/binder/MemoryBase.cpp
new file mode 100644
index 0000000..033066b
--- /dev/null
+++ b/libs/binder/MemoryBase.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <binder/MemoryBase.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
+ ssize_t offset, size_t size)
+ : mSize(size), mOffset(offset), mHeap(heap)
+{
+}
+
+sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
+{
+ if (offset) *offset = mOffset;
+ if (size) *size = mSize;
+ return mHeap;
+}
+
+MemoryBase::~MemoryBase()
+{
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp
new file mode 100644
index 0000000..d5ffe7f
--- /dev/null
+++ b/libs/binder/MemoryDealer.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MemoryDealer"
+
+#include <binder/MemoryDealer.h>
+
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <binder/MemoryBase.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/file.h>
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+HeapInterface::HeapInterface() { }
+HeapInterface::~HeapInterface() { }
+
+// ----------------------------------------------------------------------------
+
+AllocatorInterface::AllocatorInterface() { }
+AllocatorInterface::~AllocatorInterface() { }
+
+// ----------------------------------------------------------------------------
+
+class SimpleMemory : public MemoryBase {
+public:
+ SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
+ virtual ~SimpleMemory();
+};
+
+
+// ----------------------------------------------------------------------------
+
+MemoryDealer::Allocation::Allocation(
+ const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
+ const sp<IMemory>& memory)
+ : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory)
+{
+}
+
+MemoryDealer::Allocation::~Allocation()
+{
+ if (mSize) {
+ /* NOTE: it's VERY important to not free allocations of size 0 because
+ * they're special as they don't have any record in the allocator
+ * and could alias some real allocation (their offset is zero). */
+ mDealer->deallocate(mOffset);
+ }
+}
+
+sp<IMemoryHeap> MemoryDealer::Allocation::getMemory(
+ ssize_t* offset, size_t* size) const
+{
+ return mMemory->getMemory(offset, size);
+}
+
+// ----------------------------------------------------------------------------
+
+MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name)
+ : mHeap(new SharedHeap(size, flags, name)),
+ mAllocator(new SimpleBestFitAllocator(size))
+{
+}
+
+MemoryDealer::MemoryDealer(const sp<HeapInterface>& heap)
+ : mHeap(heap),
+ mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
+{
+}
+
+MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
+ const sp<AllocatorInterface>& allocator)
+ : mHeap(heap), mAllocator(allocator)
+{
+}
+
+MemoryDealer::~MemoryDealer()
+{
+}
+
+sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
+{
+ sp<IMemory> memory;
+ const ssize_t offset = allocator()->allocate(size, flags);
+ if (offset >= 0) {
+ sp<IMemory> new_memory = heap()->mapMemory(offset, size);
+ if (new_memory != 0) {
+ memory = new Allocation(this, offset, size, new_memory);
+ } else {
+ LOGE("couldn't map [%8lx, %u]", offset, size);
+ if (size) {
+ /* NOTE: it's VERY important to not free allocations of size 0
+ * because they're special as they don't have any record in the
+ * allocator and could alias some real allocation
+ * (their offset is zero). */
+ allocator()->deallocate(offset);
+ }
+ }
+ }
+ return memory;
+}
+
+void MemoryDealer::deallocate(size_t offset)
+{
+ allocator()->deallocate(offset);
+}
+
+void MemoryDealer::dump(const char* what, uint32_t flags) const
+{
+ allocator()->dump(what, flags);
+}
+
+const sp<HeapInterface>& MemoryDealer::heap() const {
+ return mHeap;
+}
+
+const sp<AllocatorInterface>& MemoryDealer::allocator() const {
+ return mAllocator;
+}
+
+// ----------------------------------------------------------------------------
+
+// align all the memory blocks on a cache-line boundary
+const int SimpleBestFitAllocator::kMemoryAlign = 32;
+
+SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size)
+{
+ size_t pagesize = getpagesize();
+ mHeapSize = ((size + pagesize-1) & ~(pagesize-1));
+
+ chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign);
+ mList.insertHead(node);
+}
+
+SimpleBestFitAllocator::~SimpleBestFitAllocator()
+{
+ while(!mList.isEmpty()) {
+ delete mList.remove(mList.head());
+ }
+}
+
+size_t SimpleBestFitAllocator::size() const
+{
+ return mHeapSize;
+}
+
+size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
+{
+ Mutex::Autolock _l(mLock);
+ ssize_t offset = alloc(size, flags);
+ return offset;
+}
+
+status_t SimpleBestFitAllocator::deallocate(size_t offset)
+{
+ Mutex::Autolock _l(mLock);
+ chunk_t const * const freed = dealloc(offset);
+ if (freed) {
+ return NO_ERROR;
+ }
+ return NAME_NOT_FOUND;
+}
+
+ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags)
+{
+ if (size == 0) {
+ return 0;
+ }
+ size = (size + kMemoryAlign-1) / kMemoryAlign;
+ chunk_t* free_chunk = 0;
+ chunk_t* cur = mList.head();
+
+ size_t pagesize = getpagesize();
+ while (cur) {
+ int extra = 0;
+ if (flags & PAGE_ALIGNED)
+ extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ;
+
+ // best fit
+ if (cur->free && (cur->size >= (size+extra))) {
+ if ((!free_chunk) || (cur->size < free_chunk->size)) {
+ free_chunk = cur;
+ }
+ if (cur->size == size) {
+ break;
+ }
+ }
+ cur = cur->next;
+ }
+
+ if (free_chunk) {
+ const size_t free_size = free_chunk->size;
+ free_chunk->free = 0;
+ free_chunk->size = size;
+ if (free_size > size) {
+ int extra = 0;
+ if (flags & PAGE_ALIGNED)
+ extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ;
+ if (extra) {
+ chunk_t* split = new chunk_t(free_chunk->start, extra);
+ free_chunk->start += extra;
+ mList.insertBefore(free_chunk, split);
+ }
+
+ LOGE_IF((flags&PAGE_ALIGNED) &&
+ ((free_chunk->start*kMemoryAlign)&(pagesize-1)),
+ "PAGE_ALIGNED requested, but page is not aligned!!!");
+
+ const ssize_t tail_free = free_size - (size+extra);
+ if (tail_free > 0) {
+ chunk_t* split = new chunk_t(
+ free_chunk->start + free_chunk->size, tail_free);
+ mList.insertAfter(free_chunk, split);
+ }
+ }
+ return (free_chunk->start)*kMemoryAlign;
+ }
+ return NO_MEMORY;
+}
+
+SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start)
+{
+ start = start / kMemoryAlign;
+ chunk_t* cur = mList.head();
+ while (cur) {
+ if (cur->start == start) {
+ LOG_FATAL_IF(cur->free,
+ "block at offset 0x%08lX of size 0x%08lX already freed",
+ cur->start*kMemoryAlign, cur->size*kMemoryAlign);
+
+ // merge freed blocks together
+ chunk_t* freed = cur;
+ cur->free = 1;
+ do {
+ chunk_t* const p = cur->prev;
+ chunk_t* const n = cur->next;
+ if (p && (p->free || !cur->size)) {
+ freed = p;
+ p->size += cur->size;
+ mList.remove(cur);
+ delete cur;
+ }
+ cur = n;
+ } while (cur && cur->free);
+
+ #ifndef NDEBUG
+ if (!freed->free) {
+ dump_l("dealloc (!freed->free)");
+ }
+ #endif
+ LOG_FATAL_IF(!freed->free,
+ "freed block at offset 0x%08lX of size 0x%08lX is not free!",
+ freed->start * kMemoryAlign, freed->size * kMemoryAlign);
+
+ return freed;
+ }
+ cur = cur->next;
+ }
+ return 0;
+}
+
+void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const
+{
+ Mutex::Autolock _l(mLock);
+ dump_l(what, flags);
+}
+
+void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const
+{
+ String8 result;
+ dump_l(result, what, flags);
+ LOGD("%s", result.string());
+}
+
+void SimpleBestFitAllocator::dump(String8& result,
+ const char* what, uint32_t flags) const
+{
+ Mutex::Autolock _l(mLock);
+ dump_l(result, what, flags);
+}
+
+void SimpleBestFitAllocator::dump_l(String8& result,
+ const char* what, uint32_t flags) const
+{
+ size_t size = 0;
+ int32_t i = 0;
+ chunk_t const* cur = mList.head();
+
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, " %s (%p, size=%u)\n",
+ what, this, (unsigned int)mHeapSize);
+
+ result.append(buffer);
+
+ while (cur) {
+ const char* errs[] = {"", "| link bogus NP",
+ "| link bogus PN", "| link bogus NP+PN" };
+ int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0;
+ int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0;
+
+ snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n",
+ i, int(cur), int(cur->start*kMemoryAlign),
+ int(cur->size*kMemoryAlign),
+ int(cur->free) ? "F" : "A",
+ errs[np|pn]);
+
+ result.append(buffer);
+
+ if (!cur->free)
+ size += cur->size*kMemoryAlign;
+
+ i++;
+ cur = cur->next;
+ }
+ snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024));
+ result.append(buffer);
+}
+
+// ----------------------------------------------------------------------------
+
+SharedHeap::SharedHeap()
+ : HeapInterface(), MemoryHeapBase()
+{
+}
+
+SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name)
+ : MemoryHeapBase(size, flags, name)
+{
+}
+
+SharedHeap::~SharedHeap()
+{
+}
+
+sp<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
+{
+ return new SimpleMemory(this, offset, size);
+}
+
+
+SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& heap,
+ ssize_t offset, size_t size)
+ : MemoryBase(heap, offset, size)
+{
+#ifndef NDEBUG
+ void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
+ memset(start_ptr, 0xda, size);
+#endif
+}
+
+SimpleMemory::~SimpleMemory()
+{
+ size_t freedOffset = getOffset();
+ size_t freedSize = getSize();
+
+ // keep the size to unmap in excess
+ size_t pagesize = getpagesize();
+ size_t start = freedOffset;
+ size_t end = start + freedSize;
+ start &= ~(pagesize-1);
+ end = (end + pagesize-1) & ~(pagesize-1);
+
+ // give back to the kernel the pages we don't need
+ size_t free_start = freedOffset;
+ size_t free_end = free_start + freedSize;
+ if (start < free_start)
+ start = free_start;
+ if (end > free_end)
+ end = free_end;
+ start = (start + pagesize-1) & ~(pagesize-1);
+ end &= ~(pagesize-1);
+
+ if (start < end) {
+ void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start);
+ size_t size = end-start;
+
+#ifndef NDEBUG
+ memset(start_ptr, 0xdf, size);
+#endif
+
+ // MADV_REMOVE is not defined on Dapper based Goobuntu
+#ifdef MADV_REMOVE
+ if (size) {
+ int err = madvise(start_ptr, size, MADV_REMOVE);
+ LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s",
+ start_ptr, size, err<0 ? strerror(errno) : "Ok");
+ }
+#endif
+ }
+}
+
+}; // namespace android
diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp
new file mode 100644
index 0000000..5df078f
--- /dev/null
+++ b/libs/binder/MemoryHeapBase.cpp
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MemoryHeapBase"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <cutils/log.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+
+#include <binder/MemoryHeapBase.h>
+
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#endif
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+MemoryHeapBase::MemoryHeapBase()
+ : mFD(-1), mSize(0), mBase(MAP_FAILED),
+ mDevice(NULL), mNeedUnmap(false)
+{
+}
+
+MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
+ : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
+ mDevice(0), mNeedUnmap(false)
+{
+ const size_t pagesize = getpagesize();
+ size = ((size + pagesize-1) & ~(pagesize-1));
+ int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
+ LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
+ if (fd >= 0) {
+ if (mapfd(fd, size) == NO_ERROR) {
+ if (flags & READ_ONLY) {
+ ashmem_set_prot_region(fd, PROT_READ);
+ }
+ }
+ }
+}
+
+MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
+ : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
+ mDevice(0), mNeedUnmap(false)
+{
+ int fd = open(device, O_RDWR);
+ LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
+ if (fd >= 0) {
+ const size_t pagesize = getpagesize();
+ size = ((size + pagesize-1) & ~(pagesize-1));
+ if (mapfd(fd, size) == NO_ERROR) {
+ mDevice = device;
+ }
+ }
+}
+
+MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags, uint32_t offset)
+ : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
+ mDevice(0), mNeedUnmap(false)
+{
+ const size_t pagesize = getpagesize();
+ size = ((size + pagesize-1) & ~(pagesize-1));
+ mapfd(dup(fd), size, offset);
+}
+
+status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
+{
+ if (mFD != -1) {
+ return INVALID_OPERATION;
+ }
+ mFD = fd;
+ mBase = base;
+ mSize = size;
+ mFlags = flags;
+ mDevice = device;
+ return NO_ERROR;
+}
+
+status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset)
+{
+ if (size == 0) {
+ // try to figure out the size automatically
+#if HAVE_ANDROID_OS
+ // first try the PMEM ioctl
+ pmem_region reg;
+ int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, &reg);
+ if (err == 0)
+ size = reg.len;
+#endif
+ if (size == 0) { // try fstat
+ struct stat sb;
+ if (fstat(fd, &sb) == 0)
+ size = sb.st_size;
+ }
+ // if it didn't work, let mmap() fail.
+ }
+
+ if ((mFlags & DONT_MAP_LOCALLY) == 0) {
+ void* base = (uint8_t*)mmap(0, size,
+ PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+ if (base == MAP_FAILED) {
+ LOGE("mmap(fd=%d, size=%u) failed (%s)",
+ fd, uint32_t(size), strerror(errno));
+ close(fd);
+ return -errno;
+ }
+ //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
+ mBase = base;
+ mNeedUnmap = true;
+ } else {
+ mBase = 0; // not MAP_FAILED
+ mNeedUnmap = false;
+ }
+ mFD = fd;
+ mSize = size;
+ return NO_ERROR;
+}
+
+MemoryHeapBase::~MemoryHeapBase()
+{
+ dispose();
+}
+
+void MemoryHeapBase::dispose()
+{
+ int fd = android_atomic_or(-1, &mFD);
+ if (fd >= 0) {
+ if (mNeedUnmap) {
+ //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
+ munmap(mBase, mSize);
+ }
+ mBase = 0;
+ mSize = 0;
+ close(fd);
+ }
+}
+
+int MemoryHeapBase::getHeapID() const {
+ return mFD;
+}
+
+void* MemoryHeapBase::getBase() const {
+ return mBase;
+}
+
+size_t MemoryHeapBase::getSize() const {
+ return mSize;
+}
+
+uint32_t MemoryHeapBase::getFlags() const {
+ return mFlags;
+}
+
+const char* MemoryHeapBase::getDevice() const {
+ return mDevice;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/binder/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp
new file mode 100644
index 0000000..3806a42
--- /dev/null
+++ b/libs/binder/MemoryHeapPmem.cpp
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MemoryHeapPmem"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <cutils/log.h>
+
+#include <binder/MemoryHeapPmem.h>
+#include <binder/MemoryHeapBase.h>
+
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#endif
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap)
+ : BnMemory(), mClientHeap(heap)
+{
+}
+
+MemoryHeapPmem::MemoryPmem::~MemoryPmem() {
+ if (mClientHeap != NULL) {
+ mClientHeap->remove(this);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+class SubRegionMemory : public MemoryHeapPmem::MemoryPmem {
+public:
+ SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size);
+ virtual ~SubRegionMemory();
+ virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
+private:
+ friend class MemoryHeapPmem;
+ void revoke();
+ size_t mSize;
+ ssize_t mOffset;
+};
+
+SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap,
+ ssize_t offset, size_t size)
+ : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset)
+{
+#ifndef NDEBUG
+ void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset);
+ memset(start_ptr, 0xda, size);
+#endif
+
+#if HAVE_ANDROID_OS
+ if (size > 0) {
+ const size_t pagesize = getpagesize();
+ size = (size + pagesize-1) & ~(pagesize-1);
+ int our_fd = heap->heapID();
+ struct pmem_region sub = { offset, size };
+ int err = ioctl(our_fd, PMEM_MAP, &sub);
+ LOGE_IF(err<0, "PMEM_MAP failed (%s), "
+ "mFD=%d, sub.offset=%lu, sub.size=%lu",
+ strerror(errno), our_fd, sub.offset, sub.len);
+}
+#endif
+}
+
+sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const
+{
+ if (offset) *offset = mOffset;
+ if (size) *size = mSize;
+ return getHeap();
+}
+
+SubRegionMemory::~SubRegionMemory()
+{
+ revoke();
+}
+
+
+void SubRegionMemory::revoke()
+{
+ // NOTE: revoke() doesn't need to be protected by a lock because it
+ // can only be called from MemoryHeapPmem::revoke(), which means
+ // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(),
+ // which means MemoryHeapPmem::revoke() wouldn't have been able to
+ // promote() it.
+
+#if HAVE_ANDROID_OS
+ if (mSize != 0) {
+ const sp<MemoryHeapPmem>& heap(getHeap());
+ int our_fd = heap->heapID();
+ struct pmem_region sub;
+ sub.offset = mOffset;
+ sub.len = mSize;
+ int err = ioctl(our_fd, PMEM_UNMAP, &sub);
+ LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
+ "mFD=%d, sub.offset=%lu, sub.size=%lu",
+ strerror(errno), our_fd, sub.offset, sub.len);
+ mSize = 0;
+ }
+#endif
+}
+
+// ---------------------------------------------------------------------------
+
+MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
+ uint32_t flags)
+ : HeapInterface(), MemoryHeapBase()
+{
+ char const * const device = pmemHeap->getDevice();
+#if HAVE_ANDROID_OS
+ if (device) {
+ int fd = open(device, O_RDWR);
+ LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno));
+ if (fd >= 0) {
+ int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID());
+ if (err < 0) {
+ LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d",
+ strerror(errno), fd, pmemHeap->heapID());
+ close(fd);
+ } else {
+ // everything went well...
+ mParentHeap = pmemHeap;
+ MemoryHeapBase::init(fd,
+ pmemHeap->getBase(),
+ pmemHeap->getSize(),
+ pmemHeap->getFlags() | flags,
+ device);
+ }
+ }
+ }
+#else
+ mParentHeap = pmemHeap;
+ MemoryHeapBase::init(
+ dup(pmemHeap->heapID()),
+ pmemHeap->getBase(),
+ pmemHeap->getSize(),
+ pmemHeap->getFlags() | flags,
+ device);
+#endif
+}
+
+MemoryHeapPmem::~MemoryHeapPmem()
+{
+}
+
+sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size)
+{
+ sp<MemoryPmem> memory = createMemory(offset, size);
+ if (memory != 0) {
+ Mutex::Autolock _l(mLock);
+ mAllocations.add(memory);
+ }
+ return memory;
+}
+
+sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory(
+ size_t offset, size_t size)
+{
+ sp<SubRegionMemory> memory;
+ if (heapID() > 0)
+ memory = new SubRegionMemory(this, offset, size);
+ return memory;
+}
+
+status_t MemoryHeapPmem::slap()
+{
+#if HAVE_ANDROID_OS
+ size_t size = getSize();
+ const size_t pagesize = getpagesize();
+ size = (size + pagesize-1) & ~(pagesize-1);
+ int our_fd = getHeapID();
+ struct pmem_region sub = { 0, size };
+ int err = ioctl(our_fd, PMEM_MAP, &sub);
+ LOGE_IF(err<0, "PMEM_MAP failed (%s), "
+ "mFD=%d, sub.offset=%lu, sub.size=%lu",
+ strerror(errno), our_fd, sub.offset, sub.len);
+ return -errno;
+#else
+ return NO_ERROR;
+#endif
+}
+
+status_t MemoryHeapPmem::unslap()
+{
+#if HAVE_ANDROID_OS
+ size_t size = getSize();
+ const size_t pagesize = getpagesize();
+ size = (size + pagesize-1) & ~(pagesize-1);
+ int our_fd = getHeapID();
+ struct pmem_region sub = { 0, size };
+ int err = ioctl(our_fd, PMEM_UNMAP, &sub);
+ LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
+ "mFD=%d, sub.offset=%lu, sub.size=%lu",
+ strerror(errno), our_fd, sub.offset, sub.len);
+ return -errno;
+#else
+ return NO_ERROR;
+#endif
+}
+
+void MemoryHeapPmem::revoke()
+{
+ SortedVector< wp<MemoryPmem> > allocations;
+
+ { // scope for lock
+ Mutex::Autolock _l(mLock);
+ allocations = mAllocations;
+ }
+
+ ssize_t count = allocations.size();
+ for (ssize_t i=0 ; i<count ; i++) {
+ sp<MemoryPmem> memory(allocations[i].promote());
+ if (memory != 0)
+ memory->revoke();
+ }
+}
+
+void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory)
+{
+ Mutex::Autolock _l(mLock);
+ mAllocations.remove(memory);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
new file mode 100644
index 0000000..e397bce
--- /dev/null
+++ b/libs/binder/Parcel.cpp
@@ -0,0 +1,1336 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "Parcel"
+//#define LOG_NDEBUG 0
+
+#include <binder/Parcel.h>
+
+#include <binder/Binder.h>
+#include <binder/BpBinder.h>
+#include <utils/Debug.h>
+#include <binder/ProcessState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include <utils/TextOutput.h>
+#include <utils/misc.h>
+
+#include <private/binder/binder_module.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#ifndef INT32_MAX
+#define INT32_MAX ((int32_t)(2147483647))
+#endif
+
+#define LOG_REFS(...)
+//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__)
+
+// ---------------------------------------------------------------------------
+
+#define PAD_SIZE(s) (((s)+3)&~3)
+
+// XXX This can be made public if we want to provide
+// support for typed data.
+struct small_flat_data
+{
+ uint32_t type;
+ uint32_t data;
+};
+
+namespace android {
+
+void acquire_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who)
+{
+ switch (obj.type) {
+ case BINDER_TYPE_BINDER:
+ if (obj.binder) {
+ LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
+ static_cast<IBinder*>(obj.cookie)->incStrong(who);
+ }
+ return;
+ case BINDER_TYPE_WEAK_BINDER:
+ if (obj.binder)
+ static_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
+ return;
+ case BINDER_TYPE_HANDLE: {
+ const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
+ if (b != NULL) {
+ LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get());
+ b->incStrong(who);
+ }
+ return;
+ }
+ case BINDER_TYPE_WEAK_HANDLE: {
+ const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
+ if (b != NULL) b.get_refs()->incWeak(who);
+ return;
+ }
+ case BINDER_TYPE_FD: {
+ // intentionally blank -- nothing to do to acquire this, but we do
+ // recognize it as a legitimate object type.
+ return;
+ }
+ }
+
+ LOGD("Invalid object type 0x%08lx", obj.type);
+}
+
+void release_object(const sp<ProcessState>& proc,
+ const flat_binder_object& obj, const void* who)
+{
+ switch (obj.type) {
+ case BINDER_TYPE_BINDER:
+ if (obj.binder) {
+ LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
+ static_cast<IBinder*>(obj.cookie)->decStrong(who);
+ }
+ return;
+ case BINDER_TYPE_WEAK_BINDER:
+ if (obj.binder)
+ static_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who);
+ return;
+ case BINDER_TYPE_HANDLE: {
+ const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
+ if (b != NULL) {
+ LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get());
+ b->decStrong(who);
+ }
+ return;
+ }
+ case BINDER_TYPE_WEAK_HANDLE: {
+ const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
+ if (b != NULL) b.get_refs()->decWeak(who);
+ return;
+ }
+ case BINDER_TYPE_FD: {
+ if (obj.cookie != (void*)0) close(obj.handle);
+ return;
+ }
+ }
+
+ LOGE("Invalid object type 0x%08lx", obj.type);
+}
+
+inline static status_t finish_flatten_binder(
+ const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
+{
+ return out->writeObject(flat, false);
+}
+
+status_t flatten_binder(const sp<ProcessState>& proc,
+ const sp<IBinder>& binder, Parcel* out)
+{
+ flat_binder_object obj;
+
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ if (binder != NULL) {
+ IBinder *local = binder->localBinder();
+ if (!local) {
+ BpBinder *proxy = binder->remoteBinder();
+ if (proxy == NULL) {
+ LOGE("null proxy");
+ }
+ const int32_t handle = proxy ? proxy->handle() : 0;
+ obj.type = BINDER_TYPE_HANDLE;
+ obj.handle = handle;
+ obj.cookie = NULL;
+ } else {
+ obj.type = BINDER_TYPE_BINDER;
+ obj.binder = local->getWeakRefs();
+ obj.cookie = local;
+ }
+ } else {
+ obj.type = BINDER_TYPE_BINDER;
+ obj.binder = NULL;
+ obj.cookie = NULL;
+ }
+
+ return finish_flatten_binder(binder, obj, out);
+}
+
+status_t flatten_binder(const sp<ProcessState>& proc,
+ const wp<IBinder>& binder, Parcel* out)
+{
+ flat_binder_object obj;
+
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ if (binder != NULL) {
+ sp<IBinder> real = binder.promote();
+ if (real != NULL) {
+ IBinder *local = real->localBinder();
+ if (!local) {
+ BpBinder *proxy = real->remoteBinder();
+ if (proxy == NULL) {
+ LOGE("null proxy");
+ }
+ const int32_t handle = proxy ? proxy->handle() : 0;
+ obj.type = BINDER_TYPE_WEAK_HANDLE;
+ obj.handle = handle;
+ obj.cookie = NULL;
+ } else {
+ obj.type = BINDER_TYPE_WEAK_BINDER;
+ obj.binder = binder.get_refs();
+ obj.cookie = binder.unsafe_get();
+ }
+ return finish_flatten_binder(real, obj, out);
+ }
+
+ // XXX How to deal? In order to flatten the given binder,
+ // we need to probe it for information, which requires a primary
+ // reference... but we don't have one.
+ //
+ // The OpenBinder implementation uses a dynamic_cast<> here,
+ // but we can't do that with the different reference counting
+ // implementation we are using.
+ LOGE("Unable to unflatten Binder weak reference!");
+ obj.type = BINDER_TYPE_BINDER;
+ obj.binder = NULL;
+ obj.cookie = NULL;
+ return finish_flatten_binder(NULL, obj, out);
+
+ } else {
+ obj.type = BINDER_TYPE_BINDER;
+ obj.binder = NULL;
+ obj.cookie = NULL;
+ return finish_flatten_binder(NULL, obj, out);
+ }
+}
+
+inline static status_t finish_unflatten_binder(
+ BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
+{
+ return NO_ERROR;
+}
+
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const Parcel& in, sp<IBinder>* out)
+{
+ const flat_binder_object* flat = in.readObject(false);
+
+ if (flat) {
+ switch (flat->type) {
+ case BINDER_TYPE_BINDER:
+ *out = static_cast<IBinder*>(flat->cookie);
+ return finish_unflatten_binder(NULL, *flat, in);
+ case BINDER_TYPE_HANDLE:
+ *out = proc->getStrongProxyForHandle(flat->handle);
+ return finish_unflatten_binder(
+ static_cast<BpBinder*>(out->get()), *flat, in);
+ }
+ }
+ return BAD_TYPE;
+}
+
+status_t unflatten_binder(const sp<ProcessState>& proc,
+ const Parcel& in, wp<IBinder>* out)
+{
+ const flat_binder_object* flat = in.readObject(false);
+
+ if (flat) {
+ switch (flat->type) {
+ case BINDER_TYPE_BINDER:
+ *out = static_cast<IBinder*>(flat->cookie);
+ return finish_unflatten_binder(NULL, *flat, in);
+ case BINDER_TYPE_WEAK_BINDER:
+ if (flat->binder != NULL) {
+ out->set_object_and_refs(
+ static_cast<IBinder*>(flat->cookie),
+ static_cast<RefBase::weakref_type*>(flat->binder));
+ } else {
+ *out = NULL;
+ }
+ return finish_unflatten_binder(NULL, *flat, in);
+ case BINDER_TYPE_HANDLE:
+ case BINDER_TYPE_WEAK_HANDLE:
+ *out = proc->getWeakProxyForHandle(flat->handle);
+ return finish_unflatten_binder(
+ static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
+ }
+ }
+ return BAD_TYPE;
+}
+
+// ---------------------------------------------------------------------------
+
+Parcel::Parcel()
+{
+ initState();
+}
+
+Parcel::~Parcel()
+{
+ freeDataNoInit();
+}
+
+const uint8_t* Parcel::data() const
+{
+ return mData;
+}
+
+size_t Parcel::dataSize() const
+{
+ return (mDataSize > mDataPos ? mDataSize : mDataPos);
+}
+
+size_t Parcel::dataAvail() const
+{
+ // TODO: decide what to do about the possibility that this can
+ // report an available-data size that exceeds a Java int's max
+ // positive value, causing havoc. Fortunately this will only
+ // happen if someone constructs a Parcel containing more than two
+ // gigabytes of data, which on typical phone hardware is simply
+ // not possible.
+ return dataSize() - dataPosition();
+}
+
+size_t Parcel::dataPosition() const
+{
+ return mDataPos;
+}
+
+size_t Parcel::dataCapacity() const
+{
+ return mDataCapacity;
+}
+
+status_t Parcel::setDataSize(size_t size)
+{
+ status_t err;
+ err = continueWrite(size);
+ if (err == NO_ERROR) {
+ mDataSize = size;
+ LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize);
+ }
+ return err;
+}
+
+void Parcel::setDataPosition(size_t pos) const
+{
+ mDataPos = pos;
+ mNextObjectHint = 0;
+}
+
+status_t Parcel::setDataCapacity(size_t size)
+{
+ if (size > mDataSize) return continueWrite(size);
+ return NO_ERROR;
+}
+
+status_t Parcel::setData(const uint8_t* buffer, size_t len)
+{
+ status_t err = restartWrite(len);
+ if (err == NO_ERROR) {
+ memcpy(const_cast<uint8_t*>(data()), buffer, len);
+ mDataSize = len;
+ mFdsKnown = false;
+ }
+ return err;
+}
+
+status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
+{
+ const sp<ProcessState> proc(ProcessState::self());
+ status_t err;
+ uint8_t *data = parcel->mData;
+ size_t *objects = parcel->mObjects;
+ size_t size = parcel->mObjectsSize;
+ int startPos = mDataPos;
+ int firstIndex = -1, lastIndex = -2;
+
+ if (len == 0) {
+ return NO_ERROR;
+ }
+
+ // range checks against the source parcel size
+ if ((offset > parcel->mDataSize)
+ || (len > parcel->mDataSize)
+ || (offset + len > parcel->mDataSize)) {
+ return BAD_VALUE;
+ }
+
+ // Count objects in range
+ for (int i = 0; i < (int) size; i++) {
+ size_t off = objects[i];
+ if ((off >= offset) && (off < offset + len)) {
+ if (firstIndex == -1) {
+ firstIndex = i;
+ }
+ lastIndex = i;
+ }
+ }
+ int numObjects = lastIndex - firstIndex + 1;
+
+ // grow data
+ err = growData(len);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ // append data
+ memcpy(mData + mDataPos, data + offset, len);
+ mDataPos += len;
+ mDataSize += len;
+
+ if (numObjects > 0) {
+ // grow objects
+ if (mObjectsCapacity < mObjectsSize + numObjects) {
+ int newSize = ((mObjectsSize + numObjects)*3)/2;
+ size_t *objects =
+ (size_t*)realloc(mObjects, newSize*sizeof(size_t));
+ if (objects == (size_t*)0) {
+ return NO_MEMORY;
+ }
+ mObjects = objects;
+ mObjectsCapacity = newSize;
+ }
+
+ // append and acquire objects
+ int idx = mObjectsSize;
+ for (int i = firstIndex; i <= lastIndex; i++) {
+ size_t off = objects[i] - offset + startPos;
+ mObjects[idx++] = off;
+ mObjectsSize++;
+
+ flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(mData + off);
+ acquire_object(proc, *flat, this);
+
+ if (flat->type == BINDER_TYPE_FD) {
+ // If this is a file descriptor, we need to dup it so the
+ // new Parcel now owns its own fd, and can declare that we
+ // officially know we have fds.
+ flat->handle = dup(flat->handle);
+ flat->cookie = (void*)1;
+ mHasFds = mFdsKnown = true;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+bool Parcel::hasFileDescriptors() const
+{
+ if (!mFdsKnown) {
+ scanForFds();
+ }
+ return mHasFds;
+}
+
+status_t Parcel::writeInterfaceToken(const String16& interface)
+{
+ // currently the interface identification token is just its name as a string
+ return writeString16(interface);
+}
+
+bool Parcel::checkInterface(IBinder* binder) const
+{
+ return enforceInterface(binder->getInterfaceDescriptor());
+}
+
+bool Parcel::enforceInterface(const String16& interface) const
+{
+ const String16 str(readString16());
+ if (str == interface) {
+ return true;
+ } else {
+ LOGW("**** enforceInterface() expected '%s' but read '%s'\n",
+ String8(interface).string(), String8(str).string());
+ return false;
+ }
+}
+
+const size_t* Parcel::objects() const
+{
+ return mObjects;
+}
+
+size_t Parcel::objectsCount() const
+{
+ return mObjectsSize;
+}
+
+status_t Parcel::errorCheck() const
+{
+ return mError;
+}
+
+void Parcel::setError(status_t err)
+{
+ mError = err;
+}
+
+status_t Parcel::finishWrite(size_t len)
+{
+ //printf("Finish write of %d\n", len);
+ mDataPos += len;
+ LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
+ if (mDataPos > mDataSize) {
+ mDataSize = mDataPos;
+ LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
+ }
+ //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
+ return NO_ERROR;
+}
+
+status_t Parcel::writeUnpadded(const void* data, size_t len)
+{
+ size_t end = mDataPos + len;
+ if (end < mDataPos) {
+ // integer overflow
+ return BAD_VALUE;
+ }
+
+ if (end <= mDataCapacity) {
+restart_write:
+ memcpy(mData+mDataPos, data, len);
+ return finishWrite(len);
+ }
+
+ status_t err = growData(len);
+ if (err == NO_ERROR) goto restart_write;
+ return err;
+}
+
+status_t Parcel::write(const void* data, size_t len)
+{
+ void* const d = writeInplace(len);
+ if (d) {
+ memcpy(d, data, len);
+ return NO_ERROR;
+ }
+ return mError;
+}
+
+void* Parcel::writeInplace(size_t len)
+{
+ const size_t padded = PAD_SIZE(len);
+
+ // sanity check for integer overflow
+ if (mDataPos+padded < mDataPos) {
+ return NULL;
+ }
+
+ if ((mDataPos+padded) <= mDataCapacity) {
+restart_write:
+ //printf("Writing %ld bytes, padded to %ld\n", len, padded);
+ uint8_t* const data = mData+mDataPos;
+
+ // Need to pad at end?
+ if (padded != len) {
+#if BYTE_ORDER == BIG_ENDIAN
+ static const uint32_t mask[4] = {
+ 0x00000000, 0xffffff00, 0xffff0000, 0xff000000
+ };
+#endif
+#if BYTE_ORDER == LITTLE_ENDIAN
+ static const uint32_t mask[4] = {
+ 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
+ };
+#endif
+ //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
+ // *reinterpret_cast<void**>(data+padded-4));
+ *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
+ }
+
+ finishWrite(padded);
+ return data;
+ }
+
+ status_t err = growData(padded);
+ if (err == NO_ERROR) goto restart_write;
+ return NULL;
+}
+
+status_t Parcel::writeInt32(int32_t val)
+{
+ return writeAligned(val);
+}
+
+status_t Parcel::writeInt64(int64_t val)
+{
+ return writeAligned(val);
+}
+
+status_t Parcel::writeFloat(float val)
+{
+ return writeAligned(val);
+}
+
+status_t Parcel::writeDouble(double val)
+{
+ return writeAligned(val);
+}
+
+status_t Parcel::writeIntPtr(intptr_t val)
+{
+ return writeAligned(val);
+}
+
+status_t Parcel::writeCString(const char* str)
+{
+ return write(str, strlen(str)+1);
+}
+
+status_t Parcel::writeString8(const String8& str)
+{
+ status_t err = writeInt32(str.bytes());
+ if (err == NO_ERROR) {
+ err = write(str.string(), str.bytes()+1);
+ }
+ return err;
+}
+
+status_t Parcel::writeString16(const String16& str)
+{
+ return writeString16(str.string(), str.size());
+}
+
+status_t Parcel::writeString16(const char16_t* str, size_t len)
+{
+ if (str == NULL) return writeInt32(-1);
+
+ status_t err = writeInt32(len);
+ if (err == NO_ERROR) {
+ len *= sizeof(char16_t);
+ uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
+ if (data) {
+ memcpy(data, str, len);
+ *reinterpret_cast<char16_t*>(data+len) = 0;
+ return NO_ERROR;
+ }
+ err = mError;
+ }
+ return err;
+}
+
+status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
+{
+ return flatten_binder(ProcessState::self(), val, this);
+}
+
+status_t Parcel::writeWeakBinder(const wp<IBinder>& val)
+{
+ return flatten_binder(ProcessState::self(), val, this);
+}
+
+status_t Parcel::writeNativeHandle(const native_handle* handle)
+{
+ if (!handle || handle->version != sizeof(native_handle))
+ return BAD_TYPE;
+
+ status_t err;
+ err = writeInt32(handle->numFds);
+ if (err != NO_ERROR) return err;
+
+ err = writeInt32(handle->numInts);
+ if (err != NO_ERROR) return err;
+
+ for (int i=0 ; err==NO_ERROR && i<handle->numFds ; i++)
+ err = writeDupFileDescriptor(handle->data[i]);
+
+ if (err != NO_ERROR) {
+ LOGD("write native handle, write dup fd failed");
+ return err;
+ }
+ err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
+ return err;
+}
+
+status_t Parcel::writeFileDescriptor(int fd)
+{
+ flat_binder_object obj;
+ obj.type = BINDER_TYPE_FD;
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj.handle = fd;
+ obj.cookie = (void*)0;
+ return writeObject(obj, true);
+}
+
+status_t Parcel::writeDupFileDescriptor(int fd)
+{
+ flat_binder_object obj;
+ obj.type = BINDER_TYPE_FD;
+ obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
+ obj.handle = dup(fd);
+ obj.cookie = (void*)1;
+ return writeObject(obj, true);
+}
+
+status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
+{
+ const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
+ const bool enoughObjects = mObjectsSize < mObjectsCapacity;
+ if (enoughData && enoughObjects) {
+restart_write:
+ *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
+
+ // Need to write meta-data?
+ if (nullMetaData || val.binder != NULL) {
+ mObjects[mObjectsSize] = mDataPos;
+ acquire_object(ProcessState::self(), val, this);
+ mObjectsSize++;
+ }
+
+ // remember if it's a file descriptor
+ if (val.type == BINDER_TYPE_FD) {
+ mHasFds = mFdsKnown = true;
+ }
+
+ return finishWrite(sizeof(flat_binder_object));
+ }
+
+ if (!enoughData) {
+ const status_t err = growData(sizeof(val));
+ if (err != NO_ERROR) return err;
+ }
+ if (!enoughObjects) {
+ size_t newSize = ((mObjectsSize+2)*3)/2;
+ size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));
+ if (objects == NULL) return NO_MEMORY;
+ mObjects = objects;
+ mObjectsCapacity = newSize;
+ }
+
+ goto restart_write;
+}
+
+
+void Parcel::remove(size_t start, size_t amt)
+{
+ LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
+}
+
+status_t Parcel::read(void* outData, size_t len) const
+{
+ if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
+ memcpy(outData, mData+mDataPos, len);
+ mDataPos += PAD_SIZE(len);
+ LOGV("read Setting data pos of %p to %d\n", this, mDataPos);
+ return NO_ERROR;
+ }
+ return NOT_ENOUGH_DATA;
+}
+
+const void* Parcel::readInplace(size_t len) const
+{
+ if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
+ const void* data = mData+mDataPos;
+ mDataPos += PAD_SIZE(len);
+ LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos);
+ return data;
+ }
+ return NULL;
+}
+
+template<class T>
+status_t Parcel::readAligned(T *pArg) const {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
+
+ if ((mDataPos+sizeof(T)) <= mDataSize) {
+ const void* data = mData+mDataPos;
+ mDataPos += sizeof(T);
+ *pArg = *reinterpret_cast<const T*>(data);
+ return NO_ERROR;
+ } else {
+ return NOT_ENOUGH_DATA;
+ }
+}
+
+template<class T>
+T Parcel::readAligned() const {
+ T result;
+ if (readAligned(&result) != NO_ERROR) {
+ result = 0;
+ }
+
+ return result;
+}
+
+template<class T>
+status_t Parcel::writeAligned(T val) {
+ COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
+
+ if ((mDataPos+sizeof(val)) <= mDataCapacity) {
+restart_write:
+ *reinterpret_cast<T*>(mData+mDataPos) = val;
+ return finishWrite(sizeof(val));
+ }
+
+ status_t err = growData(sizeof(val));
+ if (err == NO_ERROR) goto restart_write;
+ return err;
+}
+
+status_t Parcel::readInt32(int32_t *pArg) const
+{
+ return readAligned(pArg);
+}
+
+int32_t Parcel::readInt32() const
+{
+ return readAligned<int32_t>();
+}
+
+
+status_t Parcel::readInt64(int64_t *pArg) const
+{
+ return readAligned(pArg);
+}
+
+
+int64_t Parcel::readInt64() const
+{
+ return readAligned<int64_t>();
+}
+
+status_t Parcel::readFloat(float *pArg) const
+{
+ return readAligned(pArg);
+}
+
+
+float Parcel::readFloat() const
+{
+ return readAligned<float>();
+}
+
+status_t Parcel::readDouble(double *pArg) const
+{
+ return readAligned(pArg);
+}
+
+
+double Parcel::readDouble() const
+{
+ return readAligned<double>();
+}
+
+status_t Parcel::readIntPtr(intptr_t *pArg) const
+{
+ return readAligned(pArg);
+}
+
+
+intptr_t Parcel::readIntPtr() const
+{
+ return readAligned<intptr_t>();
+}
+
+
+const char* Parcel::readCString() const
+{
+ const size_t avail = mDataSize-mDataPos;
+ if (avail > 0) {
+ const char* str = reinterpret_cast<const char*>(mData+mDataPos);
+ // is the string's trailing NUL within the parcel's valid bounds?
+ const char* eos = reinterpret_cast<const char*>(memchr(str, 0, avail));
+ if (eos) {
+ const size_t len = eos - str;
+ mDataPos += PAD_SIZE(len+1);
+ LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos);
+ return str;
+ }
+ }
+ return NULL;
+}
+
+String8 Parcel::readString8() const
+{
+ int32_t size = readInt32();
+ // watch for potential int overflow adding 1 for trailing NUL
+ if (size > 0 && size < INT32_MAX) {
+ const char* str = (const char*)readInplace(size+1);
+ if (str) return String8(str, size);
+ }
+ return String8();
+}
+
+String16 Parcel::readString16() const
+{
+ size_t len;
+ const char16_t* str = readString16Inplace(&len);
+ if (str) return String16(str, len);
+ LOGE("Reading a NULL string not supported here.");
+ return String16();
+}
+
+const char16_t* Parcel::readString16Inplace(size_t* outLen) const
+{
+ int32_t size = readInt32();
+ // watch for potential int overflow from size+1
+ if (size >= 0 && size < INT32_MAX) {
+ *outLen = size;
+ const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
+ if (str != NULL) {
+ return str;
+ }
+ }
+ *outLen = 0;
+ return NULL;
+}
+
+sp<IBinder> Parcel::readStrongBinder() const
+{
+ sp<IBinder> val;
+ unflatten_binder(ProcessState::self(), *this, &val);
+ return val;
+}
+
+wp<IBinder> Parcel::readWeakBinder() const
+{
+ wp<IBinder> val;
+ unflatten_binder(ProcessState::self(), *this, &val);
+ return val;
+}
+
+
+native_handle* Parcel::readNativeHandle() const
+{
+ int numFds, numInts;
+ status_t err;
+ err = readInt32(&numFds);
+ if (err != NO_ERROR) return 0;
+ err = readInt32(&numInts);
+ if (err != NO_ERROR) return 0;
+
+ native_handle* h = native_handle_create(numFds, numInts);
+ for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
+ h->data[i] = dup(readFileDescriptor());
+ if (h->data[i] < 0) err = BAD_VALUE;
+ }
+ err = read(h->data + numFds, sizeof(int)*numInts);
+ if (err != NO_ERROR) {
+ native_handle_close(h);
+ native_handle_delete(h);
+ h = 0;
+ }
+ return h;
+}
+
+
+int Parcel::readFileDescriptor() const
+{
+ const flat_binder_object* flat = readObject(true);
+ if (flat) {
+ switch (flat->type) {
+ case BINDER_TYPE_FD:
+ //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this);
+ return flat->handle;
+ }
+ }
+ return BAD_TYPE;
+}
+
+const flat_binder_object* Parcel::readObject(bool nullMetaData) const
+{
+ const size_t DPOS = mDataPos;
+ if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) {
+ const flat_binder_object* obj
+ = reinterpret_cast<const flat_binder_object*>(mData+DPOS);
+ mDataPos = DPOS + sizeof(flat_binder_object);
+ if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) {
+ // When transferring a NULL object, we don't write it into
+ // the object list, so we don't want to check for it when
+ // reading.
+ LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+ return obj;
+ }
+
+ // Ensure that this object is valid...
+ size_t* const OBJS = mObjects;
+ const size_t N = mObjectsSize;
+ size_t opos = mNextObjectHint;
+
+ if (N > 0) {
+ LOGV("Parcel %p looking for obj at %d, hint=%d\n",
+ this, DPOS, opos);
+
+ // Start at the current hint position, looking for an object at
+ // the current data position.
+ if (opos < N) {
+ while (opos < (N-1) && OBJS[opos] < DPOS) {
+ opos++;
+ }
+ } else {
+ opos = N-1;
+ }
+ if (OBJS[opos] == DPOS) {
+ // Found it!
+ LOGV("Parcel found obj %d at index %d with forward search",
+ this, DPOS, opos);
+ mNextObjectHint = opos+1;
+ LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+ return obj;
+ }
+
+ // Look backwards for it...
+ while (opos > 0 && OBJS[opos] > DPOS) {
+ opos--;
+ }
+ if (OBJS[opos] == DPOS) {
+ // Found it!
+ LOGV("Parcel found obj %d at index %d with backward search",
+ this, DPOS, opos);
+ mNextObjectHint = opos+1;
+ LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
+ return obj;
+ }
+ }
+ LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list",
+ this, DPOS);
+ }
+ return NULL;
+}
+
+void Parcel::closeFileDescriptors()
+{
+ size_t i = mObjectsSize;
+ if (i > 0) {
+ //LOGI("Closing file descriptors for %d objects...", mObjectsSize);
+ }
+ while (i > 0) {
+ i--;
+ const flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
+ if (flat->type == BINDER_TYPE_FD) {
+ //LOGI("Closing fd: %ld\n", flat->handle);
+ close(flat->handle);
+ }
+ }
+}
+
+const uint8_t* Parcel::ipcData() const
+{
+ return mData;
+}
+
+size_t Parcel::ipcDataSize() const
+{
+ return (mDataSize > mDataPos ? mDataSize : mDataPos);
+}
+
+const size_t* Parcel::ipcObjects() const
+{
+ return mObjects;
+}
+
+size_t Parcel::ipcObjectsCount() const
+{
+ return mObjectsSize;
+}
+
+void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
+ const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
+{
+ freeDataNoInit();
+ mError = NO_ERROR;
+ mData = const_cast<uint8_t*>(data);
+ mDataSize = mDataCapacity = dataSize;
+ //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid());
+ mDataPos = 0;
+ LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos);
+ mObjects = const_cast<size_t*>(objects);
+ mObjectsSize = mObjectsCapacity = objectsCount;
+ mNextObjectHint = 0;
+ mOwner = relFunc;
+ mOwnerCookie = relCookie;
+ scanForFds();
+}
+
+void Parcel::print(TextOutput& to, uint32_t flags) const
+{
+ to << "Parcel(";
+
+ if (errorCheck() != NO_ERROR) {
+ const status_t err = errorCheck();
+ to << "Error: " << (void*)err << " \"" << strerror(-err) << "\"";
+ } else if (dataSize() > 0) {
+ const uint8_t* DATA = data();
+ to << indent << HexDump(DATA, dataSize()) << dedent;
+ const size_t* OBJS = objects();
+ const size_t N = objectsCount();
+ for (size_t i=0; i<N; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
+ to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
+ << TypeCode(flat->type & 0x7f7f7f00)
+ << " = " << flat->binder;
+ }
+ } else {
+ to << "NULL";
+ }
+
+ to << ")";
+}
+
+void Parcel::releaseObjects()
+{
+ const sp<ProcessState> proc(ProcessState::self());
+ size_t i = mObjectsSize;
+ uint8_t* const data = mData;
+ size_t* const objects = mObjects;
+ while (i > 0) {
+ i--;
+ const flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(data+objects[i]);
+ release_object(proc, *flat, this);
+ }
+}
+
+void Parcel::acquireObjects()
+{
+ const sp<ProcessState> proc(ProcessState::self());
+ size_t i = mObjectsSize;
+ uint8_t* const data = mData;
+ size_t* const objects = mObjects;
+ while (i > 0) {
+ i--;
+ const flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(data+objects[i]);
+ acquire_object(proc, *flat, this);
+ }
+}
+
+void Parcel::freeData()
+{
+ freeDataNoInit();
+ initState();
+}
+
+void Parcel::freeDataNoInit()
+{
+ if (mOwner) {
+ //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ } else {
+ releaseObjects();
+ if (mData) free(mData);
+ if (mObjects) free(mObjects);
+ }
+}
+
+status_t Parcel::growData(size_t len)
+{
+ size_t newSize = ((mDataSize+len)*3)/2;
+ return (newSize <= mDataSize)
+ ? (status_t) NO_MEMORY
+ : continueWrite(newSize);
+}
+
+status_t Parcel::restartWrite(size_t desired)
+{
+ if (mOwner) {
+ freeData();
+ return continueWrite(desired);
+ }
+
+ uint8_t* data = (uint8_t*)realloc(mData, desired);
+ if (!data && desired > mDataCapacity) {
+ mError = NO_MEMORY;
+ return NO_MEMORY;
+ }
+
+ releaseObjects();
+
+ if (data) {
+ mData = data;
+ mDataCapacity = desired;
+ }
+
+ mDataSize = mDataPos = 0;
+ LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize);
+ LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos);
+
+ free(mObjects);
+ mObjects = NULL;
+ mObjectsSize = mObjectsCapacity = 0;
+ mNextObjectHint = 0;
+ mHasFds = false;
+ mFdsKnown = true;
+
+ return NO_ERROR;
+}
+
+status_t Parcel::continueWrite(size_t desired)
+{
+ // If shrinking, first adjust for any objects that appear
+ // after the new data size.
+ size_t objectsSize = mObjectsSize;
+ if (desired < mDataSize) {
+ if (desired == 0) {
+ objectsSize = 0;
+ } else {
+ while (objectsSize > 0) {
+ if (mObjects[objectsSize-1] < desired)
+ break;
+ objectsSize--;
+ }
+ }
+ }
+
+ if (mOwner) {
+ // If the size is going to zero, just release the owner's data.
+ if (desired == 0) {
+ freeData();
+ return NO_ERROR;
+ }
+
+ // If there is a different owner, we need to take
+ // posession.
+ uint8_t* data = (uint8_t*)malloc(desired);
+ if (!data) {
+ mError = NO_MEMORY;
+ return NO_MEMORY;
+ }
+ size_t* objects = NULL;
+
+ if (objectsSize) {
+ objects = (size_t*)malloc(objectsSize*sizeof(size_t));
+ if (!objects) {
+ mError = NO_MEMORY;
+ return NO_MEMORY;
+ }
+
+ // Little hack to only acquire references on objects
+ // we will be keeping.
+ size_t oldObjectsSize = mObjectsSize;
+ mObjectsSize = objectsSize;
+ acquireObjects();
+ mObjectsSize = oldObjectsSize;
+ }
+
+ if (mData) {
+ memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
+ }
+ if (objects && mObjects) {
+ memcpy(objects, mObjects, objectsSize*sizeof(size_t));
+ }
+ //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
+ mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
+ mOwner = NULL;
+
+ mData = data;
+ mObjects = objects;
+ mDataSize = (mDataSize < desired) ? mDataSize : desired;
+ LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+ mDataCapacity = desired;
+ mObjectsSize = mObjectsCapacity = objectsSize;
+ mNextObjectHint = 0;
+
+ } else if (mData) {
+ if (objectsSize < mObjectsSize) {
+ // Need to release refs on any objects we are dropping.
+ const sp<ProcessState> proc(ProcessState::self());
+ for (size_t i=objectsSize; i<mObjectsSize; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
+ if (flat->type == BINDER_TYPE_FD) {
+ // will need to rescan because we may have lopped off the only FDs
+ mFdsKnown = false;
+ }
+ release_object(proc, *flat, this);
+ }
+ size_t* objects =
+ (size_t*)realloc(mObjects, objectsSize*sizeof(size_t));
+ if (objects) {
+ mObjects = objects;
+ }
+ mObjectsSize = objectsSize;
+ mNextObjectHint = 0;
+ }
+
+ // We own the data, so we can just do a realloc().
+ if (desired > mDataCapacity) {
+ uint8_t* data = (uint8_t*)realloc(mData, desired);
+ if (data) {
+ mData = data;
+ mDataCapacity = desired;
+ } else if (desired > mDataCapacity) {
+ mError = NO_MEMORY;
+ return NO_MEMORY;
+ }
+ } else {
+ mDataSize = desired;
+ LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+ if (mDataPos > desired) {
+ mDataPos = desired;
+ LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
+ }
+ }
+
+ } else {
+ // This is the first data. Easy!
+ uint8_t* data = (uint8_t*)malloc(desired);
+ if (!data) {
+ mError = NO_MEMORY;
+ return NO_MEMORY;
+ }
+
+ if(!(mDataCapacity == 0 && mObjects == NULL
+ && mObjectsCapacity == 0)) {
+ LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
+ }
+
+ mData = data;
+ mDataSize = mDataPos = 0;
+ LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+ LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
+ mDataCapacity = desired;
+ }
+
+ return NO_ERROR;
+}
+
+void Parcel::initState()
+{
+ mError = NO_ERROR;
+ mData = 0;
+ mDataSize = 0;
+ mDataCapacity = 0;
+ mDataPos = 0;
+ LOGV("initState Setting data size of %p to %d\n", this, mDataSize);
+ LOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
+ mObjects = NULL;
+ mObjectsSize = 0;
+ mObjectsCapacity = 0;
+ mNextObjectHint = 0;
+ mHasFds = false;
+ mFdsKnown = true;
+ mOwner = NULL;
+}
+
+void Parcel::scanForFds() const
+{
+ bool hasFds = false;
+ for (size_t i=0; i<mObjectsSize; i++) {
+ const flat_binder_object* flat
+ = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
+ if (flat->type == BINDER_TYPE_FD) {
+ hasFds = true;
+ break;
+ }
+ }
+ mHasFds = hasFds;
+ mFdsKnown = true;
+}
+
+}; // namespace android
diff --git a/libs/binder/Permission.cpp b/libs/binder/Permission.cpp
new file mode 100644
index 0000000..fd8fe69
--- /dev/null
+++ b/libs/binder/Permission.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/Permission.h>
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+Permission::Permission(char const* name)
+ : mPermissionName(name), mPid(getpid())
+{
+}
+
+Permission::Permission(const String16& name)
+ : mPermissionName(name), mPid(getpid())
+{
+}
+
+Permission::Permission(const Permission& rhs)
+ : mPermissionName(rhs.mPermissionName),
+ mGranted(rhs.mGranted),
+ mPid(rhs.mPid)
+{
+}
+
+Permission::~Permission()
+{
+}
+
+bool Permission::operator < (const Permission& rhs) const
+{
+ return mPermissionName < rhs.mPermissionName;
+}
+
+bool Permission::checkCalling() const
+{
+ IPCThreadState* ipcState = IPCThreadState::self();
+ pid_t pid = ipcState->getCallingPid();
+ uid_t uid = ipcState->getCallingUid();
+ return doCheckPermission(pid, uid);
+}
+
+bool Permission::check(pid_t pid, uid_t uid) const
+{
+ return doCheckPermission(pid, uid);
+}
+
+bool Permission::doCheckPermission(pid_t pid, uid_t uid) const
+{
+ if ((uid == 0) || (pid == mPid)) {
+ // root and ourselves is always okay
+ return true;
+ } else {
+ // see if we already granted this permission for this uid
+ Mutex::Autolock _l(mLock);
+ if (mGranted.indexOf(uid) >= 0)
+ return true;
+ }
+
+ bool granted = checkPermission(mPermissionName, pid, uid);
+ if (granted) {
+ Mutex::Autolock _l(mLock);
+ // no need to check again, the old item will be replaced if it is
+ // already there.
+ mGranted.add(uid);
+ }
+ return granted;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
new file mode 100644
index 0000000..d7daf73
--- /dev/null
+++ b/libs/binder/ProcessState.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+#define LOG_TAG "ProcessState"
+
+#include <cutils/process_name.h>
+
+#include <binder/ProcessState.h>
+
+#include <utils/Atomic.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <binder/IServiceManager.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include <private/binder/binder_module.h>
+#include <private/binder/Static.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#define BINDER_VM_SIZE (1*1024*1024)
+
+static bool gSingleProcess = false;
+
+
+// ---------------------------------------------------------------------------
+
+namespace android {
+
+// Global variables
+int mArgC;
+const char* const* mArgV;
+int mArgLen;
+
+class PoolThread : public Thread
+{
+public:
+ PoolThread(bool isMain)
+ : mIsMain(isMain)
+ {
+ }
+
+protected:
+ virtual bool threadLoop()
+ {
+ IPCThreadState::self()->joinThreadPool(mIsMain);
+ return false;
+ }
+
+ const bool mIsMain;
+};
+
+sp<ProcessState> ProcessState::self()
+{
+ if (gProcess != NULL) return gProcess;
+
+ AutoMutex _l(gProcessMutex);
+ if (gProcess == NULL) gProcess = new ProcessState;
+ return gProcess;
+}
+
+void ProcessState::setSingleProcess(bool singleProcess)
+{
+ gSingleProcess = singleProcess;
+}
+
+
+void ProcessState::setContextObject(const sp<IBinder>& object)
+{
+ setContextObject(object, String16("default"));
+}
+
+sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
+{
+ if (supportsProcesses()) {
+ return getStrongProxyForHandle(0);
+ } else {
+ return getContextObject(String16("default"), caller);
+ }
+}
+
+void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name)
+{
+ AutoMutex _l(mLock);
+ mContexts.add(name, object);
+}
+
+sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller)
+{
+ mLock.lock();
+ sp<IBinder> object(
+ mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL);
+ mLock.unlock();
+
+ //printf("Getting context object %s for %p\n", String8(name).string(), caller.get());
+
+ if (object != NULL) return object;
+
+ // Don't attempt to retrieve contexts if we manage them
+ if (mManagesContexts) {
+ LOGE("getContextObject(%s) failed, but we manage the contexts!\n",
+ String8(name).string());
+ return NULL;
+ }
+
+ IPCThreadState* ipc = IPCThreadState::self();
+ {
+ Parcel data, reply;
+ // no interface token on this magic transaction
+ data.writeString16(name);
+ data.writeStrongBinder(caller);
+ status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0);
+ if (result == NO_ERROR) {
+ object = reply.readStrongBinder();
+ }
+ }
+
+ ipc->flushCommands();
+
+ if (object != NULL) setContextObject(object, name);
+ return object;
+}
+
+bool ProcessState::supportsProcesses() const
+{
+ return mDriverFD >= 0;
+}
+
+void ProcessState::startThreadPool()
+{
+ AutoMutex _l(mLock);
+ if (!mThreadPoolStarted) {
+ mThreadPoolStarted = true;
+ spawnPooledThread(true);
+ }
+}
+
+bool ProcessState::isContextManager(void) const
+{
+ return mManagesContexts;
+}
+
+bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
+{
+ if (!mManagesContexts) {
+ AutoMutex _l(mLock);
+ mBinderContextCheckFunc = checkFunc;
+ mBinderContextUserData = userData;
+ if (mDriverFD >= 0) {
+ int dummy = 0;
+#if defined(HAVE_ANDROID_OS)
+ status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+#else
+ status_t result = INVALID_OPERATION;
+#endif
+ if (result == 0) {
+ mManagesContexts = true;
+ } else if (result == -1) {
+ mBinderContextCheckFunc = NULL;
+ mBinderContextUserData = NULL;
+ LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
+ }
+ } else {
+ // If there is no driver, our only world is the local
+ // process so we can always become the context manager there.
+ mManagesContexts = true;
+ }
+ }
+ return mManagesContexts;
+}
+
+ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
+{
+ const size_t N=mHandleToObject.size();
+ if (N <= (size_t)handle) {
+ handle_entry e;
+ e.binder = NULL;
+ e.refs = NULL;
+ status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
+ if (err < NO_ERROR) return NULL;
+ }
+ return &mHandleToObject.editItemAt(handle);
+}
+
+sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
+{
+ sp<IBinder> result;
+
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ if (e != NULL) {
+ // We need to create a new BpBinder if there isn't currently one, OR we
+ // are unable to acquire a weak reference on this current one. See comment
+ // in getWeakProxyForHandle() for more info about this.
+ IBinder* b = e->binder;
+ if (b == NULL || !e->refs->attemptIncWeak(this)) {
+ b = new BpBinder(handle);
+ e->binder = b;
+ if (b) e->refs = b->getWeakRefs();
+ result = b;
+ } else {
+ // This little bit of nastyness is to allow us to add a primary
+ // reference to the remote proxy when this team doesn't have one
+ // but another team is sending the handle to us.
+ result.force_set(b);
+ e->refs->decWeak(this);
+ }
+ }
+
+ return result;
+}
+
+wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle)
+{
+ wp<IBinder> result;
+
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ if (e != NULL) {
+ // We need to create a new BpBinder if there isn't currently one, OR we
+ // are unable to acquire a weak reference on this current one. The
+ // attemptIncWeak() is safe because we know the BpBinder destructor will always
+ // call expungeHandle(), which acquires the same lock we are holding now.
+ // We need to do this because there is a race condition between someone
+ // releasing a reference on this BpBinder, and a new reference on its handle
+ // arriving from the driver.
+ IBinder* b = e->binder;
+ if (b == NULL || !e->refs->attemptIncWeak(this)) {
+ b = new BpBinder(handle);
+ result = b;
+ e->binder = b;
+ if (b) e->refs = b->getWeakRefs();
+ } else {
+ result = b;
+ e->refs->decWeak(this);
+ }
+ }
+
+ return result;
+}
+
+void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
+{
+ AutoMutex _l(mLock);
+
+ handle_entry* e = lookupHandleLocked(handle);
+
+ // This handle may have already been replaced with a new BpBinder
+ // (if someone failed the AttemptIncWeak() above); we don't want
+ // to overwrite it.
+ if (e && e->binder == binder) e->binder = NULL;
+}
+
+void ProcessState::setArgs(int argc, const char* const argv[])
+{
+ mArgC = argc;
+ mArgV = (const char **)argv;
+
+ mArgLen = 0;
+ for (int i=0; i<argc; i++) {
+ mArgLen += strlen(argv[i]) + 1;
+ }
+ mArgLen--;
+}
+
+int ProcessState::getArgC() const
+{
+ return mArgC;
+}
+
+const char* const* ProcessState::getArgV() const
+{
+ return mArgV;
+}
+
+void ProcessState::setArgV0(const char* txt)
+{
+ if (mArgV != NULL) {
+ strncpy((char*)mArgV[0], txt, mArgLen);
+ set_process_name(txt);
+ }
+}
+
+void ProcessState::spawnPooledThread(bool isMain)
+{
+ if (mThreadPoolStarted) {
+ int32_t s = android_atomic_add(1, &mThreadPoolSeq);
+ char buf[32];
+ sprintf(buf, "Binder Thread #%d", s);
+ LOGV("Spawning new pooled thread, name=%s\n", buf);
+ sp<Thread> t = new PoolThread(isMain);
+ t->run(buf);
+ }
+}
+
+static int open_driver()
+{
+ if (gSingleProcess) {
+ return -1;
+ }
+
+ int fd = open("/dev/binder", O_RDWR);
+ if (fd >= 0) {
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ int vers;
+#if defined(HAVE_ANDROID_OS)
+ status_t result = ioctl(fd, BINDER_VERSION, &vers);
+#else
+ status_t result = -1;
+ errno = EPERM;
+#endif
+ if (result == -1) {
+ LOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
+ close(fd);
+ fd = -1;
+ }
+ if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
+ LOGE("Binder driver protocol does not match user space protocol!");
+ close(fd);
+ fd = -1;
+ }
+#if defined(HAVE_ANDROID_OS)
+ size_t maxThreads = 15;
+ result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
+ if (result == -1) {
+ LOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
+ }
+#endif
+
+ } else {
+ LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
+ }
+ return fd;
+}
+
+ProcessState::ProcessState()
+ : mDriverFD(open_driver())
+ , mVMStart(MAP_FAILED)
+ , mManagesContexts(false)
+ , mBinderContextCheckFunc(NULL)
+ , mBinderContextUserData(NULL)
+ , mThreadPoolStarted(false)
+ , mThreadPoolSeq(1)
+{
+ if (mDriverFD >= 0) {
+ // XXX Ideally, there should be a specific define for whether we
+ // have mmap (or whether we could possibly have the kernel module
+ // availabla).
+#if !defined(HAVE_WIN32_IPC)
+ // mmap the binder, providing a chunk of virtual address space to receive transactions.
+ mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
+ if (mVMStart == MAP_FAILED) {
+ // *sigh*
+ LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
+ close(mDriverFD);
+ mDriverFD = -1;
+ }
+#else
+ mDriverFD = -1;
+#endif
+ }
+ if (mDriverFD < 0) {
+ // Need to run without the driver, starting our own thread pool.
+ }
+}
+
+ProcessState::~ProcessState()
+{
+}
+
+}; // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
new file mode 100644
index 0000000..12b0308
--- /dev/null
+++ b/libs/binder/Static.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// All static variables go here, to control initialization and
+// destruction order in the library.
+
+#include <private/binder/Static.h>
+
+#include <binder/IPCThreadState.h>
+#include <utils/Log.h>
+
+namespace android {
+
+// ------------ ProcessState.cpp
+
+Mutex gProcessMutex;
+sp<ProcessState> gProcess;
+
+class LibUtilsIPCtStatics
+{
+public:
+ LibUtilsIPCtStatics()
+ {
+ }
+
+ ~LibUtilsIPCtStatics()
+ {
+ IPCThreadState::shutdown();
+ }
+};
+
+static LibUtilsIPCtStatics gIPCStatics;
+
+// ------------ ServiceManager.cpp
+
+Mutex gDefaultServiceManagerLock;
+sp<IServiceManager> gDefaultServiceManager;
+sp<IPermissionController> gPermissionController;
+
+} // namespace android
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
new file mode 100644
index 0000000..bc466be
--- /dev/null
+++ b/libs/rs/Android.mk
@@ -0,0 +1,115 @@
+
+LOCAL_PATH:=$(call my-dir)
+
+
+# Build rsg-generator ====================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := rsg-generator
+
+# These symbols are normally defined by BUILD_XXX, but we need to define them
+# here so that local-intermediates-dir works.
+
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+intermediates := $(local-intermediates-dir)
+
+LOCAL_SRC_FILES:= \
+ spec.l \
+ rsg_generator.c
+
+include $(BUILD_HOST_EXECUTABLE)
+
+# TODO: This should go into build/core/config.mk
+RSG_GENERATOR:=$(LOCAL_BUILT_MODULE)
+
+
+
+# Build render script lib ====================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libRS
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+intermediates:= $(local-intermediates-dir)
+
+# Generate custom headers
+
+GEN := $(addprefix $(intermediates)/, \
+ rsgApiStructs.h \
+ rsgApiFuncDecl.h \
+ )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.h : $(LOCAL_PATH)/%.h.rsg
+ $(transform-generated-source)
+
+# used in jni/Android.mk
+rs_generated_source += $(GEN)
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# Generate custom source files
+
+GEN := $(addprefix $(intermediates)/, \
+ rsgApi.cpp \
+ rsgApiReplay.cpp \
+ )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
+ $(transform-generated-source)
+
+# used in jni/Android.mk
+rs_generated_source += $(GEN)
+
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# libRS needs libacc, which isn't 64-bit clean, and so can't be built
+# for the simulator on gHardy, and therefore libRS needs to be excluded
+# from the simulator as well.
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_SRC_FILES:= \
+ rsAdapter.cpp \
+ rsAllocation.cpp \
+ rsComponent.cpp \
+ rsContext.cpp \
+ rsDevice.cpp \
+ rsElement.cpp \
+ rsFileA3D.cpp \
+ rsLight.cpp \
+ rsLocklessFifo.cpp \
+ rsObjectBase.cpp \
+ rsMatrix.cpp \
+ rsMesh.cpp \
+ rsNoise.cpp \
+ rsProgram.cpp \
+ rsProgramFragment.cpp \
+ rsProgramFragmentStore.cpp \
+ rsProgramVertex.cpp \
+ rsSampler.cpp \
+ rsScript.cpp \
+ rsScriptC.cpp \
+ rsScriptC_Lib.cpp \
+ rsSimpleMesh.cpp \
+ rsThreadIO.cpp \
+ rsType.cpp \
+ rsTriangleMesh.cpp
+
+LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc
+LOCAL_LDLIBS := -lpthread -ldl
+LOCAL_MODULE:= libRS
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Include the subdirectories ====================
+include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\
+ java \
+ ))
+
+endif #simulator
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
new file mode 100644
index 0000000..1e24cd2
--- /dev/null
+++ b/libs/rs/RenderScript.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef RENDER_SCRIPT_H
+#define RENDER_SCRIPT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//////////////////////////////////////////////////////
+//
+
+typedef void * RsAdapter1D;
+typedef void * RsAdapter2D;
+typedef void * RsAllocation;
+typedef void * RsContext;
+typedef void * RsDevice;
+typedef void * RsElement;
+typedef void * RsFile;
+typedef void * RsSampler;
+typedef void * RsScript;
+typedef void * RsScriptBasicTemp;
+typedef void * RsTriangleMesh;
+typedef void * RsSimpleMesh;
+typedef void * RsType;
+typedef void * RsLight;
+
+typedef void * RsProgramVertex;
+typedef void * RsProgramFragment;
+typedef void * RsProgramFragmentStore;
+
+RsDevice rsDeviceCreate();
+void rsDeviceDestroy(RsDevice);
+
+RsContext rsContextCreate(RsDevice, void *, uint32_t version, bool useDepth);
+void rsContextDestroy(RsContext);
+void rsObjDestroyOOB(RsContext, void *);
+
+#define RS_MAX_TEXTURE 2
+
+enum RsDataType {
+ RS_TYPE_FLOAT,
+ RS_TYPE_UNSIGNED,
+ RS_TYPE_SIGNED
+};
+
+enum RsDataKind {
+ RS_KIND_USER,
+ RS_KIND_RED,
+ RS_KIND_GREEN,
+ RS_KIND_BLUE,
+ RS_KIND_ALPHA,
+ RS_KIND_LUMINANCE,
+ RS_KIND_INTENSITY,
+ RS_KIND_X,
+ RS_KIND_Y,
+ RS_KIND_Z,
+ RS_KIND_W,
+ RS_KIND_S,
+ RS_KIND_T,
+ RS_KIND_Q,
+ RS_KIND_R,
+ RS_KIND_NX,
+ RS_KIND_NY,
+ RS_KIND_NZ,
+ RS_KIND_INDEX,
+ RS_KIND_POINT_SIZE
+};
+
+enum RsElementPredefined {
+ RS_ELEMENT_USER_U8,
+ RS_ELEMENT_USER_I8,
+ RS_ELEMENT_USER_U16,
+ RS_ELEMENT_USER_I16,
+ RS_ELEMENT_USER_U32,
+ RS_ELEMENT_USER_I32,
+ RS_ELEMENT_USER_FLOAT,
+
+ RS_ELEMENT_A_8, // 7
+ RS_ELEMENT_RGB_565, // 8
+ RS_ELEMENT_RGBA_5551, // 9
+ RS_ELEMENT_RGBA_4444, // 10
+ RS_ELEMENT_RGB_888, // 11
+ RS_ELEMENT_RGBA_8888, // 12
+
+ RS_ELEMENT_INDEX_16, //13
+ RS_ELEMENT_INDEX_32,
+ RS_ELEMENT_XY_F32,
+ RS_ELEMENT_XYZ_F32,
+ RS_ELEMENT_ST_XY_F32,
+ RS_ELEMENT_ST_XYZ_F32,
+ RS_ELEMENT_NORM_XYZ_F32,
+ RS_ELEMENT_NORM_ST_XYZ_F32,
+};
+
+enum RsSamplerParam {
+ RS_SAMPLER_MIN_FILTER,
+ RS_SAMPLER_MAG_FILTER,
+ RS_SAMPLER_WRAP_S,
+ RS_SAMPLER_WRAP_T,
+ RS_SAMPLER_WRAP_R
+};
+
+enum RsSamplerValue {
+ RS_SAMPLER_NEAREST,
+ RS_SAMPLER_LINEAR,
+ RS_SAMPLER_LINEAR_MIP_LINEAR,
+ RS_SAMPLER_WRAP,
+ RS_SAMPLER_CLAMP
+};
+
+enum RsDimension {
+ RS_DIMENSION_X,
+ RS_DIMENSION_Y,
+ RS_DIMENSION_Z,
+ RS_DIMENSION_LOD,
+ RS_DIMENSION_FACE,
+
+ RS_DIMENSION_ARRAY_0 = 100,
+ RS_DIMENSION_ARRAY_1,
+ RS_DIMENSION_ARRAY_2,
+ RS_DIMENSION_ARRAY_3,
+ RS_DIMENSION_MAX = RS_DIMENSION_ARRAY_3
+};
+
+enum RsDepthFunc {
+ RS_DEPTH_FUNC_ALWAYS,
+ RS_DEPTH_FUNC_LESS,
+ RS_DEPTH_FUNC_LEQUAL,
+ RS_DEPTH_FUNC_GREATER,
+ RS_DEPTH_FUNC_GEQUAL,
+ RS_DEPTH_FUNC_EQUAL,
+ RS_DEPTH_FUNC_NOTEQUAL
+};
+
+enum RsBlendSrcFunc {
+ RS_BLEND_SRC_ZERO, // 0
+ RS_BLEND_SRC_ONE, // 1
+ RS_BLEND_SRC_DST_COLOR, // 2
+ RS_BLEND_SRC_ONE_MINUS_DST_COLOR, // 3
+ RS_BLEND_SRC_SRC_ALPHA, // 4
+ RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA, // 5
+ RS_BLEND_SRC_DST_ALPHA, // 6
+ RS_BLEND_SRC_ONE_MINUS_DST_ALPHA, // 7
+ RS_BLEND_SRC_SRC_ALPHA_SATURATE // 8
+};
+
+enum RsBlendDstFunc {
+ RS_BLEND_DST_ZERO, // 0
+ RS_BLEND_DST_ONE, // 1
+ RS_BLEND_DST_SRC_COLOR, // 2
+ RS_BLEND_DST_ONE_MINUS_SRC_COLOR, // 3
+ RS_BLEND_DST_SRC_ALPHA, // 4
+ RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, // 5
+ RS_BLEND_DST_DST_ALPHA, // 6
+ RS_BLEND_DST_ONE_MINUS_DST_ALPHA // 7
+};
+
+enum RsTexEnvMode {
+ RS_TEX_ENV_MODE_REPLACE,
+ RS_TEX_ENV_MODE_MODULATE,
+ RS_TEX_ENV_MODE_DECAL
+};
+
+enum RsPrimitive {
+ RS_PRIMITIVE_POINT,
+ RS_PRIMITIVE_LINE,
+ RS_PRIMITIVE_LINE_STRIP,
+ RS_PRIMITIVE_TRIANGLE,
+ RS_PRIMITIVE_TRIANGLE_STRIP,
+ RS_PRIMITIVE_TRIANGLE_FAN
+};
+
+
+#include "rsgApiFuncDecl.h"
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // RENDER_SCRIPT_H
+
+
+
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
new file mode 100644
index 0000000..5eb8912
--- /dev/null
+++ b/libs/rs/RenderScriptEnv.h
@@ -0,0 +1,33 @@
+#include <stdint.h>
+
+
+typedef void * RsAdapter1D;
+typedef void * RsAdapter2D;
+typedef void * RsAllocation;
+typedef void * RsContext;
+typedef void * RsDevice;
+typedef void * RsElement;
+typedef void * RsSampler;
+typedef void * RsScript;
+typedef void * RsScriptBasicTemp;
+typedef void * RsTriangleMesh;
+typedef void * RsSimpleMesh;
+typedef void * RsType;
+typedef void * RsProgramFragment;
+typedef void * RsProgramFragmentStore;
+typedef void * RsLight;
+
+
+typedef struct {
+ float m[16];
+} rsc_Matrix;
+
+
+typedef struct {
+ float v[4];
+} rsc_Vector4;
+
+#define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0
+#define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
+#define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
+
diff --git a/libs/rs/java/Android.mk b/libs/rs/java/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/libs/rs/java/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/libs/rs/java/Film/Android.mk b/libs/rs/java/Film/Android.mk
new file mode 100644
index 0000000..b7f98fc
--- /dev/null
+++ b/libs/rs/java/Film/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Film
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml
new file mode 100644
index 0000000..a5ce8a1
--- /dev/null
+++ b/libs/rs/java/Film/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.film">
+ <application android:label="Film">
+ <activity android:name="Film"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Black.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/rs/java/Film/res/drawable/p01.png b/libs/rs/java/Film/res/drawable/p01.png
new file mode 100644
index 0000000..a9b9bdb
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p01.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p02.png b/libs/rs/java/Film/res/drawable/p02.png
new file mode 100644
index 0000000..8162c82
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p02.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p03.png b/libs/rs/java/Film/res/drawable/p03.png
new file mode 100644
index 0000000..e3e26c0
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p03.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p04.png b/libs/rs/java/Film/res/drawable/p04.png
new file mode 100644
index 0000000..daee603
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p04.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p05.png b/libs/rs/java/Film/res/drawable/p05.png
new file mode 100644
index 0000000..fac5248
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p05.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p06.png b/libs/rs/java/Film/res/drawable/p06.png
new file mode 100644
index 0000000..3b51261
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p06.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p07.png b/libs/rs/java/Film/res/drawable/p07.png
new file mode 100644
index 0000000..d8bd938
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p07.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p08.png b/libs/rs/java/Film/res/drawable/p08.png
new file mode 100644
index 0000000..ef175e8
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p08.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p09.png b/libs/rs/java/Film/res/drawable/p09.png
new file mode 100644
index 0000000..7bf3874
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p09.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p10.png b/libs/rs/java/Film/res/drawable/p10.png
new file mode 100644
index 0000000..908827d
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p10.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p11.png b/libs/rs/java/Film/res/drawable/p11.png
new file mode 100644
index 0000000..1289f71
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p11.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p12.png b/libs/rs/java/Film/res/drawable/p12.png
new file mode 100644
index 0000000..e1af16a
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p12.png
Binary files differ
diff --git a/libs/rs/java/Film/res/drawable/p13.png b/libs/rs/java/Film/res/drawable/p13.png
new file mode 100644
index 0000000..d08bcbe
--- /dev/null
+++ b/libs/rs/java/Film/res/drawable/p13.png
Binary files differ
diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c
new file mode 100644
index 0000000..3bd9496
--- /dev/null
+++ b/libs/rs/java/Film/res/raw/filmimage.c
@@ -0,0 +1,110 @@
+// Fountain test script
+
+#pragma version(1)
+#pragma stateVertex(orthoWindow)
+#pragma stateRaster(flat)
+#pragma stateFragment(PgmFragBackground)
+#pragma stateFragmentStore(MyBlend)
+
+
+int main(void* con, int ft, int launchID) {
+ int count, touch, x, y, rate, maxLife, lifeShift;
+ int life;
+ int ct, ct2;
+ int newPart;
+ int drawCount;
+ int dx, dy, idx;
+ int posx,posy;
+ int c;
+ int srcIdx;
+ int dstIdx;
+
+ count = loadI32(con, 0, 1);
+ touch = loadI32(con, 0, 2);
+ x = loadI32(con, 0, 3);
+ y = loadI32(con, 0, 4);
+
+ rate = 4;
+ maxLife = (count / rate) - 1;
+ lifeShift = 0;
+ {
+ life = maxLife;
+ while (life > 255) {
+ life = life >> 1;
+ lifeShift ++;
+ }
+ }
+
+ drawRect(con, 0, 256, 0, 512);
+ contextBindProgramFragment(con, NAMED_PgmFragParts);
+
+ if (touch) {
+ newPart = loadI32(con, 2, 0);
+ for (ct2=0; ct2<rate; ct2++) {
+ dx = scriptRand(con, 0x10000) - 0x8000;
+ dy = scriptRand(con, 0x10000) - 0x8000;
+
+ idx = newPart * 5 + 1;
+ storeI32(con, 2, idx, dx);
+ storeI32(con, 2, idx + 1, dy);
+ storeI32(con, 2, idx + 2, maxLife);
+ storeI32(con, 2, idx + 3, x << 16);
+ storeI32(con, 2, idx + 4, y << 16);
+
+ newPart++;
+ if (newPart >= count) {
+ newPart = 0;
+ }
+ }
+ storeI32(con, 2, 0, newPart);
+ }
+
+ drawCount = 0;
+ for (ct=0; ct < count; ct++) {
+ srcIdx = ct * 5 + 1;
+
+ dx = loadI32(con, 2, srcIdx);
+ dy = loadI32(con, 2, srcIdx + 1);
+ life = loadI32(con, 2, srcIdx + 2);
+ posx = loadI32(con, 2, srcIdx + 3);
+ posy = loadI32(con, 2, srcIdx + 4);
+
+ if (life) {
+ if (posy < (480 << 16)) {
+ dstIdx = drawCount * 9;
+ c = 0xffafcf | ((life >> lifeShift) << 24);
+
+ storeU32(con, 1, dstIdx, c);
+ storeI32(con, 1, dstIdx + 1, posx);
+ storeI32(con, 1, dstIdx + 2, posy);
+
+ storeU32(con, 1, dstIdx + 3, c);
+ storeI32(con, 1, dstIdx + 4, posx + 0x10000);
+ storeI32(con, 1, dstIdx + 5, posy + dy * 4);
+
+ storeU32(con, 1, dstIdx + 6, c);
+ storeI32(con, 1, dstIdx + 7, posx - 0x10000);
+ storeI32(con, 1, dstIdx + 8, posy + dy * 4);
+ drawCount ++;
+ } else {
+ if (dy > 0) {
+ dy = (-dy) >> 1;
+ }
+ }
+
+ posx = posx + dx;
+ posy = posy + dy;
+ dy = dy + 0x400;
+ life --;
+
+ //storeI32(con, 2, srcIdx, dx);
+ storeI32(con, 2, srcIdx + 1, dy);
+ storeI32(con, 2, srcIdx + 2, life);
+ storeI32(con, 2, srcIdx + 3, posx);
+ storeI32(con, 2, srcIdx + 4, posy);
+ }
+ }
+
+ drawTriangleArray(con, NAMED_PartBuffer, drawCount);
+ return 1;
+}
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
new file mode 100644
index 0000000..8fbfee1
--- /dev/null
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -0,0 +1,94 @@
+// Fountain test script
+
+#pragma version(1)
+#pragma stateVertex(PVBackground)
+#pragma stateFragment(PFBackground)
+#pragma stateFragmentStore(PSBackground)
+
+#define STATE_TRIANGLE_OFFSET_COUNT 0
+#define STATE_LAST_FOCUS 1
+
+
+// The script enviroment has 3 env allocations.
+// bank0: (r) The enviroment structure
+// bank1: (r) The position information
+// bank2: (rw) The temporary texture state
+
+int lastFocus;
+
+int main(int index)
+{
+ float mat1[16];
+
+ float trans = Pos->translate;
+ float rot = Pos->rotate;
+
+ matrixLoadScale(mat1, 2.f, 2.f, 2.f);
+ matrixTranslate(mat1, 0.f, 0.f, trans);
+ matrixRotate(mat1, 90.f, 0.f, 0.f, 1.f);
+ matrixRotate(mat1, rot, 1.f, 0.f, 0.f);
+ vpLoadModelMatrix(mat1);
+
+ // Draw the lighting effect in the strip and fill the Z buffer.
+ drawSimpleMesh(NAMED_mesh);
+
+ // Start of images.
+ bindProgramFragmentStore(NAMED_PSImages);
+ bindProgramFragment(NAMED_PFImages);
+ bindProgramVertex(NAMED_PVImages);
+
+ float focusPos = Pos->focus;
+ int focusID = 0;
+ int lastFocusID = loadI32(2, STATE_LAST_FOCUS);
+ int imgCount = 13;
+
+ if (trans > (-.3f)) {
+ focusID = -1.0f - focusPos;
+ if (focusID >= imgCount) {
+ focusID = -1;
+ }
+ } else {
+ focusID = -1;
+ }
+
+ /*
+ if (focusID != lastFocusID) {
+ if (lastFocusID >= 0) {
+ uploadToTexture(con, env->tex[lastFocusID], 1);
+ }
+ if (focusID >= 0) {
+ uploadToTexture(con, env->tex[focusID], 0);
+ }
+ }
+ */
+ lastFocus = focusID;
+
+ int triangleOffsetsCount = Pos->triangleOffsetCount;
+
+ int imgId = 0;
+ for (imgId=1; imgId <= imgCount; imgId++) {
+ float pos = focusPos + imgId + 0.4f;
+ int offset = (int)floorf(pos * 2.f);
+ pos = pos - 0.75f;
+
+ offset = offset + triangleOffsetsCount / 2;
+ if (!((offset < 0) || (offset >= triangleOffsetsCount))) {
+ int start = offset -2;
+ int end = offset + 2;
+
+ if (start < 0) {
+ start = 0;
+ }
+ if (end >= triangleOffsetsCount) {
+ end = triangleOffsetsCount-1;
+ }
+
+ bindTexture(NAMED_PFImages, 0, loadI32(0, imgId - 1));
+ matrixLoadTranslate(mat1, -pos - loadF(5, triangleOffsetsCount / 2), 0, 0);
+ vpLoadTextureMatrix(mat1);
+ drawSimpleMeshRange(NAMED_mesh, loadI32(4, start), (loadI32(4, end) - loadI32(4, start)));
+ }
+ }
+ return 0;
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java
new file mode 100644
index 0000000..6e99816
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/Film.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Film extends Activity {
+ //EventListener mListener = new EventListener();
+
+ private static final String LOG_TAG = "libRS_jni";
+ private static final boolean DEBUG = false;
+ private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+ private FilmView mView;
+
+ // get the current looper (from your Activity UI thread for instance
+
+
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mView = new FilmView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mView.onPause();
+
+ Runtime.getRuntime().exit(0);
+ }
+
+
+ static void log(String message) {
+ if (LOG_ENABLED) {
+ Log.v(LOG_TAG, message);
+ }
+ }
+
+
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
new file mode 100644
index 0000000..cee827b
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import java.io.Writer;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.util.Log;
+
+import android.renderscript.*;
+
+public class FilmRS {
+ class StripPosition {
+ public float translate;
+ public float rotate;
+ public float focus;
+ public int triangleOffsetCount;
+ }
+ StripPosition mPos = new StripPosition();
+
+
+ private final int STATE_LAST_FOCUS = 1;
+
+ public FilmRS() {
+ }
+
+ public void init(RenderScript rs, Resources res, int width, int height) {
+ mRS = rs;
+ mRes = res;
+ initRS();
+ }
+
+ public void setFilmStripPosition(int x, int y)
+ {
+ if (x < 50) {
+ x = 50;
+ }
+ if (x > 270) {
+ x = 270;
+ }
+
+ float anim = ((float)x-50) / 270.f;
+ mPos.translate = 2f * anim + 0.5f; // translation
+ mPos.rotate = (anim * 40); // rotation
+ mPos.focus = ((float)y) / 16.f - 10.f; // focusPos
+ mPos.triangleOffsetCount = mFSM.mTriangleOffsetsCount;
+ mAllocPos.data(mPos);
+ }
+
+
+ private Resources mRes;
+ private RenderScript mRS;
+ private Script mScriptStrip;
+ private Script mScriptImage;
+ private Sampler mSampler;
+ private ProgramStore mPSBackground;
+ private ProgramStore mPSImages;
+ private ProgramFragment mPFBackground;
+ private ProgramFragment mPFImages;
+ private ProgramVertex mPVBackground;
+ private ProgramVertex mPVImages;
+ private ProgramVertex.MatrixAllocation mPVA;
+ private Type mStripPositionType;
+
+ private Allocation mImages[];
+ private Allocation mAllocIDs;
+ private Allocation mAllocPos;
+ private Allocation mAllocState;
+ private Allocation mAllocPV;
+ private Allocation mAllocOffsetsTex;
+ private Allocation mAllocOffsets;
+
+ private SimpleMesh mMesh;
+ private Light mLight;
+
+ private FilmStripMesh mFSM;
+
+ private int[] mBufferIDs;
+ private float[] mBufferPos = new float[3];
+ private int[] mBufferState;
+
+ private void initPFS() {
+ ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null);
+
+ b.setDepthFunc(ProgramStore.DepthFunc.LESS);
+ b.setDitherEnable(true);
+ b.setDepthMask(true);
+ mPSBackground = b.create();
+ mPSBackground.setName("PSBackground");
+
+ b.setDepthFunc(ProgramStore.DepthFunc.EQUAL);
+ b.setDitherEnable(false);
+ b.setDepthMask(false);
+ b.setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
+ ProgramStore.BlendDstFunc.ONE);
+ mPSImages = b.create();
+ mPSImages.setName("PSImages");
+ }
+
+ private void initPF() {
+ Sampler.Builder bs = new Sampler.Builder(mRS);
+ bs.setMin(Sampler.Value.LINEAR);//_MIP_LINEAR);
+ bs.setMag(Sampler.Value.LINEAR);
+ bs.setWrapS(Sampler.Value.CLAMP);
+ bs.setWrapT(Sampler.Value.WRAP);
+ mSampler = bs.create();
+
+ ProgramFragment.Builder b = new ProgramFragment.Builder(mRS, null, null);
+
+ mPFBackground = b.create();
+ mPFBackground.setName("PFBackground");
+
+ b.setTexEnable(true, 0);
+ b.setTexEnvMode(ProgramFragment.EnvMode.REPLACE, 0);
+ mPFImages = b.create();
+ mPFImages.bindSampler(mSampler, 0);
+ mPFImages.setName("PFImages");
+ }
+
+ private void initPV() {
+ mLight = (new Light.Builder(mRS)).create();
+ mLight.setPosition(0, -0.5f, -1.0f);
+
+ ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+ pvb.addLight(mLight);
+ mPVBackground = pvb.create();
+ mPVBackground.setName("PVBackground");
+
+ pvb = new ProgramVertex.Builder(mRS, null, null);
+ pvb.setTextureMatrixEnable(true);
+ mPVImages = pvb.create();
+ mPVImages.setName("PVImages");
+ }
+
+ private void loadImages() {
+ mBufferIDs = new int[13];
+ mImages = new Allocation[13];
+ mAllocIDs = Allocation.createSized(mRS,
+ Element.USER_FLOAT, mBufferIDs.length);
+
+ Element ie = Element.RGB_565;
+ mImages[0] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p01, ie, true);
+ mImages[1] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p02, ie, true);
+ mImages[2] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p03, ie, true);
+ mImages[3] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p04, ie, true);
+ mImages[4] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p05, ie, true);
+ mImages[5] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p06, ie, true);
+ mImages[6] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p07, ie, true);
+ mImages[7] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p08, ie, true);
+ mImages[8] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p09, ie, true);
+ mImages[9] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p10, ie, true);
+ mImages[10] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p11, ie, true);
+ mImages[11] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p12, ie, true);
+ mImages[12] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p13, ie, true);
+
+ int black[] = new int[1024];
+ for(int ct=0; ct < mImages.length; ct++) {
+ Allocation.Adapter2D a = mImages[ct].createAdapter2D();
+
+ int size = 512;
+ int mip = 0;
+ while(size >= 2) {
+ a.subData(0, 0, 2, size, black);
+ a.subData(size-2, 0, 2, size, black);
+ a.subData(0, 0, size, 2, black);
+ a.subData(0, size-2, size, 2, black);
+ size >>= 1;
+ mip++;
+ a.setConstraint(Dimension.LOD, mip);
+ }
+
+ mImages[ct].uploadToTexture(1);
+ mBufferIDs[ct] = mImages[ct].getID();
+ }
+ mAllocIDs.data(mBufferIDs);
+ }
+
+ private void initState()
+ {
+ mBufferState = new int[10];
+ mAllocState = Allocation.createSized(mRS,
+ Element.USER_FLOAT, mBufferState.length);
+ mBufferState[STATE_LAST_FOCUS] = -1;
+ mAllocState.data(mBufferState);
+ }
+
+ private void initRS() {
+ mFSM = new FilmStripMesh();
+ mMesh = mFSM.init(mRS);
+ mMesh.setName("mesh");
+
+ initPFS();
+ initPF();
+ initPV();
+
+ Log.e("rs", "Done loading named");
+
+ mStripPositionType = Type.createFromClass(mRS, StripPosition.class, 1);
+
+ ScriptC.Builder sb = new ScriptC.Builder(mRS);
+ sb.setScript(mRes, R.raw.filmstrip);
+ sb.setRoot(true);
+ sb.setType(mStripPositionType, "Pos", 1);
+ mScriptStrip = sb.create();
+ mScriptStrip.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ mAllocPos = Allocation.createTyped(mRS, mStripPositionType);
+
+ loadImages();
+ initState();
+
+ mPVA = new ProgramVertex.MatrixAllocation(mRS);
+ mPVBackground.bindAllocation(mPVA);
+ mPVImages.bindAllocation(mPVA);
+ mPVA.setupProjectionNormalized(320, 480);
+
+
+ mScriptStrip.bindAllocation(mAllocIDs, 0);
+ mScriptStrip.bindAllocation(mAllocPos, 1);
+ mScriptStrip.bindAllocation(mAllocState, 2);
+ mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
+
+
+ mAllocOffsets = Allocation.createSized(mRS,
+ Element.USER_I32, mFSM.mTriangleOffsets.length);
+ mAllocOffsets.data(mFSM.mTriangleOffsets);
+ mScriptStrip.bindAllocation(mAllocOffsets, 4);
+
+ mAllocOffsetsTex = Allocation.createSized(mRS,
+ Element.USER_FLOAT, mFSM.mTriangleOffsetsTex.length);
+ mAllocOffsetsTex.data(mFSM.mTriangleOffsetsTex);
+ mScriptStrip.bindAllocation(mAllocOffsetsTex, 5);
+
+ setFilmStripPosition(0, 0);
+ mRS.contextBindRootScript(mScriptStrip);
+ }
+}
+
+
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
new file mode 100644
index 0000000..64aac26
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.film;
+
+import java.io.Writer;
+import java.lang.Math;
+import android.util.Log;
+
+import android.renderscript.RenderScript;
+import android.renderscript.SimpleMesh;
+
+
+class FilmStripMesh {
+
+ class Vertex {
+ float nx;
+ float ny;
+ float nz;
+ float s;
+ float t;
+ float x;
+ float y;
+ float z;
+
+ Vertex() {
+ nx = 0;
+ ny = 0;
+ nz = 0;
+ s = 0;
+ t = 0;
+ x = 0;
+ y = 0;
+ z = 0;
+ }
+
+ void xyz(float _x, float _y, float _z) {
+ x = _x;
+ y = _y;
+ z = _z;
+ }
+
+ void nxyz(float _x, float _y, float _z) {
+ nx = _x;
+ ny = _y;
+ nz = _z;
+ }
+
+ void st(float _s, float _t) {
+ s = _s;
+ t = _t;
+ }
+
+ void computeNorm(Vertex v1, Vertex v2) {
+ float dx = v1.x - v2.x;
+ float dy = v1.y - v2.y;
+ float dz = v1.z - v2.z;
+ float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz);
+ dx /= len;
+ dy /= len;
+ dz /= len;
+
+ nx = dx * dz;
+ ny = dy * dz;
+ nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy);
+
+ len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz);
+ nx /= len;
+ ny /= len;
+ nz /= len;
+ }
+ }
+
+ int[] mTriangleOffsets;
+ float[] mTriangleOffsetsTex;
+ int mTriangleOffsetsCount;
+
+ SimpleMesh init(RenderScript rs)
+ {
+ float vtx[] = new float[] {
+ 60.431003f, 124.482050f,
+ 60.862074f, 120.872604f,
+ 61.705303f, 117.336662f,
+ 62.949505f, 113.921127f,
+ 64.578177f, 110.671304f,
+ 66.569716f, 107.630302f,
+ 68.897703f, 104.838457f,
+ 71.531259f, 102.332803f,
+ 74.435452f, 100.146577f,
+ 77.571757f, 98.308777f,
+ 80.898574f, 96.843781f,
+ 84.371773f, 95.771023f,
+ 87.945283f, 95.104731f,
+ 98.958994f, 95.267098f,
+ 109.489523f, 98.497596f,
+ 118.699582f, 104.539366f,
+ 125.856872f, 112.912022f,
+ 130.392311f, 122.949849f,
+ 131.945283f, 133.854731f,
+ 130.392311f, 144.759613f,
+ 125.856872f, 154.797439f,
+ 118.699582f, 163.170096f,
+ 109.489523f, 169.211866f,
+ 98.958994f, 172.442364f,
+ 87.945283f, 172.604731f,
+ 72.507313f, 172.672927f,
+ 57.678920f, 168.377071f,
+ 44.668135f, 160.067134f,
+ 34.534908f, 148.420104f,
+ 28.104767f, 134.384831f,
+ 25.901557f, 119.104731f,
+ 28.104767f, 103.824631f,
+ 34.534908f, 89.789358f,
+ 44.668135f, 78.142327f,
+ 57.678920f, 69.832390f,
+ 72.507313f, 65.536534f,
+ 87.945283f, 65.604731f,
+ 106.918117f, 65.688542f,
+ 125.141795f, 60.409056f,
+ 141.131686f, 50.196376f,
+ 153.585137f, 35.882502f,
+ 161.487600f, 18.633545f,
+ 164.195283f, -0.145269f,
+ 161.487600f, -18.924084f,
+ 153.585137f, -36.173040f,
+ 141.131686f, -50.486914f,
+ 125.141795f, -60.699594f,
+ 106.918117f, -65.979081f,
+ 87.945283f, -65.895269f,
+ 80f, -65.895269f,
+ 60f, -65.895269f,
+ 40f, -65.895269f,
+ 20f, -65.895269f,
+ 0f, -65.895269f,
+ -20f, -65.895269f,
+ -40f, -65.895269f,
+ -60f, -65.895269f,
+ -80f, -65.895269f,
+ -87.945283f, -65.895269f,
+ -106.918117f, -65.979081f,
+ -125.141795f, -60.699594f,
+ -141.131686f, -50.486914f,
+ -153.585137f, -36.173040f,
+ -161.487600f, -18.924084f,
+ -164.195283f, -0.145269f,
+ -161.487600f, 18.633545f,
+ -153.585137f, 35.882502f,
+ -141.131686f, 50.196376f,
+ -125.141795f, 60.409056f,
+ -106.918117f, 65.688542f,
+ -87.945283f, 65.604731f,
+ -72.507313f, 65.536534f,
+ -57.678920f, 69.832390f,
+ -44.668135f, 78.142327f,
+ -34.534908f, 89.789358f,
+ -28.104767f, 103.824631f,
+ -25.901557f, 119.104731f,
+ -28.104767f, 134.384831f,
+ -34.534908f, 148.420104f,
+ -44.668135f, 160.067134f,
+ -57.678920f, 168.377071f,
+ -72.507313f, 172.672927f,
+ -87.945283f, 172.604731f,
+ -98.958994f, 172.442364f,
+ -109.489523f, 169.211866f,
+ -118.699582f, 163.170096f,
+ -125.856872f, 154.797439f,
+ -130.392311f, 144.759613f,
+ -131.945283f, 133.854731f,
+ -130.392311f, 122.949849f,
+ -125.856872f, 112.912022f,
+ -118.699582f, 104.539366f,
+ -109.489523f, 98.497596f,
+ -98.958994f, 95.267098f,
+ -87.945283f, 95.104731f,
+ -84.371773f, 95.771023f,
+ -80.898574f, 96.843781f,
+ -77.571757f, 98.308777f,
+ -74.435452f, 100.146577f,
+ -71.531259f, 102.332803f,
+ -68.897703f, 104.838457f,
+ -66.569716f, 107.630302f,
+ -64.578177f, 110.671304f,
+ -62.949505f, 113.921127f,
+ -61.705303f, 117.336662f,
+ -60.862074f, 120.872604f,
+ -60.431003f, 124.482050f
+ };
+
+
+ mTriangleOffsets = new int[64];
+ mTriangleOffsetsTex = new float[64];
+
+ mTriangleOffsets[0] = 0;
+ mTriangleOffsetsCount = 1;
+
+ Vertex t = new Vertex();
+ t.nxyz(1, 0, 0);
+ int count = vtx.length / 2;
+
+ SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder(rs, 3, true, true);
+
+ float runningS = 0;
+ for (int ct=0; ct < (count-1); ct++) {
+ t.x = -vtx[ct*2] / 100.f;
+ t.z = vtx[ct*2+1] / 100.f;
+ t.s = runningS;
+ t.nx = (vtx[ct*2+3] - vtx[ct*2 +1]);
+ t.ny = (vtx[ct*2+2] - vtx[ct*2 ]);
+ float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny);
+ runningS += len / 100;
+ t.nx /= len;
+ t.ny /= len;
+ t.y = -0.5f;
+ t.t = 0;
+ tm.add_XYZ_ST_NORM(t.x, t.y, t.z, t.s, t.t, t.nx, t.ny, t.nz);
+ //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t);
+ t.y = .5f;
+ t.t = 1;
+ tm.add_XYZ_ST_NORM(t.x, t.y, t.z, t.s, t.t, t.nx, t.ny, t.nz);
+ //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t);
+
+ if((runningS*2) > mTriangleOffsetsCount) {
+ mTriangleOffsets[mTriangleOffsetsCount] = ct*2 * 3;
+ mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s;
+ mTriangleOffsetsCount ++;
+ }
+ }
+
+ count = (count * 2 - 2);
+ for (int ct=0; ct < (count-2); ct+= 2) {
+ tm.addTriangle(ct, ct+1, ct+2);
+ tm.addTriangle(ct+1, ct+3, ct+2);
+ }
+ return tm.create();
+ }
+
+
+}
+
diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java
new file mode 100644
index 0000000..1c5b2bc
--- /dev/null
+++ b/libs/rs/java/Film/src/com/android/film/FilmView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.film;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FilmView extends RSSurfaceView {
+
+ public FilmView(Context context) {
+ super(context);
+
+ //setFocusable(true);
+ }
+
+ private RenderScript mRS;
+ private FilmRS mRender;
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+
+ mRS = createRenderScript(true);
+ mRender = new FilmRS();
+ mRender.init(mRS, getResources(), w, h);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ // break point at here
+ // this method doesn't work when 'extends View' include 'extends ScrollView'.
+ return super.onKeyDown(keyCode, event);
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev)
+ {
+ boolean ret = true;
+ int act = ev.getAction();
+ if (act == ev.ACTION_UP) {
+ ret = false;
+ }
+ mRender.setFilmStripPosition((int)ev.getX(), (int)ev.getY() / 5);
+ return ret;
+ }
+}
+
+
diff --git a/libs/rs/java/Fountain/Android.mk b/libs/rs/java/Fountain/Android.mk
new file mode 100644
index 0000000..b6a9f10
--- /dev/null
+++ b/libs/rs/java/Fountain/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Fountain
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml
new file mode 100644
index 0000000..dd0e428
--- /dev/null
+++ b/libs/rs/java/Fountain/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.fountain">
+ <application android:label="Fountain">
+ <activity android:name="Fountain"
+ android:theme="@android:style/Theme.Translucent">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
new file mode 100755
index 0000000..e91bfb4
--- /dev/null
+++ b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png
Binary files differ
diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c
new file mode 100644
index 0000000..36516c2
--- /dev/null
+++ b/libs/rs/java/Fountain/res/raw/fountain.c
@@ -0,0 +1,55 @@
+// Fountain test script
+#pragma version(1)
+#pragma stateVertex(default)
+#pragma stateFragment(default)
+#pragma stateFragmentStore(default)
+
+int newPart = 0;
+
+int main(int launchID) {
+ int ct;
+ int count = Control->count;
+ int rate = Control->rate;
+ float height = getHeight();
+ struct point_s * p = (struct point_s *)point;
+
+ if (rate) {
+ float rMax = ((float)rate) * 0.005f;
+ int x = Control->x;
+ int y = Control->y;
+ char r = Control->r * 255.f;
+ char g = Control->g * 255.f;
+ char b = Control->b * 255.f;
+ char a = 0xf0;
+
+ while (rate--) {
+ vec2Rand((float *)(p + newPart), rMax);
+ p[newPart].x = x;
+ p[newPart].y = y;
+ p[newPart].r = r;
+ p[newPart].g = g;
+ p[newPart].b = b;
+ p[newPart].a = a;
+ newPart++;
+ if (newPart >= count) {
+ newPart = 0;
+ }
+ }
+ }
+
+ for (ct=0; ct < count; ct++) {
+ float dy = p->dy + 0.15f;
+ float posy = p->y + dy;
+ if ((posy > height) && (dy > 0)) {
+ dy *= -0.3f;
+ }
+ p->dy = dy;
+ p->x += p->dx;
+ p->y = posy;
+ p++;
+ }
+
+ uploadToBufferObject(NAMED_PartBuffer);
+ drawSimpleMesh(NAMED_PartMesh);
+ return 1;
+}
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
new file mode 100644
index 0000000..58c78fa
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Fountain extends Activity {
+ //EventListener mListener = new EventListener();
+
+ private static final String LOG_TAG = "libRS_jni";
+ private static final boolean DEBUG = false;
+ private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+ private FountainView mView;
+
+ // get the current looper (from your Activity UI thread for instance
+
+
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mView = new FountainView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mView.onPause();
+
+ Runtime.getRuntime().exit(0);
+ }
+
+
+ static void log(String message) {
+ if (LOG_ENABLED) {
+ Log.v(LOG_TAG, message);
+ }
+ }
+
+
+}
+
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
new file mode 100644
index 0000000..f4f9b0c
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import android.content.res.Resources;
+import android.renderscript.*;
+import android.util.Log;
+
+
+public class FountainRS {
+ public static final int PART_COUNT = 20000;
+
+ static class SomeData {
+ public int x;
+ public int y;
+ public int rate;
+ public int count;
+ public float r;
+ public float g;
+ public float b;
+ }
+
+ public FountainRS() {
+ }
+
+ public void init(RenderScript rs, Resources res, int width, int height) {
+ mRS = rs;
+ mRes = res;
+ initRS();
+ }
+
+ public void newTouchPosition(int x, int y, int rate) {
+ if (mSD.rate == 0) {
+ mSD.r = ((x & 0x1) != 0) ? 0.f : 1.f;
+ mSD.g = ((x & 0x2) != 0) ? 0.f : 1.f;
+ mSD.b = ((x & 0x4) != 0) ? 0.f : 1.f;
+ if ((mSD.r + mSD.g + mSD.b) < 0.9f) {
+ mSD.r = 0.8f;
+ mSD.g = 0.5f;
+ mSD.b = 1.f;
+ }
+ }
+ mSD.rate = rate;
+ mSD.x = x;
+ mSD.y = y;
+ mIntAlloc.data(mSD);
+ }
+
+
+ /////////////////////////////////////////
+
+ private Resources mRes;
+
+ private RenderScript mRS;
+ private Allocation mIntAlloc;
+ private SimpleMesh mSM;
+ private SomeData mSD;
+ private Type mSDType;
+
+ private void initRS() {
+ mSD = new SomeData();
+ mSDType = Type.createFromClass(mRS, SomeData.class, 1, "SomeData");
+ mIntAlloc = Allocation.createTyped(mRS, mSDType);
+ mSD.count = PART_COUNT;
+ mIntAlloc.data(mSD);
+
+ Element.Builder eb = new Element.Builder(mRS);
+ eb.addFloat(Element.DataKind.USER, "dx");
+ eb.addFloat(Element.DataKind.USER, "dy");
+ eb.addFloatXY("");
+ eb.addUNorm8RGBA("");
+ Element primElement = eb.create();
+
+
+ SimpleMesh.Builder smb = new SimpleMesh.Builder(mRS);
+ int vtxSlot = smb.addVertexType(primElement, PART_COUNT);
+ smb.setPrimitive(Primitive.POINT);
+ mSM = smb.create();
+ mSM.setName("PartMesh");
+
+ Allocation partAlloc = mSM.createVertexAllocation(vtxSlot);
+ partAlloc.setName("PartBuffer");
+ mSM.bindVertexAllocation(partAlloc, 0);
+
+ // All setup of named objects should be done by this point
+ // because we are about to compile the script.
+ ScriptC.Builder sb = new ScriptC.Builder(mRS);
+ sb.setScript(mRes, R.raw.fountain);
+ sb.setRoot(true);
+ sb.setType(mSDType, "Control", 0);
+ sb.setType(mSM.getVertexType(0), "point", 1);
+ Script script = sb.create();
+ script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ script.bindAllocation(mIntAlloc, 0);
+ script.bindAllocation(partAlloc, 1);
+ mRS.contextBindRootScript(script);
+ }
+
+}
+
+
diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
new file mode 100644
index 0000000..7826161
--- /dev/null
+++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fountain;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+public class FountainView extends RSSurfaceView {
+
+ public FountainView(Context context) {
+ super(context);
+
+ //setFocusable(true);
+ }
+
+ private RenderScript mRS;
+ private FountainRS mRender;
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+
+ mRS = createRenderScript(false);
+ mRender = new FountainRS();
+ mRender.init(mRS, getResources(), w, h);
+ }
+
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev)
+ {
+ int act = ev.getAction();
+ if (act == ev.ACTION_UP) {
+ mRender.newTouchPosition(0, 0, 0);
+ return false;
+ }
+ float rate = (ev.getPressure() * 50.f);
+ rate *= rate;
+ if(rate > 2000.f) {
+ rate = 2000.f;
+ }
+ mRender.newTouchPosition((int)ev.getX(), (int)ev.getY(), (int)rate);
+ return true;
+ }
+}
+
+
diff --git a/libs/rs/java/Rollo/Android.mk b/libs/rs/java/Rollo/Android.mk
new file mode 100644
index 0000000..5a4957c
--- /dev/null
+++ b/libs/rs/java/Rollo/Android.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript
+
+LOCAL_PACKAGE_NAME := Rollo
+
+include $(BUILD_PACKAGE)
diff --git a/libs/rs/java/Rollo/AndroidManifest.xml b/libs/rs/java/Rollo/AndroidManifest.xml
new file mode 100644
index 0000000..127a140
--- /dev/null
+++ b/libs/rs/java/Rollo/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.rollo">
+ <application android:label="Rollo">
+ <activity android:name="Rollo"
+ android:theme="@android:style/Theme.Translucent">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/libs/rs/java/Rollo/res/raw/browser.png b/libs/rs/java/Rollo/res/raw/browser.png
new file mode 100644
index 0000000..513f0be
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/browser.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/calendar.png b/libs/rs/java/Rollo/res/raw/calendar.png
new file mode 100644
index 0000000..030ae73
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/calendar.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/g1155.png b/libs/rs/java/Rollo/res/raw/g1155.png
new file mode 100644
index 0000000..68e1843
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/g1155.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/g2140.png b/libs/rs/java/Rollo/res/raw/g2140.png
new file mode 100644
index 0000000..8c4e853
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/g2140.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/maps.png b/libs/rs/java/Rollo/res/raw/maps.png
new file mode 100644
index 0000000..fd5fc39
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/maps.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/market.png b/libs/rs/java/Rollo/res/raw/market.png
new file mode 100644
index 0000000..83b6910
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/market.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path1920.png b/libs/rs/java/Rollo/res/raw/path1920.png
new file mode 100644
index 0000000..3510665
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path1920.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path1927.png b/libs/rs/java/Rollo/res/raw/path1927.png
new file mode 100644
index 0000000..fccc846
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path1927.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path3099.png b/libs/rs/java/Rollo/res/raw/path3099.png
new file mode 100644
index 0000000..527ebf6
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path3099.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path3950.png b/libs/rs/java/Rollo/res/raw/path3950.png
new file mode 100644
index 0000000..59a646a
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path3950.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path431.png b/libs/rs/java/Rollo/res/raw/path431.png
new file mode 100644
index 0000000..5d2ed75
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path431.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path4481.png b/libs/rs/java/Rollo/res/raw/path4481.png
new file mode 100644
index 0000000..78be0fc
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path4481.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path5168.png b/libs/rs/java/Rollo/res/raw/path5168.png
new file mode 100644
index 0000000..a7c3a19
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path5168.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path676.png b/libs/rs/java/Rollo/res/raw/path676.png
new file mode 100644
index 0000000..2099690
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path676.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path754.png b/libs/rs/java/Rollo/res/raw/path754.png
new file mode 100644
index 0000000..88aed5b
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path754.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/path815.png b/libs/rs/java/Rollo/res/raw/path815.png
new file mode 100644
index 0000000..407570f
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/path815.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/photos.png b/libs/rs/java/Rollo/res/raw/photos.png
new file mode 100644
index 0000000..1ed8f1e
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/photos.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/polygon2408.png b/libs/rs/java/Rollo/res/raw/polygon2408.png
new file mode 100644
index 0000000..4413954
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/polygon2408.png
Binary files differ
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
new file mode 100644
index 0000000..6376715
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -0,0 +1,184 @@
+#pragma version(1)
+#pragma stateVertex(PV)
+#pragma stateFragment(PF)
+#pragma stateFragmentStore(PFS)
+
+// Scratch buffer layout
+#define SCRATCH_FADE 0
+#define SCRATCH_ZOOM 1
+#define SCRATCH_ROT 2
+
+//#define STATE_POS_X 0
+#define STATE_DONE 1
+//#define STATE_PRESSURE 2
+#define STATE_ZOOM 3
+//#define STATE_WARP 4
+#define STATE_ORIENTATION 5
+#define STATE_SELECTION 6
+#define STATE_FIRST_VISIBLE 7
+#define STATE_COUNT 8
+#define STATE_TOUCH 9
+
+
+float filter(float val, float target, float str)
+{
+ float delta = (target - val);
+ return val + delta * str;
+}
+
+int main(void* con, int ft, int launchID)
+{
+ int rowCount;
+ int row;
+ int col;
+ int imageID;
+ int done = loadI32(0, STATE_DONE);
+ int selectedID = loadI32(0, STATE_SELECTION);
+
+ float f = loadF(2, 0);
+
+ pfClearColor(0.0f, 0.0f, 0.0f, f);
+ if (done) {
+ if (f > 0.02f) {
+ //f = f - 0.02f;
+ //storeF(2, 0, f);
+ }
+ } else {
+ if (f < 0.8f) {
+ f = f + 0.02f;
+ storeF(2, 0, f);
+ }
+ }
+
+ float touchCut = 1.f;
+ if (loadI32(0, STATE_TOUCH)) {
+ touchCut = 4.f;
+ }
+
+
+ float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f;
+ float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut);
+ storeF(2, SCRATCH_ZOOM, zoom);
+
+ float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f;
+ targetRot = targetRot * 0.80f - .12f;
+ float drawRot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
+ storeF(2, SCRATCH_ROT, drawRot);
+
+ float diam = 8.f;
+ float scale = 1.0f / zoom;
+
+ // Bug makes 1.0f alpha fail.
+ color(1.0f, 1.0f, 1.0f, 0.99f);
+
+ float rot = drawRot * scale;
+ float rotStep = 16.0f / 180.0f * 3.14f * scale;
+ rowCount = 4;
+ int index = 0;
+ int iconCount = loadI32(0, STATE_COUNT);
+ while (iconCount) {
+ float tmpSin = sinf(rot);
+ float tmpCos = cosf(rot);
+ //debugF("rot", rot);
+
+ float tx1 = tmpSin * diam - (tmpCos * scale * 0.9f);
+ float tx2 = tx1 + (tmpCos * scale * 1.8f);
+ float tz1 = tmpCos * diam + (tmpSin * scale * 0.9f);
+ float tz2 = tz1 - (tmpSin * scale * 1.8f);
+
+ int y;
+ for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+ float ty1 = ((y * 3.1f) - 5.f) * scale;
+ float ty2 = ty1 + scale * 1.8f;
+ bindTexture(NAMED_PF, 0, loadI32(1, index));
+ drawQuad(tx1, ty1, tz1,
+ tx2, ty1, tz2,
+ tx2, ty2, tz2,
+ tx1, ty2, tz1);
+
+ iconCount--;
+ index++;
+ }
+ rot = rot + rotStep;
+ }
+
+ if ((zoom < 1.1f) && (zoom > 0.9f)) {
+ bindProgramVertex(NAMED_PVOrtho);
+ bindProgramFragment(NAMED_PFText);
+ bindProgramFragmentStore(NAMED_PFSText);
+
+ rot = drawRot * scale;
+ index = 0;
+ iconCount = loadI32(0, STATE_COUNT);
+ while (iconCount) {
+ int y;
+
+ float tx = 240.f + floorf(sinf(rot) * 430.f) - 64.f + 16.f;
+
+ float alpha = 2.4f - (fabsf(tx - 240.f + 48.f) / 76.f);
+ if (alpha > 0.99f) {
+ alpha = 0.99f;
+ }
+ alpha = alpha * (1.f - (fabsf(zoom - 1.f) * 10.f));
+
+ tx = tx + 0.25f;
+
+ for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+
+ if (alpha > 0) {
+ color(1.0f, 1.0f, 1.0f, alpha);
+
+ float ty = 605.f - y * 150.f;
+
+ ty = ty + 0.25f;
+
+ bindTexture(NAMED_PFText, 0, loadI32(3, index));
+ drawRect(tx, ty, tx + 128.f, ty + 32.f, 0.5f);
+ }
+ iconCount--;
+ index++;
+ }
+ rot = rot + rotStep;
+ }
+
+
+ bindProgramVertex(NAMED_PV);
+ bindProgramFragment(NAMED_PF);
+ bindProgramFragmentStore(NAMED_PFS);
+ }
+
+ // Draw the selected icon
+ color(1.0f, 1.0f, 1.0f, 0.9f);
+ rot = drawRot * scale;
+ index = 0;
+ iconCount = loadI32(0, STATE_COUNT);
+ while (iconCount) {
+ int y;
+ for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+ if (index == selectedID) {
+
+ float tmpSin = sinf(rot) * scale;
+ float tmpCos = cosf(rot) * scale;
+ float tx1 = tmpSin * diam * 0.9f - tmpCos * 2.f;
+ float tx2 = tx1 + (tmpCos * 4.f);
+ float tz1 = tmpCos * diam * 0.9f + tmpSin * 2.f;
+ float tz2 = tz1 - (tmpSin * 4.f);
+
+ float ty1 = ((y * 3.1f) - 4.5f) * scale;
+ float ty2 = ty1 + scale * 4.f;
+ bindTexture(NAMED_PF, 0, loadI32(1, index));
+ drawQuad(tx1, ty1, tz1,
+ tx2, ty1, tz2,
+ tx2, ty2, tz2,
+ tx1, ty2, tz1);
+ }
+ iconCount--;
+ index++;
+ }
+ rot = rot + rotStep;
+ }
+
+ return 1;
+}
+
+
diff --git a/libs/rs/java/Rollo/res/raw/rollo2.c b/libs/rs/java/Rollo/res/raw/rollo2.c
new file mode 100644
index 0000000..256fa3c
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/rollo2.c
@@ -0,0 +1,155 @@
+#pragma version(1)
+#pragma stateVertex(PV)
+#pragma stateFragment(PF)
+#pragma stateFragmentStore(PFS)
+
+// Scratch buffer layout
+#define SCRATCH_FADE 0
+#define SCRATCH_ZOOM 1
+#define SCRATCH_ROT 2
+
+//#define STATE_POS_X 0
+#define STATE_DONE 1
+//#define STATE_PRESSURE 2
+#define STATE_ZOOM 3
+//#define STATE_WARP 4
+#define STATE_ORIENTATION 5
+#define STATE_SELECTION 6
+#define STATE_FIRST_VISIBLE 7
+#define STATE_COUNT 8
+#define STATE_TOUCH 9
+
+float filter(float val, float target, float str)
+{
+ float delta = (target - val);
+ return val + delta * str;
+}
+
+
+int main(void* con, int ft, int launchID)
+{
+ int rowCount;
+ int imageID;
+ int done = loadI32(0, STATE_DONE);
+ int selectedID = loadI32(0, STATE_SELECTION);
+ int iconCount = loadI32(0, STATE_COUNT);
+
+ float f = loadF(2, 0);
+
+ float iconSize = 1.f;
+ float iconSpacing = 0.2f;
+ float z = 4.f;
+
+ pfClearColor(0.0f, 0.0f, 0.0f, f);
+ if (done) {
+ } else {
+ if (f < 0.8f) {
+ f = f + 0.02f;
+ storeF(2, 0, f);
+ }
+ }
+
+ float touchCut = 1.f;
+ if (loadI32(0, STATE_TOUCH)) {
+ touchCut = 5.f;
+ }
+
+
+ float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f;
+ float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut);
+ storeF(2, SCRATCH_ZOOM, zoom);
+
+ float targetPos = loadI32(0, STATE_FIRST_VISIBLE) / (-20.0f);
+ float pos = filter(loadF(2, SCRATCH_ROT), targetPos, 0.1f * touchCut);
+ storeF(2, SCRATCH_ROT, pos);
+ pos = pos - 1.f;
+
+ color(1.0f, 1.0f, 1.0f, 1.0f);
+
+
+ // Draw flat icons first
+ int index = ((int)pos) * 4;
+ int row;
+ int col;
+ float xoffset = -0.3f;
+ float gridSize = iconSize * 4.f + iconSpacing * 3.f;
+ float yoffset = (pos - ((int)pos));
+ for (row = 0; row < 4; row ++) {
+ float ty1 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing) - iconSize;
+ float ty2 = ty1 + iconSize;
+
+ for (col = 0; (col < 4) && (index < iconCount); col ++) {
+ if (index >= 0) {
+ bindTexture(NAMED_PF, 0, loadI32(1, index));
+ float fcol = col;
+ float tx1 = xoffset + (-gridSize / 2.f) + (fcol * (iconSize + iconSpacing));
+ float tx2 = tx1 + iconSize;
+
+ drawQuad(tx1, ty1, z,
+ tx2, ty1, z,
+ tx2, ty2, z,
+ tx1, ty2, z);
+ }
+ index++;
+ }
+ }
+
+ // bottom roller
+ {
+ float roll = (1.f - yoffset) * 0.5f * 3.14f;
+ float tmpSin = sinf(roll);
+ float tmpCos = cosf(roll);
+
+ for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+ float ty2 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing);
+ float ty1 = ty2 - tmpCos * iconSize;
+
+ float tz1 = z + tmpSin * iconSize;
+ float tz2 = z;
+
+ float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+ float tx2 = tx1 + iconSize;
+
+ bindTexture(NAMED_PF, 0, loadI32(1, index));
+ drawQuad(tx1, ty1, tz1,
+ tx2, ty1, tz1,
+ tx2, ty2, tz2,
+ tx1, ty2, tz2);
+ index++;
+ }
+ }
+
+ // Top roller
+ {
+ index = (((int)pos) * 4) - 4;
+ float roll = yoffset * 0.5f * 3.14f;
+ float tmpSin = sinf(roll);
+ float tmpCos = cosf(roll);
+
+ for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+ float ty1 = (gridSize / 2.f) - ((float)-1.f - yoffset) * (iconSize + iconSpacing) - iconSize;
+ float ty2 = ty1 + tmpCos * iconSize;
+
+ float tz1 = z;
+ float tz2 = z + tmpSin * iconSize;
+
+ float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+ float tx2 = tx1 + iconSize;
+
+ bindTexture(NAMED_PF, 0, loadI32(1, index));
+ drawQuad(tx1, ty1, tz1,
+ tx2, ty1, tz1,
+ tx2, ty2, tz2,
+ tx1, ty2, tz2);
+ index++;
+ }
+ }
+
+
+
+
+ return 1;
+}
+
+
+
diff --git a/libs/rs/java/Rollo/res/raw/settings.png b/libs/rs/java/Rollo/res/raw/settings.png
new file mode 100644
index 0000000..dd2cd95
--- /dev/null
+++ b/libs/rs/java/Rollo/res/raw/settings.png
Binary files differ
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java
new file mode 100644
index 0000000..400d801
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.app.Activity;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings.System;
+import android.util.Config;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ListView;
+
+import java.lang.Runtime;
+
+public class Rollo extends Activity {
+ //EventListener mListener = new EventListener();
+
+ private static final String LOG_TAG = "libRS_jni";
+ private static final boolean DEBUG = false;
+ private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
+
+ private RolloView mView;
+
+ // get the current looper (from your Activity UI thread for instance
+
+
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Create our Preview view and set it as the content of our
+ // Activity
+ mView = new RolloView(this);
+ setContentView(mView);
+ }
+
+ @Override
+ protected void onResume() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onResume();
+ mView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ // Ideally a game should implement onResume() and onPause()
+ // to take appropriate action when the activity looses focus
+ super.onPause();
+ mView.onPause();
+
+ Runtime.getRuntime().exit(0);
+ }
+
+
+ static void log(String message) {
+ if (LOG_ENABLED) {
+ Log.v(LOG_TAG, message);
+ }
+ }
+
+
+}
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
new file mode 100644
index 0000000..ba74b58
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import java.io.Writer;
+
+import android.renderscript.RenderScript;
+import android.renderscript.ProgramVertex;
+import android.renderscript.Element;
+import android.renderscript.Allocation;
+import android.renderscript.Script;
+import android.renderscript.ScriptC;
+import android.renderscript.ProgramFragment;
+import android.renderscript.ProgramStore;
+import android.renderscript.Sampler;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.Typeface;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+public class RolloRS {
+ //public static final int STATE_SELECTED_ID = 0;
+ public static final int STATE_DONE = 1;
+ //public static final int STATE_PRESSURE = 2;
+ public static final int STATE_ZOOM = 3;
+ //public static final int STATE_WARP = 4;
+ public static final int STATE_ORIENTATION = 5;
+ public static final int STATE_SELECTION = 6;
+ public static final int STATE_FIRST_VISIBLE = 7;
+ public static final int STATE_COUNT = 8;
+ public static final int STATE_TOUCH = 9;
+
+
+ public RolloRS() {
+ }
+
+ public void init(RenderScript rs, Resources res, int width, int height) {
+ mRS = rs;
+ mRes = res;
+ mWidth = width;
+ mHeight = height;
+ initNamed();
+ initRS();
+ }
+
+ public void setPosition(float column) {
+ mAllocStateBuf[STATE_FIRST_VISIBLE] = (int)(column * (-20));
+ mAllocState.data(mAllocStateBuf);
+ }
+
+ public void setTouch(boolean touch) {
+ mAllocStateBuf[STATE_TOUCH] = touch ? 1 : 0;
+ mAllocState.data(mAllocStateBuf);
+ }
+
+ public void setZoom(float z) {
+ //Log.e("rs", "zoom " + Float.toString(z));
+
+ mAllocStateBuf[STATE_ZOOM] = (int)(z * 1000.f);
+ mAllocState.data(mAllocStateBuf);
+ }
+
+ public void setSelected(int index) {
+ //Log.e("rs", "setSelected " + Integer.toString(index));
+
+ mAllocStateBuf[STATE_SELECTION] = index;
+ mAllocStateBuf[STATE_DONE] = 1;
+ mAllocState.data(mAllocStateBuf);
+ }
+
+ private int mWidth;
+ private int mHeight;
+
+ private Resources mRes;
+ private RenderScript mRS;
+ private Script mScript;
+ private Sampler mSampler;
+ private Sampler mSamplerText;
+ private ProgramStore mPSBackground;
+ private ProgramStore mPSText;
+ private ProgramFragment mPFImages;
+ private ProgramFragment mPFText;
+ private ProgramVertex mPV;
+ private ProgramVertex.MatrixAllocation mPVAlloc;
+ private ProgramVertex mPVOrtho;
+ private ProgramVertex.MatrixAllocation mPVOrthoAlloc;
+ private Allocation[] mIcons;
+ private Allocation[] mLabels;
+
+ private int[] mAllocStateBuf;
+ private Allocation mAllocState;
+
+ private int[] mAllocIconIDBuf;
+ private Allocation mAllocIconID;
+
+ private int[] mAllocLabelIDBuf;
+ private Allocation mAllocLabelID;
+
+ private int[] mAllocScratchBuf;
+ private Allocation mAllocScratch;
+
+ private void initNamed() {
+ Sampler.Builder sb = new Sampler.Builder(mRS);
+ sb.setMin(Sampler.Value.LINEAR);//_MIP_LINEAR);
+ sb.setMag(Sampler.Value.LINEAR);
+ sb.setWrapS(Sampler.Value.CLAMP);
+ sb.setWrapT(Sampler.Value.CLAMP);
+ mSampler = sb.create();
+
+ sb.setMin(Sampler.Value.NEAREST);
+ sb.setMag(Sampler.Value.NEAREST);
+ mSamplerText = sb.create();
+
+
+ ProgramFragment.Builder bf = new ProgramFragment.Builder(mRS, null, null);
+ bf.setTexEnable(true, 0);
+ bf.setTexEnvMode(ProgramFragment.EnvMode.MODULATE, 0);
+ mPFImages = bf.create();
+ mPFImages.setName("PF");
+ mPFImages.bindSampler(mSampler, 0);
+
+ bf.setTexEnvMode(ProgramFragment.EnvMode.MODULATE, 0);
+ mPFText = bf.create();
+ mPFText.setName("PFText");
+ mPFText.bindSampler(mSamplerText, 0);
+
+ ProgramStore.Builder bs = new ProgramStore.Builder(mRS, null, null);
+ bs.setDepthFunc(ProgramStore.DepthFunc.LESS);
+ bs.setDitherEnable(false);
+ bs.setDepthMask(true);
+ bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
+ ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+ mPSBackground = bs.create();
+ mPSBackground.setName("PFS");
+
+ bs.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
+ bs.setDepthMask(false);
+ bs.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA,
+ ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
+ mPSText = bs.create();
+ mPSText.setName("PFSText");
+
+ mPVAlloc = new ProgramVertex.MatrixAllocation(mRS);
+ mPVAlloc.setupProjectionNormalized(mWidth, mHeight);
+
+ ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
+ mPV = pvb.create();
+ mPV.setName("PV");
+ mPV.bindAllocation(mPVAlloc);
+
+ mPVOrthoAlloc = new ProgramVertex.MatrixAllocation(mRS);
+ mPVOrthoAlloc.setupOrthoWindow(mWidth, mHeight);
+
+ pvb.setTextureMatrixEnable(true);
+ mPVOrtho = pvb.create();
+ mPVOrtho.setName("PVOrtho");
+ mPVOrtho.bindAllocation(mPVOrthoAlloc);
+
+ mRS.contextBindProgramVertex(mPV);
+
+ mAllocScratchBuf = new int[32];
+ mAllocScratch = Allocation.createSized(mRS,
+ Element.USER_I32, mAllocScratchBuf.length);
+ mAllocScratch.data(mAllocScratchBuf);
+
+ Log.e("rs", "Done loading named");
+
+
+
+ {
+ mIcons = new Allocation[29];
+ mAllocIconIDBuf = new int[mIcons.length];
+ mAllocIconID = Allocation.createSized(mRS,
+ Element.USER_I32, mAllocIconIDBuf.length);
+
+ mLabels = new Allocation[29];
+ mAllocLabelIDBuf = new int[mLabels.length];
+ mAllocLabelID = Allocation.createSized(mRS,
+ Element.USER_I32, mLabels.length);
+
+ Element ie8888 = Element.RGBA_8888;
+
+ mIcons[0] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.browser, ie8888, true);
+ mIcons[1] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.market, ie8888, true);
+ mIcons[2] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.photos, ie8888, true);
+ mIcons[3] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.settings, ie8888, true);
+ mIcons[4] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.calendar, ie8888, true);
+ mIcons[5] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.g1155, ie8888, true);
+ mIcons[6] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.g2140, ie8888, true);
+ mIcons[7] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.maps, ie8888, true);
+ mIcons[8] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path431, ie8888, true);
+ mIcons[9] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path676, ie8888, true);
+ mIcons[10] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path754, ie8888, true);
+ mIcons[11] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path815, ie8888, true);
+ mIcons[12] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path1920, ie8888, true);
+ mIcons[13] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path1927, ie8888, true);
+ mIcons[14] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path3099, ie8888, true);
+ mIcons[15] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path3950, ie8888, true);
+ mIcons[16] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path4481, ie8888, true);
+ mIcons[17] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.path5168, ie8888, true);
+ mIcons[18] = Allocation.createFromBitmapResource(mRS, mRes, R.raw.polygon2408, ie8888, true);
+
+ mLabels[0] = makeTextBitmap("browser");
+ mLabels[1] = makeTextBitmap("market");
+ mLabels[2] = makeTextBitmap("photos");
+ mLabels[3] = makeTextBitmap("settings");
+ mLabels[4] = makeTextBitmap("calendar");
+ mLabels[5] = makeTextBitmap("g1155");
+ mLabels[6] = makeTextBitmap("g2140");
+ mLabels[7] = makeTextBitmap("maps");
+ mLabels[8] = makeTextBitmap("path431");
+ mLabels[9] = makeTextBitmap("path676");
+ mLabels[10] = makeTextBitmap("path754");
+ mLabels[11] = makeTextBitmap("path815");
+ mLabels[12] = makeTextBitmap("path1920");
+ mLabels[13] = makeTextBitmap("path1927");
+ mLabels[14] = makeTextBitmap("path3099");
+ mLabels[15] = makeTextBitmap("path3950");
+ mLabels[16] = makeTextBitmap("path4481");
+ mLabels[17] = makeTextBitmap("path5168");
+ mLabels[18] = makeTextBitmap("polygon2408");
+
+ mIcons[19] = mIcons[0];
+ mIcons[20] = mIcons[1];
+ mIcons[21] = mIcons[2];
+ mIcons[22] = mIcons[3];
+ mIcons[23] = mIcons[4];
+ mIcons[24] = mIcons[5];
+ mIcons[25] = mIcons[6];
+ mIcons[26] = mIcons[7];
+ mIcons[27] = mIcons[8];
+ mIcons[28] = mIcons[9];
+
+ mLabels[19] = mLabels[0];
+ mLabels[20] = mLabels[1];
+ mLabels[21] = mLabels[2];
+ mLabels[22] = mLabels[3];
+ mLabels[23] = mLabels[4];
+ mLabels[24] = mLabels[5];
+ mLabels[25] = mLabels[6];
+ mLabels[26] = mLabels[7];
+ mLabels[27] = mLabels[8];
+ mLabels[28] = mLabels[9];
+
+ for(int ct=0; ct < mIcons.length; ct++) {
+ mIcons[ct].uploadToTexture(0);
+ mLabels[ct].uploadToTexture(0);
+ mAllocIconIDBuf[ct] = mIcons[ct].getID();
+ mAllocLabelIDBuf[ct] = mLabels[ct].getID();
+ }
+ mAllocIconID.data(mAllocIconIDBuf);
+ mAllocLabelID.data(mAllocLabelIDBuf);
+ }
+
+ }
+
+ Allocation makeTextBitmap(String t) {
+ Bitmap b = Bitmap.createBitmap(128, 32, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ Paint p = new Paint();
+ p.setTypeface(Typeface.DEFAULT_BOLD);
+ p.setTextSize(20);
+ p.setColor(0xffffffff);
+ c.drawText(t, 2, 26, p);
+ return Allocation.createFromBitmap(mRS, b, Element.RGBA_8888, true);
+ }
+
+
+ private void initRS() {
+ ScriptC.Builder sb = new ScriptC.Builder(mRS);
+ sb.setScript(mRes, R.raw.rollo);
+ //sb.setScript(mRes, R.raw.rollo2);
+ sb.setRoot(true);
+ mScript = sb.create();
+ mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, -1, 0, mAllocIconIDBuf.length, 0, 0};
+ mAllocState = Allocation.createSized(mRS,
+ Element.USER_I32, mAllocStateBuf.length);
+ mScript.bindAllocation(mAllocState, 0);
+ mScript.bindAllocation(mAllocIconID, 1);
+ mScript.bindAllocation(mAllocScratch, 2);
+ mScript.bindAllocation(mAllocLabelID, 3);
+ setPosition(0);
+ setZoom(1);
+
+ //RenderScript.File f = mRS.fileOpen("/sdcard/test.a3d");
+
+ mRS.contextBindRootScript(mScript);
+ }
+}
+
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
new file mode 100644
index 0000000..7524a0e
--- /dev/null
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.rollo;
+
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import java.lang.Float;
+
+import android.renderscript.RSSurfaceView;
+import android.renderscript.RenderScript;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.graphics.PixelFormat;
+
+
+public class RolloView extends RSSurfaceView {
+ public RolloView(Context context) {
+ super(context);
+ setFocusable(true);
+ getHolder().setFormat(PixelFormat.TRANSLUCENT);
+ }
+
+ private RenderScript mRS;
+ private RolloRS mRender;
+
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ super.surfaceChanged(holder, format, w, h);
+
+ mRS = createRenderScript(false);
+ mRender = new RolloRS();
+ mRender.init(mRS, getResources(), w, h);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event)
+ {
+ // break point at here
+ // this method doesn't work when 'extends View' include 'extends ScrollView'.
+ return super.onKeyDown(keyCode, event);
+ }
+
+ boolean mControlMode = false;
+ boolean mZoomMode = false;
+ boolean mFlingMode = false;
+ float mFlingX = 0;
+ float mFlingY = 0;
+ float mColumn = -1;
+ float mOldColumn;
+ float mZoom = 1;
+
+ int mIconCount = 29;
+ int mRows = 4;
+ int mColumns = (mIconCount + mRows - 1) / mRows;
+
+ float mMaxZoom = ((float)mColumns) / 3.f;
+
+
+ void setColumn(boolean clamp)
+ {
+ //Log.e("rs", " col = " + Float.toString(mColumn));
+ float c = mColumn;
+ if(c > (mColumns -2)) {
+ c = (mColumns -2);
+ }
+ if(c < 0) {
+ c = 0;
+ }
+ mRender.setPosition(c);
+ if(clamp) {
+ mColumn = c;
+ }
+ }
+
+ void computeSelection(float x, float y)
+ {
+ float col = mColumn + (x - 0.5f) * 4 + 1.25f;
+ int iCol = (int)(col + 0.25f);
+
+ float row = (y / 0.8f) * mRows;
+ int iRow = (int)(row - 0.5f);
+
+ mRender.setSelected(iCol * mRows + iRow);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev)
+ {
+ boolean ret = true;
+ int act = ev.getAction();
+ if (act == ev.ACTION_UP) {
+ ret = false;
+ }
+
+ float nx = ev.getX() / getWidth();
+ float ny = ev.getY() / getHeight();
+
+ //Log.e("rs", "width=" + Float.toString(getWidth()));
+ //Log.e("rs", "height=" + Float.toString(getHeight()));
+
+ mRender.setTouch(ret);
+
+ if((ny > 0.85f) || mControlMode) {
+ mFlingMode = false;
+
+ // Projector control
+ if((nx > 0.2f) && (nx < 0.8f) || mControlMode) {
+ if(act != ev.ACTION_UP) {
+ float zoom = mMaxZoom;
+ if(mControlMode) {
+ if(!mZoomMode) {
+ zoom = 1.f;
+ }
+ float dx = nx - mFlingX;
+
+ if((ny < 0.9) && mZoomMode) {
+ zoom = mMaxZoom - ((0.9f - ny) * 10.f);
+ if(zoom < 1) {
+ zoom = 1;
+ mZoomMode = false;
+ }
+ mOldColumn = mColumn;
+ }
+ mColumn += dx * 4;// * zoom;
+ if(zoom > 1.01f) {
+ mColumn += (mZoom - zoom) * (nx - 0.5f) * 4 * zoom;
+ }
+ } else {
+ mOldColumn = mColumn;
+ mColumn = ((float)mColumns) / 2;
+ mControlMode = true;
+ mZoomMode = true;
+ }
+ mZoom = zoom;
+ mFlingX = nx;
+ mRender.setZoom(zoom);
+ if(mZoom < 1.01f) {
+ computeSelection(nx, ny);
+ }
+ } else {
+ mControlMode = false;
+ mColumn = mOldColumn;
+ mRender.setZoom(1.f);
+ mRender.setSelected(-1);
+ }
+ } else {
+ // Do something with corners here....
+ }
+ setColumn(true);
+
+ } else {
+ // icon control
+ if(act != ev.ACTION_UP) {
+ if(mFlingMode) {
+ mColumn += (mFlingX - nx) * 4;
+ setColumn(true);
+ }
+ mFlingMode = true;
+ mFlingX = nx;
+ mFlingY = ny;
+ } else {
+ mFlingMode = false;
+ mColumn = (float)(java.lang.Math.floor(mColumn * 0.25f + 0.3f) * 4.f) + 1.f;
+ setColumn(true);
+ }
+ }
+
+
+ return ret;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent ev)
+ {
+ float x = ev.getX();
+ float y = ev.getY();
+ //Float tx = new Float(x);
+ //Float ty = new Float(y);
+ //Log.e("rs", "tbe " + tx.toString() + ", " + ty.toString());
+
+
+ return true;
+ }
+
+}
+
+
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
new file mode 100644
index 0000000..ac2e738
--- /dev/null
+++ b/libs/rs/rs.spec
@@ -0,0 +1,478 @@
+
+
+ContextBindRootScript {
+ param RsScript sampler
+ }
+
+ContextBindProgramFragmentStore {
+ param RsProgramFragmentStore pgm
+ }
+
+ContextBindProgramFragment {
+ param RsProgramFragment pgm
+ }
+
+ContextBindProgramVertex {
+ param RsProgramVertex pgm
+ }
+
+ContextSetDefineF {
+ param const char* name
+ param float value
+ }
+
+ContextSetDefineI32 {
+ param const char* name
+ param int32_t value
+ }
+
+AssignName {
+ param void *obj
+ param const char *name
+ param size_t len
+ }
+
+ObjDestroy {
+ param void *obj
+ }
+
+ElementBegin {
+}
+
+ElementAdd {
+ param RsDataKind dataKind
+ param RsDataType dataType
+ param bool isNormalized
+ param size_t bits
+ param const char * name
+ }
+
+ElementCreate {
+ ret RsElement
+ }
+
+ElementGetPredefined {
+ param RsElementPredefined predef
+ ret RsElement
+ }
+
+TypeBegin {
+ param RsElement type
+ }
+
+TypeAdd {
+ param RsDimension dim
+ param size_t value
+ }
+
+TypeCreate {
+ ret RsType
+ }
+
+AllocationCreateTyped {
+ param RsType type
+ ret RsAllocation
+ }
+
+AllocationCreatePredefSized {
+ param RsElementPredefined predef
+ param size_t count
+ ret RsAllocation
+ }
+
+AllocationCreateSized {
+ param RsElement e
+ param size_t count
+ ret RsAllocation
+ }
+
+AllocationCreateFromFile {
+ param const char *file
+ param bool genMips
+ ret RsAllocation
+ }
+
+AllocationCreateFromBitmap {
+ param uint32_t width
+ param uint32_t height
+ param RsElement dstFmt
+ param RsElement srcFmt
+ param bool genMips
+ param const void * data
+ ret RsAllocation
+ }
+
+AllocationCreateFromBitmapBoxed {
+ param uint32_t width
+ param uint32_t height
+ param RsElement dstFmt
+ param RsElement srcFmt
+ param bool genMips
+ param const void * data
+ ret RsAllocation
+ }
+
+
+AllocationUploadToTexture {
+ param RsAllocation alloc
+ param uint32_t baseMipLevel
+ }
+
+AllocationUploadToBufferObject {
+ param RsAllocation alloc
+ }
+
+
+AllocationData {
+ param RsAllocation va
+ param const void * data
+ param uint32_t bytes
+ handcodeApi
+ togglePlay
+ }
+
+Allocation1DSubData {
+ param RsAllocation va
+ param uint32_t xoff
+ param uint32_t count
+ param const void *data
+ param uint32_t bytes
+ handcodeApi
+ togglePlay
+ }
+
+Allocation2DSubData {
+ param RsAllocation va
+ param uint32_t xoff
+ param uint32_t yoff
+ param uint32_t w
+ param uint32_t h
+ param const void *data
+ param uint32_t bytes
+ }
+
+AllocationRead {
+ param RsAllocation va
+ param void * data
+ }
+
+Adapter1DCreate {
+ ret RsAdapter1D
+ }
+
+Adapter1DBindAllocation {
+ param RsAdapter1D adapt
+ param RsAllocation alloc
+ }
+
+Adapter1DSetConstraint {
+ param RsAdapter1D adapter
+ param RsDimension dim
+ param uint32_t value
+ }
+
+Adapter1DData {
+ param RsAdapter1D adapter
+ param const void * data
+ }
+
+Adapter1DSubData {
+ param RsAdapter1D adapter
+ param uint32_t xoff
+ param uint32_t count
+ param const void *data
+ }
+
+Adapter2DCreate {
+ ret RsAdapter2D
+ }
+
+Adapter2DBindAllocation {
+ param RsAdapter2D adapt
+ param RsAllocation alloc
+ }
+
+Adapter2DSetConstraint {
+ param RsAdapter2D adapter
+ param RsDimension dim
+ param uint32_t value
+ }
+
+Adapter2DData {
+ param RsAdapter2D adapter
+ param const void *data
+ }
+
+Adapter2DSubData {
+ param RsAdapter2D adapter
+ param uint32_t xoff
+ param uint32_t yoff
+ param uint32_t w
+ param uint32_t h
+ param const void *data
+ }
+
+SamplerBegin {
+ }
+
+SamplerSet {
+ param RsSamplerParam p
+ param RsSamplerValue value
+ }
+
+SamplerCreate {
+ ret RsSampler
+ }
+
+
+TriangleMeshBegin {
+ param RsElement vertex
+ param RsElement index
+ }
+
+TriangleMeshAddVertex {
+ param const void *vtx
+ }
+
+TriangleMeshAddTriangle {
+ param uint32_t idx1
+ param uint32_t idx2
+ param uint32_t idx3
+ }
+
+TriangleMeshCreate {
+ ret RsTriangleMesh
+ }
+
+
+TriangleMeshRender {
+ param RsTriangleMesh vtm
+ }
+
+TriangleMeshRenderRange {
+ param RsTriangleMesh vtm
+ param uint32_t start
+ param uint32_t count
+ }
+
+
+ScriptBindAllocation {
+ param RsScript vtm
+ param RsAllocation va
+ param uint32_t slot
+ }
+
+
+ScriptCBegin {
+ }
+
+ScriptSetClearColor {
+ param RsScript s
+ param float r
+ param float g
+ param float b
+ param float a
+ }
+
+ScriptSetTimeZone {
+ param RsScript s
+ param const char * timeZone
+ param uint32_t length
+ }
+
+ScriptSetClearDepth {
+ param RsScript s
+ param float depth
+ }
+
+ScriptSetClearStencil {
+ param RsScript s
+ param uint32_t stencil
+ }
+
+ScriptSetType {
+ param RsType type
+ param uint32_t slot
+ param bool isWritable
+ param const char * name
+ }
+
+ScriptSetRoot {
+ param bool isRoot
+ }
+
+
+
+ScriptCSetScript {
+ param void * codePtr
+ }
+
+ScriptCSetText {
+ param const char * text
+ param uint32_t length
+ }
+
+ScriptCCreate {
+ ret RsScript
+ }
+
+ScriptCSetDefineF {
+ param const char* name
+ param float value
+ }
+
+ScriptCSetDefineI32 {
+ param const char* name
+ param int32_t value
+ }
+
+ProgramFragmentStoreBegin {
+ param RsElement in
+ param RsElement out
+ }
+
+ProgramFragmentStoreColorMask {
+ param bool r
+ param bool g
+ param bool b
+ param bool a
+ }
+
+ProgramFragmentStoreBlendFunc {
+ param RsBlendSrcFunc srcFunc
+ param RsBlendDstFunc destFunc
+ }
+
+ProgramFragmentStoreDepthMask {
+ param bool enable
+}
+
+ProgramFragmentStoreDither {
+ param bool enable
+}
+
+ProgramFragmentStoreDepthFunc {
+ param RsDepthFunc func
+}
+
+ProgramFragmentStoreCreate {
+ ret RsProgramFragmentStore
+ }
+
+
+
+ProgramFragmentBegin {
+ param RsElement in
+ param RsElement out
+ param bool pointSpriteEnable
+ }
+
+ProgramFragmentBindTexture {
+ param RsProgramFragment pf
+ param uint32_t slot
+ param RsAllocation a
+ }
+
+ProgramFragmentBindSampler {
+ param RsProgramFragment pf
+ param uint32_t slot
+ param RsSampler s
+ }
+
+ProgramFragmentSetSlot {
+ param uint32_t slot
+ param bool enable
+ param RsTexEnvMode env
+ param RsType t
+ }
+
+ProgramFragmentCreate {
+ ret RsProgramFragment
+ }
+
+
+ProgramVertexBegin {
+ param RsElement in
+ param RsElement out
+ }
+
+ProgramVertexCreate {
+ ret RsProgramVertex
+ }
+
+ProgramVertexBindAllocation {
+ param RsProgramVertex vpgm
+ param RsAllocation constants
+ }
+
+ProgramVertexSetTextureMatrixEnable {
+ param bool enable
+ }
+
+ProgramVertexAddLight {
+ param RsLight light
+ }
+
+LightBegin {
+ }
+
+LightSetLocal {
+ param bool isLocal
+ }
+
+LightSetMonochromatic {
+ param bool isMono
+ }
+
+LightCreate {
+ ret RsLight light
+ }
+
+
+LightSetPosition {
+ param RsLight light
+ param float x
+ param float y
+ param float z
+ }
+
+LightSetColor {
+ param RsLight light
+ param float r
+ param float g
+ param float b
+ }
+
+FileOpen {
+ ret RsFile
+ param const char *name
+ param size_t len
+ }
+
+
+SimpleMeshCreate {
+ ret RsSimpleMesh
+ param RsAllocation prim
+ param RsAllocation index
+ param RsAllocation *vtx
+ param uint32_t vtxCount
+ param uint32_t primType
+ }
+
+
+SimpleMeshBindIndex {
+ param RsSimpleMesh mesh
+ param RsAllocation idx
+ }
+
+SimpleMeshBindPrimitive {
+ param RsSimpleMesh mesh
+ param RsAllocation prim
+ }
+
+SimpleMeshBindVertex {
+ param RsSimpleMesh mesh
+ param RsAllocation vtx
+ param uint32_t slot
+ }
+
diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp
new file mode 100644
index 0000000..d20e910
--- /dev/null
+++ b/libs/rs/rsAdapter.cpp
@@ -0,0 +1,237 @@
+
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Adapter1D::Adapter1D()
+{
+ reset();
+}
+
+Adapter1D::Adapter1D(Allocation *a)
+{
+ reset();
+ setAllocation(a);
+}
+
+void Adapter1D::reset()
+{
+ mY = 0;
+ mZ = 0;
+ mLOD = 0;
+ mFace = 0;
+}
+
+void * Adapter1D::getElement(uint32_t x)
+{
+ rsAssert(mAllocation.get());
+ rsAssert(mAllocation->getPtr());
+ rsAssert(mAllocation->getType());
+ uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr());
+ ptr += mAllocation->getType()->getLODOffset(mLOD, x, mY);
+ return ptr;
+}
+
+void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data)
+{
+ if (mAllocation.get() && mAllocation.get()->getType()) {
+ void *ptr = getElement(xoff);
+ count *= mAllocation.get()->getType()->getElementSizeBytes();
+ memcpy(ptr, data, count);
+ }
+}
+
+void Adapter1D::data(const void *data)
+{
+ memcpy(getElement(0),
+ data,
+ mAllocation.get()->getType()->getSizeBytes());
+}
+
+namespace android {
+namespace renderscript {
+
+RsAdapter1D rsi_Adapter1DCreate(Context *rsc)
+{
+ Adapter1D *a = new Adapter1D();
+ a->incUserRef();
+ return a;
+}
+
+void rsi_Adapter1DBindAllocation(Context *rsc, RsAdapter1D va, RsAllocation valloc)
+{
+ Adapter1D * a = static_cast<Adapter1D *>(va);
+ Allocation * alloc = static_cast<Allocation *>(valloc);
+ a->setAllocation(alloc);
+}
+
+void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, uint32_t value)
+{
+ Adapter1D * a = static_cast<Adapter1D *>(va);
+ switch(dim) {
+ case RS_DIMENSION_X:
+ rsAssert(!"Cannot contrain X in an 1D adapter");
+ return;
+ case RS_DIMENSION_Y:
+ a->setY(value);
+ break;
+ case RS_DIMENSION_Z:
+ a->setZ(value);
+ break;
+ case RS_DIMENSION_LOD:
+ a->setLOD(value);
+ break;
+ case RS_DIMENSION_FACE:
+ a->setFace(value);
+ break;
+ default:
+ rsAssert(!"Unimplemented constraint");
+ return;
+ }
+}
+
+void rsi_Adapter1DSubData(Context *rsc, RsAdapter1D va, uint32_t xoff, uint32_t count, const void *data)
+{
+ Adapter1D * a = static_cast<Adapter1D *>(va);
+ a->subData(xoff, count, data);
+}
+
+void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data)
+{
+ Adapter1D * a = static_cast<Adapter1D *>(va);
+ a->data(data);
+}
+
+}
+}
+
+//////////////////////////
+
+Adapter2D::Adapter2D()
+{
+ reset();
+}
+
+Adapter2D::Adapter2D(Allocation *a)
+{
+ reset();
+ setAllocation(a);
+}
+
+void Adapter2D::reset()
+{
+ mZ = 0;
+ mLOD = 0;
+ mFace = 0;
+}
+
+void * Adapter2D::getElement(uint32_t x, uint32_t y) const
+{
+ rsAssert(mAllocation.get());
+ rsAssert(mAllocation->getPtr());
+ rsAssert(mAllocation->getType());
+ uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr());
+ ptr += mAllocation->getType()->getLODOffset(mLOD, x, y);
+ return ptr;
+}
+
+void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data)
+{
+ rsAssert(mAllocation.get());
+ rsAssert(mAllocation->getPtr());
+ rsAssert(mAllocation->getType());
+
+ uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes();
+ uint32_t lineSize = eSize * w;
+ uint32_t destW = getDimX();
+
+ const uint8_t *src = static_cast<const uint8_t *>(data);
+ for (uint32_t line=yoff; line < (yoff+h); line++) {
+ memcpy(getElement(xoff, line), src, lineSize);
+ src += lineSize;
+ }
+}
+
+void Adapter2D::data(const void *data)
+{
+ memcpy(getElement(0,0),
+ data,
+ mAllocation.get()->getType()->getSizeBytes());
+}
+
+
+
+namespace android {
+namespace renderscript {
+
+RsAdapter2D rsi_Adapter2DCreate(Context *rsc)
+{
+ Adapter2D *a = new Adapter2D();
+ a->incUserRef();
+ return a;
+}
+
+void rsi_Adapter2DBindAllocation(Context *rsc, RsAdapter2D va, RsAllocation valloc)
+{
+ Adapter2D * a = static_cast<Adapter2D *>(va);
+ Allocation * alloc = static_cast<Allocation *>(valloc);
+ a->setAllocation(alloc);
+}
+
+void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, uint32_t value)
+{
+ Adapter2D * a = static_cast<Adapter2D *>(va);
+ switch(dim) {
+ case RS_DIMENSION_X:
+ rsAssert(!"Cannot contrain X in an 2D adapter");
+ return;
+ case RS_DIMENSION_Y:
+ rsAssert(!"Cannot contrain Y in an 2D adapter");
+ break;
+ case RS_DIMENSION_Z:
+ a->setZ(value);
+ break;
+ case RS_DIMENSION_LOD:
+ a->setLOD(value);
+ break;
+ case RS_DIMENSION_FACE:
+ a->setFace(value);
+ break;
+ default:
+ rsAssert(!"Unimplemented constraint");
+ return;
+ }
+}
+
+void rsi_Adapter2DData(Context *rsc, RsAdapter2D va, const void *data)
+{
+ Adapter2D * a = static_cast<Adapter2D *>(va);
+ a->data(data);
+}
+
+void rsi_Adapter2DSubData(Context *rsc, RsAdapter2D va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data)
+{
+ Adapter2D * a = static_cast<Adapter2D *>(va);
+ a->subData(xoff, yoff, w, h, data);
+}
+
+}
+}
diff --git a/libs/rs/rsAdapter.h b/libs/rs/rsAdapter.h
new file mode 100644
index 0000000..865535e
--- /dev/null
+++ b/libs/rs/rsAdapter.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_ADAPTER_H
+#define ANDROID_RS_ADAPTER_H
+
+#include "rsAllocation.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class Adapter1D : public ObjectBase
+{
+
+public:
+ // By policy this allocation will hold a pointer to the type
+ // but will not destroy it on destruction.
+ Adapter1D();
+ Adapter1D(Allocation *);
+ void reset();
+ void * getElement(uint32_t x);
+
+ void setAllocation(Allocation *a) {mAllocation.set(a);}
+
+ uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);}
+
+ const Type * getBaseType() const {return mAllocation->getType();}
+
+ inline void setY(uint32_t y) {mY = y;}
+ inline void setZ(uint32_t z) {mZ = z;}
+ inline void setLOD(uint32_t lod) {mLOD = lod;}
+ inline void setFace(uint32_t face) {mFace = face;}
+ //void setArray(uint32_t num, uint32_t value);
+
+ void subData(uint32_t xoff, uint32_t count, const void *data);
+ void data(const void *data);
+
+protected:
+ ObjectBaseRef<Allocation> mAllocation;
+ uint32_t mY;
+ uint32_t mZ;
+ uint32_t mLOD;
+ uint32_t mFace;
+};
+
+class Adapter2D : public ObjectBase
+{
+
+public:
+ // By policy this allocation will hold a pointer to the type
+ // but will not destroy it on destruction.
+ Adapter2D();
+ Adapter2D(Allocation *);
+ void reset();
+ void * getElement(uint32_t x, uint32_t y) const;
+
+ uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);}
+ uint32_t getDimY() const {return mAllocation->getType()->getLODDimY(mLOD);}
+ const Type * getBaseType() const {return mAllocation->getType();}
+
+ void setAllocation(Allocation *a) {mAllocation.set(a);}
+ inline void setZ(uint32_t z) {mZ = z;}
+ inline void setLOD(uint32_t lod) {mLOD = lod;}
+ inline void setFace(uint32_t face) {mFace = face;}
+ //void setArray(uint32_t num, uint32_t value);
+
+ void data(const void *data);
+ void subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data);
+
+protected:
+ ObjectBaseRef<Allocation> mAllocation;
+ uint32_t mZ;
+ uint32_t mLOD;
+ uint32_t mFace;
+};
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
new file mode 100644
index 0000000..c267e16
--- /dev/null
+++ b/libs/rs/rsAllocation.cpp
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+Allocation::Allocation(const Type *type)
+{
+ mPtr = NULL;
+
+ mCpuWrite = false;
+ mCpuRead = false;
+ mGpuWrite = false;
+ mGpuRead = false;
+
+ mReadWriteRatio = 0;
+ mUpdateSize = 0;
+
+ mIsTexture = false;
+ mTextureID = 0;
+
+ mIsVertexBuffer = false;
+ mBufferID = 0;
+
+ mType.set(type);
+ rsAssert(type);
+ mPtr = malloc(mType->getSizeBytes());
+ if (!mPtr) {
+ LOGE("Allocation::Allocation, alloc failure");
+ }
+}
+
+Allocation::~Allocation()
+{
+}
+
+void Allocation::setCpuWritable(bool)
+{
+}
+
+void Allocation::setGpuWritable(bool)
+{
+}
+
+void Allocation::setCpuReadable(bool)
+{
+}
+
+void Allocation::setGpuReadable(bool)
+{
+}
+
+bool Allocation::fixAllocation()
+{
+ return false;
+}
+
+void Allocation::uploadToTexture(uint32_t lodOffset)
+{
+ //rsAssert(!mTextureId);
+ rsAssert(lodOffset < mType->getLODCount());
+
+ GLenum type = mType->getElement()->getGLType();
+ GLenum format = mType->getElement()->getGLFormat();
+
+ if (!type || !format) {
+ return;
+ }
+
+ if (!mTextureID) {
+ glGenTextures(1, &mTextureID);
+ }
+ glBindTexture(GL_TEXTURE_2D, mTextureID);
+
+ Adapter2D adapt(this);
+ for(uint32_t lod = 0; (lod + lodOffset) < mType->getLODCount(); lod++) {
+ adapt.setLOD(lod+lodOffset);
+
+ uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0));
+ glTexImage2D(GL_TEXTURE_2D, lod, format,
+ adapt.getDimX(), adapt.getDimY(),
+ 0, format, type, ptr);
+ }
+}
+
+void Allocation::uploadToBufferObject()
+{
+ rsAssert(!mType->getDimY());
+ rsAssert(!mType->getDimZ());
+
+ if (!mBufferID) {
+ glGenBuffers(1, &mBufferID);
+ }
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferID);
+ glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+
+void Allocation::data(const void *data, uint32_t sizeBytes)
+{
+ uint32_t size = mType->getSizeBytes();
+ if (size != sizeBytes) {
+ LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes);
+ return;
+ }
+ memcpy(mPtr, data, size);
+}
+
+void Allocation::read(void *data)
+{
+ memcpy(data, mPtr, mType->getSizeBytes());
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes)
+{
+ uint32_t eSize = mType->getElementSizeBytes();
+ uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+ ptr += eSize * xoff;
+ uint32_t size = count * eSize;
+
+ if (size != sizeBytes) {
+ LOGE("Allocation::subData called with mismatched size expected %i, got %i", size, sizeBytes);
+ return;
+ }
+ memcpy(ptr, data, size);
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t yoff,
+ uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes)
+{
+ uint32_t eSize = mType->getElementSizeBytes();
+ uint32_t lineSize = eSize * w;
+ uint32_t destW = mType->getDimX();
+
+ const uint8_t *src = static_cast<const uint8_t *>(data);
+ uint8_t *dst = static_cast<uint8_t *>(mPtr);
+ dst += eSize * (xoff + yoff * destW);
+
+ if ((lineSize * eSize * h) != sizeBytes) {
+ rsAssert(!"Allocation::subData called with mismatched size");
+ return;
+ }
+
+ for (uint32_t line=yoff; line < (yoff+h); line++) {
+ uint8_t * ptr = static_cast<uint8_t *>(mPtr);
+ memcpy(dst, src, lineSize);
+ src += lineSize;
+ dst += destW * eSize;
+ }
+}
+
+void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+ uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes)
+{
+}
+
+
+
+/////////////////
+//
+
+
+namespace android {
+namespace renderscript {
+
+RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype)
+{
+ const Type * type = static_cast<const Type *>(vtype);
+
+ Allocation * alloc = new Allocation(type);
+ alloc->incUserRef();
+ return alloc;
+}
+
+RsAllocation rsi_AllocationCreatePredefSized(Context *rsc, RsElementPredefined t, size_t count)
+{
+ RsElement e = rsi_ElementGetPredefined(rsc, t);
+ return rsi_AllocationCreateSized(rsc, e, count);
+}
+
+RsAllocation rsi_AllocationCreateSized(Context *rsc, RsElement e, size_t count)
+{
+ Type * type = new Type();
+ type->setDimX(count);
+ type->setElement(static_cast<Element *>(e));
+ type->compute();
+ return rsi_AllocationCreateTyped(rsc, type);
+}
+
+void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, uint32_t baseMipLevel)
+{
+ Allocation *alloc = static_cast<Allocation *>(va);
+ alloc->uploadToTexture(baseMipLevel);
+}
+
+void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va)
+{
+ Allocation *alloc = static_cast<Allocation *>(va);
+ alloc->uploadToBufferObject();
+}
+
+static void mip565(const Adapter2D &out, const Adapter2D &in)
+{
+ uint32_t w = out.getDimX();
+ uint32_t h = out.getDimY();
+
+ for (uint32_t y=0; y < h; y++) {
+ uint16_t *oPtr = static_cast<uint16_t *>(out.getElement(0, y));
+ const uint16_t *i1 = static_cast<uint16_t *>(in.getElement(0, y*2));
+ const uint16_t *i2 = static_cast<uint16_t *>(in.getElement(0, y*2+1));
+
+ for (uint32_t x=0; x < w; x++) {
+ *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]);
+ oPtr ++;
+ i1 += 2;
+ i2 += 2;
+ }
+ }
+}
+
+static void mip8888(const Adapter2D &out, const Adapter2D &in)
+{
+ uint32_t w = out.getDimX();
+ uint32_t h = out.getDimY();
+
+ for (uint32_t y=0; y < h; y++) {
+ uint32_t *oPtr = static_cast<uint32_t *>(out.getElement(0, y));
+ const uint32_t *i1 = static_cast<uint32_t *>(in.getElement(0, y*2));
+ const uint32_t *i2 = static_cast<uint32_t *>(in.getElement(0, y*2+1));
+
+ for (uint32_t x=0; x < w; x++) {
+ *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]);
+ oPtr ++;
+ i1 += 2;
+ i2 += 2;
+ }
+ }
+}
+
+static void mip(const Adapter2D &out, const Adapter2D &in)
+{
+ switch(out.getBaseType()->getElement()->getSizeBits()) {
+ case 32:
+ mip8888(out, in);
+ break;
+ case 16:
+ mip565(out, in);
+ break;
+
+ }
+
+}
+
+typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count);
+
+static void elementConverter_cpy_16(void *dst, const void *src, uint32_t count)
+{
+ memcpy(dst, src, count * 2);
+}
+static void elementConverter_cpy_8(void *dst, const void *src, uint32_t count)
+{
+ memcpy(dst, src, count);
+}
+static void elementConverter_cpy_32(void *dst, const void *src, uint32_t count)
+{
+ memcpy(dst, src, count * 4);
+}
+
+
+static void elementConverter_888_to_565(void *dst, const void *src, uint32_t count)
+{
+ uint16_t *d = static_cast<uint16_t *>(dst);
+ const uint8_t *s = static_cast<const uint8_t *>(src);
+
+ while(count--) {
+ *d = rs888to565(s[0], s[1], s[2]);
+ d++;
+ s+= 3;
+ }
+}
+
+static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t count)
+{
+ uint16_t *d = static_cast<uint16_t *>(dst);
+ const uint8_t *s = static_cast<const uint8_t *>(src);
+
+ while(count--) {
+ *d = rs888to565(s[0], s[1], s[2]);
+ d++;
+ s+= 4;
+ }
+}
+
+static ElementConverter_t pickConverter(const Element *dst, const Element *src)
+{
+ GLenum srcGLType = src->getGLType();
+ GLenum srcGLFmt = src->getGLFormat();
+ GLenum dstGLType = dst->getGLType();
+ GLenum dstGLFmt = dst->getGLFormat();
+
+ if (srcGLFmt == dstGLFmt && srcGLType == dstGLType) {
+ switch(dst->getSizeBytes()) {
+ case 4:
+ return elementConverter_cpy_32;
+ case 2:
+ return elementConverter_cpy_16;
+ case 1:
+ return elementConverter_cpy_8;
+ }
+ }
+
+ if (srcGLType == GL_UNSIGNED_BYTE &&
+ srcGLFmt == GL_RGB &&
+ dstGLType == GL_UNSIGNED_SHORT_5_6_5 &&
+ dstGLType == GL_RGB) {
+
+ return elementConverter_888_to_565;
+ }
+
+ if (srcGLType == GL_UNSIGNED_BYTE &&
+ srcGLFmt == GL_RGBA &&
+ dstGLType == GL_UNSIGNED_SHORT_5_6_5 &&
+ dstGLType == GL_RGB) {
+
+ return elementConverter_8888_to_565;
+ }
+
+ LOGE("pickConverter, unsuported combo, src %p, dst %p", src, dst);
+ return 0;
+}
+
+
+RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src, bool genMips, const void *data)
+{
+ const Element *src = static_cast<const Element *>(_src);
+ const Element *dst = static_cast<const Element *>(_dst);
+ rsAssert(!(w & (w-1)));
+ rsAssert(!(h & (h-1)));
+
+ //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips);
+ rsi_TypeBegin(rsc, _dst);
+ rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
+ rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
+ if (genMips) {
+ rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1);
+ }
+ RsType type = rsi_TypeCreate(rsc);
+
+ RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type);
+ Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+ if (texAlloc == NULL) {
+ LOGE("Memory allocation failure");
+ return NULL;
+ }
+ texAlloc->incUserRef();
+
+ ElementConverter_t cvt = pickConverter(dst, src);
+ cvt(texAlloc->getPtr(), data, w * h);
+
+ if (genMips) {
+ Adapter2D adapt(texAlloc);
+ Adapter2D adapt2(texAlloc);
+ for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+ adapt.setLOD(lod);
+ adapt2.setLOD(lod + 1);
+ mip(adapt2, adapt);
+ }
+ }
+
+ return texAlloc;
+}
+
+RsAllocation rsi_AllocationCreateFromBitmapBoxed(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src, bool genMips, const void *data)
+{
+ const Element *srcE = static_cast<const Element *>(_src);
+ const Element *dstE = static_cast<const Element *>(_dst);
+ uint32_t w2 = rsHigherPow2(w);
+ uint32_t h2 = rsHigherPow2(h);
+
+ if ((w2 == w) && (h2 == h)) {
+ return rsi_AllocationCreateFromBitmap(rsc, w, h, _dst, _src, genMips, data);
+ }
+
+ uint32_t bpp = srcE->getSizeBytes();
+ size_t size = w2 * h2 * bpp;
+ uint8_t *tmp = static_cast<uint8_t *>(malloc(size));
+ memset(tmp, 0, size);
+
+ const uint8_t * src = static_cast<const uint8_t *>(data);
+ for (uint32_t y = 0; y < h; y++) {
+ uint8_t * ydst = &tmp[(y + ((h2 - h) >> 1)) * w2 * bpp];
+ memcpy(&ydst[(w2 - w) >> 1], src, w * bpp);
+ src += w * bpp;
+ }
+
+ RsAllocation ret = rsi_AllocationCreateFromBitmap(rsc, w2, h2, _dst, _src, genMips, tmp);
+ free(tmp);
+ return ret;
+
+
+
+
+}
+
+
+RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
+{
+ bool use32bpp = false;
+
+ typedef struct _Win3xBitmapHeader
+ {
+ uint16_t type;
+ uint32_t totalSize;
+ uint32_t reserved;
+ uint32_t offset;
+ int32_t hdrSize; /* Size of this header in bytes */
+ int32_t width; /* Image width in pixels */
+ int32_t height; /* Image height in pixels */
+ int16_t planes; /* Number of color planes */
+ int16_t bpp; /* Number of bits per pixel */
+ /* Fields added for Windows 3.x follow this line */
+ int32_t compression; /* Compression methods used */
+ int32_t sizeOfBitmap; /* Size of bitmap in bytes */
+ int32_t horzResolution; /* Horizontal resolution in pixels per meter */
+ int32_t vertResolution; /* Vertical resolution in pixels per meter */
+ int32_t colorsUsed; /* Number of colors in the image */
+ int32_t colorsImportant; /* Minimum number of important colors */
+ } __attribute__((__packed__)) WIN3XBITMAPHEADER;
+
+ _Win3xBitmapHeader hdr;
+
+ FILE *f = fopen(file, "rb");
+ if (f == NULL) {
+ LOGE("rsAllocationCreateFromBitmap failed to open file %s", file);
+ return NULL;
+ }
+ memset(&hdr, 0, sizeof(hdr));
+ fread(&hdr, sizeof(hdr), 1, f);
+
+ if (hdr.bpp != 24) {
+ LOGE("Unsuported BMP type");
+ fclose(f);
+ return NULL;
+ }
+
+ int32_t texWidth = rsHigherPow2(hdr.width);
+ int32_t texHeight = rsHigherPow2(hdr.height);
+
+ if (use32bpp) {
+ rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGBA_8888));
+ } else {
+ rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+ }
+ rsi_TypeAdd(rsc, RS_DIMENSION_X, texWidth);
+ rsi_TypeAdd(rsc, RS_DIMENSION_Y, texHeight);
+ if (genMips) {
+ rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1);
+ }
+ RsType type = rsi_TypeCreate(rsc);
+
+ RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type);
+ Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc);
+ texAlloc->incUserRef();
+ if (texAlloc == NULL) {
+ LOGE("Memory allocation failure");
+ fclose(f);
+ return NULL;
+ }
+
+ // offset to letterbox if height is not pow2
+ Adapter2D adapt(texAlloc);
+ uint8_t * fileInBuf = new uint8_t[texWidth * 3];
+ uint32_t yOffset = (hdr.width - hdr.height) / 2;
+
+ if (use32bpp) {
+ uint8_t *tmp = static_cast<uint8_t *>(adapt.getElement(0, yOffset));
+ for (int y=0; y < hdr.height; y++) {
+ fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+ fread(fileInBuf, 1, hdr.width * 3, f);
+ for(int x=0; x < hdr.width; x++) {
+ tmp[0] = fileInBuf[x*3 + 2];
+ tmp[1] = fileInBuf[x*3 + 1];
+ tmp[2] = fileInBuf[x*3];
+ tmp[3] = 0xff;
+ tmp += 4;
+ }
+ }
+ } else {
+ uint16_t *tmp = static_cast<uint16_t *>(adapt.getElement(0, yOffset));
+ for (int y=0; y < hdr.height; y++) {
+ fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+ fread(fileInBuf, 1, hdr.width * 3, f);
+ for(int x=0; x < hdr.width; x++) {
+ *tmp = rs888to565(fileInBuf[x*3 + 2], fileInBuf[x*3 + 1], fileInBuf[x*3]);
+ tmp++;
+ }
+ }
+ }
+
+ fclose(f);
+ delete [] fileInBuf;
+
+ if (genMips) {
+ Adapter2D adapt2(texAlloc);
+ for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
+ adapt.setLOD(lod);
+ adapt2.setLOD(lod + 1);
+ mip(adapt2, adapt);
+ }
+ }
+
+ return texAlloc;
+}
+
+void rsi_AllocationData(Context *rsc, RsAllocation va, const void *data, uint32_t sizeBytes)
+{
+ Allocation *a = static_cast<Allocation *>(va);
+ a->data(data, sizeBytes);
+ rsc->allocationCheck(a);
+}
+
+void rsi_Allocation1DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes)
+{
+ Allocation *a = static_cast<Allocation *>(va);
+ a->subData(xoff, count, data, sizeBytes);
+ rsc->allocationCheck(a);
+}
+
+void rsi_Allocation2DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes)
+{
+ Allocation *a = static_cast<Allocation *>(va);
+ a->subData(xoff, yoff, w, h, data, sizeBytes);
+ rsc->allocationCheck(a);
+}
+
+void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data)
+{
+ Allocation *a = static_cast<Allocation *>(va);
+ a->read(data);
+}
+
+
+}
+}
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
new file mode 100644
index 0000000..1f58ec5
--- /dev/null
+++ b/libs/rs/rsAllocation.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_ALLOCATION_H
+#define ANDROID_STRUCTURED_ALLOCATION_H
+
+#include "rsType.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+
+class Allocation : public ObjectBase
+{
+ // The graphics equilivent of malloc. The allocation contains a structure of elements.
+
+
+public:
+ // By policy this allocation will hold a pointer to the type
+ // but will not destroy it on destruction.
+ Allocation(const Type *);
+ virtual ~Allocation();
+
+ void setCpuWritable(bool);
+ void setGpuWritable(bool);
+ void setCpuReadable(bool);
+ void setGpuReadable(bool);
+
+ bool fixAllocation();
+
+ void * getPtr() const {return mPtr;}
+ const Type * getType() const {return mType.get();}
+
+ void uploadToTexture(uint32_t lodOffset = 0);
+ uint32_t getTextureID() const {return mTextureID;}
+
+ void uploadToBufferObject();
+ uint32_t getBufferObjectID() const {return mBufferID;}
+
+
+ void data(const void *data, uint32_t sizeBytes);
+ void subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes);
+ void subData(uint32_t xoff, uint32_t yoff,
+ uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes);
+ void subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
+ uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes);
+
+ void read(void *data);
+
+ void enableGLVertexBuffers() const;
+ void setupGLIndexBuffers() const;
+
+
+protected:
+ ObjectBaseRef<const Type> mType;
+ void * mPtr;
+
+ // Usage restrictions
+ bool mCpuWrite;
+ bool mCpuRead;
+ bool mGpuWrite;
+ bool mGpuRead;
+
+ // more usage hint data from the application
+ // which can be used by a driver to pick the best memory type.
+ // Likely ignored for now
+ float mReadWriteRatio;
+ float mUpdateSize;
+
+
+ // Is this a legal structure to be used as a texture source.
+ // Initially this will require 1D or 2D and color data
+ bool mIsTexture;
+ uint32_t mTextureID;
+
+ // Is this a legal structure to be used as a vertex source.
+ // Initially this will require 1D and x(yzw). Additional per element data
+ // is allowed.
+ bool mIsVertexBuffer;
+ uint32_t mBufferID;
+};
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp
new file mode 100644
index 0000000..4a043f3
--- /dev/null
+++ b/libs/rs/rsComponent.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsComponent.h"
+#include <GLES/gl.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Component::Component()
+{
+ mType = FLOAT;
+ mKind = USER;
+ mIsNormalized = false;
+ mBits = 0;
+}
+
+Component::Component(
+ DataKind dk, DataType dt,
+ bool isNormalized, uint32_t bits, const char * name)
+{
+ mType = dt;
+ mKind = dk;
+ mIsNormalized = isNormalized;
+ mBits = bits;
+ if (name) {
+ mName = name;
+ }
+}
+
+const char * Component::getCType() const
+{
+ switch(mType) {
+ case FLOAT:
+ return "float";
+ case SIGNED:
+ case UNSIGNED:
+ switch(mBits) {
+ case 32:
+ return "int";
+ case 16:
+ return "short";
+ case 8:
+ return "char";
+ }
+ break;
+ }
+ return NULL;
+}
+
+Component::~Component()
+{
+}
+
+uint32_t Component::getGLType() const
+{
+ switch(mType) {
+ case RS_TYPE_FLOAT:
+ rsAssert(mBits == 32);
+ return GL_FLOAT;
+ case RS_TYPE_SIGNED:
+ switch(mBits) {
+ case 32:
+ return 0;//GL_INT;
+ case 16:
+ return GL_SHORT;
+ case 8:
+ return GL_BYTE;
+ }
+ break;
+ case RS_TYPE_UNSIGNED:
+ switch(mBits) {
+ case 32:
+ return 0;//GL_UNSIGNED_INT;
+ case 16:
+ return GL_UNSIGNED_SHORT;
+ case 8:
+ return GL_UNSIGNED_BYTE;
+ }
+ break;
+ }
+ //rsAssert(!"Bad type");
+ //LOGE("mType %i, mKind %i, mBits %i, mIsNormalized %i", mType, mKind, mBits, mIsNormalized);
+ return 0;
+}
+
+
diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h
new file mode 100644
index 0000000..5856524
--- /dev/null
+++ b/libs/rs/rsComponent.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_STRUCTURED_COMPONENT_H
+#define ANDROID_RS_STRUCTURED_COMPONENT_H
+
+#include "rsUtils.h"
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Component : public ObjectBase
+{
+public:
+ enum DataType {
+ FLOAT,
+ UNSIGNED,
+ SIGNED
+ };
+
+ enum DataKind {
+ USER,
+ RED, GREEN, BLUE, ALPHA, LUMINANCE, INTENSITY,
+ X, Y, Z, W,
+ S, T, Q, R,
+ NX, NY, NZ,
+ INDEX,
+ POINT_SIZE
+ };
+
+
+ Component(DataKind dk, DataType dt, bool isNorm, uint32_t bits, const char *);
+ virtual ~Component();
+
+ DataType getType() const {return mType;}
+ bool getIsNormalized() const {return mIsNormalized;}
+ DataKind getKind() const {return mKind;}
+ uint32_t getBits() const {return mBits;}
+
+ uint32_t getGLType() const;
+ const char * getCType() const;
+
+ const char * getComponentName() const {return mName.string();}
+
+protected:
+
+ DataType mType;
+ bool mIsNormalized;
+ DataKind mKind;
+ uint32_t mBits;
+ String8 mName;
+
+private:
+ Component();
+};
+
+
+}
+}
+
+#endif //ANDROID_RS_STRUCTURED_COMPONENT_H
+
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
new file mode 100644
index 0000000..04f6e07
--- /dev/null
+++ b/libs/rs/rsContext.cpp
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsDevice.h"
+#include "rsContext.h"
+#include "rsThreadIO.h"
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+pthread_key_t Context::gThreadTLSKey = 0;
+
+void Context::initEGL()
+{
+ mEGL.mNumConfigs = -1;
+ EGLint configAttribs[128];
+ EGLint *configAttribsPtr = configAttribs;
+
+ memset(configAttribs, 0, sizeof(configAttribs));
+
+ configAttribsPtr[0] = EGL_SURFACE_TYPE;
+ configAttribsPtr[1] = EGL_WINDOW_BIT;
+ configAttribsPtr += 2;
+
+ if (mUseDepth) {
+ configAttribsPtr[0] = EGL_DEPTH_SIZE;
+ configAttribsPtr[1] = 16;
+ configAttribsPtr += 2;
+ }
+
+ configAttribsPtr[0] = EGL_NONE;
+ rsAssert(configAttribsPtr < (configAttribs + (sizeof(configAttribs) / sizeof(EGLint))));
+
+ mEGL.mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(mEGL.mDisplay, &mEGL.mMajorVersion, &mEGL.mMinorVersion);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(mEGL.mDisplay, configAttribs, mWndSurface, &mEGL.mConfig);
+ if (err) {
+ LOGE("couldn't find an EGLConfig matching the screen format\n");
+ }
+ //eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs);
+
+ if (mWndSurface) {
+ mEGL.mSurface = eglCreateWindowSurface(mEGL.mDisplay, mEGL.mConfig, mWndSurface, NULL);
+ } else {
+ mEGL.mSurface = eglCreateWindowSurface(mEGL.mDisplay, mEGL.mConfig,
+ android_createDisplaySurface(),
+ NULL);
+ }
+
+ mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, NULL, NULL);
+ eglMakeCurrent(mEGL.mDisplay, mEGL.mSurface, mEGL.mSurface, mEGL.mContext);
+ eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth);
+ eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight);
+
+
+ mGL.mVersion = glGetString(GL_VERSION);
+ mGL.mVendor = glGetString(GL_VENDOR);
+ mGL.mRenderer = glGetString(GL_RENDERER);
+ mGL.mExtensions = glGetString(GL_EXTENSIONS);
+
+ LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion);
+ LOGV("GL Version %s", mGL.mVersion);
+ LOGV("GL Vendor %s", mGL.mVendor);
+ LOGV("GL Renderer %s", mGL.mRenderer);
+ LOGV("GL Extensions %s", mGL.mExtensions);
+
+ if ((strlen((const char *)mGL.mVersion) < 12) || memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) {
+ LOGE("Error, OpenGL ES Lite not supported");
+ } else {
+ sscanf((const char *)mGL.mVersion + 13, "%i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion);
+ }
+}
+
+bool Context::runScript(Script *s, uint32_t launchID)
+{
+ ObjectBaseRef<ProgramFragment> frag(mFragment);
+ ObjectBaseRef<ProgramVertex> vtx(mVertex);
+ ObjectBaseRef<ProgramFragmentStore> store(mFragmentStore);
+
+ bool ret = s->run(this, launchID);
+
+ mFragment.set(frag);
+ mVertex.set(vtx);
+ mFragmentStore.set(store);
+ return ret;
+}
+
+
+bool Context::runRootScript()
+{
+#if RS_LOG_TIMES
+ timerSet(RS_TIMER_CLEAR_SWAP);
+#endif
+ rsAssert(mRootScript->mEnviroment.mIsRoot);
+
+ //glColor4f(1,1,1,1);
+ //glEnable(GL_LIGHT0);
+ glViewport(0, 0, mEGL.mWidth, mEGL.mHeight);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_POINT_SMOOTH);
+
+ glClearColor(mRootScript->mEnviroment.mClearColor[0],
+ mRootScript->mEnviroment.mClearColor[1],
+ mRootScript->mEnviroment.mClearColor[2],
+ mRootScript->mEnviroment.mClearColor[3]);
+ if (mUseDepth) {
+ glDepthMask(GL_TRUE);
+ glClearDepthf(mRootScript->mEnviroment.mClearDepth);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ } else {
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+#if RS_LOG_TIMES
+ timerSet(RS_TIMER_SCRIPT);
+#endif
+ bool ret = runScript(mRootScript.get(), 0);
+ return ret;
+}
+
+uint64_t Context::getTime() const
+{
+ struct timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+}
+
+void Context::timerReset()
+{
+ for (int ct=0; ct < _RS_TIMER_TOTAL; ct++) {
+ mTimers[ct] = 0;
+ }
+}
+
+void Context::timerInit()
+{
+ mTimeLast = getTime();
+ mTimeFrame = mTimeLast;
+ mTimeLastFrame = mTimeLast;
+ mTimerActive = RS_TIMER_INTERNAL;
+ timerReset();
+}
+
+void Context::timerFrame()
+{
+ mTimeLastFrame = mTimeFrame;
+ mTimeFrame = getTime();
+}
+
+void Context::timerSet(Timers tm)
+{
+ uint64_t last = mTimeLast;
+ mTimeLast = getTime();
+ mTimers[mTimerActive] += mTimeLast - last;
+ mTimerActive = tm;
+}
+
+void Context::timerPrint()
+{
+ double total = 0;
+ for (int ct = 0; ct < _RS_TIMER_TOTAL; ct++) {
+ total += mTimers[ct];
+ }
+ uint64_t frame = mTimeFrame - mTimeLastFrame;
+
+ LOGV("RS: Frame (%lli), Script %2.1f (%lli), Clear & Swap %2.1f (%lli), Idle %2.1f (%lli), Internal %2.1f (%lli)",
+ frame / 1000000,
+ 100.0 * mTimers[RS_TIMER_SCRIPT] / total, mTimers[RS_TIMER_SCRIPT] / 1000000,
+ 100.0 * mTimers[RS_TIMER_CLEAR_SWAP] / total, mTimers[RS_TIMER_CLEAR_SWAP] / 1000000,
+ 100.0 * mTimers[RS_TIMER_IDLE] / total, mTimers[RS_TIMER_IDLE] / 1000000,
+ 100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000);
+}
+
+void Context::setupCheck()
+{
+ if (mFragmentStore.get()) {
+ mFragmentStore->setupGL(this, &mStateFragmentStore);
+ }
+ if (mFragment.get()) {
+ mFragment->setupGL(this, &mStateFragment);
+ }
+ if (mVertex.get()) {
+ mVertex->setupGL(this, &mStateVertex);
+ }
+
+}
+
+
+void * Context::threadProc(void *vrsc)
+{
+ Context *rsc = static_cast<Context *>(vrsc);
+
+ rsc->initEGL();
+
+ ScriptTLSStruct *tlsStruct = new ScriptTLSStruct;
+ if (!tlsStruct) {
+ LOGE("Error allocating tls storage");
+ return NULL;
+ }
+ tlsStruct->mContext = rsc;
+ tlsStruct->mScript = NULL;
+ int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct);
+ if (status) {
+ LOGE("pthread_setspecific %i", status);
+ }
+
+ rsc->mStateVertex.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+ rsc->setVertex(NULL);
+ rsc->mStateFragment.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+ rsc->setFragment(NULL);
+ rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight);
+ rsc->setFragmentStore(NULL);
+
+ rsc->mRunning = true;
+ bool mDraw = true;
+ while (!rsc->mExit) {
+ mDraw |= rsc->mIO.playCoreCommands(rsc, !mDraw);
+ mDraw &= (rsc->mRootScript.get() != NULL);
+
+ if (mDraw) {
+ mDraw = rsc->runRootScript();
+#if RS_LOG_TIMES
+ rsc->timerSet(RS_TIMER_CLEAR_SWAP);
+#endif
+ eglSwapBuffers(rsc->mEGL.mDisplay, rsc->mEGL.mSurface);
+#if RS_LOG_TIMES
+ rsc->timerFrame();
+ rsc->timerSet(RS_TIMER_INTERNAL);
+ rsc->timerPrint();
+ rsc->timerReset();
+#endif
+ }
+ if (rsc->mObjDestroy.mNeedToEmpty) {
+ rsc->objDestroyOOBRun();
+ }
+ }
+
+ LOGV("RS Thread exiting");
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(rsc->mEGL.mDisplay, rsc->mEGL.mSurface);
+ eglTerminate(rsc->mEGL.mDisplay);
+ rsc->objDestroyOOBRun();
+ LOGV("RS Thread exited");
+ return NULL;
+}
+
+Context::Context(Device *dev, Surface *sur, bool useDepth)
+{
+ dev->addContext(this);
+ mDev = dev;
+ mRunning = false;
+ mExit = false;
+ mUseDepth = useDepth;
+
+ int status;
+ pthread_attr_t threadAttr;
+
+ status = pthread_key_create(&gThreadTLSKey, NULL);
+ if (status) {
+ LOGE("Failed to init thread tls key.");
+ return;
+ }
+
+ status = pthread_attr_init(&threadAttr);
+ if (status) {
+ LOGE("Failed to init thread attribute.");
+ return;
+ }
+
+ sched_param sparam;
+ sparam.sched_priority = ANDROID_PRIORITY_DISPLAY;
+ pthread_attr_setschedparam(&threadAttr, &sparam);
+
+ mWndSurface = sur;
+
+ objDestroyOOBInit();
+ timerInit();
+
+ LOGV("RS Launching thread");
+ status = pthread_create(&mThreadId, &threadAttr, threadProc, this);
+ if (status) {
+ LOGE("Failed to start rs context thread.");
+ }
+
+ while(!mRunning) {
+ sleep(1);
+ }
+
+ pthread_attr_destroy(&threadAttr);
+}
+
+Context::~Context()
+{
+ LOGV("Context::~Context");
+ mExit = true;
+ void *res;
+
+ mIO.shutdown();
+ int status = pthread_join(mThreadId, &res);
+ objDestroyOOBRun();
+
+ if (mDev) {
+ mDev->removeContext(this);
+ pthread_key_delete(gThreadTLSKey);
+ }
+
+ objDestroyOOBDestroy();
+}
+
+void Context::setRootScript(Script *s)
+{
+ mRootScript.set(s);
+}
+
+void Context::setFragmentStore(ProgramFragmentStore *pfs)
+{
+ if (pfs == NULL) {
+ mFragmentStore.set(mStateFragmentStore.mDefault);
+ } else {
+ mFragmentStore.set(pfs);
+ }
+}
+
+void Context::setFragment(ProgramFragment *pf)
+{
+ if (pf == NULL) {
+ mFragment.set(mStateFragment.mDefault);
+ } else {
+ mFragment.set(pf);
+ }
+}
+
+void Context::allocationCheck(const Allocation *a)
+{
+ mVertex->checkUpdatedAllocation(a);
+ mFragment->checkUpdatedAllocation(a);
+ mFragmentStore->checkUpdatedAllocation(a);
+}
+
+void Context::setVertex(ProgramVertex *pv)
+{
+ if (pv == NULL) {
+ mVertex.set(mStateVertex.mDefault);
+ } else {
+ mVertex.set(pv);
+ }
+}
+
+void Context::assignName(ObjectBase *obj, const char *name, uint32_t len)
+{
+ rsAssert(!obj->getName());
+ obj->setName(name, len);
+ mNames.add(obj);
+}
+
+void Context::removeName(ObjectBase *obj)
+{
+ for(size_t ct=0; ct < mNames.size(); ct++) {
+ if (obj == mNames[ct]) {
+ mNames.removeAt(ct);
+ return;
+ }
+ }
+}
+
+ObjectBase * Context::lookupName(const char *name) const
+{
+ for(size_t ct=0; ct < mNames.size(); ct++) {
+ if (!strcmp(name, mNames[ct]->getName())) {
+ return mNames[ct];
+ }
+ }
+ return NULL;
+}
+
+void Context::appendNameDefines(String8 *str) const
+{
+ char buf[256];
+ for (size_t ct=0; ct < mNames.size(); ct++) {
+ str->append("#define NAMED_");
+ str->append(mNames[ct]->getName());
+ str->append(" ");
+ sprintf(buf, "%i\n", (int)mNames[ct]);
+ str->append(buf);
+ }
+}
+
+void Context::appendVarDefines(String8 *str) const
+{
+ char buf[256];
+ for (size_t ct=0; ct < mInt32Defines.size(); ct++) {
+ str->append("#define ");
+ str->append(mInt32Defines.keyAt(ct));
+ str->append(" ");
+ sprintf(buf, "%i\n", (int)mInt32Defines.valueAt(ct));
+ str->append(buf);
+
+ }
+ for (size_t ct=0; ct < mFloatDefines.size(); ct++) {
+ str->append("#define ");
+ str->append(mFloatDefines.keyAt(ct));
+ str->append(" ");
+ sprintf(buf, "%ff\n", mFloatDefines.valueAt(ct));
+ str->append(buf);
+ }
+}
+
+bool Context::objDestroyOOBInit()
+{
+ int status = pthread_mutex_init(&mObjDestroy.mMutex, NULL);
+ if (status) {
+ LOGE("Context::ObjDestroyOOBInit mutex init failure");
+ return false;
+ }
+ return true;
+}
+
+void Context::objDestroyOOBRun()
+{
+ if (mObjDestroy.mNeedToEmpty) {
+ int status = pthread_mutex_lock(&mObjDestroy.mMutex);
+ if (status) {
+ LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status);
+ return;
+ }
+
+ for (size_t ct = 0; ct < mObjDestroy.mDestroyList.size(); ct++) {
+ mObjDestroy.mDestroyList[ct]->decUserRef();
+ }
+ mObjDestroy.mDestroyList.clear();
+ mObjDestroy.mNeedToEmpty = false;
+
+ status = pthread_mutex_unlock(&mObjDestroy.mMutex);
+ if (status) {
+ LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status);
+ }
+ }
+}
+
+void Context::objDestroyOOBDestroy()
+{
+ rsAssert(!mObjDestroy.mNeedToEmpty);
+ pthread_mutex_destroy(&mObjDestroy.mMutex);
+}
+
+void Context::objDestroyAdd(ObjectBase *obj)
+{
+ int status = pthread_mutex_lock(&mObjDestroy.mMutex);
+ if (status) {
+ LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status);
+ return;
+ }
+
+ mObjDestroy.mNeedToEmpty = true;
+ mObjDestroy.mDestroyList.add(obj);
+
+ status = pthread_mutex_unlock(&mObjDestroy.mMutex);
+ if (status) {
+ LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status);
+ }
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+//
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_ContextBindRootScript(Context *rsc, RsScript vs)
+{
+ Script *s = static_cast<Script *>(vs);
+ rsc->setRootScript(s);
+}
+
+void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs)
+{
+ Sampler *s = static_cast<Sampler *>(vs);
+
+ if (slot > RS_MAX_SAMPLER_SLOT) {
+ LOGE("Invalid sampler slot");
+ return;
+ }
+
+ s->bindToContext(&rsc->mStateSampler, slot);
+}
+
+void rsi_ContextBindProgramFragmentStore(Context *rsc, RsProgramFragmentStore vpfs)
+{
+ ProgramFragmentStore *pfs = static_cast<ProgramFragmentStore *>(vpfs);
+ rsc->setFragmentStore(pfs);
+}
+
+void rsi_ContextBindProgramFragment(Context *rsc, RsProgramFragment vpf)
+{
+ ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+ rsc->setFragment(pf);
+}
+
+void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv)
+{
+ ProgramVertex *pv = static_cast<ProgramVertex *>(vpv);
+ rsc->setVertex(pv);
+}
+
+void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len)
+{
+ ObjectBase *ob = static_cast<ObjectBase *>(obj);
+ rsc->assignName(ob, name, len);
+}
+
+void rsi_ObjDestroy(Context *rsc, void *obj)
+{
+ ObjectBase *ob = static_cast<ObjectBase *>(obj);
+ rsc->removeName(ob);
+ ob->decUserRef();
+}
+
+void rsi_ContextSetDefineF(Context *rsc, const char* name, float value)
+{
+ rsc->addInt32Define(name, value);
+}
+
+void rsi_ContextSetDefineI32(Context *rsc, const char* name, int32_t value)
+{
+ rsc->addFloatDefine(name, value);
+}
+
+}
+}
+
+
+RsContext rsContextCreate(RsDevice vdev, void *sur, uint32_t version, bool useDepth)
+{
+ Device * dev = static_cast<Device *>(vdev);
+ Context *rsc = new Context(dev, (Surface *)sur, useDepth);
+ return rsc;
+}
+
+void rsContextDestroy(RsContext vrsc)
+{
+ Context * rsc = static_cast<Context *>(vrsc);
+ delete rsc;
+}
+
+void rsObjDestroyOOB(RsContext vrsc, void *obj)
+{
+ Context * rsc = static_cast<Context *>(vrsc);
+ rsc->objDestroyAdd(static_cast<ObjectBase *>(obj));
+}
+
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
new file mode 100644
index 0000000..634416b
--- /dev/null
+++ b/libs/rs/rsContext.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_CONTEXT_H
+#define ANDROID_RS_CONTEXT_H
+
+#include "rsUtils.h"
+
+#include <ui/Surface.h>
+
+#include "rsThreadIO.h"
+#include "rsType.h"
+#include "rsMatrix.h"
+#include "rsAllocation.h"
+#include "rsTriangleMesh.h"
+#include "rsSimpleMesh.h"
+#include "rsMesh.h"
+#include "rsDevice.h"
+#include "rsScriptC.h"
+#include "rsAllocation.h"
+#include "rsAdapter.h"
+#include "rsSampler.h"
+#include "rsLight.h"
+#include "rsProgramFragment.h"
+#include "rsProgramFragmentStore.h"
+#include "rsProgramVertex.h"
+
+#include "rsgApiStructs.h"
+#include "rsLocklessFifo.h"
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Context
+{
+public:
+ Context(Device *, Surface *, bool useDepth);
+ ~Context();
+
+ static pthread_key_t gThreadTLSKey;
+ struct ScriptTLSStruct {
+ Context * mContext;
+ Script * mScript;
+ };
+
+
+ //StructuredAllocationContext mStateAllocation;
+ ElementState mStateElement;
+ TypeState mStateType;
+ SamplerState mStateSampler;
+ ProgramFragmentState mStateFragment;
+ ProgramFragmentStoreState mStateFragmentStore;
+ ProgramVertexState mStateVertex;
+ LightState mStateLight;
+
+ TriangleMeshContext mStateTriangleMesh;
+
+ ScriptCState mScriptC;
+
+ void swapBuffers();
+ void setRootScript(Script *);
+ void setVertex(ProgramVertex *);
+ void setFragment(ProgramFragment *);
+ void setFragmentStore(ProgramFragmentStore *);
+
+ void updateSurface(void *sur);
+
+ const ProgramFragment * getFragment() {return mFragment.get();}
+ const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();}
+ const ProgramVertex * getVertex() {return mVertex.get();}
+
+ void setupCheck();
+ void allocationCheck(const Allocation *);
+
+ void assignName(ObjectBase *obj, const char *name, uint32_t len);
+ void removeName(ObjectBase *obj);
+ ObjectBase * lookupName(const char *name) const;
+ void appendNameDefines(String8 *str) const;
+ void appendVarDefines(String8 *str) const;
+
+ ProgramFragment * getDefaultProgramFragment() const {
+ return mStateFragment.mDefault.get();
+ }
+ ProgramVertex * getDefaultProgramVertex() const {
+ return mStateVertex.mDefault.get();
+ }
+ ProgramFragmentStore * getDefaultProgramFragmentStore() const {
+ return mStateFragmentStore.mDefault.get();
+ }
+
+ void addInt32Define(const char* name, int32_t value) {
+ mInt32Defines.add(String8(name), value);
+ }
+
+ void addFloatDefine(const char* name, float value) {
+ mFloatDefines.add(String8(name), value);
+ }
+
+ uint32_t getWidth() const {return mEGL.mWidth;}
+ uint32_t getHeight() const {return mEGL.mHeight;}
+
+
+ ThreadIO mIO;
+ void objDestroyAdd(ObjectBase *);
+
+ // Timers
+ enum Timers {
+ RS_TIMER_IDLE,
+ RS_TIMER_INTERNAL,
+ RS_TIMER_SCRIPT,
+ RS_TIMER_CLEAR_SWAP,
+ _RS_TIMER_TOTAL
+ };
+ uint64_t getTime() const;
+ void timerInit();
+ void timerReset();
+ void timerSet(Timers);
+ void timerPrint();
+ void timerFrame();
+
+ bool checkVersion1_1() const {return (mGL.mMajorVersion > 1) || (mGL.mMinorVersion >= 1); }
+ bool checkVersion2_0() const {return mGL.mMajorVersion >= 2; }
+
+protected:
+ Device *mDev;
+
+ struct {
+ EGLint mNumConfigs;
+ EGLint mMajorVersion;
+ EGLint mMinorVersion;
+ EGLConfig mConfig;
+ EGLContext mContext;
+ EGLSurface mSurface;
+ EGLint mWidth;
+ EGLint mHeight;
+ EGLDisplay mDisplay;
+ } mEGL;
+
+ struct {
+ const uint8_t * mVendor;
+ const uint8_t * mRenderer;
+ const uint8_t * mVersion;
+ const uint8_t * mExtensions;
+
+ uint32_t mMajorVersion;
+ uint32_t mMinorVersion;
+
+ } mGL;
+
+ bool mRunning;
+ bool mExit;
+ bool mUseDepth;
+
+ pthread_t mThreadId;
+
+ ObjectBaseRef<Script> mRootScript;
+ ObjectBaseRef<ProgramFragment> mFragment;
+ ObjectBaseRef<ProgramVertex> mVertex;
+ ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
+
+
+ struct ObjDestroyOOB {
+ pthread_mutex_t mMutex;
+ Vector<ObjectBase *> mDestroyList;
+ bool mNeedToEmpty;
+ };
+ ObjDestroyOOB mObjDestroy;
+ bool objDestroyOOBInit();
+ void objDestroyOOBRun();
+ void objDestroyOOBDestroy();
+
+private:
+ Context();
+
+ void initEGL();
+
+ bool runScript(Script *s, uint32_t launchID);
+ bool runRootScript();
+
+ static void * threadProc(void *);
+
+ Surface *mWndSurface;
+
+ Vector<ObjectBase *> mNames;
+ KeyedVector<String8,int> mInt32Defines;
+ KeyedVector<String8,float> mFloatDefines;
+
+ uint64_t mTimers[_RS_TIMER_TOTAL];
+ Timers mTimerActive;
+ uint64_t mTimeLast;
+ uint64_t mTimeFrame;
+ uint64_t mTimeLastFrame;
+};
+
+
+}
+}
+#endif
diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp
new file mode 100644
index 0000000..1b3c41b
--- /dev/null
+++ b/libs/rs/rsDevice.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsDevice.h"
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Device::Device()
+{
+
+}
+
+Device::~Device()
+{
+
+}
+
+void Device::addContext(Context *rsc)
+{
+ mContexts.add(rsc);
+}
+
+void Device::removeContext(Context *rsc)
+{
+ for (size_t idx=0; idx < mContexts.size(); idx++) {
+ if (mContexts[idx] == rsc) {
+ mContexts.removeAt(idx);
+ break;
+ }
+ }
+}
+
+
+
+RsDevice rsDeviceCreate()
+{
+ Device * d = new Device();
+ return d;
+}
+
+void rsDeviceDestroy(RsDevice dev)
+{
+ Device * d = static_cast<Device *>(dev);
+ delete d;
+
+}
+
diff --git a/libs/rs/rsDevice.h b/libs/rs/rsDevice.h
new file mode 100644
index 0000000..156315f
--- /dev/null
+++ b/libs/rs/rsDevice.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_DEVICE_H
+#define ANDROID_RS_DEVICE_H
+
+#include "rsUtils.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Context;
+
+class Device {
+public:
+ Device();
+ ~Device();
+
+ void addContext(Context *);
+ void removeContext(Context *);
+
+protected:
+ Vector<Context *> mContexts;
+
+
+};
+
+
+
+
+
+}
+}
+#endif
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
new file mode 100644
index 0000000..6794522
--- /dev/null
+++ b/libs/rs/rsElement.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include <GLES/gl.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+void ElementState::initPredefined()
+{
+ Component * u_8 = new Component(Component::USER, Component::UNSIGNED, true, 8, 0);
+ Component * i_8 = new Component(Component::USER, Component::SIGNED, true, 8, 0);
+ Component * u_16 = new Component(Component::USER, Component::UNSIGNED, true, 16, 0);
+ Component * i_16 = new Component(Component::USER, Component::SIGNED, true, 16, 0);
+ Component * u_32 = new Component(Component::USER, Component::UNSIGNED, true, 32, 0);
+ Component * i_32 = new Component(Component::USER, Component::SIGNED, true, 32, 0);
+ Component * f_32 = new Component(Component::USER, Component::FLOAT, true, 32, 0);
+
+
+ Component * r_4 = new Component(Component::RED, Component::UNSIGNED, true, 4, 0);
+ Component * r_5 = new Component(Component::RED, Component::UNSIGNED, true, 5, 0);
+ Component * r_8 = new Component(Component::RED, Component::UNSIGNED, true, 8, 0);
+
+ Component * g_4 = new Component(Component::GREEN, Component::UNSIGNED, true, 4, 0);
+ Component * g_5 = new Component(Component::GREEN, Component::UNSIGNED, true, 5, 0);
+ Component * g_6 = new Component(Component::GREEN, Component::UNSIGNED, true, 6, 0);
+ Component * g_8 = new Component(Component::GREEN, Component::UNSIGNED, true, 8, 0);
+
+ Component * b_4 = new Component(Component::BLUE, Component::UNSIGNED, true, 4, 0);
+ Component * b_5 = new Component(Component::BLUE, Component::UNSIGNED, true, 5, 0);
+ Component * b_8 = new Component(Component::BLUE, Component::UNSIGNED, true, 8, 0);
+
+ Component * a_1 = new Component(Component::ALPHA, Component::UNSIGNED, true, 1, 0);
+ Component * a_4 = new Component(Component::ALPHA, Component::UNSIGNED, true, 4, 0);
+ Component * a_8 = new Component(Component::ALPHA, Component::UNSIGNED, true, 8, 0);
+
+ Component * idx_16 = new Component(Component::INDEX, Component::UNSIGNED, false, 16, 0);
+ Component * idx_32 = new Component(Component::INDEX, Component::UNSIGNED, false, 32, 0);
+
+ Component * x = new Component(Component::X, Component::FLOAT, false, 32, 0);
+ Component * y = new Component(Component::Y, Component::FLOAT, false, 32, 0);
+ Component * z = new Component(Component::Z, Component::FLOAT, false, 32, 0);
+
+ Component * nx = new Component(Component::NX, Component::FLOAT, false, 32, 0);
+ Component * ny = new Component(Component::NY, Component::FLOAT, false, 32, 0);
+ Component * nz = new Component(Component::NZ, Component::FLOAT, false, 32, 0);
+
+ Component * s = new Component(Component::S, Component::FLOAT, false, 32, 0);
+ Component * t = new Component(Component::T, Component::FLOAT, false, 32, 0);
+
+ Element * e;
+
+ e = new Element(1);
+ e->setComponent(0, u_8);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_U8, e));
+
+ e = new Element(1);
+ e->setComponent(0, i_8);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_I8, e));
+
+ e = new Element(1);
+ e->setComponent(0, u_16);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_U16, e));
+
+ e = new Element(1);
+ e->setComponent(0, i_16);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_I16, e));
+
+ e = new Element(1);
+ e->setComponent(0, u_32);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_U32, e));
+
+ e = new Element(1);
+ e->setComponent(0, i_32);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_I32, e));
+
+ e = new Element(1);
+ e->setComponent(0, f_32);
+ mPredefinedList.add(Predefined(RS_ELEMENT_USER_FLOAT, e));
+
+ e = new Element(1);
+ e->setComponent(0, a_8);
+ mPredefinedList.add(Predefined(RS_ELEMENT_A_8, e));
+
+ e = new Element(3);
+ e->setComponent(0, r_5);
+ e->setComponent(1, g_6);
+ e->setComponent(2, b_5);
+ mPredefinedList.add(Predefined(RS_ELEMENT_RGB_565, e));
+
+ e = new Element(4);
+ e->setComponent(0, r_5);
+ e->setComponent(1, g_5);
+ e->setComponent(2, b_5);
+ e->setComponent(3, a_1);
+ mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_5551, e));
+
+ e = new Element(4);
+ e->setComponent(0, r_4);
+ e->setComponent(1, g_4);
+ e->setComponent(2, b_4);
+ e->setComponent(3, a_4);
+ mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_4444, e));
+
+ e = new Element(3);
+ e->setComponent(0, r_8);
+ e->setComponent(1, g_8);
+ e->setComponent(2, b_8);
+ mPredefinedList.add(Predefined(RS_ELEMENT_RGB_888, e));
+
+ e = new Element(4);
+ e->setComponent(0, r_8);
+ e->setComponent(1, g_8);
+ e->setComponent(2, b_8);
+ e->setComponent(3, a_8);
+ mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_8888, e));
+
+ e = new Element(1);
+ e->setComponent(0, idx_16);
+ mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_16, e));
+
+ e = new Element(1);
+ e->setComponent(0, idx_32);
+ mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_32, e));
+
+ e = new Element(2);
+ e->setComponent(0, x);
+ e->setComponent(1, y);
+ mPredefinedList.add(Predefined(RS_ELEMENT_XY_F32, e));
+
+ e = new Element(3);
+ e->setComponent(0, x);
+ e->setComponent(1, y);
+ e->setComponent(2, z);
+ mPredefinedList.add(Predefined(RS_ELEMENT_XYZ_F32, e));
+
+ e = new Element(4);
+ e->setComponent(0, s);
+ e->setComponent(1, t);
+ e->setComponent(2, x);
+ e->setComponent(3, y);
+ mPredefinedList.add(Predefined(RS_ELEMENT_ST_XY_F32, e));
+
+ e = new Element(5);
+ e->setComponent(0, s);
+ e->setComponent(1, t);
+ e->setComponent(2, x);
+ e->setComponent(3, y);
+ e->setComponent(4, z);
+ mPredefinedList.add(Predefined(RS_ELEMENT_ST_XYZ_F32, e));
+
+ e = new Element(6);
+ e->setComponent(0, nx);
+ e->setComponent(1, ny);
+ e->setComponent(2, nz);
+ e->setComponent(3, x);
+ e->setComponent(4, y);
+ e->setComponent(5, z);
+ mPredefinedList.add(Predefined(RS_ELEMENT_NORM_XYZ_F32, e));
+
+ e = new Element(8);
+ e->setComponent(0, nx);
+ e->setComponent(1, ny);
+ e->setComponent(2, nz);
+ e->setComponent(3, s);
+ e->setComponent(4, t);
+ e->setComponent(5, x);
+ e->setComponent(6, y);
+ e->setComponent(7, z);
+ mPredefinedList.add(Predefined(RS_ELEMENT_NORM_ST_XYZ_F32, e));
+}
+
+
+Element::Element()
+{
+ mComponents = NULL;
+ mComponentCount = 0;
+}
+
+Element::Element(uint32_t count)
+{
+ mComponents = new ObjectBaseRef<Component> [count];
+ mComponentCount = count;
+}
+
+Element::~Element()
+{
+ clear();
+}
+
+void Element::clear()
+{
+ delete [] mComponents;
+ mComponents = NULL;
+ mComponentCount = 0;
+}
+
+void Element::setComponent(uint32_t idx, Component *c)
+{
+ rsAssert(!mComponents[idx].get());
+ rsAssert(idx < mComponentCount);
+ mComponents[idx].set(c);
+ c->incUserRef();
+}
+
+
+size_t Element::getSizeBits() const
+{
+ size_t total = 0;
+ for (size_t ct=0; ct < mComponentCount; ct++) {
+ total += mComponents[ct]->getBits();
+ }
+ return total;
+}
+
+size_t Element::getComponentOffsetBits(uint32_t componentNumber) const
+{
+ size_t offset = 0;
+ for (uint32_t ct = 0; ct < componentNumber; ct++) {
+ offset += mComponents[ct]->getBits();
+ }
+ return offset;
+}
+
+uint32_t Element::getGLType() const
+{
+ int bits[4];
+
+ if (mComponentCount > 4) {
+ return 0;
+ }
+
+ for (uint32_t ct=0; ct < mComponentCount; ct++) {
+ bits[ct] = mComponents[ct]->getBits();
+ if (mComponents[ct]->getType() != Component::UNSIGNED) {
+ return 0;
+ }
+ if (!mComponents[ct]->getIsNormalized()) {
+ return 0;
+ }
+ }
+
+ switch(mComponentCount) {
+ case 1:
+ if (bits[0] == 8) {
+ return GL_UNSIGNED_BYTE;
+ }
+ return 0;
+ case 2:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ return 0;
+ case 3:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8) &&
+ (bits[2] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ if ((bits[0] == 5) &&
+ (bits[1] == 6) &&
+ (bits[2] == 5)) {
+ return GL_UNSIGNED_SHORT_5_6_5;
+ }
+ return 0;
+ case 4:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8) &&
+ (bits[2] == 8) &&
+ (bits[3] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ if ((bits[0] == 4) &&
+ (bits[1] == 4) &&
+ (bits[2] == 4) &&
+ (bits[3] == 4)) {
+ return GL_UNSIGNED_SHORT_4_4_4_4;
+ }
+ if ((bits[0] == 5) &&
+ (bits[1] == 5) &&
+ (bits[2] == 5) &&
+ (bits[3] == 1)) {
+ return GL_UNSIGNED_SHORT_5_5_5_1;
+ }
+ }
+ return 0;
+}
+
+uint32_t Element::getGLFormat() const
+{
+ switch(mComponentCount) {
+ case 1:
+ if (mComponents[0]->getKind() == Component::ALPHA) {
+ return GL_ALPHA;
+ }
+ if (mComponents[0]->getKind() == Component::LUMINANCE) {
+ return GL_LUMINANCE;
+ }
+ break;
+ case 2:
+ if ((mComponents[0]->getKind() == Component::LUMINANCE) &&
+ (mComponents[1]->getKind() == Component::ALPHA)) {
+ return GL_LUMINANCE_ALPHA;
+ }
+ break;
+ case 3:
+ if ((mComponents[0]->getKind() == Component::RED) &&
+ (mComponents[1]->getKind() == Component::GREEN) &&
+ (mComponents[2]->getKind() == Component::BLUE)) {
+ return GL_RGB;
+ }
+ break;
+ case 4:
+ if ((mComponents[0]->getKind() == Component::RED) &&
+ (mComponents[1]->getKind() == Component::GREEN) &&
+ (mComponents[2]->getKind() == Component::BLUE) &&
+ (mComponents[3]->getKind() == Component::ALPHA)) {
+ return GL_RGBA;
+ }
+ break;
+ }
+ return 0;
+}
+
+
+ElementState::ElementState()
+{
+}
+
+ElementState::~ElementState()
+{
+}
+
+/////////////////////////////////////////
+//
+
+namespace android {
+namespace renderscript {
+
+void rsi_ElementBegin(Context *rsc)
+{
+ rsc->mStateElement.mComponentBuildList.clear();
+}
+
+void rsi_ElementAddPredefined(Context *rsc, RsElementPredefined predef)
+{
+ ElementState * sec = &rsc->mStateElement;
+
+ RsElement ve = rsi_ElementGetPredefined(rsc, predef);
+ const Element *e = static_cast<const Element *>(ve);
+
+ for(size_t ct = 0; ct < sec->mPredefinedList[predef].mElement->getComponentCount(); ct++) {
+ sec->mComponentBuildList.add(sec->mPredefinedList[predef].mElement->getComponent(ct));
+ }
+}
+
+RsElement rsi_ElementGetPredefined(Context *rsc, RsElementPredefined predef)
+{
+ ElementState * sec = &rsc->mStateElement;
+
+ if (!sec->mPredefinedList.size()) {
+ sec->initPredefined();
+ }
+
+ if ((predef < 0) ||
+ (static_cast<uint32_t>(predef) >= sec->mPredefinedList.size())) {
+ LOGE("rsElementGetPredefined: Request for bad predefined type");
+ // error
+ return NULL;
+ }
+
+ rsAssert(sec->mPredefinedList[predef].mEnum == predef);
+ Element * e = sec->mPredefinedList[predef].mElement;
+ e->incUserRef();
+ return e;
+}
+
+void rsi_ElementAdd(Context *rsc, RsDataKind dk, RsDataType dt, bool isNormalized, size_t bits, const char *name)
+{
+ ElementState * sec = &rsc->mStateElement;
+ Component *c = new Component(static_cast<Component::DataKind>(dk),
+ static_cast<Component::DataType>(dt),
+ isNormalized,
+ bits,
+ name);
+ sec->mComponentBuildList.add(c);
+}
+
+RsElement rsi_ElementCreate(Context *rsc)
+{
+ ElementState * sec = &rsc->mStateElement;
+ Element *se = new Element(sec->mComponentBuildList.size());
+
+ for (size_t ct = 0; ct < se->getComponentCount(); ct++) {
+ se->setComponent(ct, sec->mComponentBuildList[ct]);
+ }
+
+ rsc->mStateElement.mComponentBuildList.clear();
+ se->incUserRef();
+ return se;
+}
+
+
+}
+}
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
new file mode 100644
index 0000000..0918522
--- /dev/null
+++ b/libs/rs/rsElement.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_ELEMENT_H
+#define ANDROID_STRUCTURED_ELEMENT_H
+
+#include "rsComponent.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Element : public ObjectBase
+{
+public:
+ Element(uint32_t count);
+ ~Element();
+
+
+ void setComponent(uint32_t idx, Component *c);
+
+ uint32_t getGLType() const;
+ uint32_t getGLFormat() const;
+
+
+ size_t getSizeBits() const;
+ size_t getSizeBytes() const {
+ return (getSizeBits() + 7) >> 3;
+ }
+
+ size_t getComponentOffsetBits(uint32_t componentNumber) const;
+ size_t getComponentOffsetBytes(uint32_t componentNumber) const {
+ return (getComponentOffsetBits(componentNumber) + 7) >> 3;
+ }
+
+ uint32_t getComponentCount() const {return mComponentCount;}
+ Component * getComponent(uint32_t idx) const {return mComponents[idx].get();}
+
+protected:
+ // deallocate any components that are part of this element.
+ void clear();
+
+ size_t mComponentCount;
+ ObjectBaseRef<Component> * mComponents;
+ //uint32_t *mOffsetTable;
+
+ Element();
+};
+
+
+class ElementState {
+public:
+ ElementState();
+ ~ElementState();
+
+ Vector<Component *> mComponentBuildList;
+
+
+
+ struct Predefined {
+ Predefined() {
+ mElement = NULL;
+ }
+ Predefined(RsElementPredefined en, Element *e) {
+ mEnum = en;
+ mElement = e;
+ }
+ RsElementPredefined mEnum;
+ Element * mElement;
+ };
+ Vector<Predefined> mPredefinedList;
+
+ void initPredefined();
+
+};
+
+
+}
+}
+#endif //ANDROID_STRUCTURED_ELEMENT_H
diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp
new file mode 100644
index 0000000..347ef23
--- /dev/null
+++ b/libs/rs/rsFileA3D.cpp
@@ -0,0 +1,384 @@
+
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+
+#include <utils/String8.h>
+#include "rsFileA3D.h"
+
+#include "rsMesh.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+
+FileA3D::FileA3D()
+{
+ mRsc = NULL;
+}
+
+FileA3D::~FileA3D()
+{
+}
+
+bool FileA3D::load(Context *rsc, FILE *f)
+{
+ char magicString[12];
+ size_t len;
+
+ LOGE("file open 1");
+ len = fread(magicString, 1, 12, f);
+ if ((len != 12) ||
+ memcmp(magicString, "Android3D_ff", 12)) {
+ return false;
+ }
+
+ LOGE("file open 2");
+ len = fread(&mMajorVersion, 1, sizeof(mMajorVersion), f);
+ if (len != sizeof(mMajorVersion)) {
+ return false;
+ }
+
+ LOGE("file open 3");
+ len = fread(&mMinorVersion, 1, sizeof(mMinorVersion), f);
+ if (len != sizeof(mMinorVersion)) {
+ return false;
+ }
+
+ LOGE("file open 4");
+ uint32_t flags;
+ len = fread(&flags, 1, sizeof(flags), f);
+ if (len != sizeof(flags)) {
+ return false;
+ }
+ mUse64BitOffsets = (flags & 1) != 0;
+
+ LOGE("file open 64bit = %i", mUse64BitOffsets);
+
+ if (mUse64BitOffsets) {
+ len = fread(&mDataSize, 1, sizeof(mDataSize), f);
+ if (len != sizeof(mDataSize)) {
+ return false;
+ }
+ } else {
+ uint32_t tmp;
+ len = fread(&tmp, 1, sizeof(tmp), f);
+ if (len != sizeof(tmp)) {
+ return false;
+ }
+ mDataSize = tmp;
+ }
+
+ LOGE("file open size = %lli", mDataSize);
+
+ // We should know enough to read the file in at this point.
+ fseek(f, SEEK_SET, 0);
+ mAlloc= malloc(mDataSize);
+ if (!mAlloc) {
+ return false;
+ }
+ mData = (uint8_t *)mAlloc;
+ len = fread(mAlloc, 1, mDataSize, f);
+ if (len != mDataSize) {
+ return false;
+ }
+
+ LOGE("file start processing");
+ return process(rsc);
+}
+
+bool FileA3D::processIndex(Context *rsc, A3DIndexEntry *ie)
+{
+ bool ret = false;
+ IO io(mData + ie->mOffset, mUse64BitOffsets);
+
+ LOGE("process index, type %i", ie->mType);
+
+ switch(ie->mType) {
+ case CHUNK_ELEMENT:
+ processChunk_Element(rsc, &io, ie);
+ break;
+ case CHUNK_ELEMENT_SOURCE:
+ processChunk_ElementSource(rsc, &io, ie);
+ break;
+ case CHUNK_VERTICIES:
+ processChunk_Verticies(rsc, &io, ie);
+ break;
+ case CHUNK_MESH:
+ processChunk_Mesh(rsc, &io, ie);
+ break;
+ case CHUNK_PRIMITIVE:
+ processChunk_Primitive(rsc, &io, ie);
+ break;
+ default:
+ LOGE("FileA3D Unknown chunk type");
+ break;
+ }
+ return (ie->mRsObj != NULL);
+}
+
+bool FileA3D::process(Context *rsc)
+{
+ LOGE("process");
+ IO io(mData + 12, mUse64BitOffsets);
+ bool ret = true;
+
+ // Build the index first
+ LOGE("process 1");
+ io.loadU32(); // major version, already loaded
+ io.loadU32(); // minor version, already loaded
+ LOGE("process 2");
+
+ io.loadU32(); // flags
+ io.loadOffset(); // filesize, already loaded.
+ LOGE("process 4");
+ uint64_t mIndexOffset = io.loadOffset();
+ uint64_t mStringOffset = io.loadOffset();
+
+ LOGE("process mIndexOffset= 0x%016llx", mIndexOffset);
+ LOGE("process mStringOffset= 0x%016llx", mStringOffset);
+
+ IO index(mData + mIndexOffset, mUse64BitOffsets);
+ IO stringTable(mData + mStringOffset, mUse64BitOffsets);
+
+ uint32_t stringEntryCount = stringTable.loadU32();
+ LOGE("stringEntryCount %i", stringEntryCount);
+ mStrings.setCapacity(stringEntryCount);
+ mStringIndexValues.setCapacity(stringEntryCount);
+ if (stringEntryCount) {
+ uint32_t stringType = stringTable.loadU32();
+ LOGE("stringType %i", stringType);
+ rsAssert(stringType==0);
+ for (uint32_t ct = 0; ct < stringEntryCount; ct++) {
+ uint64_t offset = stringTable.loadOffset();
+ LOGE("string offset 0x%016llx", offset);
+ IO tmp(mData + offset, mUse64BitOffsets);
+ String8 s;
+ tmp.loadString(&s);
+ LOGE("string %s", s.string());
+ mStrings.push(s);
+ }
+ }
+
+ LOGE("strings done");
+ uint32_t indexEntryCount = index.loadU32();
+ LOGE("index count %i", indexEntryCount);
+ mIndex.setCapacity(indexEntryCount);
+ for (uint32_t ct = 0; ct < indexEntryCount; ct++) {
+ A3DIndexEntry e;
+ uint32_t stringIndex = index.loadU32();
+ LOGE("index %i", ct);
+ LOGE(" string index %i", stringIndex);
+ e.mType = (A3DChunkType)index.loadU32();
+ LOGE(" type %i", e.mType);
+ e.mOffset = index.loadOffset();
+ LOGE(" offset 0x%016llx", e.mOffset);
+
+ if (stringIndex && (stringIndex < mStrings.size())) {
+ e.mID = mStrings[stringIndex];
+ mStringIndexValues.editItemAt(stringIndex) = ct;
+ LOGE(" id %s", e.mID.string());
+ }
+
+ mIndex.push(e);
+ }
+ LOGE("index done");
+
+ // At this point the index should be fully populated.
+ // We can now walk though it and load all the objects.
+ for (uint32_t ct = 0; ct < indexEntryCount; ct++) {
+ LOGE("processing index entry %i", ct);
+ processIndex(rsc, &mIndex.editItemAt(ct));
+ }
+
+ return ret;
+}
+
+
+FileA3D::IO::IO(const uint8_t *buf, bool use64)
+{
+ mData = buf;
+ mPos = 0;
+ mUse64 = use64;
+}
+
+uint64_t FileA3D::IO::loadOffset()
+{
+ uint64_t tmp;
+ if (mUse64) {
+ mPos = (mPos + 7) & (~7);
+ tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0];
+ mPos += sizeof(uint64_t);
+ return tmp;
+ }
+ return loadU32();
+}
+
+void FileA3D::IO::loadString(String8 *s)
+{
+ LOGE("loadString");
+ uint32_t len = loadU32();
+ LOGE("loadString len %i", len);
+ s->setTo((const char *)&mData[mPos], len);
+ mPos += len;
+}
+
+
+void FileA3D::processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie)
+{
+ Mesh * m = new Mesh;
+
+ m->mPrimitivesCount = io->loadU32();
+ m->mPrimitives = new Mesh::Primitive_t *[m->mPrimitivesCount];
+
+ for (uint32_t ct = 0; ct < m->mPrimitivesCount; ct++) {
+ uint32_t index = io->loadU32();
+
+ m->mPrimitives[ct] = (Mesh::Primitive_t *)mIndex[index].mRsObj;
+ }
+ ie->mRsObj = m;
+}
+
+void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie)
+{
+ Mesh::Primitive_t * p = new Mesh::Primitive_t;
+
+ p->mIndexCount = io->loadU32();
+ uint32_t vertIdx = io->loadU32();
+ p->mRestartCounts = io->loadU16();
+ uint32_t bits = io->loadU8();
+ p->mType = (RsPrimitive)io->loadU8();
+
+ LOGE("processChunk_Primitive count %i, bits %i", p->mIndexCount, bits);
+
+ p->mVerticies = (Mesh::Verticies_t *)mIndex[vertIdx].mRsObj;
+
+ p->mIndicies = new uint16_t[p->mIndexCount];
+ for (uint32_t ct = 0; ct < p->mIndexCount; ct++) {
+ switch(bits) {
+ case 8:
+ p->mIndicies[ct] = io->loadU8();
+ break;
+ case 16:
+ p->mIndicies[ct] = io->loadU16();
+ break;
+ case 32:
+ p->mIndicies[ct] = io->loadU32();
+ break;
+ }
+ LOGE(" idx %i", p->mIndicies[ct]);
+ }
+
+ if (p->mRestartCounts) {
+ p->mRestarts = new uint16_t[p->mRestartCounts];
+ for (uint32_t ct = 0; ct < p->mRestartCounts; ct++) {
+ switch(bits) {
+ case 8:
+ p->mRestarts[ct] = io->loadU8();
+ break;
+ case 16:
+ p->mRestarts[ct] = io->loadU16();
+ break;
+ case 32:
+ p->mRestarts[ct] = io->loadU32();
+ break;
+ }
+ LOGE(" idx %i", p->mRestarts[ct]);
+ }
+ } else {
+ p->mRestarts = NULL;
+ }
+
+ ie->mRsObj = p;
+}
+
+void FileA3D::processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie)
+{
+ Mesh::Verticies_t *cv = new Mesh::Verticies_t;
+ cv->mAllocationCount = io->loadU32();
+ cv->mAllocations = new Allocation *[cv->mAllocationCount];
+ LOGE("processChunk_Verticies count %i", cv->mAllocationCount);
+ for (uint32_t ct = 0; ct < cv->mAllocationCount; ct++) {
+ uint32_t i = io->loadU32();
+ cv->mAllocations[ct] = (Allocation *)mIndex[i].mRsObj;
+ LOGE(" idx %i", i);
+ }
+ ie->mRsObj = cv;
+}
+
+void FileA3D::processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie)
+{
+ rsi_ElementBegin(rsc);
+
+ uint32_t count = io->loadU32();
+ LOGE("processChunk_Element count %i", count);
+ while (count--) {
+ RsDataKind dk = (RsDataKind)io->loadU8();
+ RsDataType dt = (RsDataType)io->loadU8();
+ uint32_t bits = io->loadU8();
+ bool isNorm = io->loadU8() != 0;
+ LOGE(" %i %i %i %i", dk, dt, bits, isNorm);
+ rsi_ElementAdd(rsc, dk, dt, isNorm, bits, 0);
+ }
+ LOGE("processChunk_Element create");
+ ie->mRsObj = rsi_ElementCreate(rsc);
+}
+
+void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie)
+{
+ uint32_t index = io->loadU32();
+ uint32_t count = io->loadU32();
+
+ LOGE("processChunk_ElementSource count %i, index %i", count, index);
+
+ RsElement e = (RsElement)mIndex[index].mRsObj;
+
+ RsAllocation a = rsi_AllocationCreateSized(rsc, e, count);
+ Allocation * alloc = static_cast<Allocation *>(a);
+
+ float * data = (float *)alloc->getPtr();
+ while(count--) {
+ *data = io->loadF();
+ LOGE(" %f", *data);
+ data++;
+ }
+ ie->mRsObj = alloc;
+}
+
+namespace android {
+namespace renderscript {
+
+
+RsFile rsi_FileOpen(Context *rsc, char const *path, unsigned int len)
+{
+ FileA3D *fa3d = new FileA3D;
+
+ FILE *f = fopen("/sdcard/test.a3d", "rb");
+ if (f) {
+ fa3d->load(rsc, f);
+ fclose(f);
+ return fa3d;
+ }
+ delete fa3d;
+ return NULL;
+}
+
+
+}
+}
diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h
new file mode 100644
index 0000000..9ee08ec
--- /dev/null
+++ b/libs/rs/rsFileA3D.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_FILE_A3D_H
+#define ANDROID_RS_FILE_A3D_H
+
+#include "RenderScript.h"
+#include "rsFileA3DDecls.h"
+#include "rsMesh.h"
+
+#include <utils/String8.h>
+#include <stdio.h>
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class FileA3D
+{
+public:
+ FileA3D();
+ ~FileA3D();
+
+ uint32_t mMajorVersion;
+ uint32_t mMinorVersion;
+ uint64_t mIndexOffset;
+ uint64_t mStringTableOffset;
+ bool mUse64BitOffsets;
+
+ struct A3DIndexEntry {
+ String8 mID;
+ A3DChunkType mType;
+ uint64_t mOffset;
+ void * mRsObj;
+ };
+
+ bool load(Context *rsc, FILE *f);
+
+protected:
+ class IO
+ {
+ public:
+ IO(const uint8_t *, bool use64);
+
+ float loadF() {
+ mPos = (mPos + 3) & (~3);
+ float tmp = reinterpret_cast<const float *>(&mData[mPos])[0];
+ mPos += sizeof(float);
+ return tmp;
+ }
+ int32_t loadI32() {
+ mPos = (mPos + 3) & (~3);
+ int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0];
+ mPos += sizeof(int32_t);
+ return tmp;
+ }
+ uint32_t loadU32() {
+ mPos = (mPos + 3) & (~3);
+ uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0];
+ mPos += sizeof(uint32_t);
+ return tmp;
+ }
+ uint16_t loadU16() {
+ mPos = (mPos + 1) & (~1);
+ uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0];
+ mPos += sizeof(uint16_t);
+ return tmp;
+ }
+ uint8_t loadU8() {
+ uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0];
+ mPos += sizeof(uint8_t);
+ return tmp;
+ }
+ uint64_t loadOffset();
+ void loadString(String8 *s);
+ uint64_t getPos() const {return mPos;}
+ const uint8_t * getPtr() const;
+ protected:
+ const uint8_t * mData;
+ uint64_t mPos;
+ bool mUse64;
+ };
+
+
+ bool process(Context *rsc);
+ bool processIndex(Context *rsc, A3DIndexEntry *);
+ void processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie);
+ void processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie);
+ void processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie);
+ void processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie);
+ void processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie);
+
+ const uint8_t * mData;
+ void * mAlloc;
+ uint64_t mDataSize;
+ Context * mRsc;
+
+ Vector<A3DIndexEntry> mIndex;
+ Vector<String8> mStrings;
+ Vector<uint32_t> mStringIndexValues;
+
+};
+
+
+}
+}
+#endif //ANDROID_RS_FILE_A3D_H
+
+
diff --git a/libs/rs/rsFileA3DDecls.h b/libs/rs/rsFileA3DDecls.h
new file mode 100644
index 0000000..2a08bd3
--- /dev/null
+++ b/libs/rs/rsFileA3DDecls.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_FILE_A3D_DECLS_H
+#define ANDROID_RS_FILE_A3D_DECLS_H
+
+
+#define A3D_MAGIC_KEY "Android3D_ff"
+
+namespace android {
+namespace renderscript {
+
+ enum A3DChunkType {
+ CHUNK_EMPTY,
+
+ CHUNK_ELEMENT,
+ CHUNK_ELEMENT_SOURCE,
+ CHUNK_VERTICIES,
+ CHUNK_MESH,
+ CHUNK_PRIMITIVE,
+
+ CHUNK_LAST
+ };
+
+
+}
+}
+#endif //ANDROID_RS_FILE_A3D_H
+
+
+
diff --git a/libs/rs/rsHandcode.h b/libs/rs/rsHandcode.h
new file mode 100644
index 0000000..800eddd
--- /dev/null
+++ b/libs/rs/rsHandcode.h
@@ -0,0 +1,47 @@
+
+#define DATA_SYNC_SIZE 1024
+
+static inline void rsHCAPI_AllocationData (RsContext rsc, RsAllocation va, const void * data, uint32_t sizeBytes)
+{
+ ThreadIO *io = &((Context *)rsc)->mIO;
+ uint32_t size = sizeof(RS_CMD_AllocationData);
+ if (sizeBytes < DATA_SYNC_SIZE) {
+ size += (sizeBytes + 3) & ~3;
+ }
+ RS_CMD_AllocationData *cmd = static_cast<RS_CMD_AllocationData *>(io->mToCore.reserve(size));
+ cmd->va = va;
+ cmd->bytes = sizeBytes;
+ cmd->data = data;
+ if (sizeBytes < DATA_SYNC_SIZE) {
+ cmd->data = (void *)(cmd+1);
+ memcpy(cmd+1, data, sizeBytes);
+ io->mToCore.commit(RS_CMD_ID_AllocationData, size);
+ } else {
+ io->mToCore.commitSync(RS_CMD_ID_AllocationData, size);
+ }
+}
+
+
+static inline void rsHCAPI_Allocation1DSubData (RsContext rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void * data, uint32_t sizeBytes)
+{
+ ThreadIO *io = &((Context *)rsc)->mIO;
+ uint32_t size = sizeof(RS_CMD_Allocation1DSubData);
+ if (sizeBytes < DATA_SYNC_SIZE) {
+ size += (sizeBytes + 3) & ~3;
+ }
+ RS_CMD_Allocation1DSubData *cmd = static_cast<RS_CMD_Allocation1DSubData *>(io->mToCore.reserve(size));
+ cmd->va = va;
+ cmd->xoff = xoff;
+ cmd->count = count;
+ cmd->data = data;
+ cmd->bytes = sizeBytes;
+ if (sizeBytes < DATA_SYNC_SIZE) {
+ cmd->data = (void *)(cmd+1);
+ memcpy(cmd+1, data, sizeBytes);
+ io->mToCore.commit(RS_CMD_ID_Allocation1DSubData, size);
+ } else {
+ io->mToCore.commitSync(RS_CMD_ID_Allocation1DSubData, size);
+ }
+
+}
+
diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp
new file mode 100644
index 0000000..ad06c1f
--- /dev/null
+++ b/libs/rs/rsLight.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include <GLES/gl.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Light::Light(bool isLocal, bool isMono)
+{
+ mIsLocal = isLocal;
+ mIsMono = isMono;
+
+ mPosition[0] = 0;
+ mPosition[1] = 0;
+ mPosition[2] = 1;
+ mPosition[3] = 0;
+
+ mColor[0] = 1.f;
+ mColor[1] = 1.f;
+ mColor[2] = 1.f;
+ mColor[3] = 1.f;
+}
+
+Light::~Light()
+{
+}
+
+void Light::setPosition(float x, float y, float z)
+{
+ mPosition[0] = x;
+ mPosition[1] = y;
+ mPosition[2] = z;
+}
+
+void Light::setColor(float r, float g, float b)
+{
+ mColor[0] = r;
+ mColor[1] = g;
+ mColor[2] = b;
+}
+
+void Light::setupGL(uint32_t num) const
+{
+ glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor);
+ glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor);
+ glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition);
+}
+
+////////////////////////////////////////////
+
+LightState::LightState()
+{
+ clear();
+}
+
+LightState::~LightState()
+{
+}
+
+void LightState::clear()
+{
+ mIsLocal = false;
+ mIsMono = false;
+}
+
+
+////////////////////////////////////////////////////
+//
+
+namespace android {
+namespace renderscript {
+
+void rsi_LightBegin(Context *rsc)
+{
+ rsc->mStateLight.clear();
+}
+
+void rsi_LightSetLocal(Context *rsc, bool isLocal)
+{
+ rsc->mStateLight.mIsLocal = isLocal;
+}
+
+void rsi_LightSetMonochromatic(Context *rsc, bool isMono)
+{
+ rsc->mStateLight.mIsMono = isMono;
+}
+
+RsLight rsi_LightCreate(Context *rsc)
+{
+ Light *l = new Light(rsc->mStateLight.mIsLocal,
+ rsc->mStateLight.mIsMono);
+ l->incUserRef();
+ return l;
+}
+
+void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b)
+{
+ Light *l = static_cast<Light *>(vl);
+ l->setColor(r, g, b);
+}
+
+void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z)
+{
+ Light *l = static_cast<Light *>(vl);
+ l->setPosition(x, y, z);
+}
+
+
+
+}
+}
diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h
new file mode 100644
index 0000000..b0c3386
--- /dev/null
+++ b/libs/rs/rsLight.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIGHT_H
+#define ANDROID_LIGHT_H
+
+
+#include "rsObjectBase.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Light : public ObjectBase
+{
+public:
+ Light(bool isLocal, bool isMono);
+ virtual ~Light();
+
+ // Values, mutable after creation.
+ void setPosition(float x, float y, float z);
+ void setColor(float r, float g, float b);
+
+ void setupGL(uint32_t num) const;
+
+protected:
+ float mColor[4];
+ float mPosition[4];
+ bool mIsLocal;
+ bool mIsMono;
+};
+
+
+class LightState {
+public:
+ LightState();
+ ~LightState();
+
+ void clear();
+
+ bool mIsMono;
+ bool mIsLocal;
+};
+
+
+}
+}
+#endif //ANDROID_LIGHT_H
+
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
new file mode 100644
index 0000000..0c40389
--- /dev/null
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsLocklessFifo.h"
+
+using namespace android;
+
+
+LocklessCommandFifo::LocklessCommandFifo()
+{
+}
+
+LocklessCommandFifo::~LocklessCommandFifo()
+{
+ if (!mInShutdown) {
+ shutdown();
+ }
+ free(mBuffer);
+}
+
+void LocklessCommandFifo::shutdown()
+{
+ mInShutdown = true;
+ mSignalToWorker.set();
+}
+
+bool LocklessCommandFifo::init(uint32_t sizeInBytes)
+{
+ // Add room for a buffer reset command
+ mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4));
+ if (!mBuffer) {
+ LOGE("LocklessFifo allocation failure");
+ return false;
+ }
+
+ if (!mSignalToControl.init() || !mSignalToWorker.init()) {
+ LOGE("Signal setup failed");
+ free(mBuffer);
+ return false;
+ }
+
+ mInShutdown = false;
+ mSize = sizeInBytes;
+ mPut = mBuffer;
+ mGet = mBuffer;
+ mEnd = mBuffer + (sizeInBytes) - 1;
+ dumpState("init");
+ return true;
+}
+
+uint32_t LocklessCommandFifo::getFreeSpace() const
+{
+ int32_t freeSpace = 0;
+ //dumpState("getFreeSpace");
+
+ if (mPut >= mGet) {
+ freeSpace = mEnd - mPut;
+ } else {
+ freeSpace = mGet - mPut;
+ }
+
+ if (freeSpace < 0) {
+ freeSpace = 0;
+ }
+ return freeSpace;
+}
+
+bool LocklessCommandFifo::isEmpty() const
+{
+ return mPut == mGet;
+}
+
+
+void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
+{
+ // Add space for command header and loop token;
+ sizeInBytes += 8;
+
+ //dumpState("reserve");
+ if (getFreeSpace() < sizeInBytes) {
+ makeSpace(sizeInBytes);
+ }
+
+ return mPut + 4;
+}
+
+void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes)
+{
+ //dumpState("commit 1");
+ reinterpret_cast<uint16_t *>(mPut)[0] = command;
+ reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes;
+ mPut += ((sizeInBytes + 3) & ~3) + 4;
+ //dumpState("commit 2");
+ mSignalToWorker.set();
+}
+
+void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes)
+{
+ commit(command, sizeInBytes);
+ flush();
+}
+
+void LocklessCommandFifo::flush()
+{
+ //dumpState("flush 1");
+ while(mPut != mGet) {
+ mSignalToControl.wait();
+ }
+ //dumpState("flush 2");
+}
+
+const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData)
+{
+ while(1) {
+ //dumpState("get");
+ while(isEmpty() && !mInShutdown) {
+ mSignalToControl.set();
+ mSignalToWorker.wait();
+ }
+
+ *command = reinterpret_cast<const uint16_t *>(mGet)[0];
+ *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1];
+ if (*command) {
+ // non-zero command is valid
+ return mGet+4;
+ }
+
+ // zero command means reset to beginning.
+ mGet = mBuffer;
+ }
+}
+
+void LocklessCommandFifo::next()
+{
+ uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1];
+ mGet += ((bytes + 3) & ~3) + 4;
+ if (isEmpty()) {
+ mSignalToControl.set();
+ }
+ //dumpState("next");
+}
+
+void LocklessCommandFifo::makeSpace(uint32_t bytes)
+{
+ //dumpState("make space");
+ if ((mPut+bytes) > mEnd) {
+ // Need to loop regardless of where get is.
+ while((mGet > mPut) && (mBuffer+4 >= mGet)) {
+ sleep(1);
+ }
+
+ // Toss in a reset then the normal wait for space will do the rest.
+ reinterpret_cast<uint16_t *>(mPut)[0] = 0;
+ reinterpret_cast<uint16_t *>(mPut)[1] = 0;
+ mPut = mBuffer;
+ }
+
+ // it will fit here so we just need to wait for space.
+ while(getFreeSpace() < bytes) {
+ sleep(1);
+ }
+
+}
+
+void LocklessCommandFifo::dumpState(const char *s) const
+{
+ LOGV("%s put %p, get %p, buf %p, end %p", s, mPut, mGet, mBuffer, mEnd);
+}
+
+LocklessCommandFifo::Signal::Signal()
+{
+ mSet = true;
+}
+
+LocklessCommandFifo::Signal::~Signal()
+{
+ pthread_mutex_destroy(&mMutex);
+ pthread_cond_destroy(&mCondition);
+}
+
+bool LocklessCommandFifo::Signal::init()
+{
+ int status = pthread_mutex_init(&mMutex, NULL);
+ if (status) {
+ LOGE("LocklessFifo mutex init failure");
+ return false;
+ }
+
+ status = pthread_cond_init(&mCondition, NULL);
+ if (status) {
+ LOGE("LocklessFifo condition init failure");
+ pthread_mutex_destroy(&mMutex);
+ return false;
+ }
+
+ return true;
+}
+
+void LocklessCommandFifo::Signal::set()
+{
+ int status;
+
+ status = pthread_mutex_lock(&mMutex);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i locking for set condition.", status);
+ return;
+ }
+
+ mSet = true;
+
+ status = pthread_cond_signal(&mCondition);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i on set condition.", status);
+ }
+
+ status = pthread_mutex_unlock(&mMutex);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status);
+ }
+}
+
+void LocklessCommandFifo::Signal::wait()
+{
+ int status;
+
+ status = pthread_mutex_lock(&mMutex);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i locking for condition.", status);
+ return;
+ }
+
+ if (!mSet) {
+ status = pthread_cond_wait(&mCondition, &mMutex);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i waiting on condition.", status);
+ }
+ }
+ mSet = false;
+
+ status = pthread_mutex_unlock(&mMutex);
+ if (status) {
+ LOGE("LocklessCommandFifo: error %i unlocking for condition.", status);
+ }
+}
+
diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h
new file mode 100644
index 0000000..d0a4356
--- /dev/null
+++ b/libs/rs/rsLocklessFifo.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_LOCKLESS_FIFO_H
+#define ANDROID_RS_LOCKLESS_FIFO_H
+
+
+#include "rsUtils.h"
+
+namespace android {
+
+
+// A simple FIFO to be used as a producer / consumer between two
+// threads. One is writer and one is reader. The common cases
+// will not require locking. It is not threadsafe for multiple
+// readers or writers by design.
+
+class LocklessCommandFifo
+{
+public:
+ bool init(uint32_t size);
+ void shutdown();
+
+ LocklessCommandFifo();
+ ~LocklessCommandFifo();
+
+
+protected:
+ class Signal {
+ public:
+ Signal();
+ ~Signal();
+
+ bool init();
+
+ void set();
+ void wait();
+
+ protected:
+ bool mSet;
+ pthread_mutex_t mMutex;
+ pthread_cond_t mCondition;
+ };
+
+ uint8_t * volatile mPut;
+ uint8_t * volatile mGet;
+ uint8_t * mBuffer;
+ uint8_t * mEnd;
+ uint8_t mSize;
+ bool mInShutdown;
+
+ Signal mSignalToWorker;
+ Signal mSignalToControl;
+
+
+
+public:
+ void * reserve(uint32_t bytes);
+ void commit(uint32_t command, uint32_t bytes);
+ void commitSync(uint32_t command, uint32_t bytes);
+
+ void flush();
+ const void * get(uint32_t *command, uint32_t *bytesData);
+ void next();
+
+ void makeSpace(uint32_t bytes);
+
+ bool isEmpty() const;
+ uint32_t getFreeSpace() const;
+
+
+private:
+ void dumpState(const char *) const;
+};
+
+
+}
+#endif
diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp
new file mode 100644
index 0000000..5f68197
--- /dev/null
+++ b/libs/rs/rsMatrix.cpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsMatrix.h"
+
+#include "stdlib.h"
+#include "string.h"
+#include "math.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+
+void Matrix::loadIdentity()
+{
+ set(0, 0, 1);
+ set(1, 0, 0);
+ set(2, 0, 0);
+ set(3, 0, 0);
+
+ set(0, 1, 0);
+ set(1, 1, 1);
+ set(2, 1, 0);
+ set(3, 1, 0);
+
+ set(0, 2, 0);
+ set(1, 2, 0);
+ set(2, 2, 1);
+ set(3, 2, 0);
+
+ set(0, 3, 0);
+ set(1, 3, 0);
+ set(2, 3, 0);
+ set(3, 3, 1);
+}
+
+void Matrix::load(const float *v)
+{
+ memcpy(m, v, sizeof(m));
+}
+
+void Matrix::load(const Matrix *v)
+{
+ memcpy(m, v->m, sizeof(m));
+}
+
+void Matrix::loadRotate(float rot, float x, float y, float z)
+{
+ float c, s;
+ m[3] = 0;
+ m[7] = 0;
+ m[11]= 0;
+ m[12]= 0;
+ m[13]= 0;
+ m[14]= 0;
+ m[15]= 1;
+ rot *= float(M_PI / 180.0f);
+ c = cosf(rot);
+ s = sinf(rot);
+
+ const float len = sqrtf(x*x + y*y + z*z);
+ if (!(len != 1)) {
+ const float recipLen = 1.f / len;
+ x *= recipLen;
+ y *= recipLen;
+ z *= recipLen;
+ }
+ const float nc = 1.0f - c;
+ const float xy = x * y;
+ const float yz = y * z;
+ const float zx = z * x;
+ const float xs = x * s;
+ const float ys = y * s;
+ const float zs = z * s;
+ m[ 0] = x*x*nc + c;
+ m[ 4] = xy*nc - zs;
+ m[ 8] = zx*nc + ys;
+ m[ 1] = xy*nc + zs;
+ m[ 5] = y*y*nc + c;
+ m[ 9] = yz*nc - xs;
+ m[ 2] = zx*nc - ys;
+ m[ 6] = yz*nc + xs;
+ m[10] = z*z*nc + c;
+}
+
+void Matrix::loadScale(float x, float y, float z)
+{
+ loadIdentity();
+ m[0] = x;
+ m[5] = y;
+ m[10] = z;
+}
+
+void Matrix::loadTranslate(float x, float y, float z)
+{
+ loadIdentity();
+ m[12] = x;
+ m[13] = y;
+ m[14] = z;
+}
+
+void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs)
+{
+ for (int i=0 ; i<4 ; i++) {
+ float ri0 = 0;
+ float ri1 = 0;
+ float ri2 = 0;
+ float ri3 = 0;
+ for (int j=0 ; j<4 ; j++) {
+ const float rhs_ij = rhs->get(i,j);
+ ri0 += lhs->get(j,0) * rhs_ij;
+ ri1 += lhs->get(j,1) * rhs_ij;
+ ri2 += lhs->get(j,2) * rhs_ij;
+ ri3 += lhs->get(j,3) * rhs_ij;
+ }
+ set(i,0, ri0);
+ set(i,1, ri1);
+ set(i,2, ri2);
+ set(i,3, ri3);
+ }
+}
+
+void Matrix::loadOrtho(float l, float r, float b, float t, float n, float f) {
+ loadIdentity();
+ m[0] = 2 / (r - l);
+ m[5] = 2 / (t - b);
+ m[10]= -2 / (f - n);
+ m[12]= -(r + l) / (r - l);
+ m[13]= -(t + b) / (t - b);
+ m[14]= -(f + n) / (f - n);
+}
+
+void Matrix::loadFrustum(float l, float r, float b, float t, float n, float f) {
+ loadIdentity();
+ m[0] = 2 * n / (r - l);
+ m[5] = 2 * n / (t - b);
+ m[8] = (r + l) / (r - l);
+ m[9] = (t + b) / (t - b);
+ m[10]= -(f + n) / (f - n);
+ m[11]= -1;
+ m[14]= -2*f*n / (f - n);
+ m[15]= 0;
+}
+
+
diff --git a/libs/rs/rsMatrix.h b/libs/rs/rsMatrix.h
new file mode 100644
index 0000000..7dc4165
--- /dev/null
+++ b/libs/rs/rsMatrix.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_MATRIX_H
+#define ANDROID_RS_MATRIX_H
+
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+struct Matrix
+{
+ float m[16];
+
+ inline float get(int i, int j) const {
+ return m[i*4 + j];
+ }
+
+ inline void set(int i, int j, float v) {
+ m[i*4 + j] = v;
+ }
+
+ void loadIdentity();
+ void load(const float *);
+ void load(const Matrix *);
+
+ void loadRotate(float rot, float x, float y, float z);
+ void loadScale(float x, float y, float z);
+ void loadTranslate(float x, float y, float z);
+ void loadMultiply(const Matrix *lhs, const Matrix *rhs);
+
+ void loadOrtho(float l, float r, float b, float t, float n, float f);
+ void loadFrustum(float l, float r, float b, float t, float n, float f);
+
+ void multiply(const Matrix *rhs) {
+ Matrix tmp;
+ tmp.loadMultiply(this, rhs);
+ load(&tmp);
+ }
+ void rotate(float rot, float x, float y, float z) {
+ Matrix tmp;
+ tmp.loadRotate(rot, x, y, z);
+ multiply(&tmp);
+ }
+ void scale(float x, float y, float z) {
+ Matrix tmp;
+ tmp.loadScale(x, y, z);
+ multiply(&tmp);
+ }
+ void translate(float x, float y, float z) {
+ Matrix tmp;
+ tmp.loadTranslate(x, y, z);
+ multiply(&tmp);
+ }
+
+
+
+};
+
+
+
+}
+}
+
+
+
+
+#endif
+
+
+
+
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
new file mode 100644
index 0000000..aeb52ed
--- /dev/null
+++ b/libs/rs/rsMesh.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+Mesh::Mesh()
+{
+ mVerticies = NULL;
+ mVerticiesCount = 0;
+ mPrimitives = NULL;
+ mPrimitivesCount = 0;
+}
+
+Mesh::~Mesh()
+{
+}
+
+
+
+MeshContext::MeshContext()
+{
+}
+
+MeshContext::~MeshContext()
+{
+}
+
diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h
new file mode 100644
index 0000000..be207a3
--- /dev/null
+++ b/libs/rs/rsMesh.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_MESH_H
+#define ANDROID_RS_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class Mesh : public ObjectBase
+{
+public:
+ Mesh();
+ ~Mesh();
+
+ struct Verticies_t
+ {
+ Allocation ** mAllocations;
+ uint32_t mAllocationCount;
+
+ size_t mVertexDataSize;
+
+ size_t mOffsetCoord;
+ size_t mOffsetTex;
+ size_t mOffsetNorm;
+
+ size_t mSizeCoord;
+ size_t mSizeTex;
+ size_t mSizeNorm;
+
+ uint32_t mBufferObject;
+ };
+
+ struct Primitive_t
+ {
+ RsPrimitive mType;
+ Verticies_t *mVerticies;
+
+ uint32_t mIndexCount;
+ uint16_t *mIndicies;
+
+ uint32_t mRestartCounts;
+ uint16_t *mRestarts;
+ };
+
+ Verticies_t * mVerticies;
+ uint32_t mVerticiesCount;
+
+ Primitive_t ** mPrimitives;
+ uint32_t mPrimitivesCount;
+
+
+
+ void analyzeElement();
+protected:
+};
+
+class MeshContext
+{
+public:
+ MeshContext();
+ ~MeshContext();
+
+};
+
+
+}
+}
+#endif //ANDROID_RS_TRIANGLE_MESH_H
+
+
diff --git a/libs/rs/rsNoise.cpp b/libs/rs/rsNoise.cpp
new file mode 100644
index 0000000..764dc1a
--- /dev/null
+++ b/libs/rs/rsNoise.cpp
@@ -0,0 +1,256 @@
+/*
+ * This implementation of the noise functions was ported from the Java
+ * implementation by Jerry Huxtable (http://www.jhlabs.com) under
+ * Apache License 2.0 (see http://jhlabs.com/ip/filters/download.html)
+ *
+ * Original header:
+ *
+ * Copyright 2006 Jerry Huxtable
+ *
+ * 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.
+ */
+
+#include "rsNoise.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+
+namespace android {
+namespace renderscript {
+
+#define B 0x100
+#define BM 0xff
+#define N 0x1000
+
+static int p[B + B + 2];
+static float g3[B + B + 2][3];
+static float g2[B + B + 2][2];
+static float g1[B + B + 2];
+static bool noise_start = true;
+
+#define lerpf(start, stop, amount) start + (stop - start) * amount
+
+static inline float noise_sCurve(float t)
+{
+ return t * t * (3.0f - 2.0f * t);
+}
+
+inline void SC_normalizef2(float v[])
+{
+ float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1]);
+ v[0] = v[0] / s;
+ v[1] = v[1] / s;
+}
+
+inline void SC_normalizef3(float v[])
+{
+ float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] = v[0] / s;
+ v[1] = v[1] / s;
+ v[2] = v[2] / s;
+}
+
+static void noise_init()
+{
+ int i, j, k;
+
+ for (i = 0; i < B; i++) {
+ p[i] = i;
+
+ g1[i] = (float)((rand() % (B + B)) - B) / B;
+
+ for (j = 0; j < 2; j++)
+ g2[i][j] = (float)((rand() % (B + B)) - B) / B;
+ SC_normalizef2(g2[i]);
+
+ for (j = 0; j < 3; j++)
+ g3[i][j] = (float)((rand() % (B + B)) - B) / B;
+ SC_normalizef3(g3[i]);
+ }
+
+ for (i = B-1; i >= 0; i--) {
+ k = p[i];
+ p[i] = p[j = rand() % B];
+ p[j] = k;
+ }
+
+ for (i = 0; i < B + 2; i++) {
+ p[B + i] = p[i];
+ g1[B + i] = g1[i];
+ for (j = 0; j < 2; j++)
+ g2[B + i][j] = g2[i][j];
+ for (j = 0; j < 3; j++)
+ g3[B + i][j] = g3[i][j];
+ }
+}
+
+float SC_noisef(float x)
+{
+ srand(time(NULL));
+ int bx0, bx1;
+ float rx0, rx1, sx, t, u, v;
+
+ if (noise_start) {
+ noise_start = false;
+ noise_init();
+ }
+
+ t = x + N;
+ bx0 = ((int)t) & BM;
+ bx1 = (bx0+1) & BM;
+ rx0 = t - (int)t;
+ rx1 = rx0 - 1.0f;
+
+ sx = noise_sCurve(rx0);
+
+ u = rx0 * g1[p[bx0]];
+ v = rx1 * g1[p[bx1]];
+ return 2.3f * lerpf(u, v, sx);
+}
+
+float SC_noisef2(float x, float y)
+{
+ srand(time(NULL));
+ int bx0, bx1, by0, by1, b00, b10, b01, b11;
+ float rx0, rx1, ry0, ry1, sx, sy, a, b, t, u, v;
+ float *q;
+ int i, j;
+
+ if (noise_start) {
+ noise_start = false;
+ noise_init();
+ }
+
+ t = x + N;
+ bx0 = ((int)t) & BM;
+ bx1 = (bx0+1) & BM;
+ rx0 = t - (int)t;
+ rx1 = rx0 - 1.0f;
+
+ t = y + N;
+ by0 = ((int)t) & BM;
+ by1 = (by0+1) & BM;
+ ry0 = t - (int)t;
+ ry1 = ry0 - 1.0f;
+
+ i = p[bx0];
+ j = p[bx1];
+
+ b00 = p[i + by0];
+ b10 = p[j + by0];
+ b01 = p[i + by1];
+ b11 = p[j + by1];
+
+ sx = noise_sCurve(rx0);
+ sy = noise_sCurve(ry0);
+
+ q = g2[b00]; u = rx0 * q[0] + ry0 * q[1];
+ q = g2[b10]; v = rx1 * q[0] + ry0 * q[1];
+ a = lerpf(u, v, sx);
+
+ q = g2[b01]; u = rx0 * q[0] + ry1 * q[1];
+ q = g2[b11]; v = rx1 * q[0] + ry1 * q[1];
+ b = lerpf(u, v, sx);
+
+ return 1.5f*lerpf(a, b, sy);
+}
+
+float SC_noisef3(float x, float y, float z)
+{
+ srand(time(NULL));
+ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
+ float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v;
+ float *q;
+ int i, j;
+
+ if (noise_start) {
+ noise_start = false;
+ noise_init();
+ }
+
+ t = x + N;
+ bx0 = ((int)t) & BM;
+ bx1 = (bx0+1) & BM;
+ rx0 = t - (int)t;
+ rx1 = rx0 - 1.0f;
+
+ t = y + N;
+ by0 = ((int)t) & BM;
+ by1 = (by0+1) & BM;
+ ry0 = t - (int)t;
+ ry1 = ry0 - 1.0f;
+
+ t = z + N;
+ bz0 = ((int)t) & BM;
+ bz1 = (bz0+1) & BM;
+ rz0 = t - (int)t;
+ rz1 = rz0 - 1.0f;
+
+ i = p[bx0];
+ j = p[bx1];
+
+ b00 = p[i + by0];
+ b10 = p[j + by0];
+ b01 = p[i + by1];
+ b11 = p[j + by1];
+
+ t = noise_sCurve(rx0);
+ sy = noise_sCurve(ry0);
+ sz = noise_sCurve(rz0);
+
+ q = g3[b00 + bz0]; u = rx0 * q[0] + ry0 * q[1] + rz0 * q[2];
+ q = g3[b10 + bz0]; v = rx1 * q[0] + ry0 * q[1] + rz0 * q[2];
+ a = lerpf(u, v, t);
+
+ q = g3[b01 + bz0]; u = rx0 * q[0] + ry1 * q[1] + rz0 * q[2];
+ q = g3[b11 + bz0]; v = rx1 * q[0] + ry1 * q[1] + rz0 * q[2];
+ b = lerpf(u, v, t);
+
+ c = lerpf(a, b, sy);
+
+ q = g3[b00 + bz1]; u = rx0 * q[0] + ry0 * q[1] + rz1 * q[2];
+ q = g3[b10 + bz1]; v = rx1 * q[0] + ry0 * q[1] + rz1 * q[2];
+ a = lerpf(u, v, t);
+
+ q = g3[b01 + bz1]; u = rx0 * q[0] + ry1 * q[1] + rz1 * q[2];
+ q = g3[b11 + bz1]; v = rx1 * q[0] + ry1 * q[1] + rz1 * q[2];
+ b = lerpf(u, v, t);
+
+ d = lerpf(a, b, sy);
+
+ return 1.5f*lerpf(c, d, sz);
+}
+
+float SC_turbulencef2(float x, float y, float octaves)
+{
+ srand(time(NULL));
+ float t = 0.0f;
+
+ for (float f = 1.0f; f <= octaves; f *= 2)
+ t += fabs(SC_noisef2(f * x, f * y)) / f;
+ return t;
+}
+
+float SC_turbulencef3(float x, float y, float z, float octaves)
+{
+ srand(time(NULL));
+ float t = 0.0f;
+
+ for (float f = 1.0f; f <= octaves; f *= 2)
+ t += fabs(SC_noisef3(f * x, f * y, f * z)) / f;
+ return t;
+}
+
+}
+} \ No newline at end of file
diff --git a/libs/rs/rsNoise.h b/libs/rs/rsNoise.h
new file mode 100644
index 0000000..9040751
--- /dev/null
+++ b/libs/rs/rsNoise.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_NOISE_H
+#define ANDROID_RS_NOISE_H
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+void SC_normalizef2(float v[]);
+void SC_normalizef3(float v[]);
+float SC_noisef(float x);
+float SC_noisef2(float x, float y);
+float SC_noisef3(float x, float y, float z);
+float SC_turbulencef2(float x, float y, float octaves);
+float SC_turbulencef3(float x, float y, float z, float octaves);
+
+}
+}
+
+#endif
diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp
new file mode 100644
index 0000000..7e7afab
--- /dev/null
+++ b/libs/rs/rsObjectBase.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsObjectBase.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+ObjectBase::ObjectBase()
+{
+ mUserRefCount = 0;
+ mSysRefCount = 0;
+ mName = NULL;
+}
+
+ObjectBase::~ObjectBase()
+{
+ //LOGV("~ObjectBase %p ref %i", this, mRefCount);
+ rsAssert(!mUserRefCount);
+ rsAssert(!mSysRefCount);
+}
+
+void ObjectBase::incUserRef() const
+{
+ mUserRefCount ++;
+ //LOGV("ObjectBase %p inc ref %i", this, mRefCount);
+}
+
+void ObjectBase::incSysRef() const
+{
+ mSysRefCount ++;
+ //LOGV("ObjectBase %p inc ref %i", this, mRefCount);
+}
+
+void ObjectBase::decUserRef() const
+{
+ rsAssert(mUserRefCount > 0);
+ mUserRefCount --;
+ //LOGV("ObjectBase %p dec ref %i", this, mRefCount);
+ if (!(mSysRefCount | mUserRefCount)) {
+ if (mName) {
+ LOGV("Deleting RS object %p, name %s", this, mName);
+ } else {
+ LOGV("Deleting RS object %p, no name", this);
+ }
+ delete this;
+ }
+}
+
+void ObjectBase::decSysRef() const
+{
+ rsAssert(mSysRefCount > 0);
+ mSysRefCount --;
+ //LOGV("ObjectBase %p dec ref %i", this, mRefCount);
+ if (!(mSysRefCount | mUserRefCount)) {
+ if (mName) {
+ LOGV("Deleting RS object %p, name %s", this, mName);
+ } else {
+ LOGV("Deleting RS object %p, no name", this);
+ }
+ delete this;
+ }
+}
+
+void ObjectBase::setName(const char *name)
+{
+ delete mName;
+ mName = NULL;
+ if (name) {
+ mName = new char[strlen(name) +1];
+ strcpy(mName, name);
+ }
+}
+
+void ObjectBase::setName(const char *name, uint32_t len)
+{
+ delete mName;
+ mName = NULL;
+ if (name) {
+ mName = new char[len + 1];
+ memcpy(mName, name, len);
+ mName[len] = 0;
+ }
+}
+
diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h
new file mode 100644
index 0000000..d1e6baa
--- /dev/null
+++ b/libs/rs/rsObjectBase.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_OBJECT_BASE_H
+#define ANDROID_RS_OBJECT_BASE_H
+
+#include "rsUtils.h"
+
+
+namespace android {
+namespace renderscript {
+
+// An element is a group of Components that occupies one cell in a structure.
+class ObjectBase
+{
+public:
+ ObjectBase();
+ virtual ~ObjectBase();
+
+ void incSysRef() const;
+ void decSysRef() const;
+
+ void incUserRef() const;
+ void decUserRef() const;
+
+ const char * getName() const {
+ return mName;
+ }
+ void setName(const char *);
+ void setName(const char *, uint32_t len);
+
+private:
+ char * mName;
+ mutable int32_t mSysRefCount;
+ mutable int32_t mUserRefCount;
+
+
+};
+
+template<class T>
+class ObjectBaseRef
+{
+public:
+ ObjectBaseRef() {
+ mRef = NULL;
+ }
+
+ ObjectBaseRef(const ObjectBaseRef &ref) {
+ mRef = ref.get();
+ if (mRef) {
+ mRef->incSysRef();
+ }
+ }
+
+ ObjectBaseRef(T *ref) {
+ mRef = ref;
+ if (mRef) {
+ ref->incSysRef();
+ }
+ }
+
+ ~ObjectBaseRef() {
+ clear();
+ }
+
+ void set(T *ref) {
+ if (mRef != ref) {
+ clear();
+ mRef = ref;
+ if (mRef) {
+ ref->incSysRef();
+ }
+ }
+ }
+
+ void set(const ObjectBaseRef &ref) {
+ set(ref.mRef);
+ }
+
+ void clear() {
+ if (mRef) {
+ mRef->decSysRef();
+ }
+ mRef = NULL;
+ }
+
+ inline T * get() const {
+ return mRef;
+ }
+
+ inline T * operator-> () const {
+ return mRef;
+ }
+
+protected:
+ T * mRef;
+
+};
+
+
+}
+}
+
+#endif //ANDROID_RS_OBJECT_BASE_H
+
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
new file mode 100644
index 0000000..18eacfb
--- /dev/null
+++ b/libs/rs/rsProgram.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgram.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Program::Program(Element *in, Element *out)
+{
+ mElementIn.set(in);
+ mElementOut.set(out);
+
+
+}
+
+Program::~Program()
+{
+}
+
+
+void Program::bindAllocation(Allocation *alloc)
+{
+ mConstants.set(alloc);
+ mDirty = true;
+}
+
+void Program::checkUpdatedAllocation(const Allocation *alloc)
+{
+ if (mConstants.get() == alloc) {
+ mDirty = true;
+ }
+}
+
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
new file mode 100644
index 0000000..bb3d9ac
--- /dev/null
+++ b/libs/rs/rsProgram.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_H
+#define ANDROID_RS_PROGRAM_H
+
+#include "rsObjectBase.h"
+#include "rsElement.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+
+class Program : public ObjectBase
+{
+public:
+ Program(Element *in, Element *out);
+ virtual ~Program();
+
+ void bindAllocation(Allocation *);
+ void checkUpdatedAllocation(const Allocation *);
+
+protected:
+ // Components not listed in "in" will be passed though
+ // unless overwritten by components in out.
+ ObjectBaseRef<Element> mElementIn;
+ ObjectBaseRef<Element> mElementOut;
+
+ ObjectBaseRef<Allocation> mConstants;
+
+ mutable bool mDirty;
+};
+
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
new file mode 100644
index 0000000..6cf64a4
--- /dev/null
+++ b/libs/rs/rsProgramFragment.cpp
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramFragment.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramFragment::ProgramFragment(Element *in, Element *out, bool pointSpriteEnable) :
+ Program(in, out)
+{
+ for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+ mEnvModes[ct] = RS_TEX_ENV_MODE_REPLACE;
+ mTextureDimensions[ct] = 2;
+ }
+ mTextureEnableMask = 0;
+ mPointSpriteEnable = pointSpriteEnable;
+ mEnvModes[1] = RS_TEX_ENV_MODE_DECAL;
+}
+
+ProgramFragment::~ProgramFragment()
+{
+}
+
+void ProgramFragment::setupGL(const Context *rsc, ProgramFragmentState *state)
+{
+ if ((state->mLast.get() == this) && !mDirty) {
+ return;
+ }
+ state->mLast.set(this);
+
+ for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) {
+ glActiveTexture(GL_TEXTURE0 + ct);
+ if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) {
+ glDisable(GL_TEXTURE_2D);
+ continue;
+ }
+
+ glEnable(GL_TEXTURE_2D);
+ if (rsc->checkVersion1_1()) {
+ if (mPointSpriteEnable) {
+ glEnable(GL_POINT_SPRITE_OES);
+ } else {
+ glDisable(GL_POINT_SPRITE_OES);
+ }
+ glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, mPointSpriteEnable);
+ }
+ glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
+
+ switch(mEnvModes[ct]) {
+ case RS_TEX_ENV_MODE_REPLACE:
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ break;
+ case RS_TEX_ENV_MODE_MODULATE:
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ break;
+ case RS_TEX_ENV_MODE_DECAL:
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
+ break;
+ }
+
+ if (mSamplers[ct].get()) {
+ mSamplers[ct]->setupGL();
+ } else {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+
+ // Gross hack.
+ if (ct == 2) {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+ }
+ }
+ glActiveTexture(GL_TEXTURE0);
+ mDirty = false;
+}
+
+
+void ProgramFragment::bindTexture(uint32_t slot, Allocation *a)
+{
+ if (slot >= MAX_TEXTURE) {
+ LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE");
+ return;
+ }
+
+ //LOGE("bindtex %i %p", slot, a);
+ mTextures[slot].set(a);
+ mDirty = true;
+}
+
+void ProgramFragment::bindSampler(uint32_t slot, Sampler *s)
+{
+ if (slot >= MAX_TEXTURE) {
+ LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE");
+ return;
+ }
+
+ mSamplers[slot].set(s);
+ mDirty = true;
+}
+
+void ProgramFragment::setType(uint32_t slot, const Element *e, uint32_t dim)
+{
+ if (slot >= MAX_TEXTURE) {
+ LOGE("Attempt to setType to a slot > MAX_TEXTURE");
+ return;
+ }
+
+ if (dim >= 4) {
+ LOGE("Attempt to setType to a dimension > 3");
+ return;
+ }
+
+ mTextureFormats[slot].set(e);
+ mTextureDimensions[slot] = dim;
+}
+
+void ProgramFragment::setEnvMode(uint32_t slot, RsTexEnvMode env)
+{
+ if (slot >= MAX_TEXTURE) {
+ LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE");
+ return;
+ }
+
+ mEnvModes[slot] = env;
+}
+
+void ProgramFragment::setTexEnable(uint32_t slot, bool enable)
+{
+ if (slot >= MAX_TEXTURE) {
+ LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE");
+ return;
+ }
+
+ uint32_t bit = 1 << slot;
+ mTextureEnableMask &= ~bit;
+ if (enable) {
+ mTextureEnableMask |= bit;
+ }
+}
+
+
+
+ProgramFragmentState::ProgramFragmentState()
+{
+ mPF = NULL;
+}
+
+ProgramFragmentState::~ProgramFragmentState()
+{
+ delete mPF;
+
+}
+
+void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h)
+{
+ ProgramFragment *pf = new ProgramFragment(NULL, NULL, false);
+ mDefault.set(pf);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramFragmentBegin(Context * rsc, RsElement in, RsElement out, bool pointSpriteEnable)
+{
+ delete rsc->mStateFragment.mPF;
+ rsc->mStateFragment.mPF = new ProgramFragment((Element *)in, (Element *)out, pointSpriteEnable);
+}
+
+void rsi_ProgramFragmentBindTexture(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsAllocation a)
+{
+ ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+ pf->bindTexture(slot, static_cast<Allocation *>(a));
+}
+
+void rsi_ProgramFragmentBindSampler(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsSampler s)
+{
+ ProgramFragment *pf = static_cast<ProgramFragment *>(vpf);
+ pf->bindSampler(slot, static_cast<Sampler *>(s));
+}
+
+void rsi_ProgramFragmentSetSlot(Context *rsc, uint32_t slot, bool enable, RsTexEnvMode env, RsType vt)
+{
+ const Type *t = static_cast<const Type *>(vt);
+ if (t) {
+ uint32_t dim = 1;
+ if (t->getDimY()) {
+ dim ++;
+ if (t->getDimZ()) {
+ dim ++;
+ }
+ }
+ rsc->mStateFragment.mPF->setType(slot, t->getElement(), dim);
+ }
+ rsc->mStateFragment.mPF->setEnvMode(slot, env);
+ rsc->mStateFragment.mPF->setTexEnable(slot, enable);
+}
+
+RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc)
+{
+ ProgramFragment *pf = rsc->mStateFragment.mPF;
+ pf->incUserRef();
+ rsc->mStateFragment.mPF = 0;
+ return pf;
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h
new file mode 100644
index 0000000..51117eb
--- /dev/null
+++ b/libs/rs/rsProgramFragment.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_FRAGMENT_H
+#define ANDROID_RS_PROGRAM_FRAGMENT_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramFragmentState;
+
+class ProgramFragment : public Program
+{
+public:
+ const static uint32_t MAX_TEXTURE = 2;
+
+
+
+ ProgramFragment(Element *in, Element *out, bool pointSpriteEnable);
+ virtual ~ProgramFragment();
+
+ virtual void setupGL(const Context *, ProgramFragmentState *);
+
+
+
+ void bindTexture(uint32_t slot, Allocation *);
+ void bindSampler(uint32_t slot, Sampler *);
+ void setType(uint32_t slot, const Element *, uint32_t dim);
+
+ void setEnvMode(uint32_t slot, RsTexEnvMode);
+ void setTexEnable(uint32_t slot, bool);
+
+
+
+protected:
+ // The difference between Textures and Constants is how they are accessed
+ // Texture lookups go though a sampler which in effect converts normalized
+ // coordinates into type specific. Multiple samples may also be taken
+ // and filtered.
+ //
+ // Constants are strictly accessed by programetic loads.
+ ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE];
+ ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE];
+ ObjectBaseRef<const Element> mTextureFormats[MAX_TEXTURE];
+ uint32_t mTextureDimensions[MAX_TEXTURE];
+
+
+ // Hacks to create a program for now
+ RsTexEnvMode mEnvModes[MAX_TEXTURE];
+ uint32_t mTextureEnableMask;
+ bool mPointSpriteEnable;
+};
+
+class ProgramFragmentState
+{
+public:
+ ProgramFragmentState();
+ ~ProgramFragmentState();
+
+ ProgramFragment *mPF;
+ void init(Context *rsc, int32_t w, int32_t h);
+
+ ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
+ ObjectBaseRef<ProgramFragment> mDefault;
+ Vector<ProgramFragment *> mPrograms;
+
+ ObjectBaseRef<ProgramFragment> mLast;
+};
+
+
+}
+}
+#endif
+
+
+
+
diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp
new file mode 100644
index 0000000..3179484
--- /dev/null
+++ b/libs/rs/rsProgramFragmentStore.cpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramFragmentStore.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramFragmentStore::ProgramFragmentStore(Element *in, Element *out) :
+ Program(in, out)
+{
+ mDitherEnable = true;
+ mBlendEnable = false;
+ mColorRWriteEnable = true;
+ mColorGWriteEnable = true;
+ mColorBWriteEnable = true;
+ mColorAWriteEnable = true;
+ mBlendSrc = GL_ONE;
+ mBlendDst = GL_ZERO;
+
+
+ mDepthTestEnable = false;
+ mDepthWriteEnable = true;
+ mDepthFunc = GL_LESS;
+
+
+}
+
+ProgramFragmentStore::~ProgramFragmentStore()
+{
+}
+
+void ProgramFragmentStore::setupGL(const Context *rsc, ProgramFragmentStoreState *state)
+{
+ if (state->mLast.get() == this) {
+ return;
+ }
+ state->mLast.set(this);
+
+ glColorMask(mColorRWriteEnable,
+ mColorGWriteEnable,
+ mColorBWriteEnable,
+ mColorAWriteEnable);
+ if (mBlendEnable) {
+ glEnable(GL_BLEND);
+ glBlendFunc(mBlendSrc, mBlendDst);
+ } else {
+ glDisable(GL_BLEND);
+ }
+
+ //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc);
+
+ glDepthMask(mDepthWriteEnable);
+ if(mDepthTestEnable || mDepthWriteEnable) {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(mDepthFunc);
+ } else {
+ glDisable(GL_DEPTH_TEST);
+ }
+
+ if (mDitherEnable) {
+ glEnable(GL_DITHER);
+ } else {
+ glDisable(GL_DITHER);
+ }
+
+
+}
+
+void ProgramFragmentStore::setDitherEnable(bool enable)
+{
+ mDitherEnable = enable;
+}
+
+void ProgramFragmentStore::setDepthFunc(RsDepthFunc func)
+{
+ mDepthTestEnable = true;
+
+ switch(func) {
+ case RS_DEPTH_FUNC_ALWAYS:
+ mDepthTestEnable = false;
+ mDepthFunc = GL_ALWAYS;
+ break;
+ case RS_DEPTH_FUNC_LESS:
+ mDepthFunc = GL_LESS;
+ break;
+ case RS_DEPTH_FUNC_LEQUAL:
+ mDepthFunc = GL_LEQUAL;
+ break;
+ case RS_DEPTH_FUNC_GREATER:
+ mDepthFunc = GL_GREATER;
+ break;
+ case RS_DEPTH_FUNC_GEQUAL:
+ mDepthFunc = GL_GEQUAL;
+ break;
+ case RS_DEPTH_FUNC_EQUAL:
+ mDepthFunc = GL_EQUAL;
+ break;
+ case RS_DEPTH_FUNC_NOTEQUAL:
+ mDepthFunc = GL_NOTEQUAL;
+ break;
+ }
+}
+
+void ProgramFragmentStore::setDepthMask(bool mask)
+{
+ mDepthWriteEnable = mask;
+}
+
+void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+ mBlendEnable = true;
+ if ((src == RS_BLEND_SRC_ONE) &&
+ (dst == RS_BLEND_DST_ZERO)) {
+ mBlendEnable = false;
+ }
+
+ switch(src) {
+ case RS_BLEND_SRC_ZERO:
+ mBlendSrc = GL_ZERO;
+ break;
+ case RS_BLEND_SRC_ONE:
+ mBlendSrc = GL_ONE;
+ break;
+ case RS_BLEND_SRC_DST_COLOR:
+ mBlendSrc = GL_DST_COLOR;
+ break;
+ case RS_BLEND_SRC_ONE_MINUS_DST_COLOR:
+ mBlendSrc = GL_ONE_MINUS_DST_COLOR;
+ break;
+ case RS_BLEND_SRC_SRC_ALPHA:
+ mBlendSrc = GL_SRC_ALPHA;
+ break;
+ case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA:
+ mBlendSrc = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case RS_BLEND_SRC_DST_ALPHA:
+ mBlendSrc = GL_DST_ALPHA;
+ break;
+ case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA:
+ mBlendSrc = GL_ONE_MINUS_DST_ALPHA;
+ break;
+ case RS_BLEND_SRC_SRC_ALPHA_SATURATE:
+ mBlendSrc = GL_SRC_ALPHA_SATURATE;
+ break;
+ }
+
+ switch(dst) {
+ case RS_BLEND_DST_ZERO:
+ mBlendDst = GL_ZERO;
+ break;
+ case RS_BLEND_DST_ONE:
+ mBlendDst = GL_ONE;
+ break;
+ case RS_BLEND_DST_SRC_COLOR:
+ mBlendDst = GL_SRC_COLOR;
+ break;
+ case RS_BLEND_DST_ONE_MINUS_SRC_COLOR:
+ mBlendDst = GL_ONE_MINUS_SRC_COLOR;
+ break;
+ case RS_BLEND_DST_SRC_ALPHA:
+ mBlendDst = GL_SRC_ALPHA;
+ break;
+ case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA:
+ mBlendDst = GL_ONE_MINUS_SRC_ALPHA;
+ break;
+ case RS_BLEND_DST_DST_ALPHA:
+ mBlendDst = GL_DST_ALPHA;
+ break;
+ case RS_BLEND_DST_ONE_MINUS_DST_ALPHA:
+ mBlendDst = GL_ONE_MINUS_DST_ALPHA;
+ break;
+ }
+}
+
+void ProgramFragmentStore::setColorMask(bool r, bool g, bool b, bool a)
+{
+ mColorRWriteEnable = r;
+ mColorGWriteEnable = g;
+ mColorBWriteEnable = b;
+ mColorAWriteEnable = a;
+}
+
+
+ProgramFragmentStoreState::ProgramFragmentStoreState()
+{
+ mPFS = NULL;
+}
+
+ProgramFragmentStoreState::~ProgramFragmentStoreState()
+{
+ delete mPFS;
+
+}
+
+void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h)
+{
+ ProgramFragmentStore *pfs = new ProgramFragmentStore(NULL, NULL);
+ mDefault.set(pfs);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramFragmentStoreBegin(Context * rsc, RsElement in, RsElement out)
+{
+ delete rsc->mStateFragmentStore.mPFS;
+ rsc->mStateFragmentStore.mPFS = new ProgramFragmentStore((Element *)in, (Element *)out);
+
+}
+
+void rsi_ProgramFragmentStoreDepthFunc(Context *rsc, RsDepthFunc func)
+{
+ rsc->mStateFragmentStore.mPFS->setDepthFunc(func);
+}
+
+void rsi_ProgramFragmentStoreDepthMask(Context *rsc, bool mask)
+{
+ rsc->mStateFragmentStore.mPFS->setDepthMask(mask);
+}
+
+void rsi_ProgramFragmentStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a)
+{
+ rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a);
+}
+
+void rsi_ProgramFragmentStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst)
+{
+ rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst);
+}
+
+RsProgramFragmentStore rsi_ProgramFragmentStoreCreate(Context *rsc)
+{
+ ProgramFragmentStore *pfs = rsc->mStateFragmentStore.mPFS;
+ pfs->incUserRef();
+ rsc->mStateFragmentStore.mPFS = 0;
+ return pfs;
+}
+
+void rsi_ProgramFragmentStoreDither(Context *rsc, bool enable)
+{
+ rsc->mStateFragmentStore.mPFS->setDitherEnable(enable);
+}
+
+
+}
+}
diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramFragmentStore.h
new file mode 100644
index 0000000..e646e03
--- /dev/null
+++ b/libs/rs/rsProgramFragmentStore.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+#define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramFragmentStoreState;
+
+class ProgramFragmentStore : public Program
+{
+public:
+ ProgramFragmentStore(Element *in, Element *out);
+ virtual ~ProgramFragmentStore();
+
+ virtual void setupGL(const Context *, ProgramFragmentStoreState *);
+
+ void setDepthFunc(RsDepthFunc);
+ void setDepthMask(bool);
+
+ void setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst);
+ void setColorMask(bool, bool, bool, bool);
+
+ void setDitherEnable(bool);
+
+protected:
+ bool mDitherEnable;
+
+ bool mBlendEnable;
+ bool mColorRWriteEnable;
+ bool mColorGWriteEnable;
+ bool mColorBWriteEnable;
+ bool mColorAWriteEnable;
+ int32_t mBlendSrc;
+ int32_t mBlendDst;
+
+ bool mDepthTestEnable;
+ bool mDepthWriteEnable;
+ int32_t mDepthFunc;
+
+ bool mStencilTestEnable;
+};
+
+class ProgramFragmentStoreState
+{
+public:
+ ProgramFragmentStoreState();
+ ~ProgramFragmentStoreState();
+ void init(Context *rsc, int32_t w, int32_t h);
+
+ ObjectBaseRef<ProgramFragmentStore> mDefault;
+ ObjectBaseRef<ProgramFragmentStore> mLast;
+
+
+ ProgramFragmentStore *mPFS;
+};
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
new file mode 100644
index 0000000..a07e166
--- /dev/null
+++ b/libs/rs/rsProgramVertex.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsProgramVertex.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+
+ProgramVertex::ProgramVertex(Element *in, Element *out) :
+ Program(in, out)
+{
+ mTextureMatrixEnable = false;
+ mLightCount = 0;
+}
+
+ProgramVertex::~ProgramVertex()
+{
+}
+
+static void logMatrix(const char *txt, const float *f)
+{
+ LOGV("Matrix %s, %p", txt, f);
+ LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[0], f[4], f[8], f[12]);
+ LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[1], f[5], f[9], f[13]);
+ LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[2], f[6], f[10], f[14]);
+ LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[3], f[7], f[11], f[15]);
+}
+
+void ProgramVertex::setupGL(const Context *rsc, ProgramVertexState *state)
+{
+ if ((state->mLast.get() == this) && !mDirty) {
+ return;
+ }
+ state->mLast.set(this);
+
+ const float *f = static_cast<const float *>(mConstants->getPtr());
+
+ glMatrixMode(GL_TEXTURE);
+ if (mTextureMatrixEnable) {
+ glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
+ } else {
+ glLoadIdentity();
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ if (mLightCount) {
+ int v = 0;
+ glEnable(GL_LIGHTING);
+ glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v);
+ for (uint32_t ct = 0; ct < mLightCount; ct++) {
+ const Light *l = mLights[ct].get();
+ glEnable(GL_LIGHT0 + ct);
+ l->setupGL(ct);
+ }
+ for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) {
+ glDisable(GL_LIGHT0 + ct);
+ }
+ } else {
+ glDisable(GL_LIGHTING);
+ }
+
+ if (!f) {
+ LOGE("Must bind constants to vertex program");
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]);
+
+ mDirty = false;
+}
+
+void ProgramVertex::addLight(const Light *l)
+{
+ if (mLightCount < MAX_LIGHTS) {
+ mLights[mLightCount].set(l);
+ mLightCount++;
+ }
+}
+
+void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
+{
+ float *f = static_cast<float *>(mConstants->getPtr());
+ memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
+ mDirty = true;
+}
+
+void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
+{
+ float *f = static_cast<float *>(mConstants->getPtr());
+ memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
+ mDirty = true;
+}
+
+void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
+{
+ float *f = static_cast<float *>(mConstants->getPtr());
+ memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
+ mDirty = true;
+}
+
+
+
+ProgramVertexState::ProgramVertexState()
+{
+ mPV = NULL;
+}
+
+ProgramVertexState::~ProgramVertexState()
+{
+ delete mPV;
+}
+
+void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h)
+{
+ ProgramVertex *pv = new ProgramVertex(NULL, NULL);
+ Allocation *alloc = (Allocation *)
+ rsi_AllocationCreatePredefSized(rsc, RS_ELEMENT_USER_FLOAT, 48);
+ mDefaultAlloc.set(alloc);
+ mDefault.set(pv);
+
+ pv->bindAllocation(alloc);
+
+ Matrix m;
+ m.loadOrtho(0,w, h,0, -1,1);
+ alloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0], 16*4);
+
+ m.loadIdentity();
+ alloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0], 16*4);
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ProgramVertexBegin(Context *rsc, RsElement in, RsElement out)
+{
+ delete rsc->mStateVertex.mPV;
+ rsc->mStateVertex.mPV = new ProgramVertex((Element *)in, (Element *)out);
+}
+
+RsProgramVertex rsi_ProgramVertexCreate(Context *rsc)
+{
+ ProgramVertex *pv = rsc->mStateVertex.mPV;
+ pv->incUserRef();
+ rsc->mStateVertex.mPV = 0;
+ return pv;
+}
+
+void rsi_ProgramVertexBindAllocation(Context *rsc, RsProgramVertex vpgm, RsAllocation constants)
+{
+ ProgramVertex *pv = static_cast<ProgramVertex *>(vpgm);
+ pv->bindAllocation(static_cast<Allocation *>(constants));
+}
+
+void rsi_ProgramVertexSetTextureMatrixEnable(Context *rsc, bool enable)
+{
+ rsc->mStateVertex.mPV->setTextureMatrixEnable(enable);
+}
+
+void rsi_ProgramVertexAddLight(Context *rsc, RsLight light)
+{
+ rsc->mStateVertex.mPV->addLight(static_cast<const Light *>(light));
+}
+
+
+}
+}
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
new file mode 100644
index 0000000..523c3ed
--- /dev/null
+++ b/libs/rs/rsProgramVertex.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_PROGRAM_VERTEX_H
+#define ANDROID_RS_PROGRAM_VERTEX_H
+
+#include "rsProgram.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramVertexState;
+
+class ProgramVertex : public Program
+{
+public:
+ const static uint32_t MAX_LIGHTS = 8;
+
+ ProgramVertex(Element *in, Element *out);
+ virtual ~ProgramVertex();
+
+ virtual void setupGL(const Context *rsc, ProgramVertexState *state);
+
+
+ void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
+ void addLight(const Light *);
+
+ void setProjectionMatrix(const rsc_Matrix *) const;
+ void setModelviewMatrix(const rsc_Matrix *) const;
+ void setTextureMatrix(const rsc_Matrix *) const;
+
+protected:
+ uint32_t mLightCount;
+ ObjectBaseRef<const Light> mLights[MAX_LIGHTS];
+
+ // Hacks to create a program for now
+ bool mTextureMatrixEnable;
+};
+
+
+class ProgramVertexState
+{
+public:
+ ProgramVertexState();
+ ~ProgramVertexState();
+
+ void init(Context *rsc, int32_t w, int32_t h);
+
+ ObjectBaseRef<ProgramVertex> mDefault;
+ ObjectBaseRef<ProgramVertex> mLast;
+ ObjectBaseRef<Allocation> mDefaultAlloc;
+
+
+
+ ProgramVertex *mPV;
+
+ //ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE];
+
+
+};
+
+
+}
+}
+#endif
+
+
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
new file mode 100644
index 0000000..3f56faa
--- /dev/null
+++ b/libs/rs/rsSampler.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "rsContext.h"
+#include "rsSampler.h"
+
+
+using namespace android;
+using namespace android::renderscript;
+
+
+Sampler::Sampler()
+{
+ // Should not get called.
+ rsAssert(0);
+}
+
+Sampler::Sampler(RsSamplerValue magFilter,
+ RsSamplerValue minFilter,
+ RsSamplerValue wrapS,
+ RsSamplerValue wrapT,
+ RsSamplerValue wrapR)
+{
+ mMagFilter = magFilter;
+ mMinFilter = minFilter;
+ mWrapS = wrapS;
+ mWrapT = wrapT;
+ mWrapR = wrapR;
+}
+
+Sampler::~Sampler()
+{
+}
+
+void Sampler::setupGL()
+{
+ GLenum trans[] = {
+ GL_NEAREST, //RS_SAMPLER_NEAREST,
+ GL_LINEAR, //RS_SAMPLER_LINEAR,
+ GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR,
+ GL_REPEAT, //RS_SAMPLER_WRAP,
+ GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP
+
+ };
+
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
+
+}
+
+void Sampler::bindToContext(SamplerState *ss, uint32_t slot)
+{
+ ss->mSamplers[slot].set(this);
+ mBoundSlot = slot;
+}
+
+void Sampler::unbindFromContext(SamplerState *ss)
+{
+ int32_t slot = mBoundSlot;
+ mBoundSlot = -1;
+ ss->mSamplers[slot].clear();
+}
+
+void SamplerState::setupGL()
+{
+ for (uint32_t ct=0; ct < RS_MAX_SAMPLER_SLOT; ct++) {
+ Sampler *s = mSamplers[ct].get();
+ if (s) {
+ s->setupGL();
+ } else {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+ }
+}
+
+////////////////////////////////
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_SamplerBegin(Context *rsc)
+{
+ SamplerState * ss = &rsc->mStateSampler;
+
+ ss->mMagFilter = RS_SAMPLER_LINEAR;
+ ss->mMinFilter = RS_SAMPLER_LINEAR;
+ ss->mWrapS = RS_SAMPLER_WRAP;
+ ss->mWrapT = RS_SAMPLER_WRAP;
+ ss->mWrapR = RS_SAMPLER_WRAP;
+}
+
+void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value)
+{
+ SamplerState * ss = &rsc->mStateSampler;
+
+ switch(param) {
+ case RS_SAMPLER_MAG_FILTER:
+ ss->mMagFilter = value;
+ break;
+ case RS_SAMPLER_MIN_FILTER:
+ ss->mMinFilter = value;
+ break;
+ case RS_SAMPLER_WRAP_S:
+ ss->mWrapS = value;
+ break;
+ case RS_SAMPLER_WRAP_T:
+ ss->mWrapT = value;
+ break;
+ case RS_SAMPLER_WRAP_R:
+ ss->mWrapR = value;
+ break;
+ }
+
+}
+
+RsSampler rsi_SamplerCreate(Context *rsc)
+{
+ SamplerState * ss = &rsc->mStateSampler;
+
+
+ Sampler * s = new Sampler(ss->mMagFilter,
+ ss->mMinFilter,
+ ss->mWrapS,
+ ss->mWrapT,
+ ss->mWrapR);
+ s->incUserRef();
+ return s;
+}
+
+
+}}
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
new file mode 100644
index 0000000..4b504f6
--- /dev/null
+++ b/libs/rs/rsSampler.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SAMPLER_H
+#define ANDROID_RS_SAMPLER_H
+
+#include "rsAllocation.h"
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+const static uint32_t RS_MAX_SAMPLER_SLOT = 16;
+
+class SamplerState;
+
+class Sampler : public ObjectBase
+{
+public:
+ Sampler(RsSamplerValue magFilter,
+ RsSamplerValue minFilter,
+ RsSamplerValue wrapS,
+ RsSamplerValue wrapT,
+ RsSamplerValue wrapR);
+
+ virtual ~Sampler();
+
+ void bind(Allocation *);
+ void setupGL();
+
+ void bindToContext(SamplerState *, uint32_t slot);
+ void unbindFromContext(SamplerState *);
+
+protected:
+ RsSamplerValue mMagFilter;
+ RsSamplerValue mMinFilter;
+ RsSamplerValue mWrapS;
+ RsSamplerValue mWrapT;
+ RsSamplerValue mWrapR;
+
+ int32_t mBoundSlot;
+
+private:
+ Sampler();
+
+};
+
+
+class SamplerState
+{
+public:
+
+ RsSamplerValue mMagFilter;
+ RsSamplerValue mMinFilter;
+ RsSamplerValue mWrapS;
+ RsSamplerValue mWrapT;
+ RsSamplerValue mWrapR;
+
+
+ ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT];
+
+ void setupGL();
+
+};
+
+
+
+}
+}
+#endif //ANDROID_RS_SAMPLER_H
+
+
+
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
new file mode 100644
index 0000000..fde31a1
--- /dev/null
+++ b/libs/rs/rsScript.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+Script::Script()
+{
+ memset(&mEnviroment, 0, sizeof(mEnviroment));
+ mEnviroment.mClearColor[0] = 0;
+ mEnviroment.mClearColor[1] = 0;
+ mEnviroment.mClearColor[2] = 0;
+ mEnviroment.mClearColor[3] = 1;
+ mEnviroment.mClearDepth = 1;
+}
+
+Script::~Script()
+{
+}
+
+namespace android {
+namespace renderscript {
+
+
+void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot)
+{
+ Script *s = static_cast<Script *>(vs);
+ s->mSlots[slot].set(static_cast<Allocation *>(va));
+}
+
+void rsi_ScriptSetClearColor(Context * rsc, RsScript vs, float r, float g, float b, float a)
+{
+ Script *s = static_cast<Script *>(vs);
+ s->mEnviroment.mClearColor[0] = r;
+ s->mEnviroment.mClearColor[1] = g;
+ s->mEnviroment.mClearColor[2] = b;
+ s->mEnviroment.mClearColor[3] = a;
+}
+
+void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, uint32_t length)
+{
+ Script *s = static_cast<Script *>(vs);
+ s->mEnviroment.mTimeZone = timeZone;
+}
+
+void rsi_ScriptSetClearDepth(Context * rsc, RsScript vs, float v)
+{
+ Script *s = static_cast<Script *>(vs);
+ s->mEnviroment.mClearDepth = v;
+}
+
+void rsi_ScriptSetClearStencil(Context * rsc, RsScript vs, uint32_t v)
+{
+ Script *s = static_cast<Script *>(vs);
+ s->mEnviroment.mClearStencil = v;
+}
+
+void rsi_ScriptSetType(Context * rsc, RsType vt, uint32_t slot, bool writable, const char *name)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ const Type *t = static_cast<const Type *>(vt);
+ ss->mConstantBufferTypes[slot].set(t);
+ ss->mSlotWritable[slot] = writable;
+ if (name) {
+ ss->mSlotNames[slot].setTo(name);
+ } else {
+ ss->mSlotNames[slot].setTo("");
+ }
+}
+
+void rsi_ScriptSetRoot(Context * rsc, bool isRoot)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->mEnviroment.mIsRoot = isRoot;
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
new file mode 100644
index 0000000..60f83a6
--- /dev/null
+++ b/libs/rs/rsScript.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SCRIPT_H
+#define ANDROID_RS_SCRIPT_H
+
+#include "rsAllocation.h"
+
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class ProgramVertex;
+class ProgramFragment;
+class ProgramRaster;
+class ProgramFragmentStore;
+
+#define MAX_SCRIPT_BANKS 16
+
+class Script : public ObjectBase
+{
+public:
+
+ Script();
+ virtual ~Script();
+
+
+ struct Enviroment_t {
+ bool mIsRoot;
+ float mClearColor[4];
+ float mClearDepth;
+ uint32_t mClearStencil;
+
+ uint32_t mStartTimeMillis;
+ const char* mTimeZone;
+
+ ObjectBaseRef<ProgramVertex> mVertex;
+ ObjectBaseRef<ProgramFragment> mFragment;
+ //ObjectBaseRef<ProgramRaster> mRaster;
+ ObjectBaseRef<ProgramFragmentStore> mFragmentStore;
+
+ };
+ Enviroment_t mEnviroment;
+
+ uint32_t mCounstantBufferCount;
+
+ ObjectBaseRef<Allocation> mSlots[MAX_SCRIPT_BANKS];
+ ObjectBaseRef<const Type> mTypes[MAX_SCRIPT_BANKS];
+ String8 mSlotNames[MAX_SCRIPT_BANKS];
+ bool mSlotWritable[MAX_SCRIPT_BANKS];
+
+ virtual bool run(Context *, uint32_t launchID) = 0;
+};
+
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
new file mode 100644
index 0000000..8230cbc
--- /dev/null
+++ b/libs/rs/rsScriptC.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+#include "rsMatrix.h"
+
+#include "acc/acc.h"
+#include "utils/Timers.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+#define GET_TLS() Context::ScriptTLSStruct * tls = \
+ (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+ Context * rsc = tls->mContext; \
+ ScriptC * sc = (ScriptC *) tls->mScript
+
+
+ScriptC::ScriptC()
+{
+ mAccScript = NULL;
+ memset(&mProgram, 0, sizeof(mProgram));
+}
+
+ScriptC::~ScriptC()
+{
+ if (mAccScript) {
+ accDeleteScript(mAccScript);
+ }
+}
+
+
+bool ScriptC::run(Context *rsc, uint32_t launchIndex)
+{
+ Context::ScriptTLSStruct * tls =
+ (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey);
+
+ if (mEnviroment.mFragmentStore.get()) {
+ rsc->setFragmentStore(mEnviroment.mFragmentStore.get());
+ }
+ if (mEnviroment.mFragment.get()) {
+ rsc->setFragment(mEnviroment.mFragment.get());
+ }
+ if (mEnviroment.mVertex.get()) {
+ rsc->setVertex(mEnviroment.mVertex.get());
+ }
+
+ if (launchIndex == 0) {
+ mEnviroment.mStartTimeMillis
+ = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+ }
+
+ for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
+ if (mProgram.mSlotPointers[ct]) {
+ *mProgram.mSlotPointers[ct] = mSlots[ct]->getPtr();
+ }
+ }
+
+ bool ret = false;
+ tls->mScript = this;
+ ret = mProgram.mScript(launchIndex) != 0;
+ tls->mScript = NULL;
+ return ret;
+}
+
+ScriptCState::ScriptCState()
+{
+ clear();
+}
+
+ScriptCState::~ScriptCState()
+{
+ if (mAccScript) {
+ accDeleteScript(mAccScript);
+ }
+}
+
+void ScriptCState::clear()
+{
+ memset(&mProgram, 0, sizeof(mProgram));
+
+ for (uint32_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
+ mConstantBufferTypes[ct].clear();
+ mSlotNames[ct].setTo("");
+ mSlotWritable[ct] = false;
+ }
+
+ memset(&mEnviroment, 0, sizeof(mEnviroment));
+ mEnviroment.mClearColor[0] = 0;
+ mEnviroment.mClearColor[1] = 0;
+ mEnviroment.mClearColor[2] = 0;
+ mEnviroment.mClearColor[3] = 1;
+ mEnviroment.mClearDepth = 1;
+ mEnviroment.mClearStencil = 0;
+ mEnviroment.mIsRoot = false;
+
+ mAccScript = NULL;
+
+ mInt32Defines.clear();
+ mFloatDefines.clear();
+}
+
+static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name)
+{
+ const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name);
+ if (sym) {
+ return sym->mPtr;
+ }
+ LOGE("ScriptC sym lookup failed for %s", name);
+ return NULL;
+}
+
+void ScriptCState::runCompiler(Context *rsc)
+{
+ mAccScript = accCreateScript();
+ String8 tmp;
+
+ rsc->appendNameDefines(&tmp);
+ appendDecls(&tmp);
+ rsc->appendVarDefines(&tmp);
+ appendVarDefines(&tmp);
+ appendTypes(&tmp);
+ tmp.append("#line 1\n");
+
+ const char* scriptSource[] = {tmp.string(), mProgram.mScriptText};
+ int scriptLength[] = {tmp.length(), mProgram.mScriptTextLength} ;
+ accScriptSource(mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength);
+ accRegisterSymbolCallback(mAccScript, symbolLookup, NULL);
+ accCompileScript(mAccScript);
+ accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript);
+ accGetScriptLabel(mAccScript, "init", (ACCvoid**) &mProgram.mInit);
+ rsAssert(mProgram.mScript);
+
+ if (!mProgram.mScript) {
+ ACCchar buf[4096];
+ ACCsizei len;
+ accGetScriptInfoLog(mAccScript, sizeof(buf), &len, buf);
+ LOGE(buf);
+ }
+
+ if (mProgram.mInit) {
+ mProgram.mInit();
+ }
+
+ for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
+ if (mSlotNames[ct].length() > 0) {
+ accGetScriptLabel(mAccScript,
+ mSlotNames[ct].string(),
+ (ACCvoid**) &mProgram.mSlotPointers[ct]);
+ LOGE("var %s %p", mSlotNames[ct].string(), mProgram.mSlotPointers[ct]);
+ }
+ }
+
+ mEnviroment.mFragment.set(rsc->getDefaultProgramFragment());
+ mEnviroment.mVertex.set(rsc->getDefaultProgramVertex());
+ mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore());
+
+ if (mProgram.mScript) {
+ const static int pragmaMax = 16;
+ ACCsizei pragmaCount;
+ ACCchar * str[pragmaMax];
+ accGetPragmas(mAccScript, &pragmaCount, pragmaMax, &str[0]);
+
+ for (int ct=0; ct < pragmaCount; ct+=2) {
+ if (!strcmp(str[ct], "version")) {
+ continue;
+ }
+
+ if (!strcmp(str[ct], "stateVertex")) {
+ if (!strcmp(str[ct+1], "default")) {
+ continue;
+ }
+ if (!strcmp(str[ct+1], "parent")) {
+ mEnviroment.mVertex.clear();
+ continue;
+ }
+ ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]);
+ if (pv != NULL) {
+ mEnviroment.mVertex.set(pv);
+ continue;
+ }
+ LOGE("Unreconized value %s passed to stateVertex", str[ct+1]);
+ }
+
+ if (!strcmp(str[ct], "stateRaster")) {
+ LOGE("Unreconized value %s passed to stateRaster", str[ct+1]);
+ }
+
+ if (!strcmp(str[ct], "stateFragment")) {
+ if (!strcmp(str[ct+1], "default")) {
+ continue;
+ }
+ if (!strcmp(str[ct+1], "parent")) {
+ mEnviroment.mFragment.clear();
+ continue;
+ }
+ ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]);
+ if (pf != NULL) {
+ mEnviroment.mFragment.set(pf);
+ continue;
+ }
+ LOGE("Unreconized value %s passed to stateFragment", str[ct+1]);
+ }
+
+ if (!strcmp(str[ct], "stateFragmentStore")) {
+ if (!strcmp(str[ct+1], "default")) {
+ continue;
+ }
+ if (!strcmp(str[ct+1], "parent")) {
+ mEnviroment.mFragmentStore.clear();
+ continue;
+ }
+ ProgramFragmentStore * pfs =
+ (ProgramFragmentStore *)rsc->lookupName(str[ct+1]);
+ if (pfs != NULL) {
+ mEnviroment.mFragmentStore.set(pfs);
+ continue;
+ }
+ LOGE("Unreconized value %s passed to stateFragmentStore", str[ct+1]);
+ }
+
+ }
+
+
+ } else {
+ // Deal with an error.
+ }
+}
+
+static void appendElementBody(String8 *s, const Element *e)
+{
+ s->append(" {\n");
+ for (size_t ct2=0; ct2 < e->getComponentCount(); ct2++) {
+ const Component *c = e->getComponent(ct2);
+ s->append(" ");
+ s->append(c->getCType());
+ s->append(" ");
+ s->append(c->getComponentName());
+ s->append(";\n");
+ }
+ s->append("}");
+}
+
+void ScriptCState::appendVarDefines(String8 *str)
+{
+ char buf[256];
+ LOGD("appendVarDefines mInt32Defines.size()=%d mFloatDefines.size()=%d\n",
+ mInt32Defines.size(), mFloatDefines.size());
+ for (size_t ct=0; ct < mInt32Defines.size(); ct++) {
+ str->append("#define ");
+ str->append(mInt32Defines.keyAt(ct));
+ str->append(" ");
+ sprintf(buf, "%i\n", (int)mInt32Defines.valueAt(ct));
+ str->append(buf);
+ }
+ for (size_t ct=0; ct < mFloatDefines.size(); ct++) {
+ str->append("#define ");
+ str->append(mFloatDefines.keyAt(ct));
+ str->append(" ");
+ sprintf(buf, "%ff\n", mFloatDefines.valueAt(ct));
+ str->append(buf);
+ }
+}
+
+
+
+void ScriptCState::appendTypes(String8 *str)
+{
+ char buf[256];
+ String8 tmp;
+
+ str->append("struct vec2_s {float x; float y;};");
+ str->append("struct vec3_s {float x; float y; float z;};");
+ str->append("struct vec4_s {float x; float y; float z; float w;};");
+
+ for (size_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
+ const Type *t = mConstantBufferTypes[ct].get();
+ if (!t) {
+ continue;
+ }
+ const Element *e = t->getElement();
+ if (e->getName() && (e->getComponentCount() > 1)) {
+ String8 s("struct struct_");
+ s.append(e->getName());
+ appendElementBody(&s, e);
+ s.append(";\n");
+ s.append("#define ");
+ s.append(e->getName());
+ s.append("_t struct struct_");
+ s.append(e->getName());
+ s.append("\n\n");
+ LOGD(s);
+ str->append(s);
+ }
+
+ if (t->getName()) {
+ for (size_t ct2=0; ct2 < e->getComponentCount(); ct2++) {
+ const Component *c = e->getComponent(ct2);
+ tmp.setTo("#define OFFSETOF_");
+ tmp.append(t->getName());
+ tmp.append("_");
+ tmp.append(c->getComponentName());
+ sprintf(buf, " %i\n", ct2);
+ tmp.append(buf);
+ LOGD(tmp);
+ str->append(tmp);
+ }
+ }
+
+ if (mSlotNames[ct].length() > 0) {
+ String8 s;
+ if (e->getComponentCount() > 1) {
+ if (e->getName()) {
+ // Use the named struct
+ s.setTo(e->getName());
+ s.append("_t *");
+ } else {
+ // create an struct named from the slot.
+ s.setTo("struct ");
+ s.append(mSlotNames[ct]);
+ s.append("_s");
+ appendElementBody(&s, e);
+ s.append(";\n");
+ s.append("struct ");
+ s.append(mSlotNames[ct]);
+ s.append("_s * ");
+ }
+ } else {
+ // Just make an array
+ s.setTo(e->getComponent(0)->getCType());
+ s.append("_t *");
+ }
+ s.append(mSlotNames[ct]);
+ s.append(";\n");
+ LOGD(s);
+ str->append(s);
+#if 0
+ for (size_t ct2=0; ct2 < e->getComponentCount(); ct2++) {
+ const Component *c = e->getComponent(ct2);
+ tmp.setTo("#define ");
+ tmp.append(mSlotNames[ct]);
+ tmp.append("_");
+ tmp.append(c->getComponentName());
+ switch (c->getType()) {
+ case Component::FLOAT:
+ tmp.append(" loadF(");
+ break;
+ case Component::SIGNED:
+ sprintf(buf, " loadI%i(", c->getBits());
+ tmp.append(buf);
+ break;
+ case Component::UNSIGNED:
+ sprintf(buf, " loadU%i(", c->getBits());
+ tmp.append(buf);
+ break;
+ }
+ sprintf(buf, "%i, %i)\n", ct, ct2);
+ tmp.append(buf);
+
+ LOGD(tmp);
+ str->append(tmp);
+ }
+#endif
+ }
+ }
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_ScriptCBegin(Context * rsc)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->clear();
+}
+
+void rsi_ScriptCSetScript(Context * rsc, void *vp)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp);
+}
+
+void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->mProgram.mScriptText = text;
+ ss->mProgram.mScriptTextLength = len;
+}
+
+
+RsScript rsi_ScriptCCreate(Context * rsc)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+
+ ss->runCompiler(rsc);
+
+ ScriptC *s = new ScriptC();
+ s->incUserRef();
+ s->mAccScript = ss->mAccScript;
+ ss->mAccScript = NULL;
+ s->mEnviroment = ss->mEnviroment;
+ s->mProgram = ss->mProgram;
+ for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) {
+ s->mTypes[ct].set(ss->mConstantBufferTypes[ct].get());
+ s->mSlotNames[ct] = ss->mSlotNames[ct];
+ s->mSlotWritable[ct] = ss->mSlotWritable[ct];
+ }
+
+ ss->clear();
+ return s;
+}
+
+void rsi_ScriptCSetDefineF(Context *rsc, const char* name, float value)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->mFloatDefines.add(String8(name), value);
+}
+
+void rsi_ScriptCSetDefineI32(Context *rsc, const char* name, int32_t value)
+{
+ ScriptCState *ss = &rsc->mScriptC;
+ ss->mInt32Defines.add(String8(name), value);
+}
+
+}
+}
+
+
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
new file mode 100644
index 0000000..8aa99ef
--- /dev/null
+++ b/libs/rs/rsScriptC.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SCRIPT_C_H
+#define ANDROID_RS_SCRIPT_C_H
+
+#include "rsScript.h"
+
+#include "RenderScriptEnv.h"
+
+#include <utils/KeyedVector.h>
+
+struct ACCscript;
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+
+class ScriptC : public Script
+{
+public:
+ typedef int (*RunScript_t)(uint32_t launchIndex);
+ typedef void (*VoidFunc_t)();
+
+ ScriptC();
+ virtual ~ScriptC();
+
+ struct Program_t {
+ const char * mScriptText;
+ uint32_t mScriptTextLength;
+
+
+ int mVersionMajor;
+ int mVersionMinor;
+
+ RunScript_t mScript;
+ VoidFunc_t mInit;
+
+ void ** mSlotPointers[MAX_SCRIPT_BANKS];
+ };
+
+ Program_t mProgram;
+
+ ACCscript* mAccScript;
+
+ virtual bool run(Context *, uint32_t launchID);
+};
+
+class ScriptCState
+{
+public:
+ ScriptCState();
+ ~ScriptCState();
+
+ ACCscript* mAccScript;
+
+ ScriptC::Program_t mProgram;
+ Script::Enviroment_t mEnviroment;
+
+ ObjectBaseRef<const Type> mConstantBufferTypes[MAX_SCRIPT_BANKS];
+ String8 mSlotNames[MAX_SCRIPT_BANKS];
+ bool mSlotWritable[MAX_SCRIPT_BANKS];
+
+ void clear();
+ void runCompiler(Context *rsc);
+ void appendVarDefines(String8 *str);
+ void appendTypes(String8 *str);
+
+ struct SymbolTable_t {
+ const char * mName;
+ void * mPtr;
+ const char * mRet;
+ const char * mParam;
+ };
+ static SymbolTable_t gSyms[];
+ static const SymbolTable_t * lookupSymbol(const char *);
+ static void appendDecls(String8 *str);
+
+ KeyedVector<String8,int> mInt32Defines;
+ KeyedVector<String8,float> mFloatDefines;
+};
+
+
+}
+}
+#endif
+
+
+
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
new file mode 100644
index 0000000..d10076b
--- /dev/null
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -0,0 +1,1254 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include "rsScriptC.h"
+#include "rsMatrix.h"
+#include "rsNoise.h"
+
+#include "acc/acc.h"
+#include "utils/Timers.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <time.h>
+#include <cutils/tztime.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+#define GET_TLS() Context::ScriptTLSStruct * tls = \
+ (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \
+ Context * rsc = tls->mContext; \
+ ScriptC * sc = (ScriptC *) tls->mScript
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+} vec3_t;
+
+typedef struct {
+ float x;
+ float y;
+ float z;
+ float w;
+} vec4_t;
+
+typedef struct {
+ float x;
+ float y;
+} vec2_t;
+
+//////////////////////////////////////////////////////////////////////////////
+// IO routines
+//////////////////////////////////////////////////////////////////////////////
+
+static float SC_loadF(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]);
+ return f[offset];
+}
+
+static int32_t SC_loadI32(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const int32_t *i = static_cast<const int32_t *>(vp);
+ //LOGE("loadI32 %i %i = %i", bank, offset, t);
+ return i[offset];
+}
+
+static float* SC_loadArrayF(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ return f + offset;
+}
+
+static int32_t* SC_loadArrayI32(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ int32_t *i = static_cast<int32_t *>(vp);
+ return i + offset;
+}
+
+static float* SC_loadTriangleMeshVerticesF(RsTriangleMesh mesh)
+{
+ TriangleMesh *tm = static_cast<TriangleMesh *>(mesh);
+ void *vp = tm->mVertexData;
+ float *f = static_cast<float *>(vp);
+ return f;
+}
+
+static void SC_updateTriangleMesh(RsTriangleMesh mesh)
+{
+ TriangleMesh *tm = static_cast<TriangleMesh *>(mesh);
+ glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]);
+ glBufferData(GL_ARRAY_BUFFER, tm->mVertexDataSize, tm->mVertexData, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, tm->mIndexDataSize, tm->mIndexData, GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+static uint32_t SC_loadU32(uint32_t bank, uint32_t offset)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const uint32_t *i = static_cast<const uint32_t *>(vp);
+ return i[offset];
+}
+
+static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ memcpy(v, &f[offset], sizeof(rsc_Vector4));
+}
+
+static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m)
+{
+ GET_TLS();
+ const void *vp = sc->mSlots[bank]->getPtr();
+ const float *f = static_cast<const float *>(vp);
+ memcpy(m, &f[offset], sizeof(rsc_Matrix));
+}
+
+
+static void SC_storeF(uint32_t bank, uint32_t offset, float v)
+{
+ //LOGE("storeF %i %i %f", bank, offset, v);
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ f[offset] = v;
+}
+
+static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ int32_t *f = static_cast<int32_t *>(vp);
+ static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ uint32_t *f = static_cast<uint32_t *>(vp);
+ static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v;
+}
+
+static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ memcpy(&f[offset], v, sizeof(rsc_Vector4));
+}
+
+static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m)
+{
+ GET_TLS();
+ void *vp = sc->mSlots[bank]->getPtr();
+ float *f = static_cast<float *>(vp);
+ memcpy(&f[offset], m, sizeof(rsc_Matrix));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Vec3 routines
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_vec3Norm(vec3_t *v)
+{
+ float len = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
+ len = 1 / len;
+ v->x *= len;
+ v->y *= len;
+ v->z *= len;
+}
+
+static float SC_vec3Length(const vec3_t *v)
+{
+ return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z);
+}
+
+static void SC_vec3Add(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
+{
+ dest->x = lhs->x + rhs->x;
+ dest->y = lhs->y + rhs->y;
+ dest->z = lhs->z + rhs->z;
+}
+
+static void SC_vec3Sub(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
+{
+ dest->x = lhs->x - rhs->x;
+ dest->y = lhs->y - rhs->y;
+ dest->z = lhs->z - rhs->z;
+}
+
+static void SC_vec3Cross(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs)
+{
+ float x = lhs->y * rhs->z - lhs->z * rhs->y;
+ float y = lhs->z * rhs->x - lhs->x * rhs->z;
+ float z = lhs->x * rhs->y - lhs->y * rhs->x;
+ dest->x = x;
+ dest->y = y;
+ dest->z = z;
+}
+
+static float SC_vec3Dot(const vec3_t *lhs, const vec3_t *rhs)
+{
+ return lhs->x * rhs->x + lhs->y * rhs->y + lhs->z * rhs->z;
+}
+
+static void SC_vec3Scale(vec3_t *lhs, float scale)
+{
+ lhs->x *= scale;
+ lhs->y *= scale;
+ lhs->z *= scale;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Math routines
+//////////////////////////////////////////////////////////////////////////////
+
+#define PI 3.1415926f
+#define DEG_TO_RAD PI / 180.0f
+#define RAD_TO_DEG 180.0f / PI
+
+static float SC_sinf_fast(float x)
+{
+ const float A = 1.0f / (2.0f * M_PI);
+ const float B = -16.0f;
+ const float C = 8.0f;
+
+ // scale angle for easy argument reduction
+ x *= A;
+
+ if (fabsf(x) >= 0.5f) {
+ // argument reduction
+ x = x - ceilf(x + 0.5f) + 1.0f;
+ }
+
+ const float y = B * x * fabsf(x) + C * x;
+ return 0.2215f * (y * fabsf(y) - y) + y;
+}
+
+static float SC_cosf_fast(float x)
+{
+ x += float(M_PI / 2);
+
+ const float A = 1.0f / (2.0f * M_PI);
+ const float B = -16.0f;
+ const float C = 8.0f;
+
+ // scale angle for easy argument reduction
+ x *= A;
+
+ if (fabsf(x) >= 0.5f) {
+ // argument reduction
+ x = x - ceilf(x + 0.5f) + 1.0f;
+ }
+
+ const float y = B * x * fabsf(x) + C * x;
+ return 0.2215f * (y * fabsf(y) - y) + y;
+}
+
+static float SC_randf(float max)
+{
+ float r = (float)rand();
+ return r / RAND_MAX * max;
+}
+
+static float SC_randf2(float min, float max)
+{
+ float r = (float)rand();
+ return r / RAND_MAX * (max - min) + min;
+}
+
+static float SC_clampf(float amount, float low, float high)
+{
+ return amount < low ? low : (amount > high ? high : amount);
+}
+
+static int SC_clamp(int amount, int low, int high)
+{
+ return amount < low ? low : (amount > high ? high : amount);
+}
+
+static float SC_maxf(float a, float b)
+{
+ return a > b ? a : b;
+}
+
+static float SC_minf(float a, float b)
+{
+ return a < b ? a : b;
+}
+
+static float SC_sqrf(float v)
+{
+ return v * v;
+}
+
+static int SC_sqr(int v)
+{
+ return v * v;
+}
+
+static float SC_distf2(float x1, float y1, float x2, float y2)
+{
+ float x = x2 - x1;
+ float y = y2 - y1;
+ return sqrtf(x * x + y * y);
+}
+
+static float SC_distf3(float x1, float y1, float z1, float x2, float y2, float z2)
+{
+ float x = x2 - x1;
+ float y = y2 - y1;
+ float z = z2 - z1;
+ return sqrtf(x * x + y * y + z * z);
+}
+
+static float SC_magf2(float a, float b)
+{
+ return sqrtf(a * a + b * b);
+}
+
+static float SC_magf3(float a, float b, float c)
+{
+ return sqrtf(a * a + b * b + c * c);
+}
+
+static float SC_radf(float degrees)
+{
+ return degrees * DEG_TO_RAD;
+}
+
+static float SC_degf(float radians)
+{
+ return radians * RAD_TO_DEG;
+}
+
+static float SC_lerpf(float start, float stop, float amount)
+{
+ return start + (stop - start) * amount;
+}
+
+static float SC_normf(float start, float stop, float value)
+{
+ return (value - start) / (stop - start);
+}
+
+static float SC_mapf(float minStart, float minStop, float maxStart, float maxStop, float value)
+{
+ return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Time routines
+//////////////////////////////////////////////////////////////////////////////
+
+static int32_t SC_second()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_sec;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_sec;
+ }
+}
+
+static int32_t SC_minute()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_min;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_min;
+ }
+}
+
+static int32_t SC_hour()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_hour;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_hour;
+ }
+}
+
+static int32_t SC_day()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_mday;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_mday;
+ }
+}
+
+static int32_t SC_month()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_mon;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_mon;
+ }
+}
+
+static int32_t SC_year()
+{
+ GET_TLS();
+
+ time_t rawtime;
+ time(&rawtime);
+
+ if (sc->mEnviroment.mTimeZone) {
+ struct tm timeinfo;
+ localtime_tz(&rawtime, &timeinfo, sc->mEnviroment.mTimeZone);
+ return timeinfo.tm_year;
+ } else {
+ struct tm *timeinfo;
+ timeinfo = localtime(&rawtime);
+ return timeinfo->tm_year;
+ }
+}
+
+static int32_t SC_uptimeMillis()
+{
+ return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+}
+
+static int32_t SC_startTimeMillis()
+{
+ GET_TLS();
+ return sc->mEnviroment.mStartTimeMillis;
+}
+
+static int32_t SC_elapsedTimeMillis()
+{
+ GET_TLS();
+ return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC))
+ - sc->mEnviroment.mStartTimeMillis;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Matrix routines
+//////////////////////////////////////////////////////////////////////////////
+
+
+static void SC_matrixLoadIdentity(rsc_Matrix *mat)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadIdentity();
+}
+
+static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->load(f);
+}
+
+static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->load(reinterpret_cast<const Matrix *>(newmat));
+}
+
+static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadRotate(rot, x, y, z);
+}
+
+static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadScale(x, y, z);
+}
+
+static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadTranslate(x, y, z);
+}
+
+static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->loadMultiply(reinterpret_cast<const Matrix *>(lhs),
+ reinterpret_cast<const Matrix *>(rhs));
+}
+
+static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->multiply(reinterpret_cast<const Matrix *>(rhs));
+}
+
+static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->rotate(rot, x, y, z);
+}
+
+static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->scale(x, y, z);
+}
+
+static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z)
+{
+ Matrix *m = reinterpret_cast<Matrix *>(mat);
+ m->translate(x, y, z);
+}
+
+
+static void SC_vec2Rand(float *vec, float maxLen)
+{
+ float angle = SC_randf(PI * 2);
+ float len = SC_randf(maxLen);
+ vec[0] = len * sinf(angle);
+ vec[1] = len * cosf(angle);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Context
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va)
+{
+ GET_TLS();
+ rsi_ProgramFragmentBindTexture(rsc,
+ static_cast<ProgramFragment *>(vpf),
+ slot,
+ static_cast<Allocation *>(va));
+
+}
+
+static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs)
+{
+ GET_TLS();
+ rsi_ProgramFragmentBindSampler(rsc,
+ static_cast<ProgramFragment *>(vpf),
+ slot,
+ static_cast<Sampler *>(vs));
+
+}
+
+static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs)
+{
+ GET_TLS();
+ rsi_ContextBindProgramFragmentStore(rsc, pfs);
+
+}
+
+static void SC_bindProgramFragment(RsProgramFragment pf)
+{
+ GET_TLS();
+ rsi_ContextBindProgramFragment(rsc, pf);
+
+}
+
+static void SC_bindProgramVertex(RsProgramVertex pv)
+{
+ GET_TLS();
+ rsi_ContextBindProgramVertex(rsc, pv);
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// VP
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
+{
+ GET_TLS();
+ rsc->getVertex()->setModelviewMatrix(m);
+}
+
+static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
+{
+ GET_TLS();
+ rsc->getVertex()->setTextureMatrix(m);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Drawing
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_drawTriangleMesh(RsTriangleMesh mesh)
+{
+ GET_TLS();
+ rsi_TriangleMeshRender(rsc, mesh);
+}
+
+static void SC_drawTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count)
+{
+ GET_TLS();
+ rsi_TriangleMeshRenderRange(rsc, mesh, start, count);
+}
+
+static void SC_drawLine(float x1, float y1, float z1,
+ float x2, float y2, float z2)
+{
+ GET_TLS();
+ rsc->setupCheck();
+
+ float vtx[] = { x1, y1, z1, x2, y2, z2 };
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, vtx);
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ glDrawArrays(GL_LINES, 0, 2);
+}
+
+static void SC_drawQuadTexCoords(float x1, float y1, float z1,
+ float u1, float v1,
+ float x2, float y2, float z2,
+ float u2, float v2,
+ float x3, float y3, float z3,
+ float u3, float v3,
+ float x4, float y4, float z4,
+ float u4, float v4)
+{
+ GET_TLS();
+
+ //LOGE("Quad");
+ //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1);
+ //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2);
+ //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3);
+ //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4);
+
+ float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4};
+ const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4};
+
+ rsc->setupCheck();
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, vtx);
+
+ glClientActiveTexture(GL_TEXTURE0);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glClientActiveTexture(GL_TEXTURE1);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glClientActiveTexture(GL_TEXTURE0);
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+}
+
+static void SC_drawQuad(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ float x3, float y3, float z3,
+ float x4, float y4, float z4)
+{
+ SC_drawQuadTexCoords(x1, y1, z1, 0, 1,
+ x2, y2, z2, 1, 1,
+ x3, y3, z3, 1, 0,
+ x4, y4, z4, 0, 0);
+}
+
+static void SC_drawRect(float x1, float y1,
+ float x2, float y2, float z)
+{
+ SC_drawQuad(x1, y2, z,
+ x2, y2, z,
+ x2, y1, z,
+ x1, y1, z);
+}
+
+static void SC_drawSimpleMesh(RsSimpleMesh vsm)
+{
+ GET_TLS();
+ SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
+ rsc->setupCheck();
+ sm->render();
+}
+
+static void SC_drawSimpleMeshRange(RsSimpleMesh vsm, uint32_t start, uint32_t len)
+{
+ GET_TLS();
+ SimpleMesh *sm = static_cast<SimpleMesh *>(vsm);
+ rsc->setupCheck();
+ sm->renderRange(start, len);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_color(float r, float g, float b, float a)
+{
+ glColor4f(r, g, b, a);
+}
+
+static void SC_ambient(float r, float g, float b, float a)
+{
+ GLfloat params[] = { r, g, b, a };
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, params);
+}
+
+static void SC_diffuse(float r, float g, float b, float a)
+{
+ GLfloat params[] = { r, g, b, a };
+ glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, params);
+}
+
+static void SC_specular(float r, float g, float b, float a)
+{
+ GLfloat params[] = { r, g, b, a };
+ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, params);
+}
+
+static void SC_emission(float r, float g, float b, float a)
+{
+ GLfloat params[] = { r, g, b, a };
+ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, params);
+}
+
+static void SC_shininess(float s)
+{
+ glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, s);
+}
+
+static void SC_pointAttenuation(float a, float b, float c)
+{
+ GLfloat params[] = { a, b, c };
+ glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, params);
+}
+
+static void SC_hsbToRgb(float h, float s, float b, float* rgb)
+{
+ float red = 0.0f;
+ float green = 0.0f;
+ float blue = 0.0f;
+
+ float x = h;
+ float y = s;
+ float z = b;
+
+ float hf = (x - (int) x) * 6.0f;
+ int ihf = (int) hf;
+ float f = hf - ihf;
+ float pv = z * (1.0f - y);
+ float qv = z * (1.0f - y * f);
+ float tv = z * (1.0f - y * (1.0f - f));
+
+ switch (ihf) {
+ case 0: // Red is the dominant color
+ red = z;
+ green = tv;
+ blue = pv;
+ break;
+ case 1: // Green is the dominant color
+ red = qv;
+ green = z;
+ blue = pv;
+ break;
+ case 2:
+ red = pv;
+ green = z;
+ blue = tv;
+ break;
+ case 3: // Blue is the dominant color
+ red = pv;
+ green = qv;
+ blue = z;
+ break;
+ case 4:
+ red = tv;
+ green = pv;
+ blue = z;
+ break;
+ case 5: // Red is the dominant color
+ red = z;
+ green = pv;
+ blue = qv;
+ break;
+ }
+
+ rgb[0] = red;
+ rgb[1] = green;
+ rgb[2] = blue;
+}
+
+static int SC_hsbToAbgr(float h, float s, float b, float a)
+{
+ float rgb[3];
+ SC_hsbToRgb(h, s, b, rgb);
+ return int(a * 255.0f) << 24 |
+ int(rgb[2] * 255.0f) << 16 |
+ int(rgb[1] * 255.0f) << 8 |
+ int(rgb[0] * 255.0f);
+}
+
+static void SC_hsb(float h, float s, float b, float a)
+{
+ float rgb[3];
+ SC_hsbToRgb(h, s, b, rgb);
+ glColor4f(rgb[0], rgb[1], rgb[2], a);
+}
+
+static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+{
+ GET_TLS();
+ rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
+}
+
+static void SC_uploadToBufferObject(RsAllocation va)
+{
+ GET_TLS();
+ rsi_AllocationUploadToBufferObject(rsc, va);
+}
+
+static void SC_ClearColor(float r, float g, float b, float a)
+{
+ //LOGE("c %f %f %f %f", r, g, b, a);
+ GET_TLS();
+ sc->mEnviroment.mClearColor[0] = r;
+ sc->mEnviroment.mClearColor[1] = g;
+ sc->mEnviroment.mClearColor[2] = b;
+ sc->mEnviroment.mClearColor[3] = a;
+}
+
+static void SC_debugF(const char *s, float f)
+{
+ LOGE("%s %f", s, f);
+}
+
+static void SC_debugHexF(const char *s, float f)
+{
+ LOGE("%s 0x%x", s, *((int *) (&f)));
+}
+
+static void SC_debugI32(const char *s, int32_t i)
+{
+ LOGE("%s %i", s, i);
+}
+
+static void SC_debugHexI32(const char *s, int32_t i)
+{
+ LOGE("%s 0x%x", s, i);
+}
+
+static uint32_t SC_getWidth()
+{
+ GET_TLS();
+ return rsc->getWidth();
+}
+
+static uint32_t SC_getHeight()
+{
+ GET_TLS();
+ return rsc->getHeight();
+}
+
+static uint32_t SC_colorFloatRGBAtoUNorm8(float r, float g, float b, float a)
+{
+ uint32_t c = 0;
+ c |= (uint32_t)(r * 255.f + 0.5f);
+ c |= ((uint32_t)(g * 255.f + 0.5f)) << 8;
+ c |= ((uint32_t)(b * 255.f + 0.5f)) << 16;
+ c |= ((uint32_t)(a * 255.f + 0.5f)) << 24;
+ return c;
+}
+
+static uint32_t SC_colorFloatRGBAto565(float r, float g, float b)
+{
+ uint32_t ir = (uint32_t)(r * 255.f + 0.5f);
+ uint32_t ig = (uint32_t)(g * 255.f + 0.5f);
+ uint32_t ib = (uint32_t)(b * 255.f + 0.5f);
+ return rs888to565(ir, ig, ib);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Class implementation
+//////////////////////////////////////////////////////////////////////////////
+
+ScriptCState::SymbolTable_t ScriptCState::gSyms[] = {
+ // IO
+ { "loadI32", (void *)&SC_loadI32,
+ "int", "(int, int)" },
+ //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" },
+ { "loadF", (void *)&SC_loadF,
+ "float", "(int, int)" },
+ { "loadArrayF", (void *)&SC_loadArrayF,
+ "float*", "(int, int)" },
+ { "loadArrayI32", (void *)&SC_loadArrayI32,
+ "int*", "(int, int)" },
+ { "loadVec4", (void *)&SC_loadVec4,
+ "void", "(int, int, float *)" },
+ { "loadMatrix", (void *)&SC_loadMatrix,
+ "void", "(int, int, float *)" },
+ { "storeI32", (void *)&SC_storeI32,
+ "void", "(int, int, int)" },
+ //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" },
+ { "storeF", (void *)&SC_storeF,
+ "void", "(int, int, float)" },
+ { "storeVec4", (void *)&SC_storeVec4,
+ "void", "(int, int, float *)" },
+ { "storeMatrix", (void *)&SC_storeMatrix,
+ "void", "(int, int, float *)" },
+ { "loadTriangleMeshVerticesF", (void *)&SC_loadTriangleMeshVerticesF,
+ "float*", "(int)" },
+ { "updateTriangleMesh", (void *)&SC_updateTriangleMesh,
+ "void", "(int)" },
+
+ // math
+ { "modf", (void *)&fmod,
+ "float", "(float, float)" },
+ { "abs", (void *)&abs,
+ "int", "(int)" },
+ { "absf", (void *)&fabs,
+ "float", "(float)" },
+ { "sinf_fast", (void *)&SC_sinf_fast,
+ "float", "(float)" },
+ { "cosf_fast", (void *)&SC_cosf_fast,
+ "float", "(float)" },
+ { "sinf", (void *)&sinf,
+ "float", "(float)" },
+ { "cosf", (void *)&cosf,
+ "float", "(float)" },
+ { "asinf", (void *)&asinf,
+ "float", "(float)" },
+ { "acosf", (void *)&acosf,
+ "float", "(float)" },
+ { "atanf", (void *)&atanf,
+ "float", "(float)" },
+ { "atan2f", (void *)&atan2f,
+ "float", "(float, float)" },
+ { "fabsf", (void *)&fabsf,
+ "float", "(float)" },
+ { "randf", (void *)&SC_randf,
+ "float", "(float)" },
+ { "randf2", (void *)&SC_randf2,
+ "float", "(float, float)" },
+ { "floorf", (void *)&floorf,
+ "float", "(float)" },
+ { "ceilf", (void *)&ceilf,
+ "float", "(float)" },
+ { "expf", (void *)&expf,
+ "float", "(float)" },
+ { "logf", (void *)&logf,
+ "float", "(float)" },
+ { "powf", (void *)&powf,
+ "float", "(float, float)" },
+ { "maxf", (void *)&SC_maxf,
+ "float", "(float, float)" },
+ { "minf", (void *)&SC_minf,
+ "float", "(float, float)" },
+ { "sqrt", (void *)&sqrt,
+ "int", "(int)" },
+ { "sqrtf", (void *)&sqrtf,
+ "float", "(float)" },
+ { "sqr", (void *)&SC_sqr,
+ "int", "(int)" },
+ { "sqrf", (void *)&SC_sqrf,
+ "float", "(float)" },
+ { "clamp", (void *)&SC_clamp,
+ "int", "(int, int, int)" },
+ { "clampf", (void *)&SC_clampf,
+ "float", "(float, float, float)" },
+ { "distf2", (void *)&SC_distf2,
+ "float", "(float, float, float, float)" },
+ { "distf3", (void *)&SC_distf3,
+ "float", "(float, float, float, float, float, float)" },
+ { "magf2", (void *)&SC_magf2,
+ "float", "(float, float)" },
+ { "magf3", (void *)&SC_magf3,
+ "float", "(float, float, float)" },
+ { "radf", (void *)&SC_radf,
+ "float", "(float)" },
+ { "degf", (void *)&SC_degf,
+ "float", "(float)" },
+ { "lerpf", (void *)&SC_lerpf,
+ "float", "(float, float, float)" },
+ { "normf", (void *)&SC_normf,
+ "float", "(float, float, float)" },
+ { "mapf", (void *)&SC_mapf,
+ "float", "(float, float, float, float, float)" },
+ { "noisef", (void *)&SC_noisef,
+ "float", "(float)" },
+ { "noisef2", (void *)&SC_noisef2,
+ "float", "(float, float)" },
+ { "noisef3", (void *)&SC_noisef3,
+ "float", "(float, float, float)" },
+ { "turbulencef2", (void *)&SC_turbulencef2,
+ "float", "(float, float, float)" },
+ { "turbulencef3", (void *)&SC_turbulencef3,
+ "float", "(float, float, float, float)" },
+
+ // time
+ { "second", (void *)&SC_second,
+ "int", "()" },
+ { "minute", (void *)&SC_minute,
+ "int", "()" },
+ { "hour", (void *)&SC_hour,
+ "int", "()" },
+ { "day", (void *)&SC_day,
+ "int", "()" },
+ { "month", (void *)&SC_month,
+ "int", "()" },
+ { "year", (void *)&SC_year,
+ "int", "()" },
+ { "uptimeMillis", (void*)&SC_uptimeMillis,
+ "int", "()" }, // TODO: use long instead
+ { "startTimeMillis", (void*)&SC_startTimeMillis,
+ "int", "()" }, // TODO: use long instead
+ { "elapsedTimeMillis", (void*)&SC_elapsedTimeMillis,
+ "int", "()" }, // TODO: use long instead
+
+ // matrix
+ { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity,
+ "void", "(float *mat)" },
+ { "matrixLoadFloat", (void *)&SC_matrixLoadFloat,
+ "void", "(float *mat, float *f)" },
+ { "matrixLoadMat", (void *)&SC_matrixLoadMat,
+ "void", "(float *mat, float *newmat)" },
+ { "matrixLoadRotate", (void *)&SC_matrixLoadRotate,
+ "void", "(float *mat, float rot, float x, float y, float z)" },
+ { "matrixLoadScale", (void *)&SC_matrixLoadScale,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply,
+ "void", "(float *mat, float *lhs, float *rhs)" },
+ { "matrixMultiply", (void *)&SC_matrixMultiply,
+ "void", "(float *mat, float *rhs)" },
+ { "matrixRotate", (void *)&SC_matrixRotate,
+ "void", "(float *mat, float rot, float x, float y, float z)" },
+ { "matrixScale", (void *)&SC_matrixScale,
+ "void", "(float *mat, float x, float y, float z)" },
+ { "matrixTranslate", (void *)&SC_matrixTranslate,
+ "void", "(float *mat, float x, float y, float z)" },
+
+ // vector
+ { "vec2Rand", (void *)&SC_vec2Rand,
+ "void", "(float *vec, float maxLen)" },
+
+ // vec3
+ { "vec3Norm", (void *)&SC_vec3Norm,
+ "void", "(struct vec3_s *)" },
+ { "vec3Length", (void *)&SC_vec3Length,
+ "float", "(struct vec3_s *)" },
+ { "vec3Add", (void *)&SC_vec3Add,
+ "void", "(struct vec3_s *dest, struct vec3_s *lhs, struct vec3_s *rhs)" },
+ { "vec3Sub", (void *)&SC_vec3Sub,
+ "void", "(struct vec3_s *dest, struct vec3_s *lhs, struct vec3_s *rhs)" },
+ { "vec3Cross", (void *)&SC_vec3Cross,
+ "void", "(struct vec3_s *dest, struct vec3_s *lhs, struct vec3_s *rhs)" },
+ { "vec3Dot", (void *)&SC_vec3Dot,
+ "float", "(struct vec3_s *lhs, struct vec3_s *rhs)" },
+ { "vec3Scale", (void *)&SC_vec3Scale,
+ "void", "(struct vec3_s *lhs, float scale)" },
+
+ // context
+ { "bindProgramFragment", (void *)&SC_bindProgramFragment,
+ "void", "(int)" },
+ { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore,
+ "void", "(int)" },
+ { "bindProgramVertex", (void *)&SC_bindProgramVertex,
+ "void", "(int)" },
+ { "bindSampler", (void *)&SC_bindSampler,
+ "void", "(int, int, int)" },
+ { "bindTexture", (void *)&SC_bindTexture,
+ "void", "(int, int, int)" },
+
+ // vp
+ { "vpLoadModelMatrix", (void *)&SC_vpLoadModelMatrix,
+ "void", "(void *)" },
+ { "vpLoadTextureMatrix", (void *)&SC_vpLoadTextureMatrix,
+ "void", "(void *)" },
+
+
+
+ // drawing
+ { "drawRect", (void *)&SC_drawRect,
+ "void", "(float x1, float y1, float x2, float y2, float z)" },
+ { "drawQuad", (void *)&SC_drawQuad,
+ "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" },
+ { "drawQuadTexCoords", (void *)&SC_drawQuadTexCoords,
+ "void", "(float x1, float y1, float z1, float u1, float v1, float x2, float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3, float x4, float y4, float z4, float u4, float v4)" },
+ { "drawTriangleMesh", (void *)&SC_drawTriangleMesh,
+ "void", "(int mesh)" },
+ { "drawTriangleMeshRange", (void *)&SC_drawTriangleMeshRange,
+ "void", "(int mesh, int start, int count)" },
+ { "drawLine", (void *)&SC_drawLine,
+ "void", "(float x1, float y1, float z1, float x2, float y2, float z2)" },
+ { "drawSimpleMesh", (void *)&SC_drawSimpleMesh,
+ "void", "(int ism)" },
+ { "drawSimpleMeshRange", (void *)&SC_drawSimpleMeshRange,
+ "void", "(int ism, int start, int len)" },
+
+
+ // misc
+ { "pfClearColor", (void *)&SC_ClearColor,
+ "void", "(float, float, float, float)" },
+ { "color", (void *)&SC_color,
+ "void", "(float, float, float, float)" },
+ { "hsb", (void *)&SC_hsb,
+ "void", "(float, float, float, float)" },
+ { "hsbToRgb", (void *)&SC_hsbToRgb,
+ "void", "(float, float, float, float*)" },
+ { "hsbToAbgr", (void *)&SC_hsbToAbgr,
+ "int", "(float, float, float, float)" },
+ { "ambient", (void *)&SC_ambient,
+ "void", "(float, float, float, float)" },
+ { "diffuse", (void *)&SC_diffuse,
+ "void", "(float, float, float, float)" },
+ { "specular", (void *)&SC_specular,
+ "void", "(float, float, float, float)" },
+ { "emission", (void *)&SC_emission,
+ "void", "(float, float, float, float)" },
+ { "shininess", (void *)&SC_shininess,
+ "void", "(float)" },
+ { "pointAttenuation", (void *)&SC_pointAttenuation,
+ "void", "(float, float, float)" },
+
+ { "uploadToTexture", (void *)&SC_uploadToTexture,
+ "void", "(int, int)" },
+ { "uploadToBufferObject", (void *)&SC_uploadToBufferObject,
+ "void", "(int)" },
+
+ { "colorFloatRGBAtoUNorm8", (void *)&SC_colorFloatRGBAtoUNorm8,
+ "int", "(float, float, float, float)" },
+ { "colorFloatRGBto565", (void *)&SC_colorFloatRGBAto565,
+ "int", "(float, float, float)" },
+
+
+ { "getWidth", (void *)&SC_getWidth,
+ "int", "()" },
+ { "getHeight", (void *)&SC_getHeight,
+ "int", "()" },
+
+
+
+ { "debugF", (void *)&SC_debugF,
+ "void", "(void *, float)" },
+ { "debugI32", (void *)&SC_debugI32,
+ "void", "(void *, int)" },
+ { "debugHexF", (void *)&SC_debugHexF,
+ "void", "(void *, float)" },
+ { "debugHexI32", (void *)&SC_debugHexI32,
+ "void", "(void *, int)" },
+
+
+ { NULL, NULL, NULL, NULL }
+};
+
+const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym)
+{
+ ScriptCState::SymbolTable_t *syms = gSyms;
+
+ while (syms->mPtr) {
+ if (!strcmp(syms->mName, sym)) {
+ return syms;
+ }
+ syms++;
+ }
+ return NULL;
+}
+
+void ScriptCState::appendDecls(String8 *str)
+{
+ ScriptCState::SymbolTable_t *syms = gSyms;
+ while (syms->mPtr) {
+ str->append(syms->mRet);
+ str->append(" ");
+ str->append(syms->mName);
+ str->append(syms->mParam);
+ str->append(";\n");
+ syms++;
+ }
+}
+
+
diff --git a/libs/rs/rsSimpleMesh.cpp b/libs/rs/rsSimpleMesh.cpp
new file mode 100644
index 0000000..7c73eb9
--- /dev/null
+++ b/libs/rs/rsSimpleMesh.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+SimpleMesh::SimpleMesh()
+{
+}
+
+SimpleMesh::~SimpleMesh()
+{
+}
+
+void SimpleMesh::render() const
+{
+ if (mPrimitiveType.get()) {
+ renderRange(0, mPrimitiveType->getDimX());
+ return;
+ }
+
+ if (mIndexType.get()) {
+ renderRange(0, mIndexType->getDimX());
+ return;
+ }
+
+ renderRange(0, mVertexTypes[0]->getDimX());
+}
+
+void SimpleMesh::renderRange(uint32_t start, uint32_t len) const
+{
+ if (len < 1) {
+ return;
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
+ glClientActiveTexture(GL_TEXTURE0 + ct);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+ glClientActiveTexture(GL_TEXTURE0);
+
+ for (uint32_t ct=0; ct < mVertexTypeCount; ct++) {
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[ct]->getBufferObjectID());
+ mVertexTypes[ct]->enableGLVertexBuffer();
+ }
+
+ if (mIndexType.get()) {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
+ glDrawElements(mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2));
+ } else {
+ glDrawArrays(mGLPrimitive, start, len);
+ }
+}
+
+
+
+SimpleMeshContext::SimpleMeshContext()
+{
+}
+
+SimpleMeshContext::~SimpleMeshContext()
+{
+}
+
+
+namespace android {
+namespace renderscript {
+
+
+RsSimpleMesh rsi_SimpleMeshCreate(Context *rsc, RsType prim, RsType idx, RsType *vtx, uint32_t vtxCount, uint32_t primType)
+{
+ SimpleMesh *sm = new SimpleMesh();
+ sm->incUserRef();
+
+ sm->mIndexType.set((const Type *)idx);
+ sm->mPrimitiveType.set((const Type *)prim);
+
+ sm->mVertexTypeCount = vtxCount;
+ sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount];
+ sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount];
+ for (uint32_t ct=0; ct < vtxCount; ct++) {
+ sm->mVertexTypes[ct].set((const Type *)vtx[ct]);
+ }
+
+ sm->mPrimitive = (RsPrimitive)primType;
+ switch(sm->mPrimitive) {
+ case RS_PRIMITIVE_POINT: sm->mGLPrimitive = GL_POINTS; break;
+ case RS_PRIMITIVE_LINE: sm->mGLPrimitive = GL_LINES; break;
+ case RS_PRIMITIVE_LINE_STRIP: sm->mGLPrimitive = GL_LINE_STRIP; break;
+ case RS_PRIMITIVE_TRIANGLE: sm->mGLPrimitive = GL_TRIANGLES; break;
+ case RS_PRIMITIVE_TRIANGLE_STRIP: sm->mGLPrimitive = GL_TRIANGLE_STRIP; break;
+ case RS_PRIMITIVE_TRIANGLE_FAN: sm->mGLPrimitive = GL_TRIANGLE_FAN; break;
+ }
+ return sm;
+}
+
+void rsi_SimpleMeshBindVertex(Context *rsc, RsSimpleMesh mv, RsAllocation va, uint32_t slot)
+{
+ SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
+ rsAssert(slot < sm->mVertexTypeCount);
+
+ sm->mVertexBuffers[slot].set((Allocation *)va);
+}
+
+void rsi_SimpleMeshBindIndex(Context *rsc, RsSimpleMesh mv, RsAllocation va)
+{
+ SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
+ sm->mIndexBuffer.set((Allocation *)va);
+}
+
+void rsi_SimpleMeshBindPrimitive(Context *rsc, RsSimpleMesh mv, RsAllocation va)
+{
+ SimpleMesh *sm = static_cast<SimpleMesh *>(mv);
+ sm->mPrimitiveBuffer.set((Allocation *)va);
+}
+
+
+
+
+}}
+
diff --git a/libs/rs/rsSimpleMesh.h b/libs/rs/rsSimpleMesh.h
new file mode 100644
index 0000000..03b6c2c
--- /dev/null
+++ b/libs/rs/rsSimpleMesh.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_SIMPLE_MESH_H
+#define ANDROID_RS_SIMPLE_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class SimpleMesh : public ObjectBase
+{
+public:
+ SimpleMesh();
+ ~SimpleMesh();
+
+ ObjectBaseRef<const Type> mIndexType;
+ ObjectBaseRef<const Type> mPrimitiveType;
+ ObjectBaseRef<const Type> *mVertexTypes;
+ uint32_t mVertexTypeCount;
+
+ ObjectBaseRef<Allocation> mIndexBuffer;
+ ObjectBaseRef<Allocation> mPrimitiveBuffer;
+ ObjectBaseRef<Allocation> *mVertexBuffers;
+
+ RsPrimitive mPrimitive;
+ uint32_t mGLPrimitive;
+
+
+ void render() const;
+ void renderRange(uint32_t start, uint32_t len) const;
+
+
+protected:
+};
+
+class SimpleMeshContext
+{
+public:
+ SimpleMeshContext();
+ ~SimpleMeshContext();
+
+
+};
+
+
+}
+}
+#endif //ANDROID_RS_SIMPLE_MESH_H
+
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
new file mode 100644
index 0000000..db4bb81
--- /dev/null
+++ b/libs/rs/rsThreadIO.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+#include "rsThreadIO.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+ThreadIO::ThreadIO()
+{
+ mToCore.init(16 * 1024);
+}
+
+ThreadIO::~ThreadIO()
+{
+}
+
+void ThreadIO::shutdown()
+{
+ mToCore.shutdown();
+}
+
+bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand)
+{
+ bool ret = false;
+ while(!mToCore.isEmpty() || waitForCommand) {
+ uint32_t cmdID = 0;
+ uint32_t cmdSize = 0;
+ ret = true;
+#if RS_LOG_TIMES
+ con->timerSet(Context::RS_TIMER_IDLE);
+#endif
+ const void * data = mToCore.get(&cmdID, &cmdSize);
+#if RS_LOG_TIMES
+ con->timerSet(Context::RS_TIMER_INTERNAL);
+#endif
+ waitForCommand = false;
+ //LOGV("playCoreCommands 3 %i %i", cmdID, cmdSize);
+
+ gPlaybackFuncs[cmdID](con, data);
+ mToCore.next();
+ }
+ return ret;
+}
+
+
diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h
new file mode 100644
index 0000000..1f6a0c2
--- /dev/null
+++ b/libs/rs/rsThreadIO.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_THREAD_IO_H
+#define ANDROID_RS_THREAD_IO_H
+
+#include "rsUtils.h"
+#include "rsLocklessFifo.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+class Context;
+
+class ThreadIO {
+public:
+ ThreadIO();
+ ~ThreadIO();
+
+ void shutdown();
+
+ // Plays back commands from the client.
+ // Returns true if any commands were processed.
+ bool playCoreCommands(Context *con, bool waitForCommand);
+
+
+ LocklessCommandFifo mToCore;
+ //LocklessCommandFifo mToClient;
+
+ intptr_t mToCoreRet;
+
+};
+
+
+}
+}
+#endif
+
diff --git a/libs/rs/rsTriangleMesh.cpp b/libs/rs/rsTriangleMesh.cpp
new file mode 100644
index 0000000..64bb71b
--- /dev/null
+++ b/libs/rs/rsTriangleMesh.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+
+using namespace android;
+using namespace android::renderscript;
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+TriangleMesh::TriangleMesh()
+{
+ mVertexElement = NULL;
+ mIndexElement = NULL;
+ mVertexData = NULL;
+ mIndexData = NULL;
+ mTriangleCount = 0;
+ mVertexDataSize = 0;
+ mIndexDataSize = 0;
+
+ mBufferObjects[0] = 0;
+ mBufferObjects[1] = 0;
+
+ mOffsetCoord = 0;
+ mOffsetTex = 0;
+ mOffsetNorm = 0;
+
+ mSizeCoord = 0;
+ mSizeTex = 0;
+ mSizeNorm = 0;
+
+}
+
+TriangleMesh::~TriangleMesh()
+{
+ free(mVertexData);
+ free(mIndexData);
+}
+
+
+
+TriangleMeshContext::TriangleMeshContext()
+{
+ clear();
+}
+
+TriangleMeshContext::~TriangleMeshContext()
+{
+}
+
+void TriangleMeshContext::clear()
+{
+ mVertexElement = NULL;
+ mVertexSizeBits = 0;
+ mIndexElement = NULL;
+ mIndexSizeBits = 0;
+ mTriangleCount = 0;
+ mVertexData.clear();
+ mIndexData.clear();
+}
+
+void TriangleMesh::analyzeElement()
+{
+ for (uint32_t ct=0; ct < mVertexElement->getComponentCount(); ct++) {
+ const Component *c = mVertexElement->getComponent(ct);
+
+ if (c->getKind() == Component::X) {
+ rsAssert(mSizeCoord == 0);
+ mSizeCoord = 1;
+ mOffsetCoord = ct;
+ }
+ if (c->getKind() == Component::Y) {
+ rsAssert(mSizeCoord == 1);
+ mSizeCoord = 2;
+ }
+ if (c->getKind() == Component::Z) {
+ rsAssert(mSizeCoord == 2);
+ mSizeCoord = 3;
+ }
+ if (c->getKind() == Component::W) {
+ rsAssert(mSizeCoord == 4);
+ mSizeCoord = 4;
+ }
+
+ if (c->getKind() == Component::NX) {
+ rsAssert(mSizeNorm == 0);
+ mSizeNorm = 1;
+ mOffsetNorm = ct;
+ }
+ if (c->getKind() == Component::NY) {
+ rsAssert(mSizeNorm == 1);
+ mSizeNorm = 2;
+ }
+ if (c->getKind() == Component::NZ) {
+ rsAssert(mSizeNorm == 2);
+ mSizeNorm = 3;
+ }
+
+ if (c->getKind() == Component::S) {
+ rsAssert(mSizeTex == 0);
+ mSizeTex = 1;
+ mOffsetTex = ct;
+ }
+ if (c->getKind() == Component::T) {
+ rsAssert(mSizeTex == 1);
+ mSizeTex = 2;
+ }
+ }
+ LOGV("TriangleMesh %i,%i %i,%i %i,%i", mSizeCoord, mOffsetCoord, mSizeNorm, mOffsetNorm, mSizeTex, mOffsetTex);
+
+}
+
+
+namespace android {
+namespace renderscript {
+
+void rsi_TriangleMeshBegin(Context *rsc, RsElement vertex, RsElement index)
+{
+ TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+ tmc->clear();
+ tmc->mVertexElement = static_cast<Element *>(vertex);
+ tmc->mVertexSizeBits = tmc->mVertexElement->getSizeBits();
+ tmc->mIndexElement = static_cast<Element *>(index);
+ tmc->mIndexSizeBits = tmc->mIndexElement->getSizeBits();
+
+ assert(!(tmc->mVertexSizeBits & 0x7));
+ assert(!(tmc->mIndexSizeBits & 0x7));
+}
+
+void rsi_TriangleMeshAddVertex(Context *rsc, const void *data)
+{
+ TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+ // todo: Make this efficient.
+ for (uint32_t ct = 0; (ct * 8) < tmc->mVertexSizeBits; ct++) {
+ tmc->mVertexData.add(static_cast<const uint8_t *>(data) [ct]);
+ }
+}
+
+void rsi_TriangleMeshAddTriangle(Context *rsc, uint32_t idx1, uint32_t idx2, uint32_t idx3)
+{
+ TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+ // todo: Make this efficient.
+ switch(tmc->mIndexSizeBits) {
+ case 16:
+ tmc->mIndexData.add(idx1);
+ tmc->mIndexData.add(idx2);
+ tmc->mIndexData.add(idx3);
+ break;
+ default:
+ assert(0);
+ }
+
+ tmc->mTriangleCount++;
+}
+
+RsTriangleMesh rsi_TriangleMeshCreate(Context *rsc)
+{
+ TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+
+ TriangleMesh * tm = new TriangleMesh();
+ if (!tm) {
+ LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY");
+ // error
+ return 0;
+ }
+
+ tm->mTriangleCount = tmc->mTriangleCount;
+ tm->mIndexDataSize = tmc->mIndexData.size() * tmc->mIndexSizeBits >> 3;
+ tm->mVertexDataSize = tmc->mVertexData.size();
+ tm->mIndexElement = tmc->mIndexElement;
+ tm->mVertexElement = tmc->mVertexElement;
+
+ tm->mIndexData = malloc(tm->mIndexDataSize);
+ tm->mVertexData = malloc(tm->mVertexDataSize);
+ if (!tm->mIndexData || !tm->mVertexData) {
+ LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY");
+ delete tm;
+ return 0;
+ }
+
+ memcpy(tm->mVertexData, tmc->mVertexData.array(), tm->mVertexDataSize);
+ memcpy(tm->mIndexData, tmc->mIndexData.array(), tm->mIndexDataSize);
+ tm->analyzeElement();
+
+ tm->incUserRef();
+ return tm;
+}
+
+void rsi_TriangleMeshDestroy(Context *rsc, RsTriangleMesh vtm)
+{
+ TriangleMeshContext *tmc = &rsc->mStateTriangleMesh;
+ TriangleMesh * tm = static_cast<TriangleMesh *>(vtm);
+
+ free(tm->mIndexData);
+ free(tm->mVertexData);
+ delete tm;
+}
+
+
+
+void rsi_TriangleMeshRenderRange(Context *rsc, RsTriangleMesh vtm, uint32_t first, uint32_t count)
+{
+ TriangleMesh * tm = static_cast<TriangleMesh *>(vtm);
+
+ rsc->setupCheck();
+
+ if (!tm->mBufferObjects[0]) {
+ glGenBuffers(2, &tm->mBufferObjects[0]);
+
+ glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]);
+ glBufferData(GL_ARRAY_BUFFER, tm->mVertexDataSize, tm->mVertexData, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, tm->mIndexDataSize, tm->mIndexData, GL_STATIC_DRAW);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+
+ if (first >= tm->mTriangleCount) {
+ return;
+ }
+ if (count >= (tm->mTriangleCount - first)) {
+ count = tm->mTriangleCount - first;
+ }
+ if (!count) {
+ return;
+ }
+
+ const float *f = (const float *)tm->mVertexData;
+
+ glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(tm->mSizeCoord,
+ GL_FLOAT,
+ tm->mVertexElement->getSizeBytes(),
+ (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetCoord));
+
+ if (tm->mSizeTex) {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(tm->mSizeTex,
+ GL_FLOAT,
+ tm->mVertexElement->getSizeBytes(),
+ (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetTex));
+ } else {
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ }
+
+ if (tm->mSizeNorm) {
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glNormalPointer(GL_FLOAT,
+ tm->mVertexElement->getSizeBytes(),
+ (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetNorm));
+ } else {
+ glDisableClientState(GL_NORMAL_ARRAY);
+ }
+
+ glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, (GLvoid *)(first * 3 * 2));
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+void rsi_TriangleMeshRender(Context *rsc, RsTriangleMesh vtm)
+{
+ rsi_TriangleMeshRenderRange(rsc, vtm, 0, 0xffffff);
+}
+
+}}
diff --git a/libs/rs/rsTriangleMesh.h b/libs/rs/rsTriangleMesh.h
new file mode 100644
index 0000000..e56c7c2
--- /dev/null
+++ b/libs/rs/rsTriangleMesh.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_TRIANGLE_MESH_H
+#define ANDROID_RS_TRIANGLE_MESH_H
+
+
+#include "RenderScript.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+// An element is a group of Components that occupies one cell in a structure.
+class TriangleMesh : public ObjectBase
+{
+public:
+ TriangleMesh();
+ ~TriangleMesh();
+
+ const Element * mVertexElement;
+ const Element * mIndexElement;
+
+ void * mVertexData;
+ void * mIndexData;
+
+ size_t mVertexDataSize;
+ size_t mIndexDataSize;
+ uint32_t mTriangleCount;
+
+ size_t mOffsetCoord;
+ size_t mOffsetTex;
+ size_t mOffsetNorm;
+
+ size_t mSizeCoord;
+ size_t mSizeTex;
+ size_t mSizeNorm;
+
+ // GL buffer info
+ uint32_t mBufferObjects[2];
+
+ void analyzeElement();
+protected:
+};
+
+class TriangleMeshContext
+{
+public:
+ TriangleMeshContext();
+ ~TriangleMeshContext();
+
+ const Element * mVertexElement;
+ const Element * mIndexElement;
+ size_t mVertexSizeBits;
+ size_t mIndexSizeBits;
+
+ Vector<uint8_t> mVertexData;
+ Vector<uint16_t> mIndexData;
+
+ uint32_t mTriangleCount;
+
+ void clear();
+};
+
+
+}
+}
+#endif //ANDROID_RS_TRIANGLE_MESH_H
+
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
new file mode 100644
index 0000000..1838fa6
--- /dev/null
+++ b/libs/rs/rsType.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "rsContext.h"
+#include <GLES/gl.h>
+
+using namespace android;
+using namespace android::renderscript;
+
+Type::Type()
+{
+ mLODs = 0;
+ mLODCount = 0;
+ memset(&mGL, 0, sizeof(mGL));
+ clear();
+}
+
+Type::~Type()
+{
+ if (mLODs) {
+ delete [] mLODs;
+ }
+}
+
+void Type::clear()
+{
+ if (mLODs) {
+ delete [] mLODs;
+ mLODs = NULL;
+ }
+ mDimX = 0;
+ mDimY = 0;
+ mDimZ = 0;
+ mDimLOD = 0;
+ mFaces = false;
+ mElement.clear();
+}
+
+TypeState::TypeState()
+{
+}
+
+TypeState::~TypeState()
+{
+}
+
+size_t Type::getOffsetForFace(uint32_t face) const
+{
+ rsAssert(mFaces);
+ return 0;
+}
+
+void Type::compute()
+{
+ uint32_t oldLODCount = mLODCount;
+ if (mDimLOD) {
+ uint32_t l2x = rsFindHighBit(mDimX) + 1;
+ uint32_t l2y = rsFindHighBit(mDimY) + 1;
+ uint32_t l2z = rsFindHighBit(mDimZ) + 1;
+
+ mLODCount = rsMax(l2x, l2y);
+ mLODCount = rsMax(mLODCount, l2z);
+ } else {
+ mLODCount = 1;
+ }
+ if (mLODCount != oldLODCount) {
+ delete [] mLODs;
+ mLODs = new LOD[mLODCount];
+ }
+
+ uint32_t tx = mDimX;
+ uint32_t ty = mDimY;
+ uint32_t tz = mDimZ;
+ size_t offset = 0;
+ for (uint32_t lod=0; lod < mLODCount; lod++) {
+ mLODs[lod].mX = tx;
+ mLODs[lod].mY = ty;
+ mLODs[lod].mZ = tz;
+ mLODs[lod].mOffset = offset;
+ offset += tx * rsMax(ty, 1u) * rsMax(tz, 1u) * mElement->getSizeBytes();
+ tx = (tx + 1) >> 1;
+ ty = (ty + 1) >> 1;
+ tz = (tz + 1) >> 1;
+ }
+
+ // At this point the offset is the size of a mipmap chain;
+ mMipChainSizeBytes = offset;
+
+ if (mFaces) {
+ offset *= 6;
+ }
+ mTotalSizeBytes = offset;
+
+ makeGLComponents();
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const
+{
+ uint32_t offset = mLODs[lod].mOffset;
+ offset += x * mElement->getSizeBytes();
+ return offset;
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const
+{
+ uint32_t offset = mLODs[lod].mOffset;
+ offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes();
+ return offset;
+}
+
+uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const
+{
+ uint32_t offset = mLODs[lod].mOffset;
+ offset += (x + y*mLODs[lod].mX + z*mLODs[lod].mX*mLODs[lod].mY) * mElement->getSizeBytes();
+ return offset;
+}
+
+
+void Type::makeGLComponents()
+{
+ uint32_t texNum = 0;
+ memset(&mGL, 0, sizeof(mGL));
+
+ for (uint32_t ct=0; ct < getElement()->getComponentCount(); ct++) {
+ const Component *c = getElement()->getComponent(ct);
+
+ switch(c->getKind()) {
+ case Component::X:
+ rsAssert(mGL.mVtx.size == 0);
+ mGL.mVtx.size = 1;
+ mGL.mVtx.offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mVtx.type = c->getGLType();
+ break;
+ case Component::Y:
+ rsAssert(mGL.mVtx.size == 1);
+ rsAssert(mGL.mVtx.type == c->getGLType());
+ mGL.mVtx.size = 2;
+ break;
+ case Component::Z:
+ rsAssert(mGL.mVtx.size == 2);
+ rsAssert(mGL.mVtx.type == c->getGLType());
+ mGL.mVtx.size = 3;
+ break;
+ case Component::W:
+ rsAssert(mGL.mVtx.size == 4);
+ rsAssert(mGL.mVtx.type == c->getGLType());
+ mGL.mVtx.size = 4;
+ break;
+
+ case Component::RED:
+ rsAssert(mGL.mColor.size == 0);
+ mGL.mColor.size = 1;
+ mGL.mColor.offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mColor.type = c->getGLType();
+ break;
+ case Component::GREEN:
+ rsAssert(mGL.mColor.size == 1);
+ rsAssert(mGL.mColor.type == c->getGLType());
+ mGL.mColor.size = 2;
+ break;
+ case Component::BLUE:
+ rsAssert(mGL.mColor.size == 2);
+ rsAssert(mGL.mColor.type == c->getGLType());
+ mGL.mColor.size = 3;
+ break;
+ case Component::ALPHA:
+ // Can be RGBA or A at this point
+ if (mGL.mColor.size > 0) {
+ rsAssert(mGL.mColor.size == 3);
+ rsAssert(mGL.mColor.type == c->getGLType());
+ mGL.mColor.size = 4;
+ } else {
+ mGL.mColor.size = 1;
+ mGL.mColor.offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mColor.type = c->getGLType();
+ }
+ break;
+
+ case Component::NX:
+ rsAssert(mGL.mNorm.size == 0);
+ mGL.mNorm.size = 1;
+ mGL.mNorm.offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mNorm.type = c->getGLType();
+ break;
+ case Component::NY:
+ rsAssert(mGL.mNorm.size == 1);
+ rsAssert(mGL.mNorm.type == c->getGLType());
+ mGL.mNorm.size = 2;
+ break;
+ case Component::NZ:
+ rsAssert(mGL.mNorm.size == 2);
+ rsAssert(mGL.mNorm.type == c->getGLType());
+ mGL.mNorm.size = 3;
+ break;
+
+ case Component::S:
+ if (mGL.mTex[texNum].size) {
+ texNum++;
+ }
+ mGL.mTex[texNum].size = 1;
+ mGL.mTex[texNum].offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mTex[texNum].type = c->getGLType();
+ break;
+ case Component::T:
+ rsAssert(mGL.mTex[texNum].size == 1);
+ rsAssert(mGL.mTex[texNum].type == c->getGLType());
+ mGL.mTex[texNum].size = 2;
+ break;
+ case Component::R:
+ rsAssert(mGL.mTex[texNum].size == 2);
+ rsAssert(mGL.mTex[texNum].type == c->getGLType());
+ mGL.mTex[texNum].size = 3;
+ break;
+ case Component::Q:
+ rsAssert(mGL.mTex[texNum].size == 3);
+ rsAssert(mGL.mTex[texNum].type == c->getGLType());
+ mGL.mTex[texNum].size = 4;
+ break;
+
+ case Component::POINT_SIZE:
+ rsAssert(!mGL.mPointSize.size);
+ mGL.mPointSize.size = 1;
+ mGL.mPointSize.offset = mElement->getComponentOffsetBytes(ct);
+ mGL.mPointSize.type = c->getGLType();
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void Type::enableGLVertexBuffer() const
+{
+ // Note: We are only going to enable buffers and never disable them
+ // here. The reasonis more than one Allocation may be used as a vertex
+ // source. So we cannot disable arrays that may have been in use by
+ // another allocation.
+
+ uint32_t stride = mElement->getSizeBytes();
+ if (mGL.mVtx.size) {
+ //LOGE("va vtx %i %x, %i, %p", mGL.mVtx.size, mGL.mVtx.type, stride, (void *)mGL.mVtx.offset);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(mGL.mVtx.size,
+ mGL.mVtx.type,
+ stride,
+ (void *)mGL.mVtx.offset);
+ }
+
+ if (mGL.mNorm.size) {
+ //LOGE("va norm %i %x, %i, %p", mGL.mNorm.size, mGL.mNorm.type, stride, (void *)mGL.mNorm.offset);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ rsAssert(mGL.mNorm.size == 3);
+ glNormalPointer(mGL.mNorm.type,
+ stride,
+ (void *)mGL.mNorm.offset);
+ }
+
+ if (mGL.mColor.size) {
+ glEnableClientState(GL_COLOR_ARRAY);
+ glColorPointer(mGL.mColor.size,
+ mGL.mColor.type,
+ stride,
+ (void *)mGL.mColor.offset);
+ }
+
+ for (uint32_t ct=0; ct < RS_MAX_TEXTURE; ct++) {
+ if (mGL.mTex[ct].size) {
+ //LOGE("va tex%i %i %x, %i, %p", ct, mGL.mTex[ct].size, mGL.mTex[ct].type, stride, (void *)mGL.mTex[ct].offset);
+ glClientActiveTexture(GL_TEXTURE0 + ct);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(mGL.mTex[ct].size,
+ mGL.mTex[ct].type,
+ stride,
+ (void *)mGL.mTex[ct].offset);
+ }
+ }
+ glClientActiveTexture(GL_TEXTURE0);
+
+ if (mGL.mPointSize.size) {
+ glEnableClientState(GL_POINT_SIZE_ARRAY_OES);
+ glPointSizePointerOES(mGL.mPointSize.type,
+ stride,
+ (void *)mGL.mPointSize.offset);
+ }
+
+}
+
+
+//////////////////////////////////////////////////
+//
+namespace android {
+namespace renderscript {
+
+void rsi_TypeBegin(Context *rsc, RsElement vse)
+{
+ TypeState * stc = &rsc->mStateType;
+
+ stc->mX = 0;
+ stc->mY = 0;
+ stc->mZ = 0;
+ stc->mLOD = false;
+ stc->mFaces = false;
+ stc->mElement.set(static_cast<const Element *>(vse));
+}
+
+void rsi_TypeAdd(Context *rsc, RsDimension dim, size_t value)
+{
+ TypeState * stc = &rsc->mStateType;
+
+ if (dim < 0) {
+ //error
+ return;
+ }
+
+
+ switch (dim) {
+ case RS_DIMENSION_X:
+ stc->mX = value;
+ return;
+ case RS_DIMENSION_Y:
+ stc->mY = value;
+ return;
+ case RS_DIMENSION_Z:
+ stc->mZ = value;
+ return;
+ case RS_DIMENSION_FACE:
+ stc->mFaces = (value != 0);
+ return;
+ case RS_DIMENSION_LOD:
+ stc->mLOD = (value != 0);
+ return;
+ default:
+ break;
+ }
+
+
+ int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0;
+ if ((dim < 0) || (dim > RS_DIMENSION_MAX)) {
+ LOGE("rsTypeAdd: Bad dimension");
+ //error
+ return;
+ }
+
+ // todo: implement array support
+
+}
+
+RsType rsi_TypeCreate(Context *rsc)
+{
+ TypeState * stc = &rsc->mStateType;
+
+ Type * st = new Type();
+ st->incUserRef();
+ st->setDimX(stc->mX);
+ st->setDimY(stc->mY);
+ st->setDimZ(stc->mZ);
+ st->setElement(stc->mElement.get());
+ st->setDimLOD(stc->mLOD);
+ st->setDimFaces(stc->mFaces);
+ st->compute();
+
+ return st;
+}
+
+
+}
+}
+
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
new file mode 100644
index 0000000..6c39a4c
--- /dev/null
+++ b/libs/rs/rsType.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_STRUCTURED_TYPE_H
+#define ANDROID_STRUCTURED_TYPE_H
+
+#include "rsElement.h"
+
+// ---------------------------------------------------------------------------
+namespace android {
+namespace renderscript {
+
+
+class Type : public ObjectBase
+{
+public:
+ Type();
+ virtual ~Type();
+
+ Type * createTex2D(const Element *, size_t w, size_t h, bool mip);
+
+
+ size_t getOffsetForFace(uint32_t face) const;
+
+ size_t getSizeBytes() const {return mTotalSizeBytes;}
+ size_t getElementSizeBytes() const {return mElement->getSizeBytes();}
+ const Element * getElement() const {return mElement.get();}
+
+ uint32_t getDimX() const {return mDimX;}
+ uint32_t getDimY() const {return mDimY;}
+ uint32_t getDimZ() const {return mDimZ;}
+ uint32_t getDimLOD() const {return mDimLOD;}
+ bool getDimFaces() const {return mFaces;}
+
+ uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;}
+ uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;}
+ uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;}
+ uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;}
+
+ uint32_t getLODOffset(uint32_t lod, uint32_t x) const;
+ uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const;
+ uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
+
+ uint32_t getLODCount() const {return mLODCount;}
+
+
+ void setElement(const Element *e) {mElement.set(e);}
+ void setDimX(uint32_t v) {mDimX = v;}
+ void setDimY(uint32_t v) {mDimY = v;}
+ void setDimZ(uint32_t v) {mDimZ = v;}
+ void setDimFaces(bool v) {mFaces = v;}
+ void setDimLOD(bool v) {mDimLOD = v;}
+
+ void clear();
+ void compute();
+
+ void enableGLVertexBuffer() const;
+
+
+protected:
+ struct LOD {
+ size_t mX;
+ size_t mY;
+ size_t mZ;
+ size_t mOffset;
+ };
+
+ void makeLODTable();
+
+ // Internal structure from most to least significant.
+ // * Array dimensions
+ // * Faces
+ // * Mipmaps
+ // * xyz
+
+ ObjectBaseRef<const Element> mElement;
+
+ // Size of the structure in the various dimensions. A missing Dimension is
+ // specified as a 0 and not a 1.
+ size_t mDimX;
+ size_t mDimY;
+ size_t mDimZ;
+ bool mDimLOD;
+ bool mFaces;
+
+ // A list of array dimensions. The count is the number of array dimensions and the
+ // sizes is a per array size.
+ //Vector<size_t> mDimArraysSizes;
+
+ // count of mipmap levels, 0 indicates no mipmapping
+
+ size_t mMipChainSizeBytes;
+ size_t mTotalSizeBytes;
+ LOD *mLODs;
+ uint32_t mLODCount;
+
+ struct VertexComponent_t {
+ uint32_t offset;
+ uint32_t type;
+ uint32_t size;
+ uint32_t stride;
+ };
+ struct GLState_t {
+ VertexComponent_t mVtx;
+ VertexComponent_t mNorm;
+ VertexComponent_t mColor;
+ VertexComponent_t mTex[RS_MAX_TEXTURE];
+ VertexComponent_t mPointSize;
+ };
+ GLState_t mGL;
+ void makeGLComponents();
+
+private:
+ Type(const Type &);
+};
+
+
+class TypeState {
+public:
+ TypeState();
+ ~TypeState();
+
+ size_t mX;
+ size_t mY;
+ size_t mZ;
+ uint32_t mLOD;
+ bool mFaces;
+ ObjectBaseRef<const Element> mElement;
+
+ ObjectBaseRef<const Type> mIndexType;
+ ObjectBaseRef<const Type> mPrimitiveType;
+ ObjectBaseRef<const Type> *mVertexTypes;
+
+
+};
+
+
+}
+}
+#endif //ANDROID_STRUCTURED_TYPE
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
new file mode 100644
index 0000000..ec928db
--- /dev/null
+++ b/libs/rs/rsUtils.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_RS_UTILS_H
+#define ANDROID_RS_UTILS_H
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "rs"
+#include <utils/Log.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <EGL/egl.h>
+#include <math.h>
+
+#include "RenderScript.h"
+
+namespace android {
+namespace renderscript {
+
+#if 1
+#define rsAssert(v) do {if(!(v)) LOGE("rsAssert failed: %s, in %s at %i", #v, __FILE__, __LINE__);} while(0)
+#else
+#define rsAssert(v) while(0)
+#endif
+
+#define RS_LOG_TIMES 0
+
+template<typename T>
+T rsMin(T in1, T in2)
+{
+ if (in1 > in2) {
+ return in2;
+ }
+ return in1;
+}
+
+template<typename T>
+T rsMax(T in1, T in2)
+{
+ if (in1 < in2) {
+ return in2;
+ }
+ return in1;
+}
+
+template<typename T>
+T rsFindHighBit(T val)
+{
+ uint32_t bit = 0;
+ while(val > 1) {
+ bit++;
+ val>>=1;
+ }
+ return bit;
+}
+
+template<typename T>
+bool rsIsPow2(T val)
+{
+ return (val & (val-1)) == 0;
+}
+
+template<typename T>
+T rsHigherPow2(T v)
+{
+ if (rsIsPow2(v)) {
+ return v;
+ }
+ return 1 << (rsFindHighBit(v) + 1);
+}
+
+template<typename T>
+T rsLowerPow2(T v)
+{
+ if (rsIsPow2(v)) {
+ return v;
+ }
+ return 1 << rsFindHighBit(v);
+}
+
+
+static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b)
+{
+ uint16_t t = 0;
+ t |= b >> 3;
+ t |= (g >> 2) << 5;
+ t |= (r >> 3) << 11;
+ return t;
+}
+
+static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4)
+{
+ uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f));
+ uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f);
+ uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11));
+ return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11);
+}
+
+static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4)
+{
+ uint32_t r = (i1 & 0xff) + (i2 & 0xff) + (i3 & 0xff) + (i4 & 0xff);
+ uint32_t g = ((i1 >> 8) & 0xff) + ((i2 >> 8) & 0xff) + ((i3 >> 8) & 0xff) + ((i4 >> 8) & 0xff);
+ uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff);
+ uint32_t a = ((i1 >> 24) & 0xff) + ((i2 >> 24) & 0xff) + ((i3 >> 24) & 0xff) + ((i4 >> 24) & 0xff);
+ return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24);
+}
+
+
+
+}
+}
+
+#endif //ANDROID_RS_OBJECT_BASE_H
+
+
diff --git a/libs/rs/rsgApi.cpp.rsg b/libs/rs/rsgApi.cpp.rsg
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/libs/rs/rsgApi.cpp.rsg
@@ -0,0 +1 @@
+2
diff --git a/libs/rs/rsgApiFuncDecl.h.rsg b/libs/rs/rsgApiFuncDecl.h.rsg
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/libs/rs/rsgApiFuncDecl.h.rsg
@@ -0,0 +1 @@
+1
diff --git a/libs/rs/rsgApiReplay.cpp.rsg b/libs/rs/rsgApiReplay.cpp.rsg
new file mode 100644
index 0000000..00750ed
--- /dev/null
+++ b/libs/rs/rsgApiReplay.cpp.rsg
@@ -0,0 +1 @@
+3
diff --git a/libs/rs/rsgApiStructs.h.rsg b/libs/rs/rsgApiStructs.h.rsg
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/libs/rs/rsgApiStructs.h.rsg
@@ -0,0 +1 @@
+0
diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c
new file mode 100644
index 0000000..74ba248
--- /dev/null
+++ b/libs/rs/rsg_generator.c
@@ -0,0 +1,306 @@
+
+#include "spec.h"
+#include <stdio.h>
+
+void printFileHeader(FILE *f)
+{
+ fprintf(f, "/*\n");
+ fprintf(f, " * Copyright (C) 2009 The Android Open Source Project\n");
+ fprintf(f, " *\n");
+ fprintf(f, " * Licensed under the Apache License, Version 2.0 (the \"License\");\n");
+ fprintf(f, " * you may not use this file except in compliance with the License.\n");
+ fprintf(f, " * You may obtain a copy of the License at\n");
+ fprintf(f, " *\n");
+ fprintf(f, " * http://www.apache.org/licenses/LICENSE-2.0\n");
+ fprintf(f, " *\n");
+ fprintf(f, " * Unless required by applicable law or agreed to in writing, software\n");
+ fprintf(f, " * distributed under the License is distributed on an \"AS IS\" BASIS,\n");
+ fprintf(f, " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n");
+ fprintf(f, " * See the License for the specific language governing permissions and\n");
+ fprintf(f, " * limitations under the License.\n");
+ fprintf(f, " */\n\n");
+}
+
+void printVarType(FILE *f, const VarType *vt)
+{
+ int ct;
+ if (vt->isConst) {
+ fprintf(f, "const ");
+ }
+
+ switch(vt->type) {
+ case 0:
+ fprintf(f, "void");
+ break;
+ case 1:
+ fprintf(f, "int%i_t", vt->bits);
+ break;
+ case 2:
+ fprintf(f, "uint%i_t", vt->bits);
+ break;
+ case 3:
+ if (vt->bits == 32)
+ fprintf(f, "float");
+ else
+ fprintf(f, "double");
+ break;
+ case 4:
+ fprintf(f, "%s", vt->typeName);
+ break;
+ }
+
+ if(vt->ptrLevel) {
+ fprintf(f, " ");
+ for(ct=0; ct < vt->ptrLevel; ct++) {
+ fprintf(f, "*");
+ }
+ }
+
+ if(vt->name[0]) {
+ fprintf(f, " %s", vt->name);
+ }
+}
+
+void printArgList(FILE *f, const ApiEntry * api, int assumePrevious)
+{
+ int ct;
+ for(ct=0; ct < api->paramCount; ct++) {
+ if (ct || assumePrevious) {
+ fprintf(f, ", ");
+ }
+ printVarType(f, &api->params[ct]);
+ }
+}
+
+void printStructures(FILE *f)
+{
+ int ct;
+ int ct2;
+
+ for(ct=0; ct < apiCount; ct++) {
+ fprintf(f, "typedef struct RS_CMD_%s_rec RS_CMD_%s;\n", apis[ct].name, apis[ct].name);
+ }
+ fprintf(f, "\n");
+
+ for(ct=0; ct < apiCount; ct++) {
+ const ApiEntry * api = &apis[ct];
+ fprintf(f, "#define RS_CMD_ID_%s %i\n", api->name, ct+1);
+ fprintf(f, "struct RS_CMD_%s_rec {\n", api->name);
+ //fprintf(f, " RsCommandHeader _hdr;\n");
+
+ for(ct2=0; ct2 < api->paramCount; ct2++) {
+ fprintf(f, " ");
+ printVarType(f, &api->params[ct2]);
+ fprintf(f, ";\n");
+ }
+ fprintf(f, "};\n\n");
+ }
+}
+
+void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext)
+{
+ printVarType(f, &api->ret);
+ fprintf(f, " %s%s (", prefix, api->name);
+ if (addContext) {
+ fprintf(f, "Context *");
+ } else {
+ fprintf(f, "RsContext rsc");
+ }
+ printArgList(f, api, 1);
+ fprintf(f, ")");
+}
+
+void printFuncDecls(FILE *f, const char *prefix, int addContext)
+{
+ int ct;
+ for(ct=0; ct < apiCount; ct++) {
+ printFuncDecl(f, &apis[ct], prefix, addContext);
+ fprintf(f, ";\n");
+ }
+ fprintf(f, "\n\n");
+}
+
+void printPlaybackFuncs(FILE *f, const char *prefix)
+{
+ int ct;
+ for(ct=0; ct < apiCount; ct++) {
+ fprintf(f, "void %s%s (Context *, const void *);\n", prefix, apis[ct].name);
+ }
+}
+
+void printApiCpp(FILE *f)
+{
+ int ct;
+ int ct2;
+
+ fprintf(f, "#include \"rsDevice.h\"\n");
+ fprintf(f, "#include \"rsContext.h\"\n");
+ fprintf(f, "#include \"rsThreadIO.h\"\n");
+ //fprintf(f, "#include \"rsgApiStructs.h\"\n");
+ fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
+ fprintf(f, "\n");
+ fprintf(f, "using namespace android;\n");
+ fprintf(f, "using namespace android::renderscript;\n");
+ fprintf(f, "#include \"rsHandcode.h\"\n");
+ fprintf(f, "\n");
+
+ for(ct=0; ct < apiCount; ct++) {
+ int needFlush = 0;
+ const ApiEntry * api = &apis[ct];
+
+ printFuncDecl(f, api, "rs", 0);
+ fprintf(f, "\n{\n");
+ if (api->handcodeApi) {
+ fprintf(f, " rsHCAPI_%s(rsc", api->name);
+ for(ct2=0; ct2 < api->paramCount; ct2++) {
+ const VarType *vt = &api->params[ct2];
+ fprintf(f, ", %s", vt->name);
+ }
+ fprintf(f, ");\n");
+ } else {
+ fprintf(f, " ThreadIO *io = &((Context *)rsc)->mIO;\n");
+ //fprintf(f, " LOGE(\"add command %s\\n\");\n", api->name);
+ fprintf(f, " RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(sizeof(RS_CMD_%s)));\n", api->name, api->name, api->name);
+ fprintf(f, " uint32_t size = sizeof(RS_CMD_%s);\n", api->name);
+
+ for(ct2=0; ct2 < api->paramCount; ct2++) {
+ const VarType *vt = &api->params[ct2];
+ needFlush += vt->ptrLevel;
+ fprintf(f, " cmd->%s = %s;\n", vt->name, vt->name);
+ }
+ if (api->ret.typeName[0]) {
+ needFlush = 1;
+ }
+
+ fprintf(f, " io->mToCore.commit");
+ if (needFlush) {
+ fprintf(f, "Sync");
+ }
+ fprintf(f, "(RS_CMD_ID_%s, size);\n", api->name);
+
+ if (api->ret.typeName[0]) {
+ fprintf(f, " return reinterpret_cast<");
+ printVarType(f, &api->ret);
+ fprintf(f, ">(io->mToCoreRet);\n");
+ }
+ }
+ fprintf(f, "};\n\n");
+ }
+}
+
+void printPlaybackCpp(FILE *f)
+{
+ int ct;
+ int ct2;
+
+ fprintf(f, "#include \"rsDevice.h\"\n");
+ fprintf(f, "#include \"rsContext.h\"\n");
+ fprintf(f, "#include \"rsThreadIO.h\"\n");
+ //fprintf(f, "#include \"rsgApiStructs.h\"\n");
+ fprintf(f, "#include \"rsgApiFuncDecl.h\"\n");
+ fprintf(f, "\n");
+ fprintf(f, "namespace android {\n");
+ fprintf(f, "namespace renderscript {\n");
+ fprintf(f, "#include \"rsHandcode.h\"\n");
+ fprintf(f, "\n");
+
+ for(ct=0; ct < apiCount; ct++) {
+ const ApiEntry * api = &apis[ct];
+
+ fprintf(f, "void rsp_%s(Context *con, const void *vp)\n", api->name);
+ fprintf(f, "{\n");
+ if (api->handcodePlay) {
+ fprintf(f, " rsHCPLAY_%s(con, vp);\n", api->name);
+ } else {
+ //fprintf(f, " LOGE(\"play command %s\\n\");\n", api->name);
+ fprintf(f, " const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name);
+ fprintf(f, " ");
+ if (api->ret.typeName[0]) {
+ fprintf(f, "con->mIO.mToCoreRet = (intptr_t)");
+ }
+ fprintf(f, "rsi_%s(con", api->name);
+ for(ct2=0; ct2 < api->paramCount; ct2++) {
+ const VarType *vt = &api->params[ct2];
+ fprintf(f, ",\n cmd->%s", vt->name);
+ }
+ fprintf(f, ");\n");
+ }
+ fprintf(f, "};\n\n");
+ }
+
+ fprintf(f, "RsPlaybackFunc gPlaybackFuncs[] = {\n");
+ fprintf(f, " NULL,\n");
+ for(ct=0; ct < apiCount; ct++) {
+ fprintf(f, " %s%s,\n", "rsp_", apis[ct].name);
+ }
+ fprintf(f, "};\n");
+
+ fprintf(f, "};\n");
+ fprintf(f, "};\n");
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 3) {
+ fprintf(stderr, "usage: %s commandFile outFile\n", argv[0]);
+ return 1;
+ }
+ const char* rsgFile = argv[1];
+ const char* outFile = argv[2];
+ FILE* input = fopen(rsgFile, "r");
+
+ char choice = fgetc(input);
+ fclose(input);
+
+ if (choice < '0' || choice > '3') {
+ fprintf(stderr, "Uknown command: \'%c\'\n", choice);
+ return -2;
+ }
+
+ yylex();
+ // printf("# of lines = %d\n", num_lines);
+
+ FILE *f = fopen(outFile, "w");
+
+ printFileHeader(f);
+ switch(choice) {
+ case '0': // rsgApiStructs.h
+ {
+ fprintf(f, "\n");
+ fprintf(f, "#include \"rsContext.h\"\n");
+ fprintf(f, "\n");
+ fprintf(f, "namespace android {\n");
+ fprintf(f, "namespace renderscript {\n");
+ printStructures(f);
+ printFuncDecls(f, "rsi_", 1);
+ printPlaybackFuncs(f, "rsp_");
+ fprintf(f, "\n\ntypedef void (*RsPlaybackFunc)(Context *, const void *);\n");
+ fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[];\n");
+
+ fprintf(f, "}\n");
+ fprintf(f, "}\n");
+ }
+ break;
+
+ case '1': // rsgApiFuncDecl.h
+ {
+ printFuncDecls(f, "rs", 0);
+ }
+ break;
+
+ case '2': // rsgApi.cpp
+ {
+ printApiCpp(f);
+ }
+ break;
+
+ case '3': // rsgApiReplay.cpp
+ {
+ printFileHeader(f);
+ printPlaybackCpp(f);
+ }
+ break;
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/libs/rs/spec.h b/libs/rs/spec.h
new file mode 100644
index 0000000..82650a7
--- /dev/null
+++ b/libs/rs/spec.h
@@ -0,0 +1,43 @@
+#ifndef SPEC_H
+#define SPEC_H
+
+#include <string.h>
+#include <stdlib.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+extern int num_lines;
+
+typedef struct {
+ int isConst;
+ int type;
+ int bits;
+ int ptrLevel;
+ char name[256];
+ char typeName[256];
+} VarType;
+
+extern VarType *currType;
+
+typedef struct {
+ char name[256];
+ int sync;
+ int handcodeApi;
+ int handcodePlay;
+ int paramCount;
+ VarType ret;
+ VarType params[16];
+} ApiEntry;
+
+extern ApiEntry apis[128];
+extern int apiCount;
+
+extern int typeNextState;
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // SPEC_H
diff --git a/libs/rs/spec.l b/libs/rs/spec.l
new file mode 100644
index 0000000..d81d47e
--- /dev/null
+++ b/libs/rs/spec.l
@@ -0,0 +1,165 @@
+%option stack
+
+%x comment
+%x api_entry
+%x api_entry2
+%x api_entry_param
+%x var_type
+
+DIGIT [0-9]
+ID [a-zA-Z_][a-zA-Z0-9_]*
+
+ #include "spec.h"
+
+ int num_lines = 0;
+
+ VarType *currType = 0;
+
+ ApiEntry apis[128];
+ int apiCount = 0;
+
+ int typeNextState;
+
+ extern "C" int yylex();
+
+%%
+
+"/*" BEGIN(comment);
+<comment>[^*\n]* /* eat anything that's not a '*' */
+<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+<comment>\n ++num_lines;
+<comment>"*"+"/" BEGIN(INITIAL);
+
+<*>" " //printf("found ' '\n");
+<*>"\n" ++num_lines; //printf("found lf \n");
+
+{ID} {
+ memset(&apis[apiCount], 0, sizeof(ApiEntry));
+ memcpy(apis[apiCount].name, yytext, yyleng);
+ BEGIN(api_entry);
+ }
+
+<api_entry>"{" {
+ BEGIN(api_entry2);
+ }
+
+<api_entry2>"sync" {
+ apis[apiCount].sync = 1;
+ }
+
+<api_entry2>"handcodeApi" {
+ apis[apiCount].handcodeApi = 1;
+ }
+
+<api_entry2>"handcodePlay" {
+ apis[apiCount].handcodePlay = 1;
+ }
+
+<api_entry2>"ret" {
+ currType = &apis[apiCount].ret;
+ typeNextState = api_entry2;
+ BEGIN(var_type);
+ }
+
+<api_entry2>"param" {
+ currType = &apis[apiCount].params[apis[apiCount].paramCount];
+ apis[apiCount].paramCount++;
+ typeNextState = api_entry_param;
+ BEGIN(var_type);
+ }
+
+<var_type>"const" {
+ currType->isConst = 1;
+ }
+
+<var_type>"i8" {
+ currType->type = 1;
+ currType->bits = 8;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"i16" {
+ currType->type = 1;
+ currType->bits = 16;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"i32" {
+ currType->type = 1;
+ currType->bits = 32;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"i64" {
+ currType->type = 1;
+ currType->bits = 64;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"u8" {
+ currType->type = 2;
+ currType->bits = 8;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"u16" {
+ currType->type = 2;
+ currType->bits = 16;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"u32" {
+ currType->type = 2;
+ currType->bits = 32;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"u64" {
+ currType->type = 2;
+ currType->bits = 64;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"f" {
+ currType->type = 3;
+ currType->bits = 32;
+ BEGIN(typeNextState);
+ }
+
+<var_type>"d" {
+ currType->type = 3;
+ currType->bits = 64;
+ BEGIN(typeNextState);
+ }
+
+<var_type>{ID} {
+ currType->type = 4;
+ currType->bits = 32;
+ memcpy(currType->typeName, yytext, yyleng);
+ BEGIN(typeNextState);
+ }
+
+<api_entry_param>"*" {
+ currType->ptrLevel ++;
+ }
+
+<api_entry_param>{ID} {
+ memcpy(currType->name, yytext, yyleng);
+ BEGIN(api_entry2);
+ }
+
+
+<api_entry2>"}" {
+ apiCount++;
+ BEGIN(INITIAL);
+ }
+
+
+%%
+
+
+int yywrap()
+{
+ return 1;
+}
+
diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk
index ec5aa3f..49da111 100644
--- a/libs/surfaceflinger/Android.mk
+++ b/libs/surfaceflinger/Android.mk
@@ -5,44 +5,52 @@ LOCAL_SRC_FILES:= \
clz.cpp.arm \
DisplayHardware/DisplayHardware.cpp \
DisplayHardware/DisplayHardwareBase.cpp \
- GPUHardware/GPUHardware.cpp \
BlurFilter.cpp.arm \
- CPUGauge.cpp \
+ Buffer.cpp \
+ BufferAllocator.cpp \
Layer.cpp \
LayerBase.cpp \
LayerBuffer.cpp \
LayerBlur.cpp \
- LayerBitmap.cpp \
LayerDim.cpp \
- LayerOrientationAnim.cpp \
- OrientationAnimation.cpp \
+ MessageQueue.cpp \
SurfaceFlinger.cpp \
Tokenizer.cpp \
- Transform.cpp \
- VRamHeap.cpp
+ Transform.cpp
+LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+ifeq ($(TARGET_BOARD_PLATFORM), msm7k)
+ LOCAL_CFLAGS += -DDIM_WITH_TEXTURE
+endif
+ifeq ($(TARGET_BOARD_PLATFORM), qsd8k)
+ LOCAL_CFLAGS += -DDIM_WITH_TEXTURE
+endif
# need "-lrt" on Linux simulator to pick up clock_gettime
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(HOST_OS),linux)
- LOCAL_LDLIBS += -lrt
+ LOCAL_LDLIBS += -lrt -lpthread
endif
endif
LOCAL_SHARED_LIBRARIES := \
- libhardware \
- libutils \
libcutils \
- libui \
- libcorecg \
- libsgl \
libpixelflinger \
+ libhardware \
+ libutils \
+ libskia \
libEGL \
- libGLESv1_CM
+ libGLESv1_CM \
+ libbinder \
+ libui
LOCAL_C_INCLUDES := \
$(call include-path-for, corecg graphics)
+LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc
+
LOCAL_MODULE:= libsurfaceflinger
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/surfaceflinger/Buffer.cpp b/libs/surfaceflinger/Buffer.cpp
new file mode 100644
index 0000000..4a7c55e
--- /dev/null
+++ b/libs/surfaceflinger/Buffer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/MemoryBase.h>
+#include <binder/IMemory.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Surface.h>
+#include <pixelflinger/pixelflinger.h>
+
+#include "Buffer.h"
+#include "BufferAllocator.h"
+#include "SurfaceFlinger.h"
+
+
+namespace android {
+
+// ===========================================================================
+// Buffer and implementation of android_native_buffer_t
+// ===========================================================================
+
+Buffer::Buffer()
+ : SurfaceBuffer(), mInitCheck(NO_ERROR), mVStride(0)
+{
+}
+
+Buffer::Buffer(uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t reqUsage, uint32_t flags)
+ : SurfaceBuffer(), mInitCheck(NO_INIT), mVStride(0)
+{
+ mInitCheck = initSize(w, h, format, reqUsage, flags);
+}
+
+Buffer::~Buffer()
+{
+ if (handle) {
+ BufferAllocator& allocator(BufferAllocator::get());
+ allocator.free(handle);
+ }
+}
+
+status_t Buffer::initCheck() const {
+ return mInitCheck;
+}
+
+android_native_buffer_t* Buffer::getNativeBuffer() const
+{
+ return static_cast<android_native_buffer_t*>(const_cast<Buffer*>(this));
+}
+
+status_t Buffer::reallocate(uint32_t w, uint32_t h, PixelFormat f,
+ uint32_t reqUsage, uint32_t flags)
+{
+ if (handle) {
+ BufferAllocator& allocator(BufferAllocator::get());
+ allocator.free(handle);
+ handle = 0;
+ }
+ return initSize(w, h, f, reqUsage, flags);
+}
+
+status_t Buffer::initSize(uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t reqUsage, uint32_t flags)
+{
+ status_t err = NO_ERROR;
+
+ BufferAllocator& allocator = BufferAllocator::get();
+
+ /*
+ * buffers used for software rendering, but h/w composition
+ * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE
+ *
+ * buffers used for h/w rendering and h/w composition
+ * are allocated with HW_RENDER | HW_TEXTURE
+ *
+ * buffers used with h/w rendering and either NPOT or no egl_image_ext
+ * are allocated with SW_READ_RARELY | HW_RENDER
+ *
+ */
+
+ if (flags & Buffer::SECURE) {
+ // secure buffer, don't store it into the GPU
+ usage = BufferAllocator::USAGE_SW_READ_OFTEN |
+ BufferAllocator::USAGE_SW_WRITE_OFTEN;
+ } else {
+ // it's allowed to modify the usage flags here, but generally
+ // the requested flags should be honored.
+ usage = reqUsage | BufferAllocator::USAGE_HW_TEXTURE;
+ }
+
+ err = allocator.alloc(w, h, format, usage, &handle, &stride);
+ if (err == NO_ERROR) {
+ this->width = w;
+ this->height = h;
+ this->format = format;
+ mVStride = 0;
+ }
+
+ return err;
+}
+
+status_t Buffer::lock(GGLSurface* sur, uint32_t usage)
+{
+ void* vaddr;
+ status_t res = SurfaceBuffer::lock(usage, &vaddr);
+ if (res == NO_ERROR && sur) {
+ sur->version = sizeof(GGLSurface);
+ sur->width = width;
+ sur->height = height;
+ sur->stride = stride;
+ sur->format = format;
+ sur->vstride = mVStride;
+ sur->data = static_cast<GGLubyte*>(vaddr);
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/surfaceflinger/Buffer.h b/libs/surfaceflinger/Buffer.h
new file mode 100644
index 0000000..79f4eeb
--- /dev/null
+++ b/libs/surfaceflinger/Buffer.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LAYER_BITMAP_H
+#define ANDROID_LAYER_BITMAP_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <hardware/gralloc.h>
+
+#include <utils/Atomic.h>
+
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+#include <ui/Surface.h>
+
+#include <pixelflinger/pixelflinger.h>
+
+#include <private/ui/SharedBufferStack.h>
+#include <private/ui/SurfaceBuffer.h>
+
+class copybit_image_t;
+struct android_native_buffer_t;
+
+namespace android {
+
+// ===========================================================================
+// Buffer
+// ===========================================================================
+
+class NativeBuffer;
+
+class Buffer : public SurfaceBuffer
+{
+public:
+ enum {
+ DONT_CLEAR = 0x00000001,
+ SECURE = 0x00000004
+ };
+
+ Buffer();
+
+ // creates w * h buffer
+ Buffer(uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t reqUsage, uint32_t flags = 0);
+
+ // return status
+ status_t initCheck() const;
+
+ uint32_t getWidth() const { return width; }
+ uint32_t getHeight() const { return height; }
+ uint32_t getStride() const { return stride; }
+ uint32_t getUsage() const { return usage; }
+ PixelFormat getPixelFormat() const { return format; }
+ Rect getBounds() const { return Rect(width, height); }
+
+ status_t lock(GGLSurface* surface, uint32_t usage);
+
+ android_native_buffer_t* getNativeBuffer() const;
+
+ status_t reallocate(uint32_t w, uint32_t h, PixelFormat f,
+ uint32_t reqUsage, uint32_t flags);
+
+private:
+ friend class LightRefBase<Buffer>;
+ Buffer(const Buffer& rhs);
+ virtual ~Buffer();
+ Buffer& operator = (const Buffer& rhs);
+ const Buffer& operator = (const Buffer& rhs) const;
+
+ status_t initSize(uint32_t w, uint32_t h, PixelFormat format,
+ uint32_t reqUsage, uint32_t flags);
+
+ ssize_t mInitCheck;
+ uint32_t mVStride;
+};
+
+}; // namespace android
+
+#endif // ANDROID_LAYER_BITMAP_H
diff --git a/libs/surfaceflinger/BufferAllocator.cpp b/libs/surfaceflinger/BufferAllocator.cpp
new file mode 100644
index 0000000..19867a5
--- /dev/null
+++ b/libs/surfaceflinger/BufferAllocator.cpp
@@ -0,0 +1,127 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
+
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+#include "BufferAllocator.h"
+
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( BufferAllocator )
+
+Mutex BufferAllocator::sLock;
+KeyedVector<buffer_handle_t, BufferAllocator::alloc_rec_t> BufferAllocator::sAllocList;
+
+BufferAllocator::BufferAllocator()
+ : mAllocDev(0)
+{
+ hw_module_t const* module;
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);
+ if (err == 0) {
+ gralloc_open(module, &mAllocDev);
+ }
+}
+
+BufferAllocator::~BufferAllocator()
+{
+ gralloc_close(mAllocDev);
+}
+
+void BufferAllocator::dump(String8& result) const
+{
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ size_t total = 0;
+ const size_t SIZE = 512;
+ char buffer[SIZE];
+ snprintf(buffer, SIZE, "Allocated buffers:\n");
+ result.append(buffer);
+ const size_t c = list.size();
+ for (size_t i=0 ; i<c ; i++) {
+ const alloc_rec_t& rec(list.valueAt(i));
+ snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n",
+ list.keyAt(i), rec.size/1024.0f,
+ rec.w, rec.h, rec.format, rec.usage);
+ result.append(buffer);
+ total += rec.size;
+ }
+ snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f);
+ result.append(buffer);
+}
+
+static inline uint32_t clamp(uint32_t c) {
+ return c>0 ? c : 1;
+}
+
+status_t BufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format,
+ int usage, buffer_handle_t* handle, int32_t* stride)
+{
+ Mutex::Autolock _l(mLock);
+
+ // make sure to not allocate a 0 x 0 buffer
+ w = clamp(w);
+ h = clamp(h);
+
+ // we have a h/w allocator and h/w buffer is requested
+ status_t err = mAllocDev->alloc(mAllocDev,
+ w, h, format, usage, handle, stride);
+
+ LOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)",
+ w, h, format, usage, err, strerror(-err));
+
+ if (err == NO_ERROR) {
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.w = w;
+ rec.h = h;
+ rec.format = format;
+ rec.usage = usage;
+ rec.vaddr = 0;
+ rec.size = h * stride[0] * bytesPerPixel(format);
+ list.add(*handle, rec);
+ }
+
+ return err;
+}
+
+status_t BufferAllocator::free(buffer_handle_t handle)
+{
+ Mutex::Autolock _l(mLock);
+
+ status_t err = mAllocDev->free(mAllocDev, handle);
+ LOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err));
+
+ if (err == NO_ERROR) {
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ list.removeItem(handle);
+ }
+
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/surfaceflinger/BufferAllocator.h b/libs/surfaceflinger/BufferAllocator.h
new file mode 100644
index 0000000..a279ded
--- /dev/null
+++ b/libs/surfaceflinger/BufferAllocator.h
@@ -0,0 +1,96 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_BUFFER_ALLOCATOR_H
+#define ANDROID_BUFFER_ALLOCATOR_H
+
+#include <stdint.h>
+
+#include <cutils/native_handle.h>
+
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/threads.h>
+#include <utils/Singleton.h>
+
+#include <ui/PixelFormat.h>
+
+#include <hardware/gralloc.h>
+
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+class String8;
+
+class BufferAllocator : public Singleton<BufferAllocator>
+{
+public:
+ enum {
+ USAGE_SW_READ_NEVER = GRALLOC_USAGE_SW_READ_NEVER,
+ USAGE_SW_READ_RARELY = GRALLOC_USAGE_SW_READ_RARELY,
+ USAGE_SW_READ_OFTEN = GRALLOC_USAGE_SW_READ_OFTEN,
+ USAGE_SW_READ_MASK = GRALLOC_USAGE_SW_READ_MASK,
+
+ USAGE_SW_WRITE_NEVER = GRALLOC_USAGE_SW_WRITE_NEVER,
+ USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY,
+ USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN,
+ USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK,
+
+ USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK,
+
+ USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE,
+ USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER,
+ USAGE_HW_2D = GRALLOC_USAGE_HW_2D,
+ USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK
+ };
+
+ static inline BufferAllocator& get() { return getInstance(); }
+
+
+ status_t alloc(uint32_t w, uint32_t h, PixelFormat format, int usage,
+ buffer_handle_t* handle, int32_t* stride);
+
+ status_t free(buffer_handle_t handle);
+
+ void dump(String8& res) const;
+
+private:
+ struct alloc_rec_t {
+ uint32_t w;
+ uint32_t h;
+ PixelFormat format;
+ uint32_t usage;
+ void* vaddr;
+ size_t size;
+ };
+
+ static Mutex sLock;
+ static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
+
+ friend class Singleton<BufferAllocator>;
+ BufferAllocator();
+ ~BufferAllocator();
+
+ mutable Mutex mLock;
+ alloc_device_t *mAllocDev;
+};
+
+// ---------------------------------------------------------------------------
+}; // namespace android
+
+#endif // ANDROID_BUFFER_ALLOCATOR_H
diff --git a/libs/surfaceflinger/CPUGauge.cpp b/libs/surfaceflinger/CPUGauge.cpp
deleted file mode 100644
index 74a9270..0000000
--- a/libs/surfaceflinger/CPUGauge.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "CPUGauge"
-
-#include <stdint.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <math.h>
-
-#include <utils/threads.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/DisplayInfo.h>
-#include <ui/ISurfaceComposer.h>
-#include <ui/ISurfaceFlingerClient.h>
-
-#include <pixelflinger/pixelflinger.h>
-
-#include "CPUGauge.h"
-
-namespace android {
-
-CPUGauge::CPUGauge( const sp<ISurfaceComposer>& composer,
- nsecs_t interval,
- int clock,
- int refclock)
- : Thread(false),
- mInterval(interval), mClock(clock), mRefClock(refclock),
- mReferenceTime(0),
- mReferenceWorkingTime(0), mCpuUsage(0),
- mRefIdleTime(0), mIdleTime(0)
-{
- mFd = fopen("/proc/stat", "r");
- setvbuf(mFd, NULL, _IONBF, 0);
-
- mSession = SurfaceComposerClient::clientForConnection(
- composer->createConnection()->asBinder());
-}
-
-CPUGauge::~CPUGauge()
-{
- fclose(mFd);
-}
-
-const sp<SurfaceComposerClient>& CPUGauge::session() const
-{
- return mSession;
-}
-
-void CPUGauge::onFirstRef()
-{
- run("CPU Gauge");
-}
-
-status_t CPUGauge::readyToRun()
-{
- LOGI("Starting CPU gauge...");
- return NO_ERROR;
-}
-
-bool CPUGauge::threadLoop()
-{
- DisplayInfo dinfo;
- session()->getDisplayInfo(0, &dinfo);
- sp<Surface> s(session()->createSurface(getpid(), 0, dinfo.w, 4, PIXEL_FORMAT_OPAQUE));
- session()->openTransaction();
- s->setLayer(INT_MAX);
- session()->closeTransaction();
-
- static const GGLfixed colors[4][4] = {
- { 0x00000, 0x10000, 0x00000, 0x10000 },
- { 0x10000, 0x10000, 0x00000, 0x10000 },
- { 0x10000, 0x00000, 0x00000, 0x10000 },
- { 0x00000, 0x00000, 0x00000, 0x10000 },
- };
-
- GGLContext* gl;
- gglInit(&gl);
- gl->activeTexture(gl, 0);
- gl->disable(gl, GGL_TEXTURE_2D);
- gl->disable(gl, GGL_BLEND);
-
- const int w = dinfo.w;
-
- while(!exitPending())
- {
- mLock.lock();
- const float cpuUsage = this->cpuUsage();
- const float totalCpuUsage = 1.0f - idle();
- mLock.unlock();
-
- Surface::SurfaceInfo info;
- s->lock(&info);
- GGLSurface fb;
- fb.version = sizeof(GGLSurface);
- fb.width = info.w;
- fb.height = info.h;
- fb.stride = info.w;
- fb.format = info.format;
- fb.data = (GGLubyte*)info.bits;
-
- gl->colorBuffer(gl, &fb);
- gl->color4xv(gl, colors[3]);
- gl->recti(gl, 0, 0, w, 4);
- gl->color4xv(gl, colors[2]); // red
- gl->recti(gl, 0, 0, int(totalCpuUsage*w), 2);
- gl->color4xv(gl, colors[0]); // green
- gl->recti(gl, 0, 2, int(cpuUsage*w), 4);
-
- s->unlockAndPost();
-
- usleep(ns2us(mInterval));
- }
-
- gglUninit(gl);
- return false;
-}
-
-void CPUGauge::sample()
-{
- if (mLock.tryLock() == NO_ERROR) {
- const nsecs_t now = systemTime(mRefClock);
- const nsecs_t referenceTime = now-mReferenceTime;
- if (referenceTime >= mInterval) {
- const float reftime = 1.0f / referenceTime;
- const nsecs_t nowWorkingTime = systemTime(mClock);
-
- char buf[256];
- fgets(buf, 256, mFd);
- rewind(mFd);
- char *str = buf+5;
- char const * const usermode = strsep(&str, " "); (void)usermode;
- char const * const usernice = strsep(&str, " "); (void)usernice;
- char const * const systemmode = strsep(&str, " ");(void)systemmode;
- char const * const idle = strsep(&str, " ");
- const nsecs_t nowIdleTime = atoi(idle) * 10000000LL;
- mIdleTime = float(nowIdleTime - mRefIdleTime) * reftime;
- mRefIdleTime = nowIdleTime;
-
- const nsecs_t workingTime = nowWorkingTime - mReferenceWorkingTime;
- const float newCpuUsage = float(workingTime) * reftime;
- if (mCpuUsage != newCpuUsage) {
- mCpuUsage = newCpuUsage;
- mReferenceWorkingTime = nowWorkingTime;
- mReferenceTime = now;
- }
- }
- mLock.unlock();
- }
-}
-
-
-}; // namespace android
diff --git a/libs/surfaceflinger/CPUGauge.h b/libs/surfaceflinger/CPUGauge.h
deleted file mode 100644
index 5bb53c0..0000000
--- a/libs/surfaceflinger/CPUGauge.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_CPUGAUGE_H
-#define ANDROID_CPUGAUGE_H
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Timers.h>
-
-#include <ui/SurfaceComposerClient.h>
-
-namespace android {
-
-class CPUGauge : public Thread
-{
-public:
- CPUGauge( const sp<ISurfaceComposer>& composer,
- nsecs_t interval=s2ns(1),
- int clock=SYSTEM_TIME_THREAD,
- int refclock=SYSTEM_TIME_MONOTONIC);
-
- ~CPUGauge();
-
- const sp<SurfaceComposerClient>& session() const;
-
- void sample();
-
- inline float cpuUsage() const { return mCpuUsage; }
- inline float idle() const { return mIdleTime; }
-
-private:
- virtual void onFirstRef();
- virtual status_t readyToRun();
- virtual bool threadLoop();
-
- Mutex mLock;
-
- sp<SurfaceComposerClient> mSession;
-
- const nsecs_t mInterval;
- const int mClock;
- const int mRefClock;
-
- nsecs_t mReferenceTime;
- nsecs_t mReferenceWorkingTime;
- float mCpuUsage;
- nsecs_t mRefIdleTime;
- float mIdleTime;
- FILE* mFd;
-};
-
-
-}; // namespace android
-
-#endif // ANDROID_CPUGAUGE_H
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index ab02fa0..d893f0a 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -23,63 +21,50 @@
#include <cutils/properties.h>
+#include <utils/RefBase.h>
#include <utils/Log.h>
-#include <ui/EGLDisplaySurface.h>
+#include <ui/PixelFormat.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
#include <GLES/gl.h>
+#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <pixelflinger/pixelflinger.h>
#include "DisplayHardware/DisplayHardware.h"
#include <hardware/copybit.h>
#include <hardware/overlay.h>
+#include <hardware/gralloc.h>
using namespace android;
-static __attribute__((noinline))
-const char *egl_strerror(EGLint err)
-{
- switch (err){
- case EGL_SUCCESS: return "EGL_SUCCESS";
- case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
- case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
- case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
- case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
- case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
- case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
- case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
- case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
- case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
- case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
- case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
- case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
- case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
- case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
- default: return "UNKNOWN";
- }
-}
static __attribute__((noinline))
void checkGLErrors()
{
- GLenum error = glGetError();
- if (error != GL_NO_ERROR)
+ do {
+ // there could be more than one error flag
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR)
+ break;
LOGE("GL error 0x%04x", int(error));
+ } while(true);
}
static __attribute__((noinline))
void checkEGLErrors(const char* token)
{
EGLint error = eglGetError();
- // GLESonGL seems to be returning 0 when there is no errors?
- if (error && error != EGL_SUCCESS)
- LOGE("%s error 0x%04x (%s)",
- token, int(error), egl_strerror(error));
+ if (error && error != EGL_SUCCESS) {
+ LOGE("%s: EGL error 0x%04x (%s)",
+ token, int(error), EGLUtils::strerror(error));
+ }
}
-
/*
* Initialize the display to the specified values.
*
@@ -108,17 +93,34 @@ PixelFormat DisplayHardware::getFormat() const { return mFormat; }
void DisplayHardware::init(uint32_t dpy)
{
+ mNativeWindow = new FramebufferNativeWindow();
+ framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
+
+ mOverlayEngine = NULL;
+ hw_module_t const* module;
+ if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
+ overlay_control_open(module, &mOverlayEngine);
+ }
+
// initialize EGL
- const EGLint attribs[] = {
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_DEPTH_SIZE, 0,
+ EGLint attribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE, 0,
EGL_NONE
};
+
+ // debug: disable h/w rendering
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get("debug.sf.hw", property, NULL) > 0) {
+ if (atoi(property) == 0) {
+ LOGW("H/W composition disabled");
+ attribs[2] = EGL_CONFIG_CAVEAT;
+ attribs[3] = EGL_SLOW_CONFIG;
+ }
+ }
+
EGLint w, h, dummy;
- EGLint numConfigs, n;
- EGLConfig config;
+ EGLint numConfigs=0;
EGLSurface surface;
EGLContext context;
mFlags = 0;
@@ -129,7 +131,17 @@ void DisplayHardware::init(uint32_t dpy)
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, NULL, NULL);
eglGetConfigs(display, NULL, 0, &numConfigs);
- eglChooseConfig(display, attribs, &config, 1, &n);
+
+ EGLConfig config;
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ display, attribs, mNativeWindow.get(), &config);
+ LOGE_IF(err, "couldn't find an EGLConfig matching the screen format");
+
+ EGLint r,g,b,a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
/*
* Gather EGL extensions
@@ -144,13 +156,13 @@ void DisplayHardware::init(uint32_t dpy)
LOGI("version : %s", eglQueryString(display, EGL_VERSION));
LOGI("extensions: %s", egl_extensions);
LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
-
- // TODO: get this from the devfb driver (probably should be HAL module)
- mFlags |= SWAP_RECTANGLE_EXTENSION;
+ LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
- // TODO: get the real "update_on_demand" behavior (probably should be HAL module)
- mFlags |= UPDATE_ON_DEMAND;
+ if (mNativeWindow->isUpdateOnDemand()) {
+ mFlags |= UPDATE_ON_DEMAND;
+ }
+
if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) {
if (dummy == EGL_SLOW_CONFIG)
mFlags |= SLOW_CONFIG;
@@ -160,37 +172,41 @@ void DisplayHardware::init(uint32_t dpy)
* Create our main surface
*/
- mDisplaySurface = new EGLDisplaySurface();
-
- surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL);
- //checkEGLErrors("eglCreateDisplaySurfaceANDROID");
+ surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
if (dummy == EGL_BUFFER_PRESERVED) {
mFlags |= BUFFER_PRESERVED;
}
}
-
- GLint value = EGL_UNKNOWN;
- eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value);
- if (value == EGL_UNKNOWN) {
- mDpiX = 160.0f;
- } else {
- mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING;
- }
- value = EGL_UNKNOWN;
- eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value);
- if (value == EGL_UNKNOWN) {
- mDpiY = 160.0f;
- } else {
- mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING;
+
+ eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
+ eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
+
+#ifdef EGL_ANDROID_swap_rectangle
+ if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) {
+ if (eglSetSwapRectangleANDROID(display, surface,
+ 0, 0, mWidth, mHeight) == EGL_TRUE) {
+ // This could fail if this extension is not supported by this
+ // specific surface (of config)
+ mFlags |= SWAP_RECTANGLE;
+ }
}
- mRefreshRate = 60.f; // TODO: get the real refresh rate
+ // when we have the choice between UPDATE_ON_DEMAND and SWAP_RECTANGLE
+ // choose UPDATE_ON_DEMAND, which is more efficient
+ if (mFlags & UPDATE_ON_DEMAND)
+ mFlags &= ~SWAP_RECTANGLE;
+#endif
+
+ LOGI("flags : %08x", mFlags);
+
+ mDpiX = mNativeWindow->xdpi;
+ mDpiY = mNativeWindow->ydpi;
+ mRefreshRate = fbDev->fps;
- char property[PROPERTY_VALUE_MAX];
/* Read density from build-specific ro.sf.lcd_density property
- * except if it is overriden by qemu.sf.lcd_density.
+ * except if it is overridden by qemu.sf.lcd_density.
*/
if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
@@ -209,11 +225,6 @@ void DisplayHardware::init(uint32_t dpy)
*/
context = eglCreateContext(display, config, NULL, NULL);
- //checkEGLErrors("eglCreateContext");
-
- eglQuerySurface(display, surface, EGL_WIDTH, &mWidth);
- eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);
-
/*
* Gather OpenGL ES extensions
@@ -233,7 +244,10 @@ void DisplayHardware::init(uint32_t dpy)
if (strstr(gl_extensions, "GL_OES_draw_texture")) {
mFlags |= DRAW_TEXTURE_EXTENSION;
}
- if (strstr(gl_extensions, "GL_ANDROID_direct_texture")) {
+ if (strstr( gl_extensions, "GL_OES_EGL_image") &&
+ (strstr(egl_extensions, "EGL_KHR_image_base") ||
+ strstr(egl_extensions, "EGL_KHR_image")) &&
+ strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) {
mFlags |= DIRECT_TEXTURE;
}
@@ -244,19 +258,8 @@ void DisplayHardware::init(uint32_t dpy)
mConfig = config;
mSurface = surface;
mContext = context;
- mFormat = GGL_PIXEL_FORMAT_RGB_565;
-
- hw_module_t const* module;
-
- mBlitEngine = NULL;
- if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
- copybit_open(module, &mBlitEngine);
- }
-
- mOverlayEngine = NULL;
- if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
- overlay_control_open(module, &mOverlayEngine);
- }
+ mFormat = fbDev->format;
+ mPageFlipCount = 0;
}
/*
@@ -270,7 +273,6 @@ void DisplayHardware::fini()
{
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglTerminate(mDisplay);
- copybit_close(mBlitEngine);
overlay_control_close(mOverlayEngine);
}
@@ -284,28 +286,8 @@ void DisplayHardware::acquireScreen() const
DisplayHardwareBase::acquireScreen();
}
-void DisplayHardware::getDisplaySurface(copybit_image_t* img) const
-{
- img->w = mDisplaySurface->stride;
- img->h = mDisplaySurface->height;
- img->format = mDisplaySurface->format;
- img->offset = mDisplaySurface->offset;
- img->base = (void*)mDisplaySurface->base;
- img->fd = mDisplaySurface->fd;
-}
-
-void DisplayHardware::getDisplaySurface(GGLSurface* fb) const
-{
- fb->version= sizeof(GGLSurface);
- fb->width = mDisplaySurface->width;
- fb->height = mDisplaySurface->height;
- fb->stride = mDisplaySurface->stride;
- fb->format = mDisplaySurface->format;
- fb->data = (GGLubyte*)mDisplaySurface->base + mDisplaySurface->offset;
-}
-
uint32_t DisplayHardware::getPageFlipCount() const {
- return mDisplaySurface->getPageFlipCount();
+ return mPageFlipCount;
}
/*
@@ -319,21 +301,20 @@ void DisplayHardware::flip(const Region& dirty) const
EGLDisplay dpy = mDisplay;
EGLSurface surface = mSurface;
- Region newDirty(dirty);
- newDirty.andSelf(Rect(mWidth, mHeight));
-
- if (mFlags & BUFFER_PRESERVED) {
- const Region copyback(mDirty.subtract(newDirty));
- mDirty = newDirty;
- mDisplaySurface->copyFrontToBack(copyback);
- }
-
- if (mFlags & SWAP_RECTANGLE_EXTENSION) {
- const Rect& b(newDirty.bounds());
- mDisplaySurface->setSwapRectangle(
+#ifdef EGL_ANDROID_swap_rectangle
+ if (mFlags & SWAP_RECTANGLE) {
+ const Region newDirty(dirty.intersect(bounds()));
+ const Rect b(newDirty.getBounds());
+ eglSetSwapRectangleANDROID(dpy, surface,
b.left, b.top, b.width(), b.height());
+ }
+#endif
+
+ if (mFlags & UPDATE_ON_DEMAND) {
+ mNativeWindow->setUpdateRectangle(dirty.getBounds());
}
-
+
+ mPageFlipCount++;
eglSwapBuffers(dpy, surface);
checkEGLErrors("eglSwapBuffers");
@@ -351,11 +332,3 @@ void DisplayHardware::makeCurrent() const
{
eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
}
-
-void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const {
- mDisplaySurface->copyFrontToImage(front);
-}
-
-void DisplayHardware::copyBackToImage(const copybit_image_t& front) const {
- mDisplaySurface->copyBackToImage(front);
-}
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
index 550a4d1..8972d51 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -22,31 +22,35 @@
#include <ui/PixelFormat.h>
#include <ui/Region.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <pixelflinger/pixelflinger.h>
#include "DisplayHardware/DisplayHardwareBase.h"
struct overlay_control_device_t;
-struct copybit_device_t;
+struct framebuffer_device_t;
struct copybit_image_t;
-struct copybit_t;
namespace android {
-class EGLDisplaySurface;
+class FramebufferNativeWindow;
class DisplayHardware : public DisplayHardwareBase
{
public:
enum {
DIRECT_TEXTURE = 0x00000002,
- SWAP_RECTANGLE_EXTENSION= 0x00000004,
COPY_BITS_EXTENSION = 0x00000008,
NPOT_EXTENSION = 0x00000100,
DRAW_TEXTURE_EXTENSION = 0x00000200,
BUFFER_PRESERVED = 0x00010000,
UPDATE_ON_DEMAND = 0x00020000, // video driver feature
SLOW_CONFIG = 0x00040000, // software
+ SWAP_RECTANGLE = 0x00080000,
};
DisplayHardware(
@@ -73,15 +77,9 @@ public:
void makeCurrent() const;
uint32_t getPageFlipCount() const;
- void getDisplaySurface(copybit_image_t* img) const;
- void getDisplaySurface(GGLSurface* fb) const;
EGLDisplay getEGLDisplay() const { return mDisplay; }
- copybit_device_t* getBlitEngine() const { return mBlitEngine; }
overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
- void copyFrontToImage(const copybit_image_t& front) const;
- void copyBackToImage(const copybit_image_t& front) const;
-
Rect bounds() const {
return Rect(mWidth, mHeight);
}
@@ -102,9 +100,9 @@ private:
int mHeight;
PixelFormat mFormat;
uint32_t mFlags;
- mutable Region mDirty;
- sp<EGLDisplaySurface> mDisplaySurface;
- copybit_device_t* mBlitEngine;
+ mutable uint32_t mPageFlipCount;
+
+ sp<FramebufferNativeWindow> mNativeWindow;
overlay_control_device_t* mOverlayEngine;
};
diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
index f75e5c2..1d09f84 100644
--- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
+++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/GPUHardware/GPUHardware.cpp
deleted file mode 100644
index 7168bf2..0000000
--- a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp
+++ /dev/null
@@ -1,585 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <math.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <utils/IBinder.h>
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/IPCThreadState.h>
-#include <utils/StopWatch.h>
-
-#include <ui/ISurfaceComposer.h>
-
-#include "VRamHeap.h"
-#include "GPUHardware.h"
-
-#if HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-#include "GPUHardware/GPUHardware.h"
-
-
-/*
- * Manage the GPU. This implementation is very specific to the G1.
- * There are no abstraction here.
- *
- * All this code will soon go-away and be replaced by a new architecture
- * for managing graphics accelerators.
- *
- * In the meantime, it is conceptually possible to instantiate a
- * GPUHardwareInterface for another GPU (see GPUFactory at the bottom
- * of this file); practically... doubtful.
- *
- */
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class GPUClientHeap;
-class GPUAreaHeap;
-
-class GPUHardware : public GPUHardwareInterface, public IBinder::DeathRecipient
-{
-public:
- static const int GPU_RESERVED_SIZE;
- static const int GPUR_SIZE;
-
- GPUHardware();
- virtual ~GPUHardware();
-
- virtual void revoke(int pid);
- virtual sp<MemoryDealer> request(int pid);
- virtual status_t request(int pid,
- const sp<IGPUCallback>& callback,
- ISurfaceComposer::gpu_info_t* gpu);
-
- virtual status_t friendlyRevoke();
- virtual void unconditionalRevoke();
-
- virtual pid_t getOwner() const { return mOwner; }
-
- // used for debugging only...
- virtual sp<SimpleBestFitAllocator> getAllocator() const;
-
-private:
-
-
- enum {
- NO_OWNER = -1,
- };
-
- struct GPUArea {
- sp<GPUAreaHeap> heap;
- sp<MemoryHeapPmem> clientHeap;
- sp<IMemory> map();
- };
-
- struct Client {
- pid_t pid;
- GPUArea smi;
- GPUArea ebi;
- GPUArea reg;
- void createClientHeaps();
- void revokeAllHeaps();
- };
-
- Client& getClientLocked(pid_t pid);
- status_t requestLocked(int pid);
- void releaseLocked();
- void takeBackGPULocked();
- void registerCallbackLocked(const sp<IGPUCallback>& callback,
- Client& client);
-
- virtual void binderDied(const wp<IBinder>& who);
-
- mutable Mutex mLock;
- sp<GPUAreaHeap> mSMIHeap;
- sp<GPUAreaHeap> mEBIHeap;
- sp<GPUAreaHeap> mREGHeap;
-
- KeyedVector<pid_t, Client> mClients;
- DefaultKeyedVector< wp<IBinder>, pid_t > mRegisteredClients;
-
- pid_t mOwner;
-
- sp<MemoryDealer> mCurrentAllocator;
- sp<IGPUCallback> mCallback;
-
- sp<SimpleBestFitAllocator> mAllocator;
-
- Condition mCondition;
-};
-
-// size reserved for GPU surfaces
-// 1200 KB fits exactly:
-// - two 320*480 16-bits double-buffered surfaces
-// - one 320*480 32-bits double-buffered surface
-// - one 320*240 16-bits double-buffered, 4x anti-aliased surface
-const int GPUHardware::GPU_RESERVED_SIZE = 1200 * 1024;
-const int GPUHardware::GPUR_SIZE = 1 * 1024 * 1024;
-
-// ---------------------------------------------------------------------------
-
-/*
- * GPUHandle is a special IMemory given to the client. It represents their
- * handle to the GPU. Once they give it up, they loose GPU access, or if
- * they explicitly revoke their access through the binder code 1000.
- * In both cases, this triggers a callback to revoke()
- * first, and then actually powers down the chip.
- *
- * In the case of a misbehaving app, GPUHardware can ask for an immediate
- * release of the GPU to the target process which should answer by calling
- * code 1000 on GPUHandle. If it doesn't in a timely manner, the GPU will
- * be revoked from under their feet.
- *
- * We should never hold a strong reference on GPUHandle. In practice this
- * shouldn't be a big issue though because clients should use code 1000 and
- * not rely on the dtor being called.
- *
- */
-
-class GPUClientHeap : public MemoryHeapPmem
-{
-public:
- GPUClientHeap(const wp<GPUHardware>& gpu,
- const sp<MemoryHeapBase>& heap)
- : MemoryHeapPmem(heap), mGPU(gpu) { }
-protected:
- wp<GPUHardware> mGPU;
-};
-
-class GPUAreaHeap : public MemoryHeapBase
-{
-public:
- GPUAreaHeap(const wp<GPUHardware>& gpu,
- const char* const vram, size_t size=0, size_t reserved=0)
- : MemoryHeapBase(vram, size), mGPU(gpu) {
- if (base() != MAP_FAILED) {
- if (reserved == 0)
- reserved = virtualSize();
- mAllocator = new SimpleBestFitAllocator(reserved);
- }
- }
- virtual sp<MemoryHeapPmem> createClientHeap() {
- sp<MemoryHeapBase> parentHeap(this);
- return new GPUClientHeap(mGPU, parentHeap);
- }
- virtual const sp<SimpleBestFitAllocator>& getAllocator() const {
- return mAllocator;
- }
-private:
- sp<SimpleBestFitAllocator> mAllocator;
-protected:
- wp<GPUHardware> mGPU;
-};
-
-class GPURegisterHeap : public GPUAreaHeap
-{
-public:
- GPURegisterHeap(const sp<GPUHardware>& gpu)
- : GPUAreaHeap(gpu, "/dev/hw3d", GPUHardware::GPUR_SIZE) { }
- virtual sp<MemoryHeapPmem> createClientHeap() {
- sp<MemoryHeapBase> parentHeap(this);
- return new MemoryHeapRegs(mGPU, parentHeap);
- }
-private:
- class MemoryHeapRegs : public GPUClientHeap {
- public:
- MemoryHeapRegs(const wp<GPUHardware>& gpu,
- const sp<MemoryHeapBase>& heap)
- : GPUClientHeap(gpu, heap) { }
- sp<MemoryHeapPmem::MemoryPmem> createMemory(size_t offset, size_t size);
- virtual void revoke();
- private:
- class GPUHandle : public MemoryHeapPmem::MemoryPmem {
- public:
- GPUHandle(const sp<GPUHardware>& gpu,
- const sp<MemoryHeapPmem>& heap)
- : MemoryHeapPmem::MemoryPmem(heap),
- mGPU(gpu), mOwner(gpu->getOwner()) { }
- virtual ~GPUHandle();
- virtual sp<IMemoryHeap> getMemory(
- ssize_t* offset, size_t* size) const;
- virtual void revoke() { };
- virtual status_t onTransact(
- uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
- private:
- void revokeNotification();
- wp<GPUHardware> mGPU;
- pid_t mOwner;
- };
- };
-};
-
-GPURegisterHeap::MemoryHeapRegs::GPUHandle::~GPUHandle() {
- //LOGD("GPUHandle %p released, revoking GPU", this);
- revokeNotification();
-}
-void GPURegisterHeap::MemoryHeapRegs::GPUHandle::revokeNotification() {
- sp<GPUHardware> hw(mGPU.promote());
- if (hw != 0) {
- hw->revoke(mOwner);
- }
-}
-sp<IMemoryHeap> GPURegisterHeap::MemoryHeapRegs::GPUHandle::getMemory(
- ssize_t* offset, size_t* size) const
-{
- sp<MemoryHeapPmem> heap = getHeap();
- if (offset) *offset = 0;
- if (size) *size = heap !=0 ? heap->virtualSize() : 0;
- return heap;
-}
-status_t GPURegisterHeap::MemoryHeapRegs::GPUHandle::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- status_t err = BnMemory::onTransact(code, data, reply, flags);
- if (err == UNKNOWN_TRANSACTION && code == 1000) {
- int callingPid = IPCThreadState::self()->getCallingPid();
- //LOGD("pid %d voluntarily revoking gpu", callingPid);
- if (callingPid == mOwner) {
- revokeNotification();
- // we've revoked the GPU, don't do it again later when we
- // are destroyed.
- mGPU.clear();
- } else {
- LOGW("%d revoking someone else's gpu? (owner=%d)",
- callingPid, mOwner);
- }
- err = NO_ERROR;
- }
- return err;
-}
-
-// ---------------------------------------------------------------------------
-
-
-sp<MemoryHeapPmem::MemoryPmem> GPURegisterHeap::MemoryHeapRegs::createMemory(
- size_t offset, size_t size)
-{
- sp<GPUHandle> memory;
- sp<GPUHardware> gpu = mGPU.promote();
- if (heapID()>0 && gpu!=0) {
-#if HAVE_ANDROID_OS
- /* this is where the GPU is powered on and the registers are mapped
- * in the client */
- //LOGD("ioctl(HW3D_GRANT_GPU)");
- int err = ioctl(heapID(), HW3D_GRANT_GPU, base());
- if (err) {
- // it can happen if the master heap has been closed already
- // in which case the GPU already is revoked (app crash for
- // instance).
- LOGW("HW3D_GRANT_GPU failed (%s), mFD=%d, base=%p",
- strerror(errno), heapID(), base());
- }
- memory = new GPUHandle(gpu, this);
-#endif
- }
- return memory;
-}
-
-void GPURegisterHeap::MemoryHeapRegs::revoke()
-{
- MemoryHeapPmem::revoke();
-#if HAVE_ANDROID_OS
- if (heapID() > 0) {
- //LOGD("ioctl(HW3D_REVOKE_GPU)");
- int err = ioctl(heapID(), HW3D_REVOKE_GPU, base());
- LOGE_IF(err, "HW3D_REVOKE_GPU failed (%s), mFD=%d, base=%p",
- strerror(errno), heapID(), base());
- }
-#endif
-}
-
-/*****************************************************************************/
-
-GPUHardware::GPUHardware()
- : mOwner(NO_OWNER)
-{
-}
-
-GPUHardware::~GPUHardware()
-{
-}
-
-status_t GPUHardware::requestLocked(int pid)
-{
- const int self_pid = getpid();
- if (pid == self_pid) {
- // can't use GPU from surfaceflinger's process
- return PERMISSION_DENIED;
- }
-
- if (mOwner != pid) {
- if (mREGHeap != 0) {
- if (mOwner != NO_OWNER) {
- // someone already has the gpu.
- takeBackGPULocked();
- releaseLocked();
- }
- } else {
- // first time, initialize the stuff.
- if (mSMIHeap == 0)
- mSMIHeap = new GPUAreaHeap(this, "/dev/pmem_gpu0");
- if (mEBIHeap == 0)
- mEBIHeap = new GPUAreaHeap(this,
- "/dev/pmem_gpu1", 0, GPU_RESERVED_SIZE);
- mREGHeap = new GPURegisterHeap(this);
- mAllocator = mEBIHeap->getAllocator();
- if (mAllocator == NULL) {
- // something went terribly wrong.
- mSMIHeap.clear();
- mEBIHeap.clear();
- mREGHeap.clear();
- return INVALID_OPERATION;
- }
- }
- Client& client = getClientLocked(pid);
- mCurrentAllocator = new MemoryDealer(client.ebi.clientHeap, mAllocator);
- mOwner = pid;
- }
- return NO_ERROR;
-}
-
-sp<MemoryDealer> GPUHardware::request(int pid)
-{
- sp<MemoryDealer> dealer;
- Mutex::Autolock _l(mLock);
- Client* client;
- LOGD("pid %d requesting gpu surface (current owner = %d)", pid, mOwner);
- if (requestLocked(pid) == NO_ERROR) {
- dealer = mCurrentAllocator;
- LOGD_IF(dealer!=0, "gpu surface granted to pid %d", mOwner);
- }
- return dealer;
-}
-
-status_t GPUHardware::request(int pid, const sp<IGPUCallback>& callback,
- ISurfaceComposer::gpu_info_t* gpu)
-{
- if (callback == 0)
- return BAD_VALUE;
-
- sp<IMemory> gpuHandle;
- LOGD("pid %d requesting gpu core (owner = %d)", pid, mOwner);
- Mutex::Autolock _l(mLock);
- status_t err = requestLocked(pid);
- if (err == NO_ERROR) {
- // it's guaranteed to be there, be construction
- Client& client = mClients.editValueFor(pid);
- registerCallbackLocked(callback, client);
- gpu->count = 2;
- gpu->regions[0].region = client.smi.map();
- gpu->regions[1].region = client.ebi.map();
- gpu->regs = client.reg.map();
- gpu->regions[0].reserved = 0;
- gpu->regions[1].reserved = GPU_RESERVED_SIZE;
- if (gpu->regs != 0) {
- //LOGD("gpu core granted to pid %d, handle base=%p",
- // mOwner, gpu->regs->pointer());
- }
- mCallback = callback;
- } else {
- LOGW("couldn't grant gpu core to pid %d", pid);
- }
- return err;
-}
-
-void GPUHardware::revoke(int pid)
-{
- Mutex::Autolock _l(mLock);
- if (mOwner > 0) {
- if (pid != mOwner) {
- LOGW("GPU owned by %d, revoke from %d", mOwner, pid);
- return;
- }
- //LOGD("revoke pid=%d, owner=%d", pid, mOwner);
- // mOwner could be <0 if the same process acquired the GPU
- // several times without releasing it first.
- mCondition.signal();
- releaseLocked();
- }
-}
-
-status_t GPUHardware::friendlyRevoke()
-{
- Mutex::Autolock _l(mLock);
- //LOGD("friendlyRevoke owner=%d", mOwner);
- takeBackGPULocked();
- releaseLocked();
- return NO_ERROR;
-}
-
-void GPUHardware::takeBackGPULocked()
-{
- sp<IGPUCallback> callback = mCallback;
- mCallback.clear();
- if (callback != 0) {
- callback->gpuLost(); // one-way
- mCondition.waitRelative(mLock, ms2ns(250));
- }
-}
-
-void GPUHardware::releaseLocked()
-{
- //LOGD("revoking gpu from pid %d", mOwner);
- if (mOwner != NO_OWNER) {
- // this may fail because the client might have died, and have
- // been removed from the list.
- ssize_t index = mClients.indexOfKey(mOwner);
- if (index >= 0) {
- Client& client(mClients.editValueAt(index));
- client.revokeAllHeaps();
- }
- mOwner = NO_OWNER;
- mCurrentAllocator.clear();
- mCallback.clear();
- }
-}
-
-GPUHardware::Client& GPUHardware::getClientLocked(pid_t pid)
-{
- ssize_t index = mClients.indexOfKey(pid);
- if (index < 0) {
- Client client;
- client.pid = pid;
- client.smi.heap = mSMIHeap;
- client.ebi.heap = mEBIHeap;
- client.reg.heap = mREGHeap;
- index = mClients.add(pid, client);
- }
- Client& client(mClients.editValueAt(index));
- client.createClientHeaps();
- return client;
-}
-
-// ----------------------------------------------------------------------------
-// for debugging / testing ...
-
-sp<SimpleBestFitAllocator> GPUHardware::getAllocator() const {
- Mutex::Autolock _l(mLock);
- return mAllocator;
-}
-
-void GPUHardware::unconditionalRevoke()
-{
- Mutex::Autolock _l(mLock);
- releaseLocked();
-}
-
-// ---------------------------------------------------------------------------
-
-sp<IMemory> GPUHardware::GPUArea::map() {
- sp<IMemory> memory;
- if (clientHeap != 0 && heap != 0) {
- memory = clientHeap->mapMemory(0, heap->virtualSize());
- }
- return memory;
-}
-
-void GPUHardware::Client::createClientHeaps()
-{
- if (smi.clientHeap == 0)
- smi.clientHeap = smi.heap->createClientHeap();
- if (ebi.clientHeap == 0)
- ebi.clientHeap = ebi.heap->createClientHeap();
- if (reg.clientHeap == 0)
- reg.clientHeap = reg.heap->createClientHeap();
-}
-
-void GPUHardware::Client::revokeAllHeaps()
-{
- if (smi.clientHeap != 0)
- smi.clientHeap->revoke();
- if (ebi.clientHeap != 0)
- ebi.clientHeap->revoke();
- if (reg.clientHeap != 0)
- reg.clientHeap->revoke();
-}
-
-void GPUHardware::registerCallbackLocked(const sp<IGPUCallback>& callback,
- Client& client)
-{
- sp<IBinder> binder = callback->asBinder();
- if (mRegisteredClients.add(binder, client.pid) >= 0) {
- binder->linkToDeath(this);
- }
-}
-
-void GPUHardware::binderDied(const wp<IBinder>& who)
-{
- Mutex::Autolock _l(mLock);
- pid_t pid = mRegisteredClients.valueFor(who);
- if (pid != 0) {
- ssize_t index = mClients.indexOfKey(pid);
- if (index >= 0) {
- //LOGD("*** removing client at %d", index);
- Client& client(mClients.editValueAt(index));
- client.revokeAllHeaps(); // not really needed in theory
- mClients.removeItemsAt(index);
- if (mClients.size() == 0) {
- //LOGD("*** was last client closing everything");
- mCallback.clear();
- mAllocator.clear();
- mCurrentAllocator.clear();
- mSMIHeap.clear();
- mREGHeap.clear();
-
- // NOTE: we cannot clear the EBI heap because surfaceflinger
- // itself may be using it, since this is where surfaces
- // are allocated. if we're in the middle of compositing
- // a surface (even if its process just died), we cannot
- // rip the heap under our feet.
-
- mOwner = NO_OWNER;
- }
- }
- }
-}
-
-// ---------------------------------------------------------------------------
-
-sp<GPUHardwareInterface> GPUFactory::getGPU()
-{
- sp<GPUHardwareInterface> gpu;
- if (access("/dev/hw3d", F_OK) == 0) {
- gpu = new GPUHardware();
- }
- return gpu;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/GPUHardware/GPUHardware.h
deleted file mode 100644
index 3354528..0000000
--- a/libs/surfaceflinger/GPUHardware/GPUHardware.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_GPU_HARDWARE_H
-#define ANDROID_GPU_HARDWARE_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-#include <utils/KeyedVector.h>
-
-#include <ui/ISurfaceComposer.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class IGPUCallback;
-
-class GPUHardwareInterface : public virtual RefBase
-{
-public:
- virtual void revoke(int pid) = 0;
- virtual sp<MemoryDealer> request(int pid) = 0;
- virtual status_t request(int pid, const sp<IGPUCallback>& callback,
- ISurfaceComposer::gpu_info_t* gpu) = 0;
-
- virtual status_t friendlyRevoke() = 0;
-
- // used for debugging only...
- virtual sp<SimpleBestFitAllocator> getAllocator() const = 0;
- virtual pid_t getOwner() const = 0;
- virtual void unconditionalRevoke() = 0;
-};
-
-// ---------------------------------------------------------------------------
-
-class GPUFactory
-{
-public:
- // the gpu factory
- static sp<GPUHardwareInterface> getGPU();
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_GPU_HARDWARE_H
diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp
index 96395a8..ecb6b32 100644
--- a/libs/surfaceflinger/Layer.cpp
+++ b/libs/surfaceflinger/Layer.cpp
@@ -14,26 +14,24 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <cutils/properties.h>
+#include <cutils/native_handle.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/StopWatch.h>
#include <ui/PixelFormat.h>
-#include <ui/EGLDisplaySurface.h>
+#include <ui/Surface.h>
+#include "Buffer.h"
#include "clz.h"
#include "Layer.h"
-#include "LayerBitmap.h"
#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
#include "DisplayHardware/DisplayHardware.h"
@@ -49,113 +47,159 @@ const char* const Layer::typeID = "Layer";
// ---------------------------------------------------------------------------
-Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i)
- : LayerBaseClient(flinger, display, c, i),
+Layer::Layer(SurfaceFlinger* flinger, DisplayID display,
+ const sp<Client>& c, int32_t i)
+ : LayerBaseClient(flinger, display, c, i), lcblk(NULL),
mSecure(false),
- mFrontBufferIndex(1),
- mNeedsBlending(true),
- mResizeTransactionDone(false),
- mTextureName(-1U), mTextureWidth(0), mTextureHeight(0)
+ mNeedsBlending(true)
{
// no OpenGL operation is possible here, since we might not be
// in the OpenGL thread.
+ lcblk = new SharedBufferServer(c->ctrlblk, i, NUM_BUFFERS);
+ mFrontBufferIndex = lcblk->getFrontBuffer();
}
Layer::~Layer()
{
- client->free(clientIndex());
- // this should always be called from the OpenGL thread
- if (mTextureName != -1U) {
- //glDeleteTextures(1, &mTextureName);
- deletedTextures.add(mTextureName);
- }
+ destroy();
+ // the actual buffers will be destroyed here
+ delete lcblk;
+
}
-void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags)
+void Layer::destroy()
{
- LayerBase::initStates(w,h,flags);
-
- if (flags & ISurfaceComposer::eDestroyBackbuffer)
- lcblk->flags |= eNoCopyBack;
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ if (mTextures[i].name != -1U) {
+ glDeleteTextures(1, &mTextures[i].name);
+ mTextures[i].name = -1U;
+ }
+ if (mTextures[i].image != EGL_NO_IMAGE_KHR) {
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+ eglDestroyImageKHR(dpy, mTextures[i].image);
+ mTextures[i].image = EGL_NO_IMAGE_KHR;
+ }
+ mBuffers[i].clear();
+ }
+ mSurface.clear();
}
-sp<LayerBaseClient::Surface> Layer::getSurface() const
+sp<LayerBaseClient::Surface> Layer::createSurface() const
{
return mSurface;
}
-status_t Layer::setBuffers( Client* client,
- uint32_t w, uint32_t h,
+status_t Layer::ditch()
+{
+ // the layer is not on screen anymore. free as much resources as possible
+ destroy();
+ return NO_ERROR;
+}
+
+status_t Layer::setBuffers( uint32_t w, uint32_t h,
PixelFormat format, uint32_t flags)
{
PixelFormatInfo info;
status_t err = getPixelFormatInfo(format, &info);
if (err) return err;
- // TODO: if eHardware is explicitly requested, we should fail
- // on systems where we can't allocate memory that can be used with
- // DMA engines for instance.
-
- // FIXME: we always ask for hardware for now (this should come from copybit)
- flags |= ISurfaceComposer::eHardware;
+ uint32_t bufferFlags = 0;
+ if (flags & ISurfaceComposer::eSecure)
+ bufferFlags |= Buffer::SECURE;
- const uint32_t memory_flags = flags &
- (ISurfaceComposer::eGPU |
- ISurfaceComposer::eHardware |
- ISurfaceComposer::eSecure);
-
- // pixel-alignment. the final alignment may be bigger because
- // we always force a 4-byte aligned bpr.
- uint32_t alignment = 1;
-
- if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) {
- // FIXME: this value should come from the h/w
- alignment = 8;
- // FIXME: this is msm7201A specific, as its GPU only supports
- // BGRA_8888.
- if (format == PIXEL_FORMAT_RGBA_8888) {
- format = PIXEL_FORMAT_BGRA_8888;
- }
- }
-
- mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;
+ mFormat = format;
+ mWidth = w;
+ mHeight = h;
+ mSecure = (bufferFlags & Buffer::SECURE) ? true : false;
mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;
- sp<MemoryDealer> allocators[2];
- for (int i=0 ; i<2 ; i++) {
- allocators[i] = client->createAllocator(memory_flags);
- if (allocators[i] == 0)
- return NO_MEMORY;
- mBuffers[i].init(allocators[i]);
- int err = mBuffers[i].setBits(w, h, alignment, format, LayerBitmap::SECURE_BITS);
- if (err != NO_ERROR)
- return err;
- mBuffers[i].clear(); // clear the bits for security
- mBuffers[i].getInfo(lcblk->surface + i);
+ mBufferFlags = bufferFlags;
+ for (size_t i=0 ; i<NUM_BUFFERS ; i++) {
+ mBuffers[i] = new Buffer();
}
-
- mSurface = new Surface(clientIndex(),
- allocators[0]->getMemoryHeap(),
- allocators[1]->getMemoryHeap(),
- mIdentity);
-
+ mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);
return NO_ERROR;
}
void Layer::reloadTexture(const Region& dirty)
{
- if (UNLIKELY(mTextureName == -1U)) {
- // create the texture name the first time
- // can't do that in the ctor, because it runs in another thread.
- mTextureName = createTexture();
+ Mutex::Autolock _l(mLock);
+ sp<Buffer> buffer(getFrontBuffer());
+ if (LIKELY(mFlags & DisplayHardware::DIRECT_TEXTURE)) {
+ int index = mFrontBufferIndex;
+ if (LIKELY(!mTextures[index].dirty)) {
+ glBindTexture(GL_TEXTURE_2D, mTextures[index].name);
+ } else {
+ // we need to recreate the texture
+ EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay());
+
+ // create the new texture name if needed
+ if (UNLIKELY(mTextures[index].name == -1U)) {
+ mTextures[index].name = createTexture();
+ } else {
+ glBindTexture(GL_TEXTURE_2D, mTextures[index].name);
+ }
+
+ // free the previous image
+ if (mTextures[index].image != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(dpy, mTextures[index].image);
+ mTextures[index].image = EGL_NO_IMAGE_KHR;
+ }
+
+ // construct an EGL_NATIVE_BUFFER_ANDROID
+ android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+
+ // create the new EGLImageKHR
+ const EGLint attrs[] = {
+ EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
+ EGL_NONE, EGL_NONE
+ };
+ mTextures[index].image = eglCreateImageKHR(
+ dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)clientBuf, attrs);
+
+ LOGE_IF(mTextures[index].image == EGL_NO_IMAGE_KHR,
+ "eglCreateImageKHR() failed. err=0x%4x",
+ eglGetError());
+
+ if (mTextures[index].image != EGL_NO_IMAGE_KHR) {
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
+ (GLeglImageOES)mTextures[index].image);
+ GLint error = glGetError();
+ if (UNLIKELY(error != GL_NO_ERROR)) {
+ // this failed, for instance, because we don't support
+ // NPOT.
+ // FIXME: do something!
+ mFlags &= ~DisplayHardware::DIRECT_TEXTURE;
+ } else {
+ // Everything went okay!
+ mTextures[index].dirty = false;
+ mTextures[index].width = clientBuf->width;
+ mTextures[index].height = clientBuf->height;
+ }
+ }
+ }
+ } else {
+ GGLSurface t;
+ status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_RARELY);
+ LOGE_IF(res, "error %d (%s) locking buffer %p",
+ res, strerror(res), buffer.get());
+ if (res == NO_ERROR) {
+ if (UNLIKELY(mTextures[0].name == -1U)) {
+ mTextures[0].name = createTexture();
+ }
+ loadTexture(&mTextures[0], mTextures[0].name, dirty, t);
+ buffer->unlock();
+ }
}
- const GGLSurface& t(frontBuffer().surface());
- loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight);
}
void Layer::onDraw(const Region& clip) const
{
- if (UNLIKELY(mTextureName == -1LU)) {
+ const int index = (mFlags & DisplayHardware::DIRECT_TEXTURE) ?
+ mFrontBufferIndex : 0;
+ GLuint textureName = mTextures[index].name;
+ if (UNLIKELY(textureName == -1LU)) {
//LOGW("Layer %p doesn't have a texture", this);
// the texture has not been created yet, this Layer has
// in fact never been drawn into. this happens frequently with
@@ -164,63 +208,55 @@ void Layer::onDraw(const Region& clip) const
return;
}
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- const LayerBitmap& front(frontBuffer());
- const GGLSurface& t(front.surface());
-
- status_t err = NO_ERROR;
- const int can_use_copybit = canUseCopybit();
- if (can_use_copybit) {
- // StopWatch watch("copybit");
- const State& s(drawingState());
-
- copybit_image_t dst;
- hw.getDisplaySurface(&dst);
- const copybit_rect_t& drect
- = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds);
-
- copybit_image_t src;
- front.getBitmapSurface(&src);
- copybit_rect_t srect = { 0, 0, t.width, t.height };
-
- copybit_device_t* copybit = mFlinger->getBlitEngine();
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation());
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha);
- copybit->set_parameter(copybit, COPYBIT_DITHER,
- s.flags & ISurfaceComposer::eLayerDither ?
- COPYBIT_ENABLE : COPYBIT_DISABLE);
-
- region_iterator it(clip);
- err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
- }
-
- if (!can_use_copybit || err) {
- drawWithOpenGL(clip, mTextureName, t);
- }
+ drawWithOpenGL(clip, mTextures[index]);
}
-status_t Layer::reallocateBuffer(int32_t index, uint32_t w, uint32_t h)
+sp<SurfaceBuffer> Layer::requestBuffer(int index, int usage)
{
- LOGD_IF(DEBUG_RESIZE,
- "reallocateBuffer (layer=%p), "
- "requested (%dx%d), "
- "index=%d, (%dx%d), (%dx%d)",
- this,
- int(w), int(h),
- int(index),
- int(mBuffers[0].width()), int(mBuffers[0].height()),
- int(mBuffers[1].width()), int(mBuffers[1].height()));
-
- status_t err = mBuffers[index].resize(w, h);
- if (err == NO_ERROR) {
- mBuffers[index].getInfo(lcblk->surface + index);
+ /*
+ * This is called from the client's Surface::dequeue(). This can happen
+ * at any time, especially while we're in the middle of using the
+ * buffer 'index' as our front buffer.
+ *
+ * Make sure the buffer we're resizing is not the front buffer and has been
+ * dequeued. Once this condition is asserted, we are guaranteed that this
+ * buffer cannot become the front buffer under our feet, since we're called
+ * from Surface::dequeue()
+ */
+ status_t err = lcblk->assertReallocate(index);
+ LOGE_IF(err, "assertReallocate(%d) failed (%s)", index, strerror(-err));
+
+ Mutex::Autolock _l(mLock);
+ uint32_t w = mWidth;
+ uint32_t h = mHeight;
+
+ sp<Buffer>& buffer(mBuffers[index]);
+ if (buffer->getStrongCount() == 1) {
+ err = buffer->reallocate(w, h, mFormat, usage, mBufferFlags);
} else {
- LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s",
- index, w, h, err, strerror(err));
- // XXX: what to do, what to do? We could try to free some
- // hidden surfaces, instead of killing this one?
+ // here we have to reallocate a new buffer because we could have a
+ // client in our process with a reference to it (eg: status bar),
+ // and we can't release the handle under its feet.
+ buffer.clear();
+ buffer = new Buffer(w, h, mFormat, usage, mBufferFlags);
+ err = buffer->initCheck();
+ }
+
+ if (err || buffer->handle == 0) {
+ LOGE_IF(err || buffer->handle == 0,
+ "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d failed (%s)",
+ this, index, w, h, strerror(-err));
+ } else {
+ LOGD_IF(DEBUG_RESIZE,
+ "Layer::requestBuffer(this=%p), index=%d, w=%d, h=%d",
+ this, index, w, h);
+ }
+
+ if (err == NO_ERROR && buffer->handle != 0) {
+ // texture is now dirty...
+ mTextures[index].dirty = true;
}
- return err;
+ return buffer;
}
uint32_t Layer::doTransaction(uint32_t flags)
@@ -228,113 +264,48 @@ uint32_t Layer::doTransaction(uint32_t flags)
const Layer::State& front(drawingState());
const Layer::State& temp(currentState());
- // the test front.{w|h} != temp.{w|h} is not enough because it is possible
- // that the size changed back to its previous value before the buffer
- // was resized (in the eLocked case below), in which case, we still
- // need to execute the code below so the clients have a chance to be
- // release. resze() deals with the fact that the size can be the same.
-
- /*
- * Various states we could be in...
-
- resize = state & eResizeRequested;
- if (backbufferChanged) {
- if (resize == 0) {
- // ERROR, the resized buffer doesn't have its resize flag set
- } else if (resize == mask) {
- // ERROR one of the buffer has already been resized
- } else if (resize == mask ^ eResizeRequested) {
- // ERROR, the resized buffer doesn't have its resize flag set
- } else if (resize == eResizeRequested) {
- // OK, Normal case, proceed with resize
- }
- } else {
- if (resize == 0) {
- // OK, nothing special, do nothing
- } else if (resize == mask) {
- // restarted transaction, do nothing
- } else if (resize == mask ^ eResizeRequested) {
- // restarted transaction, do nothing
- } else if (resize == eResizeRequested) {
- // OK, size reset to previous value, proceed with resize
- }
- }
- */
-
// Index of the back buffer
const bool backbufferChanged = (front.w != temp.w) || (front.h != temp.h);
- const uint32_t state = lcblk->swapState;
- const int32_t clientBackBufferIndex = layer_cblk_t::backBuffer(state);
- const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0;
- uint32_t resizeFlags = state & eResizeRequested;
-
- if (UNLIKELY(backbufferChanged && (resizeFlags != eResizeRequested))) {
- LOGE( "backbuffer size changed, but both resize flags are not set! "
- "(layer=%p), state=%08x, requested (%dx%d), drawing (%d,%d), "
- "index=%d, (%dx%d), (%dx%d)",
- this, state,
- int(temp.w), int(temp.h),
- int(drawingState().w), int(drawingState().h),
- int(clientBackBufferIndex),
- int(mBuffers[0].width()), int(mBuffers[0].height()),
- int(mBuffers[1].width()), int(mBuffers[1].height()));
- // if we get there we're pretty screwed. the only reasonable
- // thing to do is to pretend we should do the resize since
- // backbufferChanged is set (this also will give a chance to
- // client to get unblocked)
- resizeFlags = eResizeRequested;
- }
-
- if (resizeFlags == eResizeRequested) {
- // NOTE: asserting that clientBackBufferIndex!=mFrontBufferIndex
- // here, would be wrong and misleading because by this point
- // mFrontBufferIndex has not been updated yet.
+ if (backbufferChanged) {
+ // the size changed, we need to ask our client to request a new buffer
LOGD_IF(DEBUG_RESIZE,
- "resize (layer=%p), state=%08x, "
- "requested (%dx%d), "
- "drawing (%d,%d), "
- "index=%d, (%dx%d), (%dx%d)",
- this, state,
- int(temp.w), int(temp.h),
+ "resize (layer=%p), requested (%dx%d), "
+ "drawing (%d,%d), (%dx%d), (%dx%d)",
+ this, int(temp.w), int(temp.h),
int(drawingState().w), int(drawingState().h),
- int(clientBackBufferIndex),
- int(mBuffers[0].width()), int(mBuffers[0].height()),
- int(mBuffers[1].width()), int(mBuffers[1].height()));
-
- if (state & eLocked) {
- // if the buffer is locked, we can't resize anything because
- // - the backbuffer is currently in use by the user
- // - the front buffer is being shown
- // We just act as if the transaction didn't happen and we
- // reschedule it later...
- flags |= eRestartTransaction;
- } else {
- // This buffer needs to be resized
- status_t err =
- resize(clientBackBufferIndex, temp.w, temp.h, "transaction");
- if (err == NO_ERROR) {
- const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0;
- android_atomic_and(~mask, &(lcblk->swapState));
- // since a buffer became available, we can let the client go...
- mFlinger->scheduleBroadcast(client);
- mResizeTransactionDone = true;
-
- // we're being resized and there is a freeze display request,
- // acquire a freeze lock, so that the screen stays put
- // until we've redrawn at the new size; this is to avoid
- // glitches upon orientation changes.
- if (mFlinger->hasFreezeRequest()) {
- // if the surface is hidden, don't try to acquire the
- // freeze lock, since hidden surfaces may never redraw
- if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
- mFreezeLock = mFlinger->getFreezeLock();
- }
- }
+ int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()),
+ int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight()));
+
+ // record the new size, form this point on, when the client request a
+ // buffer, it'll get the new size.
+ setDrawingSize(temp.w, temp.h);
+
+ // all buffers need reallocation
+ lcblk->reallocate();
+
+ // recompute the visible region
+ // FIXME: ideally we would do that only when we have received
+ // a buffer of the right size
+ flags |= Layer::eVisibleRegion;
+ this->contentDirty = true;
+
+#if 0
+ // FIXME: handle freeze lock
+ // we're being resized and there is a freeze display request,
+ // acquire a freeze lock, so that the screen stays put
+ // until we've redrawn at the new size; this is to avoid
+ // glitches upon orientation changes.
+ if (mFlinger->hasFreezeRequest()) {
+ // if the surface is hidden, don't try to acquire the
+ // freeze lock, since hidden surfaces may never redraw
+ if (!(front.flags & ISurfaceComposer::eLayerHidden)) {
+ mFreezeLock = mFlinger->getFreezeLock();
}
}
+#endif
}
-
+
if (temp.sequence != front.sequence) {
if (temp.flags & ISurfaceComposer::eLayerHidden || temp.alpha == 0) {
// this surface is now hidden, so it shouldn't hold a freeze lock
@@ -346,54 +317,10 @@ uint32_t Layer::doTransaction(uint32_t flags)
return LayerBase::doTransaction(flags);
}
-status_t Layer::resize(
- int32_t clientBackBufferIndex,
- uint32_t width, uint32_t height,
- const char* what)
-{
- /*
- * handle resize (backbuffer and frontbuffer reallocation)
- */
-
- const LayerBitmap& clientBackBuffer(mBuffers[clientBackBufferIndex]);
-
- // if the new (transaction) size is != from the the backbuffer
- // then we need to reallocate the backbuffer
- bool backbufferChanged = (clientBackBuffer.width() != width) ||
- (clientBackBuffer.height() != height);
-
- LOGD_IF(!backbufferChanged,
- "(%s) eResizeRequested (layer=%p), but size not changed: "
- "requested (%dx%d), drawing (%d,%d), current (%d,%d),"
- "state=%08lx, index=%d, (%dx%d), (%dx%d)",
- what, this,
- int(width), int(height),
- int(drawingState().w), int(drawingState().h),
- int(currentState().w), int(currentState().h),
- long(lcblk->swapState),
- int(clientBackBufferIndex),
- int(mBuffers[0].width()), int(mBuffers[0].height()),
- int(mBuffers[1].width()), int(mBuffers[1].height()));
-
- // this can happen when changing the size back and forth quickly
- status_t err = NO_ERROR;
- if (backbufferChanged) {
- err = reallocateBuffer(clientBackBufferIndex, width, height);
- }
- if (UNLIKELY(err != NO_ERROR)) {
- // couldn't reallocate the surface
- android_atomic_write(eInvalidSurface, &lcblk->swapState);
- memset(lcblk->surface+clientBackBufferIndex, 0, sizeof(surface_info_t));
- }
- return err;
-}
-
-void Layer::setSizeChanged(uint32_t w, uint32_t h)
-{
- LOGD_IF(DEBUG_RESIZE,
- "setSizeChanged w=%d, h=%d (old: w=%d, h=%d)",
- w, h, mCurrentState.w, mCurrentState.h);
- android_atomic_or(eResizeRequested, &(lcblk->swapState));
+void Layer::setDrawingSize(uint32_t w, uint32_t h) {
+ Mutex::Autolock _l(mLock);
+ mWidth = w;
+ mHeight = h;
}
// ----------------------------------------------------------------------------
@@ -402,128 +329,26 @@ void Layer::setSizeChanged(uint32_t w, uint32_t h)
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
- uint32_t state = android_atomic_or(eBusy, &(lcblk->swapState));
- // preemptively block the client, because he might set
- // eFlipRequested at any time and want to use this buffer
- // for the next frame. This will be unset below if it
- // turns out we didn't need it.
-
- uint32_t mask = eInvalidSurface | eFlipRequested | eResizeRequested;
- if (!(state & mask))
- return;
-
- if (UNLIKELY(state & eInvalidSurface)) {
- // if eInvalidSurface is set, this means the surface
- // became invalid during a transaction (NO_MEMORY for instance)
- mFlinger->scheduleBroadcast(client);
+ ssize_t buf = lcblk->retireAndLock();
+ if (buf < NO_ERROR) {
+ //LOGW("nothing to retire (%s)", strerror(-buf));
+ // NOTE: here the buffer is locked because we will used
+ // for composition later in the loop
return;
}
+
+ // we retired a buffer, which becomes the new front buffer
+ mFrontBufferIndex = buf;
- if (UNLIKELY(state & eFlipRequested)) {
- uint32_t oldState;
- mPostedDirtyRegion = post(&oldState, recomputeVisibleRegions);
- if (oldState & eNextFlipPending) {
- // Process another round (we know at least a buffer
- // is ready for that client).
- mFlinger->signalEvent();
- }
- }
-}
-
-Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions)
-{
- // atomically swap buffers and (re)set eFlipRequested
- int32_t oldValue, newValue;
- layer_cblk_t * const lcblk = this->lcblk;
- do {
- oldValue = lcblk->swapState;
- // get the current value
-
- LOG_ASSERT(oldValue&eFlipRequested,
- "eFlipRequested not set, yet we're flipping! (state=0x%08lx)",
- long(oldValue));
-
- newValue = (oldValue ^ eIndex);
- // swap buffers
-
- newValue &= ~(eFlipRequested | eNextFlipPending);
- // clear eFlipRequested and eNextFlipPending
-
- if (oldValue & eNextFlipPending)
- newValue |= eFlipRequested;
- // if eNextFlipPending is set (second buffer already has something
- // in it) we need to reset eFlipRequested because the client
- // might never do it
-
- } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState)));
- *previousSate = oldValue;
-
- const int32_t index = (newValue & eIndex) ^ 1;
- mFrontBufferIndex = index;
-
- // ... post the new front-buffer
- Region dirty(lcblk->region + index);
- dirty.andSelf(frontBuffer().bounds());
-
- //LOGI("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n",
- // oldValue, newValue, mFrontBufferIndex);
- //dirty.dump("dirty");
-
- if (UNLIKELY(oldValue & eResizeRequested)) {
-
- LOGD_IF(DEBUG_RESIZE,
- "post (layer=%p), state=%08x, "
- "index=%d, (%dx%d), (%dx%d)",
- this, newValue,
- int(1-index),
- int(mBuffers[0].width()), int(mBuffers[0].height()),
- int(mBuffers[1].width()), int(mBuffers[1].height()));
-
- // here, we just posted the surface and we have resolved
- // the front/back buffer indices. The client is blocked, so
- // it cannot start using the new backbuffer.
-
- // If the backbuffer was resized in THIS round, we actually cannot
- // resize the frontbuffer because it has *just* been drawn (and we
- // would have nothing to draw). In this case we just skip the resize
- // it'll happen after the next page flip or during the next
- // transaction.
-
- const uint32_t mask = (1-index) ? eResizeBuffer1 : eResizeBuffer0;
- if (mResizeTransactionDone && (newValue & mask)) {
- // Resize the layer's second buffer only if the transaction
- // happened. It may not have happened yet if eResizeRequested
- // was set immediately after the "transactionRequested" test,
- // in which case the drawing state's size would be wrong.
- mFreezeLock.clear();
- const Layer::State& s(drawingState());
- if (resize(1-index, s.w, s.h, "post") == NO_ERROR) {
- do {
- oldValue = lcblk->swapState;
- if ((oldValue & eResizeRequested) == eResizeRequested) {
- // ugh, another resize was requested since we processed
- // the first buffer, don't free the client, and let
- // the next transaction handle everything.
- break;
- }
- newValue = oldValue & ~mask;
- } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState)));
- }
- mResizeTransactionDone = false;
- recomputeVisibleRegions = true;
- this->contentDirty = true;
- }
- }
-
- reloadTexture(dirty);
+ // get the dirty region
+ sp<Buffer> newFrontBuffer(getBuffer(buf));
+ const Region dirty(lcblk->getDirtyRegion(buf));
+ mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
- return dirty;
-}
+ // FIXME: signal an event if we have more buffers waiting
+ // mFlinger->signalEvent();
-Point Layer::getPhysicalSize() const
-{
- const LayerBitmap& front(frontBuffer());
- return Point(front.width(), front.height());
+ reloadTexture( mPostedDirtyRegion );
}
void Layer::unlockPageFlip(
@@ -544,23 +369,42 @@ void Layer::unlockPageFlip(
// is in screen space as well).
dirtyRegion.andSelf(visibleRegionScreen);
outDirtyRegion.orSelf(dirtyRegion);
-
- // client could be blocked, so signal them so they get a
- // chance to reevaluate their condition.
- mFlinger->scheduleBroadcast(client);
}
}
void Layer::finishPageFlip()
{
- if (LIKELY(!(lcblk->swapState & eInvalidSurface))) {
- LOGE_IF(!(lcblk->swapState & eBusy),
- "layer %p wasn't locked!", this);
- android_atomic_and(~eBusy, &(lcblk->swapState));
- }
- mFlinger->scheduleBroadcast(client);
+ status_t err = lcblk->unlock( mFrontBufferIndex );
+ LOGE_IF(err!=NO_ERROR,
+ "layer %p, buffer=%d wasn't locked!",
+ this, mFrontBufferIndex);
}
+// ---------------------------------------------------------------------------
+
+Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<Layer>& owner)
+ : Surface(flinger, id, owner->getIdentity(), owner)
+{
+}
+
+Layer::SurfaceLayer::~SurfaceLayer()
+{
+}
+
+sp<SurfaceBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage)
+{
+ sp<SurfaceBuffer> buffer;
+ sp<Layer> owner(getOwner());
+ if (owner != 0) {
+ LOGE_IF(uint32_t(index)>=NUM_BUFFERS,
+ "getBuffer() index (%d) out of range", index);
+ if (uint32_t(index) < NUM_BUFFERS) {
+ buffer = owner->requestBuffer(index, usage);
+ }
+ }
+ return buffer;
+}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h
index 2867f2b..3b4489e 100644
--- a/libs/surfaceflinger/Layer.h
+++ b/libs/surfaceflinger/Layer.h
@@ -21,13 +21,14 @@
#include <sys/types.h>
#include <ui/PixelFormat.h>
-
-#include <private/ui/SharedState.h>
-#include <private/ui/LayerState.h>
-
#include <pixelflinger/pixelflinger.h>
-#include "LayerBitmap.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "Buffer.h"
#include "LayerBase.h"
#include "Transform.h"
@@ -36,12 +37,13 @@ namespace android {
// ---------------------------------------------------------------------------
class Client;
-class LayerBitmap;
-class MemoryDealer;
class FreezeLock;
+class Buffer;
// ---------------------------------------------------------------------------
+const size_t NUM_BUFFERS = 2;
+
class Layer : public LayerBaseClient
{
public:
@@ -50,67 +52,77 @@ public:
virtual char const* getTypeID() const { return typeID; }
virtual uint32_t getTypeInfo() const { return typeInfo; }
+
+ SharedBufferServer* lcblk;
+
+
Layer(SurfaceFlinger* flinger, DisplayID display,
- Client* c, int32_t i);
+ const sp<Client>& client, int32_t i);
virtual ~Layer();
- inline PixelFormat pixelFormat() const {
- return frontBuffer().pixelFormat();
- }
+ status_t setBuffers(uint32_t w, uint32_t h,
+ PixelFormat format, uint32_t flags=0);
- status_t setBuffers( Client* client,
- uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags=0);
+ void setDrawingSize(uint32_t w, uint32_t h);
virtual void onDraw(const Region& clip) const;
- virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
- virtual void setSizeChanged(uint32_t w, uint32_t h);
virtual uint32_t doTransaction(uint32_t transactionFlags);
- virtual Point getPhysicalSize() const;
virtual void lockPageFlip(bool& recomputeVisibleRegions);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
virtual void finishPageFlip();
virtual bool needsBlending() const { return mNeedsBlending; }
virtual bool isSecure() const { return mSecure; }
- virtual GLuint getTextureName() const { return mTextureName; }
- virtual sp<Surface> getSurface() const;
-
- const LayerBitmap& getBuffer(int i) const { return mBuffers[i]; }
- LayerBitmap& getBuffer(int i) { return mBuffers[i]; }
-
+ virtual sp<Surface> createSurface() const;
+ virtual status_t ditch();
+
+ // only for debugging
+ inline sp<Buffer> getBuffer(int i) { return mBuffers[i]; }
// only for debugging
- const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
+ inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; }
+ // only for debugging
+ inline PixelFormat pixelFormat() const { return mFormat; }
private:
- inline const LayerBitmap&
- frontBuffer() const { return getBuffer(mFrontBufferIndex); }
- inline LayerBitmap&
- frontBuffer() { return getBuffer(mFrontBufferIndex); }
- inline const LayerBitmap&
- backBuffer() const { return getBuffer(1-mFrontBufferIndex); }
- inline LayerBitmap&
- backBuffer() { return getBuffer(1-mFrontBufferIndex); }
-
+ inline sp<Buffer> getFrontBuffer() {
+ return mBuffers[mFrontBufferIndex];
+ }
+
void reloadTexture(const Region& dirty);
- status_t resize(int32_t index, uint32_t w, uint32_t h, const char* what);
- Region post(uint32_t* oldState, bool& recomputeVisibleRegions);
- status_t reallocateBuffer(int32_t index, uint32_t w, uint32_t h);
-
+ sp<SurfaceBuffer> requestBuffer(int index, int usage);
+ void destroy();
+
+ class SurfaceLayer : public LayerBaseClient::Surface {
+ public:
+ SurfaceLayer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<Layer>& owner);
+ ~SurfaceLayer();
+ private:
+ virtual sp<SurfaceBuffer> requestBuffer(int index, int usage);
+ sp<Layer> getOwner() const {
+ return static_cast<Layer*>(Surface::getOwner().get());
+ }
+ };
+ friend class SurfaceLayer;
+
sp<Surface> mSurface;
bool mSecure;
- LayerBitmap mBuffers[2];
int32_t mFrontBufferIndex;
bool mNeedsBlending;
- bool mResizeTransactionDone;
Region mPostedDirtyRegion;
sp<FreezeLock> mFreezeLock;
+ PixelFormat mFormat;
+ uint32_t mBufferFlags;
+
+ // protected by mLock
+ sp<Buffer> mBuffers[NUM_BUFFERS];
+ Texture mTextures[NUM_BUFFERS];
+ uint32_t mWidth;
+ uint32_t mHeight;
- GLuint mTextureName;
- GLuint mTextureWidth;
- GLuint mTextureHeight;
+ mutable Mutex mLock;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp
index 0cf53f7..62e41b0 100644
--- a/libs/surfaceflinger/LayerBase.cpp
+++ b/libs/surfaceflinger/LayerBase.cpp
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
@@ -30,7 +30,6 @@
#include "clz.h"
#include "LayerBase.h"
-#include "LayerBlur.h"
#include "SurfaceFlinger.h"
#include "DisplayHardware/DisplayHardware.h"
@@ -53,19 +52,13 @@ const char* const LayerBaseClient::typeID = "LayerBaseClient";
// ---------------------------------------------------------------------------
-Vector<GLuint> LayerBase::deletedTextures;
-
-int32_t LayerBase::sIdentity = 0;
-
LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)
: dpy(display), contentDirty(false),
mFlinger(flinger),
mTransformed(false),
mOrientation(0),
- mCanUseCopyBit(false),
mTransactionFlags(0),
mPremultipliedAlpha(true),
- mIdentity(uint32_t(android_atomic_inc(&sIdentity))),
mInvalidate(0)
{
const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
@@ -133,9 +126,6 @@ uint32_t LayerBase::setTransactionFlags(uint32_t flags) {
return android_atomic_or(flags, &mTransactionFlags);
}
-void LayerBase::setSizeChanged(uint32_t w, uint32_t h) {
-}
-
bool LayerBase::setPosition(int32_t x, int32_t y) {
if (mCurrentState.transform.tx() == x && mCurrentState.transform.ty() == y)
return false;
@@ -155,7 +145,6 @@ bool LayerBase::setLayer(uint32_t z) {
bool LayerBase::setSize(uint32_t w, uint32_t h) {
if (mCurrentState.w == w && mCurrentState.h == h)
return false;
- setSizeChanged(w, h);
mCurrentState.w = w;
mCurrentState.h = h;
requestTransaction();
@@ -225,21 +214,14 @@ uint32_t LayerBase::doTransaction(uint32_t flags)
return flags;
}
-Point LayerBase::getPhysicalSize() const
-{
- const Layer::State& front(drawingState());
- return Point(front.w, front.h);
-}
-
void LayerBase::validateVisibility(const Transform& planeTransform)
{
const Layer::State& s(drawingState());
const Transform tr(planeTransform * s.transform);
const bool transformed = tr.transformed();
- const Point size(getPhysicalSize());
- uint32_t w = size.x;
- uint32_t h = size.y;
+ uint32_t w = s.w;
+ uint32_t h = s.h;
tr.transform(mVertices[0], 0, 0);
tr.transform(mVertices[1], 0, h);
tr.transform(mVertices[2], w, h);
@@ -265,43 +247,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform)
mTransformed = transformed;
mLeft = tr.tx();
mTop = tr.ty();
-
- // see if we can/should use 2D h/w with the new configuration
- mCanUseCopyBit = false;
- copybit_device_t* copybit = mFlinger->getBlitEngine();
- if (copybit) {
- const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG);
- const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS);
- mCanUseCopyBit = true;
- if ((mOrientation < 0) && (step > 1)) {
- // arbitrary orientations not supported
- mCanUseCopyBit = false;
- } else if ((mOrientation > 0) && (step > 90)) {
- // 90 deg rotations not supported
- mCanUseCopyBit = false;
- } else if ((tr.getType() & SkMatrix::kScale_Mask) && (scaleBits < 12)) {
- // arbitrary scaling not supported
- mCanUseCopyBit = false;
- }
-#if HONOR_PREMULTIPLIED_ALPHA
- else if (needsBlending() && mPremultipliedAlpha) {
- // pre-multiplied alpha not supported
- mCanUseCopyBit = false;
- }
-#endif
- else {
- // here, we determined we can use copybit
- if (tr.getType() & SkMatrix::kScale_Mask) {
- // and we have scaling
- if (!transparentRegionScreen.isRect()) {
- // we punt because blending is cheap (h/w) and the region is
- // complex, which may causes artifacts when copying
- // scaled content
- transparentRegionScreen.clear();
- }
- }
- }
- }
}
void LayerBase::lockPageFlip(bool& recomputeVisibleRegions)
@@ -329,8 +274,9 @@ void LayerBase::invalidate()
void LayerBase::drawRegion(const Region& reg) const
{
- Region::iterator iterator(reg);
- if (iterator) {
+ Region::const_iterator it = reg.begin();
+ Region::const_iterator const end = reg.end();
+ if (it != end) {
Rect r;
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const int32_t fbWidth = hw.getWidth();
@@ -338,7 +284,8 @@ void LayerBase::drawRegion(const Region& reg) const
const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 },
{ fbWidth, fbHeight }, { 0, fbHeight } };
glVertexPointer(2, GL_SHORT, 0, vertices);
- while (iterator.iterate(&r)) {
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -395,20 +342,24 @@ GLuint LayerBase::createTexture() const
return textureName;
}
-void LayerBase::clearWithOpenGL(const Region& clip) const
+void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red,
+ GLclampx green, GLclampx blue,
+ GLclampx alpha) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight = hw.getHeight();
- glColor4x(0,0,0,0);
+ glColor4x(red,green,blue,alpha);
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
- Rect r;
- Region::iterator iterator(clip);
- if (iterator) {
+
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (it != end) {
glEnable(GL_SCISSOR_TEST);
glVertexPointer(2, GL_FIXED, 0, mVertices);
- while (iterator.iterate(&r)) {
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -416,19 +367,27 @@ void LayerBase::clearWithOpenGL(const Region& clip) const
}
}
-void LayerBase::drawWithOpenGL(const Region& clip,
- GLint textureName, const GGLSurface& t, int transform) const
+void LayerBase::clearWithOpenGL(const Region& clip) const
+{
+ clearWithOpenGL(clip,0,0,0,0);
+}
+
+void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
{
const DisplayHardware& hw(graphicPlane(0).displayHardware());
const uint32_t fbHeight = hw.getHeight();
const State& s(drawingState());
-
+
// bind our texture
- validateTexture(textureName);
+ validateTexture(texture.name);
+ uint32_t width = texture.width;
+ uint32_t height = texture.height;
+
glEnable(GL_TEXTURE_2D);
// Dithering...
- if (s.flags & ISurfaceComposer::eLayerDither) {
+ bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG);
+ if (fast || s.flags & ISurfaceComposer::eLayerDither) {
glEnable(GL_DITHER);
} else {
glDisable(GL_DITHER);
@@ -472,8 +431,9 @@ void LayerBase::drawWithOpenGL(const Region& clip,
|| !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) ))
{
//StopWatch watch("GL transformed");
- Region::iterator iterator(clip);
- if (iterator) {
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (it != end) {
// always use high-quality filtering with fast configurations
bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG);
if (!fast && s.flags & ISurfaceComposer::eLayerFilter) {
@@ -490,21 +450,24 @@ void LayerBase::drawWithOpenGL(const Region& clip,
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
- if (transform == HAL_TRANSFORM_ROT_90) {
+ // the texture's source is rotated
+ if (texture.transform == HAL_TRANSFORM_ROT_90) {
+ // TODO: handle the other orientations
glTranslatef(0, 1, 0);
glRotatef(-90, 0, 0, 1);
}
- if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
+ if (!(mFlags & (DisplayHardware::NPOT_EXTENSION |
+ DisplayHardware::DIRECT_TEXTURE))) {
// find the smallest power-of-two that will accommodate our surface
- GLuint tw = 1 << (31 - clz(t.width));
- GLuint th = 1 << (31 - clz(t.height));
- if (tw < t.width) tw <<= 1;
- if (th < t.height) th <<= 1;
+ GLuint tw = 1 << (31 - clz(width));
+ GLuint th = 1 << (31 - clz(height));
+ if (tw < width) tw <<= 1;
+ if (th < height) th <<= 1;
// this divide should be relatively fast because it's
// a power-of-two (optimized path in libgcc)
- GLfloat ws = GLfloat(t.width) /tw;
- GLfloat hs = GLfloat(t.height)/th;
+ GLfloat ws = GLfloat(width) /tw;
+ GLfloat hs = GLfloat(height)/th;
glScalef(ws, hs, 1.0f);
}
@@ -512,8 +475,8 @@ void LayerBase::drawWithOpenGL(const Region& clip,
glVertexPointer(2, GL_FIXED, 0, mVertices);
glTexCoordPointer(2, GL_FIXED, 0, texCoords);
- Rect r;
- while (iterator.iterate(&r)) {
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -526,18 +489,19 @@ void LayerBase::drawWithOpenGL(const Region& clip,
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
} else {
- Region::iterator iterator(clip);
- if (iterator) {
- Rect r;
- GLint crop[4] = { 0, t.height, t.width, -t.height };
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (it != end) {
+ GLint crop[4] = { 0, height, width, -height };
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
int x = tx();
int y = ty();
- y = fbHeight - (y + t.height);
- while (iterator.iterate(&r)) {
+ y = fbHeight - (y + height);
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
- glDrawTexiOES(x, y, 0, t.width, t.height);
+ glDrawTexiOES(x, y, 0, width, height);
}
}
}
@@ -550,13 +514,16 @@ void LayerBase::validateTexture(GLint textureName) const
// this is currently done in loadTexture() below
}
-void LayerBase::loadTexture(const Region& dirty,
- GLint textureName, const GGLSurface& t,
- GLuint& textureWidth, GLuint& textureHeight) const
+void LayerBase::loadTexture(Texture* texture, GLint textureName,
+ const Region& dirty, const GGLSurface& t) const
{
// TODO: defer the actual texture reload until LayerBase::validateTexture
// is called.
+ texture->name = textureName;
+ GLuint& textureWidth(texture->width);
+ GLuint& textureHeight(texture->height);
+
uint32_t flags = mFlags;
glBindTexture(GL_TEXTURE_2D, textureName);
@@ -565,8 +532,7 @@ void LayerBase::loadTexture(const Region& dirty,
/*
* In OpenGL ES we can't specify a stride with glTexImage2D (however,
- * GL_UNPACK_ALIGNMENT is 4, which in essence allows a limited form of
- * stride).
+ * GL_UNPACK_ALIGNMENT is a limited form of stride).
* So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
* need to do something reasonable (here creating a bigger texture).
*
@@ -579,9 +545,11 @@ void LayerBase::loadTexture(const Region& dirty,
*
* This should never be a problem with POT textures
*/
-
- tw += (((t.stride - tw) * bytesPerPixel(t.format)) / 4);
-
+
+ int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
+ unpack = 1 << ((unpack > 3) ? 3 : unpack);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
+
/*
* round to POT if needed
*/
@@ -594,146 +562,210 @@ void LayerBase::loadTexture(const Region& dirty,
texture_h = 1 << (31 - clz(t.height));
if (texture_w < t.width) texture_w <<= 1;
if (texture_h < t.height) texture_h <<= 1;
- if (texture_w != tw || texture_h != th) {
- // we can't use DIRECT_TEXTURE since we changed the size
- // of the texture
- flags &= ~DisplayHardware::DIRECT_TEXTURE;
- }
}
-
- if (flags & DisplayHardware::DIRECT_TEXTURE) {
- // here we're guaranteed that texture_{w|h} == t{w|h}
- if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
- glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0,
- GL_RGB, tw, th, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t.data);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
- glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0,
- GL_RGBA, tw, th, 0,
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t.data);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
- glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0,
- GL_RGBA, tw, th, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, t.data);
- } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) {
- // TODO: add GL_BGRA extension
- } else {
- // oops, we don't handle this format, try the regular path
- goto regular;
- }
- textureWidth = tw;
- textureHeight = th;
- } else {
+
regular:
- Rect bounds(dirty.bounds());
- GLvoid* data = 0;
- if (texture_w!=textureWidth || texture_h!=textureHeight) {
- // texture size changed, we need to create a new one
-
- if (!textureWidth || !textureHeight) {
- // this is the first time, load the whole texture
- if (texture_w==tw && texture_h==th) {
- // we can do it one pass
- data = t.data;
- } else {
- // we have to create the texture first because it
- // doesn't match the size of the buffer
- bounds.set(Rect(tw, th));
- }
- }
-
- if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGB, texture_w, texture_h, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture_w, texture_h, 0,
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_RGBA, texture_w, texture_h, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, data);
- } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP ||
- t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
- // just show the Y plane of YUV buffers
+ Rect bounds(dirty.bounds());
+ GLvoid* data = 0;
+ if (texture_w!=textureWidth || texture_h!=textureHeight) {
+ // texture size changed, we need to create a new one
+
+ if (!textureWidth || !textureHeight) {
+ // this is the first time, load the whole texture
+ if (texture_w==tw && texture_h==th) {
+ // we can do it one pass
data = t.data;
- glTexImage2D(GL_TEXTURE_2D, 0,
- GL_LUMINANCE, texture_w, texture_h, 0,
- GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
} else {
- // oops, we don't handle this format!
- LOGE("layer %p, texture=%d, using format %d, which is not "
- "supported by the GL", this, textureName, t.format);
- textureName = -1;
+ // we have to create the texture first because it
+ // doesn't match the size of the buffer
+ bounds.set(Rect(tw, th));
}
- textureWidth = texture_w;
- textureHeight = texture_h;
}
- if (!data && textureName>=0) {
- if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
- t.data + bounds.top*t.width*2);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
- t.data + bounds.top*t.width*2);
- } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
- glTexSubImage2D(GL_TEXTURE_2D, 0,
- 0, bounds.top, t.width, bounds.height(),
- GL_RGBA, GL_UNSIGNED_BYTE,
- t.data + bounds.top*t.width*4);
- }
+
+ if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGB, texture_w, texture_h, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
+ } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture_w, texture_h, 0,
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
+ } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_RGBA, texture_w, texture_h, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, data);
+ } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP ||
+ t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
+ // just show the Y plane of YUV buffers
+ glTexImage2D(GL_TEXTURE_2D, 0,
+ GL_LUMINANCE, texture_w, texture_h, 0,
+ GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
+ } else {
+ // oops, we don't handle this format!
+ LOGE("layer %p, texture=%d, using format %d, which is not "
+ "supported by the GL", this, textureName, t.format);
+ textureName = -1;
+ }
+ textureWidth = texture_w;
+ textureHeight = texture_h;
+ }
+ if (!data && textureName>=0) {
+ if (t.format == GGL_PIXEL_FORMAT_RGB_565) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
+ t.data + bounds.top*t.stride*2);
+ } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) {
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride*4);
+ } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP ||
+ t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) {
+ // just show the Y plane of YUV buffers
+ glTexSubImage2D(GL_TEXTURE_2D, 0,
+ 0, bounds.top, t.width, bounds.height(),
+ GL_LUMINANCE, GL_UNSIGNED_BYTE,
+ t.data + bounds.top*t.stride);
}
}
-}
-
-bool LayerBase::canUseCopybit() const
-{
- return mCanUseCopyBit;
}
// ---------------------------------------------------------------------------
+int32_t LayerBaseClient::sIdentity = 0;
+
LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- Client* c, int32_t i)
- : LayerBase(flinger, display), client(c),
- lcblk( c ? &(c->ctrlblk->layers[i]) : 0 ),
- mIndex(i)
+ const sp<Client>& client, int32_t i)
+ : LayerBase(flinger, display), client(client),
+ mIndex(i), mIdentity(uint32_t(android_atomic_inc(&sIdentity)))
{
- if (client) {
- client->bindLayer(this, i);
-
- // Initialize this layer's control block
- memset(this->lcblk, 0, sizeof(layer_cblk_t));
- this->lcblk->identity = mIdentity;
- Region::writeEmpty(&(this->lcblk->region[0]), sizeof(flat_region_t));
- Region::writeEmpty(&(this->lcblk->region[1]), sizeof(flat_region_t));
+}
+
+void LayerBaseClient::onFirstRef()
+{
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
+ client->bindLayer(this, mIndex);
+ // Initialize this layer's identity
+ client->ctrlblk->setIdentity(mIndex, mIdentity);
}
}
LayerBaseClient::~LayerBaseClient()
{
- if (client) {
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
client->free(mIndex);
}
}
-int32_t LayerBaseClient::serverIndex() const {
- if (client) {
+int32_t LayerBaseClient::serverIndex() const
+{
+ sp<Client> client(this->client.promote());
+ if (client != 0) {
return (client->cid<<16)|mIndex;
}
return 0xFFFF0000 | mIndex;
}
-sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() const
+sp<LayerBaseClient::Surface> LayerBaseClient::getSurface()
+{
+ sp<Surface> s;
+ Mutex::Autolock _l(mLock);
+ s = mClientSurface.promote();
+ if (s == 0) {
+ s = createSurface();
+ mClientSurface = s;
+ }
+ return s;
+}
+
+sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const
+{
+ return new Surface(mFlinger, clientIndex(), mIdentity,
+ const_cast<LayerBaseClient *>(this));
+}
+
+// ---------------------------------------------------------------------------
+
+LayerBaseClient::Surface::Surface(
+ const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, int identity,
+ const sp<LayerBaseClient>& owner)
+ : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner)
+{
+}
+
+
+LayerBaseClient::Surface::~Surface()
+{
+ /*
+ * This is a good place to clean-up all client resources
+ */
+
+ // destroy client resources
+ sp<LayerBaseClient> layer = getOwner();
+ if (layer != 0) {
+ mFlinger->destroySurface(layer);
+ }
+}
+
+sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const {
+ sp<LayerBaseClient> owner(mOwner.promote());
+ return owner;
+}
+
+status_t LayerBaseClient::Surface::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case REGISTER_BUFFERS:
+ case UNREGISTER_BUFFERS:
+ case CREATE_OVERLAY:
+ {
+ if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
+ }
+ }
+ }
+ return BnSurface::onTransact(code, data, reply, flags);
+}
+
+sp<SurfaceBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage)
+{
+ return NULL;
+}
+
+status_t LayerBaseClient::Surface::registerBuffers(
+ const ISurface::BufferHeap& buffers)
+{
+ return INVALID_OPERATION;
+}
+
+void LayerBaseClient::Surface::postBuffer(ssize_t offset)
+{
+}
+
+void LayerBaseClient::Surface::unregisterBuffers()
{
- return new Surface(clientIndex(), mIdentity);
}
+sp<OverlayRef> LayerBaseClient::Surface::createOverlay(
+ uint32_t w, uint32_t h, int32_t format)
+{
+ return NULL;
+};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h
index a020f44..78bb4bf 100644
--- a/libs/surfaceflinger/LayerBase.h
+++ b/libs/surfaceflinger/LayerBase.h
@@ -20,8 +20,14 @@
#include <stdint.h>
#include <sys/types.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/LayerState.h>
+#include <utils/RefBase.h>
+
#include <ui/Region.h>
#include <ui/Overlay.h>
@@ -37,10 +43,12 @@ class SurfaceFlinger;
class DisplayHardware;
class GraphicPlane;
class Client;
+class SurfaceBuffer;
+class Buffer;
// ---------------------------------------------------------------------------
-class LayerBase
+class LayerBase : public RefBase
{
// poor man's dynamic_cast below
template<typename T>
@@ -69,10 +77,7 @@ public:
}
- static Vector<GLuint> deletedTextures;
-
LayerBase(SurfaceFlinger* flinger, DisplayID display);
- virtual ~LayerBase();
DisplayID dpy;
mutable bool contentDirty;
@@ -133,11 +138,6 @@ public:
virtual void initStates(uint32_t w, uint32_t h, uint32_t flags);
/**
- * setSizeChanged - called when the *current* state's size is changed.
- */
- virtual void setSizeChanged(uint32_t w, uint32_t h);
-
- /**
* doTransaction - process the transaction. This is a good place to figure
* out which attributes of the surface have changed.
*/
@@ -157,13 +157,6 @@ public:
virtual void setCoveredRegion(const Region& coveredRegion);
/**
- * getPhysicalSize - returns the physical size of the drawing state of
- * the surface. If the surface is backed by a bitmap, this is the size of
- * the bitmap (as opposed to the size of the drawing state).
- */
- virtual Point getPhysicalSize() const;
-
- /**
* validateVisibility - cache a bunch of things
*/
virtual void validateVisibility(const Transform& globalTransform);
@@ -201,21 +194,29 @@ public:
/**
* isSecure - true if this surface is secure, that is if it prevents
- * screenshots or vns servers.
+ * screenshots or VNC servers.
*/
virtual bool isSecure() const { return false; }
- enum { // flags for doTransaction()
- eVisibleRegion = 0x00000002,
- eRestartTransaction = 0x00000008
- };
+ /** signal this layer that it's not needed any longer. called from the
+ * main thread */
+ virtual status_t ditch() { return NO_ERROR; }
+
+
+
+ enum { // flags for doTransaction()
+ eVisibleRegion = 0x00000002,
+ eRestartTransaction = 0x00000008
+ };
inline const State& drawingState() const { return mDrawingState; }
inline const State& currentState() const { return mCurrentState; }
inline State& currentState() { return mCurrentState; }
- static int compareCurrentStateZ(LayerBase*const* layerA, LayerBase*const* layerB) {
+ static int compareCurrentStateZ(
+ sp<LayerBase> const * layerA,
+ sp<LayerBase> const * layerB) {
return layerA[0]->currentState().z - layerB[0]->currentState().z;
}
@@ -229,20 +230,26 @@ protected:
GLuint createTexture() const;
- void drawWithOpenGL(const Region& clip,
- GLint textureName,
- const GGLSurface& surface,
- int transform = 0) const;
-
+ struct Texture {
+ Texture() : name(-1U), width(0), height(0),
+ image(EGL_NO_IMAGE_KHR), transform(0), dirty(true) { }
+ GLuint name;
+ GLuint width;
+ GLuint height;
+ EGLImageKHR image;
+ uint32_t transform;
+ bool dirty;
+ };
+
+ void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g,
+ GLclampx b, GLclampx alpha) const;
void clearWithOpenGL(const Region& clip) const;
+ void drawWithOpenGL(const Region& clip, const Texture& texture) const;
+ void loadTexture(Texture* texture, GLint textureName,
+ const Region& dirty, const GGLSurface& t) const;
- void loadTexture(const Region& dirty,
- GLint textureName, const GGLSurface& t,
- GLuint& textureWidth, GLuint& textureHeight) const;
-
- bool canUseCopybit() const;
- SurfaceFlinger* mFlinger;
+ sp<SurfaceFlinger> mFlinger;
uint32_t mFlags;
// cached during validateVisibility()
@@ -250,7 +257,6 @@ protected:
int32_t mOrientation;
GLfixed mVertices[4][2];
Rect mTransformedBounds;
- bool mCanUseCopyBit;
int mLeft;
int mTop;
@@ -262,16 +268,16 @@ protected:
// don't change, don't need a lock
bool mPremultipliedAlpha;
- // only read
- const uint32_t mIdentity;
-
// atomic
volatile int32_t mInvalidate;
+protected:
+ virtual ~LayerBase();
+
private:
- void validateTexture(GLint textureName) const;
- static int32_t sIdentity;
+ LayerBase(const LayerBase& rhs);
+ void validateTexture(GLint textureName) const;
};
@@ -287,66 +293,62 @@ public:
virtual uint32_t getTypeInfo() const { return typeInfo; }
LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i);
+ const sp<Client>& client, int32_t i);
virtual ~LayerBaseClient();
+ virtual void onFirstRef();
+ wp<Client> client;
+// SharedBufferServer* lcblk;
- Client* const client;
- layer_cblk_t* const lcblk;
-
+ inline uint32_t getIdentity() const { return mIdentity; }
inline int32_t clientIndex() const { return mIndex; }
int32_t serverIndex() const;
- virtual sp<Surface> getSurface() const;
- uint32_t getIdentity() const { return mIdentity; }
+ sp<Surface> getSurface();
+ virtual sp<Surface> createSurface() const;
+
class Surface : public BnSurface
{
public:
- Surface(SurfaceID id, int identity) {
- mParams.token = id;
- mParams.identity = identity;
- }
- Surface(SurfaceID id,
- const sp<IMemoryHeap>& heap0,
- const sp<IMemoryHeap>& heap1,
- int identity)
- {
- mParams.token = id;
- mParams.identity = identity;
- mParams.heap[0] = heap0;
- mParams.heap[1] = heap1;
- }
- virtual ~Surface() {
- // TODO: We now have a point here were we can clean-up the
- // client's mess.
- // This is also where surface id should be recycled.
- //LOGD("Surface %d, heaps={%p, %p} destroyed",
- // mId, mHeap[0].get(), mHeap[1].get());
- }
-
- virtual void getSurfaceData(
- ISurfaceFlingerClient::surface_data_t* params) const {
- *params = mParams;
- }
-
- virtual status_t registerBuffers(const ISurface::BufferHeap& buffers)
- { return INVALID_OPERATION; }
- virtual void postBuffer(ssize_t offset) { }
- virtual void unregisterBuffers() { };
- virtual sp<OverlayRef> createOverlay(
- uint32_t w, uint32_t h, int32_t format) {
- return NULL;
- };
+ int32_t getToken() const { return mToken; }
+ int32_t getIdentity() const { return mIdentity; }
+
+ protected:
+ Surface(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, int identity,
+ const sp<LayerBaseClient>& owner);
+ virtual ~Surface();
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+ sp<LayerBaseClient> getOwner() const;
private:
- ISurfaceFlingerClient::surface_data_t mParams;
+ virtual sp<SurfaceBuffer> requestBuffer(int index, int usage);
+ virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
+ virtual void postBuffer(ssize_t offset);
+ virtual void unregisterBuffers();
+ virtual sp<OverlayRef> createOverlay(uint32_t w, uint32_t h,
+ int32_t format);
+
+ protected:
+ friend class LayerBaseClient;
+ sp<SurfaceFlinger> mFlinger;
+ int32_t mToken;
+ int32_t mIdentity;
+ wp<LayerBaseClient> mOwner;
};
-private:
- int32_t mIndex;
+ friend class Surface;
+private:
+ int32_t mIndex;
+ mutable Mutex mLock;
+ mutable wp<Surface> mClientSurface;
+ // only read
+ const uint32_t mIdentity;
+ static int32_t sIdentity;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp
deleted file mode 100644
index 397ddc8..0000000
--- a/libs/surfaceflinger/LayerBitmap.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/memory.h>
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/IMemory.h>
-#include <ui/PixelFormat.h>
-#include <pixelflinger/pixelflinger.h>
-
-#include "LayerBitmap.h"
-#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-LayerBitmap::LayerBitmap()
- : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2)
-{
- memset(&mSurface, 0, sizeof(mSurface));
-}
-
-LayerBitmap::~LayerBitmap()
-{
- mSurface.data = 0;
-}
-
-status_t LayerBitmap::init(const sp<MemoryDealer>& allocator)
-{
- if (mAllocator != NULL)
- return BAD_VALUE;
- mAllocator = allocator;
- return NO_ERROR;
-}
-
-status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment,
- PixelFormat format, uint32_t flags)
-{
- const sp<MemoryDealer>& allocator(mAllocator);
- if (allocator == NULL)
- return NO_INIT;
-
- if (UNLIKELY(w == mSurface.width && h == mSurface.height &&
- format == mSurface.format))
- { // same format and size, do nothing.
- return NO_ERROR;
- }
-
- PixelFormatInfo info;
- getPixelFormatInfo(format, &info);
-
- uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED;
- const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT
- const uint32_t Bpp = info.bytesPerPixel;
- uint32_t stride = (w + (alignment-1)) & ~(alignment-1);
- stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp;
- size_t size = info.getScanlineSize(stride) * h;
- if (allocFlags & MemoryDealer::PAGE_ALIGNED) {
- size_t pagesize = getpagesize();
- size = (size + (pagesize-1)) & ~(pagesize-1);
- }
-
- /* FIXME: we should be able to have a h/v stride because the user of the
- * surface might have stride limitation (for instance h/w codecs often do)
- */
- int32_t vstride = 0;
-
- mAlignment = alignment;
- mAllocFlags = allocFlags;
- mOffset = 0;
- if (mSize != size) {
- // would be nice to have a reallocate() api
- mBitsMemory.clear(); // free-memory
- mBitsMemory = allocator->allocate(size, allocFlags);
- mSize = size;
- } else {
- // don't erase memory if we didn't have to reallocate
- flags &= ~SECURE_BITS;
- }
- if (mBitsMemory != 0) {
- mOffset = mBitsMemory->offset();
- mSurface.data = static_cast<GGLubyte*>(mBitsMemory->pointer());
- mSurface.version = sizeof(GGLSurface);
- mSurface.width = w;
- mSurface.height = h;
- mSurface.stride = stride;
- mSurface.vstride = vstride;
- mSurface.format = format;
- if (flags & SECURE_BITS)
- clear();
- }
-
- if (mBitsMemory==0 || mSurface.data==0) {
- LOGE("not enough memory for layer bitmap "
- "size=%u (w=%d, h=%d, stride=%d, format=%d)",
- size, int(w), int(h), int(stride), int(format));
- allocator->dump("LayerBitmap");
- mSurface.data = 0;
- mSize = -1U;
- return NO_MEMORY;
- }
- return NO_ERROR;
-}
-
-void LayerBitmap::clear()
-{
- // NOTE: this memset should not be necessary, at least for
- // opaque surface. However, for security reasons it's better to keep it
- // (in the case of pmem, it's possible that the memory contains old
- // data)
- if (mSurface.data) {
- memset(mSurface.data, 0, mSize);
- //if (bytesPerPixel(mSurface.format) == 4) {
- // android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize);
- //} else {
- // android_memset16((uint16_t*)mSurface.data, 0xF800, mSize);
- //}
- }
-}
-
-status_t LayerBitmap::getInfo(surface_info_t* info) const
-{
- if (mSurface.data == 0) {
- memset(info, 0, sizeof(surface_info_t));
- info->bits_offset = NO_MEMORY;
- return NO_MEMORY;
- }
- info->w = uint16_t(width());
- info->h = uint16_t(height());
- info->stride= uint16_t(stride());
- info->bpr = uint16_t(stride() * bytesPerPixel(pixelFormat()));
- info->format= uint8_t(pixelFormat());
- info->flags = surface_info_t::eBufferDirty;
- info->bits_offset = ssize_t(mOffset);
- return NO_ERROR;
-}
-
-status_t LayerBitmap::resize(uint32_t w, uint32_t h)
-{
- int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS);
- return err;
-}
-
-size_t LayerBitmap::size() const
-{
- return mSize;
-}
-
-void LayerBitmap::getBitmapSurface(copybit_image_t* img) const
-{
- const sp<IMemoryHeap>& mh(getAllocator()->getMemoryHeap());
- void* sbase = mh->base();
- const GGLSurface& t(surface());
- img->w = t.stride ?: t.width;
- img->h = t.vstride ?: t.height;
- img->format = t.format;
- img->offset = intptr_t(t.data) - intptr_t(sbase);
- img->base = sbase;
- img->fd = mh->heapID();
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h
deleted file mode 100644
index 9ad64c4..0000000
--- a/libs/surfaceflinger/LayerBitmap.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_BITMAP_H
-#define ANDROID_LAYER_BITMAP_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Atomic.h>
-#include <ui/PixelFormat.h>
-#include <ui/Rect.h>
-#include <private/ui/SharedState.h>
-#include <pixelflinger/pixelflinger.h>
-
-class copybit_image_t;
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class IMemory;
-class MemoryDealer;
-class LayerBitmap;
-
-// ---------------------------------------------------------------------------
-
-class LayerBitmap
-{
-public:
-
- enum {
- // erase memory to ensure security when necessary
- SECURE_BITS = 0x00000001
- };
-
- LayerBitmap();
- ~LayerBitmap();
- status_t init(const sp<MemoryDealer>& allocator);
-
- status_t setBits(uint32_t w, uint32_t h, uint32_t alignment,
- PixelFormat format, uint32_t flags = 0);
- void clear();
-
- status_t getInfo(surface_info_t* info) const;
- status_t resize(uint32_t w, uint32_t h);
-
- const GGLSurface& surface() const { return mSurface; }
- Rect bounds() const { return Rect(width(), height()); }
- uint32_t width() const { return surface().width; }
- uint32_t height() const { return surface().height; }
- uint32_t stride() const { return surface().stride; }
- PixelFormat pixelFormat() const { return surface().format; }
- void* serverBits() const { return surface().data; }
- size_t size() const;
- const sp<MemoryDealer>& getAllocator() const { return mAllocator; }
- void getBitmapSurface(copybit_image_t* img) const;
-
-private:
- sp<MemoryDealer> mAllocator;
- sp<IMemory> mBitsMemory;
- uint32_t mAllocFlags;
- ssize_t mOffset;
- GGLSurface mSurface;
- size_t mSize;
- uint32_t mAlignment;
-};
-
-}; // namespace android
-
-#endif // ANDROID_LAYER_BITMAP_H
diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp
index d3e456f..e14f35b 100644
--- a/libs/surfaceflinger/LayerBlur.cpp
+++ b/libs/surfaceflinger/LayerBlur.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
@@ -26,6 +24,7 @@
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include "clz.h"
#include "BlurFilter.h"
#include "LayerBlur.h"
#include "SurfaceFlinger.h"
@@ -40,17 +39,17 @@ const char* const LayerBlur::typeID = "LayerBlur";
// ---------------------------------------------------------------------------
LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i)
+ const sp<Client>& client, int32_t i)
: LayerBaseClient(flinger, display, client, i), mCacheDirty(true),
- mRefreshCache(true), mCacheAge(0), mTextureName(-1U)
+ mRefreshCache(true), mCacheAge(0), mTextureName(-1U),
+ mWidthScale(1.0f), mHeightScale(1.0f)
{
}
LayerBlur::~LayerBlur()
{
if (mTextureName != -1U) {
- //glDeleteTextures(1, &mTextureName);
- deletedTextures.add(mTextureName);
+ glDeleteTextures(1, &mTextureName);
}
}
@@ -139,8 +138,9 @@ void LayerBlur::onDraw(const Region& clip) const
glGenTextures(1, &mTextureName);
}
- Region::iterator iterator(clip);
- if (iterator) {
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (it != end) {
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, mTextureName);
@@ -166,11 +166,26 @@ void LayerBlur::onDraw(const Region& clip) const
bl.format = GGL_PIXEL_FORMAT_RGB_565;
bl.data = (GGLubyte*)pixels;
blurFilter(&bl, 8, 2);
-
- // NOTE: this works only because we have POT. we'd have to round the
- // texture size up, otherwise.
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
- GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels);
+
+ if (mFlags & (DisplayHardware::NPOT_EXTENSION)) {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels);
+ mWidthScale = 1.0f / w;
+ mHeightScale =-1.0f / h;
+ mYOffset = 0;
+ } else {
+ GLuint tw = 1 << (31 - clz(w));
+ GLuint th = 1 << (31 - clz(h));
+ if (tw < w) tw <<= 1;
+ if (th < h) th <<= 1;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, NULL);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, pixels);
+ mWidthScale = 1.0f / tw;
+ mHeightScale =-1.0f / th;
+ mYOffset = th-h;
+ }
free((void*)pixels);
}
@@ -186,7 +201,12 @@ void LayerBlur::onDraw(const Region& clip) const
glDisable(GL_BLEND);
}
- glDisable(GL_DITHER);
+ if (mFlags & DisplayHardware::SLOW_CONFIG) {
+ glDisable(GL_DITHER);
+ } else {
+ glEnable(GL_DITHER);
+ }
+
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -196,37 +216,34 @@ void LayerBlur::onDraw(const Region& clip) const
// This is a very rare scenario.
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
- glScalef(1.0f/w, -1.0f/h, 1);
- glTranslatef(-x, -y, 0);
+ glScalef(mWidthScale, mHeightScale, 1);
+ glTranslatef(-x, mYOffset - y, 0);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(2, GL_FIXED, 0, mVertices);
glTexCoordPointer(2, GL_FIXED, 0, mVertices);
- Rect r;
- while (iterator.iterate(&r)) {
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = fbHeight - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
} else {
- Region::iterator iterator(clip);
- if (iterator) {
- // NOTE: this is marginally faster with the software gl, because
- // glReadPixels() reads the fb bottom-to-top, however we'll
- // skip all the jaccobian computations.
- Rect r;
- GLint crop[4] = { 0, 0, w, h };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- y = fbHeight - (y + h);
- while (iterator.iterate(&r)) {
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawTexiOES(x, y, 0, w, h);
- }
+ // NOTE: this is marginally faster with the software gl, because
+ // glReadPixels() reads the fb bottom-to-top, however we'll
+ // skip all the jaccobian computations.
+ Rect r;
+ GLint crop[4] = { 0, 0, w, h };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ y = fbHeight - (y + h);
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawTexiOES(x, y, 0, w, h);
}
}
}
-
- glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h
index 24b1156..bf36ae4 100644
--- a/libs/surfaceflinger/LayerBlur.h
+++ b/libs/surfaceflinger/LayerBlur.h
@@ -39,7 +39,7 @@ public:
virtual uint32_t getTypeInfo() const { return typeInfo; }
LayerBlur(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i);
+ const sp<Client>& client, int32_t i);
virtual ~LayerBlur();
virtual void onDraw(const Region& clip) const;
@@ -56,6 +56,9 @@ private:
mutable bool mAutoRefreshPending;
nsecs_t mCacheAge;
mutable GLuint mTextureName;
+ mutable GLfloat mWidthScale;
+ mutable GLfloat mHeightScale;
+ mutable GLfloat mYOffset;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp
index 00fab70..bbfc54b 100644
--- a/libs/surfaceflinger/LayerBuffer.cpp
+++ b/libs/surfaceflinger/LayerBuffer.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
@@ -25,17 +23,18 @@
#include <utils/Log.h>
#include <utils/StopWatch.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-
#include <ui/PixelFormat.h>
-#include <ui/EGLDisplaySurface.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <hardware/copybit.h>
+
+#include "Buffer.h"
+#include "BufferAllocator.h"
#include "LayerBuffer.h"
#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
#include "DisplayHardware/DisplayHardware.h"
+#include "gralloc_priv.h" // needed for msm / copybit
namespace android {
@@ -47,7 +46,7 @@ const char* const LayerBuffer::typeID = "LayerBuffer";
// ---------------------------------------------------------------------------
LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i)
+ const sp<Client>& client, int32_t i)
: LayerBaseClient(flinger, display, client, i),
mNeedsBlending(false)
{
@@ -55,30 +54,24 @@ LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
LayerBuffer::~LayerBuffer()
{
- sp<SurfaceBuffer> s(getClientSurface());
- if (s != 0) {
- s->disown();
- mClientSurface.clear();
- }
}
-sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const
+void LayerBuffer::onFirstRef()
{
- Mutex::Autolock _l(mLock);
- return mClientSurface.promote();
+ LayerBaseClient::onFirstRef();
+ mSurface = new SurfaceLayerBuffer(mFlinger, clientIndex(),
+ const_cast<LayerBuffer *>(this));
}
-sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const
+sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const
{
- sp<SurfaceBuffer> s;
- Mutex::Autolock _l(mLock);
- s = mClientSurface.promote();
- if (s == 0) {
- s = new SurfaceBuffer(clientIndex(),
- const_cast<LayerBuffer *>(this));
- mClientSurface = s;
- }
- return s;
+ return mSurface;
+}
+
+status_t LayerBuffer::ditch()
+{
+ mSurface.clear();
+ return NO_ERROR;
}
bool LayerBuffer::needsBlending() const {
@@ -140,6 +133,14 @@ bool LayerBuffer::transformed() const
return false;
}
+void LayerBuffer::serverDestroy()
+{
+ sp<Source> source(clearSource());
+ if (source != 0) {
+ source->destroy();
+ }
+}
+
/**
* This creates a "buffer" source for this surface
*/
@@ -189,85 +190,52 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() {
}
// ============================================================================
-// LayerBuffer::SurfaceBuffer
+// LayerBuffer::SurfaceLayerBuffer
// ============================================================================
-LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner)
-: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner)
+LayerBuffer::SurfaceLayerBuffer::SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<LayerBuffer>& owner)
+ : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner)
{
}
-LayerBuffer::SurfaceBuffer::~SurfaceBuffer()
+LayerBuffer::SurfaceLayerBuffer::~SurfaceLayerBuffer()
{
unregisterBuffers();
- mOwner = 0;
-}
-
-status_t LayerBuffer::SurfaceBuffer::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case REGISTER_BUFFERS:
- case UNREGISTER_BUFFERS:
- case CREATE_OVERLAY:
- {
- // codes that require permission check
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int self_pid = getpid();
- if (LIKELY(pid != self_pid)) {
- // we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.ACCESS_SURFACE_FLINGER")))
- {
- const int uid = ipc->getCallingUid();
- LOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
- }
- }
- }
- return LayerBaseClient::Surface::onTransact(code, data, reply, flags);
}
-status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers)
+status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers(
+ const ISurface::BufferHeap& buffers)
{
- LayerBuffer* owner(getOwner());
- if (owner)
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
return owner->registerBuffers(buffers);
return NO_INIT;
}
-void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset)
+void LayerBuffer::SurfaceLayerBuffer::postBuffer(ssize_t offset)
{
- LayerBuffer* owner(getOwner());
- if (owner)
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
owner->postBuffer(offset);
}
-void LayerBuffer::SurfaceBuffer::unregisterBuffers()
+void LayerBuffer::SurfaceLayerBuffer::unregisterBuffers()
{
- LayerBuffer* owner(getOwner());
- if (owner)
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
owner->unregisterBuffers();
}
-sp<OverlayRef> LayerBuffer::SurfaceBuffer::createOverlay(
+sp<OverlayRef> LayerBuffer::SurfaceLayerBuffer::createOverlay(
uint32_t w, uint32_t h, int32_t format) {
sp<OverlayRef> result;
- LayerBuffer* owner(getOwner());
- if (owner)
+ sp<LayerBuffer> owner(getOwner());
+ if (owner != 0)
result = owner->createOverlay(w, h, format);
return result;
}
-void LayerBuffer::SurfaceBuffer::disown()
-{
- Mutex::Autolock _l(mLock);
- mOwner = 0;
-}
-
// ============================================================================
// LayerBuffer::Buffer
// ============================================================================
@@ -276,20 +244,30 @@ LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset)
: mBufferHeap(buffers)
{
NativeBuffer& src(mNativeBuffer);
+
src.crop.l = 0;
src.crop.t = 0;
src.crop.r = buffers.w;
src.crop.b = buffers.h;
- src.img.w = buffers.hor_stride ?: buffers.w;
- src.img.h = buffers.ver_stride ?: buffers.h;
- src.img.format = buffers.format;
- src.img.offset = offset;
- src.img.base = buffers.heap->base();
- src.img.fd = buffers.heap->heapID();
+
+ src.img.w = buffers.hor_stride ?: buffers.w;
+ src.img.h = buffers.ver_stride ?: buffers.h;
+ src.img.format = buffers.format;
+ src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset);
+
+ // FIXME: gross hack, we should never access private_handle_t from here,
+ // but this is needed by msm drivers
+ private_handle_t* hnd = new private_handle_t(
+ buffers.heap->heapID(), buffers.heap->getSize(), 0);
+ hnd->offset = offset;
+ src.img.handle = hnd;
}
LayerBuffer::Buffer::~Buffer()
{
+ NativeBuffer& src(mNativeBuffer);
+ if (src.img.handle)
+ delete (private_handle_t*)src.img.handle;
}
// ============================================================================
@@ -323,8 +301,7 @@ bool LayerBuffer::Source::transformed() const {
LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
const ISurface::BufferHeap& buffers)
- : Source(layer), mStatus(NO_ERROR),
- mBufferSize(0), mTextureName(-1U)
+ : Source(layer), mStatus(NO_ERROR), mBufferSize(0)
{
if (buffers.heap == NULL) {
// this is allowed, but in this case, it is illegal to receive
@@ -363,13 +340,21 @@ LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer,
mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0);
mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride;
mLayer.forceVisibilityTransaction();
-
+
+ hw_module_t const* module;
+ mBlitEngine = NULL;
+ if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
+ copybit_open(module, &mBlitEngine);
+ }
}
LayerBuffer::BufferSource::~BufferSource()
{
- if (mTextureName != -1U) {
- LayerBase::deletedTextures.add(mTextureName);
+ if (mTexture.name != -1U) {
+ glDeleteTextures(1, &mTexture.name);
+ }
+ if (mBlitEngine) {
+ copybit_close(mBlitEngine);
}
}
@@ -377,7 +362,7 @@ void LayerBuffer::BufferSource::postBuffer(ssize_t offset)
{
ISurface::BufferHeap buffers;
{ // scope for the lock
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mBufferSourceLock);
buffers = mBufferHeap;
if (buffers.heap != 0) {
const size_t memorySize = buffers.heap->getSize();
@@ -402,7 +387,7 @@ void LayerBuffer::BufferSource::postBuffer(ssize_t offset)
void LayerBuffer::BufferSource::unregisterBuffers()
{
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mBufferSourceLock);
mBufferHeap.heap.clear();
mBuffer.clear();
mLayer.invalidate();
@@ -410,13 +395,13 @@ void LayerBuffer::BufferSource::unregisterBuffers()
sp<LayerBuffer::Buffer> LayerBuffer::BufferSource::getBuffer() const
{
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mBufferSourceLock);
return mBuffer;
}
void LayerBuffer::BufferSource::setBuffer(const sp<LayerBuffer::Buffer>& buffer)
{
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mBufferSourceLock);
mBuffer = buffer;
}
@@ -427,19 +412,19 @@ bool LayerBuffer::BufferSource::transformed() const
void LayerBuffer::BufferSource::onDraw(const Region& clip) const
{
- sp<Buffer> buffer(getBuffer());
- if (UNLIKELY(buffer == 0)) {
+ sp<Buffer> ourBuffer(getBuffer());
+ if (UNLIKELY(ourBuffer == 0)) {
// nothing to do, we don't have a buffer
mLayer.clearWithOpenGL(clip);
return;
}
status_t err = NO_ERROR;
- NativeBuffer src(buffer->getBuffer());
- const Rect& transformedBounds = mLayer.getTransformedBounds();
- const int can_use_copybit = mLayer.canUseCopybit();
+ NativeBuffer src(ourBuffer->getBuffer());
+ const Rect transformedBounds(mLayer.getTransformedBounds());
+ copybit_device_t* copybit = mBlitEngine;
- if (can_use_copybit) {
+ if (copybit) {
const int src_width = src.crop.r - src.crop.l;
const int src_height = src.crop.b - src.crop.t;
int W = transformedBounds.width();
@@ -448,89 +433,110 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
int t(W); W=H; H=t;
}
- /* With LayerBuffer, it is likely that we'll have to rescale the
- * surface, because this is often used for video playback or
- * camera-preview. Since we want these operation as fast as possible
- * we make sure we can use the 2D H/W even if it doesn't support
- * the requested scale factor, in which case we perform the scaling
- * in several passes. */
-
- copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine();
- const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT);
- const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT);
-
- float xscale = 1.0f;
- if (src_width > W*min) xscale = 1.0f / min;
- else if (src_width*mag < W) xscale = mag;
-
- float yscale = 1.0f;
- if (src_height > H*min) yscale = 1.0f / min;
- else if (src_height*mag < H) yscale = mag;
-
- if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) {
- if (UNLIKELY(mTemporaryDealer == 0)) {
- // allocate a memory-dealer for this the first time
- mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager()
- ->createHeap(ISurfaceComposer::eHardware);
- mTempBitmap.init(mTemporaryDealer);
+#ifdef EGL_ANDROID_get_render_buffer
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLSurface draw = eglGetCurrentSurface(EGL_DRAW);
+ EGLClientBuffer clientBuf = eglGetRenderBufferANDROID(dpy, draw);
+ android_native_buffer_t* nb = (android_native_buffer_t*)clientBuf;
+ if (nb == 0) {
+ err = BAD_VALUE;
+ } else {
+ copybit_image_t dst;
+ dst.w = nb->width;
+ dst.h = nb->height;
+ dst.format = nb->format;
+ dst.base = NULL; // unused by copybit on msm7k
+ dst.handle = (native_handle_t *)nb->handle;
+
+ /* With LayerBuffer, it is likely that we'll have to rescale the
+ * surface, because this is often used for video playback or
+ * camera-preview. Since we want these operation as fast as possible
+ * we make sure we can use the 2D H/W even if it doesn't support
+ * the requested scale factor, in which case we perform the scaling
+ * in several passes. */
+
+ const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT);
+ const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT);
+
+ float xscale = 1.0f;
+ if (src_width > W*min) xscale = 1.0f / min;
+ else if (src_width*mag < W) xscale = mag;
+
+ float yscale = 1.0f;
+ if (src_height > H*min) yscale = 1.0f / min;
+ else if (src_height*mag < H) yscale = mag;
+
+ if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) {
+ const int tmp_w = floorf(src_width * xscale);
+ const int tmp_h = floorf(src_height * yscale);
+
+ if (mTempBitmap==0 ||
+ mTempBitmap->getWidth() < size_t(tmp_w) ||
+ mTempBitmap->getHeight() < size_t(tmp_h)) {
+ mTempBitmap.clear();
+ mTempBitmap = new android::Buffer(
+ tmp_w, tmp_h, src.img.format,
+ BufferAllocator::USAGE_HW_2D);
+ err = mTempBitmap->initCheck();
+ }
+
+ if (LIKELY(err == NO_ERROR)) {
+ NativeBuffer tmp;
+ tmp.img.w = tmp_w;
+ tmp.img.h = tmp_h;
+ tmp.img.format = src.img.format;
+ tmp.img.handle = (native_handle_t*)mTempBitmap->getNativeBuffer()->handle;
+ tmp.crop.l = 0;
+ tmp.crop.t = 0;
+ tmp.crop.r = tmp.img.w;
+ tmp.crop.b = tmp.img.h;
+
+ region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b)));
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
+ err = copybit->stretch(copybit,
+ &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it);
+ src = tmp;
+ }
}
- const int tmp_w = floorf(src_width * xscale);
- const int tmp_h = floorf(src_height * yscale);
- err = mTempBitmap.setBits(tmp_w, tmp_h, 1, src.img.format);
-
- if (LIKELY(err == NO_ERROR)) {
- NativeBuffer tmp;
- mTempBitmap.getBitmapSurface(&tmp.img);
- tmp.crop.l = 0;
- tmp.crop.t = 0;
- tmp.crop.r = tmp.img.w;
- tmp.crop.b = tmp.img.h;
-
- region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b)));
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
- err = copybit->stretch(copybit,
- &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it);
- src = tmp;
+ const Rect transformedBounds(mLayer.getTransformedBounds());
+ const copybit_rect_t& drect =
+ reinterpret_cast<const copybit_rect_t&>(transformedBounds);
+ const State& s(mLayer.drawingState());
+ region_iterator it(clip);
+
+ // pick the right orientation for this buffer
+ int orientation = mLayer.getOrientation();
+ if (UNLIKELY(mBufferHeap.transform)) {
+ Transform rot90;
+ GraphicPlane::orientationToTransfrom(
+ ISurfaceComposer::eOrientation90, 0, 0, &rot90);
+ const Transform& planeTransform(mLayer.graphicPlane(0).transform());
+ const Layer::State& s(mLayer.drawingState());
+ Transform tr(planeTransform * s.transform * rot90);
+ orientation = tr.getOrientation();
}
- }
- const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware());
- copybit_image_t dst;
- hw.getDisplaySurface(&dst);
- const copybit_rect_t& drect
- = reinterpret_cast<const copybit_rect_t&>(transformedBounds);
- const State& s(mLayer.drawingState());
- region_iterator it(clip);
-
- // pick the right orientation for this buffer
- int orientation = mLayer.getOrientation();
- if (UNLIKELY(mBufferHeap.transform)) {
- Transform rot90;
- GraphicPlane::orientationToTransfrom(
- ISurfaceComposer::eOrientation90, 0, 0, &rot90);
- const Transform& planeTransform(mLayer.graphicPlane(0).transform());
- const Layer::State& s(mLayer.drawingState());
- Transform tr(planeTransform * s.transform * rot90);
- orientation = tr.getOrientation();
- }
-
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
-
- err = copybit->stretch(copybit,
- &dst, &src.img, &drect, &src.crop, &it);
- if (err != NO_ERROR) {
- LOGE("copybit failed (%s)", strerror(err));
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
+
+ err = copybit->stretch(copybit,
+ &dst, &src.img, &drect, &src.crop, &it);
+ if (err != NO_ERROR) {
+ LOGE("copybit failed (%s)", strerror(err));
+ }
}
}
-
- if (!can_use_copybit || err) {
- if (UNLIKELY(mTextureName == -1LU)) {
- mTextureName = mLayer.createTexture();
+#endif
+
+ if (!copybit || err)
+ {
+ // OpenGL fall-back
+ if (UNLIKELY(mTexture.name == -1LU)) {
+ mTexture.name = mLayer.createTexture();
}
GLuint w = 0;
GLuint h = 0;
@@ -541,10 +547,11 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const
t.stride = src.img.w;
t.vstride= src.img.h;
t.format = src.img.format;
- t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset);
+ t.data = (GGLubyte*)src.img.base;
const Region dirty(Rect(t.width, t.height));
- mLayer.loadTexture(dirty, mTextureName, t, w, h);
- mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform);
+ mLayer.loadTexture(&mTexture, mTexture.name, dirty, t);
+ mTexture.transform = mBufferHeap.transform;
+ mLayer.drawWithOpenGL(clip, mTexture);
}
}
@@ -580,12 +587,11 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
mFormat = overlay->format;
mWidthStride = overlay->w_stride;
mHeightStride = overlay->h_stride;
+ mInitialized = false;
mOverlayHandle = overlay->getHandleRef(overlay);
- // NOTE: here it's okay to acquire a reference to "this"m as long as
- // the reference is not released before we leave the ctor.
- sp<OverlayChannel> channel = new OverlayChannel(this);
+ sp<OverlayChannel> channel = new OverlayChannel( &layer );
*overlayRef = new OverlayRef(mOverlayHandle, channel,
mWidth, mHeight, mFormat, mWidthStride, mHeightStride);
@@ -599,6 +605,14 @@ LayerBuffer::OverlaySource::~OverlaySource()
}
}
+void LayerBuffer::OverlaySource::onDraw(const Region& clip) const
+{
+ GLclampx red = 0;
+ GLclampx green = 0;
+ GLclampx blue = 0x1818;
+ mLayer.clearWithOpenGL(clip, red, green, blue, 0);
+}
+
void LayerBuffer::OverlaySource::onTransaction(uint32_t flags)
{
const Layer::State& front(mLayer.drawingState());
@@ -614,37 +628,33 @@ void LayerBuffer::OverlaySource::onVisibilityResolved(
// this code-path must be as tight as possible, it's called each time
// the screen is composited.
if (UNLIKELY(mOverlay != 0)) {
- if (mVisibilityChanged) {
+ if (mVisibilityChanged || !mInitialized) {
mVisibilityChanged = false;
- const Rect& bounds = mLayer.getTransformedBounds();
+ mInitialized = true;
+ const Rect bounds(mLayer.getTransformedBounds());
int x = bounds.left;
int y = bounds.top;
int w = bounds.width();
int h = bounds.height();
// we need a lock here to protect "destroy"
- Mutex::Autolock _l(mLock);
+ Mutex::Autolock _l(mOverlaySourceLock);
if (mOverlay) {
overlay_control_device_t* overlay_dev = mOverlayDevice;
overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h);
- overlay_dev->setParameter(overlay_dev, mOverlay,
+ overlay_dev->setParameter(overlay_dev, mOverlay,
OVERLAY_TRANSFORM, mLayer.getOrientation());
+ overlay_dev->commit(overlay_dev, mOverlay);
}
}
}
}
-void LayerBuffer::OverlaySource::serverDestroy()
-{
- mLayer.clearSource();
- destroyOverlay();
-}
-
-void LayerBuffer::OverlaySource::destroyOverlay()
+void LayerBuffer::OverlaySource::destroy()
{
// we need a lock here to protect "onVisibilityResolved"
- Mutex::Autolock _l(mLock);
- if (mOverlay) {
+ Mutex::Autolock _l(mOverlaySourceLock);
+ if (mOverlay && mOverlayDevice) {
overlay_control_device_t* overlay_dev = mOverlayDevice;
overlay_dev->destroyOverlay(overlay_dev, mOverlay);
mOverlay = 0;
diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h
index 2dc77f1..0452818 100644
--- a/libs/surfaceflinger/LayerBuffer.h
+++ b/libs/surfaceflinger/LayerBuffer.h
@@ -20,21 +20,23 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <private/ui/LayerState.h>
-#include <EGL/eglnatives.h>
#include "LayerBase.h"
-#include "LayerBitmap.h"
+
+struct copybit_device_t;
namespace android {
// ---------------------------------------------------------------------------
-class MemoryDealer;
+class Buffer;
class Region;
class OverlayRef;
+// ---------------------------------------------------------------------------
+
class LayerBuffer : public LayerBaseClient
{
class Source : public LightRefBase<Source> {
@@ -47,11 +49,11 @@ class LayerBuffer : public LayerBaseClient
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
virtual bool transformed() const;
+ virtual void destroy() { }
protected:
LayerBuffer& mLayer;
};
-
public:
static const uint32_t typeInfo;
static const char* const typeID;
@@ -59,12 +61,14 @@ public:
virtual uint32_t getTypeInfo() const { return typeInfo; }
LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i);
+ const sp<Client>& client, int32_t i);
virtual ~LayerBuffer();
+ virtual void onFirstRef();
virtual bool needsBlending() const;
- virtual sp<LayerBaseClient::Surface> getSurface() const;
+ virtual sp<LayerBaseClient::Surface> createSurface() const;
+ virtual status_t ditch();
virtual void onDraw(const Region& clip) const;
virtual uint32_t doTransaction(uint32_t flags);
virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion);
@@ -78,10 +82,12 @@ public:
sp<Source> getSource() const;
sp<Source> clearSource();
void setNeedsBlending(bool blending);
- const Rect& getTransformedBounds() const {
+ Rect getTransformedBounds() const {
return mTransformedBounds;
}
+ void serverDestroy();
+
private:
struct NativeBuffer {
copybit_image_t img;
@@ -120,15 +126,16 @@ private:
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
virtual bool transformed() const;
+ virtual void destroy() { }
private:
- mutable Mutex mLock;
- sp<Buffer> mBuffer;
- status_t mStatus;
- ISurface::BufferHeap mBufferHeap;
- size_t mBufferSize;
- mutable sp<MemoryDealer> mTemporaryDealer;
- mutable LayerBitmap mTempBitmap;
- mutable GLuint mTextureName;
+ mutable Mutex mBufferSourceLock;
+ sp<Buffer> mBuffer;
+ status_t mStatus;
+ ISurface::BufferHeap mBufferHeap;
+ size_t mBufferSize;
+ mutable sp<android::Buffer> mTempBitmap;
+ mutable LayerBase::Texture mTexture;
+ copybit_device_t* mBlitEngine;
};
class OverlaySource : public Source {
@@ -137,30 +144,26 @@ private:
sp<OverlayRef>* overlayRef,
uint32_t w, uint32_t h, int32_t format);
virtual ~OverlaySource();
+ virtual void onDraw(const Region& clip) const;
virtual void onTransaction(uint32_t flags);
virtual void onVisibilityResolved(const Transform& planeTransform);
+ virtual void destroy();
private:
- void serverDestroy();
- void destroyOverlay();
+
class OverlayChannel : public BnOverlay {
- mutable Mutex mLock;
- sp<OverlaySource> mSource;
+ wp<LayerBuffer> mLayer;
virtual void destroy() {
- sp<OverlaySource> source;
- { // scope for the lock;
- Mutex::Autolock _l(mLock);
- source = mSource;
- mSource.clear();
- }
- if (source != 0) {
- source->serverDestroy();
+ sp<LayerBuffer> layer(mLayer.promote());
+ if (layer != 0) {
+ layer->serverDestroy();
}
}
public:
- OverlayChannel(const sp<OverlaySource>& source)
- : mSource(source) {
+ OverlayChannel(const sp<LayerBuffer>& layer)
+ : mLayer(layer) {
}
};
+
friend class OverlayChannel;
bool mVisibilityChanged;
@@ -172,41 +175,35 @@ private:
int32_t mFormat;
int32_t mWidthStride;
int32_t mHeightStride;
- mutable Mutex mLock;
+ mutable Mutex mOverlaySourceLock;
+ bool mInitialized;
};
- class SurfaceBuffer : public LayerBaseClient::Surface
+ class SurfaceLayerBuffer : public LayerBaseClient::Surface
{
public:
- SurfaceBuffer(SurfaceID id, LayerBuffer* owner);
- virtual ~SurfaceBuffer();
- virtual status_t onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ SurfaceLayerBuffer(const sp<SurfaceFlinger>& flinger,
+ SurfaceID id, const sp<LayerBuffer>& owner);
+ virtual ~SurfaceLayerBuffer();
+
virtual status_t registerBuffers(const ISurface::BufferHeap& buffers);
virtual void postBuffer(ssize_t offset);
virtual void unregisterBuffers();
+
virtual sp<OverlayRef> createOverlay(
uint32_t w, uint32_t h, int32_t format);
- void disown();
private:
- LayerBuffer* getOwner() const {
- Mutex::Autolock _l(mLock);
- return mOwner;
+ sp<LayerBuffer> getOwner() const {
+ return static_cast<LayerBuffer*>(Surface::getOwner().get());
}
- mutable Mutex mLock;
- LayerBuffer* mOwner;
};
- friend class SurfaceFlinger;
- sp<SurfaceBuffer> getClientSurface() const;
-
mutable Mutex mLock;
sp<Source> mSource;
-
+ sp<Surface> mSurface;
bool mInvalidate;
bool mNeedsBlending;
- mutable wp<SurfaceBuffer> mClientSurface;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp
index 0c347cc..8ba0a9d 100644
--- a/libs/surfaceflinger/LayerDim.cpp
+++ b/libs/surfaceflinger/LayerDim.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
@@ -23,9 +21,10 @@
#include <utils/Errors.h>
#include <utils/Log.h>
+#include "Buffer.h"
+#include "BufferAllocator.h"
#include "LayerDim.h"
#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
#include "DisplayHardware/DisplayHardware.h"
namespace android {
@@ -33,27 +32,77 @@ namespace android {
const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10;
const char* const LayerDim::typeID = "LayerDim";
-sp<MemoryDealer> LayerDim::mDimmerDealer;
-LayerBitmap LayerDim::mDimmerBitmap;
+
+bool LayerDim::sUseTexture;
+GLuint LayerDim::sTexId;
+EGLImageKHR LayerDim::sImage;
+int32_t LayerDim::sWidth;
+int32_t LayerDim::sHeight;
// ---------------------------------------------------------------------------
LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i)
- : LayerBaseClient(flinger, display, client, i)
+ const sp<Client>& client, int32_t i)
+ : LayerBaseClient(flinger, display, client, i)
{
}
void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h)
{
- // must only be called once.
- mDimmerDealer = flinger->getSurfaceHeapManager()
- ->createHeap(ISurfaceComposer::eHardware);
- if (mDimmerDealer != 0) {
- mDimmerBitmap.init(mDimmerDealer);
- mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565);
- mDimmerBitmap.clear();
+ sTexId = -1;
+ sImage = EGL_NO_IMAGE_KHR;
+ sWidth = w;
+ sHeight = h;
+ sUseTexture = false;
+
+#ifdef DIM_WITH_TEXTURE
+
+#warning "using a texture to implement LayerDim"
+
+ /* On some h/w like msm7K, it is faster to use a texture because the
+ * software renderer will defer to copybit, for this to work we need to
+ * use an EGLImage texture so copybit can actually make use of it.
+ * This burns a full-screen worth of graphic memory.
+ */
+
+ const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
+ uint32_t flags = hw.getFlags();
+
+ if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) {
+ // TODO: api to pass the usage flags
+ sp<Buffer> buffer = new Buffer(w, h, PIXEL_FORMAT_RGB_565,
+ BufferAllocator::USAGE_SW_WRITE_OFTEN |
+ BufferAllocator::USAGE_HW_TEXTURE);
+
+ android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
+
+ glGenTextures(1, &sTexId);
+ glBindTexture(GL_TEXTURE_2D, sTexId);
+
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0);
+ if (sImage == EGL_NO_IMAGE_KHR) {
+ LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
+ return;
+ }
+
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage);
+ GLint error = glGetError();
+ if (error != GL_NO_ERROR) {
+ eglDestroyImageKHR(dpy, sImage);
+ LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error);
+ return;
+ }
+
+ // initialize the texture with zeros
+ GGLSurface t;
+ buffer->lock(&t, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ memset(t.data, 0, t.stride * t.height * 2);
+ buffer->unlock();
+ sUseTexture = true;
}
+#endif
}
LayerDim::~LayerDim()
@@ -63,49 +112,56 @@ LayerDim::~LayerDim()
void LayerDim::onDraw(const Region& clip) const
{
const State& s(drawingState());
-
- Region::iterator iterator(clip);
- if (s.alpha>0 && iterator) {
+ Region::const_iterator it = clip.begin();
+ Region::const_iterator const end = clip.end();
+ if (s.alpha>0 && (it != end)) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
-
- status_t err = NO_ERROR;
- const int can_use_copybit = canUseCopybit();
- if (can_use_copybit) {
- // StopWatch watch("copybit");
- copybit_image_t dst;
- hw.getDisplaySurface(&dst);
- const copybit_rect_t& drect
- = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds);
-
- copybit_image_t src;
- mDimmerBitmap.getBitmapSurface(&src);
- const copybit_rect_t& srect(drect);
-
- copybit_device_t* copybit = mFlinger->getBlitEngine();
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
- region_iterator it(clip);
- err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
+ const GGLfixed alpha = (s.alpha << 16)/255;
+ const uint32_t fbHeight = hw.getHeight();
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glColor4x(0, 0, 0, alpha);
+
+#ifdef DIM_WITH_TEXTURE
+ if (sUseTexture) {
+ glBindTexture(GL_TEXTURE_2D, sTexId);
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ const GLshort texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { 1, 0 }
+ };
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexCoordPointer(2, GL_SHORT, 0, texCoords);
+ } else
+#endif
+ {
+ glDisable(GL_TEXTURE_2D);
}
- if (!can_use_copybit || err) {
- const GGLfixed alpha = (s.alpha << 16)/255;
- const uint32_t fbHeight = hw.getHeight();
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_DITHER);
- glEnable(GL_BLEND);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
- glColor4x(0, 0, 0, alpha);
- glVertexPointer(2, GL_FIXED, 0, mVertices);
- Rect r;
- while (iterator.iterate(&r)) {
- const GLint sy = fbHeight - (r.top + r.height());
- glScissor(r.left, sy, r.width(), r.height());
- glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
- }
+ GLshort w = sWidth;
+ GLshort h = sHeight;
+ const GLshort vertices[4][2] = {
+ { 0, 0 },
+ { 0, h },
+ { w, h },
+ { w, 0 }
+ };
+ glVertexPointer(2, GL_SHORT, 0, vertices);
+
+ while (it != end) {
+ const Rect& r = *it++;
+ const GLint sy = fbHeight - (r.top + r.height());
+ glScissor(r.left, sy, r.width(), r.height());
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
}
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h
index 3e37a47..d4672a1 100644
--- a/libs/surfaceflinger/LayerDim.h
+++ b/libs/surfaceflinger/LayerDim.h
@@ -20,15 +20,22 @@
#include <stdint.h>
#include <sys/types.h>
-#include "LayerBase.h"
-#include "LayerBitmap.h"
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
-namespace android {
+#include "LayerBase.h"
// ---------------------------------------------------------------------------
+namespace android {
+
class LayerDim : public LayerBaseClient
{
+ static bool sUseTexture;
+ static GLuint sTexId;
+ static EGLImageKHR sImage;
+ static int32_t sWidth;
+ static int32_t sHeight;
public:
static const uint32_t typeInfo;
static const char* const typeID;
@@ -36,7 +43,7 @@ public:
virtual uint32_t getTypeInfo() const { return typeInfo; }
LayerDim(SurfaceFlinger* flinger, DisplayID display,
- Client* client, int32_t i);
+ const sp<Client>& client, int32_t i);
virtual ~LayerDim();
virtual void onDraw(const Region& clip) const;
@@ -44,10 +51,6 @@ public:
virtual bool isSecure() const { return false; }
static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h);
-
-private:
- static sp<MemoryDealer> mDimmerDealer;
- static LayerBitmap mDimmerBitmap;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp
deleted file mode 100644
index 79e5328..0000000
--- a/libs/surfaceflinger/LayerOrientationAnim.cpp
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-#include <utils/StopWatch.h>
-
-#include <core/SkBitmap.h>
-
-#include <ui/EGLDisplaySurface.h>
-
-#include "BlurFilter.h"
-#include "LayerBase.h"
-#include "LayerOrientationAnim.h"
-#include "SurfaceFlinger.h"
-#include "DisplayHardware/DisplayHardware.h"
-#include "OrientationAnimation.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80;
-const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim";
-
-// ---------------------------------------------------------------------------
-
-// Animation...
-const float DURATION = ms2ns(200);
-const float BOUNCES_PER_SECOND = 0.5f;
-const float DIM_TARGET = 0.40f;
-#define INTERPOLATED_TIME(_t) (_t)
-
-// ---------------------------------------------------------------------------
-
-LayerOrientationAnim::LayerOrientationAnim(
- SurfaceFlinger* flinger, DisplayID display,
- OrientationAnimation* anim,
- const LayerBitmap& bitmapIn,
- const LayerBitmap& bitmapOut)
- : LayerOrientationAnimBase(flinger, display), mAnim(anim),
- mBitmapIn(bitmapIn), mBitmapOut(bitmapOut),
- mTextureName(-1), mTextureNameIn(-1)
-{
- // blur that texture.
- mOrientationCompleted = false;
- mNeedsBlending = false;
-}
-
-LayerOrientationAnim::~LayerOrientationAnim()
-{
- if (mTextureName != -1U) {
- LayerBase::deletedTextures.add(mTextureName);
- }
- if (mTextureNameIn != -1U) {
- LayerBase::deletedTextures.add(mTextureNameIn);
- }
-}
-
-bool LayerOrientationAnim::needsBlending() const
-{
- return mNeedsBlending;
-}
-
-Point LayerOrientationAnim::getPhysicalSize() const
-{
- const GraphicPlane& plane(graphicPlane(0));
- const DisplayHardware& hw(plane.displayHardware());
- return Point(hw.getWidth(), hw.getHeight());
-}
-
-void LayerOrientationAnim::validateVisibility(const Transform&)
-{
- const Layer::State& s(drawingState());
- const Transform tr(s.transform);
- const Point size(getPhysicalSize());
- uint32_t w = size.x;
- uint32_t h = size.y;
- mTransformedBounds = tr.makeBounds(w, h);
- mLeft = tr.tx();
- mTop = tr.ty();
- transparentRegionScreen.clear();
- mTransformed = true;
- mCanUseCopyBit = false;
- copybit_device_t* copybit = mFlinger->getBlitEngine();
- if (copybit) {
- mCanUseCopyBit = true;
- }
-}
-
-void LayerOrientationAnim::onOrientationCompleted()
-{
- mAnim->onAnimationFinished();
-}
-
-void LayerOrientationAnim::onDraw(const Region& clip) const
-{
- float alphaIn = DIM_TARGET;
-
- // clear screen
- // TODO: with update on demand, we may be able
- // to not erase the screen at all during the animation
- if (!mOrientationCompleted) {
- glDisable(GL_BLEND);
- glDisable(GL_DITHER);
- glDisable(GL_SCISSOR_TEST);
- glClearColor(0,0,0,0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
-
- copybit_image_t dst;
- const GraphicPlane& plane(graphicPlane(0));
- const DisplayHardware& hw(plane.displayHardware());
- hw.getDisplaySurface(&dst);
-
- copybit_image_t src;
- mBitmapIn.getBitmapSurface(&src);
-
- copybit_image_t srcOut;
- mBitmapOut.getBitmapSurface(&srcOut);
-
- const int w = dst.w;
- const int h = dst.h;
- const int xc = uint32_t(dst.w-w)/2;
- const int yc = uint32_t(dst.h-h)/2;
- const copybit_rect_t drect = { xc, yc, xc+w, yc+h };
- const copybit_rect_t srect = { 0, 0, src.w, src.h };
- const Region reg(Rect( drect.l, drect.t, drect.r, drect.b ));
-
- int err = NO_ERROR;
- const int can_use_copybit = canUseCopybit();
- if (can_use_copybit) {
- copybit_device_t* copybit = mFlinger->getBlitEngine();
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
-
- if (alphaIn > 0) {
- region_iterator it(reg);
- copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_ENABLE);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alphaIn*255));
- err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
- copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_DISABLE);
- }
- LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err));
- }
- if (!can_use_copybit || err) {
- GGLSurface t;
- t.version = sizeof(GGLSurface);
- t.width = src.w;
- t.height = src.h;
- t.stride = src.w;
- t.vstride= src.h;
- t.format = src.format;
- t.data = (GGLubyte*)(intptr_t(src.base) + src.offset);
-
- Transform tr;
- tr.set(xc, yc);
-
- // FIXME: we should not access mVertices and mDrawingState like that,
- // but since we control the animation, we know it's going to work okay.
- // eventually we'd need a more formal way of doing things like this.
- LayerOrientationAnim& self(const_cast<LayerOrientationAnim&>(*this));
- tr.transform(self.mVertices[0], 0, 0);
- tr.transform(self.mVertices[1], 0, src.h);
- tr.transform(self.mVertices[2], src.w, src.h);
- tr.transform(self.mVertices[3], src.w, 0);
- if (!(mFlags & DisplayHardware::SLOW_CONFIG)) {
- // Too slow to do this in software
- self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter;
- }
-
- if (alphaIn > 0.0f) {
- t.data = (GGLubyte*)(intptr_t(src.base) + src.offset);
- if (UNLIKELY(mTextureNameIn == -1LU)) {
- mTextureNameIn = createTexture();
- GLuint w=0, h=0;
- const Region dirty(Rect(t.width, t.height));
- loadTexture(dirty, mTextureNameIn, t, w, h);
- }
- self.mDrawingState.alpha = int(alphaIn*255);
- drawWithOpenGL(reg, mTextureNameIn, t);
- }
- }
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/LayerOrientationAnim.h
deleted file mode 100644
index 12b6f1c..0000000
--- a/libs/surfaceflinger/LayerOrientationAnim.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H
-#define ANDROID_LAYER_ORIENTATION_ANIM_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/threads.h>
-#include <utils/Parcel.h>
-
-#include "LayerBase.h"
-#include "LayerBitmap.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-class OrientationAnimation;
-
-
-class LayerOrientationAnimBase : public LayerBase
-{
-public:
- LayerOrientationAnimBase(SurfaceFlinger* flinger, DisplayID display)
- : LayerBase(flinger, display) {
- }
- virtual void onOrientationCompleted() = 0;
-};
-
-// ---------------------------------------------------------------------------
-
-class LayerOrientationAnim : public LayerOrientationAnimBase
-{
-public:
- static const uint32_t typeInfo;
- static const char* const typeID;
- virtual char const* getTypeID() const { return typeID; }
- virtual uint32_t getTypeInfo() const { return typeInfo; }
-
- LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display,
- OrientationAnimation* anim,
- const LayerBitmap& bitmapIn,
- const LayerBitmap& bitmapOut);
- virtual ~LayerOrientationAnim();
-
- void onOrientationCompleted();
-
- virtual void onDraw(const Region& clip) const;
- virtual Point getPhysicalSize() const;
- virtual void validateVisibility(const Transform& globalTransform);
- virtual bool needsBlending() const;
- virtual bool isSecure() const { return false; }
-private:
- OrientationAnimation* mAnim;
- LayerBitmap mBitmapIn;
- LayerBitmap mBitmapOut;
- bool mOrientationCompleted;
- mutable GLuint mTextureName;
- mutable GLuint mTextureNameIn;
- mutable bool mNeedsBlending;
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_LAYER_ORIENTATION_ANIM_H
diff --git a/libs/surfaceflinger/MessageQueue.cpp b/libs/surfaceflinger/MessageQueue.cpp
new file mode 100644
index 0000000..b43d801
--- /dev/null
+++ b/libs/surfaceflinger/MessageQueue.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/Log.h>
+#include <binder/IPCThreadState.h>
+
+#include "MessageQueue.h"
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+void MessageList::insert(const sp<MessageBase>& node)
+{
+ LIST::iterator cur(mList.begin());
+ LIST::iterator end(mList.end());
+ while (cur != end) {
+ if (*node < **cur) {
+ mList.insert(cur, node);
+ return;
+ }
+ ++cur;
+ }
+ mList.insert(++end, node);
+}
+
+void MessageList::remove(MessageList::LIST::iterator pos)
+{
+ mList.erase(pos);
+}
+
+// ---------------------------------------------------------------------------
+
+MessageQueue::MessageQueue()
+ : mInvalidate(false)
+{
+ mInvalidateMessage = new MessageBase(INVALIDATE);
+}
+
+MessageQueue::~MessageQueue()
+{
+}
+
+MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout)
+{
+ MessageList::value_type result;
+
+ bool again;
+ do {
+ const nsecs_t timeoutTime = systemTime() + timeout;
+ while (true) {
+ Mutex::Autolock _l(mLock);
+ nsecs_t now = systemTime();
+ nsecs_t nextEventTime = -1;
+
+ // invalidate messages are always handled first
+ if (mInvalidate) {
+ mInvalidate = false;
+ mInvalidateMessage->when = now;
+ result = mInvalidateMessage;
+ break;
+ }
+
+ LIST::iterator cur(mMessages.begin());
+ if (cur != mMessages.end()) {
+ result = *cur;
+ }
+
+ if (result != 0) {
+ if (result->when <= now) {
+ // there is a message to deliver
+ mMessages.remove(cur);
+ break;
+ }
+ if (timeout>=0 && timeoutTime < now) {
+ // we timed-out, return a NULL message
+ result = 0;
+ break;
+ }
+ nextEventTime = result->when;
+ result = 0;
+ }
+
+ if (timeout >= 0 && nextEventTime > 0) {
+ if (nextEventTime > timeoutTime) {
+ nextEventTime = timeoutTime;
+ }
+ }
+
+ if (nextEventTime >= 0) {
+ //LOGD("nextEventTime = %lld ms", nextEventTime);
+ if (nextEventTime > 0) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ const nsecs_t reltime = nextEventTime - systemTime();
+ if (reltime > 0) {
+ mCondition.waitRelative(mLock, reltime);
+ }
+ }
+ } else {
+ //LOGD("going to wait");
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ mCondition.wait(mLock);
+ }
+ }
+ // here we're not holding the lock anymore
+
+ if (result == 0)
+ break;
+
+ again = result->handler();
+ if (again) {
+ // the message has been processed. release our reference to it
+ // without holding the lock.
+ result = 0;
+ }
+
+ } while (again);
+
+ return result;
+}
+
+status_t MessageQueue::postMessage(
+ const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+{
+ return queueMessage(message, relTime, flags);
+}
+
+status_t MessageQueue::invalidate() {
+ Mutex::Autolock _l(mLock);
+ mInvalidate = true;
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+status_t MessageQueue::queueMessage(
+ const MessageList::value_type& message, nsecs_t relTime, uint32_t flags)
+{
+ Mutex::Autolock _l(mLock);
+ message->when = systemTime() + relTime;
+ mMessages.insert(message);
+
+ //LOGD("MessageQueue::queueMessage time = %lld ms", message->when);
+ //dumpLocked(message);
+
+ mCondition.signal();
+ return NO_ERROR;
+}
+
+void MessageQueue::dump(const MessageList::value_type& message)
+{
+ Mutex::Autolock _l(mLock);
+ dumpLocked(message);
+}
+
+void MessageQueue::dumpLocked(const MessageList::value_type& message)
+{
+ LIST::const_iterator cur(mMessages.begin());
+ LIST::const_iterator end(mMessages.end());
+ int c = 0;
+ while (cur != end) {
+ const char tick = (*cur == message) ? '>' : ' ';
+ LOGD("%c %d: msg{.what=%08x, when=%lld}",
+ tick, c, (*cur)->what, (*cur)->when);
+ ++cur;
+ c++;
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/libs/surfaceflinger/MessageQueue.h b/libs/surfaceflinger/MessageQueue.h
new file mode 100644
index 0000000..dc8138d
--- /dev/null
+++ b/libs/surfaceflinger/MessageQueue.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_MESSAGE_QUEUE_H
+#define ANDROID_MESSAGE_QUEUE_H
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/threads.h>
+#include <utils/Timers.h>
+#include <utils/List.h>
+
+
+namespace android {
+
+// ---------------------------------------------------------------------------
+
+class MessageBase;
+
+class MessageList
+{
+ List< sp<MessageBase> > mList;
+ typedef List< sp<MessageBase> > LIST;
+public:
+ typedef sp<MessageBase> value_type;
+ inline LIST::iterator begin() { return mList.begin(); }
+ inline LIST::const_iterator begin() const { return mList.begin(); }
+ inline LIST::iterator end() { return mList.end(); }
+ inline LIST::const_iterator end() const { return mList.end(); }
+ inline bool isEmpty() const { return mList.empty(); }
+ void insert(const sp<MessageBase>& node);
+ void remove(LIST::iterator pos);
+};
+
+// ============================================================================
+
+class MessageBase :
+ public LightRefBase<MessageBase>
+{
+public:
+ nsecs_t when;
+ uint32_t what;
+ int32_t arg0;
+
+ MessageBase() : when(0), what(0), arg0(0) { }
+ MessageBase(uint32_t what, int32_t arg0=0)
+ : when(0), what(what), arg0(arg0) { }
+
+ // return true if message has a handler
+ virtual bool handler() { return false; }
+
+protected:
+ virtual ~MessageBase() { }
+
+private:
+ friend class LightRefBase<MessageBase>;
+};
+
+inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) {
+ return lhs.when < rhs.when;
+}
+
+// ---------------------------------------------------------------------------
+
+class MessageQueue
+{
+ typedef List< sp<MessageBase> > LIST;
+public:
+
+ // this is a work-around the multichar constant warning. A macro would
+ // work too, but would pollute the namespace.
+ template <int a, int b, int c, int d>
+ struct WHAT {
+ static const uint32_t Value =
+ (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)|
+ (uint32_t(c&0xff)<<8)|uint32_t(d&0xff);
+ };
+
+ MessageQueue();
+ ~MessageQueue();
+
+ // pre-defined messages
+ enum {
+ INVALIDATE = WHAT<'_','p','d','t'>::Value
+ };
+
+ MessageList::value_type waitMessage(nsecs_t timeout = -1);
+
+ status_t postMessage(const MessageList::value_type& message,
+ nsecs_t reltime=0, uint32_t flags = 0);
+
+ status_t invalidate();
+
+ void dump(const MessageList::value_type& message);
+
+private:
+ status_t queueMessage(const MessageList::value_type& message,
+ nsecs_t reltime, uint32_t flags);
+ void dumpLocked(const MessageList::value_type& message);
+
+ Mutex mLock;
+ Condition mCondition;
+ MessageList mMessages;
+ bool mInvalidate;
+ MessageList::value_type mInvalidateMessage;
+};
+
+// ---------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif /* ANDROID_MESSAGE_QUEUE_H */
diff --git a/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/OrientationAnimation.cpp
deleted file mode 100644
index 12c0eef..0000000
--- a/libs/surfaceflinger/OrientationAnimation.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <limits.h>
-
-#include "LayerOrientationAnim.h"
-#include "OrientationAnimation.h"
-#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
-
-#include "DisplayHardware/DisplayHardware.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-OrientationAnimation::OrientationAnimation(const sp<SurfaceFlinger>& flinger)
- : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE)
-{
- // allocate a memory-dealer for this the first time
- mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap(
- ISurfaceComposer::eHardware);
-}
-
-OrientationAnimation::~OrientationAnimation()
-{
-}
-
-void OrientationAnimation::onOrientationChanged(uint32_t type)
-{
- if (mState == DONE) {
- mType = type;
- if (!(type & ISurfaceComposer::eOrientationAnimationDisable)) {
- mState = PREPARE;
- }
- }
-}
-
-void OrientationAnimation::onAnimationFinished()
-{
- if (mState != DONE)
- mState = FINISH;
-}
-
-bool OrientationAnimation::run_impl()
-{
- bool skip_frame;
- switch (mState) {
- default:
- case DONE:
- skip_frame = done();
- break;
- case PREPARE:
- skip_frame = prepare();
- break;
- case PHASE1:
- skip_frame = phase1();
- break;
- case PHASE2:
- skip_frame = phase2();
- break;
- case FINISH:
- skip_frame = finished();
- break;
- }
- return skip_frame;
-}
-
-bool OrientationAnimation::done()
-{
- return done_impl();
-}
-
-bool OrientationAnimation::prepare()
-{
- mState = PHASE1;
-
- const GraphicPlane& plane(mFlinger->graphicPlane(0));
- const DisplayHardware& hw(plane.displayHardware());
- const uint32_t w = hw.getWidth();
- const uint32_t h = hw.getHeight();
-
- LayerBitmap bitmap;
- bitmap.init(mTemporaryDealer);
- bitmap.setBits(w, h, 1, hw.getFormat());
-
- LayerBitmap bitmapIn;
- bitmapIn.init(mTemporaryDealer);
- bitmapIn.setBits(w, h, 1, hw.getFormat());
-
- copybit_image_t front;
- bitmap.getBitmapSurface(&front);
- hw.copyFrontToImage(front);
-
- LayerOrientationAnimBase* l;
-
- l = new LayerOrientationAnim(
- mFlinger.get(), 0, this, bitmap, bitmapIn);
-
- l->initStates(w, h, 0);
- l->setLayer(INT_MAX-1);
- mFlinger->addLayer(l);
- mLayerOrientationAnim = l;
- return true;
-}
-
-bool OrientationAnimation::phase1()
-{
- if (mFlinger->isFrozen() == false) {
- // start phase 2
- mState = PHASE2;
- mLayerOrientationAnim->onOrientationCompleted();
- mLayerOrientationAnim->invalidate();
- return true;
-
- }
- //mLayerOrientationAnim->invalidate();
- return false;
-}
-
-bool OrientationAnimation::phase2()
-{
- // do the 2nd phase of the animation
- mLayerOrientationAnim->invalidate();
- return false;
-}
-
-bool OrientationAnimation::finished()
-{
- mState = DONE;
- mFlinger->removeLayer(mLayerOrientationAnim);
- mLayerOrientationAnim = NULL;
- return true;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/OrientationAnimation.h
deleted file mode 100644
index cafa38d..0000000
--- a/libs/surfaceflinger/OrientationAnimation.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ORIENTATION_ANIMATION_H
-#define ANDROID_ORIENTATION_ANIMATION_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "SurfaceFlinger.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class SurfaceFlinger;
-class MemoryDealer;
-class LayerOrientationAnim;
-
-class OrientationAnimation
-{
-public:
- OrientationAnimation(const sp<SurfaceFlinger>& flinger);
- virtual ~OrientationAnimation();
-
- void onOrientationChanged(uint32_t type);
- void onAnimationFinished();
- inline bool run() {
- if (LIKELY(mState == DONE))
- return done_impl();
- return run_impl();
- }
-
-private:
- enum {
- DONE = 0,
- PREPARE,
- PHASE1,
- PHASE2,
- FINISH
- };
-
- bool run_impl();
- inline bool done_impl() {
- if (mFlinger->isFrozen()) {
- // we are not allowed to draw, but pause a bit to make sure
- // apps don't end up using the whole CPU, if they depend on
- // surfaceflinger for synchronization.
- usleep(8333); // 8.3ms ~ 120fps
- return true;
- }
- return false;
- }
-
- bool done();
- bool prepare();
- bool phase1();
- bool phase2();
- bool finished();
-
- sp<SurfaceFlinger> mFlinger;
- sp<MemoryDealer> mTemporaryDealer;
- LayerOrientationAnimBase* mLayerOrientationAnim;
- int mState;
- uint32_t mType;
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_ORIENTATION_ANIMATION_H
diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp
index 97dfecc..b368db6 100644
--- a/libs/surfaceflinger/SurfaceFlinger.cpp
+++ b/libs/surfaceflinger/SurfaceFlinger.cpp
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlinger"
-
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
@@ -23,6 +21,7 @@
#include <fcntl.h>
#include <errno.h>
#include <math.h>
+#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
@@ -30,36 +29,30 @@
#include <cutils/log.h>
#include <cutils/properties.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+
#include <utils/String8.h>
#include <utils/String16.h>
#include <utils/StopWatch.h>
#include <ui/PixelFormat.h>
#include <ui/DisplayInfo.h>
-#include <ui/EGLDisplaySurface.h>
#include <pixelflinger/pixelflinger.h>
#include <GLES/gl.h>
#include "clz.h"
-#include "CPUGauge.h"
+#include "Buffer.h"
+#include "BufferAllocator.h"
#include "Layer.h"
#include "LayerBlur.h"
#include "LayerBuffer.h"
#include "LayerDim.h"
-#include "LayerBitmap.h"
-#include "LayerOrientationAnim.h"
-#include "OrientationAnimation.h"
#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
#include "DisplayHardware/DisplayHardware.h"
-#include "GPUHardware/GPUHardware.h"
-
/* ideally AID_GRAPHICS would be in a semi-public header
* or there would be a way to map a user/group name to its id
@@ -93,30 +86,30 @@ SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs)
}
ssize_t SurfaceFlinger::LayerVector::indexOf(
- LayerBase* key, size_t guess) const
+ const sp<LayerBase>& key, size_t guess) const
{
if (guess<size() && lookup.keyAt(guess) == key)
return guess;
const ssize_t i = lookup.indexOfKey(key);
if (i>=0) {
const size_t idx = lookup.valueAt(i);
- LOG_ASSERT(layers[idx]==key,
+ LOGE_IF(layers[idx]!=key,
"LayerVector[%p]: layers[%d]=%p, key=%p",
- this, int(idx), layers[idx], key);
+ this, int(idx), layers[idx].get(), key.get());
return idx;
}
return i;
}
ssize_t SurfaceFlinger::LayerVector::add(
- LayerBase* layer,
- Vector<LayerBase*>::compar_t cmp)
+ const sp<LayerBase>& layer,
+ Vector< sp<LayerBase> >::compar_t cmp)
{
size_t count = layers.size();
ssize_t l = 0;
ssize_t h = count-1;
ssize_t mid;
- LayerBase* const* a = layers.array();
+ sp<LayerBase> const* a = layers.array();
while (l <= h) {
mid = l + (h - l)/2;
const int c = cmp(a+mid, &layer);
@@ -139,14 +132,14 @@ ssize_t SurfaceFlinger::LayerVector::add(
return order;
}
-ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer)
+ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer)
{
const ssize_t keyIndex = lookup.indexOfKey(layer);
if (keyIndex >= 0) {
const size_t index = lookup.valueAt(keyIndex);
- LOG_ASSERT(layers[index]==layer,
+ LOGE_IF(layers[index]!=layer,
"LayerVector[%p]: layers[%u]=%p, layer=%p",
- this, int(index), layers[index], layer);
+ this, int(index), layers[index].get(), layer.get());
layers.removeItemsAt(index);
lookup.removeItemsAt(keyIndex);
const size_t count = lookup.size();
@@ -161,8 +154,8 @@ ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer)
}
ssize_t SurfaceFlinger::LayerVector::reorder(
- LayerBase* layer,
- Vector<LayerBase*>::compar_t cmp)
+ const sp<LayerBase>& layer,
+ Vector< sp<LayerBase> >::compar_t cmp)
{
// XXX: it's a little lame. but oh well...
ssize_t err = remove(layer);
@@ -180,19 +173,23 @@ SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(), Thread(false),
mTransactionFlags(0),
mTransactionCount(0),
+ mResizeTransationPending(false),
+ mLayersRemoved(false),
mBootTime(systemTime()),
- mLastScheduledBroadcast(NULL),
+ mHardwareTest("android.permission.HARDWARE_TEST"),
+ mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
+ mDump("android.permission.DUMP"),
mVisibleRegionsDirty(false),
mDeferReleaseConsole(false),
mFreezeDisplay(false),
mFreezeCount(0),
mFreezeDisplayTime(0),
mDebugRegion(0),
- mDebugCpu(0),
- mDebugFps(0),
mDebugBackground(0),
- mSyncObject(),
- mDeplayedTransactionPending(0),
+ mDebugInSwapBuffers(0),
+ mLastSwapBufferTime(0),
+ mDebugInTransaction(0),
+ mLastTransactionTime(0),
mConsoleSignals(0),
mSecureFrameBuffer(0)
{
@@ -207,28 +204,16 @@ void SurfaceFlinger::init()
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
- property_get("debug.sf.showcpu", value, "0");
- mDebugCpu = atoi(value);
property_get("debug.sf.showbackground", value, "0");
mDebugBackground = atoi(value);
- property_get("debug.sf.showfps", value, "0");
- mDebugFps = atoi(value);
LOGI_IF(mDebugRegion, "showupdates enabled");
- LOGI_IF(mDebugCpu, "showcpu enabled");
LOGI_IF(mDebugBackground, "showbackground enabled");
- LOGI_IF(mDebugFps, "showfps enabled");
}
SurfaceFlinger::~SurfaceFlinger()
{
glDeleteTextures(1, &mWormholeTexName);
- delete mOrientationAnimation;
-}
-
-copybit_device_t* SurfaceFlinger::getBlitEngine() const
-{
- return graphicPlane(0).displayHardware().getBlitEngine();
}
overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const
@@ -236,29 +221,9 @@ overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const
return graphicPlane(0).displayHardware().getOverlayEngine();
}
-sp<IMemory> SurfaceFlinger::getCblk() const
-{
- return mServerCblkMemory;
-}
-
-status_t SurfaceFlinger::requestGPU(const sp<IGPUCallback>& callback,
- gpu_info_t* gpu)
+sp<IMemoryHeap> SurfaceFlinger::getCblk() const
{
- if (mGPU == 0)
- return INVALID_OPERATION;
-
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- status_t err = mGPU->request(pid, callback, gpu);
- return err;
-}
-
-status_t SurfaceFlinger::revokeGPU()
-{
- if (mGPU == 0)
- return INVALID_OPERATION;
-
- return mGPU->friendlyRevoke();
+ return mServerHeap;
}
sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
@@ -266,33 +231,34 @@ sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()
Mutex::Autolock _l(mStateLock);
uint32_t token = mTokens.acquire();
- Client* client = new Client(token, this);
- if ((client == 0) || (client->ctrlblk == 0)) {
+ sp<Client> client = new Client(token, this);
+ if (client->ctrlblk == 0) {
mTokens.release(token);
return 0;
}
status_t err = mClientsMap.add(token, client);
if (err < 0) {
- delete client;
mTokens.release(token);
return 0;
}
sp<BClient> bclient =
- new BClient(this, token, client->controlBlockMemory());
+ new BClient(this, token, client->getControlBlockMemory());
return bclient;
}
void SurfaceFlinger::destroyConnection(ClientID cid)
{
Mutex::Autolock _l(mStateLock);
- Client* const client = mClientsMap.valueFor(cid);
- if (client) {
+ sp<Client> client = mClientsMap.valueFor(cid);
+ if (client != 0) {
// free all the layers this client owns
- const Vector<LayerBaseClient*>& layers = client->getLayers();
+ Vector< wp<LayerBaseClient> > layers(client->getLayers());
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
- LayerBaseClient* const layer = layers[i];
- removeLayer_l(layer);
+ sp<LayerBaseClient> layer(layers[i].promote());
+ if (layer != 0) {
+ purgatorizeLayer_l(layer);
+ }
}
// the resources associated with this client will be freed
@@ -339,41 +305,15 @@ void SurfaceFlinger::onFirstRef()
mReadyToRunBarrier.wait();
}
-
static inline uint16_t pack565(int r, int g, int b) {
return (r<<11)|(g<<5)|b;
}
-// this is defined in libGLES_CM.so
-extern ISurfaceComposer* GLES_localSurfaceManager;
-
status_t SurfaceFlinger::readyToRun()
{
LOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- // create the shared control-block
- mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY);
- LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
-
- mServerCblkMemory = mServerHeap->allocate(4096);
- LOGE_IF(mServerCblkMemory==0, "can't create shared control block");
-
- mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer());
- LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
- new(mServerCblk) surface_flinger_cblk_t;
-
- // get a reference to the GPU if we have one
- mGPU = GPUFactory::getGPU();
-
- // create the surface Heap manager, which manages the heaps
- // (be it in RAM or VRAM) where surfaces are allocated
- // We give 8 MB per client.
- mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20);
-
-
- GLES_localSurfaceManager = static_cast<ISurfaceComposer*>(this);
-
// we only support one display currently
int dpy = 0;
@@ -384,6 +324,16 @@ status_t SurfaceFlinger::readyToRun()
plane.setDisplayHardware(hw);
}
+ // create the shared control-block
+ mServerHeap = new MemoryHeapBase(4096,
+ MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap");
+ LOGE_IF(mServerHeap==0, "can't create shared memory dealer");
+
+ mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());
+ LOGE_IF(mServerCblk==0, "can't get to shared control block's address");
+
+ new(mServerCblk) surface_flinger_cblk_t;
+
// initialize primary screen
// (other display should be initialized in the same manner, but
// asynchronously, as they could come and go. None of this is supported
@@ -450,13 +400,6 @@ status_t SurfaceFlinger::readyToRun()
* We're now ready to accept clients...
*/
- mOrientationAnimation = new OrientationAnimation(this);
-
- // start CPU gauge display
- if (mDebugCpu)
- mCpuGauge = new CPUGauge(this, ms2ns(500));
-
-
// start boot animation
property_set("ctl.start", "bootanim");
@@ -471,45 +414,53 @@ status_t SurfaceFlinger::readyToRun()
void SurfaceFlinger::waitForEvent()
{
- // wait for something to do
- if (UNLIKELY(isFrozen())) {
- // wait 5 seconds
- const nsecs_t freezeDisplayTimeout = ms2ns(5000);
- const nsecs_t now = systemTime();
- if (mFreezeDisplayTime == 0) {
- mFreezeDisplayTime = now;
+ while (true) {
+ nsecs_t timeout = -1;
+ if (UNLIKELY(isFrozen())) {
+ // wait 5 seconds
+ const nsecs_t freezeDisplayTimeout = ms2ns(5000);
+ const nsecs_t now = systemTime();
+ if (mFreezeDisplayTime == 0) {
+ mFreezeDisplayTime = now;
+ }
+ nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime);
+ timeout = waitTime>0 ? waitTime : 0;
}
- nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime);
- int err = (waitTime > 0) ? mSyncObject.wait(waitTime) : TIMED_OUT;
- if (err != NO_ERROR) {
+
+ MessageList::value_type msg = mEventQueue.waitMessage(timeout);
+ if (msg != 0) {
+ mFreezeDisplayTime = 0;
+ switch (msg->what) {
+ case MessageQueue::INVALIDATE:
+ // invalidate message, just return to the main loop
+ return;
+ }
+ } else {
+ // we timed out
if (isFrozen()) {
// we timed out and are still frozen
LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d",
mFreezeDisplay, mFreezeCount);
mFreezeCount = 0;
mFreezeDisplay = false;
+ return;
}
}
- } else {
- mFreezeDisplayTime = 0;
- mSyncObject.wait();
}
}
void SurfaceFlinger::signalEvent() {
- mSyncObject.open();
+ mEventQueue.invalidate();
}
void SurfaceFlinger::signal() const {
- mSyncObject.open();
+ // this is the IPC call
+ const_cast<SurfaceFlinger*>(this)->signalEvent();
}
void SurfaceFlinger::signalDelayedEvent(nsecs_t delay)
{
- if (android_atomic_or(1, &mDeplayedTransactionPending) == 0) {
- sp<DelayedTransaction> delayedEvent(new DelayedTransaction(this, delay));
- delayedEvent->run("DelayedeEvent", PRIORITY_URGENT_DISPLAY);
- }
+ mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay);
}
// ----------------------------------------------------------------------------
@@ -546,18 +497,11 @@ bool SurfaceFlinger::threadLoop()
// release the clients before we flip ('cause flip might block)
unlockClients();
- executeScheduledBroadcasts();
-
- // sample the cpu gauge
- if (UNLIKELY(mDebugCpu)) {
- handleDebugCpu();
- }
postFramebuffer();
} else {
// pretend we did the post
unlockClients();
- executeScheduledBroadcasts();
usleep(16667); // 60 fps period
}
return true;
@@ -565,28 +509,22 @@ bool SurfaceFlinger::threadLoop()
void SurfaceFlinger::postFramebuffer()
{
- const bool skip = mOrientationAnimation->run();
- if (UNLIKELY(skip)) {
+ if (isFrozen()) {
+ // we are not allowed to draw, but pause a bit to make sure
+ // apps don't end up using the whole CPU, if they depend on
+ // surfaceflinger for synchronization.
+ usleep(8333); // 8.3ms ~ 120fps
return;
}
if (!mInvalidRegion.isEmpty()) {
const DisplayHardware& hw(graphicPlane(0).displayHardware());
-
- if (UNLIKELY(mDebugFps)) {
- debugShowFPS();
- }
-
+ const nsecs_t now = systemTime();
+ mDebugInSwapBuffers = now;
hw.flip(mInvalidRegion);
-
+ mLastSwapBufferTime = systemTime() - now;
+ mDebugInSwapBuffers = 0;
mInvalidRegion.clear();
-
- if (Layer::deletedTextures.size()) {
- glDeleteTextures(
- Layer::deletedTextures.size(),
- Layer::deletedTextures.array());
- Layer::deletedTextures.clear();
- }
}
}
@@ -601,15 +539,13 @@ void SurfaceFlinger::handleConsoleEvents()
}
if (mDeferReleaseConsole && hw.canDraw()) {
- // We got the release signal before the aquire signal
+ // We got the release signal before the acquire signal
mDeferReleaseConsole = false;
- revokeGPU();
hw.releaseScreen();
}
if (what & eConsoleReleased) {
if (hw.canDraw()) {
- revokeGPU();
hw.releaseScreen();
} else {
mDeferReleaseConsole = true;
@@ -621,9 +557,31 @@ void SurfaceFlinger::handleConsoleEvents()
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
- Mutex::Autolock _l(mStateLock);
+ Vector< sp<LayerBase> > ditchedLayers;
+
+ { // scope for the lock
+ Mutex::Autolock _l(mStateLock);
+ const nsecs_t now = systemTime();
+ mDebugInTransaction = now;
+ handleTransactionLocked(transactionFlags, ditchedLayers);
+ mLastTransactionTime = systemTime() - now;
+ mDebugInTransaction = 0;
+ }
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
+ // do this without lock held
+ const size_t count = ditchedLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ if (ditchedLayers[i] != 0) {
+ //LOGD("ditching layer %p", ditchedLayers[i].get());
+ ditchedLayers[i]->ditch();
+ }
+ }
+}
+
+void SurfaceFlinger::handleTransactionLocked(
+ uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers)
+{
+ const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
const size_t count = currentLayers.size();
/*
@@ -634,7 +592,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
const bool layersNeedTransaction = transactionFlags & eTraversalNeeded;
if (layersNeedTransaction) {
for (size_t i=0 ; i<count ; i++) {
- LayerBase* const layer = currentLayers[i];
+ const sp<LayerBase>& layer = currentLayers[i];
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) continue;
@@ -681,7 +639,6 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
mVisibleRegionsDirty = true;
mDirtyRegion.set(hw.bounds());
mFreezeDisplayTime = 0;
- mOrientationAnimation->onOrientationChanged(type);
}
if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) {
@@ -689,22 +646,25 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
mFreezeDisplay = mCurrentState.freezeDisplay;
}
- // some layers might have been removed, so
- // we need to update the regions they're exposing.
- const SortedVector<LayerBase*>& removedLayers(mRemovedLayers);
- size_t c = removedLayers.size();
- if (c) {
+ if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
+ // layers have been added
mVisibleRegionsDirty = true;
- while (c--) {
- mDirtyRegionRemovedLayer.orSelf(
- removedLayers[c]->visibleRegionScreen);
- }
}
- const LayerVector& currentLayers = mCurrentState.layersSortedByZ;
- if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) {
- // layers have been added
+ // some layers might have been removed, so
+ // we need to update the regions they're exposing.
+ if (mLayersRemoved) {
mVisibleRegionsDirty = true;
+ const LayerVector& previousLayers(mDrawingState.layersSortedByZ);
+ const size_t count = previousLayers.size();
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<LayerBase>& layer(previousLayers[i]);
+ if (currentLayers.indexOf( layer ) < 0) {
+ // this layer is not visible anymore
+ ditchedLayers.add(layer);
+ mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);
+ }
+ }
}
// get rid of all resources we don't need anymore
@@ -734,7 +694,7 @@ void SurfaceFlinger::computeVisibleRegions(
size_t i = currentLayers.size();
while (i--) {
- LayerBase* const layer = currentLayers[i];
+ const sp<LayerBase>& layer = currentLayers[i];
layer->validateVisibility(planeTransform);
// start with the whole surface at its current location
@@ -785,7 +745,7 @@ void SurfaceFlinger::computeVisibleRegions(
// accumulate to the screen dirty region
dirtyRegion.orSelf(dirty);
- // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
+ // Update aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer
aboveOpaqueLayers.orSelf(opaqueRegion);
aboveCoveredLayers.orSelf(visibleRegion);
@@ -811,7 +771,8 @@ void SurfaceFlinger::computeVisibleRegions(
void SurfaceFlinger::commitTransaction()
{
mDrawingState = mCurrentState;
- mTransactionCV.signal();
+ mResizeTransationPending = false;
+ mTransactionCV.broadcast();
}
void SurfaceFlinger::handlePageFlip()
@@ -837,9 +798,9 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
{
bool recomputeVisibleRegions = false;
size_t count = currentLayers.size();
- LayerBase* const* layers = currentLayers.array();
+ sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
- LayerBase* const layer = layers[i];
+ const sp<LayerBase>& layer = layers[i];
layer->lockPageFlip(recomputeVisibleRegions);
}
return recomputeVisibleRegions;
@@ -850,37 +811,58 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers)
const GraphicPlane& plane(graphicPlane(0));
const Transform& planeTransform(plane.transform());
size_t count = currentLayers.size();
- LayerBase* const* layers = currentLayers.array();
+ sp<LayerBase> const* layers = currentLayers.array();
for (size_t i=0 ; i<count ; i++) {
- LayerBase* const layer = layers[i];
+ const sp<LayerBase>& layer = layers[i];
layer->unlockPageFlip(planeTransform, mDirtyRegion);
}
}
+
void SurfaceFlinger::handleRepaint()
{
- // set the frame buffer
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
+ // compute the invalid region
+ mInvalidRegion.orSelf(mDirtyRegion);
+ if (mInvalidRegion.isEmpty()) {
+ // nothing to do
+ return;
+ }
if (UNLIKELY(mDebugRegion)) {
debugFlashRegions();
}
- // compute the invalid region
- mInvalidRegion.orSelf(mDirtyRegion);
+ // set the frame buffer
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
uint32_t flags = hw.getFlags();
- if (flags & DisplayHardware::BUFFER_PRESERVED) {
- // here we assume DisplayHardware::flip()'s implementation
- // performs the copy-back optimization.
+ if ((flags & DisplayHardware::SWAP_RECTANGLE) ||
+ (flags & DisplayHardware::BUFFER_PRESERVED))
+ {
+ // we can redraw only what's dirty, but since SWAP_RECTANGLE only
+ // takes a rectangle, we must make sure to update that whole
+ // rectangle in that case
+ if (flags & DisplayHardware::SWAP_RECTANGLE) {
+ // FIXME: we really should be able to pass a region to
+ // SWAP_RECTANGLE so that we don't have to redraw all this.
+ mDirtyRegion.set(mInvalidRegion.bounds());
+ } else {
+ // in the BUFFER_PRESERVED case, obviously, we can update only
+ // what's needed and nothing more.
+ // NOTE: this is NOT a common case, as preserving the backbuffer
+ // is costly and usually involves copying the whole update back.
+ }
} else {
if (flags & DisplayHardware::UPDATE_ON_DEMAND) {
- // we need to fully redraw the part that will be updated
+ // We need to redraw the rectangle that will be updated
+ // (pushed to the framebuffer).
+ // This is needed because UPDATE_ON_DEMAND only takes one
+ // rectangle instead of a region (see DisplayHardware::flip())
mDirtyRegion.set(mInvalidRegion.bounds());
} else {
- // we need to redraw everything
+ // we need to redraw everything (the whole screen)
mDirtyRegion.set(hw.bounds());
mInvalidRegion = mDirtyRegion;
}
@@ -903,9 +885,9 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty)
const SurfaceFlinger& flinger(*this);
const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
- LayerBase const* const* const layers = drawingLayers.array();
+ sp<LayerBase> const* const layers = drawingLayers.array();
for (size_t i=0 ; i<count ; ++i) {
- LayerBase const * const layer = layers[i];
+ const sp<LayerBase>& layer = layers[i];
const Region& visibleRegion(layer->visibleRegionScreen);
if (!visibleRegion.isEmpty()) {
const Region clip(dirty.intersect(visibleRegion));
@@ -920,67 +902,42 @@ void SurfaceFlinger::unlockClients()
{
const LayerVector& drawingLayers(mDrawingState.layersSortedByZ);
const size_t count = drawingLayers.size();
- LayerBase* const* const layers = drawingLayers.array();
+ sp<LayerBase> const* const layers = drawingLayers.array();
for (size_t i=0 ; i<count ; ++i) {
- LayerBase* const layer = layers[i];
+ const sp<LayerBase>& layer = layers[i];
layer->finishPageFlip();
}
}
-void SurfaceFlinger::scheduleBroadcast(Client* client)
-{
- if (mLastScheduledBroadcast != client) {
- mLastScheduledBroadcast = client;
- mScheduledBroadcasts.add(client);
- }
-}
-
-void SurfaceFlinger::executeScheduledBroadcasts()
-{
- SortedVector<Client*>& list = mScheduledBroadcasts;
- size_t count = list.size();
- while (count--) {
- per_client_cblk_t* const cblk = list[count]->ctrlblk;
- if (cblk->lock.tryLock() == NO_ERROR) {
- cblk->cv.broadcast();
- list.removeAt(count);
- cblk->lock.unlock();
- } else {
- // schedule another round
- LOGW("executeScheduledBroadcasts() skipped, "
- "contention on the client. We'll try again later...");
- signalDelayedEvent(ms2ns(4));
- }
- }
- mLastScheduledBroadcast = 0;
-}
-
-void SurfaceFlinger::handleDebugCpu()
-{
- Mutex::Autolock _l(mDebugLock);
- if (mCpuGauge != 0)
- mCpuGauge->sample();
-}
-
void SurfaceFlinger::debugFlashRegions()
{
- if (UNLIKELY(!mDirtyRegion.isRect())) {
- // TODO: do this only if we don't have preserving
- // swapBuffer. If we don't have update-on-demand,
- // redraw everything.
- composeSurfaces(Region(mDirtyRegion.bounds()));
- }
+ const DisplayHardware& hw(graphicPlane(0).displayHardware());
+ const uint32_t flags = hw.getFlags();
+ if (!((flags & DisplayHardware::SWAP_RECTANGLE) ||
+ (flags & DisplayHardware::BUFFER_PRESERVED))) {
+ const Region repaint((flags & DisplayHardware::UPDATE_ON_DEMAND) ?
+ mDirtyRegion.bounds() : hw.bounds());
+ composeSurfaces(repaint);
+ }
+
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
- glColor4x(0x10000, 0, 0x10000, 0x10000);
+ static int toggle = 0;
+ toggle = 1 - toggle;
+ if (toggle) {
+ glColor4x(0x10000, 0, 0x10000, 0x10000);
+ } else {
+ glColor4x(0x10000, 0x10000, 0, 0x10000);
+ }
- Rect r;
- Region::iterator iterator(mDirtyRegion);
- while (iterator.iterate(&r)) {
+ Region::const_iterator it = mDirtyRegion.begin();
+ Region::const_iterator const end = mDirtyRegion.end();
+ while (it != end) {
+ const Rect& r = *it++;
GLfloat vertices[][2] = {
{ r.left, r.top },
{ r.left, r.bottom },
@@ -990,10 +947,12 @@ void SurfaceFlinger::debugFlashRegions()
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
-
- const DisplayHardware& hw(graphicPlane(0).displayHardware());
- hw.flip(mDirtyRegion.merge(mInvalidRegion));
- mInvalidRegion.clear();
+
+ if (mInvalidRegion.isEmpty()) {
+ mDirtyRegion.dump("mDirtyRegion");
+ mInvalidRegion.dump("mInvalidRegion");
+ }
+ hw.flip(mInvalidRegion);
if (mDebugRegion > 1)
usleep(mDebugRegion * 1000);
@@ -1017,9 +976,10 @@ void SurfaceFlinger::drawWormhole() const
if (LIKELY(!mDebugBackground)) {
glClearColorx(0,0,0,0);
- Rect r;
- Region::iterator iterator(region);
- while (iterator.iterate(&r)) {
+ Region::const_iterator it = region.begin();
+ Region::const_iterator const end = region.end();
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = height - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glClear(GL_COLOR_BUFFER_BIT);
@@ -1037,9 +997,10 @@ void SurfaceFlinger::drawWormhole() const
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1);
- Rect r;
- Region::iterator iterator(region);
- while (iterator.iterate(&r)) {
+ Region::const_iterator it = region.begin();
+ Region::const_iterator const end = region.end();
+ while (it != end) {
+ const Rect& r = *it++;
const GLint sy = height - (r.top + r.height());
glScissor(r.left, sy, r.width(), r.height());
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
@@ -1065,7 +1026,7 @@ void SurfaceFlinger::debugShowFPS() const
// XXX: mFPS has the value we want
}
-status_t SurfaceFlinger::addLayer(LayerBase* layer)
+status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
addLayer_l(layer);
@@ -1073,91 +1034,78 @@ status_t SurfaceFlinger::addLayer(LayerBase* layer)
return NO_ERROR;
}
-status_t SurfaceFlinger::removeLayer(LayerBase* layer)
+status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer)
{
Mutex::Autolock _l(mStateLock);
- removeLayer_l(layer);
- setTransactionFlags(eTransactionNeeded);
- return NO_ERROR;
+ status_t err = purgatorizeLayer_l(layer);
+ if (err == NO_ERROR)
+ setTransactionFlags(eTransactionNeeded);
+ return err;
}
-status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer)
+status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer)
{
layer->forceVisibilityTransaction();
setTransactionFlags(eTraversalNeeded);
return NO_ERROR;
}
-status_t SurfaceFlinger::addLayer_l(LayerBase* layer)
+status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer)
{
+ if (layer == 0)
+ return BAD_VALUE;
ssize_t i = mCurrentState.layersSortedByZ.add(
layer, &LayerBase::compareCurrentStateZ);
- LayerBaseClient* lbc = LayerBase::dynamicCast<LayerBaseClient*>(layer);
- if (lbc) {
+ sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ if (lbc != 0) {
mLayerMap.add(lbc->serverIndex(), lbc);
}
- mRemovedLayers.remove(layer);
return NO_ERROR;
}
-status_t SurfaceFlinger::removeLayer_l(LayerBase* layerBase)
+status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase)
{
ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase);
if (index >= 0) {
- mRemovedLayers.add(layerBase);
- LayerBaseClient* layer = LayerBase::dynamicCast<LayerBaseClient*>(layerBase);
- if (layer) {
+ mLayersRemoved = true;
+ sp<LayerBaseClient> layer =
+ LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get());
+ if (layer != 0) {
mLayerMap.removeItem(layer->serverIndex());
}
return NO_ERROR;
}
+ return status_t(index);
+}
+
+status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase)
+{
+ // remove the layer from the main list (through a transaction).
+ ssize_t err = removeLayer_l(layerBase);
+
// it's possible that we don't find a layer, because it might
// have been destroyed already -- this is not technically an error
- // from the user because there is a race between destroySurface,
- // destroyclient and destroySurface-from-a-transaction.
- return (index == NAME_NOT_FOUND) ? status_t(NO_ERROR) : index;
+ // from the user because there is a race between BClient::destroySurface(),
+ // ~BClient() and ~ISurface().
+ return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err;
}
+
void SurfaceFlinger::free_resources_l()
{
// Destroy layers that were removed
- destroy_all_removed_layers_l();
-
+ mLayersRemoved = false;
+
// free resources associated with disconnected clients
- SortedVector<Client*>& scheduledBroadcasts(mScheduledBroadcasts);
- Vector<Client*>& disconnectedClients(mDisconnectedClients);
+ Vector< sp<Client> >& disconnectedClients(mDisconnectedClients);
const size_t count = disconnectedClients.size();
for (size_t i=0 ; i<count ; i++) {
- Client* client = disconnectedClients[i];
- // if this client is the scheduled broadcast list,
- // remove it from there (and we don't need to signal it
- // since it is dead).
- int32_t index = scheduledBroadcasts.indexOf(client);
- if (index >= 0) {
- scheduledBroadcasts.removeItemsAt(index);
- }
+ sp<Client> client = disconnectedClients[i];
mTokens.release(client->cid);
- delete client;
}
disconnectedClients.clear();
}
-void SurfaceFlinger::destroy_all_removed_layers_l()
-{
- size_t c = mRemovedLayers.size();
- while (c--) {
- LayerBase* const removed_layer = mRemovedLayers[c];
-
- LOGE_IF(mCurrentState.layersSortedByZ.indexOf(removed_layer) >= 0,
- "layer %p removed but still in the current state list",
- removed_layer);
-
- delete removed_layer;
- }
- mRemovedLayers.clear();
-}
-
-
uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags)
{
return android_atomic_and(~flags, &mTransactionFlags) & flags;
@@ -1185,6 +1133,13 @@ void SurfaceFlinger::closeGlobalTransaction()
{
if (android_atomic_dec(&mTransactionCount) == 1) {
signalEvent();
+
+ // if there is a transaction with a resize, wait for it to
+ // take effect before returning.
+ Mutex::Autolock _l(mStateLock);
+ while (mResizeTransationPending) {
+ mTransactionCV.wait(mStateLock);
+ }
}
}
@@ -1198,7 +1153,7 @@ status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags)
setTransactionFlags(eTransactionNeeded);
// flags is intended to communicate some sort of animation behavior
- // (for instance fadding)
+ // (for instance fading)
return NO_ERROR;
}
@@ -1212,7 +1167,7 @@ status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags)
setTransactionFlags(eTransactionNeeded);
// flags is intended to communicate some sort of animation behavior
- // (for instance fadding)
+ // (for instance fading)
return NO_ERROR;
}
@@ -1241,7 +1196,7 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
- LayerBaseClient* layer = 0;
+ sp<LayerBaseClient> layer;
sp<LayerBaseClient::Surface> surfaceHandle;
if (int32_t(w|h) < 0) {
@@ -1251,14 +1206,14 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
}
Mutex::Autolock _l(mStateLock);
- Client* const c = mClientsMap.valueFor(clientId);
- if (UNLIKELY(!c)) {
+ sp<Client> client = mClientsMap.valueFor(clientId);
+ if (UNLIKELY(client == 0)) {
LOGE("createSurface() failed, client not found (id=%d)", clientId);
return surfaceHandle;
}
//LOGD("createSurface for pid %d (%d x %d)", pid, w, h);
- int32_t id = c->generateId(pid);
+ int32_t id = client->generateId(pid);
if (uint32_t(id) >= NUM_LAYERS_MAX) {
LOGE("createSurface() failed, generateId = %d", id);
return surfaceHandle;
@@ -1267,32 +1222,40 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,
switch (flags & eFXSurfaceMask) {
case eFXSurfaceNormal:
if (UNLIKELY(flags & ePushBuffers)) {
- layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags);
+ layer = createPushBuffersSurfaceLocked(client, d, id,
+ w, h, flags);
} else {
- layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags);
+ layer = createNormalSurfaceLocked(client, d, id,
+ w, h, flags, format);
}
break;
case eFXSurfaceBlur:
- layer = createBlurSurfaceLocked(c, d, id, w, h, flags);
+ layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
break;
case eFXSurfaceDim:
- layer = createDimSurfaceLocked(c, d, id, w, h, flags);
+ layer = createDimSurfaceLocked(client, d, id, w, h, flags);
break;
}
- if (layer) {
+ if (layer != 0) {
setTransactionFlags(eTransactionNeeded);
surfaceHandle = layer->getSurface();
- if (surfaceHandle != 0)
- surfaceHandle->getSurfaceData(params);
+ if (surfaceHandle != 0) {
+ params->token = surfaceHandle->getToken();
+ params->identity = surfaceHandle->getIdentity();
+ params->width = w;
+ params->height = h;
+ params->format = format;
+ }
}
return surfaceHandle;
}
-LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked(
- Client* client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)
+sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ PixelFormat& format)
{
// initialize the surfaces
switch (format) { // TODO: take h/w into account
@@ -1305,57 +1268,99 @@ LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked(
break;
}
- Layer* layer = new Layer(this, display, client, id);
- status_t err = layer->setBuffers(client, w, h, format, flags);
+ sp<Layer> layer = new Layer(this, display, client, id);
+ status_t err = layer->setBuffers(w, h, format, flags);
if (LIKELY(err == NO_ERROR)) {
layer->initStates(w, h, flags);
addLayer_l(layer);
} else {
LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));
- delete layer;
- return 0;
+ layer.clear();
}
return layer;
}
-LayerBaseClient* SurfaceFlinger::createBlurSurfaceLocked(
- Client* client, DisplayID display,
+sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags)
{
- LayerBlur* layer = new LayerBlur(this, display, client, id);
+ sp<LayerBlur> layer = new LayerBlur(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
return layer;
}
-LayerBaseClient* SurfaceFlinger::createDimSurfaceLocked(
- Client* client, DisplayID display,
+sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags)
{
- LayerDim* layer = new LayerDim(this, display, client, id);
+ sp<LayerDim> layer = new LayerDim(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
return layer;
}
-LayerBaseClient* SurfaceFlinger::createPushBuffersSurfaceLocked(
- Client* client, DisplayID display,
+sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags)
{
- LayerBuffer* layer = new LayerBuffer(this, display, client, id);
+ sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id);
layer->initStates(w, h, flags);
addLayer_l(layer);
return layer;
}
-status_t SurfaceFlinger::destroySurface(SurfaceID index)
+status_t SurfaceFlinger::removeSurface(SurfaceID index)
{
+ /*
+ * called by the window manager, when a surface should be marked for
+ * destruction.
+ *
+ * The surface is removed from the current and drawing lists, but placed
+ * in the purgatory queue, so it's not destroyed right-away (we need
+ * to wait for all client's references to go away first).
+ */
+
Mutex::Autolock _l(mStateLock);
- LayerBaseClient* const layer = getLayerUser_l(index);
- status_t err = removeLayer_l(layer);
- if (err < 0)
- return err;
- setTransactionFlags(eTransactionNeeded);
+ sp<LayerBaseClient> layer = getLayerUser_l(index);
+ status_t err = purgatorizeLayer_l(layer);
+ if (err == NO_ERROR) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+ return err;
+}
+
+status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer)
+{
+ // called by ~ISurface() when all references are gone
+
+ class MessageDestroySurface : public MessageBase {
+ SurfaceFlinger* flinger;
+ sp<LayerBaseClient> layer;
+ public:
+ MessageDestroySurface(
+ SurfaceFlinger* flinger, const sp<LayerBaseClient>& layer)
+ : flinger(flinger), layer(layer) { }
+ virtual bool handler() {
+ sp<LayerBaseClient> l(layer);
+ layer.clear(); // clear it outside of the lock;
+ Mutex::Autolock _l(flinger->mStateLock);
+ /*
+ * remove the layer from the current list -- chances are that it's
+ * not in the list anyway, because it should have been removed
+ * already upon request of the client (eg: window manager).
+ * However, a buggy client could have not done that.
+ * Since we know we don't have any more clients, we don't need
+ * to use the purgatory.
+ */
+ status_t err = flinger->removeLayer_l(l);
+ LOGE_IF(err<0 && err != NAME_NOT_FOUND,
+ "error removing layer=%p (%s)", l.get(), strerror(-err));
+ return true;
+ }
+ };
+
+ mEventQueue.postMessage( new MessageDestroySurface(this, layer) );
return NO_ERROR;
}
@@ -1369,18 +1374,9 @@ status_t SurfaceFlinger::setClientState(
cid <<= 16;
for (int i=0 ; i<count ; i++) {
const layer_state_t& s = states[i];
- LayerBaseClient* layer = getLayerUser_l(s.surface | cid);
- if (layer) {
+ sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid));
+ if (layer != 0) {
const uint32_t what = s.what;
- // check if it has been destroyed first
- if (what & eDestroyed) {
- if (removeLayer_l(layer) == NO_ERROR) {
- flags |= eTransactionNeeded;
- // we skip everything else... well, no, not really
- // we skip ONLY that transaction.
- continue;
- }
- }
if (what & ePositionChanged) {
if (layer->setPosition(s.x, s.y))
flags |= eTraversalNeeded;
@@ -1395,8 +1391,10 @@ status_t SurfaceFlinger::setClientState(
}
}
if (what & eSizeChanged) {
- if (layer->setSize(s.w, s.h))
+ if (layer->setSize(s.w, s.h)) {
flags |= eTraversalNeeded;
+ mResizeTransationPending = true;
+ }
}
if (what & eAlphaChanged) {
if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
@@ -1422,9 +1420,10 @@ status_t SurfaceFlinger::setClientState(
return NO_ERROR;
}
-LayerBaseClient* SurfaceFlinger::getLayerUser_l(SurfaceID s) const
+sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const
{
- return mLayerMap.valueFor(s);
+ sp<LayerBaseClient> layer = mLayerMap.valueFor(s);
+ return layer;
}
void SurfaceFlinger::screenReleased(int dpy)
@@ -1446,20 +1445,40 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
const size_t SIZE = 1024;
char buffer[SIZE];
String8 result;
- if (checkCallingPermission(
- String16("android.permission.DUMP")) == false)
- { // not allowed
+ if (!mDump.checkCalling()) {
snprintf(buffer, SIZE, "Permission Denial: "
"can't dump SurfaceFlinger from pid=%d, uid=%d\n",
IPCThreadState::self()->getCallingPid(),
IPCThreadState::self()->getCallingUid());
result.append(buffer);
} else {
- Mutex::Autolock _l(mStateLock);
+
+ // figure out if we're stuck somewhere
+ const nsecs_t now = systemTime();
+ const nsecs_t inSwapBuffers(mDebugInSwapBuffers);
+ const nsecs_t inTransaction(mDebugInTransaction);
+ nsecs_t inSwapBuffersDuration = (inSwapBuffers) ? now-inSwapBuffers : 0;
+ nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0;
+
+ // Try to get the main lock, but don't insist if we can't
+ // (this would indicate SF is stuck, but we want to be able to
+ // print something in dumpsys).
+ int retry = 3;
+ while (mStateLock.tryLock()<0 && --retry>=0) {
+ usleep(1000000);
+ }
+ const bool locked(retry >= 0);
+ if (!locked) {
+ snprintf(buffer, SIZE,
+ "SurfaceFlinger appears to be unresponsive, "
+ "dumping anyways (no locks held)\n");
+ result.append(buffer);
+ }
+
size_t s = mClientsMap.size();
char name[64];
for (size_t i=0 ; i<s ; i++) {
- Client* client = mClientsMap.valueAt(i);
+ sp<Client> client = mClientsMap.valueAt(i);
sprintf(name, " Client (id=0x%08x)", client->cid);
client->dump(name);
}
@@ -1467,7 +1486,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
const size_t count = currentLayers.size();
for (size_t i=0 ; i<count ; i++) {
/*** LayerBase ***/
- LayerBase const * const layer = currentLayers[i];
+ const sp<LayerBase>& layer = currentLayers[i];
const Layer::State& s = layer->drawingState();
snprintf(buffer, SIZE,
"+ %s %p\n"
@@ -1475,7 +1494,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
"z=%9d, pos=(%4d,%4d), size=(%4d,%4d), "
"needsBlending=%1d, invalidate=%1d, "
"alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n",
- layer->getTypeID(), layer,
+ layer->getTypeID(), layer.get(),
s.z, layer->tx(), layer->ty(), s.w, s.h,
layer->needsBlending(), layer->contentDirty,
s.alpha, s.flags,
@@ -1484,34 +1503,36 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
result.append(buffer);
buffer[0] = 0;
/*** LayerBaseClient ***/
- LayerBaseClient* const lbc =
- LayerBase::dynamicCast<LayerBaseClient*>((LayerBase*)layer);
- if (lbc) {
+ sp<LayerBaseClient> lbc =
+ LayerBase::dynamicCast< LayerBaseClient* >(layer.get());
+ if (lbc != 0) {
+ sp<Client> client(lbc->client.promote());
snprintf(buffer, SIZE,
" "
"id=0x%08x, client=0x%08x, identity=%u\n",
- lbc->clientIndex(), lbc->client ? lbc->client->cid : 0,
+ lbc->clientIndex(), client.get() ? client->cid : 0,
lbc->getIdentity());
+
+ result.append(buffer);
+ buffer[0] = 0;
}
- result.append(buffer);
- buffer[0] = 0;
/*** Layer ***/
- Layer* const l = LayerBase::dynamicCast<Layer*>((LayerBase*)layer);
- if (l) {
- const LayerBitmap& buf0(l->getBuffer(0));
- const LayerBitmap& buf1(l->getBuffer(1));
+ sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get());
+ if (l != 0) {
+ result.append( l->lcblk->dump(" ") );
+ sp<const Buffer> buf0(l->getBuffer(0));
+ sp<const Buffer> buf1(l->getBuffer(1));
snprintf(buffer, SIZE,
" "
- "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u], mTextureName=%d,"
- " freezeLock=%p, swapState=0x%08x\n",
+ "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u],"
+ " freezeLock=%p\n",
l->pixelFormat(),
- buf0.width(), buf0.height(), buf0.stride(),
- buf1.width(), buf1.height(), buf1.stride(),
- l->getTextureName(), l->getFreezeLock().get(),
- l->lcblk->swapState);
+ buf0->getWidth(), buf0->getHeight(), buf0->getStride(),
+ buf1->getWidth(), buf1->getHeight(), buf1->getStride(),
+ l->getFreezeLock().get());
+ result.append(buffer);
+ buffer[0] = 0;
}
- result.append(buffer);
- buffer[0] = 0;
s.transparentRegion.dump(result, "transparentRegion");
layer->transparentRegionScreen.dump(result, "transparentRegionScreen");
layer->visibleRegionScreen.dump(result, "visibleRegionScreen");
@@ -1523,19 +1544,28 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args)
mFreezeDisplay?"yes":"no", mFreezeCount,
mCurrentState.orientation, hw.canDraw());
result.append(buffer);
-
- sp<AllocatorInterface> allocator;
- if (mGPU != 0) {
- snprintf(buffer, SIZE, " GPU owner: %d\n", mGPU->getOwner());
+ snprintf(buffer, SIZE,
+ " last eglSwapBuffers() time: %f us\n"
+ " last transaction time : %f us\n",
+ mLastSwapBufferTime/1000.0, mLastTransactionTime/1000.0);
+ result.append(buffer);
+ if (inSwapBuffersDuration || !locked) {
+ snprintf(buffer, SIZE, " eglSwapBuffers time: %f us\n",
+ inSwapBuffersDuration/1000.0);
result.append(buffer);
- allocator = mGPU->getAllocator();
- if (allocator != 0) {
- allocator->dump(result, "GPU Allocator");
- }
}
- allocator = mSurfaceHeapManager->getAllocator(NATIVE_MEMORY_TYPE_PMEM);
- if (allocator != 0) {
- allocator->dump(result, "PMEM Allocator");
+ if (inTransactionDuration || !locked) {
+ snprintf(buffer, SIZE, " transaction time: %f us\n",
+ inTransactionDuration/1000.0);
+ result.append(buffer);
+ }
+ snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size());
+ result.append(buffer);
+ const BufferAllocator& alloc(BufferAllocator::get());
+ alloc.dump(result);
+
+ if (locked) {
+ mStateLock.unlock();
}
}
write(fd, result.string(), result.size());
@@ -1553,57 +1583,34 @@ status_t SurfaceFlinger::onTransact(
case FREEZE_DISPLAY:
case UNFREEZE_DISPLAY:
case BOOT_FINISHED:
- case REVOKE_GPU:
{
// codes that require permission check
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
const int uid = ipc->getCallingUid();
- const int self_pid = getpid();
- if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) {
- // we're called from a different process, do the real check
- if (!checkCallingPermission(
- String16("android.permission.ACCESS_SURFACE_FLINGER")))
- {
- LOGE("Permission Denial: "
- "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
- return PERMISSION_DENIED;
- }
+ if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) {
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
+ return PERMISSION_DENIED;
}
}
}
-
status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags);
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
- // HARDWARE_TEST stuff...
- if (UNLIKELY(checkCallingPermission(
- String16("android.permission.HARDWARE_TEST")) == false))
- { // not allowed
- LOGE("Permission Denial: pid=%d, uid=%d\n",
- IPCThreadState::self()->getCallingPid(),
- IPCThreadState::self()->getCallingUid());
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ if (UNLIKELY(!mHardwareTest.checkCalling())) {
+ IPCThreadState* ipc = IPCThreadState::self();
+ const int pid = ipc->getCallingPid();
+ const int uid = ipc->getCallingUid();
+ LOGE("Permission Denial: "
+ "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid);
return PERMISSION_DENIED;
}
int n;
switch (code) {
- case 1000: // SHOW_CPU
- n = data.readInt32();
- mDebugCpu = n ? 1 : 0;
- if (mDebugCpu) {
- if (mCpuGauge == 0) {
- mCpuGauge = new CPUGauge(this, ms2ns(500));
- }
- } else {
- if (mCpuGauge != 0) {
- mCpuGauge->requestExitAndWait();
- Mutex::Autolock _l(mDebugLock);
- mCpuGauge.clear();
- }
- }
+ case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE
return NO_ERROR;
- case 1001: // SHOW_FPS
- n = data.readInt32();
- mDebugFps = n ? 1 : 0;
+ case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE
return NO_ERROR;
case 1002: // SHOW_UPDATES
n = data.readInt32();
@@ -1618,23 +1625,17 @@ status_t SurfaceFlinger::onTransact(
const DisplayHardware& hw(graphicPlane(0).displayHardware());
mDirtyRegion.set(hw.bounds()); // careful that's not thread-safe
signalEvent();
- }
- return NO_ERROR;
- case 1005: // ask GPU revoke
- if (mGPU != 0) {
- mGPU->friendlyRevoke();
- }
return NO_ERROR;
- case 1006: // revoke GPU
- if (mGPU != 0) {
- mGPU->unconditionalRevoke();
- }
+ }
+ case 1005:{ // force transaction
+ setTransactionFlags(eTransactionNeeded|eTraversalNeeded);
return NO_ERROR;
+ }
case 1007: // set mFreezeCount
mFreezeCount = data.readInt32();
return NO_ERROR;
case 1010: // interrogate.
- reply->writeInt32(mDebugCpu);
+ reply->writeInt32(0);
reply->writeInt32(0);
reply->writeInt32(mDebugRegion);
reply->writeInt32(mDebugBackground);
@@ -1658,30 +1659,24 @@ status_t SurfaceFlinger::onTransact(
Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)
: ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)
{
- mSharedHeapAllocator = getSurfaceHeapManager()->createHeap();
const int pgsize = getpagesize();
- const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1));
- mCblkHeap = new MemoryDealer(cblksize);
- mCblkMemory = mCblkHeap->allocate(cblksize);
- if (mCblkMemory != 0) {
- ctrlblk = static_cast<per_client_cblk_t *>(mCblkMemory->pointer());
- if (ctrlblk) { // construct the shared structure in-place.
- new(ctrlblk) per_client_cblk_t;
- }
+ const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));
+
+ mCblkHeap = new MemoryHeapBase(cblksize, 0,
+ "SurfaceFlinger Client control-block");
+
+ ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());
+ if (ctrlblk) { // construct the shared structure in-place.
+ new(ctrlblk) SharedClient;
}
}
Client::~Client() {
if (ctrlblk) {
- const int pgsize = getpagesize();
- ctrlblk->~per_client_cblk_t(); // destroy our shared-structure.
+ ctrlblk->~SharedClient(); // destroy our shared-structure.
}
}
-const sp<SurfaceHeapManager>& Client::getSurfaceHeapManager() const {
- return mFlinger->getSurfaceHeapManager();
-}
-
int32_t Client::generateId(int pid)
{
const uint32_t i = clz( ~mBitmap );
@@ -1693,13 +1688,15 @@ int32_t Client::generateId(int pid)
mBitmap |= 1<<(31-i);
return i;
}
-status_t Client::bindLayer(LayerBaseClient* layer, int32_t id)
+
+status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)
{
ssize_t idx = mInUse.indexOf(id);
if (idx < 0)
return NAME_NOT_FOUND;
return mLayers.insertAt(layer, idx);
}
+
void Client::free(int32_t id)
{
ssize_t idx = mInUse.remove(uint8_t(id));
@@ -1709,27 +1706,18 @@ void Client::free(int32_t id)
}
}
-sp<MemoryDealer> Client::createAllocator(uint32_t flags)
-{
- sp<MemoryDealer> allocator;
- allocator = getSurfaceHeapManager()->createHeap(
- flags, getClientPid(), mSharedHeapAllocator);
- return allocator;
-}
-
bool Client::isValid(int32_t i) const {
return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i)));
}
-const uint8_t* Client::inUseArray() const {
- return mInUse.array();
-}
-size_t Client::numActiveLayers() const {
- return mInUse.size();
-}
-LayerBaseClient* Client::getLayerUser(int32_t i) const {
+
+sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {
+ sp<LayerBaseClient> lbc;
ssize_t idx = mInUse.indexOf(uint8_t(i));
- if (idx<0) return 0;
- return mLayers[idx];
+ if (idx >= 0) {
+ lbc = mLayers[idx].promote();
+ LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx));
+ }
+ return lbc;
}
void Client::dump(const char* what)
@@ -1741,7 +1729,7 @@ void Client::dump(const char* what)
#pragma mark -
#endif
-BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemory>& cblk)
+BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)
: mId(cid), mFlinger(flinger), mCblk(cblk)
{
}
@@ -1751,8 +1739,8 @@ BClient::~BClient() {
mFlinger->destroyConnection(mId);
}
-void BClient::getControlBlocks(sp<IMemory>* ctrl) const {
- *ctrl = mCblk;
+sp<IMemoryHeap> BClient::getControlBlock() const {
+ return mCblk;
}
sp<ISurface> BClient::createSurface(
@@ -1766,7 +1754,7 @@ sp<ISurface> BClient::createSurface(
status_t BClient::destroySurface(SurfaceID sid)
{
sid |= (mId << 16); // add the client-part to id
- return mFlinger->destroySurface(sid);
+ return mFlinger->removeSurface(sid);
}
status_t BClient::setState(int32_t count, const layer_state_t* states)
@@ -1866,6 +1854,10 @@ const Transform& GraphicPlane::transform() const {
return mGlobalTransform;
}
+EGLDisplay GraphicPlane::getEGLDisplay() const {
+ return mHw->getEGLDisplay();
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h
index 0d63e1d..f207f85 100644
--- a/libs/surfaceflinger/SurfaceFlinger.h
+++ b/libs/surfaceflinger/SurfaceFlinger.h
@@ -25,21 +25,24 @@
#include <utils/threads.h>
#include <utils/Atomic.h>
#include <utils/Errors.h>
-#include <utils/MemoryDealer.h>
+#include <utils/RefBase.h>
+
+#include <binder/IMemory.h>
+#include <binder/Permission.h>
#include <ui/PixelFormat.h>
#include <ui/ISurfaceComposer.h>
#include <ui/ISurfaceFlingerClient.h>
-#include <private/ui/SharedState.h>
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/LayerState.h>
-#include <private/ui/SurfaceFlingerSynchro.h>
#include "Barrier.h"
-#include "CPUGauge.h"
#include "Layer.h"
#include "Tokenizer.h"
+#include "MessageQueue.h"
+
struct copybit_device_t;
struct overlay_device_t;
@@ -51,13 +54,8 @@ class Client;
class BClient;
class DisplayHardware;
class FreezeLock;
-class GPUHardwareInterface;
-class IGPUCallback;
class Layer;
class LayerBuffer;
-class LayerOrientationAnim;
-class OrientationAnimation;
-class SurfaceHeapManager;
typedef int32_t ClientID;
@@ -66,7 +64,7 @@ typedef int32_t ClientID;
// ---------------------------------------------------------------------------
-class Client
+class Client : public RefBase
{
public:
Client(ClientID cid, const sp<SurfaceFlinger>& flinger);
@@ -74,35 +72,34 @@ public:
int32_t generateId(int pid);
void free(int32_t id);
- status_t bindLayer(LayerBaseClient* layer, int32_t id);
- sp<MemoryDealer> createAllocator(uint32_t memory_type);
+ status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id);
inline bool isValid(int32_t i) const;
- inline const uint8_t* inUseArray() const;
- inline size_t numActiveLayers() const;
- LayerBaseClient* getLayerUser(int32_t i) const;
- const Vector<LayerBaseClient*>& getLayers() const { return mLayers; }
- const sp<IMemory>& controlBlockMemory() const { return mCblkMemory; }
+ sp<LayerBaseClient> getLayerUser(int32_t i) const;
void dump(const char* what);
- const sp<SurfaceHeapManager>& getSurfaceHeapManager() const;
+
+ const Vector< wp<LayerBaseClient> >& getLayers() const {
+ return mLayers;
+ }
+
+ const sp<IMemoryHeap>& getControlBlockMemory() const {
+ return mCblkHeap;
+ }
// pointer to this client's control block
- per_client_cblk_t* ctrlblk;
+ SharedClient* ctrlblk;
ClientID cid;
private:
- int getClientPid() const { return mPid; }
+ int getClientPid() const { return mPid; }
- int mPid;
- uint32_t mBitmap;
- SortedVector<uint8_t> mInUse;
- Vector<LayerBaseClient*> mLayers;
- sp<MemoryDealer> mCblkHeap;
- sp<SurfaceFlinger> mFlinger;
- sp<MemoryDealer> mSharedHeapAllocator;
- sp<MemoryDealer> mPMemAllocator;
- sp<IMemory> mCblkMemory;
+ int mPid;
+ uint32_t mBitmap;
+ SortedVector<uint8_t> mInUse;
+ Vector< wp<LayerBaseClient> > mLayers;
+ sp<IMemoryHeap> mCblkHeap;
+ sp<SurfaceFlinger> mFlinger;
};
// ---------------------------------------------------------------------------
@@ -125,6 +122,8 @@ public:
const DisplayHardware& displayHardware() const;
const Transform& transform() const;
+ EGLDisplay getEGLDisplay() const;
+
private:
GraphicPlane(const GraphicPlane&);
GraphicPlane operator = (const GraphicPlane&);
@@ -160,7 +159,7 @@ public:
// ISurfaceComposer interface
virtual sp<ISurfaceFlingerClient> createConnection();
- virtual sp<IMemory> getCblk() const;
+ virtual sp<IMemoryHeap> getCblk() const;
virtual void bootFinished();
virtual void openGlobalTransaction();
virtual void closeGlobalTransaction();
@@ -168,56 +167,52 @@ public:
virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags);
virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags);
virtual void signal() const;
- virtual status_t requestGPU(const sp<IGPUCallback>& callback,
- gpu_info_t* gpu);
- virtual status_t revokeGPU();
void screenReleased(DisplayID dpy);
void screenAcquired(DisplayID dpy);
- const sp<SurfaceHeapManager>& getSurfaceHeapManager() const {
- return mSurfaceHeapManager;
- }
-
- const sp<GPUHardwareInterface>& getGPU() const {
- return mGPU;
- }
-
- copybit_device_t* getBlitEngine() const;
overlay_control_device_t* getOverlayEngine() const;
- status_t removeLayer(LayerBase* layer);
- status_t addLayer(LayerBase* layer);
- status_t invalidateLayerVisibility(LayerBase* layer);
+ status_t removeLayer(const sp<LayerBase>& layer);
+ status_t addLayer(const sp<LayerBase>& layer);
+ status_t invalidateLayerVisibility(const sp<LayerBase>& layer);
private:
friend class BClient;
friend class LayerBase;
friend class LayerBuffer;
friend class LayerBaseClient;
+ friend class LayerBaseClient::Surface;
friend class Layer;
friend class LayerBlur;
+ friend class LayerDim;
sp<ISurface> createSurface(ClientID client, int pid,
ISurfaceFlingerClient::surface_data_t* params,
DisplayID display, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags);
- LayerBaseClient* createNormalSurfaceLocked(Client* client, DisplayID display,
- int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags);
+ sp<LayerBaseClient> createNormalSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
+ int32_t id, uint32_t w, uint32_t h, uint32_t flags,
+ PixelFormat& format);
- LayerBaseClient* createBlurSurfaceLocked(Client* client, DisplayID display,
+ sp<LayerBaseClient> createBlurSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags);
- LayerBaseClient* createDimSurfaceLocked(Client* client, DisplayID display,
+ sp<LayerBaseClient> createDimSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags);
- LayerBaseClient* createPushBuffersSurfaceLocked(Client* client, DisplayID display,
+ sp<LayerBaseClient> createPushBuffersSurfaceLocked(
+ const sp<Client>& client, DisplayID display,
int32_t id, uint32_t w, uint32_t h, uint32_t flags);
- status_t destroySurface(SurfaceID surface_id);
- status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
+ status_t removeSurface(SurfaceID surface_id);
+ status_t destroySurface(const sp<LayerBaseClient>& layer);
+ status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states);
class LayerVector {
@@ -225,15 +220,15 @@ private:
inline LayerVector() { }
LayerVector(const LayerVector&);
inline size_t size() const { return layers.size(); }
- inline LayerBase*const* array() const { return layers.array(); }
- ssize_t add(LayerBase*, Vector<LayerBase*>::compar_t);
- ssize_t remove(LayerBase*);
- ssize_t reorder(LayerBase*, Vector<LayerBase*>::compar_t);
- ssize_t indexOf(LayerBase* key, size_t guess=0) const;
- inline LayerBase* operator [] (size_t i) const { return layers[i]; }
+ inline sp<LayerBase> const* array() const { return layers.array(); }
+ ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
+ ssize_t remove(const sp<LayerBase>&);
+ ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t);
+ ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const;
+ inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; }
private:
- KeyedVector<LayerBase*, size_t> lookup;
- Vector<LayerBase*> layers;
+ KeyedVector< sp<LayerBase> , size_t> lookup;
+ Vector< sp<LayerBase> > layers;
};
struct State {
@@ -247,25 +242,6 @@ private:
uint8_t freezeDisplay;
};
- class DelayedTransaction : public Thread
- {
- friend class SurfaceFlinger;
- sp<SurfaceFlinger> mFlinger;
- nsecs_t mDelay;
- public:
- DelayedTransaction(const sp<SurfaceFlinger>& flinger, nsecs_t delay)
- : Thread(false), mFlinger(flinger), mDelay(delay) {
- }
- virtual bool threadLoop() {
- usleep(mDelay / 1000);
- if (android_atomic_and(~1,
- &mFlinger->mDeplayedTransactionPending) == 1) {
- mFlinger->signalEvent();
- }
- return false;
- }
- };
-
virtual bool threadLoop();
virtual status_t readyToRun();
virtual void onFirstRef();
@@ -279,6 +255,9 @@ private:
void handleConsoleEvents();
void handleTransaction(uint32_t transactionFlags);
+ void handleTransactionLocked(
+ uint32_t transactionFlags,
+ Vector< sp<LayerBase> >& ditchedLayers);
void computeVisibleRegions(
LayerVector& currentLayers,
@@ -289,19 +268,16 @@ private:
bool lockPageFlip(const LayerVector& currentLayers);
void unlockPageFlip(const LayerVector& currentLayers);
void handleRepaint();
- void handleDebugCpu();
- void scheduleBroadcast(Client* client);
- void executeScheduledBroadcasts();
void postFramebuffer();
void composeSurfaces(const Region& dirty);
void unlockClients();
void destroyConnection(ClientID cid);
- LayerBaseClient* getLayerUser_l(SurfaceID index) const;
- status_t addLayer_l(LayerBase* layer);
- status_t removeLayer_l(LayerBase* layer);
- void destroy_all_removed_layers_l();
+ sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const;
+ status_t addLayer_l(const sp<LayerBase>& layer);
+ status_t removeLayer_l(const sp<LayerBase>& layer);
+ status_t purgatorizeLayer_l(const sp<LayerBase>& layer);
void free_resources_l();
uint32_t getTransactionFlags(uint32_t flags);
@@ -323,6 +299,11 @@ private:
void debugShowFPS() const;
void drawWormhole() const;
+
+ mutable MessageQueue mEventQueue;
+
+
+
// access must be protected by mStateLock
mutable Mutex mStateLock;
State mCurrentState;
@@ -330,23 +311,24 @@ private:
volatile int32_t mTransactionFlags;
volatile int32_t mTransactionCount;
Condition mTransactionCV;
-
+ bool mResizeTransationPending;
+
// protected by mStateLock (but we could use another lock)
Tokenizer mTokens;
- DefaultKeyedVector<ClientID, Client*> mClientsMap;
- DefaultKeyedVector<SurfaceID, LayerBaseClient*> mLayerMap;
+ DefaultKeyedVector<ClientID, sp<Client> > mClientsMap;
+ DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap;
GraphicPlane mGraphicPlanes[1];
- SortedVector<LayerBase*> mRemovedLayers;
- Vector<Client*> mDisconnectedClients;
+ bool mLayersRemoved;
+ Vector< sp<Client> > mDisconnectedClients;
// constant members (no synchronization needed for access)
- sp<MemoryDealer> mServerHeap;
- sp<IMemory> mServerCblkMemory;
+ sp<IMemoryHeap> mServerHeap;
surface_flinger_cblk_t* mServerCblk;
- sp<SurfaceHeapManager> mSurfaceHeapManager;
- sp<GPUHardwareInterface> mGPU;
GLuint mWormholeTexName;
nsecs_t mBootTime;
+ Permission mHardwareTest;
+ Permission mAccessSurfaceFlinger;
+ Permission mDump;
// Can only accessed from the main thread, these members
// don't need synchronization
@@ -354,30 +336,22 @@ private:
Region mDirtyRegionRemovedLayer;
Region mInvalidRegion;
Region mWormholeRegion;
- Client* mLastScheduledBroadcast;
- SortedVector<Client*> mScheduledBroadcasts;
bool mVisibleRegionsDirty;
bool mDeferReleaseConsole;
bool mFreezeDisplay;
int32_t mFreezeCount;
nsecs_t mFreezeDisplayTime;
- friend class OrientationAnimation;
- OrientationAnimation* mOrientationAnimation;
-
- // access protected by mDebugLock
- mutable Mutex mDebugLock;
- sp<CPUGauge> mCpuGauge;
// don't use a lock for these, we don't care
int mDebugRegion;
- int mDebugCpu;
- int mDebugFps;
int mDebugBackground;
+ volatile nsecs_t mDebugInSwapBuffers;
+ nsecs_t mLastSwapBufferTime;
+ volatile nsecs_t mDebugInTransaction;
+ nsecs_t mLastTransactionTime;
// these are thread safe
mutable Barrier mReadyToRunBarrier;
- mutable SurfaceFlingerSynchro mSyncObject;
- volatile int32_t mDeplayedTransactionPending;
// atomic variables
enum {
@@ -410,11 +384,11 @@ class BClient : public BnSurfaceFlingerClient
{
public:
BClient(SurfaceFlinger *flinger, ClientID cid,
- const sp<IMemory>& cblk);
+ const sp<IMemoryHeap>& cblk);
~BClient();
// ISurfaceFlingerClient interface
- virtual void getControlBlocks(sp<IMemory>* ctrl) const;
+ virtual sp<IMemoryHeap> getControlBlock() const;
virtual sp<ISurface> createSurface(
surface_data_t* params, int pid,
@@ -427,7 +401,7 @@ public:
private:
ClientID mId;
SurfaceFlinger* mFlinger;
- sp<IMemory> mCblk;
+ sp<IMemoryHeap> mCblk;
};
// ---------------------------------------------------------------------------
diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp
index ef51d6a..be3a239 100644
--- a/libs/surfaceflinger/Tokenizer.cpp
+++ b/libs/surfaceflinger/Tokenizer.cpp
@@ -162,9 +162,10 @@ void Tokenizer::dump() const
{
const run_t* ranges = mRanges.array();
const size_t c = mRanges.size();
- printf("Tokenizer (%p, size = %lu)\n", this, c);
+ printf("Tokenizer (%p, size = %d)\n", this, int(c));
for (size_t i=0 ; i<c ; i++) {
- printf("%lu: (%u, %u)\n", i, ranges[i].first, ranges[i].length);
+ printf("%u: (%u, %u)\n", i,
+ uint32_t(ranges[i].first), uint32_t(ranges[i].length));
}
}
diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp
index e8b0f45..1501536 100644
--- a/libs/surfaceflinger/Transform.cpp
+++ b/libs/surfaceflinger/Transform.cpp
@@ -177,10 +177,10 @@ Region Transform::transform(const Region& reg) const
Region out;
if (UNLIKELY(transformed())) {
if (LIKELY(preserveRects())) {
- Rect r;
- Region::iterator iterator(reg);
- while (iterator.iterate(&r)) {
- out.orSelf(transform(r));
+ Region::const_iterator it = reg.begin();
+ Region::const_iterator const end = reg.end();
+ while (it != end) {
+ out.orSelf(transform(*it++));
}
} else {
out.set(transform(reg.bounds()));
diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/VRamHeap.cpp
deleted file mode 100644
index 5f633bd..0000000
--- a/libs/surfaceflinger/VRamHeap.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "SurfaceFlinger"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <math.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <utils/MemoryDealer.h>
-#include <utils/MemoryBase.h>
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
-
-#include <EGL/eglnatives.h>
-
-#include "GPUHardware/GPUHardware.h"
-#include "SurfaceFlinger.h"
-#include "VRamHeap.h"
-
-#if HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-/*
- * Amount of memory we reserve for surface, per client in PMEM
- * (PMEM is used for 2D acceleration)
- * 8 MB of address space per client should be enough.
- */
-static const int PMEM_SIZE = int(8 * 1024 * 1024);
-
-int SurfaceHeapManager::global_pmem_heap = 0;
-
-// ---------------------------------------------------------------------------
-
-SurfaceHeapManager::SurfaceHeapManager(const sp<SurfaceFlinger>& flinger,
- size_t clientHeapSize)
- : mFlinger(flinger), mClientHeapSize(clientHeapSize)
-{
- SurfaceHeapManager::global_pmem_heap = 1;
-}
-
-SurfaceHeapManager::~SurfaceHeapManager()
-{
-}
-
-void SurfaceHeapManager::onFirstRef()
-{
- if (global_pmem_heap) {
- const char* device = "/dev/pmem";
- mPMemHeap = new PMemHeap(device, PMEM_SIZE);
- if (mPMemHeap->base() == MAP_FAILED) {
- mPMemHeap.clear();
- global_pmem_heap = 0;
- }
- }
-}
-
-sp<MemoryDealer> SurfaceHeapManager::createHeap(
- uint32_t flags,
- pid_t client_pid,
- const sp<MemoryDealer>& defaultAllocator)
-{
- sp<MemoryDealer> dealer;
-
- if (flags & ISurfaceComposer::eGPU) {
- // don't grant GPU memory if GPU is disabled
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.egl.hw", value, "1");
- if (atoi(value) == 0) {
- flags &= ~ISurfaceComposer::eGPU;
- }
- }
-
- if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) {
- // FIXME: this is msm7201A specific, where gpu surfaces may not be secure
- if (!(flags & ISurfaceComposer::eSecure)) {
- // if GPU doesn't work, we try eHardware
- flags |= ISurfaceComposer::eHardware;
- // asked for GPU memory, try that first
- dealer = mFlinger->getGPU()->request(client_pid);
- }
- }
-
- if (dealer == NULL) {
- if (defaultAllocator != NULL)
- // if a default allocator is given, use that
- dealer = defaultAllocator;
- }
-
- if (dealer == NULL) {
- // always try h/w accelerated memory first
- if (global_pmem_heap) {
- const sp<PMemHeap>& heap(mPMemHeap);
- if (dealer == NULL && heap != NULL) {
- dealer = new MemoryDealer(
- heap->createClientHeap(),
- heap->getAllocator());
- }
- }
- }
-
- if (dealer == NULL) {
- // return the ashmem allocator (software rendering)
- dealer = new MemoryDealer(mClientHeapSize, 0, "SFNativeHeap");
- }
- return dealer;
-}
-
-sp<SimpleBestFitAllocator> SurfaceHeapManager::getAllocator(int type) const
-{
- Mutex::Autolock _l(mLock);
- sp<SimpleBestFitAllocator> allocator;
-
- // this is only used for debugging
- switch (type) {
- case NATIVE_MEMORY_TYPE_PMEM:
- if (mPMemHeap != 0) {
- allocator = mPMemHeap->getAllocator();
- }
- break;
- }
- return allocator;
-}
-
-// ---------------------------------------------------------------------------
-
-PMemHeap::PMemHeap(const char* const device, size_t size, size_t reserved)
- : MemoryHeapBase(device, size)
-{
- //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID());
- if (base() != MAP_FAILED) {
- //LOGD("%s, %u bytes", device, virtualSize());
- if (reserved == 0)
- reserved = virtualSize();
- mAllocator = new SimpleBestFitAllocator(reserved);
- }
-}
-
-PMemHeap::~PMemHeap() {
- //LOGD("%s, %p, mFD=%d", __PRETTY_FUNCTION__, this, heapID());
-}
-
-sp<MemoryHeapPmem> PMemHeap::createClientHeap() {
- sp<MemoryHeapBase> parentHeap(this);
- return new MemoryHeapPmem(parentHeap);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/VRamHeap.h
deleted file mode 100644
index 9140167..0000000
--- a/libs/surfaceflinger/VRamHeap.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_VRAM_HEAP_H
-#define ANDROID_VRAM_HEAP_H
-
-#include <stdint.h>
-#include <sys/types.h>
-#include <utils/MemoryDealer.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class PMemHeap;
-class MemoryHeapPmem;
-class SurfaceFlinger;
-
-// ---------------------------------------------------------------------------
-
-class SurfaceHeapManager : public RefBase
-{
-public:
- SurfaceHeapManager(const sp<SurfaceFlinger>& flinger, size_t clientHeapSize);
- virtual ~SurfaceHeapManager();
- virtual void onFirstRef();
- /* use ISurfaceComposer flags eGPU|eHArdware|eSecure */
- sp<MemoryDealer> createHeap(uint32_t flags=0, pid_t client_pid = 0,
- const sp<MemoryDealer>& defaultAllocator = 0);
-
- // used for debugging only...
- sp<SimpleBestFitAllocator> getAllocator(int type) const;
-
-private:
- sp<PMemHeap> getHeap(int type) const;
-
- sp<SurfaceFlinger> mFlinger;
- mutable Mutex mLock;
- size_t mClientHeapSize;
- sp<PMemHeap> mPMemHeap;
- static int global_pmem_heap;
-};
-
-// ---------------------------------------------------------------------------
-
-class PMemHeap : public MemoryHeapBase
-{
-public:
- PMemHeap(const char* const vram,
- size_t size=0, size_t reserved=0);
- virtual ~PMemHeap();
-
- virtual const sp<SimpleBestFitAllocator>& getAllocator() const {
- return mAllocator;
- }
- virtual sp<MemoryHeapPmem> createClientHeap();
-
-private:
- sp<SimpleBestFitAllocator> mAllocator;
-};
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_VRAM_HEAP_H
diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp
index f3c046f..0b9322e 100644
--- a/libs/surfaceflinger/tests/overlays/overlays.cpp
+++ b/libs/surfaceflinger/tests/overlays/overlays.cpp
@@ -1,6 +1,6 @@
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <ui/Surface.h>
diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/libs/surfaceflinger/tests/resize/Android.mk
new file mode 100644
index 0000000..ef1532f
--- /dev/null
+++ b/libs/surfaceflinger/tests/resize/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ resize.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui
+
+LOCAL_MODULE:= test-resize
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/surfaceflinger/tests/resize/resize.cpp b/libs/surfaceflinger/tests/resize/resize.cpp
new file mode 100644
index 0000000..21c6ab6
--- /dev/null
+++ b/libs/surfaceflinger/tests/resize/resize.cpp
@@ -0,0 +1,60 @@
+#include <cutils/memory.h>
+
+#include <utils/IPCThreadState.h>
+#include <utils/ProcessState.h>
+#include <utils/IServiceManager.h>
+#include <utils/Log.h>
+
+#include <ui/Surface.h>
+#include <ui/ISurface.h>
+#include <ui/Overlay.h>
+#include <ui/SurfaceComposerClient.h>
+
+using namespace android;
+
+namespace android {
+class Test {
+public:
+ static const sp<ISurface>& getISurface(const sp<Surface>& s) {
+ return s->getISurface();
+ }
+};
+};
+
+int main(int argc, char** argv)
+{
+ // set up the thread-pool
+ sp<ProcessState> proc(ProcessState::self());
+ ProcessState::self()->startThreadPool();
+
+ // create a client to surfaceflinger
+ sp<SurfaceComposerClient> client = new SurfaceComposerClient();
+
+ // create pushbuffer surface
+ sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240,
+ PIXEL_FORMAT_RGB_565);
+
+
+ client->openTransaction();
+ surface->setLayer(100000);
+ client->closeTransaction();
+
+ Surface::SurfaceInfo info;
+ surface->lock(&info);
+ ssize_t bpr = info.s * bytesPerPixel(info.format);
+ android_memset16((uint16_t*)info.bits, 0xF800, bpr*info.h);
+ surface->unlockAndPost();
+
+ surface->lock(&info);
+ android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h);
+ surface->unlockAndPost();
+
+ client->openTransaction();
+ surface->setSize(320, 240);
+ client->closeTransaction();
+
+
+ IPCThreadState::self()->joinThreadPool();
+
+ return 0;
+}
diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk
index 7bbe38b..9577044 100644
--- a/libs/ui/Android.mk
+++ b/libs/ui/Android.mk
@@ -2,12 +2,13 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ BufferMapper.cpp \
Camera.cpp \
CameraParameters.cpp \
- EGLDisplaySurface.cpp \
- EGLNativeWindowSurface.cpp \
+ EGLUtils.cpp \
EventHub.cpp \
EventRecurrence.cpp \
+ FramebufferNativeWindow.cpp \
KeyLayoutMap.cpp \
KeyCharacterMap.cpp \
ICamera.cpp \
@@ -22,18 +23,25 @@ LOCAL_SRC_FILES:= \
PixelFormat.cpp \
Rect.cpp \
Region.cpp \
+ SharedBufferStack.cpp \
Surface.cpp \
+ SurfaceBuffer.cpp \
SurfaceComposerClient.cpp \
SurfaceFlingerSynchro.cpp
LOCAL_SHARED_LIBRARIES := \
- libcorecg \
libcutils \
libutils \
+ libEGL \
+ libbinder \
libpixelflinger \
libhardware \
libhardware_legacy
LOCAL_MODULE:= libui
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/ui/BufferMapper.cpp b/libs/ui/BufferMapper.cpp
new file mode 100644
index 0000000..4add8f9
--- /dev/null
+++ b/libs/ui/BufferMapper.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BufferMapper"
+
+#include <stdint.h>
+#include <errno.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+
+#include <ui/BufferMapper.h>
+#include <ui/Rect.h>
+
+#include <hardware/gralloc.h>
+
+
+namespace android {
+// ---------------------------------------------------------------------------
+
+ANDROID_SINGLETON_STATIC_INSTANCE( BufferMapper )
+
+BufferMapper::BufferMapper()
+ : mAllocMod(0)
+{
+ hw_module_t const* module;
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
+ LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);
+ if (err == 0) {
+ mAllocMod = (gralloc_module_t const *)module;
+ }
+}
+
+status_t BufferMapper::registerBuffer(buffer_handle_t handle)
+{
+ status_t err = mAllocMod->registerBuffer(mAllocMod, handle);
+ LOGW_IF(err, "registerBuffer(%p) failed %d (%s)",
+ handle, err, strerror(-err));
+ return err;
+}
+
+status_t BufferMapper::unregisterBuffer(buffer_handle_t handle)
+{
+ status_t err = mAllocMod->unregisterBuffer(mAllocMod, handle);
+ LOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)",
+ handle, err, strerror(-err));
+ return err;
+}
+
+status_t BufferMapper::lock(buffer_handle_t handle,
+ int usage, const Rect& bounds, void** vaddr)
+{
+ status_t err = mAllocMod->lock(mAllocMod, handle, usage,
+ bounds.left, bounds.top, bounds.width(), bounds.height(), vaddr);
+ LOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err));
+ return err;
+}
+
+status_t BufferMapper::unlock(buffer_handle_t handle)
+{
+ status_t err = mAllocMod->unlock(mAllocMod, handle);
+ LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err));
+ return err;
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index 5015379..12a7725 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -19,9 +19,9 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "Camera"
#include <utils/Log.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <utils/threads.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <ui/Surface.h>
#include <ui/Camera.h>
#include <ui/ICameraService.h>
diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp
deleted file mode 100644
index d06c98b..0000000
--- a/libs/ui/EGLDisplaySurface.cpp
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- **
- ** Copyright 2007 The Android Open Source Project
- **
- ** Licensed under the Apache License Version 2.0(the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing software
- ** distributed under the License is distributed on an "AS IS" BASIS
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#define LOG_TAG "EGLDisplaySurface"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <cutils/log.h>
-#include <cutils/atomic.h>
-#include <cutils/properties.h>
-
-#include <hardware/copybit.h>
-
-#include <ui/SurfaceComposerClient.h>
-#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
-#include <ui/Region.h>
-#include <ui/EGLDisplaySurface.h>
-
-#if HAVE_ANDROID_OS
-#include <linux/msm_mdp.h>
-#endif
-
-#include <EGL/egl.h>
-
-#include <pixelflinger/format.h>
-
-
-// ----------------------------------------------------------------------------
-
-egl_native_window_t* android_createDisplaySurface()
-{
- egl_native_window_t* s = new android::EGLDisplaySurface();
- s->memory_type = NATIVE_MEMORY_TYPE_GPU;
- return s;
-}
-
-#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
-#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-EGLDisplaySurface::EGLDisplaySurface()
- : EGLNativeSurface<EGLDisplaySurface>()
-{
- egl_native_window_t::version = sizeof(egl_native_window_t);
- egl_native_window_t::ident = 0;
- egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef;
- egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef;
- egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers;
- egl_native_window_t::connect = 0;
- egl_native_window_t::disconnect = 0;
-
- mFb[0].data = 0;
- mFb[1].data = 0;
- mBlitEngine = 0;
- egl_native_window_t::fd = mapFrameBuffer();
- if (egl_native_window_t::fd >= 0) {
-
- hw_module_t const* module;
- if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
- copybit_open(module, &mBlitEngine);
- }
-
- const float in2mm = 25.4f;
- float refreshRate = 1000000000000000LLU / (
- float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres )
- * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres )
- * mInfo.pixclock);
-
- const GGLSurface& buffer = mFb[1 - mIndex];
- egl_native_window_t::width = buffer.width;
- egl_native_window_t::height = buffer.height;
- egl_native_window_t::stride = buffer.stride;
- egl_native_window_t::format = buffer.format;
- egl_native_window_t::base = intptr_t(mFb[0].data);
- egl_native_window_t::offset =
- intptr_t(buffer.data) - egl_native_window_t::base;
- egl_native_window_t::flags = 0;
- egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width;
- egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height;
- egl_native_window_t::fps = refreshRate;
- egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB;
- // no error, set the magic word
- egl_native_window_t::magic = 0x600913;
- }
- mSwapCount = -1;
- mPageFlipCount = 0;
-}
-
-EGLDisplaySurface::~EGLDisplaySurface()
-{
- magic = 0;
- copybit_close(mBlitEngine);
- mBlitEngine = 0;
- close(egl_native_window_t::fd);
- munmap(mFb[0].data, mSize);
- if (!(mFlags & PAGE_FLIP))
- free((void*)mFb[1].data);
-}
-
-void EGLDisplaySurface::hook_incRef(NativeWindowType window) {
- EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
- that->incStrong(that);
-}
-void EGLDisplaySurface::hook_decRef(NativeWindowType window) {
- EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
- that->decStrong(that);
-}
-uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) {
- EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window);
- return that->swapBuffers();
-}
-
-void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h)
-{
- mInfo.reserved[0] = 0x54445055; // "UPDT";
- mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16);
- mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16);
-}
-
-uint32_t EGLDisplaySurface::swapBuffers()
-{
-#define SHOW_FPS 0
-#if SHOW_FPS
- nsecs_t now = systemTime();
- if (mSwapCount == -1) {
- mTime = now;
- mSwapCount = 0;
- mSleep = 0;
- } else {
- nsecs_t d = now-mTime;
- if (d >= seconds(1)) {
- double fps = (mSwapCount * double(seconds(1))) / double(d);
- LOGD("%f fps, sleep=%d / frame",
- fps, (int)ns2us(mSleep / mSwapCount));
- mSwapCount = 0;
- mTime = now;
- mSleep = 0;
- } else {
- mSwapCount++;
- }
- }
-#endif
- /* If we can't do the page_flip, just copy the back buffer to the front */
- if (!(mFlags & PAGE_FLIP)) {
- memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2);
- return 0;
- }
-
- // do the actual flip
- mIndex = 1 - mIndex;
- mInfo.activate = FB_ACTIVATE_VBL;
- mInfo.yoffset = mIndex ? mInfo.yres : 0;
- if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) {
- LOGE("FBIOPUT_VSCREENINFO failed");
- return 0;
- }
-
- /*
- * this is a monstrous hack: Because the h/w accelerator is not able
- * to render directly into the framebuffer, we need to copy its
- * internal framebuffer out to the fb.
- * oem[0] is used to access the fd of internal fb.
- * All this is needed only in standalone mode, in SurfaceFlinger mode
- * we control where the GPU renders.
- * We do this only if we have copybit, since this hack is needed only
- * with msm7k.
- */
- if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) {
- copybit_device_t *copybit = mBlitEngine;
- copybit_rect_t sdrect = { 0, 0,
- egl_native_window_t::width, egl_native_window_t::height };
- copybit_image_t dst = {
- egl_native_window_t::width,
- egl_native_window_t::height,
- egl_native_window_t::format,
- egl_native_window_t::offset,
- (void*)egl_native_window_t::base,
- egl_native_window_t::fd
- };
- copybit_image_t src = {
- egl_native_window_t::width,
- egl_native_window_t::height,
- egl_native_window_t::format, // XXX: use proper format
- egl_native_window_t::offset,
- (void*)egl_native_window_t::base, // XXX: use proper base
- egl_native_window_t::oem[0]
- };
- region_iterator it(Region(Rect(
- egl_native_window_t::width, egl_native_window_t::height)));
- copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
- copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
- copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
- copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it);
- }
-
- // update the address of the buffer to draw to next
- const GGLSurface& buffer = mFb[1 - mIndex];
- egl_native_window_t::offset =
- intptr_t(buffer.data) - egl_native_window_t::base;
-
-#if SHOW_FPS
- mSleep += systemTime()-now;
-#endif
-
- mPageFlipCount++;
-
- // We don't support screen-size changes for now
- return 0;
-}
-
-int32_t EGLDisplaySurface::getPageFlipCount() const
-{
- return mPageFlipCount;
-}
-
-void EGLDisplaySurface::copyFrontToBack(const Region& copyback)
-{
-#if HAVE_ANDROID_OS
- if (mBlitEngine) {
- copybit_image_t dst = {
- w: egl_native_window_t::stride,
- h: egl_native_window_t::height,
- format: egl_native_window_t::format,
- offset: mFb[1-mIndex].data - mFb[0].data,
- base: (void*)egl_native_window_t::base,
- fd: egl_native_window_t::fd
- };
- copybit_image_t src = {
- w: egl_native_window_t::stride,
- h: egl_native_window_t::height,
- format: egl_native_window_t::format,
- offset: mFb[mIndex].data - mFb[0].data,
- base: (void*)egl_native_window_t::base,
- fd: egl_native_window_t::fd
- };
- region_iterator it(copyback);
- mBlitEngine->blit(mBlitEngine, &dst, &src, &it);
- } else
-#endif
- {
- /* no extra copy needed since we copied back to front instead of
- * flipping */
- if (!(mFlags & PAGE_FLIP)) {
- return;
- }
-
- Region::iterator iterator(copyback);
- if (iterator) {
- Rect r;
- uint8_t* const screen_src = mFb[ mIndex].data;
- uint8_t* const screen_dst = mFb[1-mIndex].data;
- const size_t bpp = bytesPerPixel(egl_native_window_t::format);
- const size_t bpr = egl_native_window_t::stride * bpp;
- while (iterator.iterate(&r)) {
- ssize_t h = r.bottom - r.top;
- if (h) {
- size_t size = (r.right - r.left) * bpp;
- size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp;
- uint8_t* s = screen_src + o;
- uint8_t* d = screen_dst + o;
- if (size == bpr) {
- size *= h;
- h = 1;
- }
- do {
- memcpy(d, s, size);
- d += bpr;
- s += bpr;
- } while (--h > 0);
- }
- }
- }
- }
-}
-
-void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst)
-{
-#if HAVE_ANDROID_OS
- if (mBlitEngine) {
- copybit_image_t src = {
- w: egl_native_window_t::stride,
- h: egl_native_window_t::height,
- format: egl_native_window_t::format,
- offset: mFb[mIndex].data - mFb[0].data,
- base: (void*)egl_native_window_t::base,
- fd: egl_native_window_t::fd
- };
- region_iterator it(Region(Rect(
- egl_native_window_t::width, egl_native_window_t::height)));
- mBlitEngine->blit(mBlitEngine, &dst, &src, &it);
- } else
-#endif
- {
- uint8_t* const screen_src = mFb[ mIndex].data;
- const size_t bpp = bytesPerPixel(egl_native_window_t::format);
- const size_t bpr = egl_native_window_t::stride * bpp;
- memcpy((char*)dst.base + dst.offset, screen_src,
- bpr*egl_native_window_t::height);
- }
-}
-
-void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst)
-{
-#if HAVE_ANDROID_OS
- if (mBlitEngine) {
- copybit_image_t src = {
- w: egl_native_window_t::stride,
- h: egl_native_window_t::height,
- format: egl_native_window_t::format,
- offset: mFb[1-mIndex].data - mFb[0].data,
- base: (void*)egl_native_window_t::base,
- fd: egl_native_window_t::fd
- };
- region_iterator it(Region(Rect(
- egl_native_window_t::width, egl_native_window_t::height)));
- mBlitEngine->blit(mBlitEngine, &dst, &src, &it);
- } else
-#endif
- {
- uint8_t* const screen_src = mFb[1-mIndex].data;
- const size_t bpp = bytesPerPixel(egl_native_window_t::format);
- const size_t bpr = egl_native_window_t::stride * bpp;
- memcpy((char*)dst.base + dst.offset, screen_src,
- bpr*egl_native_window_t::height);
- }
-}
-
-
-status_t EGLDisplaySurface::mapFrameBuffer()
-{
- char const * const device_template[] = {
- "/dev/graphics/fb%u",
- "/dev/fb%u",
- 0 };
- int fd = -1;
- int i=0;
- char name[64];
- while ((fd==-1) && device_template[i]) {
- snprintf(name, 64, device_template[i], 0);
- fd = open(name, O_RDWR, 0);
- i++;
- }
- if (fd < 0)
- return -errno;
-
- struct fb_fix_screeninfo finfo;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
- return -errno;
-
- struct fb_var_screeninfo info;
- if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
- return -errno;
-
- info.reserved[0] = 0;
- info.reserved[1] = 0;
- info.reserved[2] = 0;
- info.xoffset = 0;
- info.yoffset = 0;
- info.yres_virtual = info.yres * 2;
- info.bits_per_pixel = 16;
- /* Explicitly request 5/6/5 */
- info.red.offset = 11;
- info.red.length = 5;
- info.green.offset = 5;
- info.green.length = 6;
- info.blue.offset = 0;
- info.blue.length = 5;
- info.transp.offset = 0;
- info.transp.length = 0;
- info.activate = FB_ACTIVATE_NOW;
-
- uint32_t flags = PAGE_FLIP;
- if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
- }
-
- if (info.yres_virtual < info.yres * 2) {
- info.yres_virtual = info.yres;
- flags &= ~PAGE_FLIP;
- LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
- info.yres_virtual, info.yres*2);
- }
-
- if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
- return -errno;
-
- int refreshRate = 1000000000000000LLU /
- (
- uint64_t( info.upper_margin + info.lower_margin + info.yres )
- * ( info.left_margin + info.right_margin + info.xres )
- * info.pixclock
- );
-
- if (refreshRate == 0) {
- // bleagh, bad info from the driver
- refreshRate = 60*1000; // 60 Hz
- }
- if (int(info.width) <= 0 || int(info.height) <= 0) {
- // the driver doesn't return that information
- // default to 160 dpi
- info.width = ((info.xres * 25.4f)/160.0f + 0.5f);
- info.height = ((info.yres * 25.4f)/160.0f + 0.5f);
- }
-
- float xdpi = (info.xres * 25.4f) / info.width;
- float ydpi = (info.yres * 25.4f) / info.height;
- float fps = refreshRate / 1000.0f;
-
- LOGI( "using (fd=%d)\n"
- "id = %s\n"
- "xres = %d px\n"
- "yres = %d px\n"
- "xres_virtual = %d px\n"
- "yres_virtual = %d px\n"
- "bpp = %d\n"
- "r = %2u:%u\n"
- "g = %2u:%u\n"
- "b = %2u:%u\n",
- fd,
- finfo.id,
- info.xres,
- info.yres,
- info.xres_virtual,
- info.yres_virtual,
- info.bits_per_pixel,
- info.red.offset, info.red.length,
- info.green.offset, info.green.length,
- info.blue.offset, info.blue.length
- );
-
- LOGI( "width = %d mm (%f dpi)\n"
- "height = %d mm (%f dpi)\n"
- "refresh rate = %.2f Hz\n",
- info.width, xdpi,
- info.height, ydpi,
- fps
- );
-
-
- if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1)
- return -errno;
-
- if (finfo.smem_len <= 0)
- return -errno;
-
- /*
- * Open and map the display.
- */
-
- void* buffer = (uint16_t*) mmap(
- 0, finfo.smem_len,
- PROT_READ | PROT_WRITE,
- MAP_SHARED,
- fd, 0);
-
- if (buffer == MAP_FAILED)
- return -errno;
-
- // at least for now, always clear the fb
- memset(buffer, 0, finfo.smem_len);
-
- uint8_t* offscreen[2];
- offscreen[0] = (uint8_t*)buffer;
- if (flags & PAGE_FLIP) {
- offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres;
- } else {
- offscreen[1] = (uint8_t*)malloc(finfo.smem_len);
- if (offscreen[1] == 0) {
- munmap(buffer, finfo.smem_len);
- return NO_MEMORY;
- }
- }
-
- mFlags = flags;
- mInfo = info;
- mFinfo = finfo;
- mSize = finfo.smem_len;
- mIndex = 0;
- for (int i=0 ; i<2 ; i++) {
- mFb[i].version = sizeof(GGLSurface);
- mFb[i].width = info.xres;
- mFb[i].height = info.yres;
- mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3);
- mFb[i].data = (GGLubyte*)(offscreen[i]);
- mFb[i].format = GGL_PIXEL_FORMAT_RGB_565;
- }
- return fd;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp
deleted file mode 100644
index f1071cf..0000000
--- a/libs/ui/EGLNativeWindowSurface.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
-**
-** Copyright 2007 The Android Open Source Project
-**
-** Licensed under the Apache License Version 2.0(the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing software
-** distributed under the License is distributed on an "AS IS" BASIS
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "EGLNativeWindowSurface"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <cutils/log.h>
-#include <cutils/atomic.h>
-
-#include <ui/SurfaceComposerClient.h>
-#include <ui/DisplayInfo.h>
-#include <ui/Rect.h>
-
-#include <EGL/egl.h>
-
-#include <pixelflinger/format.h>
-
-#include <ui/EGLNativeWindowSurface.h>
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-EGLNativeWindowSurface::EGLNativeWindowSurface(const sp<Surface>& surface)
- : EGLNativeSurface<EGLNativeWindowSurface>(),
- mSurface(surface), mConnected(false)
-{
- egl_native_window_t::magic = 0x600913;
- egl_native_window_t::version = sizeof(egl_native_window_t);
- egl_native_window_t::ident = 0;
- egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef;
- egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef;
- egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers;
- egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect;
- egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect;
-
- DisplayInfo dinfo;
- SurfaceComposerClient::getDisplayInfo(0, &dinfo);
- egl_native_window_t::xdpi = dinfo.xdpi;
- egl_native_window_t::ydpi = dinfo.ydpi;
- egl_native_window_t::fps = dinfo.fps;
- egl_native_window_t::flags= EGL_NATIVES_FLAG_DESTROY_BACKBUFFER;
-}
-
-EGLNativeWindowSurface::~EGLNativeWindowSurface()
-{
- disconnect();
- mSurface.clear();
- magic = 0;
-}
-
-void EGLNativeWindowSurface::hook_incRef(NativeWindowType window)
-{
- EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window);
- that->incStrong(that);
-}
-
-void EGLNativeWindowSurface::hook_decRef(NativeWindowType window)
-{
- EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window);
- that->decStrong(that);
-}
-
-void EGLNativeWindowSurface::hook_connect(NativeWindowType window)
-{
- EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window);
- that->connect();
-}
-
-void EGLNativeWindowSurface::hook_disconnect(NativeWindowType window)
-{
- EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window);
- that->disconnect();
-}
-
-uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window)
-{
- EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window);
- return that->swapBuffers();
-}
-
-void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h)
-{
- mSurface->setSwapRectangle(Rect(l, t, l+w, t+h));
-}
-
-uint32_t EGLNativeWindowSurface::swapBuffers()
-{
- const int w = egl_native_window_t::width;
- const int h = egl_native_window_t::height;
- const sp<Surface>& surface(mSurface);
- Surface::SurfaceInfo info;
- surface->unlockAndPost();
- surface->lock(&info);
- // update the address of the buffer to draw to next
- egl_native_window_t::base = intptr_t(info.base);
- egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base);
-
- // update size if it changed
- if (w != int(info.w) || h != int(info.h)) {
- egl_native_window_t::width = info.w;
- egl_native_window_t::height = info.h;
- egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format);
- egl_native_window_t::format = info.format;
- return EGL_NATIVES_FLAG_SIZE_CHANGED;
- }
- return 0;
-}
-
-void EGLNativeWindowSurface::connect()
-{
- if (!mConnected) {
- Surface::SurfaceInfo info;
- mSurface->lock(&info);
- mSurface->setSwapRectangle(Rect(info.w, info.h));
- mConnected = true;
-
- egl_native_window_t::width = info.w;
- egl_native_window_t::height = info.h;
- egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format);
- egl_native_window_t::format = info.format;
- egl_native_window_t::base = intptr_t(info.base);
- egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base);
- // FIXME: egl_native_window_t::memory_type used to be set from
- // mSurface, but we wanted to break this dependency. We set it to
- // GPU because the software rendered doesn't care, but the h/w
- // accelerator needs it. Eventually, this value should go away
- // completely, since memory will be managed by OpenGL.
- egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU;
- egl_native_window_t::fd = 0;
- }
-}
-
-void EGLNativeWindowSurface::disconnect()
-{
- if (mConnected) {
- mSurface->unlock();
- mConnected = false;
- }
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/libs/ui/EGLUtils.cpp b/libs/ui/EGLUtils.cpp
new file mode 100644
index 0000000..1663313
--- /dev/null
+++ b/libs/ui/EGLUtils.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "EGLUtils"
+
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+#include <ui/EGLUtils.h>
+
+#include <EGL/egl.h>
+
+#include <private/ui/android_natives_priv.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+const char *EGLUtils::strerror(EGLint err)
+{
+ switch (err){
+ case EGL_SUCCESS: return "EGL_SUCCESS";
+ case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG";
+ case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY";
+ case EGL_BAD_MATCH: return "EGL_BAD_MATCH";
+ case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER";
+ case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE";
+ case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST";
+ default: return "UNKNOWN";
+ }
+}
+
+status_t EGLUtils::selectConfigForPixelFormat(
+ EGLDisplay dpy,
+ EGLint const* attrs,
+ PixelFormat format,
+ EGLConfig* outConfig)
+{
+ EGLint numConfigs = -1, n=0;
+
+ if (!attrs)
+ return BAD_VALUE;
+
+ if (outConfig == NULL)
+ return BAD_VALUE;
+
+ int err;
+ PixelFormatInfo fbFormatInfo;
+ if ((err = getPixelFormatInfo(PixelFormat(format), &fbFormatInfo)) < 0) {
+ return err;
+ }
+
+ // Get all the "potential match" configs...
+ if (eglGetConfigs(dpy, NULL, 0, &numConfigs) == EGL_FALSE)
+ return BAD_VALUE;
+
+ EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);
+ if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) {
+ free(configs);
+ return BAD_VALUE;
+ }
+
+ const int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);
+ const int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);
+ const int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);
+ const int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);
+
+ int i;
+ EGLConfig config = NULL;
+ for (i=0 ; i<n ; i++) {
+ EGLint r,g,b,a;
+ EGLConfig curr = configs[i];
+ eglGetConfigAttrib(dpy, curr, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(dpy, curr, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(dpy, curr, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(dpy, curr, EGL_ALPHA_SIZE, &a);
+ if (fbSzA == a && fbSzR == r && fbSzG == g && fbSzB == b) {
+ config = curr;
+ break;
+ }
+ }
+
+ free(configs);
+
+ if (i<n) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+status_t EGLUtils::selectConfigForNativeWindow(
+ EGLDisplay dpy,
+ EGLint const* attrs,
+ EGLNativeWindowType window,
+ EGLConfig* outConfig)
+{
+ int err;
+ int format;
+
+ if (!window)
+ return BAD_VALUE;
+
+ if ((err = window->query(window, NATIVE_WINDOW_FORMAT, &format)) < 0) {
+ return err;
+ }
+
+ return selectConfigForPixelFormat(dpy, attrs, format, outConfig);
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 7c2fc8e..e39a357 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -16,13 +16,14 @@
//#define LOG_NDEBUG 0
#include <ui/EventHub.h>
+#include <ui/KeycodeLabels.h>
#include <hardware_legacy/power.h>
#include <cutils/properties.h>
-#include <utils/IServiceManager.h>
#include <utils/Log.h>
#include <utils/Timers.h>
-#include <utils.h>
+#include <utils/threads.h>
+#include <utils/Errors.h>
#include <stdlib.h>
#include <stdio.h>
@@ -58,6 +59,18 @@
#define SEQ_SHIFT 16
#define id_to_index(id) ((id&ID_MASK)+1)
+#ifndef ABS_MT_TOUCH_MAJOR
+#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
+#endif
+
+#ifndef ABS_MT_POSITION_X
+#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
+#endif
+
+#ifndef ABS_MT_POSITION_Y
+#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
+#endif
+
namespace android {
static const char *WAKE_LOCK_ID = "KeyEvents";
@@ -69,8 +82,8 @@ static inline int max(int v1, int v2)
return (v1 > v2) ? v1 : v2;
}
-EventHub::device_t::device_t(int32_t _id, const char* _path)
- : id(_id), path(_path), classes(0)
+EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
+ : id(_id), path(_path), name(name), classes(0)
, keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
}
@@ -83,7 +96,7 @@ EventHub::EventHub(void)
: mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0)
, mDevicesById(0), mNumDevicesById(0)
, mOpeningDevices(0), mClosingDevices(0)
- , mDevices(0), mFDs(0), mFDCount(0)
+ , mDevices(0), mFDs(0), mFDCount(0), mOpened(false)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
#ifdef EV_SW
@@ -100,11 +113,6 @@ EventHub::~EventHub(void)
// we should free stuff here...
}
-void EventHub::onFirstRef()
-{
- mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
-}
-
status_t EventHub::errorCheck() const
{
return mError;
@@ -239,6 +247,41 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const
return 0;
}
+status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode,
+ int32_t* outKeycode, uint32_t* outFlags) const
+{
+ AutoMutex _l(mLock);
+ device_t* device = getDevice(deviceId);
+
+ if (device != NULL && device->layoutMap != NULL) {
+ status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (err == NO_ERROR) {
+ return NO_ERROR;
+ }
+ }
+
+ if (mHaveFirstKeyboard) {
+ device = getDevice(mFirstKeyboardId);
+
+ if (device != NULL && device->layoutMap != NULL) {
+ status_t err = device->layoutMap->map(scancode, outKeycode, outFlags);
+ if (err == NO_ERROR) {
+ return NO_ERROR;
+ }
+ }
+ }
+
+ *outKeycode = 0;
+ *outFlags = 0;
+ return NAME_NOT_FOUND;
+}
+
+void EventHub::addExcludedDevice(const char* deviceName)
+{
+ String8 name(deviceName);
+ mExcludedDevices.push_back(name);
+}
+
EventHub::device_t* EventHub::getDevice(int32_t deviceId) const
{
if (deviceId == 0) deviceId = mFirstKeyboardId;
@@ -276,7 +319,12 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
// Note that we only allow one caller to getEvent(), so don't need
// to do locking here... only when adding/removing devices.
-
+
+ if (!mOpened) {
+ mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR;
+ mOpened = true;
+ }
+
while(1) {
// First, report any devices that had last been added/removed.
@@ -474,6 +522,20 @@ int EventHub::open_device(const char *deviceName)
//fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno));
name[0] = '\0';
}
+
+ // check to see if the device is on our excluded list
+ List<String8>::iterator iter = mExcludedDevices.begin();
+ List<String8>::iterator end = mExcludedDevices.end();
+ for ( ; iter != end; iter++) {
+ const char* test = *iter;
+ if (strcmp(name, test) == 0) {
+ LOGI("ignoring event id %s driver %s\n", deviceName, test);
+ close(fd);
+ fd = -1;
+ return -1;
+ }
+ }
+
if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
//fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno));
location[0] = '\0';
@@ -531,7 +593,7 @@ int EventHub::open_device(const char *deviceName)
version >> 16, (version >> 8) & 0xff, version & 0xff);
#endif
- device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName);
+ device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name);
if (device == NULL) {
LOGE("out of memory");
return -1;
@@ -541,6 +603,8 @@ int EventHub::open_device(const char *deviceName)
mFDs[mFDCount].events = POLLIN;
// figure out the kinds of events the device reports
+
+ // See if this is a keyboard, and classify it.
uint8_t key_bitmask[(KEY_MAX+1)/8];
memset(key_bitmask, 0, sizeof(key_bitmask));
LOGV("Getting keys...");
@@ -552,15 +616,11 @@ int EventHub::open_device(const char *deviceName)
for (int i=0; i<((BTN_MISC+7)/8); i++) {
if (key_bitmask[i] != 0) {
device->classes |= CLASS_KEYBOARD;
- // 'Q' key support = cheap test of whether this is an alpha-capable kbd
- if (test_bit(KEY_Q, key_bitmask)) {
- device->classes |= CLASS_ALPHAKEY;
- }
break;
}
}
if ((device->classes & CLASS_KEYBOARD) != 0) {
- device->keyBitmask = new uint8_t[(KEY_MAX+1)/8];
+ device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
if (device->keyBitmask != NULL) {
memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
} else {
@@ -570,6 +630,8 @@ int EventHub::open_device(const char *deviceName)
}
}
}
+
+ // See if this is a trackball.
if (test_bit(BTN_MOUSE, key_bitmask)) {
uint8_t rel_bitmask[(REL_MAX+1)/8];
memset(rel_bitmask, 0, sizeof(rel_bitmask));
@@ -581,16 +643,22 @@ int EventHub::open_device(const char *deviceName)
}
}
}
- if (test_bit(BTN_TOUCH, key_bitmask)) {
- uint8_t abs_bitmask[(ABS_MAX+1)/8];
- memset(abs_bitmask, 0, sizeof(abs_bitmask));
- LOGV("Getting absolute controllers...");
- if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0)
- {
- if (test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
- device->classes |= CLASS_TOUCHSCREEN;
- }
- }
+
+ uint8_t abs_bitmask[(ABS_MAX+1)/8];
+ memset(abs_bitmask, 0, sizeof(abs_bitmask));
+ LOGV("Getting absolute controllers...");
+ ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask);
+
+ // Is this a new modern multi-touch driver?
+ if (test_bit(ABS_MT_TOUCH_MAJOR, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_X, abs_bitmask)
+ && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {
+ device->classes |= CLASS_TOUCHSCREEN | CLASS_TOUCHSCREEN_MT;
+
+ // Is this an old style single-touch driver?
+ } else if (test_bit(BTN_TOUCH, key_bitmask)
+ && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {
+ device->classes |= CLASS_TOUCHSCREEN;
}
#ifdef EV_SW
@@ -609,21 +677,15 @@ int EventHub::open_device(const char *deviceName)
}
#endif
- LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
- deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
-
if ((device->classes&CLASS_KEYBOARD) != 0) {
- char devname[101];
- char tmpfn[101];
+ char tmpfn[sizeof(name)];
char keylayoutFilename[300];
// a more descriptive name
- ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
- devname[sizeof(devname)-1] = 0;
- device->name = devname;
+ device->name = name;
// replace all the spaces with underscores
- strcpy(tmpfn, devname);
+ strcpy(tmpfn, name);
for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' '))
*p = '_';
@@ -656,12 +718,29 @@ int EventHub::open_device(const char *deviceName)
}
char propName[100];
sprintf(propName, "hw.keyboards.%u.devname", publicID);
- property_set(propName, devname);
+ property_set(propName, name);
- LOGI("New keyboard: publicID=%d device->id=%d devname='%s' propName='%s' keylayout='%s'\n",
- publicID, device->id, devname, propName, keylayoutFilename);
+ // 'Q' key support = cheap test of whether this is an alpha-capable kbd
+ if (hasKeycode(device, kKeyCodeQ)) {
+ device->classes |= CLASS_ALPHAKEY;
+ }
+
+ // See if this has a DPAD.
+ if (hasKeycode(device, kKeyCodeDpadUp) &&
+ hasKeycode(device, kKeyCodeDpadDown) &&
+ hasKeycode(device, kKeyCodeDpadLeft) &&
+ hasKeycode(device, kKeyCodeDpadRight) &&
+ hasKeycode(device, kKeyCodeDpadCenter)) {
+ device->classes |= CLASS_DPAD;
+ }
+
+ LOGI("New keyboard: publicID=%d device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n",
+ publicID, device->id, name, propName, keylayoutFilename);
}
+ LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
+ deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
+
LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n",
deviceName, device, mFDCount, devid, device->classes);
@@ -674,6 +753,25 @@ int EventHub::open_device(const char *deviceName)
return 0;
}
+bool EventHub::hasKeycode(device_t* device, int keycode) const
+{
+ if (device->keyBitmask == NULL || device->layoutMap == NULL) {
+ return false;
+ }
+
+ Vector<int32_t> scanCodes;
+ device->layoutMap->findScancodes(keycode, &scanCodes);
+ const size_t N = scanCodes.size();
+ for (size_t i=0; i<N && i<=KEY_MAX; i++) {
+ int32_t sc = scanCodes.itemAt(i);
+ if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
int EventHub::close_device(const char *deviceName)
{
AutoMutex _l(mLock);
@@ -683,11 +781,21 @@ int EventHub::close_device(const char *deviceName)
if(strcmp(mDevices[i]->path.string(), deviceName) == 0) {
//LOGD("remove device %d: %s\n", i, deviceName);
device_t* device = mDevices[i];
- int count = mFDCount - i - 1;
+
+ LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
+ device->path.string(), device->name.string(), device->id,
+ mNumDevicesById, mFDCount, mFDs[i].fd, device->classes);
+
+ // Clear this device's entry.
int index = (device->id&ID_MASK);
mDevicesById[index].device = NULL;
+
+ // Close the file descriptor and compact the fd array.
+ close(mFDs[i].fd);
+ int count = mFDCount - i - 1;
memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count);
memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count);
+ mFDCount--;
#ifdef EV_SW
for (int j=0; j<EV_SW; j++) {
@@ -700,8 +808,6 @@ int EventHub::close_device(const char *deviceName)
device->next = mClosingDevices;
mClosingDevices = device;
- mFDCount--;
-
uint32_t publicID;
if (device->id == mFirstKeyboardId) {
LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this",
@@ -718,7 +824,7 @@ int EventHub::close_device(const char *deviceName)
return 0;
}
}
- LOGE("remote device: %s not found\n", deviceName);
+ LOGE("remove device: %s not found\n", deviceName);
return -1;
}
@@ -733,6 +839,7 @@ int EventHub::read_notify(int nfd)
int event_pos = 0;
struct inotify_event *event;
+ LOGV("EventHub::read_notify nfd: %d\n", nfd);
res = read(nfd, event_buf, sizeof(event_buf));
if(res < (int)sizeof(*event)) {
if(errno == EINTR)
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
new file mode 100644
index 0000000..90b5163
--- /dev/null
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -0,0 +1,267 @@
+/*
+**
+** Copyright 2007 The Android Open Source Project
+**
+** Licensed under the Apache License Version 2.0(the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing software
+** distributed under the License is distributed on an "AS IS" BASIS
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "FramebufferNativeWindow"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+#include <cutils/atomic.h>
+#include <utils/threads.h>
+#include <utils/RefBase.h>
+
+#include <ui/SurfaceComposerClient.h>
+#include <ui/Rect.h>
+#include <ui/FramebufferNativeWindow.h>
+
+#include <EGL/egl.h>
+
+#include <pixelflinger/format.h>
+#include <pixelflinger/pixelflinger.h>
+
+#include <hardware/hardware.h>
+#include <hardware/gralloc.h>
+
+#include <private/ui/android_natives_priv.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+class NativeBuffer
+ : public EGLNativeBase<
+ android_native_buffer_t,
+ NativeBuffer,
+ LightRefBase<NativeBuffer> >
+{
+public:
+ NativeBuffer(int w, int h, int f, int u) : BASE() {
+ android_native_buffer_t::width = w;
+ android_native_buffer_t::height = h;
+ android_native_buffer_t::format = f;
+ android_native_buffer_t::usage = u;
+ }
+private:
+ friend class LightRefBase<NativeBuffer>;
+ ~NativeBuffer() { }; // this class cannot be overloaded
+};
+
+
+/*
+ * This implements the (main) framebuffer management. This class is used
+ * mostly by SurfaceFlinger, but also by command line GL application.
+ *
+ * In fact this is an implementation of android_native_window_t on top of
+ * the framebuffer.
+ *
+ * Currently it is pretty simple, it manages only two buffers (the front and
+ * back buffer).
+ *
+ */
+
+FramebufferNativeWindow::FramebufferNativeWindow()
+ : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
+{
+ hw_module_t const* module;
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {
+ int stride;
+ int err;
+ err = framebuffer_open(module, &fbDev);
+ LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
+
+ err = gralloc_open(module, &grDev);
+ LOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));
+
+ // bail out if we can't initialize the modules
+ if (!fbDev || !grDev)
+ return;
+
+ mUpdateOnDemand = (fbDev->setUpdateRect != 0);
+
+ // initialize the buffer FIFO
+ mNumBuffers = 2;
+ mNumFreeBuffers = 2;
+ mBufferHead = mNumBuffers-1;
+ buffers[0] = new NativeBuffer(
+ fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
+ buffers[1] = new NativeBuffer(
+ fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
+
+ err = grDev->alloc(grDev,
+ fbDev->width, fbDev->height, fbDev->format,
+ GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride);
+
+ LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s",
+ fbDev->width, fbDev->height, strerror(-err));
+
+ err = grDev->alloc(grDev,
+ fbDev->width, fbDev->height, fbDev->format,
+ GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride);
+
+ LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",
+ fbDev->width, fbDev->height, strerror(-err));
+ }
+
+ const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags;
+ const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi;
+ const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi;
+ const_cast<int&>(android_native_window_t::minSwapInterval) =
+ fbDev->minSwapInterval;
+ const_cast<int&>(android_native_window_t::maxSwapInterval) =
+ fbDev->maxSwapInterval;
+
+ android_native_window_t::setSwapInterval = setSwapInterval;
+ android_native_window_t::dequeueBuffer = dequeueBuffer;
+ android_native_window_t::lockBuffer = lockBuffer;
+ android_native_window_t::queueBuffer = queueBuffer;
+ android_native_window_t::query = query;
+ android_native_window_t::perform = perform;
+}
+
+FramebufferNativeWindow::~FramebufferNativeWindow()
+{
+ if (grDev) {
+ if (buffers[0] != NULL)
+ grDev->free(grDev, buffers[0]->handle);
+ if (buffers[1] != NULL)
+ grDev->free(grDev, buffers[1]->handle);
+ gralloc_close(grDev);
+ }
+
+ if (fbDev) {
+ framebuffer_close(fbDev);
+ }
+}
+
+status_t FramebufferNativeWindow::setUpdateRectangle(const Rect& r)
+{
+ if (!mUpdateOnDemand) {
+ return INVALID_OPERATION;
+ }
+ return fbDev->setUpdateRect(fbDev, r.left, r.top, r.width(), r.height());
+}
+
+int FramebufferNativeWindow::setSwapInterval(
+ android_native_window_t* window, int interval)
+{
+ framebuffer_device_t* fb = getSelf(window)->fbDev;
+ return fb->setSwapInterval(fb, interval);
+}
+
+int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window,
+ android_native_buffer_t** buffer)
+{
+ FramebufferNativeWindow* self = getSelf(window);
+ Mutex::Autolock _l(self->mutex);
+ framebuffer_device_t* fb = self->fbDev;
+
+ // wait for a free buffer
+ while (!self->mNumFreeBuffers) {
+ self->mCondition.wait(self->mutex);
+ }
+ // get this buffer
+ self->mNumFreeBuffers--;
+ int index = self->mBufferHead++;
+ if (self->mBufferHead >= self->mNumBuffers)
+ self->mBufferHead = 0;
+
+ *buffer = self->buffers[index].get();
+
+ return 0;
+}
+
+int FramebufferNativeWindow::lockBuffer(android_native_window_t* window,
+ android_native_buffer_t* buffer)
+{
+ FramebufferNativeWindow* self = getSelf(window);
+ Mutex::Autolock _l(self->mutex);
+
+ // wait that the buffer we're locking is not front anymore
+ while (self->front == buffer) {
+ self->mCondition.wait(self->mutex);
+ }
+
+ return NO_ERROR;
+}
+
+int FramebufferNativeWindow::queueBuffer(android_native_window_t* window,
+ android_native_buffer_t* buffer)
+{
+ FramebufferNativeWindow* self = getSelf(window);
+ Mutex::Autolock _l(self->mutex);
+ framebuffer_device_t* fb = self->fbDev;
+ buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;
+ int res = fb->post(fb, handle);
+ self->front = static_cast<NativeBuffer*>(buffer);
+ self->mNumFreeBuffers++;
+ self->mCondition.broadcast();
+ return res;
+}
+
+int FramebufferNativeWindow::query(android_native_window_t* window,
+ int what, int* value)
+{
+ FramebufferNativeWindow* self = getSelf(window);
+ Mutex::Autolock _l(self->mutex);
+ framebuffer_device_t* fb = self->fbDev;
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = fb->width;
+ return NO_ERROR;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = fb->height;
+ return NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = fb->format;
+ return NO_ERROR;
+ }
+ *value = 0;
+ return BAD_VALUE;
+}
+
+int FramebufferNativeWindow::perform(android_native_window_t* window,
+ int operation, ...)
+{
+ switch (operation) {
+ case NATIVE_WINDOW_SET_USAGE:
+ break;
+ default:
+ return NAME_NOT_FOUND;
+ }
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+using namespace android;
+
+EGLNativeWindowType android_createDisplaySurface(void)
+{
+ FramebufferNativeWindow* w;
+ w = new FramebufferNativeWindow();
+ if (w->getDevice() == NULL) {
+ // get a ref so it can be destroyed when we exit this block
+ sp<FramebufferNativeWindow> ref(w);
+ return NULL;
+ }
+ return (EGLNativeWindowType)w;
+}
diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp
index ab0fef1..805c2ca 100644
--- a/libs/ui/ICamera.cpp
+++ b/libs/ui/ICamera.cpp
@@ -20,7 +20,7 @@
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <ui/ICamera.h>
namespace android {
@@ -221,12 +221,6 @@ IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnCamera::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp
index 59a6cf2..42b4da4 100644
--- a/libs/ui/ICameraClient.cpp
+++ b/libs/ui/ICameraClient.cpp
@@ -78,12 +78,6 @@ IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnCameraClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp
index e5687fe..84986c6 100644
--- a/libs/ui/ICameraService.cpp
+++ b/libs/ui/ICameraService.cpp
@@ -18,9 +18,9 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <ui/ICameraService.h>
@@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnCameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp
index fed47c2..65e6b4f 100644
--- a/libs/ui/IOverlay.cpp
+++ b/libs/ui/IOverlay.cpp
@@ -18,8 +18,8 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IInterface.h>
#include <ui/IOverlay.h>
@@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnOverlay::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp
index d5e9f81..a2dbe7f 100644
--- a/libs/ui/ISurface.cpp
+++ b/libs/ui/ISurface.cpp
@@ -14,19 +14,25 @@
* limitations under the License.
*/
+#define LOG_TAG "ISurface"
+
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
#include <ui/ISurface.h>
#include <ui/Overlay.h>
+#include <ui/Surface.h>
+#include <private/ui/SurfaceBuffer.h>
namespace android {
+// ----------------------------------------------------------------------
+
ISurface::BufferHeap::BufferHeap()
: w(0), h(0), hor_stride(0), ver_stride(0), format(0),
transform(0), flags(0)
@@ -55,6 +61,8 @@ ISurface::BufferHeap::~BufferHeap()
{
}
+// ----------------------------------------------------------------------
+
class BpSurface : public BpInterface<ISurface>
{
public:
@@ -63,6 +71,17 @@ public:
{
}
+ virtual sp<SurfaceBuffer> requestBuffer(int bufferIdx, int usage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurface::getInterfaceDescriptor());
+ data.writeInt32(bufferIdx);
+ data.writeInt32(usage);
+ remote()->transact(REQUEST_BUFFER, data, &reply);
+ sp<SurfaceBuffer> buffer = new SurfaceBuffer(reply);
+ return buffer;
+ }
+
virtual status_t registerBuffers(const BufferHeap& buffers)
{
Parcel data, reply;
@@ -112,16 +131,17 @@ IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnSurface::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
+ case REQUEST_BUFFER: {
+ CHECK_INTERFACE(ISurface, data, reply);
+ int bufferIdx = data.readInt32();
+ int usage = data.readInt32();
+ sp<SurfaceBuffer> buffer(requestBuffer(bufferIdx, usage));
+ return SurfaceBuffer::writeToParcel(reply, buffer.get());
+ }
case REGISTER_BUFFERS: {
CHECK_INTERFACE(ISurface, data, reply);
BufferHeap buffer;
diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp
index 76597e1..fd2a590 100644
--- a/libs/ui/ISurfaceComposer.cpp
+++ b/libs/ui/ISurfaceComposer.cpp
@@ -20,10 +20,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <ui/ISurfaceComposer.h>
#include <ui/DisplayInfo.h>
@@ -54,12 +54,12 @@ public:
return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder());
}
- virtual sp<IMemory> getCblk() const
+ virtual sp<IMemoryHeap> getCblk() const
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply);
- return interface_cast<IMemory>(reply.readStrongBinder());
+ return interface_cast<IMemoryHeap>(reply.readStrongBinder());
}
virtual void openGlobalTransaction()
@@ -114,36 +114,6 @@ public:
remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
}
- virtual status_t requestGPU(
- const sp<IGPUCallback>& callback, gpu_info_t* gpu)
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- data.writeStrongBinder(callback->asBinder());
- remote()->transact(BnSurfaceComposer::REQUEST_GPU, data, &reply);
- gpu->regs = interface_cast<IMemory>(reply.readStrongBinder());
- gpu->count = reply.readInt32();
-
- // FIXME: for now, we don't dynamically allocate the regions array
- size_t maxCount = sizeof(gpu->regions)/sizeof(*gpu->regions);
- if (gpu->count > maxCount)
- return BAD_VALUE;
-
- for (size_t i=0 ; i<gpu->count ; i++) {
- gpu->regions[i].region = interface_cast<IMemory>(reply.readStrongBinder());
- gpu->regions[i].reserved = reply.readInt32();
- }
- return reply.readInt32();
- }
-
- virtual status_t revokeGPU()
- {
- Parcel data, reply;
- data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- remote()->transact(BnSurfaceComposer::REVOKE_GPU, data, &reply);
- return reply.readInt32();
- }
-
virtual void signal() const
{
Parcel data, reply;
@@ -156,124 +126,61 @@ IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
- status_t err = BnInterface<ISurfaceComposer>::onTransact(code, data, reply, flags);
- if (err == NO_ERROR)
- return err;
-
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
-
switch(code) {
case CREATE_CONNECTION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> b = createConnection()->asBinder();
reply->writeStrongBinder(b);
} break;
case OPEN_GLOBAL_TRANSACTION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
openGlobalTransaction();
} break;
case CLOSE_GLOBAL_TRANSACTION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
closeGlobalTransaction();
} break;
case SET_ORIENTATION: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32();
int orientation = data.readInt32();
uint32_t flags = data.readInt32();
reply->writeInt32( setOrientation(dpy, orientation, flags) );
} break;
case FREEZE_DISPLAY: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32();
uint32_t flags = data.readInt32();
reply->writeInt32( freezeDisplay(dpy, flags) );
} break;
case UNFREEZE_DISPLAY: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
DisplayID dpy = data.readInt32();
uint32_t flags = data.readInt32();
reply->writeInt32( unfreezeDisplay(dpy, flags) );
} break;
case BOOT_FINISHED: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
bootFinished();
} break;
- case REVOKE_GPU: {
- reply->writeInt32( revokeGPU() );
- } break;
case SIGNAL: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
signal();
} break;
case GET_CBLK: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> b = getCblk()->asBinder();
reply->writeStrongBinder(b);
} break;
- case REQUEST_GPU: {
- // TODO: this should be protected by a permission
- gpu_info_t info;
- sp<IGPUCallback> callback
- = interface_cast<IGPUCallback>(data.readStrongBinder());
- status_t res = requestGPU(callback, &info);
-
- // FIXME: for now, we don't dynamically allocate the regions array
- size_t maxCount = sizeof(info.regions)/sizeof(*info.regions);
- if (info.count > maxCount)
- return BAD_VALUE;
-
- reply->writeStrongBinder(info.regs->asBinder());
- reply->writeInt32(info.count);
- for (size_t i=0 ; i<info.count ; i++) {
- reply->writeStrongBinder(info.regions[i].region->asBinder());
- reply->writeInt32(info.regions[i].reserved);
- }
- reply->writeInt32(res);
- } break;
default:
- return UNKNOWN_TRANSACTION;
+ return BBinder::onTransact(code, data, reply, flags);
}
return NO_ERROR;
}
// ----------------------------------------------------------------------------
-enum {
- // Note: BOOT_FINISHED must remain this value, it is called by ActivityManagerService.
- GPU_LOST = IBinder::FIRST_CALL_TRANSACTION
-};
-
-class BpGPUCallback : public BpInterface<IGPUCallback>
-{
-public:
- BpGPUCallback(const sp<IBinder>& impl)
- : BpInterface<IGPUCallback>(impl)
- {
- }
-
- virtual void gpuLost()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IGPUCallback::getInterfaceDescriptor());
- remote()->transact(GPU_LOST, data, &reply, IBinder::FLAG_ONEWAY);
- }
-};
-
-IMPLEMENT_META_INTERFACE(GPUCallback, "android.ui.IGPUCallback");
-
-status_t BnGPUCallback::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GPU_LOST: {
- CHECK_INTERFACE(IGPUCallback, data, reply);
- gpuLost();
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
};
diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp
index dab5f71..4a6a1d7 100644
--- a/libs/ui/ISurfaceFlingerClient.cpp
+++ b/libs/ui/ISurfaceFlingerClient.cpp
@@ -21,10 +21,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <ui/ISurface.h>
#include <ui/ISurfaceFlingerClient.h>
@@ -64,12 +64,12 @@ public:
{
}
- virtual void getControlBlocks(sp<IMemory>* ctl) const
+ virtual sp<IMemoryHeap> getControlBlock() const
{
Parcel data, reply;
data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor());
remote()->transact(GET_CBLK, data, &reply);
- *ctl = interface_cast<IMemory>(reply.readStrongBinder());
+ return interface_cast<IMemoryHeap>(reply.readStrongBinder());
}
virtual sp<ISurface> createSurface( surface_data_t* params,
@@ -118,12 +118,6 @@ IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnSurfaceFlingerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -132,8 +126,7 @@ status_t BnSurfaceFlingerClient::onTransact(
switch(code) {
case GET_CBLK: {
CHECK_INTERFACE(ISurfaceFlingerClient, data, reply);
- sp<IMemory> ctl;
- getControlBlocks(&ctl);
+ sp<IMemoryHeap> ctl(getControlBlock());
reply->writeStrongBinder(ctl->asBinder());
return NO_ERROR;
} break;
@@ -196,10 +189,11 @@ status_t BnSurfaceFlingerClient::onTransact(
status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& parcel)
{
- token = parcel.readInt32();
- identity = parcel.readInt32();
- heap[0] = interface_cast<IMemoryHeap>(parcel.readStrongBinder());
- heap[1] = interface_cast<IMemoryHeap>(parcel.readStrongBinder());
+ token = parcel.readInt32();
+ identity = parcel.readInt32();
+ width = parcel.readInt32();
+ height = parcel.readInt32();
+ format = parcel.readInt32();
return NO_ERROR;
}
@@ -207,8 +201,9 @@ status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) co
{
parcel->writeInt32(token);
parcel->writeInt32(identity);
- parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL);
- parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL);
+ parcel->writeInt32(width);
+ parcel->writeInt32(height);
+ parcel->writeInt32(format);
return NO_ERROR;
}
diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp
index 0b6374b..a53ffb7 100644
--- a/libs/ui/LayerState.cpp
+++ b/libs/ui/LayerState.cpp
@@ -15,7 +15,7 @@
*/
#include <utils/Errors.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <private/ui/LayerState.h>
namespace android {
diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp
index 59c6514..3aa8950 100644
--- a/libs/ui/Overlay.cpp
+++ b/libs/ui/Overlay.cpp
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include <utils/IMemory.h>
-#include <utils/Parcel.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
#include <utils/Errors.h>
-#include <utils/MemoryHeapBase.h>
+#include <binder/MemoryHeapBase.h>
#include <ui/IOverlay.h>
#include <ui/Overlay.h>
@@ -59,6 +59,30 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer)
return mOverlayData->queueBuffer(mOverlayData, buffer);
}
+status_t Overlay::resizeInput(uint32_t width, uint32_t height)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->resizeInput(mOverlayData, width, height);
+}
+
+status_t Overlay::setParameter(int param, int value)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->setParameter(mOverlayData, param, value);
+}
+
+status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->setCrop(mOverlayData, x, y, w, h);
+}
+
+status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h)
+{
+ if (mStatus != NO_ERROR) return mStatus;
+ return mOverlayData->getCrop(mOverlayData, x, y, w, h);
+}
+
int32_t Overlay::getBufferCount() const
{
if (mStatus != NO_ERROR) return mStatus;
@@ -73,6 +97,15 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer)
void Overlay::destroy() {
if (mStatus != NO_ERROR) return;
+
+ // Must delete the objects in reverse creation order, thus the
+ // data side must be closed first and then the destroy send to
+ // the control side.
+ if (mOverlayData) {
+ overlay_data_close(mOverlayData);
+ mOverlayData = NULL;
+ }
+
mOverlayRef->mOverlayChannel->destroy();
}
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 26e694a..d21ed57 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -16,295 +16,661 @@
#define LOG_TAG "Region"
-#include <stdio.h>
-#include <utils/Atomic.h>
-#include <utils/Debug.h>
+#include <limits.h>
+
+#include <utils/Log.h>
#include <utils/String8.h>
+
+#include <ui/Rect.h>
#include <ui/Region.h>
+#include <ui/Point.h>
+
+#include <private/ui/RegionHelper.h>
+
+// ----------------------------------------------------------------------------
+#define VALIDATE_REGIONS (false)
+#define VALIDATE_WITH_CORECG (false)
+// ----------------------------------------------------------------------------
+
+#if VALIDATE_WITH_CORECG
+#include <core/SkRegion.h>
+#endif
namespace android {
+// ----------------------------------------------------------------------------
+
+enum {
+ op_nand = region_operator<Rect>::op_nand,
+ op_and = region_operator<Rect>::op_and,
+ op_or = region_operator<Rect>::op_or,
+ op_xor = region_operator<Rect>::op_xor
+};
// ----------------------------------------------------------------------------
Region::Region()
+ : mBounds(0,0)
{
}
Region::Region(const Region& rhs)
- : mRegion(rhs.mRegion)
-{
-}
-
-Region::Region(const SkRegion& rhs)
- : mRegion(rhs)
-{
-}
-
-Region::~Region()
+ : mBounds(rhs.mBounds), mStorage(rhs.mStorage)
{
}
Region::Region(const Rect& rhs)
+ : mBounds(rhs)
{
- set(rhs);
}
Region::Region(const Parcel& parcel)
{
- read(parcel);
+ status_t err = read(parcel);
+ LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err));
}
Region::Region(const void* buffer)
{
- read(buffer);
+ status_t err = read(buffer);
+ LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err));
}
-Region& Region::operator = (const Region& rhs)
+Region::~Region()
{
- mRegion = rhs.mRegion;
- return *this;
}
-const SkRegion& Region::toSkRegion() const
+Region& Region::operator = (const Region& rhs)
{
- return mRegion;
+#if VALIDATE_REGIONS
+ validate(rhs, "operator=");
+#endif
+ mBounds = rhs.mBounds;
+ mStorage = rhs.mStorage;
+ return *this;
}
-Rect Region::bounds() const
+Region& Region::makeBoundsSelf()
{
- const SkIRect& b(mRegion.getBounds());
- return Rect(b.fLeft, b.fTop, b.fRight, b.fBottom);
+ mStorage.clear();
+ return *this;
}
void Region::clear()
{
- mRegion.setEmpty();
+ mBounds.clear();
+ mStorage.clear();
}
void Region::set(const Rect& r)
{
- SkIRect ir;
- ir.set(r.left, r.top, r.right, r.bottom);
- mRegion.setRect(ir);
+ mBounds = r;
+ mStorage.clear();
+}
+
+void Region::set(uint32_t w, uint32_t h)
+{
+ mBounds = Rect(int(w), int(h));
+ mStorage.clear();
}
// ----------------------------------------------------------------------------
-Region& Region::orSelf(const Rect& r)
+void Region::addRectUnchecked(int l, int t, int r, int b)
{
- SkIRect ir;
- ir.set(r.left, r.top, r.right, r.bottom);
- mRegion.op(ir, SkRegion::kUnion_Op);
- return *this;
+ mStorage.add(Rect(l,t,r,b));
+#if VALIDATE_REGIONS
+ validate(*this, "addRectUnchecked");
+#endif
}
-Region& Region::andSelf(const Rect& r)
-{
- SkIRect ir;
- ir.set(r.left, r.top, r.right, r.bottom);
- mRegion.op(ir, SkRegion::kIntersect_Op);
+// ----------------------------------------------------------------------------
+
+Region& Region::orSelf(const Rect& r) {
+ return operationSelf(r, op_or);
+}
+Region& Region::andSelf(const Rect& r) {
+ return operationSelf(r, op_and);
+}
+Region& Region::subtractSelf(const Rect& r) {
+ return operationSelf(r, op_nand);
+}
+Region& Region::operationSelf(const Rect& r, int op) {
+ Region lhs(*this);
+ boolean_operation(op, *this, lhs, r);
return *this;
}
// ----------------------------------------------------------------------------
Region& Region::orSelf(const Region& rhs) {
- mRegion.op(rhs.mRegion, SkRegion::kUnion_Op);
- return *this;
+ return operationSelf(rhs, op_or);
}
-
Region& Region::andSelf(const Region& rhs) {
- mRegion.op(rhs.mRegion, SkRegion::kIntersect_Op);
- return *this;
+ return operationSelf(rhs, op_and);
}
-
Region& Region::subtractSelf(const Region& rhs) {
- mRegion.op(rhs.mRegion, SkRegion::kDifference_Op);
+ return operationSelf(rhs, op_nand);
+}
+Region& Region::operationSelf(const Region& rhs, int op) {
+ Region lhs(*this);
+ boolean_operation(op, *this, lhs, rhs);
return *this;
}
Region& Region::translateSelf(int x, int y) {
- if (x|y) mRegion.translate(x, y);
+ if (x|y) translate(*this, x, y);
return *this;
}
-Region Region::merge(const Region& rhs) const {
- Region result;
- result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kUnion_Op);
- return result;
-}
+// ----------------------------------------------------------------------------
-Region Region::intersect(const Region& rhs) const {
+const Region Region::merge(const Rect& rhs) const {
+ return operation(rhs, op_or);
+}
+const Region Region::intersect(const Rect& rhs) const {
+ return operation(rhs, op_and);
+}
+const Region Region::subtract(const Rect& rhs) const {
+ return operation(rhs, op_nand);
+}
+const Region Region::operation(const Rect& rhs, int op) const {
Region result;
- result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kIntersect_Op);
+ boolean_operation(op, result, *this, rhs);
return result;
}
-Region Region::subtract(const Region& rhs) const {
+// ----------------------------------------------------------------------------
+
+const Region Region::merge(const Region& rhs) const {
+ return operation(rhs, op_or);
+}
+const Region Region::intersect(const Region& rhs) const {
+ return operation(rhs, op_and);
+}
+const Region Region::subtract(const Region& rhs) const {
+ return operation(rhs, op_nand);
+}
+const Region Region::operation(const Region& rhs, int op) const {
Region result;
- result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kDifference_Op);
+ boolean_operation(op, result, *this, rhs);
return result;
}
-Region Region::translate(int x, int y) const {
+const Region Region::translate(int x, int y) const {
Region result;
- mRegion.translate(x, y, &result.mRegion);
+ translate(result, *this, x, y);
return result;
}
// ----------------------------------------------------------------------------
Region& Region::orSelf(const Region& rhs, int dx, int dy) {
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- mRegion.op(r, SkRegion::kUnion_Op);
- return *this;
+ return operationSelf(rhs, dx, dy, op_or);
}
-
Region& Region::andSelf(const Region& rhs, int dx, int dy) {
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- mRegion.op(r, SkRegion::kIntersect_Op);
- return *this;
+ return operationSelf(rhs, dx, dy, op_and);
}
-
Region& Region::subtractSelf(const Region& rhs, int dx, int dy) {
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- mRegion.op(r, SkRegion::kDifference_Op);
+ return operationSelf(rhs, dx, dy, op_nand);
+}
+Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) {
+ Region lhs(*this);
+ boolean_operation(op, *this, lhs, rhs, dx, dy);
return *this;
}
-Region Region::merge(const Region& rhs, int dx, int dy) const {
+// ----------------------------------------------------------------------------
+
+const Region Region::merge(const Region& rhs, int dx, int dy) const {
+ return operation(rhs, dx, dy, op_or);
+}
+const Region Region::intersect(const Region& rhs, int dx, int dy) const {
+ return operation(rhs, dx, dy, op_and);
+}
+const Region Region::subtract(const Region& rhs, int dx, int dy) const {
+ return operation(rhs, dx, dy, op_nand);
+}
+const Region Region::operation(const Region& rhs, int dx, int dy, int op) const {
Region result;
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- result.mRegion.op(mRegion, r, SkRegion::kUnion_Op);
+ boolean_operation(op, result, *this, rhs, dx, dy);
return result;
}
-Region Region::intersect(const Region& rhs, int dx, int dy) const {
- Region result;
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- result.mRegion.op(mRegion, r, SkRegion::kIntersect_Op);
+// ----------------------------------------------------------------------------
+
+// This is our region rasterizer, which merges rects and spans together
+// to obtain an optimal region.
+class Region::rasterizer : public region_operator<Rect>::region_rasterizer
+{
+ Rect& bounds;
+ Vector<Rect>& storage;
+ Rect* head;
+ Rect* tail;
+ Vector<Rect> span;
+ Rect* cur;
+public:
+ rasterizer(Region& reg)
+ : bounds(reg.mBounds), storage(reg.mStorage), head(), tail(), cur() {
+ bounds.top = bounds.bottom = 0;
+ bounds.left = INT_MAX;
+ bounds.right = INT_MIN;
+ storage.clear();
+ }
+
+ ~rasterizer() {
+ if (span.size()) {
+ flushSpan();
+ }
+ if (storage.size()) {
+ bounds.top = storage.itemAt(0).top;
+ bounds.bottom = storage.top().bottom;
+ if (storage.size() == 1) {
+ storage.clear();
+ }
+ } else {
+ bounds.left = 0;
+ bounds.right = 0;
+ }
+ }
+
+ virtual void operator()(const Rect& rect) {
+ //LOGD(">>> %3d, %3d, %3d, %3d",
+ // rect.left, rect.top, rect.right, rect.bottom);
+ if (span.size()) {
+ if (cur->top != rect.top) {
+ flushSpan();
+ } else if (cur->right == rect.left) {
+ cur->right = rect.right;
+ return;
+ }
+ }
+ span.add(rect);
+ cur = span.editArray() + (span.size() - 1);
+ }
+private:
+ template<typename T>
+ static inline T min(T rhs, T lhs) { return rhs < lhs ? rhs : lhs; }
+ template<typename T>
+ static inline T max(T rhs, T lhs) { return rhs > lhs ? rhs : lhs; }
+ void flushSpan() {
+ bool merge = false;
+ if (tail-head == ssize_t(span.size())) {
+ Rect const* p = cur;
+ Rect const* q = head;
+ if (p->top == q->bottom) {
+ merge = true;
+ while (q != tail) {
+ if ((p->left != q->left) || (p->right != q->right)) {
+ merge = false;
+ break;
+ }
+ p++, q++;
+ }
+ }
+ }
+ if (merge) {
+ const int bottom = span[0].bottom;
+ Rect* r = head;
+ while (r != tail) {
+ r->bottom = bottom;
+ r++;
+ }
+ } else {
+ bounds.left = min(span.itemAt(0).left, bounds.left);
+ bounds.right = max(span.top().right, bounds.right);
+ storage.appendVector(span);
+ tail = storage.editArray() + storage.size();
+ head = tail - span.size();
+ }
+ span.clear();
+ }
+};
+
+bool Region::validate(const Region& reg, const char* name)
+{
+ bool result = true;
+ const_iterator cur = reg.begin();
+ const_iterator const tail = reg.end();
+ const_iterator prev = cur++;
+ Rect b(*prev);
+ while (cur != tail) {
+ b.left = b.left < cur->left ? b.left : cur->left;
+ b.top = b.top < cur->top ? b.top : cur->top;
+ b.right = b.right > cur->right ? b.right : cur->right;
+ b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom;
+ if (cur->top == prev->top) {
+ if (cur->bottom != prev->bottom) {
+ LOGE("%s: invalid span %p", name, cur);
+ result = false;
+ } else if (cur->left < prev->right) {
+ LOGE("%s: spans overlap horizontally prev=%p, cur=%p",
+ name, prev, cur);
+ result = false;
+ }
+ } else if (cur->top < prev->bottom) {
+ LOGE("%s: spans overlap vertically prev=%p, cur=%p",
+ name, prev, cur);
+ result = false;
+ }
+ prev = cur;
+ cur++;
+ }
+ if (b != reg.getBounds()) {
+ result = false;
+ LOGE("%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name,
+ b.left, b.top, b.right, b.bottom,
+ reg.getBounds().left, reg.getBounds().top,
+ reg.getBounds().right, reg.getBounds().bottom);
+ }
+ if (result == false) {
+ reg.dump(name);
+ }
return result;
}
-Region Region::subtract(const Region& rhs, int dx, int dy) const {
- Region result;
- SkRegion r(rhs.mRegion);
- r.translate(dx, dy);
- result.mRegion.op(mRegion, r, SkRegion::kDifference_Op);
- return result;
+void Region::boolean_operation(int op, Region& dst,
+ const Region& lhs,
+ const Region& rhs, int dx, int dy)
+{
+ size_t lhs_count;
+ Rect const * const lhs_rects = lhs.getArray(&lhs_count);
+
+ size_t rhs_count;
+ Rect const * const rhs_rects = rhs.getArray(&rhs_count);
+
+ region_operator<Rect>::region lhs_region(lhs_rects, lhs_count);
+ region_operator<Rect>::region rhs_region(rhs_rects, rhs_count, dx, dy);
+ region_operator<Rect> operation(op, lhs_region, rhs_region);
+ { // scope for rasterizer (dtor has side effects)
+ rasterizer r(dst);
+ operation(r);
+ }
+
+#if VALIDATE_REGIONS
+ validate(lhs, "boolean_operation: lhs");
+ validate(rhs, "boolean_operation: rhs");
+ validate(dst, "boolean_operation: dst");
+#endif
+
+#if VALIDATE_WITH_CORECG
+ SkRegion sk_lhs;
+ SkRegion sk_rhs;
+ SkRegion sk_dst;
+
+ for (size_t i=0 ; i<lhs_count ; i++)
+ sk_lhs.op(
+ lhs_rects[i].left + dx,
+ lhs_rects[i].top + dy,
+ lhs_rects[i].right + dx,
+ lhs_rects[i].bottom + dy,
+ SkRegion::kUnion_Op);
+
+ for (size_t i=0 ; i<rhs_count ; i++)
+ sk_rhs.op(
+ rhs_rects[i].left + dx,
+ rhs_rects[i].top + dy,
+ rhs_rects[i].right + dx,
+ rhs_rects[i].bottom + dy,
+ SkRegion::kUnion_Op);
+
+ const char* name = "---";
+ SkRegion::Op sk_op;
+ switch (op) {
+ case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break;
+ case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break;
+ case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break;
+ }
+ sk_dst.op(sk_lhs, sk_rhs, sk_op);
+
+ if (sk_dst.isEmpty() && dst.isEmpty())
+ return;
+
+ bool same = true;
+ Region::const_iterator head = dst.begin();
+ Region::const_iterator const tail = dst.end();
+ SkRegion::Iterator it(sk_dst);
+ while (!it.done()) {
+ if (head != tail) {
+ if (
+ head->left != it.rect().fLeft ||
+ head->top != it.rect().fTop ||
+ head->right != it.rect().fRight ||
+ head->bottom != it.rect().fBottom
+ ) {
+ same = false;
+ break;
+ }
+ } else {
+ same = false;
+ break;
+ }
+ head++;
+ it.next();
+ }
+
+ if (head != tail) {
+ same = false;
+ }
+
+ if(!same) {
+ LOGD("---\nregion boolean %s failed", name);
+ lhs.dump("lhs");
+ rhs.dump("rhs");
+ dst.dump("dst");
+ LOGD("should be");
+ SkRegion::Iterator it(sk_dst);
+ while (!it.done()) {
+ LOGD(" [%3d, %3d, %3d, %3d]",
+ it.rect().fLeft,
+ it.rect().fTop,
+ it.rect().fRight,
+ it.rect().fBottom);
+ it.next();
+ }
+ }
+#endif
}
-// ----------------------------------------------------------------------------
+void Region::boolean_operation(int op, Region& dst,
+ const Region& lhs,
+ const Rect& rhs, int dx, int dy)
+{
+#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS
+ boolean_operation(op, dst, lhs, Region(rhs), dx, dy);
+#else
+ size_t lhs_count;
+ Rect const * const lhs_rects = lhs.getArray(&lhs_count);
+
+ region_operator<Rect>::region lhs_region(lhs_rects, lhs_count);
+ region_operator<Rect>::region rhs_region(&rhs, 1, dx, dy);
+ region_operator<Rect> operation(op, lhs_region, rhs_region);
+ { // scope for rasterizer (dtor has side effects)
+ rasterizer r(dst);
+ operation(r);
+ }
+
+#endif
+}
-Region::iterator::iterator(const Region& r)
- : mIt(r.mRegion)
+void Region::boolean_operation(int op, Region& dst,
+ const Region& lhs, const Region& rhs)
{
+ boolean_operation(op, dst, lhs, rhs, 0, 0);
}
-int Region::iterator::iterate(Rect* rect)
+void Region::boolean_operation(int op, Region& dst,
+ const Region& lhs, const Rect& rhs)
{
- if (mIt.done())
- return 0;
- const SkIRect& r(mIt.rect());
- rect->left = r.fLeft;
- rect->top = r.fTop;
- rect->right = r.fRight;
- rect->bottom= r.fBottom;
- mIt.next();
- return 1;
+ boolean_operation(op, dst, lhs, rhs, 0, 0);
}
-// ----------------------------------------------------------------------------
+void Region::translate(Region& reg, int dx, int dy)
+{
+ if (!reg.isEmpty()) {
+#if VALIDATE_REGIONS
+ validate(reg, "translate (before)");
+#endif
+ reg.mBounds.translate(dx, dy);
+ size_t count = reg.mStorage.size();
+ Rect* rects = reg.mStorage.editArray();
+ while (count) {
+ rects->translate(dx, dy);
+ rects++;
+ count--;
+ }
+#if VALIDATE_REGIONS
+ validate(reg, "translate (after)");
+#endif
+ }
+}
+
+void Region::translate(Region& dst, const Region& reg, int dx, int dy)
+{
+ dst = reg;
+ translate(dst, dx, dy);
+}
-// we write a 4byte size ahead of the actual region, so we know how much we'll need for reading
+// ----------------------------------------------------------------------------
status_t Region::write(Parcel& parcel) const
{
- int32_t size = mRegion.flatten(NULL);
- parcel.writeInt32(size);
- mRegion.flatten(parcel.writeInplace(size));
+#if VALIDATE_REGIONS
+ validate(*this, "write(Parcel)");
+#endif
+ status_t err;
+ const size_t count = mStorage.size();
+ const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect);
+ void* buffer = parcel.writeInplace(sizeNeeded);
+ if (!buffer) return NO_MEMORY;
+ ssize_t written = Region::write(buffer, sizeNeeded);
+ if (written < 0) return status_t(written);
return NO_ERROR;
}
status_t Region::read(const Parcel& parcel)
{
- size_t size = parcel.readInt32();
- mRegion.unflatten(parcel.readInplace(size));
+ void const* buffer = parcel.readInplace(sizeof(int32_t));
+ if (!buffer) return NO_MEMORY;
+ const size_t count = *static_cast<int32_t const *>(buffer);
+ void const* dummy = parcel.readInplace((1+count)*sizeof(Rect));
+ if (!dummy) return NO_MEMORY;
+ const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect);
+ const ssize_t read = Region::read(buffer);
+ if (read < 0) return status_t(read);
+#if VALIDATE_REGIONS
+ validate(*this, "read(Parcel)");
+#endif
return NO_ERROR;
}
ssize_t Region::write(void* buffer, size_t size) const
{
- size_t sizeNeeded = mRegion.flatten(NULL);
+#if VALIDATE_REGIONS
+ validate(*this, "write(buffer)");
+#endif
+ const size_t count = mStorage.size();
+ const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect);
if (sizeNeeded > size) return NO_MEMORY;
- return mRegion.flatten(buffer);
+ int32_t* const p = static_cast<int32_t*>(buffer);
+ *p = count;
+ memcpy(p+1, &mBounds, sizeof(Rect));
+ if (count) {
+ memcpy(p+5, mStorage.array(), count*sizeof(Rect));
+ }
+ return ssize_t(sizeNeeded);
}
ssize_t Region::read(const void* buffer)
{
- return mRegion.unflatten(buffer);
+ int32_t const* const p = static_cast<int32_t const*>(buffer);
+ const size_t count = *p;
+ memcpy(&mBounds, p+1, sizeof(Rect));
+ mStorage.clear();
+ if (count) {
+ mStorage.insertAt(0, count);
+ memcpy(mStorage.editArray(), p+5, count*sizeof(Rect));
+ }
+#if VALIDATE_REGIONS
+ validate(*this, "read(buffer)");
+#endif
+ return ssize_t(sizeof(int32_t) + (1+count)*sizeof(Rect));
}
ssize_t Region::writeEmpty(void* buffer, size_t size)
{
- if (size < 4) return NO_MEMORY;
- // this needs to stay in sync with SkRegion
- *static_cast<int32_t*>(buffer) = -1;
- return 4;
+ const size_t sizeNeeded = sizeof(int32_t) + sizeof(Rect);
+ if (sizeNeeded > size) return NO_MEMORY;
+ int32_t* const p = static_cast<int32_t*>(buffer);
+ memset(p, 0, sizeNeeded);
+ return ssize_t(sizeNeeded);
}
bool Region::isEmpty(void* buffer)
{
- // this needs to stay in sync with SkRegion
- return *static_cast<int32_t*>(buffer) == -1;
+ int32_t const* const p = static_cast<int32_t const*>(buffer);
+ Rect const* const b = reinterpret_cast<Rect const *>(p+1);
+ return b->isEmpty();
+}
+
+// ----------------------------------------------------------------------------
+
+Region::const_iterator Region::begin() const {
+ return isRect() ? &mBounds : mStorage.array();
+}
+
+Region::const_iterator Region::end() const {
+ return isRect() ? ((&mBounds) + 1) : (mStorage.array() + mStorage.size());
+}
+
+Rect const* Region::getArray(size_t* count) const {
+ const_iterator const b(begin());
+ const_iterator const e(end());
+ if (count) *count = e-b;
+ return b;
}
-size_t Region::rects(Vector<Rect>& rectList) const
+size_t Region::getRects(Vector<Rect>& rectList) const
{
- rectList.clear();
- if (!isEmpty()) {
- SkRegion::Iterator iterator(mRegion);
- while( !iterator.done() ) {
- const SkIRect& ir(iterator.rect());
- rectList.push(Rect(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom));
- iterator.next();
- }
+ rectList = mStorage;
+ if (rectList.isEmpty()) {
+ rectList.clear();
+ rectList.add(mBounds);
}
return rectList.size();
}
+// ----------------------------------------------------------------------------
+
void Region::dump(String8& out, const char* what, uint32_t flags) const
{
(void)flags;
- Vector<Rect> r;
- rects(r);
-
+ const_iterator head = begin();
+ const_iterator const tail = end();
+
size_t SIZE = 256;
char buffer[SIZE];
-
- snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", what, this, r.size());
+
+ snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n",
+ what, this, tail-head);
out.append(buffer);
- for (size_t i=0 ; i<r.size() ; i++) {
+ while (head != tail) {
snprintf(buffer, SIZE, " [%3d, %3d, %3d, %3d]\n",
- r[i].left, r[i].top,r[i].right,r[i].bottom);
+ head->left, head->top, head->right, head->bottom);
out.append(buffer);
+ head++;
}
}
void Region::dump(const char* what, uint32_t flags) const
{
(void)flags;
- Vector<Rect> r;
- rects(r);
- LOGD(" Region %s (this=%p, count=%d)\n", what, this, r.size());
- for (size_t i=0 ; i<r.size() ; i++) {
+ const_iterator head = begin();
+ const_iterator const tail = end();
+ LOGD(" Region %s (this=%p, count=%d)\n", what, this, tail-head);
+ while (head != tail) {
LOGD(" [%3d, %3d, %3d, %3d]\n",
- r[i].left, r[i].top,r[i].right,r[i].bottom);
+ head->left, head->top, head->right, head->bottom);
+ head++;
}
}
diff --git a/libs/ui/SharedBufferStack.cpp b/libs/ui/SharedBufferStack.cpp
new file mode 100644
index 0000000..5995af5
--- /dev/null
+++ b/libs/ui/SharedBufferStack.cpp
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SharedBufferStack"
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Debug.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <private/ui/SharedBufferStack.h>
+
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#define DEBUG_ATOMICS 0
+
+namespace android {
+// ----------------------------------------------------------------------------
+
+SharedClient::SharedClient()
+ : lock(Mutex::SHARED)
+{
+}
+
+SharedClient::~SharedClient() {
+}
+
+
+// these functions are used by the clients
+status_t SharedClient::validate(size_t i) const {
+ if (uint32_t(i) >= uint32_t(NUM_LAYERS_MAX))
+ return BAD_INDEX;
+ return surfaces[i].status;
+}
+
+uint32_t SharedClient::getIdentity(size_t token) const {
+ return uint32_t(surfaces[token].identity);
+}
+
+status_t SharedClient::setIdentity(size_t token, uint32_t identity) {
+ if (token >= NUM_LAYERS_MAX)
+ return BAD_INDEX;
+ surfaces[token].identity = identity;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+
+SharedBufferStack::SharedBufferStack()
+ : inUse(-1), identity(-1), status(NO_ERROR)
+{
+}
+
+status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
+{
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return BAD_INDEX;
+
+ // in the current implementation we only send a single rectangle
+ const Rect bounds(dirty.getBounds());
+ FlatRegion& reg(dirtyRegion[buffer]);
+ reg.count = 1;
+ reg.rects[0] = uint16_t(bounds.left);
+ reg.rects[1] = uint16_t(bounds.top);
+ reg.rects[2] = uint16_t(bounds.right);
+ reg.rects[3] = uint16_t(bounds.bottom);
+ return NO_ERROR;
+}
+
+Region SharedBufferStack::getDirtyRegion(int buffer) const
+{
+ Region res;
+ if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+ return res;
+
+ const FlatRegion& reg(dirtyRegion[buffer]);
+ res.set(Rect(reg.rects[0], reg.rects[1], reg.rects[2], reg.rects[3]));
+ return res;
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
+ int surface, int num)
+ : mSharedClient(sharedClient),
+ mSharedStack(sharedClient->surfaces + surface),
+ mNumBuffers(num)
+{
+}
+
+SharedBufferBase::~SharedBufferBase()
+{
+}
+
+uint32_t SharedBufferBase::getIdentity()
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.identity;
+}
+
+size_t SharedBufferBase::getFrontBuffer() const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return size_t( stack.head );
+}
+
+String8 SharedBufferBase::dump(char const* prefix) const
+{
+ const size_t SIZE = 1024;
+ char buffer[SIZE];
+ String8 result;
+ SharedBufferStack& stack( *mSharedStack );
+ snprintf(buffer, SIZE,
+ "%s[ head=%2d, available=%2d, queued=%2d ] "
+ "reallocMask=%08x, inUse=%2d, identity=%d, status=%d\n",
+ prefix, stack.head, stack.available, stack.queued,
+ stack.reallocMask, stack.inUse, stack.identity, stack.status);
+ result.append(buffer);
+ return result;
+}
+
+
+// ============================================================================
+// conditions and updates
+// ============================================================================
+
+SharedBufferClient::DequeueCondition::DequeueCondition(
+ SharedBufferClient* sbc) : ConditionBase(sbc) {
+}
+bool SharedBufferClient::DequeueCondition::operator()() {
+ return stack.available > 0;
+}
+
+SharedBufferClient::LockCondition::LockCondition(
+ SharedBufferClient* sbc, int buf) : ConditionBase(sbc), buf(buf) {
+}
+bool SharedBufferClient::LockCondition::operator()() {
+ return (buf != stack.head ||
+ (stack.queued > 0 && stack.inUse != buf));
+}
+
+SharedBufferServer::ReallocateCondition::ReallocateCondition(
+ SharedBufferBase* sbb, int buf) : ConditionBase(sbb), buf(buf) {
+}
+bool SharedBufferServer::ReallocateCondition::operator()() {
+ // TODO: we should also check that buf has been dequeued
+ return (buf != stack.head);
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
+}
+ssize_t SharedBufferClient::QueueUpdate::operator()() {
+ android_atomic_inc(&stack.queued);
+ return NO_ERROR;
+}
+
+SharedBufferClient::UndoDequeueUpdate::UndoDequeueUpdate(SharedBufferBase* sbb)
+ : UpdateBase(sbb) {
+}
+ssize_t SharedBufferClient::UndoDequeueUpdate::operator()() {
+ android_atomic_inc(&stack.available);
+ return NO_ERROR;
+}
+
+SharedBufferServer::UnlockUpdate::UnlockUpdate(
+ SharedBufferBase* sbb, int lockedBuffer)
+ : UpdateBase(sbb), lockedBuffer(lockedBuffer) {
+}
+ssize_t SharedBufferServer::UnlockUpdate::operator()() {
+ if (stack.inUse != lockedBuffer) {
+ LOGE("unlocking %d, but currently locked buffer is %d",
+ lockedBuffer, stack.inUse);
+ return BAD_VALUE;
+ }
+ android_atomic_write(-1, &stack.inUse);
+ return NO_ERROR;
+}
+
+SharedBufferServer::RetireUpdate::RetireUpdate(
+ SharedBufferBase* sbb, int numBuffers)
+ : UpdateBase(sbb), numBuffers(numBuffers) {
+}
+ssize_t SharedBufferServer::RetireUpdate::operator()() {
+ // head is only written in this function, which is single-thread.
+ int32_t head = stack.head;
+
+ // Preventively lock the current buffer before updating queued.
+ android_atomic_write(head, &stack.inUse);
+
+ // Decrement the number of queued buffers
+ int32_t queued;
+ do {
+ queued = stack.queued;
+ if (queued == 0) {
+ return NOT_ENOUGH_DATA;
+ }
+ } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));
+
+ // update the head pointer
+ head = ((head+1 >= numBuffers) ? 0 : head+1);
+
+ // lock the buffer before advancing head, which automatically unlocks
+ // the buffer we preventively locked upon entering this function
+ android_atomic_write(head, &stack.inUse);
+
+ // advance head
+ android_atomic_write(head, &stack.head);
+
+ // now that head has moved, we can increment the number of available buffers
+ android_atomic_inc(&stack.available);
+ return head;
+}
+
+// ============================================================================
+
+SharedBufferClient::SharedBufferClient(SharedClient* sharedClient,
+ int surface, int num)
+ : SharedBufferBase(sharedClient, surface, num), tail(0)
+{
+}
+
+ssize_t SharedBufferClient::dequeue()
+{
+ //LOGD("[%d] about to dequeue a buffer",
+ // mSharedStack->identity);
+ DequeueCondition condition(this);
+ status_t err = waitForCondition(condition);
+ if (err != NO_ERROR)
+ return ssize_t(err);
+
+
+ SharedBufferStack& stack( *mSharedStack );
+ // NOTE: 'stack.available' is part of the conditions, however
+ // decrementing it, never changes any conditions, so we don't need
+ // to do this as part of an update.
+ if (android_atomic_dec(&stack.available) == 0) {
+ LOGW("dequeue probably called from multiple threads!");
+ }
+
+ int dequeued = tail;
+ tail = ((tail+1 >= mNumBuffers) ? 0 : tail+1);
+ LOGD_IF(DEBUG_ATOMICS, "dequeued=%d, tail=%d, %s",
+ dequeued, tail, dump("").string());
+ return dequeued;
+}
+
+status_t SharedBufferClient::undoDequeue(int buf)
+{
+ UndoDequeueUpdate update(this);
+ status_t err = updateCondition( update );
+ return err;
+}
+
+status_t SharedBufferClient::lock(int buf)
+{
+ LockCondition condition(this, buf);
+ status_t err = waitForCondition(condition);
+ return err;
+}
+
+status_t SharedBufferClient::queue(int buf)
+{
+ QueueUpdate update(this);
+ status_t err = updateCondition( update );
+ LOGD_IF(DEBUG_ATOMICS, "queued=%d, %s", buf, dump("").string());
+ return err;
+}
+
+bool SharedBufferClient::needNewBuffer(int buffer) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ const uint32_t mask = 1<<buffer;
+ return (android_atomic_and(~mask, &stack.reallocMask) & mask) != 0;
+}
+
+status_t SharedBufferClient::setDirtyRegion(int buffer, const Region& reg)
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.setDirtyRegion(buffer, reg);
+}
+
+// ----------------------------------------------------------------------------
+
+SharedBufferServer::SharedBufferServer(SharedClient* sharedClient,
+ int surface, int num)
+ : SharedBufferBase(sharedClient, surface, num)
+{
+ mSharedStack->head = num-1;
+ mSharedStack->available = num;
+ mSharedStack->queued = 0;
+ mSharedStack->reallocMask = 0;
+ memset(mSharedStack->dirtyRegion, 0, sizeof(mSharedStack->dirtyRegion));
+}
+
+ssize_t SharedBufferServer::retireAndLock()
+{
+ RetireUpdate update(this, mNumBuffers);
+ ssize_t buf = updateCondition( update );
+ LOGD_IF(DEBUG_ATOMICS, "retire=%d, %s", int(buf), dump("").string());
+ return buf;
+}
+
+status_t SharedBufferServer::unlock(int buffer)
+{
+ UnlockUpdate update(this, buffer);
+ status_t err = updateCondition( update );
+ return err;
+}
+
+status_t SharedBufferServer::reallocate()
+{
+ SharedBufferStack& stack( *mSharedStack );
+ uint32_t mask = (1<<mNumBuffers)-1;
+ android_atomic_or(mask, &stack.reallocMask);
+ return NO_ERROR;
+}
+
+status_t SharedBufferServer::assertReallocate(int buffer)
+{
+ ReallocateCondition condition(this, buffer);
+ status_t err = waitForCondition(condition);
+ return err;
+}
+
+Region SharedBufferServer::getDirtyRegion(int buffer) const
+{
+ SharedBufferStack& stack( *mSharedStack );
+ return stack.getDirtyRegion(buffer);
+}
+
+// ---------------------------------------------------------------------------
+}; // namespace android
diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp
index 4ea9ae2..c3fbea2 100644
--- a/libs/ui/Surface.cpp
+++ b/libs/ui/Surface.cpp
@@ -23,233 +23,739 @@
#include <sys/types.h>
#include <sys/stat.h>
-#include <utils/Atomic.h>
#include <utils/Errors.h>
#include <utils/threads.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IMemory.h>
+#include <utils/CallStack.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IMemory.h>
#include <utils/Log.h>
+#include <ui/DisplayInfo.h>
+#include <ui/BufferMapper.h>
#include <ui/ISurface.h>
#include <ui/Surface.h>
#include <ui/SurfaceComposerClient.h>
#include <ui/Rect.h>
-#include <private/ui/SharedState.h>
+#include <pixelflinger/pixelflinger.h>
+
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/LayerState.h>
+#include <private/ui/SurfaceBuffer.h>
namespace android {
-// ---------------------------------------------------------------------------
+// ----------------------------------------------------------------------
+
+static status_t copyBlt(
+ const sp<SurfaceBuffer>& dst,
+ const sp<SurfaceBuffer>& src,
+ const Region& reg)
+{
+ status_t err;
+ uint8_t const * src_bits = NULL;
+ err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits);
+ LOGE_IF(err, "error locking src buffer %s", strerror(-err));
+
+ uint8_t* dst_bits = NULL;
+ err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits);
+ LOGE_IF(err, "error locking dst buffer %s", strerror(-err));
+
+ Region::const_iterator head(reg.begin());
+ Region::const_iterator tail(reg.end());
+ if (head != tail && src_bits && dst_bits) {
+ // NOTE: dst and src must be the same format
+ const size_t bpp = bytesPerPixel(src->format);
+ const size_t dbpr = dst->stride * bpp;
+ const size_t sbpr = src->stride * bpp;
+
+ while (head != tail) {
+ const Rect& r(*head++);
+ ssize_t h = r.height();
+ if (h <= 0) continue;
+ size_t size = r.width() * bpp;
+ uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
+ uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
+ if (dbpr==sbpr && size==sbpr) {
+ size *= h;
+ h = 1;
+ }
+ do {
+ memcpy(d, s, size);
+ d += dbpr;
+ s += sbpr;
+ } while (--h > 0);
+ }
+ }
+
+ if (src_bits)
+ src->unlock();
+
+ if (dst_bits)
+ dst->unlock();
+
+ return err;
+}
+
+// ============================================================================
+// SurfaceControl
+// ============================================================================
-Surface::Surface(const sp<SurfaceComposerClient>& client,
+SurfaceControl::SurfaceControl(
+ const sp<SurfaceComposerClient>& client,
const sp<ISurface>& surface,
const ISurfaceFlingerClient::surface_data_t& data,
- uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
- bool owner)
+ uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)
: mClient(client), mSurface(surface),
mToken(data.token), mIdentity(data.identity),
- mFormat(format), mFlags(flags), mOwner(owner)
+ mWidth(data.width), mHeight(data.height), mFormat(data.format),
+ mFlags(flags)
{
- mSwapRectangle.makeInvalid();
- mSurfaceHeapBase[0] = 0;
- mSurfaceHeapBase[1] = 0;
- mHeap[0] = data.heap[0];
- mHeap[1] = data.heap[1];
}
-
-Surface::Surface(Surface const* rhs)
- : mOwner(false)
+
+SurfaceControl::~SurfaceControl()
{
- mToken = rhs->mToken;
- mIdentity= rhs->mIdentity;
- mClient = rhs->mClient;
- mSurface = rhs->mSurface;
- mHeap[0] = rhs->mHeap[0];
- mHeap[1] = rhs->mHeap[1];
- mFormat = rhs->mFormat;
- mFlags = rhs->mFlags;
- mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0];
- mSurfaceHeapBase[1] = rhs->mSurfaceHeapBase[1];
- mSwapRectangle.makeInvalid();
+ destroy();
}
-Surface::~Surface()
+void SurfaceControl::destroy()
{
- if (mOwner && mToken>=0 && mClient!=0) {
+ if (isValid()) {
mClient->destroySurface(mToken);
}
+
+ // clear all references and trigger an IPC now, to make sure things
+ // happen without delay, since these resources are quite heavy.
mClient.clear();
mSurface.clear();
- mHeap[0].clear();
- mHeap[1].clear();
IPCThreadState::self()->flushCommands();
}
-sp<Surface> Surface::dup() const
+void SurfaceControl::clear()
{
- Surface const * r = this;
- if (this && mOwner) {
- // the only reason we need to do this is because of Java's garbage
- // collector: because we're creating a copy of the Surface
- // instead of a reference, we can garantee that when our last
- // reference goes away, the real surface will be deleted.
- // Without this hack (the code is correct too), we'd have to
- // wait for a GC for the surface to go away.
- r = new Surface(this);
+ // here, the window manager tells us explicitly that we should destroy
+ // the surface's resource. Soon after this call, it will also release
+ // its last reference (which will call the dtor); however, it is possible
+ // that a client living in the same process still holds references which
+ // would delay the call to the dtor -- that is why we need this explicit
+ // "clear()" call.
+ destroy();
+}
+
+bool SurfaceControl::isSameSurface(
+ const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs)
+{
+ if (lhs == 0 || rhs == 0)
+ return false;
+ return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
+}
+
+status_t SurfaceControl::setLayer(int32_t layer) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setLayer(mToken, layer);
+}
+status_t SurfaceControl::setPosition(int32_t x, int32_t y) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setPosition(mToken, x, y);
+}
+status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setSize(mToken, w, h);
+}
+status_t SurfaceControl::hide() {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->hide(mToken);
+}
+status_t SurfaceControl::show(int32_t layer) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->show(mToken, layer);
+}
+status_t SurfaceControl::freeze() {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->freeze(mToken);
+}
+status_t SurfaceControl::unfreeze() {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->unfreeze(mToken);
+}
+status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setFlags(mToken, flags, mask);
+}
+status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setTransparentRegionHint(mToken, transparent);
+}
+status_t SurfaceControl::setAlpha(float alpha) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setAlpha(mToken, alpha);
+}
+status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy);
+}
+status_t SurfaceControl::setFreezeTint(uint32_t tint) {
+ const sp<SurfaceComposerClient>& client(mClient);
+ if (client == 0) return NO_INIT;
+ status_t err = validate(client->mControl);
+ if (err < 0) return err;
+ return client->setFreezeTint(mToken, tint);
+}
+
+status_t SurfaceControl::validate(SharedClient const* cblk) const
+{
+ if (mToken<0 || mClient==0) {
+ LOGE("invalid token (%d, identity=%u) or client (%p)",
+ mToken, mIdentity, mClient.get());
+ return NO_INIT;
+ }
+ if (cblk == 0) {
+ LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
+ return NO_INIT;
+ }
+ status_t err = cblk->validate(mToken);
+ if (err != NO_ERROR) {
+ LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
+ mToken, mIdentity, err, strerror(-err));
+ return err;
}
- return const_cast<Surface*>(r);
+ uint32_t identity = cblk->getIdentity(mToken);
+ if (mIdentity != identity) {
+ LOGE("using an invalid surface id=%d, identity=%u should be %d",
+ mToken, mIdentity, identity);
+ return NO_INIT;
+ }
+ return NO_ERROR;
}
-status_t Surface::nextBuffer(SurfaceInfo* info) {
- return mClient->nextBuffer(this, info);
+status_t SurfaceControl::writeSurfaceToParcel(
+ const sp<SurfaceControl>& control, Parcel* parcel)
+{
+ uint32_t flags = 0;
+ uint32_t format = 0;
+ SurfaceID token = -1;
+ uint32_t identity = 0;
+ uint32_t width = 0;
+ uint32_t height = 0;
+ sp<SurfaceComposerClient> client;
+ sp<ISurface> sur;
+ if (SurfaceControl::isValid(control)) {
+ token = control->mToken;
+ identity = control->mIdentity;
+ client = control->mClient;
+ sur = control->mSurface;
+ width = control->mWidth;
+ height = control->mHeight;
+ format = control->mFormat;
+ flags = control->mFlags;
+ }
+ parcel->writeStrongBinder(client!=0 ? client->connection() : NULL);
+ parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
+ parcel->writeInt32(token);
+ parcel->writeInt32(identity);
+ parcel->writeInt32(width);
+ parcel->writeInt32(height);
+ parcel->writeInt32(format);
+ parcel->writeInt32(flags);
+ return NO_ERROR;
}
-status_t Surface::lock(SurfaceInfo* info, bool blocking) {
- return Surface::lock(info, NULL, blocking);
+sp<Surface> SurfaceControl::getSurface() const
+{
+ Mutex::Autolock _l(mLock);
+ if (mSurfaceData == 0) {
+ mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));
+ }
+ return mSurfaceData;
}
-status_t Surface::lock(SurfaceInfo* info, Region* dirty, bool blocking) {
- if (heapBase(0) == 0) return INVALID_OPERATION;
- if (heapBase(1) == 0) return INVALID_OPERATION;
- return mClient->lockSurface(this, info, dirty, blocking);
+// ============================================================================
+// Surface
+// ============================================================================
+
+Surface::Surface(const sp<SurfaceControl>& surface)
+ : mClient(surface->mClient), mSurface(surface->mSurface),
+ mToken(surface->mToken), mIdentity(surface->mIdentity),
+ mFormat(surface->mFormat), mFlags(surface->mFlags),
+ mBufferMapper(BufferMapper::get()), mSharedBufferClient(NULL),
+ mWidth(surface->mWidth), mHeight(surface->mHeight)
+{
+ mSharedBufferClient = new SharedBufferClient(
+ mClient->mControl, mToken, 2);
+
+ init();
}
-status_t Surface::unlockAndPost() {
- if (heapBase(0) == 0) return INVALID_OPERATION;
- if (heapBase(1) == 0) return INVALID_OPERATION;
- return mClient->unlockAndPostSurface(this);
+Surface::Surface(const Parcel& parcel)
+ : mBufferMapper(BufferMapper::get()), mSharedBufferClient(NULL)
+{
+ sp<IBinder> clientBinder = parcel.readStrongBinder();
+ mSurface = interface_cast<ISurface>(parcel.readStrongBinder());
+ mToken = parcel.readInt32();
+ mIdentity = parcel.readInt32();
+ mWidth = parcel.readInt32();
+ mHeight = parcel.readInt32();
+ mFormat = parcel.readInt32();
+ mFlags = parcel.readInt32();
+
+ // FIXME: what does that mean if clientBinder is NULL here?
+ if (clientBinder != NULL) {
+ mClient = SurfaceComposerClient::clientForConnection(clientBinder);
+
+ mSharedBufferClient = new SharedBufferClient(
+ mClient->mControl, mToken, 2);
+ }
+
+ init();
}
-status_t Surface::unlock() {
- if (heapBase(0) == 0) return INVALID_OPERATION;
- if (heapBase(1) == 0) return INVALID_OPERATION;
- return mClient->unlockSurface(this);
+void Surface::init()
+{
+ android_native_window_t::setSwapInterval = setSwapInterval;
+ android_native_window_t::dequeueBuffer = dequeueBuffer;
+ android_native_window_t::lockBuffer = lockBuffer;
+ android_native_window_t::queueBuffer = queueBuffer;
+ android_native_window_t::query = query;
+ android_native_window_t::perform = perform;
+ mSwapRectangle.makeInvalid();
+ DisplayInfo dinfo;
+ SurfaceComposerClient::getDisplayInfo(0, &dinfo);
+ const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi;
+ const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi;
+ // FIXME: set real values here
+ const_cast<int&>(android_native_window_t::minSwapInterval) = 1;
+ const_cast<int&>(android_native_window_t::maxSwapInterval) = 1;
+ const_cast<uint32_t&>(android_native_window_t::flags) = 0;
+ // be default we request a hardware surface
+ mUsage = GRALLOC_USAGE_HW_RENDER;
+ mUsageChanged = true;
+ mNeedFullUpdate = false;
}
-status_t Surface::setLayer(int32_t layer) {
- return mClient->setLayer(this, layer);
+Surface::~Surface()
+{
+ // this is a client-side operation, the surface is destroyed, unmap
+ // its buffers in this process.
+ for (int i=0 ; i<2 ; i++) {
+ if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) {
+ getBufferMapper().unregisterBuffer(mBuffers[i]->handle);
+ }
+ }
+
+ // clear all references and trigger an IPC now, to make sure things
+ // happen without delay, since these resources are quite heavy.
+ mClient.clear();
+ mSurface.clear();
+ delete mSharedBufferClient;
+ IPCThreadState::self()->flushCommands();
}
-status_t Surface::setPosition(int32_t x, int32_t y) {
- return mClient->setPosition(this, x, y);
+
+sp<SurfaceComposerClient> Surface::getClient() const {
+ return mClient;
}
-status_t Surface::setSize(uint32_t w, uint32_t h) {
- return mClient->setSize(this, w, h);
+
+sp<ISurface> Surface::getISurface() const {
+ return mSurface;
}
-status_t Surface::hide() {
- return mClient->hide(this);
+
+bool Surface::isValid() {
+ return mToken>=0 && mClient!=0;
}
-status_t Surface::show(int32_t layer) {
- return mClient->show(this, layer);
+
+status_t Surface::validate(SharedClient const* cblk) const
+{
+ sp<SurfaceComposerClient> client(getClient());
+ if (mToken<0 || mClient==0) {
+ LOGE("invalid token (%d, identity=%u) or client (%p)",
+ mToken, mIdentity, client.get());
+ return NO_INIT;
+ }
+ if (cblk == 0) {
+ LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
+ return NO_INIT;
+ }
+ status_t err = cblk->validate(mToken);
+ if (err != NO_ERROR) {
+ LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
+ mToken, mIdentity, err, strerror(-err));
+ return err;
+ }
+ uint32_t identity = cblk->getIdentity(mToken);
+ if (mIdentity != identity) {
+ LOGE("using an invalid surface id=%d, identity=%u should be %d",
+ mToken, mIdentity, identity);
+ return NO_INIT;
+ }
+ return NO_ERROR;
}
-status_t Surface::freeze() {
- return mClient->freeze(this);
+
+
+bool Surface::isSameSurface(
+ const sp<Surface>& lhs, const sp<Surface>& rhs)
+{
+ if (lhs == 0 || rhs == 0)
+ return false;
+
+ return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
}
-status_t Surface::unfreeze() {
- return mClient->unfreeze(this);
+
+// ----------------------------------------------------------------------------
+
+int Surface::setSwapInterval(android_native_window_t* window, int interval) {
+ return 0;
}
-status_t Surface::setFlags(uint32_t flags, uint32_t mask) {
- return mClient->setFlags(this, flags, mask);
+
+int Surface::dequeueBuffer(android_native_window_t* window,
+ android_native_buffer_t** buffer) {
+ Surface* self = getSelf(window);
+ return self->dequeueBuffer(buffer);
}
-status_t Surface::setTransparentRegionHint(const Region& transparent) {
- return mClient->setTransparentRegionHint(this, transparent);
+
+int Surface::lockBuffer(android_native_window_t* window,
+ android_native_buffer_t* buffer) {
+ Surface* self = getSelf(window);
+ return self->lockBuffer(buffer);
}
-status_t Surface::setAlpha(float alpha) {
- return mClient->setAlpha(this, alpha);
+
+int Surface::queueBuffer(android_native_window_t* window,
+ android_native_buffer_t* buffer) {
+ Surface* self = getSelf(window);
+ return self->queueBuffer(buffer);
}
-status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
- return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy);
+
+int Surface::query(android_native_window_t* window,
+ int what, int* value) {
+ Surface* self = getSelf(window);
+ return self->query(what, value);
}
-status_t Surface::setFreezeTint(uint32_t tint) {
- return mClient->setFreezeTint(this, tint);
+
+int Surface::perform(android_native_window_t* window,
+ int operation, ...) {
+ va_list args;
+ va_start(args, operation);
+ Surface* self = getSelf(window);
+ int res = self->perform(operation, args);
+ va_end(args);
+ return res;
}
-Region Surface::dirtyRegion() const {
- return mDirtyRegion;
+// ----------------------------------------------------------------------------
+
+status_t Surface::dequeueBuffer(sp<SurfaceBuffer>* buffer) {
+ android_native_buffer_t* out;
+ status_t err = dequeueBuffer(&out);
+ if (err == NO_ERROR) {
+ *buffer = SurfaceBuffer::getSelf(out);
+ }
+ return err;
}
-void Surface::setDirtyRegion(const Region& region) const {
- mDirtyRegion = region;
+
+// ----------------------------------------------------------------------------
+
+
+int Surface::dequeueBuffer(android_native_buffer_t** buffer)
+{
+ sp<SurfaceComposerClient> client(getClient());
+ status_t err = validate(client->mControl);
+ if (err != NO_ERROR)
+ return err;
+
+ ssize_t bufIdx = mSharedBufferClient->dequeue();
+ if (bufIdx < 0) {
+ LOGE("error dequeuing a buffer (%s)", strerror(bufIdx));
+ return bufIdx;
+ }
+
+ // FIXME: in case of failure below, we need to undo the dequeue
+
+ uint32_t usage;
+ const bool usageChanged = getUsage(&usage);
+ const sp<SurfaceBuffer>& backBuffer(mBuffers[bufIdx]);
+ if ((backBuffer == 0) || usageChanged ||
+ mSharedBufferClient->needNewBuffer(bufIdx)) {
+ err = getBufferLocked(bufIdx, usage);
+ LOGE_IF(err, "getBufferLocked(%ld, %08x) failed (%s)",
+ bufIdx, usage, strerror(-err));
+ if (err == NO_ERROR) {
+ // reset the width/height with the what we get from the buffer
+ mWidth = uint32_t(backBuffer->width);
+ mHeight = uint32_t(backBuffer->height);
+ }
+ }
+
+ // if we still don't have a buffer here, we probably ran out of memory
+ if (!err && backBuffer==0) {
+ err = NO_MEMORY;
+ }
+
+ if (err == NO_ERROR) {
+ mDirtyRegion.set(backBuffer->width, backBuffer->height);
+ *buffer = backBuffer.get();
+ } else {
+ mSharedBufferClient->undoDequeue(bufIdx);
+ }
+
+ return err;
}
-const Rect& Surface::swapRectangle() const {
- return mSwapRectangle;
+
+int Surface::lockBuffer(android_native_buffer_t* buffer)
+{
+ sp<SurfaceComposerClient> client(getClient());
+ status_t err = validate(client->mControl);
+ if (err != NO_ERROR)
+ return err;
+
+ int32_t bufIdx = SurfaceBuffer::getSelf(buffer)->getIndex();
+ err = mSharedBufferClient->lock(bufIdx);
+ LOGE_IF(err, "error locking buffer %d (%s)", bufIdx, strerror(-err));
+ return err;
}
-void Surface::setSwapRectangle(const Rect& r) {
- mSwapRectangle = r;
+
+int Surface::queueBuffer(android_native_buffer_t* buffer)
+{
+ sp<SurfaceComposerClient> client(getClient());
+ status_t err = validate(client->mControl);
+ if (err != NO_ERROR)
+ return err;
+
+ if (mSwapRectangle.isValid()) {
+ mDirtyRegion.set(mSwapRectangle);
+ }
+
+ int32_t bufIdx = SurfaceBuffer::getSelf(buffer)->getIndex();
+ mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
+ err = mSharedBufferClient->queue(bufIdx);
+ LOGE_IF(err, "error queuing buffer %d (%s)", bufIdx, strerror(-err));
+
+ if (err == NO_ERROR) {
+ // FIXME: can we avoid this IPC if we know there is one pending?
+ client->signalServer();
+ }
+ return err;
}
-sp<Surface> Surface::readFromParcel(Parcel* parcel)
+int Surface::query(int what, int* value)
{
- sp<SurfaceComposerClient> client;
- ISurfaceFlingerClient::surface_data_t data;
- sp<IBinder> clientBinder= parcel->readStrongBinder();
- sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder());
- data.heap[0] = interface_cast<IMemoryHeap>(parcel->readStrongBinder());
- data.heap[1] = interface_cast<IMemoryHeap>(parcel->readStrongBinder());
- data.token = parcel->readInt32();
- data.identity = parcel->readInt32();
- PixelFormat format = parcel->readInt32();
- uint32_t flags = parcel->readInt32();
+ switch (what) {
+ case NATIVE_WINDOW_WIDTH:
+ *value = int(mWidth);
+ return NO_ERROR;
+ case NATIVE_WINDOW_HEIGHT:
+ *value = int(mHeight);
+ return NO_ERROR;
+ case NATIVE_WINDOW_FORMAT:
+ *value = int(mFormat);
+ return NO_ERROR;
+ }
+ return BAD_VALUE;
+}
- if (clientBinder != NULL)
- client = SurfaceComposerClient::clientForConnection(clientBinder);
+int Surface::perform(int operation, va_list args)
+{
+ int res = NO_ERROR;
+ switch (operation) {
+ case NATIVE_WINDOW_SET_USAGE:
+ setUsage( va_arg(args, int) );
+ break;
+ default:
+ res = NAME_NOT_FOUND;
+ break;
+ }
+ return res;
+}
- return new Surface(client, surface, data, 0, 0, format, flags, false);
+void Surface::setUsage(uint32_t reqUsage)
+{
+ Mutex::Autolock _l(mSurfaceLock);
+ if (mUsage != reqUsage) {
+ mUsageChanged = true;
+ mUsage = reqUsage;
+ }
}
-status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel)
+bool Surface::getUsage(uint32_t* usage)
{
- uint32_t flags=0;
- uint32_t format=0;
- SurfaceID token = -1;
- uint32_t identity = 0;
- sp<SurfaceComposerClient> client;
- sp<ISurface> sur;
- sp<IMemoryHeap> heap[2];
- if (surface->isValid()) {
- token = surface->mToken;
- identity = surface->mIdentity;
- client = surface->mClient;
- sur = surface->mSurface;
- heap[0] = surface->mHeap[0];
- heap[1] = surface->mHeap[1];
- format = surface->mFormat;
- flags = surface->mFlags;
+ Mutex::Autolock _l(mSurfaceLock);
+ *usage = mUsage;
+ if (mUsageChanged) {
+ mUsageChanged = false;
+ return true;
}
- parcel->writeStrongBinder(client!=0 ? client->connection() : NULL);
- parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL);
- parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL);
- parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL);
- parcel->writeInt32(token);
- parcel->writeInt32(identity);
- parcel->writeInt32(format);
- parcel->writeInt32(flags);
- return NO_ERROR;
+ return false;
}
-bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs)
+// ----------------------------------------------------------------------------
+
+status_t Surface::lock(SurfaceInfo* info, bool blocking) {
+ return Surface::lock(info, NULL, blocking);
+}
+
+status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking)
{
- if (lhs == 0 || rhs == 0)
- return false;
- return lhs->mSurface->asBinder() == rhs->mSurface->asBinder();
+ if (mApiLock.tryLock() != NO_ERROR) {
+ LOGE("calling Surface::lock() from different threads!");
+ CallStack stack;
+ stack.update();
+ stack.dump("Surface::lock called from different threads");
+ return WOULD_BLOCK;
+ }
+
+ // we're intending to do software rendering from this point
+ setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+
+ sp<SurfaceBuffer> backBuffer;
+ status_t err = dequeueBuffer(&backBuffer);
+ LOGE_IF(err, "dequeueBuffer failed (%s)", strerror(-err));
+ if (err == NO_ERROR) {
+ err = lockBuffer(backBuffer.get());
+ LOGE_IF(err, "lockBuffer (idx=%d) failed (%s)",
+ backBuffer->getIndex(), strerror(-err));
+ if (err == NO_ERROR) {
+ // we handle copy-back here...
+
+ const Rect bounds(backBuffer->width, backBuffer->height);
+ Region scratch(bounds);
+ Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);
+
+ if (mNeedFullUpdate) {
+ // reset newDirtyRegion to bounds when a buffer is reallocated
+ // it would be better if this information was associated with
+ // the buffer and made available to outside of Surface.
+ // This will do for now though.
+ mNeedFullUpdate = false;
+ newDirtyRegion.set(bounds);
+ } else {
+ newDirtyRegion.andSelf(bounds);
+ }
+
+ const sp<SurfaceBuffer>& frontBuffer(mPostedBuffer);
+ if (frontBuffer !=0 &&
+ backBuffer->width == frontBuffer->width &&
+ backBuffer->height == frontBuffer->height &&
+ !(mFlags & ISurfaceComposer::eDestroyBackbuffer))
+ {
+ const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));
+ if (!copyback.isEmpty() && frontBuffer!=0) {
+ // copy front to back
+ copyBlt(backBuffer, frontBuffer, copyback);
+ }
+ }
+
+ mDirtyRegion = newDirtyRegion;
+ mOldDirtyRegion = newDirtyRegion;
+
+ void* vaddr;
+ status_t res = backBuffer->lock(
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ newDirtyRegion.bounds(), &vaddr);
+
+ LOGW_IF(res, "failed locking buffer (handle = %p)",
+ backBuffer->handle);
+
+ mLockedBuffer = backBuffer;
+ other->w = backBuffer->width;
+ other->h = backBuffer->height;
+ other->s = backBuffer->stride;
+ other->usage = backBuffer->usage;
+ other->format = backBuffer->format;
+ other->bits = vaddr;
+ }
+ }
+ mApiLock.unlock();
+ return err;
+}
+
+status_t Surface::unlockAndPost()
+{
+ if (mLockedBuffer == 0) {
+ LOGE("unlockAndPost failed, no locked buffer");
+ return BAD_VALUE;
+ }
+
+ status_t err = mLockedBuffer->unlock();
+ LOGE_IF(err, "failed unlocking buffer (%p)", mLockedBuffer->handle);
+
+ err = queueBuffer(mLockedBuffer.get());
+ LOGE_IF(err, "queueBuffer (idx=%d) failed (%s)",
+ mLockedBuffer->getIndex(), strerror(-err));
+
+ mPostedBuffer = mLockedBuffer;
+ mLockedBuffer = 0;
+ return err;
}
-void* Surface::heapBase(int i) const
+void Surface::setSwapRectangle(const Rect& r) {
+ Mutex::Autolock _l(mSurfaceLock);
+ mSwapRectangle = r;
+}
+
+status_t Surface::getBufferLocked(int index, int usage)
{
- void* heapBase = mSurfaceHeapBase[i];
- // map lazily so it doesn't get mapped in clients that don't need it
- if (heapBase == 0) {
- const sp<IMemoryHeap>& heap(mHeap[i]);
- if (heap != 0) {
- heapBase = static_cast<uint8_t*>(heap->base());
- if (heapBase == MAP_FAILED) {
- heapBase = NULL;
- LOGE("Couldn't map Surface's heap (binder=%p, heap=%p)",
- heap->asBinder().get(), heap.get());
+ sp<ISurface> s(mSurface);
+ if (s == 0) return NO_INIT;
+
+ status_t err = NO_MEMORY;
+
+ // free the current buffer
+ sp<SurfaceBuffer>& currentBuffer(mBuffers[index]);
+ if (currentBuffer != 0) {
+ getBufferMapper().unregisterBuffer(currentBuffer->handle);
+ currentBuffer.clear();
+ }
+
+ sp<SurfaceBuffer> buffer = s->requestBuffer(index, usage);
+ LOGE_IF(buffer==0,
+ "ISurface::getBuffer(%d, %08x) returned NULL",
+ index, usage);
+ if (buffer != 0) { // this should never happen by construction
+ LOGE_IF(buffer->handle == NULL,
+ "requestBuffer(%d, %08x) returned a buffer with a null handle",
+ index, usage);
+ if (buffer->handle != NULL) {
+ err = getBufferMapper().registerBuffer(buffer->handle);
+ LOGW_IF(err, "registerBuffer(...) failed %d (%s)",
+ err, strerror(-err));
+ if (err == NO_ERROR) {
+ currentBuffer = buffer;
+ currentBuffer->setIndex(index);
+ mNeedFullUpdate = true;
}
- mSurfaceHeapBase[i] = heapBase;
}
}
- return heapBase;
+ return err;
}
}; // namespace android
diff --git a/libs/ui/SurfaceBuffer.cpp b/libs/ui/SurfaceBuffer.cpp
new file mode 100644
index 0000000..0510bc1
--- /dev/null
+++ b/libs/ui/SurfaceBuffer.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SurfaceBuffer"
+
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <binder/Parcel.h>
+
+#include <ui/BufferMapper.h>
+#include <ui/Rect.h>
+#include <private/ui/SurfaceBuffer.h>
+
+namespace android {
+
+// ============================================================================
+// SurfaceBuffer
+// ============================================================================
+
+SurfaceBuffer::SurfaceBuffer()
+ : BASE(), mOwner(false), mBufferMapper(BufferMapper::get()), mIndex(-1)
+{
+ width =
+ height =
+ stride =
+ format =
+ usage = 0;
+ handle = NULL;
+}
+
+SurfaceBuffer::SurfaceBuffer(const Parcel& data)
+ : BASE(), mOwner(true), mBufferMapper(BufferMapper::get())
+{
+ // we own the handle in this case
+ width = data.readInt32();
+ if (width < 0) {
+ width = height = stride = format = usage = 0;
+ handle = 0;
+ } else {
+ height = data.readInt32();
+ stride = data.readInt32();
+ format = data.readInt32();
+ usage = data.readInt32();
+ handle = data.readNativeHandle();
+ }
+}
+
+SurfaceBuffer::~SurfaceBuffer()
+{
+ if (handle && mOwner) {
+ native_handle_close(handle);
+ native_handle_delete(const_cast<native_handle*>(handle));
+ }
+}
+
+status_t SurfaceBuffer::lock(uint32_t usage, void** vaddr)
+{
+ const Rect lockBounds(width, height);
+ status_t res = lock(usage, lockBounds, vaddr);
+ return res;
+}
+
+status_t SurfaceBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr)
+{
+ if (rect.left < 0 || rect.right > this->width ||
+ rect.top < 0 || rect.bottom > this->height) {
+ LOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)",
+ rect.left, rect.top, rect.right, rect.bottom,
+ this->width, this->height);
+ return BAD_VALUE;
+ }
+ status_t res = getBufferMapper().lock(handle, usage, rect, vaddr);
+ return res;
+}
+
+status_t SurfaceBuffer::unlock()
+{
+ status_t res = getBufferMapper().unlock(handle);
+ return res;
+}
+
+status_t SurfaceBuffer::writeToParcel(Parcel* reply,
+ android_native_buffer_t const* buffer)
+{
+ if (buffer == NULL)
+ return BAD_VALUE;
+
+ if (buffer->width < 0 || buffer->height < 0)
+ return BAD_VALUE;
+
+ status_t err = NO_ERROR;
+ if (buffer->handle == NULL) {
+ // this buffer doesn't have a handle
+ reply->writeInt32(NO_MEMORY);
+ } else {
+ reply->writeInt32(buffer->width);
+ reply->writeInt32(buffer->height);
+ reply->writeInt32(buffer->stride);
+ reply->writeInt32(buffer->format);
+ reply->writeInt32(buffer->usage);
+ err = reply->writeNativeHandle(buffer->handle);
+ }
+ return err;
+}
+
+
+void SurfaceBuffer::setIndex(int index) {
+ mIndex = index;
+}
+
+int SurfaceBuffer::getIndex() const {
+ return mIndex;
+}
+
+
+}; // namespace android
+
diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp
index fe803ff..8401cb6 100644
--- a/libs/ui/SurfaceComposerClient.cpp
+++ b/libs/ui/SurfaceComposerClient.cpp
@@ -29,27 +29,21 @@
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/KeyedVector.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/IMemory.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
#include <utils/Log.h>
+#include <ui/DisplayInfo.h>
#include <ui/ISurfaceComposer.h>
#include <ui/ISurfaceFlingerClient.h>
#include <ui/ISurface.h>
#include <ui/SurfaceComposerClient.h>
-#include <ui/DisplayInfo.h>
#include <ui/Rect.h>
-#include <ui/Point.h>
-#include <private/ui/SharedState.h>
#include <private/ui/LayerState.h>
+#include <private/ui/SharedBufferStack.h>
#include <private/ui/SurfaceFlingerSynchro.h>
-#include <pixelflinger/pixelflinger.h>
-
-#include <utils/BpBinder.h>
-
#define VERBOSE(...) ((void)0)
//#define VERBOSE LOGD
@@ -65,7 +59,7 @@ static Mutex gLock;
static sp<ISurfaceComposer> gSurfaceManager;
static DefaultKeyedVector< sp<IBinder>, sp<SurfaceComposerClient> > gActiveConnections;
static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions;
-static sp<IMemory> gServerCblkMemory;
+static sp<IMemoryHeap> gServerCblkMemory;
static volatile surface_flinger_cblk_t* gServerCblk;
const sp<ISurfaceComposer>& _get_surface_manager()
@@ -100,7 +94,7 @@ static volatile surface_flinger_cblk_t const * get_cblk()
if (gServerCblk == 0) {
gServerCblkMemory = sm->getCblk();
LOGE_IF(gServerCblkMemory==0, "Can't get server control block");
- gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->pointer();
+ gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase();
LOGE_IF(gServerCblk==0, "Can't get server control block address");
}
}
@@ -109,210 +103,6 @@ static volatile surface_flinger_cblk_t const * get_cblk()
// ---------------------------------------------------------------------------
-static void copyBlt(const GGLSurface& dst,
- const GGLSurface& src, const Region& reg)
-{
- Region::iterator iterator(reg);
- if (iterator) {
- // NOTE: dst and src must be the same format
- Rect r;
- const size_t bpp = bytesPerPixel(src.format);
- const size_t dbpr = dst.stride * bpp;
- const size_t sbpr = src.stride * bpp;
- while (iterator.iterate(&r)) {
- ssize_t h = r.bottom - r.top;
- if (h) {
- size_t size = (r.right - r.left) * bpp;
- uint8_t* s = src.data + (r.left + src.stride * r.top) * bpp;
- uint8_t* d = dst.data + (r.left + dst.stride * r.top) * bpp;
- if (dbpr==sbpr && size==sbpr) {
- size *= h;
- h = 1;
- }
- do {
- memcpy(d, s, size);
- d += dbpr;
- s += sbpr;
- } while (--h > 0);
- }
- }
- }
-}
-
-// ---------------------------------------------------------------------------
-
-surface_flinger_cblk_t::surface_flinger_cblk_t()
-{
-}
-
-// ---------------------------------------------------------------------------
-
-per_client_cblk_t::per_client_cblk_t()
-{
-}
-
-// these functions are used by the clients
-inline status_t per_client_cblk_t::validate(size_t i) const {
- if (uint32_t(i) >= NUM_LAYERS_MAX)
- return BAD_INDEX;
- if (layers[i].swapState & eInvalidSurface)
- return NO_MEMORY;
- return NO_ERROR;
-}
-
-int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags)
-{
- int32_t index;
- uint32_t state;
- int timeout = 0;
- status_t result;
- layer_cblk_t * const layer = layers + i;
- const bool blocking = flags & BLOCKING;
- const bool inspect = flags & INSPECT;
-
- do {
- state = layer->swapState;
-
- if (UNLIKELY((state&(eFlipRequested|eNextFlipPending)) == eNextFlipPending)) {
- LOGE("eNextFlipPending set but eFlipRequested not set, "
- "layer=%d (lcblk=%p), state=%08x",
- int(i), layer, int(state));
- return INVALID_OPERATION;
- }
-
- if (UNLIKELY(state&eLocked)) {
- LOGE("eLocked set when entering lock_layer(), "
- "layer=%d (lcblk=%p), state=%08x",
- int(i), layer, int(state));
- return WOULD_BLOCK;
- }
-
-
- if (state & (eFlipRequested | eNextFlipPending | eResizeRequested
- | eInvalidSurface))
- {
- int32_t resizeIndex;
- Mutex::Autolock _l(lock);
- // might block for a very short amount of time
- // will never cause the server to block (trylock())
-
- goto start_loop_here;
-
- // We block the client if:
- // eNextFlipPending: we've used both buffers already, so we need to
- // wait for one to become availlable.
- // eResizeRequested: the buffer we're going to acquire is being
- // resized. Block until it is done.
- // eFlipRequested && eBusy: the buffer we're going to acquire is
- // currently in use by the server.
- // eInvalidSurface: this is a special case, we don't block in this
- // case, we just return an error.
-
- while((state & (eNextFlipPending|eInvalidSurface)) ||
- (state & ((resizeIndex) ? eResizeBuffer1 : eResizeBuffer0)) ||
- ((state & (eFlipRequested|eBusy)) == (eFlipRequested|eBusy)) )
- {
- if (state & eInvalidSurface)
- return NO_MEMORY;
-
- if (!blocking)
- return WOULD_BLOCK;
-
- timeout = 0;
- result = cv.waitRelative(lock, seconds(1));
- if (__builtin_expect(result!=NO_ERROR, false)) {
- const int newState = layer->swapState;
- LOGW( "lock_layer timed out (is the CPU pegged?) "
- "layer=%d, lcblk=%p, state=%08x (was %08x)",
- int(i), layer, newState, int(state));
- timeout = newState != int(state);
- }
-
- start_loop_here:
- state = layer->swapState;
- resizeIndex = (state&eIndex) ^ ((state&eFlipRequested)>>1);
- }
-
- LOGW_IF(timeout,
- "lock_layer() timed out but didn't appear to need "
- "to be locked and we recovered "
- "(layer=%d, lcblk=%p, state=%08x)",
- int(i), layer, int(state));
- }
-
- // eFlipRequested is not set and cannot be set by another thread: it's
- // safe to use the first buffer without synchronization.
-
- // Choose the index depending on eFlipRequested.
- // When it's set, choose the 'other' buffer.
- index = (state&eIndex) ^ ((state&eFlipRequested)>>1);
-
- // make sure this buffer is valid
- if (layer->surface[index].bits_offset < 0) {
- return status_t(layer->surface[index].bits_offset);
- }
-
- if (inspect) {
- // we just want to inspect this layer. don't lock it.
- goto done;
- }
-
- // last thing before we're done, we need to atomically lock the state
- } while (android_atomic_cmpxchg(state, state|eLocked, &(layer->swapState)));
-
- VERBOSE("locked layer=%d (lcblk=%p), buffer=%d, state=0x%08x",
- int(i), layer, int(index), int(state));
-
- // store the index of the locked buffer (for client use only)
- layer->flags &= ~eBufferIndex;
- layer->flags |= ((index << eBufferIndexShift) & eBufferIndex);
-
-done:
- return index;
-}
-
-uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i)
-{
- // atomically set eFlipRequested and clear eLocked and optionnaly
- // set eNextFlipPending if eFlipRequested was already set
-
- layer_cblk_t * const layer = layers + i;
- int32_t oldvalue, newvalue;
- do {
- oldvalue = layer->swapState;
- // get current value
-
- newvalue = oldvalue & ~eLocked;
- // clear eLocked
-
- newvalue |= eFlipRequested;
- // set eFlipRequested
-
- if (oldvalue & eFlipRequested)
- newvalue |= eNextFlipPending;
- // if eFlipRequested was alread set, set eNextFlipPending
-
- } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState)));
-
- VERBOSE("request pageflip for layer=%d, buffer=%d, state=0x%08x",
- int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift),
- int(newvalue));
-
- // from this point, the server can kick in at anytime and use the first
- // buffer, so we cannot use it anymore, and we must use the 'other'
- // buffer instead (or wait if it is not availlable yet, see lock_layer).
-
- return newvalue;
-}
-
-void per_client_cblk_t::unlock_layer(size_t i)
-{
- layer_cblk_t * const layer = layers + i;
- android_atomic_and(~eLocked, &layer->swapState);
-}
-
-// ---------------------------------------------------------------------------
-
static inline int compare_type( const layer_state_t& lhs,
const layer_state_t& rhs) {
if (lhs.surface < rhs.surface) return -1;
@@ -360,9 +150,9 @@ void SurfaceComposerClient::_init(
return;
}
- mClient->getControlBlocks(&mControlMemory);
+ mControlMemory = mClient->getControlBlock();
mSignalServer = new SurfaceFlingerSynchro(sm);
- mControl = static_cast<per_client_cblk_t *>(mControlMemory->pointer());
+ mControl = static_cast<SharedClient *>(mControlMemory->getBase());
}
SurfaceComposerClient::~SurfaceComposerClient()
@@ -376,32 +166,6 @@ status_t SurfaceComposerClient::initCheck() const
return mStatus;
}
-status_t SurfaceComposerClient::validateSurface(
- per_client_cblk_t const* cblk, Surface const * surface)
-{
- SurfaceID index = surface->ID();
- if (cblk == 0) {
- LOGE("cblk is null (surface id=%d, identity=%u)",
- index, surface->getIdentity());
- return NO_INIT;
- }
-
- status_t err = cblk->validate(index);
- if (err != NO_ERROR) {
- LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)",
- index, surface->getIdentity(), err, strerror(-err));
- return err;
- }
-
- if (surface->getIdentity() != uint32_t(cblk->layers[index].identity)) {
- LOGE("using an invalid surface id=%d, identity=%u should be %d",
- index, surface->getIdentity(), cblk->layers[index].identity);
- return NO_INIT;
- }
-
- return NO_ERROR;
-}
-
sp<IBinder> SurfaceComposerClient::connection() const
{
return (mClient != 0) ? mClient->asBinder() : 0;
@@ -437,9 +201,8 @@ void SurfaceComposerClient::dispose()
{
// this can be called more than once.
- sp<IMemory> controlMemory;
+ sp<IMemoryHeap> controlMemory;
sp<ISurfaceFlingerClient> client;
- sp<IMemoryHeap> surfaceHeap;
{
Mutex::Autolock _lg(gLock);
@@ -462,9 +225,7 @@ void SurfaceComposerClient::dispose()
delete mPrebuiltLayerState;
mPrebuiltLayerState = 0;
controlMemory = mControlMemory;
- surfaceHeap = mSurfaceHeap;
mControlMemory.clear();
- mSurfaceHeap.clear();
mControl = 0;
mStatus = NO_INIT;
}
@@ -528,7 +289,13 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays()
return n;
}
-sp<Surface> SurfaceComposerClient::createSurface(
+
+void SurfaceComposerClient::signalServer()
+{
+ mSignalServer->signal();
+}
+
+sp<SurfaceControl> SurfaceComposerClient::createSurface(
int pid,
DisplayID display,
uint32_t w,
@@ -536,14 +303,14 @@ sp<Surface> SurfaceComposerClient::createSurface(
PixelFormat format,
uint32_t flags)
{
- sp<Surface> result;
+ sp<SurfaceControl> result;
if (mStatus == NO_ERROR) {
ISurfaceFlingerClient::surface_data_t data;
sp<ISurface> surface = mClient->createSurface(&data, pid,
display, w, h, format, flags);
if (surface != 0) {
if (uint32_t(data.token) < NUM_LAYERS_MAX) {
- result = new Surface(this, surface, data, w, h, format, flags);
+ result = new SurfaceControl(this, surface, data, w, h, format, flags);
}
}
}
@@ -568,186 +335,6 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid)
return err;
}
-status_t SurfaceComposerClient::nextBuffer(Surface* surface,
- Surface::SurfaceInfo* info)
-{
- SurfaceID index = surface->ID();
- per_client_cblk_t* const cblk = mControl;
- status_t err = validateSurface(cblk, surface);
- if (err != NO_ERROR)
- return err;
-
- int32_t backIdx = surface->mBackbufferIndex;
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- const surface_info_t* const front = lcblk->surface + (1-backIdx);
- info->w = front->w;
- info->h = front->h;
- info->format = front->format;
- info->base = surface->heapBase(1-backIdx);
- info->bits = reinterpret_cast<void*>(intptr_t(info->base) + front->bits_offset);
- info->bpr = front->bpr;
-
- return 0;
-}
-
-status_t SurfaceComposerClient::lockSurface(
- Surface* surface,
- Surface::SurfaceInfo* other,
- Region* dirty,
- bool blocking)
-{
- Mutex::Autolock _l(surface->getLock());
-
- SurfaceID index = surface->ID();
- per_client_cblk_t* const cblk = mControl;
- status_t err = validateSurface(cblk, surface);
- if (err != NO_ERROR)
- return err;
-
- int32_t backIdx = cblk->lock_layer(size_t(index),
- per_client_cblk_t::BLOCKING);
- if (backIdx >= 0) {
- surface->mBackbufferIndex = backIdx;
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- const surface_info_t* const back = lcblk->surface + backIdx;
- const surface_info_t* const front = lcblk->surface + (1-backIdx);
- other->w = back->w;
- other->h = back->h;
- other->format = back->format;
- other->base = surface->heapBase(backIdx);
- other->bits = reinterpret_cast<void*>(intptr_t(other->base) + back->bits_offset);
- other->bpr = back->bpr;
-
- const Rect bounds(other->w, other->h);
- Region newDirtyRegion;
-
- if (back->flags & surface_info_t::eBufferDirty) {
- /* it is safe to write *back here, because we're guaranteed
- * SurfaceFlinger is not touching it (since it just granted
- * access to us) */
- const_cast<surface_info_t*>(back)->flags &=
- ~surface_info_t::eBufferDirty;
-
- // content is meaningless in this case and the whole surface
- // needs to be redrawn.
-
- newDirtyRegion.set(bounds);
- if (dirty) {
- *dirty = newDirtyRegion;
- }
-
- //if (bytesPerPixel(other->format) == 4) {
- // android_memset32(
- // (uint32_t*)other->bits, 0xFF00FF00, other->h * other->bpr);
- //} else {
- // android_memset16( // fill with green
- // (uint16_t*)other->bits, 0x7E0, other->h * other->bpr);
- //}
- }
- else
- {
- if (dirty) {
- dirty->andSelf(Region(bounds));
- newDirtyRegion = *dirty;
- } else {
- newDirtyRegion.set(bounds);
- }
-
- Region copyback;
- if (!(lcblk->flags & eNoCopyBack)) {
- const Region previousDirtyRegion(surface->dirtyRegion());
- copyback = previousDirtyRegion.subtract(newDirtyRegion);
- }
-
- if (!copyback.isEmpty()) {
- // copy front to back
- GGLSurface cb;
- cb.version = sizeof(GGLSurface);
- cb.width = back->w;
- cb.height = back->h;
- cb.stride = back->stride;
- cb.data = (GGLubyte*)surface->heapBase(backIdx);
- cb.data += back->bits_offset;
- cb.format = back->format;
-
- GGLSurface t;
- t.version = sizeof(GGLSurface);
- t.width = front->w;
- t.height = front->h;
- t.stride = front->stride;
- t.data = (GGLubyte*)surface->heapBase(1-backIdx);
- t.data += front->bits_offset;
- t.format = front->format;
-
- //const Region copyback(lcblk->region + 1-backIdx);
- copyBlt(cb, t, copyback);
- }
- }
-
- // update dirty region
- surface->setDirtyRegion(newDirtyRegion);
- }
- return (backIdx < 0) ? status_t(backIdx) : status_t(NO_ERROR);
-}
-
-void SurfaceComposerClient::_signal_server()
-{
- mSignalServer->signal();
-}
-
-void SurfaceComposerClient::_send_dirty_region(
- layer_cblk_t* lcblk, const Region& dirty)
-{
- const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift;
- flat_region_t* flat_region = lcblk->region + index;
- status_t err = dirty.write(flat_region, sizeof(flat_region_t));
- if (err < NO_ERROR) {
- // region doesn't fit, use the bounds
- const Region reg(dirty.bounds());
- reg.write(flat_region, sizeof(flat_region_t));
- }
-}
-
-status_t SurfaceComposerClient::unlockAndPostSurface(Surface* surface)
-{
- Mutex::Autolock _l(surface->getLock());
-
- SurfaceID index = surface->ID();
- per_client_cblk_t* const cblk = mControl;
- status_t err = validateSurface(cblk, surface);
- if (err != NO_ERROR)
- return err;
-
- Region dirty(surface->dirtyRegion());
- const Rect& swapRect(surface->swapRectangle());
- if (swapRect.isValid()) {
- dirty.set(swapRect);
- }
-
- // transmit the dirty region
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- _send_dirty_region(lcblk, dirty);
- uint32_t newstate = cblk->unlock_layer_and_post(size_t(index));
- if (!(newstate & eNextFlipPending))
- _signal_server();
- return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::unlockSurface(Surface* surface)
-{
- Mutex::Autolock _l(surface->getLock());
-
- SurfaceID index = surface->ID();
- per_client_cblk_t* const cblk = mControl;
- status_t err = validateSurface(cblk, surface);
- if (err != NO_ERROR)
- return err;
-
- layer_cblk_t* const lcblk = &(cblk->layers[index]);
- cblk->unlock_layer(size_t(index));
- return NO_ERROR;
-}
-
void SurfaceComposerClient::openGlobalTransaction()
{
Mutex::Autolock _l(gLock);
@@ -789,18 +376,17 @@ void SurfaceComposerClient::closeGlobalTransaction()
const size_t N = clients.size();
VERBOSE("closeGlobalTransaction (%ld clients)", N);
- if (N == 1) {
- clients[0]->closeTransaction();
- } else {
- const sp<ISurfaceComposer>& sm(_get_surface_manager());
- sm->openGlobalTransaction();
- for (size_t i=0; i<N; i++) {
- clients[i]->closeTransaction();
- }
- sm->closeGlobalTransaction();
+
+ const sp<ISurfaceComposer>& sm(_get_surface_manager());
+ sm->openGlobalTransaction();
+ for (size_t i=0; i<N; i++) {
+ clients[i]->closeTransaction();
}
+ sm->closeGlobalTransaction();
+
}
+
status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
{
const sp<ISurfaceComposer>& sm(_get_surface_manager());
@@ -866,14 +452,8 @@ status_t SurfaceComposerClient::closeTransaction()
return NO_ERROR;
}
-layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface)
+layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index)
{
- SurfaceID index = surface->ID();
- per_client_cblk_t* const cblk = mControl;
- status_t err = validateSurface(cblk, surface.get());
- if (err != NO_ERROR)
- return 0;
-
// API usage error, do nothing.
if (mTransactionOpen<=0) {
LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d",
@@ -892,11 +472,11 @@ layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface)
return mStates.editArray() + i;
}
-layer_state_t* SurfaceComposerClient::_lockLayerState(const sp<Surface>& surface)
+layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id)
{
layer_state_t* s;
mLock.lock();
- s = _get_state_l(surface);
+ s = _get_state_l(id);
if (!s) mLock.unlock();
return s;
}
@@ -906,9 +486,9 @@ void SurfaceComposerClient::_unlockLayerState()
mLock.unlock();
}
-status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t y)
+status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::ePositionChanged;
s->x = x;
@@ -917,9 +497,9 @@ status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t
return NO_ERROR;
}
-status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h)
+status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eSizeChanged;
s->w = w;
@@ -928,9 +508,9 @@ status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h
return NO_ERROR;
}
-status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z)
+status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eLayerChanged;
s->z = z;
@@ -938,32 +518,32 @@ status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z)
return NO_ERROR;
}
-status_t SurfaceComposerClient::hide(Surface* surface)
+status_t SurfaceComposerClient::hide(SurfaceID id)
{
- return setFlags(surface, ISurfaceComposer::eLayerHidden,
+ return setFlags(id, ISurfaceComposer::eLayerHidden,
ISurfaceComposer::eLayerHidden);
}
-status_t SurfaceComposerClient::show(Surface* surface, int32_t)
+status_t SurfaceComposerClient::show(SurfaceID id, int32_t)
{
- return setFlags(surface, 0, ISurfaceComposer::eLayerHidden);
+ return setFlags(id, 0, ISurfaceComposer::eLayerHidden);
}
-status_t SurfaceComposerClient::freeze(Surface* surface)
+status_t SurfaceComposerClient::freeze(SurfaceID id)
{
- return setFlags(surface, ISurfaceComposer::eLayerFrozen,
+ return setFlags(id, ISurfaceComposer::eLayerFrozen,
ISurfaceComposer::eLayerFrozen);
}
-status_t SurfaceComposerClient::unfreeze(Surface* surface)
+status_t SurfaceComposerClient::unfreeze(SurfaceID id)
{
- return setFlags(surface, 0, ISurfaceComposer::eLayerFrozen);
+ return setFlags(id, 0, ISurfaceComposer::eLayerFrozen);
}
-status_t SurfaceComposerClient::setFlags(Surface* surface,
+status_t SurfaceComposerClient::setFlags(SurfaceID id,
uint32_t flags, uint32_t mask)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eVisibilityChanged;
s->flags &= ~mask;
@@ -973,11 +553,10 @@ status_t SurfaceComposerClient::setFlags(Surface* surface,
return NO_ERROR;
}
-
status_t SurfaceComposerClient::setTransparentRegionHint(
- Surface* surface, const Region& transparentRegion)
+ SurfaceID id, const Region& transparentRegion)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eTransparentRegionChanged;
s->transparentRegion = transparentRegion;
@@ -985,9 +564,9 @@ status_t SurfaceComposerClient::setTransparentRegionHint(
return NO_ERROR;
}
-status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha)
+status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eAlphaChanged;
s->alpha = alpha;
@@ -996,11 +575,11 @@ status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha)
}
status_t SurfaceComposerClient::setMatrix(
- Surface* surface,
+ SurfaceID id,
float dsdx, float dtdx,
float dsdy, float dtdy )
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eMatrixChanged;
layer_state_t::matrix22_t matrix;
@@ -1013,9 +592,9 @@ status_t SurfaceComposerClient::setMatrix(
return NO_ERROR;
}
-status_t SurfaceComposerClient::setFreezeTint(Surface* surface, uint32_t tint)
+status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint)
{
- layer_state_t* s = _lockLayerState(surface);
+ layer_state_t* s = _lockLayerState(id);
if (!s) return BAD_INDEX;
s->what |= ISurfaceComposer::eFreezeTintChanged;
s->tint = tint;
diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp
index 5cd9755..c81db71 100644
--- a/libs/ui/SurfaceFlingerSynchro.cpp
+++ b/libs/ui/SurfaceFlingerSynchro.cpp
@@ -14,19 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "SurfaceFlingerSynchro"
-
#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <utils/IPCThreadState.h>
-#include <utils/Log.h>
#include <private/ui/SurfaceFlingerSynchro.h>
@@ -34,61 +22,10 @@ namespace android {
// ---------------------------------------------------------------------------
-SurfaceFlingerSynchro::Barrier::Barrier()
- : state(CLOSED) {
-}
-
-SurfaceFlingerSynchro::Barrier::~Barrier() {
-}
-
-void SurfaceFlingerSynchro::Barrier::open() {
- asm volatile ("":::"memory");
- Mutex::Autolock _l(lock);
- state = OPENED;
- cv.broadcast();
-}
-
-void SurfaceFlingerSynchro::Barrier::close() {
- Mutex::Autolock _l(lock);
- state = CLOSED;
-}
-
-void SurfaceFlingerSynchro::Barrier::waitAndClose()
-{
- Mutex::Autolock _l(lock);
- while (state == CLOSED) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- cv.wait(lock);
- }
- state = CLOSED;
-}
-
-status_t SurfaceFlingerSynchro::Barrier::waitAndClose(nsecs_t timeout)
-{
- Mutex::Autolock _l(lock);
- while (state == CLOSED) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
- int err = cv.waitRelative(lock, timeout);
- if (err != 0)
- return err;
- }
- state = CLOSED;
- return NO_ERROR;
-}
-
-// ---------------------------------------------------------------------------
-
SurfaceFlingerSynchro::SurfaceFlingerSynchro(const sp<ISurfaceComposer>& flinger)
: mSurfaceComposer(flinger)
{
}
-
-SurfaceFlingerSynchro::SurfaceFlingerSynchro()
-{
-}
-
SurfaceFlingerSynchro::~SurfaceFlingerSynchro()
{
}
@@ -99,24 +36,6 @@ status_t SurfaceFlingerSynchro::signal()
return NO_ERROR;
}
-status_t SurfaceFlingerSynchro::wait()
-{
- mBarrier.waitAndClose();
- return NO_ERROR;
-}
-
-status_t SurfaceFlingerSynchro::wait(nsecs_t timeout)
-{
- if (timeout == 0)
- return SurfaceFlingerSynchro::wait();
- return mBarrier.waitAndClose(timeout);
-}
-
-void SurfaceFlingerSynchro::open()
-{
- mBarrier.open();
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk
new file mode 100644
index 0000000..6cc4a5a
--- /dev/null
+++ b/libs/ui/tests/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ region.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libui
+
+LOCAL_MODULE:= test-region
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region.cpp
new file mode 100644
index 0000000..0deb2ba
--- /dev/null
+++ b/libs/ui/tests/region.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "Region"
+
+#include <stdio.h>
+#include <utils/Debug.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+using namespace android;
+
+int main()
+{
+ Region reg0( Rect( 0, 0, 100, 100 ) );
+ Region reg1 = reg0;
+ Region reg2, reg3;
+
+ reg0.dump("reg0");
+ reg1.dump("reg1");
+
+ reg0 = reg0 | reg0.translate(150, 0);
+ reg0.dump("reg0");
+ reg1.dump("reg1");
+
+ reg0 = reg0 | reg0.translate(300, 0);
+ reg0.dump("reg0");
+ reg1.dump("reg1");
+
+ //reg2 = reg0 | reg0.translate(0, 100);
+ //reg0.dump("reg0");
+ //reg1.dump("reg1");
+ //reg2.dump("reg2");
+
+ //reg3 = reg0 | reg0.translate(0, 150);
+ //reg0.dump("reg0");
+ //reg1.dump("reg1");
+ //reg2.dump("reg2");
+ //reg3.dump("reg3");
+
+ LOGD("---");
+ reg2 = reg0 | reg0.translate(100, 0);
+ reg0.dump("reg0");
+ reg1.dump("reg1");
+ reg2.dump("reg2");
+
+ return 0;
+}
+
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 9bdd64a..59409a2 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -32,52 +32,24 @@ commonSources:= \
StopWatch.cpp \
String8.cpp \
String16.cpp \
+ StringArray.cpp \
SystemClock.cpp \
TextOutput.cpp \
Threads.cpp \
- TimerProbe.cpp \
Timers.cpp \
VectorImpl.cpp \
ZipFileCRO.cpp \
ZipFileRO.cpp \
ZipUtils.cpp \
- misc.cpp \
- ported.cpp \
- LogSocket.cpp
+ misc.cpp
-#
-# The cpp files listed here do not belong in the device
-# build. Consult with the swetland before even thinking about
-# putting them in commonSources.
-#
-# They're used by the simulator runtime and by host-side tools like
-# aapt and the simulator front-end.
-#
-hostSources:= \
- InetAddress.cpp \
- Pipe.cpp \
- Socket.cpp \
- ZipEntry.cpp \
- ZipFile.cpp
# For the host
# =====================================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= $(commonSources) $(hostSources)
-
-ifeq ($(HOST_OS),linux)
-# Use the futex based mutex and condition variable
-# implementation from android-arm because it's shared mem safe
- LOCAL_SRC_FILES += \
- futex_synchro.c \
- executablepath_linux.cpp
-endif
-ifeq ($(HOST_OS),darwin)
- LOCAL_SRC_FILES += \
- executablepath_darwin.cpp
-endif
+LOCAL_SRC_FILES:= $(commonSources)
LOCAL_MODULE:= libutils
@@ -103,37 +75,18 @@ include $(CLEAR_VARS)
# we have the common sources, plus some device-specific stuff
LOCAL_SRC_FILES:= \
$(commonSources) \
- Binder.cpp \
- BpBinder.cpp \
- IInterface.cpp \
- IMemory.cpp \
- IPCThreadState.cpp \
- MemoryDealer.cpp \
- MemoryBase.cpp \
- MemoryHeapBase.cpp \
- MemoryHeapPmem.cpp \
- Parcel.cpp \
- ProcessState.cpp \
- IPermissionController.cpp \
- IServiceManager.cpp \
Unicode.cpp \
BackupData.cpp \
BackupHelpers.cpp
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_SRC_FILES += $(hostSources)
-endif
-
ifeq ($(TARGET_OS),linux)
-# Use the futex based mutex and condition variable
-# implementation from android-arm because it's shared mem safe
-LOCAL_SRC_FILES += futex_synchro.c
LOCAL_LDLIBS += -lrt -ldl
endif
LOCAL_C_INCLUDES += \
external/zlib \
external/icu4c/common
+
LOCAL_LDLIBS += -lpthread
LOCAL_SHARED_LIBRARIES := \
@@ -144,15 +97,10 @@ LOCAL_SHARED_LIBRARIES := \
ifneq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86)
# This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp
-LOCAL_SHARED_LIBRARIES += \
- libdl
+LOCAL_SHARED_LIBRARIES += libdl
endif # linux-x86
endif # sim
LOCAL_MODULE:= libutils
-
-#LOCAL_CFLAGS+=
-#LOCAL_LDFLAGS:=
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp
index cce754a..0cef35a 100644
--- a/libs/utils/BackupData.cpp
+++ b/libs/utils/BackupData.cpp
@@ -193,9 +193,11 @@ BackupDataReader::Status()
if ((actual) != (expected)) { \
if ((actual) == 0) { \
m_status = EIO; \
+ m_done = true; \
} else { \
m_status = errno; \
} \
+ LOGD("CHECK_SIZE failed with at line %d m_status='%s'", __LINE__, strerror(m_status)); \
return m_status; \
} \
} while(0)
@@ -203,6 +205,7 @@ BackupDataReader::Status()
do { \
status_t err = skip_padding(); \
if (err != NO_ERROR) { \
+ LOGD("SKIP_PADDING FAILED at line %d", __LINE__); \
m_status = err; \
return err; \
} \
@@ -218,10 +221,19 @@ BackupDataReader::ReadNextHeader(bool* done, int* type)
int amt;
- // No error checking here, in case we're at the end of the stream. Just let read() fail.
- skip_padding();
+ amt = skip_padding();
+ if (amt == EIO) {
+ *done = m_done = true;
+ return NO_ERROR;
+ }
+ else if (amt != NO_ERROR) {
+ return amt;
+ }
amt = read(m_fd, &m_header, sizeof(m_header));
*done = m_done = (amt == 0);
+ if (*done) {
+ return NO_ERROR;
+ }
CHECK_SIZE(amt, sizeof(m_header));
m_pos += sizeof(m_header);
if (type) {
@@ -298,10 +310,12 @@ BackupDataReader::SkipEntityData()
}
if (m_header.entity.dataSize > 0) {
int pos = lseek(m_fd, m_dataEndPos, SEEK_SET);
- return pos == -1 ? (int)errno : (int)NO_ERROR;
- } else {
- return NO_ERROR;
+ if (pos == -1) {
+ return errno;
+ }
}
+ SKIP_PADDING();
+ return NO_ERROR;
}
ssize_t
@@ -325,6 +339,10 @@ BackupDataReader::ReadEntityData(void* data, size_t size)
m_status = errno;
return -1;
}
+ if (amt == 0) {
+ m_status = EIO;
+ m_done = true;
+ }
m_pos += amt;
return amt;
}
diff --git a/libs/utils/Binder.cpp b/libs/utils/Binder.cpp
deleted file mode 100644
index 37e4685..0000000
--- a/libs/utils/Binder.cpp
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#include <utils/Binder.h>
-
-#include <utils/Atomic.h>
-#include <utils/BpBinder.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
-
-#include <stdio.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-sp<IInterface> IBinder::queryLocalInterface(const String16& descriptor)
-{
- return NULL;
-}
-
-BBinder* IBinder::localBinder()
-{
- return NULL;
-}
-
-BpBinder* IBinder::remoteBinder()
-{
- return NULL;
-}
-
-bool IBinder::checkSubclass(const void* /*subclassID*/) const
-{
- return false;
-}
-
-// ---------------------------------------------------------------------------
-
-class BBinder::Extras
-{
-public:
- Mutex mLock;
- BpBinder::ObjectManager mObjects;
-};
-
-// ---------------------------------------------------------------------------
-
-BBinder::BBinder()
- : mExtras(NULL)
-{
-}
-
-bool BBinder::isBinderAlive() const
-{
- return true;
-}
-
-status_t BBinder::pingBinder()
-{
- return NO_ERROR;
-}
-
-String16 BBinder::getInterfaceDescriptor() const
-{
- LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this);
- return String16();
-}
-
-status_t BBinder::transact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- data.setDataPosition(0);
-
- status_t err = NO_ERROR;
- switch (code) {
- case PING_TRANSACTION:
- reply->writeInt32(pingBinder());
- break;
- default:
- err = onTransact(code, data, reply, flags);
- break;
- }
-
- if (reply != NULL) {
- reply->setDataPosition(0);
- }
-
- return err;
-}
-
-status_t BBinder::linkToDeath(
- const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
-{
- return INVALID_OPERATION;
-}
-
-status_t BBinder::unlinkToDeath(
- const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
- wp<DeathRecipient>* outRecipient)
-{
- return INVALID_OPERATION;
-}
-
-status_t BBinder::dump(int fd, const Vector<String16>& args)
-{
- return NO_ERROR;
-}
-
-void BBinder::attachObject(
- const void* objectID, void* object, void* cleanupCookie,
- object_cleanup_func func)
-{
- Extras* e = mExtras;
-
- if (!e) {
- e = new Extras;
- if (android_atomic_cmpxchg(0, reinterpret_cast<int32_t>(e),
- reinterpret_cast<volatile int32_t*>(&mExtras)) != 0) {
- delete e;
- e = mExtras;
- }
- if (e == 0) return; // out of memory
- }
-
- AutoMutex _l(e->mLock);
- e->mObjects.attach(objectID, object, cleanupCookie, func);
-}
-
-void* BBinder::findObject(const void* objectID) const
-{
- Extras* e = mExtras;
- if (!e) return NULL;
-
- AutoMutex _l(e->mLock);
- return e->mObjects.find(objectID);
-}
-
-void BBinder::detachObject(const void* objectID)
-{
- Extras* e = mExtras;
- if (!e) return;
-
- AutoMutex _l(e->mLock);
- e->mObjects.detach(objectID);
-}
-
-BBinder* BBinder::localBinder()
-{
- return this;
-}
-
-BBinder::~BBinder()
-{
- if (mExtras) delete mExtras;
-}
-
-
-status_t BBinder::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch (code) {
- case INTERFACE_TRANSACTION:
- reply->writeString16(getInterfaceDescriptor());
- return NO_ERROR;
-
- case DUMP_TRANSACTION: {
- int fd = data.readFileDescriptor();
- int argc = data.readInt32();
- Vector<String16> args;
- for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
- args.add(data.readString16());
- }
- return dump(fd, args);
- }
- default:
- return UNKNOWN_TRANSACTION;
- }
-}
-
-// ---------------------------------------------------------------------------
-
-enum {
- // This is used to transfer ownership of the remote binder from
- // the BpRefBase object holding it (when it is constructed), to the
- // owner of the BpRefBase object when it first acquires that BpRefBase.
- kRemoteAcquired = 0x00000001
-};
-
-BpRefBase::BpRefBase(const sp<IBinder>& o)
- : mRemote(o.get()), mRefs(NULL), mState(0)
-{
- extendObjectLifetime(OBJECT_LIFETIME_WEAK);
-
- if (mRemote) {
- mRemote->incStrong(this); // Removed on first IncStrong().
- mRefs = mRemote->createWeak(this); // Held for our entire lifetime.
- }
-}
-
-BpRefBase::~BpRefBase()
-{
- if (mRemote) {
- if (!(mState&kRemoteAcquired)) {
- mRemote->decStrong(this);
- }
- mRefs->decWeak(this);
- }
-}
-
-void BpRefBase::onFirstRef()
-{
- android_atomic_or(kRemoteAcquired, &mState);
-}
-
-void BpRefBase::onLastStrongRef(const void* id)
-{
- if (mRemote) {
- mRemote->decStrong(this);
- }
-}
-
-bool BpRefBase::onIncStrongAttempted(uint32_t flags, const void* id)
-{
- return mRemote ? mRefs->attemptIncStrong(this) : false;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/utils/BpBinder.cpp b/libs/utils/BpBinder.cpp
deleted file mode 100644
index 69ab195..0000000
--- a/libs/utils/BpBinder.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#define LOG_TAG "BpBinder"
-//#define LOG_NDEBUG 0
-
-#include <utils/BpBinder.h>
-
-#include <utils/IPCThreadState.h>
-#include <utils/Log.h>
-
-#include <stdio.h>
-
-//#undef LOGV
-//#define LOGV(...) fprintf(stderr, __VA_ARGS__)
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-BpBinder::ObjectManager::ObjectManager()
-{
-}
-
-BpBinder::ObjectManager::~ObjectManager()
-{
- kill();
-}
-
-void BpBinder::ObjectManager::attach(
- const void* objectID, void* object, void* cleanupCookie,
- IBinder::object_cleanup_func func)
-{
- entry_t e;
- e.object = object;
- e.cleanupCookie = cleanupCookie;
- e.func = func;
-
- if (mObjects.indexOfKey(objectID) >= 0) {
- LOGE("Trying to attach object ID %p to binder ObjectManager %p with object %p, but object ID already in use",
- objectID, this, object);
- return;
- }
-
- mObjects.add(objectID, e);
-}
-
-void* BpBinder::ObjectManager::find(const void* objectID) const
-{
- const ssize_t i = mObjects.indexOfKey(objectID);
- if (i < 0) return NULL;
- return mObjects.valueAt(i).object;
-}
-
-void BpBinder::ObjectManager::detach(const void* objectID)
-{
- mObjects.removeItem(objectID);
-}
-
-void BpBinder::ObjectManager::kill()
-{
- const size_t N = mObjects.size();
- LOGV("Killing %d objects in manager %p", N, this);
- for (size_t i=0; i<N; i++) {
- const entry_t& e = mObjects.valueAt(i);
- if (e.func != NULL) {
- e.func(mObjects.keyAt(i), e.object, e.cleanupCookie);
- }
- }
-
- mObjects.clear();
-}
-
-// ---------------------------------------------------------------------------
-
-BpBinder::BpBinder(int32_t handle)
- : mHandle(handle)
- , mAlive(1)
- , mObitsSent(0)
- , mObituaries(NULL)
-{
- LOGV("Creating BpBinder %p handle %d\n", this, mHandle);
-
- extendObjectLifetime(OBJECT_LIFETIME_WEAK);
- IPCThreadState::self()->incWeakHandle(handle);
-}
-
-String16 BpBinder::getInterfaceDescriptor() const
-{
- String16 res;
- Parcel send, reply;
- status_t err = const_cast<BpBinder*>(this)->transact(
- INTERFACE_TRANSACTION, send, &reply);
- if (err == NO_ERROR) {
- res = reply.readString16();
- }
- return res;
-}
-
-bool BpBinder::isBinderAlive() const
-{
- return mAlive != 0;
-}
-
-status_t BpBinder::pingBinder()
-{
- Parcel send;
- Parcel reply;
- status_t err = transact(PING_TRANSACTION, send, &reply);
- if (err != NO_ERROR) return err;
- if (reply.dataSize() < sizeof(status_t)) return NOT_ENOUGH_DATA;
- return (status_t)reply.readInt32();
-}
-
-status_t BpBinder::dump(int fd, const Vector<String16>& args)
-{
- Parcel send;
- Parcel reply;
- send.writeFileDescriptor(fd);
- const size_t numArgs = args.size();
- send.writeInt32(numArgs);
- for (size_t i = 0; i < numArgs; i++) {
- send.writeString16(args[i]);
- }
- status_t err = transact(DUMP_TRANSACTION, send, &reply);
- return err;
-}
-
-status_t BpBinder::transact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- // Once a binder has died, it will never come back to life.
- if (mAlive) {
- status_t status = IPCThreadState::self()->transact(
- mHandle, code, data, reply, flags);
- if (status == DEAD_OBJECT) mAlive = 0;
- return status;
- }
-
- return DEAD_OBJECT;
-}
-
-status_t BpBinder::linkToDeath(
- const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
-{
- Obituary ob;
- ob.recipient = recipient;
- ob.cookie = cookie;
- ob.flags = flags;
-
- LOG_ALWAYS_FATAL_IF(recipient == NULL,
- "linkToDeath(): recipient must be non-NULL");
-
- {
- AutoMutex _l(mLock);
-
- if (!mObitsSent) {
- if (!mObituaries) {
- mObituaries = new Vector<Obituary>;
- if (!mObituaries) {
- return NO_MEMORY;
- }
- LOGV("Requesting death notification: %p handle %d\n", this, mHandle);
- getWeakRefs()->incWeak(this);
- IPCThreadState* self = IPCThreadState::self();
- self->requestDeathNotification(mHandle, this);
- self->flushCommands();
- }
- ssize_t res = mObituaries->add(ob);
- return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
- }
- }
-
- return DEAD_OBJECT;
-}
-
-status_t BpBinder::unlinkToDeath(
- const wp<DeathRecipient>& recipient, void* cookie, uint32_t flags,
- wp<DeathRecipient>* outRecipient)
-{
- AutoMutex _l(mLock);
-
- if (mObitsSent) {
- return DEAD_OBJECT;
- }
-
- const size_t N = mObituaries ? mObituaries->size() : 0;
- for (size_t i=0; i<N; i++) {
- const Obituary& obit = mObituaries->itemAt(i);
- if ((obit.recipient == recipient
- || (recipient == NULL && obit.cookie == cookie))
- && obit.flags == flags) {
- const uint32_t allFlags = obit.flags|flags;
- if (outRecipient != NULL) {
- *outRecipient = mObituaries->itemAt(i).recipient;
- }
- mObituaries->removeAt(i);
- if (mObituaries->size() == 0) {
- LOGV("Clearing death notification: %p handle %d\n", this, mHandle);
- IPCThreadState* self = IPCThreadState::self();
- self->clearDeathNotification(mHandle, this);
- self->flushCommands();
- delete mObituaries;
- mObituaries = NULL;
- }
- return NO_ERROR;
- }
- }
-
- return NAME_NOT_FOUND;
-}
-
-void BpBinder::sendObituary()
-{
- LOGV("Sending obituary for proxy %p handle %d, mObitsSent=%s\n",
- this, mHandle, mObitsSent ? "true" : "false");
-
- mAlive = 0;
- if (mObitsSent) return;
-
- mLock.lock();
- Vector<Obituary>* obits = mObituaries;
- if(obits != NULL) {
- LOGV("Clearing sent death notification: %p handle %d\n", this, mHandle);
- IPCThreadState* self = IPCThreadState::self();
- self->clearDeathNotification(mHandle, this);
- self->flushCommands();
- mObituaries = NULL;
- }
- mObitsSent = 1;
- mLock.unlock();
-
- LOGV("Reporting death of proxy %p for %d recipients\n",
- this, obits ? obits->size() : 0);
-
- if (obits != NULL) {
- const size_t N = obits->size();
- for (size_t i=0; i<N; i++) {
- reportOneDeath(obits->itemAt(i));
- }
-
- delete obits;
- }
-}
-
-void BpBinder::reportOneDeath(const Obituary& obit)
-{
- sp<DeathRecipient> recipient = obit.recipient.promote();
- LOGV("Reporting death to recipient: %p\n", recipient.get());
- if (recipient == NULL) return;
-
- recipient->binderDied(this);
-}
-
-
-void BpBinder::attachObject(
- const void* objectID, void* object, void* cleanupCookie,
- object_cleanup_func func)
-{
- AutoMutex _l(mLock);
- LOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects);
- mObjects.attach(objectID, object, cleanupCookie, func);
-}
-
-void* BpBinder::findObject(const void* objectID) const
-{
- AutoMutex _l(mLock);
- return mObjects.find(objectID);
-}
-
-void BpBinder::detachObject(const void* objectID)
-{
- AutoMutex _l(mLock);
- mObjects.detach(objectID);
-}
-
-BpBinder* BpBinder::remoteBinder()
-{
- return this;
-}
-
-BpBinder::~BpBinder()
-{
- LOGV("Destroying BpBinder %p handle %d\n", this, mHandle);
-
- IPCThreadState* ipc = IPCThreadState::self();
-
- mLock.lock();
- Vector<Obituary>* obits = mObituaries;
- if(obits != NULL) {
- if (ipc) ipc->clearDeathNotification(mHandle, this);
- mObituaries = NULL;
- }
- mLock.unlock();
-
- if (obits != NULL) {
- // XXX Should we tell any remaining DeathRecipient
- // objects that the last strong ref has gone away, so they
- // are no longer linked?
- delete obits;
- }
-
- if (ipc) {
- ipc->expungeHandle(mHandle, this);
- ipc->decWeakHandle(mHandle);
- }
-}
-
-void BpBinder::onFirstRef()
-{
- LOGV("onFirstRef BpBinder %p handle %d\n", this, mHandle);
- IPCThreadState* ipc = IPCThreadState::self();
- if (ipc) ipc->incStrongHandle(mHandle);
-}
-
-void BpBinder::onLastStrongRef(const void* id)
-{
- LOGV("onLastStrongRef BpBinder %p handle %d\n", this, mHandle);
- IF_LOGV() {
- printRefs();
- }
- IPCThreadState* ipc = IPCThreadState::self();
- if (ipc) ipc->decStrongHandle(mHandle);
-}
-
-bool BpBinder::onIncStrongAttempted(uint32_t flags, const void* id)
-{
- LOGV("onIncStrongAttempted BpBinder %p handle %d\n", this, mHandle);
- IPCThreadState* ipc = IPCThreadState::self();
- return ipc ? ipc->attemptIncStrongHandle(mHandle) == NO_ERROR : false;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp
index 2fdaa71..55b6024 100644
--- a/libs/utils/CallStack.cpp
+++ b/libs/utils/CallStack.cpp
@@ -311,7 +311,8 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const
} else {
void const* start = 0;
name = MapInfo::mapAddressToName(ip, "<unknown>", &start);
- snprintf(tmp, 256, "pc %08lx %s", uintptr_t(ip)-uintptr_t(start), name);
+ snprintf(tmp, 256, "pc %08lx %s",
+ long(uintptr_t(ip)-uintptr_t(start)), name);
res.append(tmp);
}
res.append("\n");
diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp
deleted file mode 100644
index c6d49aa..0000000
--- a/libs/utils/IDataConnection.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Parcel.h>
-
-#include <utils/IDataConnection.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-enum
-{
- CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
- DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1
-};
-
-class BpDataConnection : public BpInterface<IDataConnection>
-{
-public:
- BpDataConnection::BpDataConnection(const sp<IBinder>& impl)
- : BpInterface<IDataConnection>(impl)
- {
- }
-
- virtual void connect()
- {
- Parcel data, reply;
- data.writeInterfaceToken(IDataConnection::descriptor());
- remote()->transact(CONNECT_TRANSACTION, data, &reply);
- }
-
- virtual void disconnect()
- {
- Parcel data, reply;
- remote()->transact(DISCONNECT_TRANSACTION, data, &reply);
- }
-};
-
-IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection");
-
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
-status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code)
- {
- case CONNECT_TRANSACTION:
- {
- CHECK_INTERFACE(IDataConnection, data, reply);
- connect();
- return NO_ERROR;
- }
-
- case DISCONNECT_TRANSACTION:
- {
- CHECK_INTERFACE(IDataConnection, data, reply);
- disconnect();
- return NO_ERROR;
- }
-
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/utils/IInterface.cpp b/libs/utils/IInterface.cpp
deleted file mode 100644
index 6ea8178..0000000
--- a/libs/utils/IInterface.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#include <utils/IInterface.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-sp<IBinder> IInterface::asBinder()
-{
- return this ? onAsBinder() : NULL;
-}
-
-sp<const IBinder> IInterface::asBinder() const
-{
- return this ? const_cast<IInterface*>(this)->onAsBinder() : NULL;
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/libs/utils/IMemory.cpp b/libs/utils/IMemory.cpp
deleted file mode 100644
index 429bc2b..0000000
--- a/libs/utils/IMemory.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "IMemory"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <sys/types.h>
-#include <sys/mman.h>
-
-#include <utils/IMemory.h>
-#include <utils/KeyedVector.h>
-#include <utils/threads.h>
-#include <utils/Atomic.h>
-#include <utils/Parcel.h>
-#include <utils/CallStack.h>
-
-#define VERBOSE 0
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class HeapCache : public IBinder::DeathRecipient
-{
-public:
- HeapCache();
- virtual ~HeapCache();
-
- virtual void binderDied(const wp<IBinder>& who);
-
- sp<IMemoryHeap> find_heap(const sp<IBinder>& binder);
- void pin_heap(const sp<IBinder>& binder);
- void free_heap(const sp<IBinder>& binder);
- sp<IMemoryHeap> get_heap(const sp<IBinder>& binder);
- void dump_heaps();
-
-private:
- // For IMemory.cpp
- struct heap_info_t {
- sp<IMemoryHeap> heap;
- int32_t count;
- };
-
- void free_heap(const wp<IBinder>& binder);
-
- Mutex mHeapCacheLock;
- KeyedVector< wp<IBinder>, heap_info_t > mHeapCache;
-};
-
-static sp<HeapCache> gHeapCache = new HeapCache();
-
-/******************************************************************************/
-
-enum {
- HEAP_ID = IBinder::FIRST_CALL_TRANSACTION
-};
-
-class BpMemoryHeap : public BpInterface<IMemoryHeap>
-{
-public:
- BpMemoryHeap(const sp<IBinder>& impl);
- virtual ~BpMemoryHeap();
-
- virtual int getHeapID() const;
- virtual void* getBase() const;
- virtual size_t getSize() const;
- virtual uint32_t getFlags() const;
-
-private:
- friend class IMemory;
- friend class HeapCache;
-
- // for debugging in this module
- static inline sp<IMemoryHeap> find_heap(const sp<IBinder>& binder) {
- return gHeapCache->find_heap(binder);
- }
- static inline void free_heap(const sp<IBinder>& binder) {
- gHeapCache->free_heap(binder);
- }
- static inline sp<IMemoryHeap> get_heap(const sp<IBinder>& binder) {
- return gHeapCache->get_heap(binder);
- }
- static inline void dump_heaps() {
- gHeapCache->dump_heaps();
- }
- void inline pin_heap() const {
- gHeapCache->pin_heap(const_cast<BpMemoryHeap*>(this)->asBinder());
- }
-
- void assertMapped() const;
- void assertReallyMapped() const;
- void pinHeap() const;
-
- mutable volatile int32_t mHeapId;
- mutable void* mBase;
- mutable size_t mSize;
- mutable uint32_t mFlags;
- mutable bool mRealHeap;
- mutable Mutex mLock;
-};
-
-// ----------------------------------------------------------------------------
-
-enum {
- GET_MEMORY = IBinder::FIRST_CALL_TRANSACTION
-};
-
-class BpMemory : public BpInterface<IMemory>
-{
-public:
- BpMemory(const sp<IBinder>& impl);
- virtual ~BpMemory();
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset=0, size_t* size=0) const;
-
-private:
- mutable sp<IMemoryHeap> mHeap;
- mutable ssize_t mOffset;
- mutable size_t mSize;
-};
-
-/******************************************************************************/
-
-void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const
-{
- sp<IMemoryHeap> realHeap = BpMemoryHeap::get_heap(binder);
- void* const base = realHeap->base();
- if (base == MAP_FAILED)
- return 0;
- return static_cast<char*>(base) + offset;
-}
-
-void* IMemory::pointer() const {
- ssize_t offset;
- sp<IMemoryHeap> heap = getMemory(&offset);
- void* const base = heap!=0 ? heap->base() : MAP_FAILED;
- if (base == MAP_FAILED)
- return 0;
- return static_cast<char*>(base) + offset;
-}
-
-size_t IMemory::size() const {
- size_t size;
- getMemory(NULL, &size);
- return size;
-}
-
-ssize_t IMemory::offset() const {
- ssize_t offset;
- getMemory(&offset);
- return offset;
-}
-
-/******************************************************************************/
-
-BpMemory::BpMemory(const sp<IBinder>& impl)
- : BpInterface<IMemory>(impl), mOffset(0), mSize(0)
-{
-}
-
-BpMemory::~BpMemory()
-{
-}
-
-sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const
-{
- if (mHeap == 0) {
- Parcel data, reply;
- data.writeInterfaceToken(IMemory::getInterfaceDescriptor());
- if (remote()->transact(GET_MEMORY, data, &reply) == NO_ERROR) {
- sp<IBinder> heap = reply.readStrongBinder();
- ssize_t o = reply.readInt32();
- size_t s = reply.readInt32();
- if (heap != 0) {
- mHeap = interface_cast<IMemoryHeap>(heap);
- if (mHeap != 0) {
- mOffset = o;
- mSize = s;
- }
- }
- }
- }
- if (offset) *offset = mOffset;
- if (size) *size = mSize;
- return mHeap;
-}
-
-// ---------------------------------------------------------------------------
-
-IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory");
-
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
-status_t BnMemory::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case GET_MEMORY: {
- CHECK_INTERFACE(IMemory, data, reply);
- ssize_t offset;
- size_t size;
- reply->writeStrongBinder( getMemory(&offset, &size)->asBinder() );
- reply->writeInt32(offset);
- reply->writeInt32(size);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-
-/******************************************************************************/
-
-BpMemoryHeap::BpMemoryHeap(const sp<IBinder>& impl)
- : BpInterface<IMemoryHeap>(impl),
- mHeapId(-1), mBase(MAP_FAILED), mSize(0), mFlags(0), mRealHeap(false)
-{
-}
-
-BpMemoryHeap::~BpMemoryHeap() {
- if (mHeapId != -1) {
- close(mHeapId);
- if (mRealHeap) {
- // by construction we're the last one
- if (mBase != MAP_FAILED) {
- sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
-
- if (VERBOSE) {
- LOGD("UNMAPPING binder=%p, heap=%p, size=%d, fd=%d",
- binder.get(), this, mSize, mHeapId);
- CallStack stack;
- stack.update();
- stack.dump("callstack");
- }
-
- munmap(mBase, mSize);
- }
- } else {
- // remove from list only if it was mapped before
- sp<IBinder> binder = const_cast<BpMemoryHeap*>(this)->asBinder();
- free_heap(binder);
- }
- }
-}
-
-void BpMemoryHeap::assertMapped() const
-{
- if (mHeapId == -1) {
- sp<IBinder> binder(const_cast<BpMemoryHeap*>(this)->asBinder());
- sp<BpMemoryHeap> heap(static_cast<BpMemoryHeap*>(find_heap(binder).get()));
- heap->assertReallyMapped();
- if (heap->mBase != MAP_FAILED) {
- Mutex::Autolock _l(mLock);
- if (mHeapId == -1) {
- mBase = heap->mBase;
- mSize = heap->mSize;
- android_atomic_write( dup( heap->mHeapId ), &mHeapId );
- }
- } else {
- // something went wrong
- free_heap(binder);
- }
- }
-}
-
-void BpMemoryHeap::assertReallyMapped() const
-{
- if (mHeapId == -1) {
-
- // remote call without mLock held, worse case scenario, we end up
- // calling transact() from multiple threads, but that's not a problem,
- // only mmap below must be in the critical section.
-
- Parcel data, reply;
- data.writeInterfaceToken(IMemoryHeap::getInterfaceDescriptor());
- status_t err = remote()->transact(HEAP_ID, data, &reply);
- int parcel_fd = reply.readFileDescriptor();
- ssize_t size = reply.readInt32();
- uint32_t flags = reply.readInt32();
-
- LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)",
- asBinder().get(), parcel_fd, size, err, strerror(-err));
-
- int fd = dup( parcel_fd );
- LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)",
- parcel_fd, size, err, strerror(errno));
-
- int access = PROT_READ;
- if (!(flags & READ_ONLY)) {
- access |= PROT_WRITE;
- }
-
- Mutex::Autolock _l(mLock);
- if (mHeapId == -1) {
- mRealHeap = true;
- mBase = mmap(0, size, access, MAP_SHARED, fd, 0);
- if (mBase == MAP_FAILED) {
- LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)",
- asBinder().get(), size, fd, strerror(errno));
- close(fd);
- } else {
- if (flags & MAP_ONCE) {
- //LOGD("pinning heap (binder=%p, size=%d, fd=%d",
- // asBinder().get(), size, fd);
- pin_heap();
- }
- mSize = size;
- mFlags = flags;
- android_atomic_write(fd, &mHeapId);
- }
- }
- }
-}
-
-int BpMemoryHeap::getHeapID() const {
- assertMapped();
- return mHeapId;
-}
-
-void* BpMemoryHeap::getBase() const {
- assertMapped();
- return mBase;
-}
-
-size_t BpMemoryHeap::getSize() const {
- assertMapped();
- return mSize;
-}
-
-uint32_t BpMemoryHeap::getFlags() const {
- assertMapped();
- return mFlags;
-}
-
-// ---------------------------------------------------------------------------
-
-IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap");
-
-status_t BnMemoryHeap::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- switch(code) {
- case HEAP_ID: {
- CHECK_INTERFACE(IMemoryHeap, data, reply);
- reply->writeFileDescriptor(getHeapID());
- reply->writeInt32(getSize());
- reply->writeInt32(getFlags());
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-/*****************************************************************************/
-
-HeapCache::HeapCache()
- : DeathRecipient()
-{
-}
-
-HeapCache::~HeapCache()
-{
-}
-
-void HeapCache::binderDied(const wp<IBinder>& binder)
-{
- //LOGD("binderDied binder=%p", binder.unsafe_get());
- free_heap(binder);
-}
-
-sp<IMemoryHeap> HeapCache::find_heap(const sp<IBinder>& binder)
-{
- Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) {
- heap_info_t& info = mHeapCache.editValueAt(i);
- LOGD_IF(VERBOSE,
- "found binder=%p, heap=%p, size=%d, fd=%d, count=%d",
- binder.get(), info.heap.get(),
- static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
- info.count);
- android_atomic_inc(&info.count);
- return info.heap;
- } else {
- heap_info_t info;
- info.heap = interface_cast<IMemoryHeap>(binder);
- info.count = 1;
- //LOGD("adding binder=%p, heap=%p, count=%d",
- // binder.get(), info.heap.get(), info.count);
- mHeapCache.add(binder, info);
- return info.heap;
- }
-}
-
-void HeapCache::pin_heap(const sp<IBinder>& binder)
-{
- Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) {
- heap_info_t& info(mHeapCache.editValueAt(i));
- android_atomic_inc(&info.count);
- binder->linkToDeath(this);
- } else {
- LOGE("pin_heap binder=%p not found!!!", binder.get());
- }
-}
-
-void HeapCache::free_heap(const sp<IBinder>& binder) {
- free_heap( wp<IBinder>(binder) );
-}
-
-void HeapCache::free_heap(const wp<IBinder>& binder)
-{
- sp<IMemoryHeap> rel;
- {
- Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) {
- heap_info_t& info(mHeapCache.editValueAt(i));
- int32_t c = android_atomic_dec(&info.count);
- if (c == 1) {
- LOGD_IF(VERBOSE,
- "removing binder=%p, heap=%p, size=%d, fd=%d, count=%d",
- binder.unsafe_get(), info.heap.get(),
- static_cast<BpMemoryHeap*>(info.heap.get())->mSize,
- static_cast<BpMemoryHeap*>(info.heap.get())->mHeapId,
- info.count);
- rel = mHeapCache.valueAt(i).heap;
- mHeapCache.removeItemsAt(i);
- }
- } else {
- LOGE("free_heap binder=%p not found!!!", binder.unsafe_get());
- }
- }
-}
-
-sp<IMemoryHeap> HeapCache::get_heap(const sp<IBinder>& binder)
-{
- sp<IMemoryHeap> realHeap;
- Mutex::Autolock _l(mHeapCacheLock);
- ssize_t i = mHeapCache.indexOfKey(binder);
- if (i>=0) realHeap = mHeapCache.valueAt(i).heap;
- else realHeap = interface_cast<IMemoryHeap>(binder);
- return realHeap;
-}
-
-void HeapCache::dump_heaps()
-{
- Mutex::Autolock _l(mHeapCacheLock);
- int c = mHeapCache.size();
- for (int i=0 ; i<c ; i++) {
- const heap_info_t& info = mHeapCache.valueAt(i);
- BpMemoryHeap const* h(static_cast<BpMemoryHeap const *>(info.heap.get()));
- LOGD("hey=%p, heap=%p, count=%d, (fd=%d, base=%p, size=%d)",
- mHeapCache.keyAt(i).unsafe_get(),
- info.heap.get(), info.count,
- h->mHeapId, h->mBase, h->mSize);
- }
-}
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/utils/IPCThreadState.cpp b/libs/utils/IPCThreadState.cpp
deleted file mode 100644
index 04ae142..0000000
--- a/libs/utils/IPCThreadState.cpp
+++ /dev/null
@@ -1,1030 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#include <utils/IPCThreadState.h>
-
-#include <utils/Binder.h>
-#include <utils/BpBinder.h>
-#include <utils/Debug.h>
-#include <utils/Log.h>
-#include <utils/TextOutput.h>
-#include <utils/threads.h>
-
-#include <private/utils/binder_module.h>
-#include <private/utils/Static.h>
-
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <errno.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#ifdef HAVE_PTHREADS
-#include <pthread.h>
-#include <sched.h>
-#include <sys/resource.h>
-#endif
-#ifdef HAVE_WIN32_THREADS
-#include <windows.h>
-#endif
-
-
-#if LOG_NDEBUG
-
-#define IF_LOG_TRANSACTIONS() if (false)
-#define IF_LOG_COMMANDS() if (false)
-#define LOG_REMOTEREFS(...)
-#define IF_LOG_REMOTEREFS() if (false)
-#define LOG_THREADPOOL(...)
-#define LOG_ONEWAY(...)
-
-#else
-
-#define IF_LOG_TRANSACTIONS() IF_LOG(LOG_VERBOSE, "transact")
-#define IF_LOG_COMMANDS() IF_LOG(LOG_VERBOSE, "ipc")
-#define LOG_REMOTEREFS(...) LOG(LOG_DEBUG, "remoterefs", __VA_ARGS__)
-#define IF_LOG_REMOTEREFS() IF_LOG(LOG_DEBUG, "remoterefs")
-#define LOG_THREADPOOL(...) LOG(LOG_DEBUG, "threadpool", __VA_ARGS__)
-#define LOG_ONEWAY(...) LOG(LOG_DEBUG, "ipc", __VA_ARGS__)
-
-#endif
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-static const char* getReturnString(size_t idx);
-static const char* getCommandString(size_t idx);
-static const void* printReturnCommand(TextOutput& out, const void* _cmd);
-static const void* printCommand(TextOutput& out, const void* _cmd);
-
-// This will result in a missing symbol failure if the IF_LOG_COMMANDS()
-// conditionals don't get stripped... but that is probably what we want.
-#if !LOG_NDEBUG
-static const char *kReturnStrings[] = {
-#if 1 /* TODO: error update strings */
- "unknown",
-#else
- "BR_OK",
- "BR_TIMEOUT",
- "BR_WAKEUP",
- "BR_TRANSACTION",
- "BR_REPLY",
- "BR_ACQUIRE_RESULT",
- "BR_DEAD_REPLY",
- "BR_TRANSACTION_COMPLETE",
- "BR_INCREFS",
- "BR_ACQUIRE",
- "BR_RELEASE",
- "BR_DECREFS",
- "BR_ATTEMPT_ACQUIRE",
- "BR_EVENT_OCCURRED",
- "BR_NOOP",
- "BR_SPAWN_LOOPER",
- "BR_FINISHED",
- "BR_DEAD_BINDER",
- "BR_CLEAR_DEATH_NOTIFICATION_DONE"
-#endif
-};
-
-static const char *kCommandStrings[] = {
-#if 1 /* TODO: error update strings */
- "unknown",
-#else
- "BC_NOOP",
- "BC_TRANSACTION",
- "BC_REPLY",
- "BC_ACQUIRE_RESULT",
- "BC_FREE_BUFFER",
- "BC_TRANSACTION_COMPLETE",
- "BC_INCREFS",
- "BC_ACQUIRE",
- "BC_RELEASE",
- "BC_DECREFS",
- "BC_INCREFS_DONE",
- "BC_ACQUIRE_DONE",
- "BC_ATTEMPT_ACQUIRE",
- "BC_RETRIEVE_ROOT_OBJECT",
- "BC_SET_THREAD_ENTRY",
- "BC_REGISTER_LOOPER",
- "BC_ENTER_LOOPER",
- "BC_EXIT_LOOPER",
- "BC_SYNC",
- "BC_STOP_PROCESS",
- "BC_STOP_SELF",
- "BC_REQUEST_DEATH_NOTIFICATION",
- "BC_CLEAR_DEATH_NOTIFICATION",
- "BC_DEAD_BINDER_DONE"
-#endif
-};
-
-static const char* getReturnString(size_t idx)
-{
- if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
- return kReturnStrings[idx];
- else
- return "unknown";
-}
-
-static const char* getCommandString(size_t idx)
-{
- if (idx < sizeof(kCommandStrings) / sizeof(kCommandStrings[0]))
- return kCommandStrings[idx];
- else
- return "unknown";
-}
-
-static const void* printBinderTransactionData(TextOutput& out, const void* data)
-{
- const binder_transaction_data* btd =
- (const binder_transaction_data*)data;
- out << "target=" << btd->target.ptr << " (cookie " << btd->cookie << ")" << endl
- << "code=" << TypeCode(btd->code) << ", flags=" << (void*)btd->flags << endl
- << "data=" << btd->data.ptr.buffer << " (" << (void*)btd->data_size
- << " bytes)" << endl
- << "offsets=" << btd->data.ptr.offsets << " (" << (void*)btd->offsets_size
- << " bytes)" << endl;
- return btd+1;
-}
-
-static const void* printReturnCommand(TextOutput& out, const void* _cmd)
-{
- static const int32_t N = sizeof(kReturnStrings)/sizeof(kReturnStrings[0]);
-
- const int32_t* cmd = (const int32_t*)_cmd;
- int32_t code = *cmd++;
- if (code == BR_ERROR) {
- out << "BR_ERROR: " << (void*)(*cmd++) << endl;
- return cmd;
- } else if (code < 0 || code >= N) {
- out << "Unknown reply: " << code << endl;
- return cmd;
- }
-
- out << kReturnStrings[code];
- switch (code) {
- case BR_TRANSACTION:
- case BR_REPLY: {
- out << ": " << indent;
- cmd = (const int32_t *)printBinderTransactionData(out, cmd);
- out << dedent;
- } break;
-
- case BR_ACQUIRE_RESULT: {
- const int32_t res = *cmd++;
- out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)");
- } break;
-
- case BR_INCREFS:
- case BR_ACQUIRE:
- case BR_RELEASE:
- case BR_DECREFS: {
- const int32_t b = *cmd++;
- const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
- } break;
-
- case BR_ATTEMPT_ACQUIRE: {
- const int32_t p = *cmd++;
- const int32_t b = *cmd++;
- const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c
- << "), pri=" << p;
- } break;
-
- case BR_DEAD_BINDER:
- case BR_CLEAR_DEATH_NOTIFICATION_DONE: {
- const int32_t c = *cmd++;
- out << ": death cookie " << (void*)c;
- } break;
- }
-
- out << endl;
- return cmd;
-}
-
-static const void* printCommand(TextOutput& out, const void* _cmd)
-{
- static const int32_t N = sizeof(kCommandStrings)/sizeof(kCommandStrings[0]);
-
- const int32_t* cmd = (const int32_t*)_cmd;
- int32_t code = *cmd++;
- if (code < 0 || code >= N) {
- out << "Unknown command: " << code << endl;
- return cmd;
- }
-
- out << kCommandStrings[code];
- switch (code) {
- case BC_TRANSACTION:
- case BC_REPLY: {
- out << ": " << indent;
- cmd = (const int32_t *)printBinderTransactionData(out, cmd);
- out << dedent;
- } break;
-
- case BC_ACQUIRE_RESULT: {
- const int32_t res = *cmd++;
- out << ": " << res << (res ? " (SUCCESS)" : " (FAILURE)");
- } break;
-
- case BC_FREE_BUFFER: {
- const int32_t buf = *cmd++;
- out << ": buffer=" << (void*)buf;
- } break;
-
- case BC_INCREFS:
- case BC_ACQUIRE:
- case BC_RELEASE:
- case BC_DECREFS: {
- const int32_t d = *cmd++;
- out << ": descriptor=" << (void*)d;
- } break;
-
- case BC_INCREFS_DONE:
- case BC_ACQUIRE_DONE: {
- const int32_t b = *cmd++;
- const int32_t c = *cmd++;
- out << ": target=" << (void*)b << " (cookie " << (void*)c << ")";
- } break;
-
- case BC_ATTEMPT_ACQUIRE: {
- const int32_t p = *cmd++;
- const int32_t d = *cmd++;
- out << ": decriptor=" << (void*)d << ", pri=" << p;
- } break;
-
- case BC_REQUEST_DEATH_NOTIFICATION:
- case BC_CLEAR_DEATH_NOTIFICATION: {
- const int32_t h = *cmd++;
- const int32_t c = *cmd++;
- out << ": handle=" << h << " (death cookie " << (void*)c << ")";
- } break;
-
- case BC_DEAD_BINDER_DONE: {
- const int32_t c = *cmd++;
- out << ": death cookie " << (void*)c;
- } break;
- }
-
- out << endl;
- return cmd;
-}
-#endif
-
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-static bool gShutdown = false;
-
-IPCThreadState* IPCThreadState::self()
-{
- if (gHaveTLS) {
-restart:
- const pthread_key_t k = gTLS;
- IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
- if (st) return st;
- return new IPCThreadState;
- }
-
- if (gShutdown) return NULL;
-
- pthread_mutex_lock(&gTLSMutex);
- if (!gHaveTLS) {
- if (pthread_key_create(&gTLS, threadDestructor) != 0) {
- pthread_mutex_unlock(&gTLSMutex);
- return NULL;
- }
- gHaveTLS = true;
- }
- pthread_mutex_unlock(&gTLSMutex);
- goto restart;
-}
-
-void IPCThreadState::shutdown()
-{
- gShutdown = true;
-
- if (gHaveTLS) {
- // XXX Need to wait for all thread pool threads to exit!
- IPCThreadState* st = (IPCThreadState*)pthread_getspecific(gTLS);
- if (st) {
- delete st;
- pthread_setspecific(gTLS, NULL);
- }
- gHaveTLS = false;
- }
-}
-
-sp<ProcessState> IPCThreadState::process()
-{
- return mProcess;
-}
-
-status_t IPCThreadState::clearLastError()
-{
- const status_t err = mLastError;
- mLastError = NO_ERROR;
- return err;
-}
-
-int IPCThreadState::getCallingPid()
-{
- return mCallingPid;
-}
-
-int IPCThreadState::getCallingUid()
-{
- return mCallingUid;
-}
-
-int64_t IPCThreadState::clearCallingIdentity()
-{
- int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
- clearCaller();
- return token;
-}
-
-void IPCThreadState::restoreCallingIdentity(int64_t token)
-{
- mCallingUid = (int)(token>>32);
- mCallingPid = (int)token;
-}
-
-void IPCThreadState::clearCaller()
-{
- if (mProcess->supportsProcesses()) {
- mCallingPid = getpid();
- mCallingUid = getuid();
- } else {
- mCallingPid = -1;
- mCallingUid = -1;
- }
-}
-
-void IPCThreadState::flushCommands()
-{
- if (mProcess->mDriverFD <= 0)
- return;
- talkWithDriver(false);
-}
-
-void IPCThreadState::joinThreadPool(bool isMain)
-{
- LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
-
- mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
-
- status_t result;
- do {
- int32_t cmd;
-
- // When we've cleared the incoming command queue, process any pending derefs
- if (mIn.dataPosition() >= mIn.dataSize()) {
- size_t numPending = mPendingWeakDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- RefBase::weakref_type* refs = mPendingWeakDerefs[i];
- refs->decWeak(mProcess.get());
- }
- mPendingWeakDerefs.clear();
- }
-
- numPending = mPendingStrongDerefs.size();
- if (numPending > 0) {
- for (size_t i = 0; i < numPending; i++) {
- BBinder* obj = mPendingStrongDerefs[i];
- obj->decStrong(mProcess.get());
- }
- mPendingStrongDerefs.clear();
- }
- }
-
- // now get the next command to be processed, waiting if necessary
- result = talkWithDriver();
- if (result >= NO_ERROR) {
- size_t IN = mIn.dataAvail();
- if (IN < sizeof(int32_t)) continue;
- cmd = mIn.readInt32();
- IF_LOG_COMMANDS() {
- alog << "Processing top-level Command: "
- << getReturnString(cmd) << endl;
- }
- result = executeCommand(cmd);
- }
-
- // Let this thread exit the thread pool if it is no longer
- // needed and it is not the main process thread.
- if(result == TIMED_OUT && !isMain) {
- break;
- }
- } while (result != -ECONNREFUSED && result != -EBADF);
-
- LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\n",
- (void*)pthread_self(), getpid(), (void*)result);
-
- mOut.writeInt32(BC_EXIT_LOOPER);
- talkWithDriver(false);
-}
-
-void IPCThreadState::stopProcess(bool immediate)
-{
- //LOGI("**** STOPPING PROCESS");
- flushCommands();
- int fd = mProcess->mDriverFD;
- mProcess->mDriverFD = -1;
- close(fd);
- //kill(getpid(), SIGKILL);
-}
-
-status_t IPCThreadState::transact(int32_t handle,
- uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags)
-{
- status_t err = data.errorCheck();
-
- flags |= TF_ACCEPT_FDS;
-
- IF_LOG_TRANSACTIONS() {
- TextOutput::Bundle _b(alog);
- alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
- << handle << " / code " << TypeCode(code) << ": "
- << indent << data << dedent << endl;
- }
-
- if (err == NO_ERROR) {
- LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
- (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
- err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
- }
-
- if (err != NO_ERROR) {
- if (reply) reply->setError(err);
- return (mLastError = err);
- }
-
- if ((flags & TF_ONE_WAY) == 0) {
- if (reply) {
- err = waitForResponse(reply);
- } else {
- Parcel fakeReply;
- err = waitForResponse(&fakeReply);
- }
-
- IF_LOG_TRANSACTIONS() {
- TextOutput::Bundle _b(alog);
- alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
- << handle << ": ";
- if (reply) alog << indent << *reply << dedent << endl;
- else alog << "(none requested)" << endl;
- }
- } else {
- err = waitForResponse(NULL, NULL);
- }
-
- return err;
-}
-
-void IPCThreadState::incStrongHandle(int32_t handle)
-{
- LOG_REMOTEREFS("IPCThreadState::incStrongHandle(%d)\n", handle);
- mOut.writeInt32(BC_ACQUIRE);
- mOut.writeInt32(handle);
-}
-
-void IPCThreadState::decStrongHandle(int32_t handle)
-{
- LOG_REMOTEREFS("IPCThreadState::decStrongHandle(%d)\n", handle);
- mOut.writeInt32(BC_RELEASE);
- mOut.writeInt32(handle);
-}
-
-void IPCThreadState::incWeakHandle(int32_t handle)
-{
- LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);
- mOut.writeInt32(BC_INCREFS);
- mOut.writeInt32(handle);
-}
-
-void IPCThreadState::decWeakHandle(int32_t handle)
-{
- LOG_REMOTEREFS("IPCThreadState::decWeakHandle(%d)\n", handle);
- mOut.writeInt32(BC_DECREFS);
- mOut.writeInt32(handle);
-}
-
-status_t IPCThreadState::attemptIncStrongHandle(int32_t handle)
-{
- mOut.writeInt32(BC_ATTEMPT_ACQUIRE);
- mOut.writeInt32(0); // xxx was thread priority
- mOut.writeInt32(handle);
- status_t result = UNKNOWN_ERROR;
-
- waitForResponse(NULL, &result);
-
-#if LOG_REFCOUNTS
- printf("IPCThreadState::attemptIncStrongHandle(%ld) = %s\n",
- handle, result == NO_ERROR ? "SUCCESS" : "FAILURE");
-#endif
-
- return result;
-}
-
-void IPCThreadState::expungeHandle(int32_t handle, IBinder* binder)
-{
-#if LOG_REFCOUNTS
- printf("IPCThreadState::expungeHandle(%ld)\n", handle);
-#endif
- self()->mProcess->expungeHandle(handle, binder);
-}
-
-status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
-{
- mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
- mOut.writeInt32((int32_t)handle);
- mOut.writeInt32((int32_t)proxy);
- return NO_ERROR;
-}
-
-status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
-{
- mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);
- mOut.writeInt32((int32_t)handle);
- mOut.writeInt32((int32_t)proxy);
- return NO_ERROR;
-}
-
-IPCThreadState::IPCThreadState()
- : mProcess(ProcessState::self())
-{
- pthread_setspecific(gTLS, this);
- clearCaller();
- mIn.setDataCapacity(256);
- mOut.setDataCapacity(256);
-}
-
-IPCThreadState::~IPCThreadState()
-{
-}
-
-status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
-{
- status_t err;
- status_t statusBuffer;
- err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
- if (err < NO_ERROR) return err;
-
- return waitForResponse(NULL, NULL);
-}
-
-status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
-{
- int32_t cmd;
- int32_t err;
-
- while (1) {
- if ((err=talkWithDriver()) < NO_ERROR) break;
- err = mIn.errorCheck();
- if (err < NO_ERROR) break;
- if (mIn.dataAvail() == 0) continue;
-
- cmd = mIn.readInt32();
-
- IF_LOG_COMMANDS() {
- alog << "Processing waitForResponse Command: "
- << getReturnString(cmd) << endl;
- }
-
- switch (cmd) {
- case BR_TRANSACTION_COMPLETE:
- if (!reply && !acquireResult) goto finish;
- break;
-
- case BR_DEAD_REPLY:
- err = DEAD_OBJECT;
- goto finish;
-
- case BR_FAILED_REPLY:
- err = FAILED_TRANSACTION;
- goto finish;
-
- case BR_ACQUIRE_RESULT:
- {
- LOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
- const int32_t result = mIn.readInt32();
- if (!acquireResult) continue;
- *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
- }
- goto finish;
-
- case BR_REPLY:
- {
- binder_transaction_data tr;
- err = mIn.read(&tr, sizeof(tr));
- LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
- if (err != NO_ERROR) goto finish;
-
- if (reply) {
- if ((tr.flags & TF_STATUS_CODE) == 0) {
- reply->ipcSetDataReference(
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(size_t),
- freeBuffer, this);
- } else {
- err = *static_cast<const status_t*>(tr.data.ptr.buffer);
- freeBuffer(NULL,
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(size_t), this);
- }
- } else {
- freeBuffer(NULL,
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(size_t), this);
- continue;
- }
- }
- goto finish;
-
- default:
- err = executeCommand(cmd);
- if (err != NO_ERROR) goto finish;
- break;
- }
- }
-
-finish:
- if (err != NO_ERROR) {
- if (acquireResult) *acquireResult = err;
- if (reply) reply->setError(err);
- mLastError = err;
- }
-
- return err;
-}
-
-status_t IPCThreadState::talkWithDriver(bool doReceive)
-{
- LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened");
-
- binder_write_read bwr;
-
- // Is the read buffer empty?
- const bool needRead = mIn.dataPosition() >= mIn.dataSize();
-
- // We don't want to write anything if we are still reading
- // from data left in the input buffer and the caller
- // has requested to read the next data.
- const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
-
- bwr.write_size = outAvail;
- bwr.write_buffer = (long unsigned int)mOut.data();
-
- // This is what we'll read.
- if (doReceive && needRead) {
- bwr.read_size = mIn.dataCapacity();
- bwr.read_buffer = (long unsigned int)mIn.data();
- } else {
- bwr.read_size = 0;
- }
-
- IF_LOG_COMMANDS() {
- TextOutput::Bundle _b(alog);
- if (outAvail != 0) {
- alog << "Sending commands to driver: " << indent;
- const void* cmds = (const void*)bwr.write_buffer;
- const void* end = ((const uint8_t*)cmds)+bwr.write_size;
- alog << HexDump(cmds, bwr.write_size) << endl;
- while (cmds < end) cmds = printCommand(alog, cmds);
- alog << dedent;
- }
- alog << "Size of receive buffer: " << bwr.read_size
- << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
- }
-
- // Return immediately if there is nothing to do.
- if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
-
- bwr.write_consumed = 0;
- bwr.read_consumed = 0;
- status_t err;
- do {
- IF_LOG_COMMANDS() {
- alog << "About to read/write, write size = " << mOut.dataSize() << endl;
- }
-#if defined(HAVE_ANDROID_OS)
- if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
- err = NO_ERROR;
- else
- err = -errno;
-#else
- err = INVALID_OPERATION;
-#endif
- IF_LOG_COMMANDS() {
- alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
- }
- } while (err == -EINTR);
-
- IF_LOG_COMMANDS() {
- alog << "Our err: " << (void*)err << ", write consumed: "
- << bwr.write_consumed << " (of " << mOut.dataSize()
- << "), read consumed: " << bwr.read_consumed << endl;
- }
-
- if (err >= NO_ERROR) {
- if (bwr.write_consumed > 0) {
- if (bwr.write_consumed < (ssize_t)mOut.dataSize())
- mOut.remove(0, bwr.write_consumed);
- else
- mOut.setDataSize(0);
- }
- if (bwr.read_consumed > 0) {
- mIn.setDataSize(bwr.read_consumed);
- mIn.setDataPosition(0);
- }
- IF_LOG_COMMANDS() {
- TextOutput::Bundle _b(alog);
- alog << "Remaining data size: " << mOut.dataSize() << endl;
- alog << "Received commands from driver: " << indent;
- const void* cmds = mIn.data();
- const void* end = mIn.data() + mIn.dataSize();
- alog << HexDump(cmds, mIn.dataSize()) << endl;
- while (cmds < end) cmds = printReturnCommand(alog, cmds);
- alog << dedent;
- }
- return NO_ERROR;
- }
-
- return err;
-}
-
-status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
- int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
-{
- binder_transaction_data tr;
-
- tr.target.handle = handle;
- tr.code = code;
- tr.flags = binderFlags;
-
- const status_t err = data.errorCheck();
- if (err == NO_ERROR) {
- tr.data_size = data.ipcDataSize();
- tr.data.ptr.buffer = data.ipcData();
- tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
- tr.data.ptr.offsets = data.ipcObjects();
- } else if (statusBuffer) {
- tr.flags |= TF_STATUS_CODE;
- *statusBuffer = err;
- tr.data_size = sizeof(status_t);
- tr.data.ptr.buffer = statusBuffer;
- tr.offsets_size = 0;
- tr.data.ptr.offsets = NULL;
- } else {
- return (mLastError = err);
- }
-
- mOut.writeInt32(cmd);
- mOut.write(&tr, sizeof(tr));
-
- return NO_ERROR;
-}
-
-sp<BBinder> the_context_object;
-
-void setTheContextObject(sp<BBinder> obj)
-{
- the_context_object = obj;
-}
-
-status_t IPCThreadState::executeCommand(int32_t cmd)
-{
- BBinder* obj;
- RefBase::weakref_type* refs;
- status_t result = NO_ERROR;
-
- switch (cmd) {
- case BR_ERROR:
- result = mIn.readInt32();
- break;
-
- case BR_OK:
- break;
-
- case BR_ACQUIRE:
- refs = (RefBase::weakref_type*)mIn.readInt32();
- obj = (BBinder*)mIn.readInt32();
- LOG_ASSERT(refs->refBase() == obj,
- "BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
- refs, obj, refs->refBase());
- obj->incStrong(mProcess.get());
- IF_LOG_REMOTEREFS() {
- LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
- obj->printRefs();
- }
- mOut.writeInt32(BC_ACQUIRE_DONE);
- mOut.writeInt32((int32_t)refs);
- mOut.writeInt32((int32_t)obj);
- break;
-
- case BR_RELEASE:
- refs = (RefBase::weakref_type*)mIn.readInt32();
- obj = (BBinder*)mIn.readInt32();
- LOG_ASSERT(refs->refBase() == obj,
- "BR_RELEASE: object %p does not match cookie %p (expected %p)",
- refs, obj, refs->refBase());
- IF_LOG_REMOTEREFS() {
- LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj);
- obj->printRefs();
- }
- mPendingStrongDerefs.push(obj);
- break;
-
- case BR_INCREFS:
- refs = (RefBase::weakref_type*)mIn.readInt32();
- obj = (BBinder*)mIn.readInt32();
- refs->incWeak(mProcess.get());
- mOut.writeInt32(BC_INCREFS_DONE);
- mOut.writeInt32((int32_t)refs);
- mOut.writeInt32((int32_t)obj);
- break;
-
- case BR_DECREFS:
- refs = (RefBase::weakref_type*)mIn.readInt32();
- obj = (BBinder*)mIn.readInt32();
- // NOTE: This assertion is not valid, because the object may no
- // longer exist (thus the (BBinder*)cast above resulting in a different
- // memory address).
- //LOG_ASSERT(refs->refBase() == obj,
- // "BR_DECREFS: object %p does not match cookie %p (expected %p)",
- // refs, obj, refs->refBase());
- mPendingWeakDerefs.push(refs);
- break;
-
- case BR_ATTEMPT_ACQUIRE:
- refs = (RefBase::weakref_type*)mIn.readInt32();
- obj = (BBinder*)mIn.readInt32();
-
- {
- const bool success = refs->attemptIncStrong(mProcess.get());
- LOG_ASSERT(success && refs->refBase() == obj,
- "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)",
- refs, obj, refs->refBase());
-
- mOut.writeInt32(BC_ACQUIRE_RESULT);
- mOut.writeInt32((int32_t)success);
- }
- break;
-
- case BR_TRANSACTION:
- {
- binder_transaction_data tr;
- result = mIn.read(&tr, sizeof(tr));
- LOG_ASSERT(result == NO_ERROR,
- "Not enough command data for brTRANSACTION");
- if (result != NO_ERROR) break;
-
- Parcel buffer;
- buffer.ipcSetDataReference(
- reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
- tr.data_size,
- reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
- tr.offsets_size/sizeof(size_t), freeBuffer, this);
-
- const pid_t origPid = mCallingPid;
- const uid_t origUid = mCallingUid;
-
- mCallingPid = tr.sender_pid;
- mCallingUid = tr.sender_euid;
-
- //LOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
-
- Parcel reply;
- IF_LOG_TRANSACTIONS() {
- TextOutput::Bundle _b(alog);
- alog << "BR_TRANSACTION thr " << (void*)pthread_self()
- << " / obj " << tr.target.ptr << " / code "
- << TypeCode(tr.code) << ": " << indent << buffer
- << dedent << endl
- << "Data addr = "
- << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
- << ", offsets addr="
- << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
- }
- if (tr.target.ptr) {
- sp<BBinder> b((BBinder*)tr.cookie);
- const status_t error = b->transact(tr.code, buffer, &reply, 0);
- if (error < NO_ERROR) reply.setError(error);
-
- } else {
- const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);
- if (error < NO_ERROR) reply.setError(error);
- }
-
- //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
- // mCallingPid, origPid, origUid);
-
- if ((tr.flags & TF_ONE_WAY) == 0) {
- LOG_ONEWAY("Sending reply to %d!", mCallingPid);
- sendReply(reply, 0);
- } else {
- LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
- }
-
- mCallingPid = origPid;
- mCallingUid = origUid;
-
- IF_LOG_TRANSACTIONS() {
- TextOutput::Bundle _b(alog);
- alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
- << tr.target.ptr << ": " << indent << reply << dedent << endl;
- }
-
- }
- break;
-
- case BR_DEAD_BINDER:
- {
- BpBinder *proxy = (BpBinder*)mIn.readInt32();
- proxy->sendObituary();
- mOut.writeInt32(BC_DEAD_BINDER_DONE);
- mOut.writeInt32((int32_t)proxy);
- } break;
-
- case BR_CLEAR_DEATH_NOTIFICATION_DONE:
- {
- BpBinder *proxy = (BpBinder*)mIn.readInt32();
- proxy->getWeakRefs()->decWeak(proxy);
- } break;
-
- case BR_FINISHED:
- result = TIMED_OUT;
- break;
-
- case BR_NOOP:
- break;
-
- case BR_SPAWN_LOOPER:
- mProcess->spawnPooledThread(false);
- break;
-
- default:
- printf("*** BAD COMMAND %d received from Binder driver\n", cmd);
- result = UNKNOWN_ERROR;
- break;
- }
-
- if (result != NO_ERROR) {
- mLastError = result;
- }
-
- return result;
-}
-
-void IPCThreadState::threadDestructor(void *st)
-{
- IPCThreadState* const self = static_cast<IPCThreadState*>(st);
- if (self) {
- self->flushCommands();
-#if defined(HAVE_ANDROID_OS)
- ioctl(self->mProcess->mDriverFD, BINDER_THREAD_EXIT, 0);
-#endif
- delete self;
- }
-}
-
-
-void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize,
- const size_t* objects, size_t objectsSize,
- void* cookie)
-{
- //LOGI("Freeing parcel %p", &parcel);
- IF_LOG_COMMANDS() {
- alog << "Writing BC_FREE_BUFFER for " << data << endl;
- }
- LOG_ASSERT(data != NULL, "Called with NULL data");
- if (parcel != NULL) parcel->closeFileDescriptors();
- IPCThreadState* state = self();
- state->mOut.writeInt32(BC_FREE_BUFFER);
- state->mOut.writeInt32((int32_t)data);
-}
-
-}; // namespace android
diff --git a/libs/utils/IPermissionController.cpp b/libs/utils/IPermissionController.cpp
deleted file mode 100644
index f01d38f..0000000
--- a/libs/utils/IPermissionController.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#define LOG_TAG "PermissionController"
-
-#include <utils/IPermissionController.h>
-
-#include <utils/Debug.h>
-#include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/String8.h>
-
-#include <private/utils/Static.h>
-
-namespace android {
-
-// ----------------------------------------------------------------------
-
-class BpPermissionController : public BpInterface<IPermissionController>
-{
-public:
- BpPermissionController(const sp<IBinder>& impl)
- : BpInterface<IPermissionController>(impl)
- {
- }
-
- virtual bool checkPermission(const String16& permission, int32_t pid, int32_t uid)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IPermissionController::getInterfaceDescriptor());
- data.writeString16(permission);
- data.writeInt32(pid);
- data.writeInt32(uid);
- remote()->transact(CHECK_PERMISSION_TRANSACTION, data, &reply);
- // fail on exception
- if (reply.readInt32() != 0) return 0;
- return reply.readInt32() != 0;
- }
-};
-
-IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController");
-
-// ----------------------------------------------------------------------
-
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
-status_t BnPermissionController::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- //printf("PermissionController received: "); data.print();
- switch(code) {
- case CHECK_PERMISSION_TRANSACTION: {
- CHECK_INTERFACE(IPermissionController, data, reply);
- String16 permission = data.readString16();
- int32_t pid = data.readInt32();
- int32_t uid = data.readInt32();
- bool res = checkPermission(permission, pid, uid);
- // write exception
- reply->writeInt32(0);
- reply->writeInt32(res ? 1 : 0);
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
-
diff --git a/libs/utils/IServiceManager.cpp b/libs/utils/IServiceManager.cpp
deleted file mode 100644
index 9beeadd..0000000
--- a/libs/utils/IServiceManager.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#define LOG_TAG "ServiceManager"
-
-#include <utils/IServiceManager.h>
-
-#include <utils/Debug.h>
-#include <utils/IPCThreadState.h>
-#include <utils/Log.h>
-#include <utils/Parcel.h>
-#include <utils/String8.h>
-#include <utils/SystemClock.h>
-
-#include <private/utils/Static.h>
-
-#include <unistd.h>
-
-namespace android {
-
-sp<IServiceManager> defaultServiceManager()
-{
- if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
-
- {
- AutoMutex _l(gDefaultServiceManagerLock);
- if (gDefaultServiceManager == NULL) {
- gDefaultServiceManager = interface_cast<IServiceManager>(
- ProcessState::self()->getContextObject(NULL));
- }
- }
-
- return gDefaultServiceManager;
-}
-
-bool checkCallingPermission(const String16& permission)
-{
- return checkCallingPermission(permission, NULL, NULL);
-}
-
-static String16 _permission("permission");
-
-bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid)
-{
- IPCThreadState* ipcState = IPCThreadState::self();
- int32_t pid = ipcState->getCallingPid();
- int32_t uid = ipcState->getCallingUid();
- if (outPid) *outPid = pid;
- if (outUid) *outUid= uid;
-
- sp<IPermissionController> pc;
- gDefaultServiceManagerLock.lock();
- pc = gPermissionController;
- gDefaultServiceManagerLock.unlock();
-
- int64_t startTime = 0;
-
- while (true) {
- if (pc != NULL) {
- bool res = pc->checkPermission(permission, pid, uid);
- if (res) {
- if (startTime != 0) {
- LOGI("Check passed after %d seconds for %s from uid=%d pid=%d",
- (int)((uptimeMillis()-startTime)/1000),
- String8(permission).string(), uid, pid);
- }
- return res;
- }
-
- // Is this a permission failure, or did the controller go away?
- if (pc->asBinder()->isBinderAlive()) {
- LOGW("Permission failure: %s from uid=%d pid=%d",
- String8(permission).string(), uid, pid);
- return false;
- }
-
- // Object is dead!
- gDefaultServiceManagerLock.lock();
- if (gPermissionController == pc) {
- gPermissionController = NULL;
- }
- gDefaultServiceManagerLock.unlock();
- }
-
- // Need to retrieve the permission controller.
- sp<IBinder> binder = defaultServiceManager()->checkService(_permission);
- if (binder == NULL) {
- // Wait for the permission controller to come back...
- if (startTime == 0) {
- startTime = uptimeMillis();
- LOGI("Waiting to check permission %s from uid=%d pid=%d",
- String8(permission).string(), uid, pid);
- }
- sleep(1);
- } else {
- pc = interface_cast<IPermissionController>(binder);
- // Install the new permission controller, and try again.
- gDefaultServiceManagerLock.lock();
- gPermissionController = pc;
- gDefaultServiceManagerLock.unlock();
- }
- }
-}
-
-// ----------------------------------------------------------------------
-
-class BpServiceManager : public BpInterface<IServiceManager>
-{
-public:
- BpServiceManager(const sp<IBinder>& impl)
- : BpInterface<IServiceManager>(impl)
- {
- }
-
- virtual sp<IBinder> getService(const String16& name) const
- {
- unsigned n;
- for (n = 0; n < 5; n++){
- sp<IBinder> svc = checkService(name);
- if (svc != NULL) return svc;
- LOGI("Waiting for sevice %s...\n", String8(name).string());
- sleep(1);
- }
- return NULL;
- }
-
- virtual sp<IBinder> checkService( const String16& name) const
- {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeString16(name);
- remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
- return reply.readStrongBinder();
- }
-
- virtual status_t addService(const String16& name, const sp<IBinder>& service)
- {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeString16(name);
- data.writeStrongBinder(service);
- status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
- return err == NO_ERROR ? reply.readInt32() : err;
- }
-
- virtual Vector<String16> listServices()
- {
- Vector<String16> res;
- int n = 0;
-
- for (;;) {
- Parcel data, reply;
- data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
- data.writeInt32(n++);
- status_t err = remote()->transact(LIST_SERVICES_TRANSACTION, data, &reply);
- if (err != NO_ERROR)
- break;
- res.add(reply.readString16());
- }
- return res;
- }
-};
-
-IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");
-
-// ----------------------------------------------------------------------
-
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
-status_t BnServiceManager::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- //printf("ServiceManager received: "); data.print();
- switch(code) {
- case GET_SERVICE_TRANSACTION: {
- CHECK_INTERFACE(IServiceManager, data, reply);
- String16 which = data.readString16();
- sp<IBinder> b = const_cast<BnServiceManager*>(this)->getService(which);
- reply->writeStrongBinder(b);
- return NO_ERROR;
- } break;
- case CHECK_SERVICE_TRANSACTION: {
- CHECK_INTERFACE(IServiceManager, data, reply);
- String16 which = data.readString16();
- sp<IBinder> b = const_cast<BnServiceManager*>(this)->checkService(which);
- reply->writeStrongBinder(b);
- return NO_ERROR;
- } break;
- case ADD_SERVICE_TRANSACTION: {
- CHECK_INTERFACE(IServiceManager, data, reply);
- String16 which = data.readString16();
- sp<IBinder> b = data.readStrongBinder();
- status_t err = addService(which, b);
- reply->writeInt32(err);
- return NO_ERROR;
- } break;
- case LIST_SERVICES_TRANSACTION: {
- CHECK_INTERFACE(IServiceManager, data, reply);
- Vector<String16> list = listServices();
- const size_t N = list.size();
- reply->writeInt32(N);
- for (size_t i=0; i<N; i++) {
- reply->writeString16(list[i]);
- }
- return NO_ERROR;
- } break;
- default:
- return BBinder::onTransact(code, data, reply, flags);
- }
-}
-
-}; // namespace android
-
diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp
deleted file mode 100644
index 39a0a68..0000000
--- a/libs/utils/InetAddress.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address class.
-//
-#ifdef HAVE_WINSOCK
-# include <winsock2.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-//# include <arpa/inet.h>
-# include <netdb.h>
-#endif
-
-#include <utils/inet_address.h>
-#include <utils/threads.h>
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- * InetAddress
- * ===========================================================================
- */
-
-// lock for the next couple of functions; could tuck into InetAddress
-static Mutex* gGHBNLock;
-
-/*
- * Lock/unlock access to the hostent struct returned by gethostbyname().
- */
-static inline void lock_gethostbyname(void)
-{
- if (gGHBNLock == NULL)
- gGHBNLock = new Mutex;
- gGHBNLock->lock();
-}
-static inline void unlock_gethostbyname(void)
-{
- assert(gGHBNLock != NULL);
- gGHBNLock->unlock();
-}
-
-
-/*
- * Constructor -- just init members. This is private so that callers
- * are required to use getByName().
- */
-InetAddress::InetAddress(void)
- : mAddress(NULL), mLength(-1), mName(NULL)
-{
-}
-
-/*
- * Destructor -- free address storage.
- */
-InetAddress::~InetAddress(void)
-{
- delete[] (char*) mAddress;
- delete[] mName;
-}
-
-/*
- * Copy constructor.
- */
-InetAddress::InetAddress(const InetAddress& orig)
-{
- *this = orig; // use assignment code
-}
-
-/*
- * Assignment operator.
- */
-InetAddress& InetAddress::operator=(const InetAddress& addr)
-{
- // handle self-assignment
- if (this == &addr)
- return *this;
- // copy mLength and mAddress
- mLength = addr.mLength;
- if (mLength > 0) {
- mAddress = new char[mLength];
- memcpy(mAddress, addr.mAddress, mLength);
- LOG(LOG_DEBUG, "socket",
- "HEY: copied %d bytes in assignment operator\n", mLength);
- } else {
- mAddress = NULL;
- }
- // copy mName
- mName = new char[strlen(addr.mName)+1];
- strcpy(mName, addr.mName);
-
- return *this;
-}
-
-/*
- * Create a new object from a name or a dotted-number IP notation.
- *
- * Returns NULL on failure.
- */
-InetAddress*
-InetAddress::getByName(const char* host)
-{
- InetAddress* newAddr = NULL;
- struct sockaddr_in addr;
- struct hostent* he;
- DurationTimer hostTimer, lockTimer;
-
- // gethostbyname() isn't reentrant, so we need to lock things until
- // we can copy the data out.
- lockTimer.start();
- lock_gethostbyname();
- hostTimer.start();
-
- he = gethostbyname(host);
- if (he == NULL) {
- LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host);
- unlock_gethostbyname();
- return NULL;
- }
-
- memcpy(&addr.sin_addr, he->h_addr, he->h_length);
- addr.sin_family = he->h_addrtype;
- addr.sin_port = 0;
-
- // got it, unlock us
- hostTimer.stop();
- he = NULL;
- unlock_gethostbyname();
-
- lockTimer.stop();
- if ((long) lockTimer.durationUsecs() > 100000) {
- long lockTime = (long) lockTimer.durationUsecs();
- long hostTime = (long) hostTimer.durationUsecs();
- LOG(LOG_DEBUG, "socket",
- "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n",
- host, lockTime / 1000000.0, hostTime / 1000000.0,
- (lockTime - hostTime) / 1000000.0);
- }
-
- // Alloc storage and copy it over.
- newAddr = new InetAddress();
- if (newAddr == NULL)
- return NULL;
-
- newAddr->mLength = sizeof(struct sockaddr_in);
- newAddr->mAddress = new char[sizeof(struct sockaddr_in)];
- if (newAddr->mAddress == NULL) {
- delete newAddr;
- return NULL;
- }
- memcpy(newAddr->mAddress, &addr, newAddr->mLength);
-
- // Keep this for debug messages.
- newAddr->mName = new char[strlen(host)+1];
- if (newAddr->mName == NULL) {
- delete newAddr;
- return NULL;
- }
- strcpy(newAddr->mName, host);
-
- return newAddr;
-}
-
-
-/*
- * ===========================================================================
- * InetSocketAddress
- * ===========================================================================
- */
-
-/*
- * Create an address with the host wildcard (INADDR_ANY).
- */
-bool InetSocketAddress::create(int port)
-{
- assert(mAddress == NULL);
-
- mAddress = InetAddress::getByName("0.0.0.0");
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const InetAddress* addr, int port)
-{
- assert(mAddress == NULL);
-
- mAddress = new InetAddress(*addr); // make a copy
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const char* host, int port)
-{
- assert(mAddress == NULL);
-
- mAddress = InetAddress::getByName(host);
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp
deleted file mode 100644
index 55c1b99..0000000
--- a/libs/utils/LogSocket.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef HAVE_WINSOCK
-//#define SOCKETLOG
-#endif
-
-#ifdef SOCKETLOG
-
-#define LOG_TAG "SOCKETLOG"
-
-#include <string.h>
-#include <cutils/log.h>
-#include "utils/LogSocket.h"
-#include "utils/logger.h"
-#include "cutils/hashmap.h"
-
-// defined in //device/data/etc/event-log-tags
-#define SOCKET_CLOSE_LOG 51000
-
-static Hashmap* statsMap = NULL;
-
-#define LOG_LIST_NUMBER 5
-
-typedef struct SocketStats {
- int fd;
- unsigned int send;
- unsigned int recv;
- unsigned int ip;
- unsigned short port;
- short reason;
-}SocketStats;
-
-SocketStats *get_socket_stats(int fd) {
- if (statsMap == NULL) {
- statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals);
- }
-
- SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
- if (s == NULL) {
- // LOGD("create SocketStats for fd %d", fd);
- s = (SocketStats*) malloc(sizeof(SocketStats));
- memset(s, 0, sizeof(SocketStats));
- s->fd = fd;
- hashmapPut(statsMap, &s->fd, s);
- }
- return s;
-}
-
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {
- // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port);
- SocketStats *s = get_socket_stats(fd);
- s->ip = ip;
- s->port = port;
-}
-
-void add_send_stats(int fd, int send) {
- if (send <=0) {
- LOGE("add_send_stats send %d", send);
- return;
- }
- SocketStats *s = get_socket_stats(fd);
- s->send += send;
- // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-void add_recv_stats(int fd, int recv) {
- if (recv <=0) {
- LOGE("add_recv_stats recv %d", recv);
- return;
- }
- SocketStats *s = get_socket_stats(fd);
- s->recv += recv;
- // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-char* put_int(char* buf, int value) {
- *buf = EVENT_TYPE_INT;
- buf++;
- memcpy(buf, &value, sizeof(int));
- return buf + sizeof(int);
-}
-
-void log_socket_close(int fd, short reason) {
- if (statsMap) {
- SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
- if (s != NULL) {
- if (s->send != 0 || s->recv != 0) {
- s->reason = reason;
- // 5 int + list type need 2 bytes
- char buf[LOG_LIST_NUMBER * 5 + 2];
- buf[0] = EVENT_TYPE_LIST;
- buf[1] = LOG_LIST_NUMBER;
- char* writePos = buf + 2;
- writePos = put_int(writePos, s->send);
- writePos = put_int(writePos, s->recv);
- writePos = put_int(writePos, s->ip);
- writePos = put_int(writePos, s->port);
- writePos = put_int(writePos, s->reason);
-
- android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf));
- // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason);
- }
- hashmapRemove(statsMap, &s->fd);
- free(s);
- }
- }
-}
-
-#else
-void add_send_stats(int fd, int send) {}
-void add_recv_stats(int fd, int recv) {}
-void log_socket_close(int fd, short reason) {}
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {}
-#endif
diff --git a/libs/utils/MemoryBase.cpp b/libs/utils/MemoryBase.cpp
deleted file mode 100644
index f25e11c..0000000
--- a/libs/utils/MemoryBase.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#include <utils/MemoryBase.h>
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-MemoryBase::MemoryBase(const sp<IMemoryHeap>& heap,
- ssize_t offset, size_t size)
- : mSize(size), mOffset(offset), mHeap(heap)
-{
-}
-
-sp<IMemoryHeap> MemoryBase::getMemory(ssize_t* offset, size_t* size) const
-{
- if (offset) *offset = mOffset;
- if (size) *size = mSize;
- return mHeap;
-}
-
-MemoryBase::~MemoryBase()
-{
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/utils/MemoryDealer.cpp b/libs/utils/MemoryDealer.cpp
deleted file mode 100644
index cf8201b..0000000
--- a/libs/utils/MemoryDealer.cpp
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MemoryDealer"
-
-#include <utils/MemoryDealer.h>
-
-#include <utils/Log.h>
-#include <utils/IPCThreadState.h>
-#include <utils/SortedVector.h>
-#include <utils/String8.h>
-#include <utils/MemoryBase.h>
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/file.h>
-
-namespace android {
-
-
-// ----------------------------------------------------------------------------
-
-class SimpleMemory : public MemoryBase {
-public:
- SimpleMemory(const sp<IMemoryHeap>& heap, ssize_t offset, size_t size);
- virtual ~SimpleMemory();
-};
-
-
-// ----------------------------------------------------------------------------
-
-MemoryDealer::Allocation::Allocation(
- const sp<MemoryDealer>& dealer, ssize_t offset, size_t size,
- const sp<IMemory>& memory)
- : mDealer(dealer), mOffset(offset), mSize(size), mMemory(memory)
-{
-}
-
-MemoryDealer::Allocation::~Allocation()
-{
- if (mSize) {
- /* NOTE: it's VERY important to not free allocations of size 0 because
- * they're special as they don't have any record in the allocator
- * and could alias some real allocation (their offset is zero). */
- mDealer->deallocate(mOffset);
- }
-}
-
-sp<IMemoryHeap> MemoryDealer::Allocation::getMemory(
- ssize_t* offset, size_t* size) const
-{
- return mMemory->getMemory(offset, size);
-}
-
-// ----------------------------------------------------------------------------
-
-MemoryDealer::MemoryDealer(size_t size, uint32_t flags, const char* name)
- : mHeap(new SharedHeap(size, flags, name)),
- mAllocator(new SimpleBestFitAllocator(size))
-{
-}
-
-MemoryDealer::MemoryDealer(const sp<HeapInterface>& heap)
- : mHeap(heap),
- mAllocator(new SimpleBestFitAllocator(heap->virtualSize()))
-{
-}
-
-MemoryDealer::MemoryDealer( const sp<HeapInterface>& heap,
- const sp<AllocatorInterface>& allocator)
- : mHeap(heap), mAllocator(allocator)
-{
-}
-
-MemoryDealer::~MemoryDealer()
-{
-}
-
-sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags)
-{
- sp<IMemory> memory;
- const ssize_t offset = allocator()->allocate(size, flags);
- if (offset >= 0) {
- sp<IMemory> new_memory = heap()->mapMemory(offset, size);
- if (new_memory != 0) {
- memory = new Allocation(this, offset, size, new_memory);
- } else {
- LOGE("couldn't map [%8x, %d]", offset, size);
- if (size) {
- /* NOTE: it's VERY important to not free allocations of size 0
- * because they're special as they don't have any record in the
- * allocator and could alias some real allocation
- * (their offset is zero). */
- allocator()->deallocate(offset);
- }
- }
- }
- return memory;
-}
-
-void MemoryDealer::deallocate(size_t offset)
-{
- allocator()->deallocate(offset);
-}
-
-void MemoryDealer::dump(const char* what, uint32_t flags) const
-{
- allocator()->dump(what, flags);
-}
-
-const sp<HeapInterface>& MemoryDealer::heap() const {
- return mHeap;
-}
-
-const sp<AllocatorInterface>& MemoryDealer::allocator() const {
- return mAllocator;
-}
-
-// ----------------------------------------------------------------------------
-
-// align all the memory blocks on a cache-line boundary
-const int SimpleBestFitAllocator::kMemoryAlign = 32;
-
-SimpleBestFitAllocator::SimpleBestFitAllocator(size_t size)
-{
- size_t pagesize = getpagesize();
- mHeapSize = ((size + pagesize-1) & ~(pagesize-1));
-
- chunk_t* node = new chunk_t(0, mHeapSize / kMemoryAlign);
- mList.insertHead(node);
-}
-
-SimpleBestFitAllocator::~SimpleBestFitAllocator()
-{
- while(!mList.isEmpty()) {
- delete mList.remove(mList.head());
- }
-}
-
-size_t SimpleBestFitAllocator::size() const
-{
- return mHeapSize;
-}
-
-size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags)
-{
- Mutex::Autolock _l(mLock);
- ssize_t offset = alloc(size, flags);
- return offset;
-}
-
-status_t SimpleBestFitAllocator::deallocate(size_t offset)
-{
- Mutex::Autolock _l(mLock);
- chunk_t const * const freed = dealloc(offset);
- if (freed) {
- return NO_ERROR;
- }
- return NAME_NOT_FOUND;
-}
-
-ssize_t SimpleBestFitAllocator::alloc(size_t size, uint32_t flags)
-{
- if (size == 0) {
- return 0;
- }
- size = (size + kMemoryAlign-1) / kMemoryAlign;
- chunk_t* free_chunk = 0;
- chunk_t* cur = mList.head();
-
- size_t pagesize = getpagesize();
- while (cur) {
- int extra = 0;
- if (flags & PAGE_ALIGNED)
- extra = ( -cur->start & ((pagesize/kMemoryAlign)-1) ) ;
-
- // best fit
- if (cur->free && (cur->size >= (size+extra))) {
- if ((!free_chunk) || (cur->size < free_chunk->size)) {
- free_chunk = cur;
- }
- if (cur->size == size) {
- break;
- }
- }
- cur = cur->next;
- }
-
- if (free_chunk) {
- const size_t free_size = free_chunk->size;
- free_chunk->free = 0;
- free_chunk->size = size;
- if (free_size > size) {
- int extra = 0;
- if (flags & PAGE_ALIGNED)
- extra = ( -free_chunk->start & ((pagesize/kMemoryAlign)-1) ) ;
- if (extra) {
- chunk_t* split = new chunk_t(free_chunk->start, extra);
- free_chunk->start += extra;
- mList.insertBefore(free_chunk, split);
- }
-
- LOGE_IF((flags&PAGE_ALIGNED) &&
- ((free_chunk->start*kMemoryAlign)&(pagesize-1)),
- "PAGE_ALIGNED requested, but page is not aligned!!!");
-
- const ssize_t tail_free = free_size - (size+extra);
- if (tail_free > 0) {
- chunk_t* split = new chunk_t(
- free_chunk->start + free_chunk->size, tail_free);
- mList.insertAfter(free_chunk, split);
- }
- }
- return (free_chunk->start)*kMemoryAlign;
- }
- return NO_MEMORY;
-}
-
-SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start)
-{
- start = start / kMemoryAlign;
- chunk_t* cur = mList.head();
- while (cur) {
- if (cur->start == start) {
- LOG_FATAL_IF(cur->free,
- "block at offset 0x%08lX of size 0x%08lX already freed",
- cur->start*kMemoryAlign, cur->size*kMemoryAlign);
-
- // merge freed blocks together
- chunk_t* freed = cur;
- cur->free = 1;
- do {
- chunk_t* const p = cur->prev;
- chunk_t* const n = cur->next;
- if (p && (p->free || !cur->size)) {
- freed = p;
- p->size += cur->size;
- mList.remove(cur);
- delete cur;
- }
- cur = n;
- } while (cur && cur->free);
-
- #ifndef NDEBUG
- if (!freed->free) {
- dump_l("dealloc (!freed->free)");
- }
- #endif
- LOG_FATAL_IF(!freed->free,
- "freed block at offset 0x%08lX of size 0x%08lX is not free!",
- freed->start * kMemoryAlign, freed->size * kMemoryAlign);
-
- return freed;
- }
- cur = cur->next;
- }
- return 0;
-}
-
-void SimpleBestFitAllocator::dump(const char* what, uint32_t flags) const
-{
- Mutex::Autolock _l(mLock);
- dump_l(what, flags);
-}
-
-void SimpleBestFitAllocator::dump_l(const char* what, uint32_t flags) const
-{
- String8 result;
- dump_l(result, what, flags);
- LOGD("%s", result.string());
-}
-
-void SimpleBestFitAllocator::dump(String8& result,
- const char* what, uint32_t flags) const
-{
- Mutex::Autolock _l(mLock);
- dump_l(result, what, flags);
-}
-
-void SimpleBestFitAllocator::dump_l(String8& result,
- const char* what, uint32_t flags) const
-{
- size_t size = 0;
- int32_t i = 0;
- chunk_t const* cur = mList.head();
-
- const size_t SIZE = 256;
- char buffer[SIZE];
- snprintf(buffer, SIZE, " %s (%p, size=%u)\n",
- what, this, (unsigned int)mHeapSize);
-
- result.append(buffer);
-
- while (cur) {
- const char* errs[] = {"", "| link bogus NP",
- "| link bogus PN", "| link bogus NP+PN" };
- int np = ((cur->next) && cur->next->prev != cur) ? 1 : 0;
- int pn = ((cur->prev) && cur->prev->next != cur) ? 2 : 0;
-
- snprintf(buffer, SIZE, " %3u: %08x | 0x%08X | 0x%08X | %s %s\n",
- i, int(cur), int(cur->start*kMemoryAlign),
- int(cur->size*kMemoryAlign),
- int(cur->free) ? "F" : "A",
- errs[np|pn]);
-
- result.append(buffer);
-
- if (!cur->free)
- size += cur->size*kMemoryAlign;
-
- i++;
- cur = cur->next;
- }
- snprintf(buffer, SIZE, " size allocated: %u (%u KB)\n", int(size), int(size/1024));
- result.append(buffer);
-}
-
-// ----------------------------------------------------------------------------
-
-
-SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name)
- : MemoryHeapBase(size, flags, name)
-{
-}
-
-SharedHeap::~SharedHeap()
-{
-}
-
-sp<IMemory> SharedHeap::mapMemory(size_t offset, size_t size)
-{
- return new SimpleMemory(this, offset, size);
-}
-
-
-SimpleMemory::SimpleMemory(const sp<IMemoryHeap>& heap,
- ssize_t offset, size_t size)
- : MemoryBase(heap, offset, size)
-{
-#ifndef NDEBUG
- void* const start_ptr = (void*)(intptr_t(heap->base()) + offset);
- memset(start_ptr, 0xda, size);
-#endif
-}
-
-SimpleMemory::~SimpleMemory()
-{
- size_t freedOffset = getOffset();
- size_t freedSize = getSize();
-
- // keep the size to unmap in excess
- size_t pagesize = getpagesize();
- size_t start = freedOffset;
- size_t end = start + freedSize;
- start &= ~(pagesize-1);
- end = (end + pagesize-1) & ~(pagesize-1);
-
- // give back to the kernel the pages we don't need
- size_t free_start = freedOffset;
- size_t free_end = free_start + freedSize;
- if (start < free_start)
- start = free_start;
- if (end > free_end)
- end = free_end;
- start = (start + pagesize-1) & ~(pagesize-1);
- end &= ~(pagesize-1);
-
- if (start < end) {
- void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + start);
- size_t size = end-start;
-
-#ifndef NDEBUG
- memset(start_ptr, 0xdf, size);
-#endif
-
- // MADV_REMOVE is not defined on Dapper based Goobuntu
-#ifdef MADV_REMOVE
- if (size) {
- int err = madvise(start_ptr, size, MADV_REMOVE);
- LOGW_IF(err, "madvise(%p, %u, MADV_REMOVE) returned %s",
- start_ptr, size, err<0 ? strerror(errno) : "Ok");
- }
-#endif
- }
-}
-
-}; // namespace android
diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/utils/MemoryHeapBase.cpp
deleted file mode 100644
index 8251728..0000000
--- a/libs/utils/MemoryHeapBase.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MemoryHeapBase"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-#include <cutils/ashmem.h>
-#include <cutils/atomic.h>
-
-#include <utils/MemoryHeapBase.h>
-
-#if HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-MemoryHeapBase::MemoryHeapBase()
- : mFD(-1), mSize(0), mBase(MAP_FAILED),
- mDevice(NULL), mNeedUnmap(false)
-{
-}
-
-MemoryHeapBase::MemoryHeapBase(size_t size, uint32_t flags, char const * name)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false)
-{
- const size_t pagesize = getpagesize();
- size = ((size + pagesize-1) & ~(pagesize-1));
- int fd = ashmem_create_region(name == NULL ? "MemoryHeapBase" : name, size);
- LOGE_IF(fd<0, "error creating ashmem region: %s", strerror(errno));
- if (fd >= 0) {
- if (mapfd(fd, size) == NO_ERROR) {
- if (flags & READ_ONLY) {
- ashmem_set_prot_region(fd, PROT_READ);
- }
- }
- }
-}
-
-MemoryHeapBase::MemoryHeapBase(const char* device, size_t size, uint32_t flags)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false)
-{
- int fd = open(device, O_RDWR);
- LOGE_IF(fd<0, "error opening %s: %s", device, strerror(errno));
- if (fd >= 0) {
- const size_t pagesize = getpagesize();
- size = ((size + pagesize-1) & ~(pagesize-1));
- if (mapfd(fd, size) == NO_ERROR) {
- mDevice = device;
- }
- }
-}
-
-MemoryHeapBase::MemoryHeapBase(int fd, size_t size, uint32_t flags)
- : mFD(-1), mSize(0), mBase(MAP_FAILED), mFlags(flags),
- mDevice(0), mNeedUnmap(false)
-{
- const size_t pagesize = getpagesize();
- size = ((size + pagesize-1) & ~(pagesize-1));
- mapfd(dup(fd), size);
-}
-
-status_t MemoryHeapBase::init(int fd, void *base, int size, int flags, const char* device)
-{
- if (mFD != -1) {
- return INVALID_OPERATION;
- }
- mFD = fd;
- mBase = base;
- mSize = size;
- mFlags = flags;
- mDevice = device;
- return NO_ERROR;
-}
-
-status_t MemoryHeapBase::mapfd(int fd, size_t size)
-{
- if (size == 0) {
- // try to figure out the size automatically
-#if HAVE_ANDROID_OS
- // first try the PMEM ioctl
- pmem_region reg;
- int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, &reg);
- if (err == 0)
- size = reg.len;
-#endif
- if (size == 0) { // try fstat
- struct stat sb;
- if (fstat(fd, &sb) == 0)
- size = sb.st_size;
- }
- // if it didn't work, let mmap() fail.
- }
-
- if ((mFlags & DONT_MAP_LOCALLY) == 0) {
- void* base = (uint8_t*)mmap(0, size,
- PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
- if (base == MAP_FAILED) {
- LOGE("mmap(fd=%d, size=%u) failed (%s)",
- fd, uint32_t(size), strerror(errno));
- close(fd);
- return -errno;
- }
- //LOGD("mmap(fd=%d, base=%p, size=%lu)", fd, base, size);
- mBase = base;
- mNeedUnmap = true;
- } else {
- mBase = 0; // not MAP_FAILED
- mNeedUnmap = false;
- }
- mFD = fd;
- mSize = size;
- return NO_ERROR;
-}
-
-MemoryHeapBase::~MemoryHeapBase()
-{
- dispose();
-}
-
-void MemoryHeapBase::dispose()
-{
- int fd = android_atomic_or(-1, &mFD);
- if (fd >= 0) {
- if (mNeedUnmap) {
- //LOGD("munmap(fd=%d, base=%p, size=%lu)", fd, mBase, mSize);
- munmap(mBase, mSize);
- }
- mBase = 0;
- mSize = 0;
- close(fd);
- }
-}
-
-int MemoryHeapBase::getHeapID() const {
- return mFD;
-}
-
-void* MemoryHeapBase::getBase() const {
- return mBase;
-}
-
-size_t MemoryHeapBase::getSize() const {
- return mSize;
-}
-
-uint32_t MemoryHeapBase::getFlags() const {
- return mFlags;
-}
-
-const char* MemoryHeapBase::getDevice() const {
- return mDevice;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/utils/MemoryHeapPmem.cpp
deleted file mode 100644
index eba2b30..0000000
--- a/libs/utils/MemoryHeapPmem.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "MemoryHeapPmem"
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#include <cutils/log.h>
-
-#include <utils/MemoryHeapPmem.h>
-#include <utils/MemoryHeapBase.h>
-
-#if HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-MemoryHeapPmem::MemoryPmem::MemoryPmem(const sp<MemoryHeapPmem>& heap)
- : BnMemory(), mClientHeap(heap)
-{
-}
-
-MemoryHeapPmem::MemoryPmem::~MemoryPmem() {
- if (mClientHeap != NULL) {
- mClientHeap->remove(this);
- }
-}
-
-// ---------------------------------------------------------------------------
-
-class SubRegionMemory : public MemoryHeapPmem::MemoryPmem {
-public:
- SubRegionMemory(const sp<MemoryHeapPmem>& heap, ssize_t offset, size_t size);
- virtual ~SubRegionMemory();
- virtual sp<IMemoryHeap> getMemory(ssize_t* offset, size_t* size) const;
-private:
- friend class MemoryHeapPmem;
- void revoke();
- size_t mSize;
- ssize_t mOffset;
-};
-
-SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap,
- ssize_t offset, size_t size)
- : MemoryHeapPmem::MemoryPmem(heap), mSize(size), mOffset(offset)
-{
-#ifndef NDEBUG
- void* const start_ptr = (void*)(intptr_t(getHeap()->base()) + offset);
- memset(start_ptr, 0xda, size);
-#endif
-
-#if HAVE_ANDROID_OS
- if (size > 0) {
- const size_t pagesize = getpagesize();
- size = (size + pagesize-1) & ~(pagesize-1);
- int our_fd = heap->heapID();
- struct pmem_region sub = { offset, size };
- int err = ioctl(our_fd, PMEM_MAP, &sub);
- LOGE_IF(err<0, "PMEM_MAP failed (%s), "
- "mFD=%d, sub.offset=%lu, sub.size=%lu",
- strerror(errno), our_fd, sub.offset, sub.len);
-}
-#endif
-}
-
-sp<IMemoryHeap> SubRegionMemory::getMemory(ssize_t* offset, size_t* size) const
-{
- if (offset) *offset = mOffset;
- if (size) *size = mSize;
- return getHeap();
-}
-
-SubRegionMemory::~SubRegionMemory()
-{
- revoke();
-}
-
-
-void SubRegionMemory::revoke()
-{
- // NOTE: revoke() doesn't need to be protected by a lock because it
- // can only be called from MemoryHeapPmem::revoke(), which means
- // that we can't be in ~SubRegionMemory(), or in ~SubRegionMemory(),
- // which means MemoryHeapPmem::revoke() wouldn't have been able to
- // promote() it.
-
-#if HAVE_ANDROID_OS
- if (mSize != NULL) {
- const sp<MemoryHeapPmem>& heap(getHeap());
- int our_fd = heap->heapID();
- struct pmem_region sub;
- sub.offset = mOffset;
- sub.len = mSize;
- int err = ioctl(our_fd, PMEM_UNMAP, &sub);
- LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
- "mFD=%d, sub.offset=%lu, sub.size=%lu",
- strerror(errno), our_fd, sub.offset, sub.len);
- mSize = 0;
- }
-#endif
-}
-
-// ---------------------------------------------------------------------------
-
-MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap,
- uint32_t flags)
- : HeapInterface(), MemoryHeapBase()
-{
- char const * const device = pmemHeap->getDevice();
-#if HAVE_ANDROID_OS
- if (device) {
- int fd = open(device, O_RDWR);
- LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno));
- if (fd >= 0) {
- int err = ioctl(fd, PMEM_CONNECT, pmemHeap->heapID());
- if (err < 0) {
- LOGE("PMEM_CONNECT failed (%s), mFD=%d, sub-fd=%d",
- strerror(errno), fd, pmemHeap->heapID());
- close(fd);
- } else {
- // everything went well...
- mParentHeap = pmemHeap;
- MemoryHeapBase::init(fd,
- pmemHeap->getBase(),
- pmemHeap->getSize(),
- pmemHeap->getFlags() | flags,
- device);
- }
- }
- }
-#else
- mParentHeap = pmemHeap;
- MemoryHeapBase::init(
- dup(pmemHeap->heapID()),
- pmemHeap->getBase(),
- pmemHeap->getSize(),
- pmemHeap->getFlags() | flags,
- device);
-#endif
-}
-
-MemoryHeapPmem::~MemoryHeapPmem()
-{
-}
-
-sp<IMemory> MemoryHeapPmem::mapMemory(size_t offset, size_t size)
-{
- sp<MemoryPmem> memory = createMemory(offset, size);
- if (memory != 0) {
- Mutex::Autolock _l(mLock);
- mAllocations.add(memory);
- }
- return memory;
-}
-
-sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory(
- size_t offset, size_t size)
-{
- sp<SubRegionMemory> memory;
- if (heapID() > 0)
- memory = new SubRegionMemory(this, offset, size);
- return memory;
-}
-
-status_t MemoryHeapPmem::slap()
-{
-#if HAVE_ANDROID_OS
- size_t size = getSize();
- const size_t pagesize = getpagesize();
- size = (size + pagesize-1) & ~(pagesize-1);
- int our_fd = getHeapID();
- struct pmem_region sub = { 0, size };
- int err = ioctl(our_fd, PMEM_MAP, &sub);
- LOGE_IF(err<0, "PMEM_MAP failed (%s), "
- "mFD=%d, sub.offset=%lu, sub.size=%lu",
- strerror(errno), our_fd, sub.offset, sub.len);
- return -errno;
-#else
- return NO_ERROR;
-#endif
-}
-
-status_t MemoryHeapPmem::unslap()
-{
-#if HAVE_ANDROID_OS
- size_t size = getSize();
- const size_t pagesize = getpagesize();
- size = (size + pagesize-1) & ~(pagesize-1);
- int our_fd = getHeapID();
- struct pmem_region sub = { 0, size };
- int err = ioctl(our_fd, PMEM_UNMAP, &sub);
- LOGE_IF(err<0, "PMEM_UNMAP failed (%s), "
- "mFD=%d, sub.offset=%lu, sub.size=%lu",
- strerror(errno), our_fd, sub.offset, sub.len);
- return -errno;
-#else
- return NO_ERROR;
-#endif
-}
-
-void MemoryHeapPmem::revoke()
-{
- SortedVector< wp<MemoryPmem> > allocations;
-
- { // scope for lock
- Mutex::Autolock _l(mLock);
- allocations = mAllocations;
- }
-
- ssize_t count = allocations.size();
- for (ssize_t i=0 ; i<count ; i++) {
- sp<MemoryPmem> memory(allocations[i].promote());
- if (memory != 0)
- memory->revoke();
- }
-}
-
-void MemoryHeapPmem::remove(const wp<MemoryPmem>& memory)
-{
- Mutex::Autolock _l(mLock);
- mAllocations.remove(memory);
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/libs/utils/Parcel.cpp b/libs/utils/Parcel.cpp
deleted file mode 100644
index b0e3750..0000000
--- a/libs/utils/Parcel.cpp
+++ /dev/null
@@ -1,1363 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#define LOG_TAG "Parcel"
-//#define LOG_NDEBUG 0
-
-#include <utils/Parcel.h>
-
-#include <utils/Binder.h>
-#include <utils/BpBinder.h>
-#include <utils/Debug.h>
-#include <utils/ProcessState.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/String16.h>
-#include <utils/TextOutput.h>
-#include <utils/misc.h>
-
-#include <private/utils/binder_module.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-
-#ifndef INT32_MAX
-#define INT32_MAX ((int32_t)(2147483647))
-#endif
-
-#define LOG_REFS(...)
-//#define LOG_REFS(...) LOG(LOG_DEBUG, "Parcel", __VA_ARGS__)
-
-// ---------------------------------------------------------------------------
-
-#define PAD_SIZE(s) (((s)+3)&~3)
-
-// XXX This can be made public if we want to provide
-// support for typed data.
-struct small_flat_data
-{
- uint32_t type;
- uint32_t data;
-};
-
-namespace android {
-
-void acquire_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who)
-{
- switch (obj.type) {
- case BINDER_TYPE_BINDER:
- if (obj.binder) {
- LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
- static_cast<IBinder*>(obj.cookie)->incStrong(who);
- }
- return;
- case BINDER_TYPE_WEAK_BINDER:
- if (obj.binder)
- static_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
- return;
- case BINDER_TYPE_HANDLE: {
- const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
- if (b != NULL) {
- LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get());
- b->incStrong(who);
- }
- return;
- }
- case BINDER_TYPE_WEAK_HANDLE: {
- const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
- if (b != NULL) b.get_refs()->incWeak(who);
- return;
- }
- case BINDER_TYPE_FD: {
- // intentionally blank -- nothing to do to acquire this, but we do
- // recognize it as a legitimate object type.
- return;
- }
- }
-
- LOGD("Invalid object type 0x%08lx", obj.type);
-}
-
-void release_object(const sp<ProcessState>& proc,
- const flat_binder_object& obj, const void* who)
-{
- switch (obj.type) {
- case BINDER_TYPE_BINDER:
- if (obj.binder) {
- LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
- static_cast<IBinder*>(obj.cookie)->decStrong(who);
- }
- return;
- case BINDER_TYPE_WEAK_BINDER:
- if (obj.binder)
- static_cast<RefBase::weakref_type*>(obj.binder)->decWeak(who);
- return;
- case BINDER_TYPE_HANDLE: {
- const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
- if (b != NULL) {
- LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get());
- b->decStrong(who);
- }
- return;
- }
- case BINDER_TYPE_WEAK_HANDLE: {
- const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
- if (b != NULL) b.get_refs()->decWeak(who);
- return;
- }
- case BINDER_TYPE_FD: {
- if (obj.cookie != (void*)0) close(obj.handle);
- return;
- }
- }
-
- LOGE("Invalid object type 0x%08lx", obj.type);
-}
-
-inline static status_t finish_flatten_binder(
- const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
-{
- return out->writeObject(flat, false);
-}
-
-status_t flatten_binder(const sp<ProcessState>& proc,
- const sp<IBinder>& binder, Parcel* out)
-{
- flat_binder_object obj;
-
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- if (binder != NULL) {
- IBinder *local = binder->localBinder();
- if (!local) {
- BpBinder *proxy = binder->remoteBinder();
- if (proxy == NULL) {
- LOGE("null proxy");
- }
- const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_HANDLE;
- obj.handle = handle;
- obj.cookie = NULL;
- } else {
- obj.type = BINDER_TYPE_BINDER;
- obj.binder = local->getWeakRefs();
- obj.cookie = local;
- }
- } else {
- obj.type = BINDER_TYPE_BINDER;
- obj.binder = NULL;
- obj.cookie = NULL;
- }
-
- return finish_flatten_binder(binder, obj, out);
-}
-
-status_t flatten_binder(const sp<ProcessState>& proc,
- const wp<IBinder>& binder, Parcel* out)
-{
- flat_binder_object obj;
-
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- if (binder != NULL) {
- sp<IBinder> real = binder.promote();
- if (real != NULL) {
- IBinder *local = real->localBinder();
- if (!local) {
- BpBinder *proxy = real->remoteBinder();
- if (proxy == NULL) {
- LOGE("null proxy");
- }
- const int32_t handle = proxy ? proxy->handle() : 0;
- obj.type = BINDER_TYPE_WEAK_HANDLE;
- obj.handle = handle;
- obj.cookie = NULL;
- } else {
- obj.type = BINDER_TYPE_WEAK_BINDER;
- obj.binder = binder.get_refs();
- obj.cookie = binder.unsafe_get();
- }
- return finish_flatten_binder(real, obj, out);
- }
-
- // XXX How to deal? In order to flatten the given binder,
- // we need to probe it for information, which requires a primary
- // reference... but we don't have one.
- //
- // The OpenBinder implementation uses a dynamic_cast<> here,
- // but we can't do that with the different reference counting
- // implementation we are using.
- LOGE("Unable to unflatten Binder weak reference!");
- obj.type = BINDER_TYPE_BINDER;
- obj.binder = NULL;
- obj.cookie = NULL;
- return finish_flatten_binder(NULL, obj, out);
-
- } else {
- obj.type = BINDER_TYPE_BINDER;
- obj.binder = NULL;
- obj.cookie = NULL;
- return finish_flatten_binder(NULL, obj, out);
- }
-}
-
-inline static status_t finish_unflatten_binder(
- BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
-{
- return NO_ERROR;
-}
-
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const Parcel& in, sp<IBinder>* out)
-{
- const flat_binder_object* flat = in.readObject(false);
-
- if (flat) {
- switch (flat->type) {
- case BINDER_TYPE_BINDER:
- *out = static_cast<IBinder*>(flat->cookie);
- return finish_unflatten_binder(NULL, *flat, in);
- case BINDER_TYPE_HANDLE:
- *out = proc->getStrongProxyForHandle(flat->handle);
- return finish_unflatten_binder(
- static_cast<BpBinder*>(out->get()), *flat, in);
- }
- }
- return BAD_TYPE;
-}
-
-status_t unflatten_binder(const sp<ProcessState>& proc,
- const Parcel& in, wp<IBinder>* out)
-{
- const flat_binder_object* flat = in.readObject(false);
-
- if (flat) {
- switch (flat->type) {
- case BINDER_TYPE_BINDER:
- *out = static_cast<IBinder*>(flat->cookie);
- return finish_unflatten_binder(NULL, *flat, in);
- case BINDER_TYPE_WEAK_BINDER:
- if (flat->binder != NULL) {
- out->set_object_and_refs(
- static_cast<IBinder*>(flat->cookie),
- static_cast<RefBase::weakref_type*>(flat->binder));
- } else {
- *out = NULL;
- }
- return finish_unflatten_binder(NULL, *flat, in);
- case BINDER_TYPE_HANDLE:
- case BINDER_TYPE_WEAK_HANDLE:
- *out = proc->getWeakProxyForHandle(flat->handle);
- return finish_unflatten_binder(
- static_cast<BpBinder*>(out->unsafe_get()), *flat, in);
- }
- }
- return BAD_TYPE;
-}
-
-// ---------------------------------------------------------------------------
-
-Parcel::Parcel()
-{
- initState();
-}
-
-Parcel::~Parcel()
-{
- freeDataNoInit();
-}
-
-const uint8_t* Parcel::data() const
-{
- return mData;
-}
-
-size_t Parcel::dataSize() const
-{
- return (mDataSize > mDataPos ? mDataSize : mDataPos);
-}
-
-size_t Parcel::dataAvail() const
-{
- // TODO: decide what to do about the possibility that this can
- // report an available-data size that exceeds a Java int's max
- // positive value, causing havoc. Fortunately this will only
- // happen if someone constructs a Parcel containing more than two
- // gigabytes of data, which on typical phone hardware is simply
- // not possible.
- return dataSize() - dataPosition();
-}
-
-size_t Parcel::dataPosition() const
-{
- return mDataPos;
-}
-
-size_t Parcel::dataCapacity() const
-{
- return mDataCapacity;
-}
-
-status_t Parcel::setDataSize(size_t size)
-{
- status_t err;
- err = continueWrite(size);
- if (err == NO_ERROR) {
- mDataSize = size;
- LOGV("setDataSize Setting data size of %p to %d\n", this, mDataSize);
- }
- return err;
-}
-
-void Parcel::setDataPosition(size_t pos) const
-{
- mDataPos = pos;
- mNextObjectHint = 0;
-}
-
-status_t Parcel::setDataCapacity(size_t size)
-{
- if (size > mDataSize) return continueWrite(size);
- return NO_ERROR;
-}
-
-status_t Parcel::setData(const uint8_t* buffer, size_t len)
-{
- status_t err = restartWrite(len);
- if (err == NO_ERROR) {
- memcpy(const_cast<uint8_t*>(data()), buffer, len);
- mDataSize = len;
- mFdsKnown = false;
- }
- return err;
-}
-
-status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
-{
- const sp<ProcessState> proc(ProcessState::self());
- status_t err;
- uint8_t *data = parcel->mData;
- size_t *objects = parcel->mObjects;
- size_t size = parcel->mObjectsSize;
- int startPos = mDataPos;
- int firstIndex = -1, lastIndex = -2;
-
- if (len == 0) {
- return NO_ERROR;
- }
-
- // range checks against the source parcel size
- if ((offset > parcel->mDataSize)
- || (len > parcel->mDataSize)
- || (offset + len > parcel->mDataSize)) {
- return BAD_VALUE;
- }
-
- // Count objects in range
- for (int i = 0; i < (int) size; i++) {
- size_t off = objects[i];
- if ((off >= offset) && (off < offset + len)) {
- if (firstIndex == -1) {
- firstIndex = i;
- }
- lastIndex = i;
- }
- }
- int numObjects = lastIndex - firstIndex + 1;
-
- // grow data
- err = growData(len);
- if (err != NO_ERROR) {
- return err;
- }
-
- // append data
- memcpy(mData + mDataPos, data + offset, len);
- mDataPos += len;
- mDataSize += len;
-
- if (numObjects > 0) {
- // grow objects
- if (mObjectsCapacity < mObjectsSize + numObjects) {
- int newSize = ((mObjectsSize + numObjects)*3)/2;
- size_t *objects =
- (size_t*)realloc(mObjects, newSize*sizeof(size_t));
- if (objects == (size_t*)0) {
- return NO_MEMORY;
- }
- mObjects = objects;
- mObjectsCapacity = newSize;
- }
-
- // append and acquire objects
- int idx = mObjectsSize;
- for (int i = firstIndex; i <= lastIndex; i++) {
- size_t off = objects[i] - offset + startPos;
- mObjects[idx++] = off;
- mObjectsSize++;
-
- flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData + off);
- acquire_object(proc, *flat, this);
-
- if (flat->type == BINDER_TYPE_FD) {
- // If this is a file descriptor, we need to dup it so the
- // new Parcel now owns its own fd, and can declare that we
- // officially know we have fds.
- flat->handle = dup(flat->handle);
- flat->cookie = (void*)1;
- mHasFds = mFdsKnown = true;
- }
- }
- }
-
- return NO_ERROR;
-}
-
-bool Parcel::hasFileDescriptors() const
-{
- if (!mFdsKnown) {
- scanForFds();
- }
- return mHasFds;
-}
-
-status_t Parcel::writeInterfaceToken(const String16& interface)
-{
- // currently the interface identification token is just its name as a string
- return writeString16(interface);
-}
-
-bool Parcel::enforceInterface(const String16& interface) const
-{
- String16 str = readString16();
- if (str == interface) {
- return true;
- } else {
- LOGW("**** enforceInterface() expected '%s' but read '%s'\n",
- String8(interface).string(), String8(str).string());
- return false;
- }
-}
-
-const size_t* Parcel::objects() const
-{
- return mObjects;
-}
-
-size_t Parcel::objectsCount() const
-{
- return mObjectsSize;
-}
-
-status_t Parcel::errorCheck() const
-{
- return mError;
-}
-
-void Parcel::setError(status_t err)
-{
- mError = err;
-}
-
-status_t Parcel::finishWrite(size_t len)
-{
- //printf("Finish write of %d\n", len);
- mDataPos += len;
- LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
- if (mDataPos > mDataSize) {
- mDataSize = mDataPos;
- LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
- }
- //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
- return NO_ERROR;
-}
-
-status_t Parcel::writeUnpadded(const void* data, size_t len)
-{
- size_t end = mDataPos + len;
- if (end < mDataPos) {
- // integer overflow
- return BAD_VALUE;
- }
-
- if (end <= mDataCapacity) {
-restart_write:
- memcpy(mData+mDataPos, data, len);
- return finishWrite(len);
- }
-
- status_t err = growData(len);
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
-status_t Parcel::write(const void* data, size_t len)
-{
- void* const d = writeInplace(len);
- if (d) {
- memcpy(d, data, len);
- return NO_ERROR;
- }
- return mError;
-}
-
-void* Parcel::writeInplace(size_t len)
-{
- const size_t padded = PAD_SIZE(len);
-
- // sanity check for integer overflow
- if (mDataPos+padded < mDataPos) {
- return NULL;
- }
-
- if ((mDataPos+padded) <= mDataCapacity) {
-restart_write:
- //printf("Writing %ld bytes, padded to %ld\n", len, padded);
- uint8_t* const data = mData+mDataPos;
-
- // Need to pad at end?
- if (padded != len) {
-#if BYTE_ORDER == BIG_ENDIAN
- static const uint32_t mask[4] = {
- 0x00000000, 0xffffff00, 0xffff0000, 0xff000000
- };
-#endif
-#if BYTE_ORDER == LITTLE_ENDIAN
- static const uint32_t mask[4] = {
- 0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
- };
-#endif
- //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
- // *reinterpret_cast<void**>(data+padded-4));
- *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
- }
-
- finishWrite(padded);
- return data;
- }
-
- status_t err = growData(padded);
- if (err == NO_ERROR) goto restart_write;
- return NULL;
-}
-
-status_t Parcel::writeInt32(int32_t val)
-{
- if ((mDataPos+sizeof(val)) <= mDataCapacity) {
-restart_write:
- *reinterpret_cast<int32_t*>(mData+mDataPos) = val;
- return finishWrite(sizeof(val));
- }
-
- status_t err = growData(sizeof(val));
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
-status_t Parcel::writeInt64(int64_t val)
-{
- if ((mDataPos+sizeof(val)) <= mDataCapacity) {
-restart_write:
- *reinterpret_cast<int64_t*>(mData+mDataPos) = val;
- return finishWrite(sizeof(val));
- }
-
- status_t err = growData(sizeof(val));
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
-status_t Parcel::writeFloat(float val)
-{
- if ((mDataPos+sizeof(val)) <= mDataCapacity) {
-restart_write:
- *reinterpret_cast<float*>(mData+mDataPos) = val;
- return finishWrite(sizeof(val));
- }
-
- status_t err = growData(sizeof(val));
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
-status_t Parcel::writeDouble(double val)
-{
- if ((mDataPos+sizeof(val)) <= mDataCapacity) {
-restart_write:
- *reinterpret_cast<double*>(mData+mDataPos) = val;
- return finishWrite(sizeof(val));
- }
-
- status_t err = growData(sizeof(val));
- if (err == NO_ERROR) goto restart_write;
- return err;
-}
-
-status_t Parcel::writeCString(const char* str)
-{
- return write(str, strlen(str)+1);
-}
-
-status_t Parcel::writeString8(const String8& str)
-{
- status_t err = writeInt32(str.bytes());
- if (err == NO_ERROR) {
- err = write(str.string(), str.bytes()+1);
- }
- return err;
-}
-
-status_t Parcel::writeString16(const String16& str)
-{
- return writeString16(str.string(), str.size());
-}
-
-status_t Parcel::writeString16(const char16_t* str, size_t len)
-{
- if (str == NULL) return writeInt32(-1);
-
- status_t err = writeInt32(len);
- if (err == NO_ERROR) {
- len *= sizeof(char16_t);
- uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
- if (data) {
- memcpy(data, str, len);
- *reinterpret_cast<char16_t*>(data+len) = 0;
- return NO_ERROR;
- }
- err = mError;
- }
- return err;
-}
-
-status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
-{
- return flatten_binder(ProcessState::self(), val, this);
-}
-
-status_t Parcel::writeWeakBinder(const wp<IBinder>& val)
-{
- return flatten_binder(ProcessState::self(), val, this);
-}
-
-status_t Parcel::writeNativeHandle(const native_handle* handle)
-{
- if (handle->version != sizeof(native_handle))
- return BAD_TYPE;
-
- status_t err;
- err = writeInt32(handle->numFds);
- if (err != NO_ERROR) return err;
-
- err = writeInt32(handle->numInts);
- if (err != NO_ERROR) return err;
-
- for (int i=0 ; err==NO_ERROR && i<handle->numFds ; i++)
- err = writeDupFileDescriptor(handle->data[i]);
-
- if (err != NO_ERROR) {
- LOGD("write native handle, write dup fd failed");
- return err;
- }
- err = write(handle->data + handle->numFds, sizeof(int)*handle->numInts);
- return err;
-}
-
-status_t Parcel::writeFileDescriptor(int fd)
-{
- flat_binder_object obj;
- obj.type = BINDER_TYPE_FD;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj.handle = fd;
- obj.cookie = (void*)0;
- return writeObject(obj, true);
-}
-
-status_t Parcel::writeDupFileDescriptor(int fd)
-{
- flat_binder_object obj;
- obj.type = BINDER_TYPE_FD;
- obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
- obj.handle = dup(fd);
- obj.cookie = (void*)1;
- return writeObject(obj, true);
-}
-
-status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
-{
- const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
- const bool enoughObjects = mObjectsSize < mObjectsCapacity;
- if (enoughData && enoughObjects) {
-restart_write:
- *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
-
- // Need to write meta-data?
- if (nullMetaData || val.binder != NULL) {
- mObjects[mObjectsSize] = mDataPos;
- acquire_object(ProcessState::self(), val, this);
- mObjectsSize++;
- }
-
- // remember if it's a file descriptor
- if (val.type == BINDER_TYPE_FD) {
- mHasFds = mFdsKnown = true;
- }
-
- return finishWrite(sizeof(flat_binder_object));
- }
-
- if (!enoughData) {
- const status_t err = growData(sizeof(val));
- if (err != NO_ERROR) return err;
- }
- if (!enoughObjects) {
- size_t newSize = ((mObjectsSize+2)*3)/2;
- size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));
- if (objects == NULL) return NO_MEMORY;
- mObjects = objects;
- mObjectsCapacity = newSize;
- }
-
- goto restart_write;
-}
-
-
-void Parcel::remove(size_t start, size_t amt)
-{
- LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
-}
-
-status_t Parcel::read(void* outData, size_t len) const
-{
- if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
- memcpy(outData, mData+mDataPos, len);
- mDataPos += PAD_SIZE(len);
- LOGV("read Setting data pos of %p to %d\n", this, mDataPos);
- return NO_ERROR;
- }
- return NOT_ENOUGH_DATA;
-}
-
-const void* Parcel::readInplace(size_t len) const
-{
- if ((mDataPos+PAD_SIZE(len)) >= mDataPos && (mDataPos+PAD_SIZE(len)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += PAD_SIZE(len);
- LOGV("readInplace Setting data pos of %p to %d\n", this, mDataPos);
- return data;
- }
- return NULL;
-}
-
-status_t Parcel::readInt32(int32_t *pArg) const
-{
- if ((mDataPos+sizeof(int32_t)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(int32_t);
- *pArg = *reinterpret_cast<const int32_t*>(data);
- return NO_ERROR;
- } else {
- return NOT_ENOUGH_DATA;
- }
-}
-
-int32_t Parcel::readInt32() const
-{
- if ((mDataPos+sizeof(int32_t)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(int32_t);
- LOGV("readInt32 Setting data pos of %p to %d\n", this, mDataPos);
- return *reinterpret_cast<const int32_t*>(data);
- }
- return 0;
-}
-
-
-status_t Parcel::readInt64(int64_t *pArg) const
-{
- if ((mDataPos+sizeof(int64_t)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(int64_t);
- *pArg = *reinterpret_cast<const int64_t*>(data);
- LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos);
- return NO_ERROR;
- } else {
- return NOT_ENOUGH_DATA;
- }
-}
-
-
-int64_t Parcel::readInt64() const
-{
- if ((mDataPos+sizeof(int64_t)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(int64_t);
- LOGV("readInt64 Setting data pos of %p to %d\n", this, mDataPos);
- return *reinterpret_cast<const int64_t*>(data);
- }
- return 0;
-}
-
-status_t Parcel::readFloat(float *pArg) const
-{
- if ((mDataPos+sizeof(float)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(float);
- LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos);
- *pArg = *reinterpret_cast<const float*>(data);
- return NO_ERROR;
- } else {
- return NOT_ENOUGH_DATA;
- }
-}
-
-
-float Parcel::readFloat() const
-{
- if ((mDataPos+sizeof(float)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(float);
- LOGV("readFloat Setting data pos of %p to %d\n", this, mDataPos);
- return *reinterpret_cast<const float*>(data);
- }
- return 0;
-}
-
-status_t Parcel::readDouble(double *pArg) const
-{
- if ((mDataPos+sizeof(double)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(double);
- LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos);
- *pArg = *reinterpret_cast<const double*>(data);
- return NO_ERROR;
- } else {
- return NOT_ENOUGH_DATA;
- }
-}
-
-
-double Parcel::readDouble() const
-{
- if ((mDataPos+sizeof(double)) <= mDataSize) {
- const void* data = mData+mDataPos;
- mDataPos += sizeof(double);
- LOGV("readDouble Setting data pos of %p to %d\n", this, mDataPos);
- return *reinterpret_cast<const double*>(data);
- }
- return 0;
-}
-
-
-const char* Parcel::readCString() const
-{
- const size_t avail = mDataSize-mDataPos;
- if (avail > 0) {
- const char* str = reinterpret_cast<const char*>(mData+mDataPos);
- // is the string's trailing NUL within the parcel's valid bounds?
- const char* eos = reinterpret_cast<const char*>(memchr(str, 0, avail));
- if (eos) {
- const size_t len = eos - str;
- mDataPos += PAD_SIZE(len+1);
- LOGV("readCString Setting data pos of %p to %d\n", this, mDataPos);
- return str;
- }
- }
- return NULL;
-}
-
-String8 Parcel::readString8() const
-{
- int32_t size = readInt32();
- // watch for potential int overflow adding 1 for trailing NUL
- if (size > 0 && size < INT32_MAX) {
- const char* str = (const char*)readInplace(size+1);
- if (str) return String8(str, size);
- }
- return String8();
-}
-
-String16 Parcel::readString16() const
-{
- size_t len;
- const char16_t* str = readString16Inplace(&len);
- if (str) return String16(str, len);
- LOGE("Reading a NULL string not supported here.");
- return String16();
-}
-
-const char16_t* Parcel::readString16Inplace(size_t* outLen) const
-{
- int32_t size = readInt32();
- // watch for potential int overflow from size+1
- if (size >= 0 && size < INT32_MAX) {
- *outLen = size;
- const char16_t* str = (const char16_t*)readInplace((size+1)*sizeof(char16_t));
- if (str != NULL) {
- return str;
- }
- }
- *outLen = 0;
- return NULL;
-}
-
-sp<IBinder> Parcel::readStrongBinder() const
-{
- sp<IBinder> val;
- unflatten_binder(ProcessState::self(), *this, &val);
- return val;
-}
-
-wp<IBinder> Parcel::readWeakBinder() const
-{
- wp<IBinder> val;
- unflatten_binder(ProcessState::self(), *this, &val);
- return val;
-}
-
-
-native_handle* Parcel::readNativeHandle() const
-{
- int numFds, numInts;
- status_t err;
- err = readInt32(&numFds);
- if (err != NO_ERROR) return 0;
- err = readInt32(&numInts);
- if (err != NO_ERROR) return 0;
-
- native_handle* h = native_handle_create(numFds, numInts);
- for (int i=0 ; err==NO_ERROR && i<numFds ; i++) {
- h->data[i] = dup(readFileDescriptor());
- if (h->data[i] < 0) err = BAD_VALUE;
- }
- err = read(h->data + numFds, sizeof(int)*numInts);
- if (err != NO_ERROR) {
- native_handle_close(h);
- native_handle_delete(h);
- h = 0;
- }
- return h;
-}
-
-
-int Parcel::readFileDescriptor() const
-{
- const flat_binder_object* flat = readObject(true);
- if (flat) {
- switch (flat->type) {
- case BINDER_TYPE_FD:
- //LOGI("Returning file descriptor %ld from parcel %p\n", flat->handle, this);
- return flat->handle;
- }
- }
- return BAD_TYPE;
-}
-
-const flat_binder_object* Parcel::readObject(bool nullMetaData) const
-{
- const size_t DPOS = mDataPos;
- if ((DPOS+sizeof(flat_binder_object)) <= mDataSize) {
- const flat_binder_object* obj
- = reinterpret_cast<const flat_binder_object*>(mData+DPOS);
- mDataPos = DPOS + sizeof(flat_binder_object);
- if (!nullMetaData && (obj->cookie == NULL && obj->binder == NULL)) {
- // When transferring a NULL object, we don't write it into
- // the object list, so we don't want to check for it when
- // reading.
- LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
- return obj;
- }
-
- // Ensure that this object is valid...
- size_t* const OBJS = mObjects;
- const size_t N = mObjectsSize;
- size_t opos = mNextObjectHint;
-
- if (N > 0) {
- LOGV("Parcel %p looking for obj at %d, hint=%d\n",
- this, DPOS, opos);
-
- // Start at the current hint position, looking for an object at
- // the current data position.
- if (opos < N) {
- while (opos < (N-1) && OBJS[opos] < DPOS) {
- opos++;
- }
- } else {
- opos = N-1;
- }
- if (OBJS[opos] == DPOS) {
- // Found it!
- LOGV("Parcel found obj %d at index %d with forward search",
- this, DPOS, opos);
- mNextObjectHint = opos+1;
- LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
- return obj;
- }
-
- // Look backwards for it...
- while (opos > 0 && OBJS[opos] > DPOS) {
- opos--;
- }
- if (OBJS[opos] == DPOS) {
- // Found it!
- LOGV("Parcel found obj %d at index %d with backward search",
- this, DPOS, opos);
- mNextObjectHint = opos+1;
- LOGV("readObject Setting data pos of %p to %d\n", this, mDataPos);
- return obj;
- }
- }
- LOGW("Attempt to read object from Parcel %p at offset %d that is not in the object list",
- this, DPOS);
- }
- return NULL;
-}
-
-void Parcel::closeFileDescriptors()
-{
- size_t i = mObjectsSize;
- if (i > 0) {
- //LOGI("Closing file descriptors for %d objects...", mObjectsSize);
- }
- while (i > 0) {
- i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
- //LOGI("Closing fd: %ld\n", flat->handle);
- close(flat->handle);
- }
- }
-}
-
-const uint8_t* Parcel::ipcData() const
-{
- return mData;
-}
-
-size_t Parcel::ipcDataSize() const
-{
- return (mDataSize > mDataPos ? mDataSize : mDataPos);
-}
-
-const size_t* Parcel::ipcObjects() const
-{
- return mObjects;
-}
-
-size_t Parcel::ipcObjectsCount() const
-{
- return mObjectsSize;
-}
-
-void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
- const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
-{
- freeDataNoInit();
- mError = NO_ERROR;
- mData = const_cast<uint8_t*>(data);
- mDataSize = mDataCapacity = dataSize;
- //LOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid());
- mDataPos = 0;
- LOGV("setDataReference Setting data pos of %p to %d\n", this, mDataPos);
- mObjects = const_cast<size_t*>(objects);
- mObjectsSize = mObjectsCapacity = objectsCount;
- mNextObjectHint = 0;
- mOwner = relFunc;
- mOwnerCookie = relCookie;
- scanForFds();
-}
-
-void Parcel::print(TextOutput& to, uint32_t flags) const
-{
- to << "Parcel(";
-
- if (errorCheck() != NO_ERROR) {
- const status_t err = errorCheck();
- to << "Error: " << (void*)err << " \"" << strerror(-err) << "\"";
- } else if (dataSize() > 0) {
- const uint8_t* DATA = data();
- to << indent << HexDump(DATA, dataSize()) << dedent;
- const size_t* OBJS = objects();
- const size_t N = objectsCount();
- for (size_t i=0; i<N; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<const flat_binder_object*>(DATA+OBJS[i]);
- to << endl << "Object #" << i << " @ " << (void*)OBJS[i] << ": "
- << TypeCode(flat->type & 0x7f7f7f00)
- << " = " << flat->binder;
- }
- } else {
- to << "NULL";
- }
-
- to << ")";
-}
-
-void Parcel::releaseObjects()
-{
- const sp<ProcessState> proc(ProcessState::self());
- size_t i = mObjectsSize;
- uint8_t* const data = mData;
- size_t* const objects = mObjects;
- while (i > 0) {
- i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(data+objects[i]);
- release_object(proc, *flat, this);
- }
-}
-
-void Parcel::acquireObjects()
-{
- const sp<ProcessState> proc(ProcessState::self());
- size_t i = mObjectsSize;
- uint8_t* const data = mData;
- size_t* const objects = mObjects;
- while (i > 0) {
- i--;
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(data+objects[i]);
- acquire_object(proc, *flat, this);
- }
-}
-
-void Parcel::freeData()
-{
- freeDataNoInit();
- initState();
-}
-
-void Parcel::freeDataNoInit()
-{
- if (mOwner) {
- //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
- } else {
- releaseObjects();
- if (mData) free(mData);
- if (mObjects) free(mObjects);
- }
-}
-
-status_t Parcel::growData(size_t len)
-{
- size_t newSize = ((mDataSize+len)*3)/2;
- return (newSize <= mDataSize)
- ? (status_t) NO_MEMORY
- : continueWrite(newSize);
-}
-
-status_t Parcel::restartWrite(size_t desired)
-{
- if (mOwner) {
- freeData();
- return continueWrite(desired);
- }
-
- uint8_t* data = (uint8_t*)realloc(mData, desired);
- if (!data && desired > mDataCapacity) {
- mError = NO_MEMORY;
- return NO_MEMORY;
- }
-
- releaseObjects();
-
- if (data) {
- mData = data;
- mDataCapacity = desired;
- }
-
- mDataSize = mDataPos = 0;
- LOGV("restartWrite Setting data size of %p to %d\n", this, mDataSize);
- LOGV("restartWrite Setting data pos of %p to %d\n", this, mDataPos);
-
- free(mObjects);
- mObjects = NULL;
- mObjectsSize = mObjectsCapacity = 0;
- mNextObjectHint = 0;
- mHasFds = false;
- mFdsKnown = true;
-
- return NO_ERROR;
-}
-
-status_t Parcel::continueWrite(size_t desired)
-{
- // If shrinking, first adjust for any objects that appear
- // after the new data size.
- size_t objectsSize = mObjectsSize;
- if (desired < mDataSize) {
- if (desired == 0) {
- objectsSize = 0;
- } else {
- while (objectsSize > 0) {
- if (mObjects[objectsSize-1] < desired)
- break;
- objectsSize--;
- }
- }
- }
-
- if (mOwner) {
- // If the size is going to zero, just release the owner's data.
- if (desired == 0) {
- freeData();
- return NO_ERROR;
- }
-
- // If there is a different owner, we need to take
- // posession.
- uint8_t* data = (uint8_t*)malloc(desired);
- if (!data) {
- mError = NO_MEMORY;
- return NO_MEMORY;
- }
- size_t* objects = NULL;
-
- if (objectsSize) {
- objects = (size_t*)malloc(objectsSize*sizeof(size_t));
- if (!objects) {
- mError = NO_MEMORY;
- return NO_MEMORY;
- }
-
- // Little hack to only acquire references on objects
- // we will be keeping.
- size_t oldObjectsSize = mObjectsSize;
- mObjectsSize = objectsSize;
- acquireObjects();
- mObjectsSize = oldObjectsSize;
- }
-
- if (mData) {
- memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
- }
- if (objects && mObjects) {
- memcpy(objects, mObjects, objectsSize*sizeof(size_t));
- }
- //LOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
- mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
- mOwner = NULL;
-
- mData = data;
- mObjects = objects;
- mDataSize = (mDataSize < desired) ? mDataSize : desired;
- LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
- mDataCapacity = desired;
- mObjectsSize = mObjectsCapacity = objectsSize;
- mNextObjectHint = 0;
-
- } else if (mData) {
- if (objectsSize < mObjectsSize) {
- // Need to release refs on any objects we are dropping.
- const sp<ProcessState> proc(ProcessState::self());
- for (size_t i=objectsSize; i<mObjectsSize; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
- // will need to rescan because we may have lopped off the only FDs
- mFdsKnown = false;
- }
- release_object(proc, *flat, this);
- }
- size_t* objects =
- (size_t*)realloc(mObjects, objectsSize*sizeof(size_t));
- if (objects) {
- mObjects = objects;
- }
- mObjectsSize = objectsSize;
- mNextObjectHint = 0;
- }
-
- // We own the data, so we can just do a realloc().
- if (desired > mDataCapacity) {
- uint8_t* data = (uint8_t*)realloc(mData, desired);
- if (data) {
- mData = data;
- mDataCapacity = desired;
- } else if (desired > mDataCapacity) {
- mError = NO_MEMORY;
- return NO_MEMORY;
- }
- } else {
- mDataSize = desired;
- LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
- if (mDataPos > desired) {
- mDataPos = desired;
- LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
- }
- }
-
- } else {
- // This is the first data. Easy!
- uint8_t* data = (uint8_t*)malloc(desired);
- if (!data) {
- mError = NO_MEMORY;
- return NO_MEMORY;
- }
-
- if(!(mDataCapacity == 0 && mObjects == NULL
- && mObjectsCapacity == 0)) {
- LOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
- }
-
- mData = data;
- mDataSize = mDataPos = 0;
- LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
- LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
- mDataCapacity = desired;
- }
-
- return NO_ERROR;
-}
-
-void Parcel::initState()
-{
- mError = NO_ERROR;
- mData = 0;
- mDataSize = 0;
- mDataCapacity = 0;
- mDataPos = 0;
- LOGV("initState Setting data size of %p to %d\n", this, mDataSize);
- LOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
- mObjects = NULL;
- mObjectsSize = 0;
- mObjectsCapacity = 0;
- mNextObjectHint = 0;
- mHasFds = false;
- mFdsKnown = true;
- mOwner = NULL;
-}
-
-void Parcel::scanForFds() const
-{
- bool hasFds = false;
- for (size_t i=0; i<mObjectsSize; i++) {
- const flat_binder_object* flat
- = reinterpret_cast<const flat_binder_object*>(mData + mObjects[i]);
- if (flat->type == BINDER_TYPE_FD) {
- hasFds = true;
- break;
- }
- }
- mHasFds = hasFds;
- mFdsKnown = true;
-}
-
-}; // namespace android
diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp
deleted file mode 100644
index 613906b..0000000
--- a/libs/utils/Pipe.cpp
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Unidirectional pipe.
-//
-
-#include <utils/Pipe.h>
-#include <utils/Log.h>
-
-#if defined(HAVE_WIN32_IPC)
-# include <windows.h>
-#else
-# include <fcntl.h>
-# include <unistd.h>
-# include <errno.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-
-using namespace android;
-
-const unsigned long kInvalidHandle = (unsigned long) -1;
-
-
-/*
- * Constructor. Do little.
- */
-Pipe::Pipe(void)
- : mReadNonBlocking(false), mReadHandle(kInvalidHandle),
- mWriteHandle(kInvalidHandle)
-{
-}
-
-/*
- * Destructor. Use the system-appropriate close call.
- */
-Pipe::~Pipe(void)
-{
-#if defined(HAVE_WIN32_IPC)
- if (mReadHandle != kInvalidHandle) {
- if (!CloseHandle((HANDLE)mReadHandle))
- LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n",
- mReadHandle);
- }
- if (mWriteHandle != kInvalidHandle) {
- FlushFileBuffers((HANDLE)mWriteHandle);
- if (!CloseHandle((HANDLE)mWriteHandle))
- LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n",
- mWriteHandle);
- }
-#else
- if (mReadHandle != kInvalidHandle) {
- if (close((int) mReadHandle) != 0)
- LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n",
- (int) mReadHandle);
- }
- if (mWriteHandle != kInvalidHandle) {
- if (close((int) mWriteHandle) != 0)
- LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n",
- (int) mWriteHandle);
- }
-#endif
-}
-
-/*
- * Create the pipe.
- *
- * Use the POSIX stuff for everything but Windows.
- */
-bool Pipe::create(void)
-{
- assert(mReadHandle == kInvalidHandle);
- assert(mWriteHandle == kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- /* we use this across processes, so they need to be inheritable */
- HANDLE handles[2];
- SECURITY_ATTRIBUTES saAttr;
-
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
- saAttr.bInheritHandle = TRUE;
- saAttr.lpSecurityDescriptor = NULL;
-
- if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) {
- LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
- return false;
- }
- mReadHandle = (unsigned long) handles[0];
- mWriteHandle = (unsigned long) handles[1];
- return true;
-#else
- int fds[2];
-
- if (pipe(fds) != 0) {
- LOG(LOG_ERROR, "pipe", "unable to create pipe\n");
- return false;
- }
- mReadHandle = fds[0];
- mWriteHandle = fds[1];
- return true;
-#endif
-}
-
-/*
- * Create a "half pipe". Please, no Segway riding.
- */
-bool Pipe::createReader(unsigned long handle)
-{
- mReadHandle = handle;
- assert(mWriteHandle == kInvalidHandle);
- return true;
-}
-
-/*
- * Create a "half pipe" for writing.
- */
-bool Pipe::createWriter(unsigned long handle)
-{
- mWriteHandle = handle;
- assert(mReadHandle == kInvalidHandle);
- return true;
-}
-
-/*
- * Return "true" if create() has been called successfully.
- */
-bool Pipe::isCreated(void)
-{
- // one or the other should be open
- return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle);
-}
-
-
-/*
- * Read data from the pipe.
- *
- * For Linux and Darwin, just call read(). For Windows, implement
- * non-blocking reads by calling PeekNamedPipe first.
- */
-int Pipe::read(void* buf, int count)
-{
- assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- DWORD totalBytesAvail = count;
- DWORD bytesRead;
-
- if (mReadNonBlocking) {
- // use PeekNamedPipe to adjust read count expectations
- if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
- &totalBytesAvail, NULL))
- {
- LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
- return -1;
- }
-
- if (totalBytesAvail == 0)
- return 0;
- }
-
- if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead,
- NULL))
- {
- DWORD err = GetLastError();
- if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE)
- return 0;
- LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err);
- return -1;
- }
-
- return (int) bytesRead;
-#else
- int cc;
- cc = ::read(mReadHandle, buf, count);
- if (cc < 0 && errno == EAGAIN)
- return 0;
- return cc;
-#endif
-}
-
-/*
- * Write data to the pipe.
- *
- * POSIX systems are trivial, Windows uses a different call and doesn't
- * handle non-blocking writes.
- *
- * If we add non-blocking support here, we probably want to make it an
- * all-or-nothing write.
- *
- * DO NOT use LOG() here, we could be writing a log message.
- */
-int Pipe::write(const void* buf, int count)
-{
- assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- DWORD bytesWritten;
-
- if (mWriteNonBlocking) {
- // BUG: can't use PeekNamedPipe() to get the amount of space
- // left. Looks like we need to use "overlapped I/O" functions.
- // I just don't care that much.
- }
-
- if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) {
- // can't LOG, use stderr
- fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError());
- return -1;
- }
-
- return (int) bytesWritten;
-#else
- int cc;
- cc = ::write(mWriteHandle, buf, count);
- if (cc < 0 && errno == EAGAIN)
- return 0;
- return cc;
-#endif
-}
-
-/*
- * Figure out if there is data available on the read fd.
- *
- * We return "true" on error because we want the caller to try to read
- * from the pipe. They'll notice the read failure and do something
- * appropriate.
- */
-bool Pipe::readReady(void)
-{
- assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- DWORD totalBytesAvail;
-
- if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL,
- &totalBytesAvail, NULL))
- {
- LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n");
- return true;
- }
-
- return (totalBytesAvail != 0);
-#else
- errno = 0;
- fd_set readfds;
- struct timeval tv = { 0, 0 };
- int cc;
-
- FD_ZERO(&readfds);
- FD_SET(mReadHandle, &readfds);
-
- cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv);
- if (cc < 0) {
- LOG(LOG_ERROR, "pipe", "select() failed\n");
- return true;
- } else if (cc == 0) {
- /* timed out, nothing available */
- return false;
- } else if (cc == 1) {
- /* our fd is ready */
- return true;
- } else {
- LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n");
- return true;
- }
-#endif
-}
-
-/*
- * Enable or disable non-blocking mode for the read descriptor.
- *
- * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to
- * actually be in non-blocking mode. If this matters -- i.e. you're not
- * using a select() call -- put a call to readReady() in front of the
- * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for
- * Darwin.
- */
-bool Pipe::setReadNonBlocking(bool val)
-{
- assert(mReadHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- // nothing to do
-#else
- int flags;
-
- if (fcntl(mReadHandle, F_GETFL, &flags) == -1) {
- LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n");
- return false;
- }
- if (val)
- flags |= O_NONBLOCK;
- else
- flags &= ~(O_NONBLOCK);
- if (fcntl(mReadHandle, F_SETFL, &flags) == -1) {
- LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n");
- return false;
- }
-#endif
-
- mReadNonBlocking = val;
- return true;
-}
-
-/*
- * Enable or disable non-blocking mode for the write descriptor.
- *
- * As with setReadNonBlocking(), this does not work on the Mac.
- */
-bool Pipe::setWriteNonBlocking(bool val)
-{
- assert(mWriteHandle != kInvalidHandle);
-
-#if defined(HAVE_WIN32_IPC)
- // nothing to do
-#else
- int flags;
-
- if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) {
- LOG(LOG_WARN, "pipe",
- "Warning: couldn't get flags for pipe write fd (errno=%d)\n",
- errno);
- return false;
- }
- if (val)
- flags |= O_NONBLOCK;
- else
- flags &= ~(O_NONBLOCK);
- if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) {
- LOG(LOG_WARN, "pipe",
- "Warning: couldn't set flags for pipe write fd (errno=%d)\n",
- errno);
- return false;
- }
-#endif
-
- mWriteNonBlocking = val;
- return true;
-}
-
-/*
- * Specify whether a file descriptor can be inherited by a child process.
- * Under Linux this means setting the close-on-exec flag, under Windows
- * this is SetHandleInformation(HANDLE_FLAG_INHERIT).
- */
-bool Pipe::disallowReadInherit(void)
-{
- if (mReadHandle == kInvalidHandle)
- return false;
-
-#if defined(HAVE_WIN32_IPC)
- if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0)
- return false;
-#else
- if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0)
- return false;
-#endif
- return true;
-}
-bool Pipe::disallowWriteInherit(void)
-{
- if (mWriteHandle == kInvalidHandle)
- return false;
-
-#if defined(HAVE_WIN32_IPC)
- if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0)
- return false;
-#else
- if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0)
- return false;
-#endif
- return true;
-}
-
-/*
- * Close read descriptor.
- */
-bool Pipe::closeRead(void)
-{
- if (mReadHandle == kInvalidHandle)
- return false;
-
-#if defined(HAVE_WIN32_IPC)
- if (mReadHandle != kInvalidHandle) {
- if (!CloseHandle((HANDLE)mReadHandle)) {
- LOG(LOG_WARN, "pipe", "failed closing read handle\n");
- return false;
- }
- }
-#else
- if (mReadHandle != kInvalidHandle) {
- if (close((int) mReadHandle) != 0) {
- LOG(LOG_WARN, "pipe", "failed closing read fd\n");
- return false;
- }
- }
-#endif
- mReadHandle = kInvalidHandle;
- return true;
-}
-
-/*
- * Close write descriptor.
- */
-bool Pipe::closeWrite(void)
-{
- if (mWriteHandle == kInvalidHandle)
- return false;
-
-#if defined(HAVE_WIN32_IPC)
- if (mWriteHandle != kInvalidHandle) {
- if (!CloseHandle((HANDLE)mWriteHandle)) {
- LOG(LOG_WARN, "pipe", "failed closing write handle\n");
- return false;
- }
- }
-#else
- if (mWriteHandle != kInvalidHandle) {
- if (close((int) mWriteHandle) != 0) {
- LOG(LOG_WARN, "pipe", "failed closing write fd\n");
- return false;
- }
- }
-#endif
- mWriteHandle = kInvalidHandle;
- return true;
-}
-
-/*
- * Get the read handle.
- */
-unsigned long Pipe::getReadHandle(void)
-{
- assert(mReadHandle != kInvalidHandle);
-
- return mReadHandle;
-}
-
-/*
- * Get the write handle.
- */
-unsigned long Pipe::getWriteHandle(void)
-{
- assert(mWriteHandle != kInvalidHandle);
-
- return mWriteHandle;
-}
-
diff --git a/libs/utils/ProcessState.cpp b/libs/utils/ProcessState.cpp
deleted file mode 100644
index 4567df6..0000000
--- a/libs/utils/ProcessState.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-#define LOG_TAG "ProcessState"
-
-#include <cutils/process_name.h>
-
-#include <utils/ProcessState.h>
-
-#include <utils/Atomic.h>
-#include <utils/BpBinder.h>
-#include <utils/IPCThreadState.h>
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/IServiceManager.h>
-#include <utils/String8.h>
-#include <utils/threads.h>
-
-#include <private/utils/binder_module.h>
-#include <private/utils/Static.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-
-#define BINDER_VM_SIZE (1*1024*1024)
-
-static bool gSingleProcess = false;
-
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-
-// Global variables
-int mArgC;
-const char* const* mArgV;
-int mArgLen;
-
-class PoolThread : public Thread
-{
-public:
- PoolThread(bool isMain)
- : mIsMain(isMain)
- {
- }
-
-protected:
- virtual bool threadLoop()
- {
- IPCThreadState::self()->joinThreadPool(mIsMain);
- return false;
- }
-
- const bool mIsMain;
-};
-
-sp<ProcessState> ProcessState::self()
-{
- if (gProcess != NULL) return gProcess;
-
- AutoMutex _l(gProcessMutex);
- if (gProcess == NULL) gProcess = new ProcessState;
- return gProcess;
-}
-
-void ProcessState::setSingleProcess(bool singleProcess)
-{
- gSingleProcess = singleProcess;
-}
-
-
-void ProcessState::setContextObject(const sp<IBinder>& object)
-{
- setContextObject(object, String16("default"));
-}
-
-sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
-{
- if (supportsProcesses()) {
- return getStrongProxyForHandle(0);
- } else {
- return getContextObject(String16("default"), caller);
- }
-}
-
-void ProcessState::setContextObject(const sp<IBinder>& object, const String16& name)
-{
- AutoMutex _l(mLock);
- mContexts.add(name, object);
-}
-
-sp<IBinder> ProcessState::getContextObject(const String16& name, const sp<IBinder>& caller)
-{
- mLock.lock();
- sp<IBinder> object(
- mContexts.indexOfKey(name) >= 0 ? mContexts.valueFor(name) : NULL);
- mLock.unlock();
-
- //printf("Getting context object %s for %p\n", String8(name).string(), caller.get());
-
- if (object != NULL) return object;
-
- // Don't attempt to retrieve contexts if we manage them
- if (mManagesContexts) {
- LOGE("getContextObject(%s) failed, but we manage the contexts!\n",
- String8(name).string());
- return NULL;
- }
-
- IPCThreadState* ipc = IPCThreadState::self();
- {
- Parcel data, reply;
- // no interface token on this magic transaction
- data.writeString16(name);
- data.writeStrongBinder(caller);
- status_t result = ipc->transact(0 /*magic*/, 0, data, &reply, 0);
- if (result == NO_ERROR) {
- object = reply.readStrongBinder();
- }
- }
-
- ipc->flushCommands();
-
- if (object != NULL) setContextObject(object, name);
- return object;
-}
-
-bool ProcessState::supportsProcesses() const
-{
- return mDriverFD >= 0;
-}
-
-void ProcessState::startThreadPool()
-{
- AutoMutex _l(mLock);
- if (!mThreadPoolStarted) {
- mThreadPoolStarted = true;
- spawnPooledThread(true);
- }
-}
-
-bool ProcessState::isContextManager(void) const
-{
- return mManagesContexts;
-}
-
-bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
-{
- if (!mManagesContexts) {
- AutoMutex _l(mLock);
- mBinderContextCheckFunc = checkFunc;
- mBinderContextUserData = userData;
- if (mDriverFD >= 0) {
- int dummy = 0;
-#if defined(HAVE_ANDROID_OS)
- status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
-#else
- status_t result = INVALID_OPERATION;
-#endif
- if (result == 0) {
- mManagesContexts = true;
- } else if (result == -1) {
- mBinderContextCheckFunc = NULL;
- mBinderContextUserData = NULL;
- LOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
- }
- } else {
- // If there is no driver, our only world is the local
- // process so we can always become the context manager there.
- mManagesContexts = true;
- }
- }
- return mManagesContexts;
-}
-
-ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
-{
- const size_t N=mHandleToObject.size();
- if (N <= (size_t)handle) {
- handle_entry e;
- e.binder = NULL;
- e.refs = NULL;
- status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
- if (err < NO_ERROR) return NULL;
- }
- return &mHandleToObject.editItemAt(handle);
-}
-
-sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
-{
- sp<IBinder> result;
-
- AutoMutex _l(mLock);
-
- handle_entry* e = lookupHandleLocked(handle);
-
- if (e != NULL) {
- // We need to create a new BpBinder if there isn't currently one, OR we
- // are unable to acquire a weak reference on this current one. See comment
- // in getWeakProxyForHandle() for more info about this.
- IBinder* b = e->binder;
- if (b == NULL || !e->refs->attemptIncWeak(this)) {
- b = new BpBinder(handle);
- e->binder = b;
- if (b) e->refs = b->getWeakRefs();
- result = b;
- } else {
- // This little bit of nastyness is to allow us to add a primary
- // reference to the remote proxy when this team doesn't have one
- // but another team is sending the handle to us.
- result.force_set(b);
- e->refs->decWeak(this);
- }
- }
-
- return result;
-}
-
-wp<IBinder> ProcessState::getWeakProxyForHandle(int32_t handle)
-{
- wp<IBinder> result;
-
- AutoMutex _l(mLock);
-
- handle_entry* e = lookupHandleLocked(handle);
-
- if (e != NULL) {
- // We need to create a new BpBinder if there isn't currently one, OR we
- // are unable to acquire a weak reference on this current one. The
- // attemptIncWeak() is safe because we know the BpBinder destructor will always
- // call expungeHandle(), which acquires the same lock we are holding now.
- // We need to do this because there is a race condition between someone
- // releasing a reference on this BpBinder, and a new reference on its handle
- // arriving from the driver.
- IBinder* b = e->binder;
- if (b == NULL || !e->refs->attemptIncWeak(this)) {
- b = new BpBinder(handle);
- result = b;
- e->binder = b;
- if (b) e->refs = b->getWeakRefs();
- } else {
- result = b;
- e->refs->decWeak(this);
- }
- }
-
- return result;
-}
-
-void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
-{
- AutoMutex _l(mLock);
-
- handle_entry* e = lookupHandleLocked(handle);
-
- // This handle may have already been replaced with a new BpBinder
- // (if someone failed the AttemptIncWeak() above); we don't want
- // to overwrite it.
- if (e && e->binder == binder) e->binder = NULL;
-}
-
-void ProcessState::setArgs(int argc, const char* const argv[])
-{
- mArgC = argc;
- mArgV = (const char **)argv;
-
- mArgLen = 0;
- for (int i=0; i<argc; i++) {
- mArgLen += strlen(argv[i]) + 1;
- }
- mArgLen--;
-}
-
-int ProcessState::getArgC() const
-{
- return mArgC;
-}
-
-const char* const* ProcessState::getArgV() const
-{
- return mArgV;
-}
-
-void ProcessState::setArgV0(const char* txt)
-{
- if (mArgV != NULL) {
- strncpy((char*)mArgV[0], txt, mArgLen);
- set_process_name(txt);
- }
-}
-
-void ProcessState::spawnPooledThread(bool isMain)
-{
- if (mThreadPoolStarted) {
- int32_t s = android_atomic_add(1, &mThreadPoolSeq);
- char buf[32];
- sprintf(buf, "Binder Thread #%d", s);
- LOGV("Spawning new pooled thread, name=%s\n", buf);
- sp<Thread> t = new PoolThread(isMain);
- t->run(buf);
- }
-}
-
-static int open_driver()
-{
- if (gSingleProcess) {
- return -1;
- }
-
- int fd = open("/dev/binder", O_RDWR);
- if (fd >= 0) {
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- int vers;
-#if defined(HAVE_ANDROID_OS)
- status_t result = ioctl(fd, BINDER_VERSION, &vers);
-#else
- status_t result = -1;
- errno = EPERM;
-#endif
- if (result == -1) {
- LOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
- close(fd);
- fd = -1;
- }
- if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
- LOGE("Binder driver protocol does not match user space protocol!");
- close(fd);
- fd = -1;
- }
-#if defined(HAVE_ANDROID_OS)
- size_t maxThreads = 15;
- result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
- if (result == -1) {
- LOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
- }
-#endif
-
- } else {
- LOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
- }
- return fd;
-}
-
-ProcessState::ProcessState()
- : mDriverFD(open_driver())
- , mVMStart(MAP_FAILED)
- , mManagesContexts(false)
- , mBinderContextCheckFunc(NULL)
- , mBinderContextUserData(NULL)
- , mThreadPoolStarted(false)
- , mThreadPoolSeq(1)
-{
- if (mDriverFD >= 0) {
- // XXX Ideally, there should be a specific define for whether we
- // have mmap (or whether we could possibly have the kernel module
- // availabla).
-#if !defined(HAVE_WIN32_IPC)
- // mmap the binder, providing a chunk of virtual address space to receive transactions.
- mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
- if (mVMStart == MAP_FAILED) {
- // *sigh*
- LOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
- close(mDriverFD);
- mDriverFD = -1;
- }
-#else
- mDriverFD = -1;
-#endif
- }
- if (mDriverFD < 0) {
- // Need to run without the driver, starting our own thread pool.
- }
-}
-
-ProcessState::~ProcessState()
-{
-}
-
-}; // namespace android
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index 0831f4a..f80843d 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -3284,7 +3284,16 @@ bool ResTable::collectString(String16* outString,
break;
}
if (c == '\'' && (quoted == 0 || quoted == '\'')) {
- break;
+ /*
+ * In practice, when people write ' instead of \'
+ * in a string, they are doing it by accident
+ * instead of really meaning to use ' as a quoting
+ * character. Warn them so they don't lose it.
+ */
+ if (outErrorMsg) {
+ *outErrorMsg = "Apostrophe not preceded by \\";
+ }
+ return false;
}
}
p++;
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
deleted file mode 100644
index 51509a3..0000000
--- a/libs/utils/Socket.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address class.
-//
-
-#ifdef HAVE_WINSOCK
-// This needs to come first, or Cygwin gets concerned about a potential
-// clash between WinSock and <sys/types.h>.
-# include <winsock2.h>
-#endif
-
-#include <utils/Socket.h>
-#include <utils/inet_address.h>
-#include <utils/Log.h>
-#include <utils/Timers.h>
-
-#ifndef HAVE_WINSOCK
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- * Socket
- * ===========================================================================
- */
-
-#ifndef INVALID_SOCKET
-# define INVALID_SOCKET (-1)
-#endif
-#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET)
-
-/*static*/ bool Socket::mBootInitialized = false;
-
-/*
- * Extract system-dependent error code.
- */
-static inline int getSocketError(void) {
-#ifdef HAVE_WINSOCK
- return WSAGetLastError();
-#else
- return errno;
-#endif
-}
-
-/*
- * One-time initialization for socket code.
- */
-/*static*/ bool Socket::bootInit(void)
-{
-#ifdef HAVE_WINSOCK
- WSADATA wsaData;
- int err;
-
- err = WSAStartup(MAKEWORD(2, 0), &wsaData);
- if (err != 0) {
- LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
- return false;
- }
-
- LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
- LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
-#endif
-
- mBootInitialized = true;
- return true;
-}
-
-/*
- * One-time shutdown for socket code.
- */
-/*static*/ void Socket::finalShutdown(void)
-{
-#ifdef HAVE_WINSOCK
- WSACleanup();
-#endif
- mBootInitialized = false;
-}
-
-
-/*
- * Simple constructor. Allow the application to create us and then make
- * bind/connect calls.
- */
-Socket::Socket(void)
- : mSock(UNDEF_SOCKET)
-{
- if (!mBootInitialized)
- LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
-}
-
-/*
- * Destructor. Closes the socket and resets our storage.
- */
-Socket::~Socket(void)
-{
- close();
-}
-
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const char* host, int port)
-{
- if (mSock != UNDEF_SOCKET) {
- LOG(LOG_WARN, "socket", "Socket already connected\n");
- return -1;
- }
-
- InetSocketAddress sockAddr;
- if (!sockAddr.create(host, port))
- return -1;
-
- //return doConnect(sockAddr);
- int foo;
- foo = doConnect(sockAddr);
- return foo;
-}
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const InetAddress* addr, int port)
-{
- if (mSock != UNDEF_SOCKET) {
- LOG(LOG_WARN, "socket", "Socket already connected\n");
- return -1;
- }
-
- InetSocketAddress sockAddr;
- if (!sockAddr.create(addr, port))
- return -1;
-
- return doConnect(sockAddr);
-}
-
-/*
- * Finish creating a socket by connecting to the remote host.
- *
- * Returns 0 on success.
- */
-int Socket::doConnect(const InetSocketAddress& sockAddr)
-{
-#ifdef HAVE_WINSOCK
- SOCKET sock;
-#else
- int sock;
-#endif
- const InetAddress* addr = sockAddr.getAddress();
- int port = sockAddr.getPort();
- struct sockaddr_in inaddr;
- DurationTimer connectTimer;
-
- assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
- memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
- inaddr.sin_port = htons(port);
-
- //fprintf(stderr, "--- connecting to %s:%d\n",
- // sockAddr.getHostName(), port);
-
- sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sock == INVALID_SOCKET) {
- int err = getSocketError();
- LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
- return (err != 0) ? err : -1;
- }
-
- connectTimer.start();
-
- if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
- int err = getSocketError();
- LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
- sockAddr.getHostName(), port, err);
- return (err != 0) ? err : -1;
- }
-
- connectTimer.stop();
- if ((long) connectTimer.durationUsecs() > 100000) {
- LOG(LOG_INFO, "socket",
- "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
- port, ((long) connectTimer.durationUsecs()) / 1000000.0);
- }
-
- mSock = (unsigned long) sock;
- LOG(LOG_VERBOSE, "socket",
- "--- connected to %s:%d\n", sockAddr.getHostName(), port);
- return 0;
-}
-
-
-/*
- * Close the socket if it needs closing.
- */
-bool Socket::close(void)
-{
- if (mSock != UNDEF_SOCKET) {
- //fprintf(stderr, "--- closing socket %lu\n", mSock);
-#ifdef HAVE_WINSOCK
- if (::closesocket((SOCKET) mSock) != 0)
- return false;
-#else
- if (::close((int) mSock) != 0)
- return false;
-#endif
- }
-
- mSock = UNDEF_SOCKET;
-
- return true;
-}
-
-/*
- * Read data from socket.
- *
- * Standard semantics: read up to "len" bytes into "buf". Returns the
- * number of bytes read, or less than zero on error.
- */
-int Socket::read(void* buf, ssize_t len) const
-{
- if (mSock == UNDEF_SOCKET) {
- LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
- return -500;
- }
-
-#ifdef HAVE_WINSOCK
- SOCKET sock = (SOCKET) mSock;
-#else
- int sock = (int) mSock;
-#endif
- int cc;
-
- cc = recv(sock, (char*)buf, len, 0);
- if (cc < 0) {
- int err = getSocketError();
- return (err > 0) ? -err : -1;
- }
-
- return cc;
-}
-
-/*
- * Write data to a socket.
- *
- * Standard semantics: write up to "len" bytes into "buf". Returns the
- * number of bytes written, or less than zero on error.
- */
-int Socket::write(const void* buf, ssize_t len) const
-{
- if (mSock == UNDEF_SOCKET) {
- LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
- return -500;
- }
-
-#ifdef HAVE_WINSOCK
- SOCKET sock = (SOCKET) mSock;
-#else
- int sock = (int) mSock;
-#endif
- int cc;
-
- cc = send(sock, (const char*)buf, len, 0);
- if (cc < 0) {
- int err = getSocketError();
- return (err > 0) ? -err : -1;
- }
-
- return cc;
-}
-
-
-/*
- * ===========================================================================
- * Socket tests
- * ===========================================================================
- */
-
-/*
- * Read all data from the socket. The data is read into a buffer that
- * expands as needed.
- *
- * On exit, the buffer is returned, and the length of the data is stored
- * in "*sz". A null byte is added to the end, but is not included in
- * the length.
- */
-static char* socketReadAll(const Socket& s, int *sz)
-{
- int max, r;
- char *data, *ptr, *tmp;
-
- data = (char*) malloc(max = 32768);
- if (data == NULL)
- return NULL;
-
- ptr = data;
-
- for (;;) {
- if ((ptr - data) == max) {
- tmp = (char*) realloc(data, max *= 2);
- if(tmp == 0) {
- free(data);
- return 0;
- }
- }
- r = s.read(ptr, max - (ptr - data));
- if (r == 0)
- break;
- if (r < 0) {
- LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
- break;
- }
- ptr += r;
- }
-
- if ((ptr - data) == max) {
- tmp = (char*) realloc(data, max + 1);
- if (tmp == NULL) {
- free(data);
- return NULL;
- }
- }
- *ptr = '\0';
- *sz = (ptr - data);
- return data;
-}
-
-/*
- * Exercise the Socket class.
- */
-void android::TestSockets(void)
-{
- printf("----- SOCKET TEST ------\n");
- Socket::bootInit();
-
- char* buf = NULL;
- int len, cc;
- const char* kTestStr =
- "GET / HTTP/1.0\n"
- "Connection: close\n"
- "\n";
-
- Socket sock;
- if (sock.connect("www.google.com", 80) != 0) {
- fprintf(stderr, "socket connected failed\n");
- goto bail;
- }
-
- cc = sock.write(kTestStr, strlen(kTestStr));
- if (cc != (int) strlen(kTestStr)) {
- fprintf(stderr, "write failed, res=%d\n", cc);
- goto bail;
- }
- buf = socketReadAll(sock, &len);
-
- printf("GOT '%s'\n", buf);
-
-bail:
- sock.close();
- free(buf);
-}
-
diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp
index 93f7e4f..4dfa578 100644
--- a/libs/utils/Static.cpp
+++ b/libs/utils/Static.cpp
@@ -20,7 +20,6 @@
#include <private/utils/Static.h>
#include <utils/BufferedTextOutput.h>
-#include <utils/IPCThreadState.h>
#include <utils/Log.h>
namespace android {
@@ -87,34 +86,4 @@ TextOutput& alog(gLogTextOutput);
TextOutput& aout(gStdoutTextOutput);
TextOutput& aerr(gStderrTextOutput);
-#ifndef LIBUTILS_NATIVE
-
-// ------------ ProcessState.cpp
-
-Mutex gProcessMutex;
-sp<ProcessState> gProcess;
-
-class LibUtilsIPCtStatics
-{
-public:
- LibUtilsIPCtStatics()
- {
- }
-
- ~LibUtilsIPCtStatics()
- {
- IPCThreadState::shutdown();
- }
-};
-
-static LibUtilsIPCtStatics gIPCStatics;
-
-// ------------ ServiceManager.cpp
-
-Mutex gDefaultServiceManagerLock;
-sp<IServiceManager> gDefaultServiceManager;
-sp<IPermissionController> gPermissionController;
-
-#endif
-
} // namespace android
diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp
new file mode 100644
index 0000000..aa42d68
--- /dev/null
+++ b/libs/utils/StringArray.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Sortable array of strings. STL-ish, but STL-free.
+//
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/StringArray.h>
+
+namespace android {
+
+//
+// An expanding array of strings. Add, get, sort, delete.
+//
+StringArray::StringArray()
+ : mMax(0), mCurrent(0), mArray(NULL)
+{
+}
+
+StringArray:: ~StringArray() {
+ for (int i = 0; i < mCurrent; i++)
+ delete[] mArray[i];
+ delete[] mArray;
+}
+
+//
+// Add a string. A copy of the string is made.
+//
+bool StringArray::push_back(const char* str) {
+ if (mCurrent >= mMax) {
+ char** tmp;
+
+ if (mMax == 0)
+ mMax = 16; // initial storage
+ else
+ mMax *= 2;
+
+ tmp = new char*[mMax];
+ if (tmp == NULL)
+ return false;
+
+ memcpy(tmp, mArray, mCurrent * sizeof(char*));
+ delete[] mArray;
+ mArray = tmp;
+ }
+
+ int len = strlen(str);
+ mArray[mCurrent] = new char[len+1];
+ memcpy(mArray[mCurrent], str, len+1);
+ mCurrent++;
+
+ return true;
+}
+
+//
+// Delete an entry.
+//
+void StringArray::erase(int idx) {
+ if (idx < 0 || idx >= mCurrent)
+ return;
+ delete[] mArray[idx];
+ if (idx < mCurrent-1) {
+ memmove(&mArray[idx], &mArray[idx+1],
+ (mCurrent-1 - idx) * sizeof(char*));
+ }
+ mCurrent--;
+}
+
+//
+// Sort the array.
+//
+void StringArray::sort(int (*compare)(const void*, const void*)) {
+ qsort(mArray, mCurrent, sizeof(char*), compare);
+}
+
+//
+// Pass this to the sort routine to do an ascending alphabetical sort.
+//
+int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) {
+ return strcmp(*(const char**)pstr1, *(const char**)pstr2);
+}
+
+//
+// Set entry N to specified string.
+// [should use operator[] here]
+//
+void StringArray::setEntry(int idx, const char* str) {
+ if (idx < 0 || idx >= mCurrent)
+ return;
+ delete[] mArray[idx];
+ int len = strlen(str);
+ mArray[idx] = new char[len+1];
+ memcpy(mArray[idx], str, len+1);
+}
+
+
+}; // namespace android
diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp
index cebee99..e04823d 100644
--- a/libs/utils/TextOutput.cpp
+++ b/libs/utils/TextOutput.cpp
@@ -22,9 +22,17 @@
#include <stdlib.h>
#include <string.h>
+namespace android {
+
// ---------------------------------------------------------------------------
-namespace android {
+TextOutput::TextOutput() {
+}
+
+TextOutput::~TextOutput() {
+}
+
+// ---------------------------------------------------------------------------
TextOutput& operator<<(TextOutput& to, bool val)
{
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 9287c0b..ec3db09 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -38,10 +38,6 @@
# define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW
#endif
-#if defined(HAVE_FUTEX)
-#include <private/utils/futex_synchro.h>
-#endif
-
#if defined(HAVE_PRCTL)
#include <sys/prctl.h>
#endif
@@ -56,10 +52,6 @@ using namespace android;
// ----------------------------------------------------------------------------
#if defined(HAVE_PTHREADS)
-#if 0
-#pragma mark -
-#pragma mark PTHREAD
-#endif
// ----------------------------------------------------------------------------
/*
@@ -163,10 +155,6 @@ android_thread_id_t androidGetThreadId()
// ----------------------------------------------------------------------------
#elif defined(HAVE_WIN32_THREADS)
-#if 0
-#pragma mark -
-#pragma mark WIN32_THREADS
-#endif
// ----------------------------------------------------------------------------
/*
@@ -252,11 +240,6 @@ android_thread_id_t androidGetThreadId()
// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Common Thread functions
-#endif
-
int androidCreateThread(android_thread_func_t fn, void* arg)
{
return createThreadEtc(fn, arg);
@@ -294,112 +277,23 @@ namespace android {
* ===========================================================================
*/
-#if 0
-#pragma mark -
-#pragma mark Mutex
-#endif
-
-#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX)
-/*
- * Simple pthread wrapper.
- */
+#if defined(HAVE_PTHREADS)
+// implemented as inlines in threads.h
+#elif defined(HAVE_WIN32_THREADS)
Mutex::Mutex()
{
- _init();
-}
-
-Mutex::Mutex(const char* name)
-{
- // XXX: name not used for now
- _init();
-}
-
-void Mutex::_init()
-{
- pthread_mutex_t* pMutex = new pthread_mutex_t;
- pthread_mutex_init(pMutex, NULL);
- mState = pMutex;
-}
-
-Mutex::~Mutex()
-{
- delete (pthread_mutex_t*) mState;
-}
-
-status_t Mutex::lock()
-{
- int res;
- while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ;
- return -res;
-}
-
-void Mutex::unlock()
-{
- pthread_mutex_unlock((pthread_mutex_t*) mState);
-}
-
-status_t Mutex::tryLock()
-{
- int res;
- while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ;
- return -res;
-}
-
-#elif defined(HAVE_FUTEX)
-#if 0
-#pragma mark -
-#endif
+ HANDLE hMutex;
-#define STATE ((futex_mutex_t*) (&mState))
+ assert(sizeof(hMutex) == sizeof(mState));
-Mutex::Mutex()
-{
- _init();
+ hMutex = CreateMutex(NULL, FALSE, NULL);
+ mState = (void*) hMutex;
}
Mutex::Mutex(const char* name)
{
- _init();
-}
-
-void
-Mutex::_init()
-{
- futex_mutex_init(STATE);
-}
-
-Mutex::~Mutex()
-{
-}
-
-status_t Mutex::lock()
-{
- int res;
- while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ;
- return -res;
-}
-
-void Mutex::unlock()
-{
- futex_mutex_unlock(STATE);
-}
-
-status_t Mutex::tryLock()
-{
- int res;
- while ((res=futex_mutex_trylock(STATE)) == EINTR) ;
- return -res;
-}
-#undef STATE
-
-#elif defined(HAVE_WIN32_THREADS)
-#if 0
-#pragma mark -
-#endif
-
-Mutex::Mutex()
-{
+ // XXX: name not used for now
HANDLE hMutex;
assert(sizeof(hMutex) == sizeof(mState));
@@ -408,11 +302,13 @@ Mutex::Mutex()
mState = (void*) hMutex;
}
-Mutex::Mutex(const char* name)
+Mutex::Mutex(int type, const char* name)
{
- // XXX: name not used for now
+ // XXX: type and name not used for now
HANDLE hMutex;
+ assert(sizeof(hMutex) == sizeof(mState));
+
hMutex = CreateMutex(NULL, FALSE, NULL);
mState = (void*) hMutex;
}
@@ -456,161 +352,9 @@ status_t Mutex::tryLock()
* ===========================================================================
*/
-#if 0
-#pragma mark -
-#pragma mark Condition
-#endif
-
-#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX)
-
-/*
- * Constructor. This is a simple pthread wrapper.
- */
-Condition::Condition()
-{
- pthread_cond_t* pCond = new pthread_cond_t;
-
- pthread_cond_init(pCond, NULL);
- mState = pCond;
-}
-
-/*
- * Destructor.
- */
-Condition::~Condition()
-{
- pthread_cond_destroy((pthread_cond_t*) mState);
- delete (pthread_cond_t*) mState;
-}
-
-/*
- * Wait on a condition variable. Lock the mutex before calling.
- */
-
-status_t Condition::wait(Mutex& mutex)
-{
- assert(mutex.mState != NULL);
-
- int cc;
- while ((cc = pthread_cond_wait((pthread_cond_t*)mState,
- (pthread_mutex_t*) mutex.mState)) == EINTR) ;
- return -cc;
-}
-
-status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
-{
- assert(mutex.mState != NULL);
-
- struct timespec ts;
- ts.tv_sec = abstime/1000000000;
- ts.tv_nsec = abstime-(ts.tv_sec*1000000000);
-
- int cc;
- while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState,
- (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ;
- return -cc;
-}
-
-status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
-{
- return wait(mutex, systemTime()+reltime);
-}
-
-/*
- * Signal the condition variable, allowing one thread to continue.
- */
-void Condition::signal()
-{
- pthread_cond_signal((pthread_cond_t*) mState);
-}
-
-/*
- * Signal the condition variable, allowing all threads to continue.
- */
-void Condition::broadcast()
-{
- pthread_cond_broadcast((pthread_cond_t*) mState);
-}
-
-#elif defined(HAVE_FUTEX)
-#if 0
-#pragma mark -
-#endif
-
-#define STATE ((futex_cond_t*) (&mState))
-
-/*
- * Constructor. This is a simple pthread wrapper.
- */
-Condition::Condition()
-{
- futex_cond_init(STATE);
-}
-
-/*
- * Destructor.
- */
-Condition::~Condition()
-{
-}
-
-/*
- * Wait on a condition variable. Lock the mutex before calling.
- */
-
-status_t Condition::wait(Mutex& mutex)
-{
- assert(mutex.mState != NULL);
-
- int res;
- while ((res = futex_cond_wait(STATE,
- (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ;
-
- return -res;
-}
-
-status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
-{
- nsecs_t reltime = abstime - systemTime();
- if (reltime <= 0) return true;
- return waitRelative(mutex, reltime);
-}
-
-status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
-{
- assert(mutex.mState != NULL);
- int res;
- unsigned msec = ns2ms(reltime);
- if(msec == 0)
- return true;
- // This code will not time out at the correct time if interrupted by signals
- while ((res = futex_cond_wait(STATE,
- (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ;
- return res;
-}
-
-/*
- * Signal the condition variable, allowing one thread to continue.
- */
-void Condition::signal()
-{
- futex_cond_signal(STATE);
-}
-
-/*
- * Signal the condition variable, allowing all threads to continue.
- */
-void Condition::broadcast()
-{
- futex_cond_broadcast(STATE);
-}
-
-#undef STATE
-
+#if defined(HAVE_PTHREADS)
+// implemented as inlines in threads.h
#elif defined(HAVE_WIN32_THREADS)
-#if 0
-#pragma mark -
-#endif
/*
* Windows doesn't have a condition variable solution. It's possible
@@ -753,17 +497,13 @@ status_t Condition::wait(Mutex& mutex)
return ((WinCondition*)mState)->wait(condState, hMutex, NULL);
}
-status_t Condition::wait(Mutex& mutex, nsecs_t abstime)
+status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
{
WinCondition* condState = (WinCondition*) mState;
HANDLE hMutex = (HANDLE) mutex.mState;
+ nsecs_t absTime = systemTime()+reltime;
- return ((WinCondition*)mState)->wait(condState, hMutex, &abstime);
-}
-
-status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)
-{
- return wait(mutex, systemTime()+reltime);
+ return ((WinCondition*)mState)->wait(condState, hMutex, &absTime);
}
/*
@@ -841,11 +581,6 @@ void Condition::broadcast()
// ----------------------------------------------------------------------------
-#if 0
-#pragma mark -
-#pragma mark Thread::Thread
-#endif
-
/*
* This is our thread object!
*/
@@ -920,6 +655,11 @@ int Thread::_threadLoop(void* user)
wp<Thread> weak(strong);
self->mHoldSelf.clear();
+#if HAVE_ANDROID_OS
+ // this is very useful for debugging with gdb
+ self->mTid = gettid();
+#endif
+
bool first = true;
do {
@@ -950,7 +690,7 @@ int Thread::_threadLoop(void* user)
self->mExitPending = true;
self->mLock.lock();
self->mRunning = false;
- self->mThreadExitedCondition.signal();
+ self->mThreadExitedCondition.broadcast();
self->mLock.unlock();
break;
}
@@ -958,7 +698,7 @@ int Thread::_threadLoop(void* user)
// Release our strong reference, to let a chance to the thread
// to die a peaceful death.
strong.clear();
- // And immediately, reacquire a strong reference for the next loop
+ // And immediately, re-acquire a strong reference for the next loop
strong = weak.promote();
} while(strong != 0);
diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp
deleted file mode 100644
index 835480d..0000000
--- a/libs/utils/TimerProbe.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/TimerProbe.h>
-
-#if ENABLE_TIMER_PROBE
-
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "time"
-
-namespace android {
-
-Vector<TimerProbe::Bucket> TimerProbe::gBuckets;
-TimerProbe* TimerProbe::gExecuteChain;
-int TimerProbe::gIndent;
-timespec TimerProbe::gRealBase;
-
-TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag)
-{
- mNext = gExecuteChain;
- gExecuteChain = this;
- mIndent = gIndent;
- gIndent += 1;
- if (mIndent > 0) {
- if (*slot == 0) {
- int count = gBuckets.add();
- *slot = count;
- Bucket& bucket = gBuckets.editItemAt(count);
- memset(&bucket, 0, sizeof(Bucket));
- bucket.mTag = tag;
- bucket.mSlotPtr = slot;
- bucket.mIndent = mIndent;
- }
- mBucket = *slot;
- }
- clock_gettime(CLOCK_REALTIME, &mRealStart);
- if (gRealBase.tv_sec == 0)
- gRealBase = mRealStart;
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart);
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart);
-}
-
-void TimerProbe::end()
-{
- timespec realEnd, pEnd, tEnd;
- clock_gettime(CLOCK_REALTIME, &realEnd);
- clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd);
- clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd);
- print(realEnd, pEnd, tEnd);
- mTag = NULL;
-}
-
-TimerProbe::~TimerProbe()
-{
- if (mTag != NULL)
- end();
- gExecuteChain = mNext;
- gIndent--;
-}
-
-
-uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end)
-{
- int sec = end.tv_sec - start.tv_sec;
- int nsec = end.tv_nsec - start.tv_nsec;
- if (nsec < 0) {
- sec--;
- nsec += 1000000000;
- }
- return sec * 1000000 + nsec / 1000;
-}
-
-void TimerProbe::print(const timespec& r, const timespec& p,
- const timespec& t) const
-{
- uint32_t es = ElapsedTime(gRealBase, mRealStart);
- uint32_t er = ElapsedTime(mRealStart, r);
- uint32_t ep = ElapsedTime(mPStart, p);
- uint32_t et = ElapsedTime(mTStart, t);
- if (mIndent > 0) {
- Bucket& bucket = gBuckets.editItemAt(mBucket);
- if (bucket.mStart == 0)
- bucket.mStart = es;
- bucket.mReal += er;
- bucket.mProcess += ep;
- bucket.mThread += et;
- bucket.mCount++;
- return;
- }
- int index = 0;
- int buckets = gBuckets.size();
- int count = 1;
- const char* tag = mTag;
- int indent = mIndent;
- do {
- LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n",
- tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0,
- er, ep, ep * 100 / er, et, et * 100 / er);
- if (index >= buckets)
- break;
- Bucket& bucket = gBuckets.editItemAt(index);
- count = bucket.mCount;
- es = bucket.mStart;
- er = bucket.mReal;
- ep = bucket.mProcess;
- et = bucket.mThread;
- tag = bucket.mTag;
- indent = bucket.mIndent;
- *bucket.mSlotPtr = 0;
- } while (++index); // always true
- gBuckets.clear();
-}
-
-}; // namespace android
-
-#endif
diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp
index 2abc811..784f035 100644
--- a/libs/utils/Timers.cpp
+++ b/libs/utils/Timers.cpp
@@ -18,7 +18,6 @@
// Timer functions.
//
#include <utils/Timers.h>
-#include <utils/ported.h> // may need usleep
#include <utils/Log.h>
#include <stdlib.h>
@@ -54,130 +53,6 @@ nsecs_t systemTime(int clock)
#endif
}
-//#define MONITOR_USLEEP
-
-/*
- * Sleep long enough that we'll wake up "interval" milliseconds after
- * the previous snooze.
- *
- * The "nextTick" argument is updated on each call, and should be passed
- * in every time. Set its fields to zero on the first call.
- *
- * Returns the #of intervals we have overslept, which will be zero if we're
- * on time. [Currently just returns 0 or 1.]
- */
-int sleepForInterval(long interval, struct timeval* pNextTick)
-{
- struct timeval now;
- long long timeBeforeNext;
- long sleepTime = 0;
- bool overSlept = false;
- //int usleepBias = 0;
-
-#ifdef USLEEP_BIAS
- /*
- * Linux likes to add 9000ms or so.
- * [not using this for now]
- */
- //usleepBias = USLEEP_BIAS;
-#endif
-
- gettimeofday(&now, NULL);
-
- if (pNextTick->tv_sec == 0) {
- /* special-case for first time through */
- *pNextTick = now;
- sleepTime = interval;
- android::DurationTimer::addToTimeval(pNextTick, interval);
- } else {
- /*
- * Compute how much time there is before the next tick. If this
- * value is negative, we've run over. If we've run over a little
- * bit we can shorten the next frame to keep the pace steady, but
- * if we've dramatically overshot we need to re-sync.
- */
- timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now);
- //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n",
- // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec,
- // (long) timeBeforeNext);
- if (timeBeforeNext < -interval) {
- /* way over */
- overSlept = true;
- sleepTime = 0;
- *pNextTick = now;
- } else if (timeBeforeNext <= 0) {
- /* slightly over, keep the pace steady */
- overSlept = true;
- sleepTime = 0;
- } else if (timeBeforeNext <= interval) {
- /* right on schedule */
- sleepTime = timeBeforeNext;
- } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) {
- /* sleep call returned early; do a longer sleep this time */
- sleepTime = timeBeforeNext;
- } else if (timeBeforeNext > interval) {
- /* we went back in time -- somebody updated system clock? */
- /* (could also be a *seriously* broken usleep()) */
- LOG(LOG_DEBUG, "",
- " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext);
- sleepTime = 0;
- *pNextTick = now;
- }
- android::DurationTimer::addToTimeval(pNextTick, interval);
- }
- //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n",
- // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec,
- // sleepTime);
-
- /*
- * Sleep for the designated period of time.
- *
- * Linux tends to sleep for longer than requested, often by 17-18ms.
- * MinGW tends to sleep for less than requested, by as much as 14ms,
- * but occasionally oversleeps for 40+ms (looks like some external
- * factors plus round-off on a 64Hz clock). Cygwin is pretty steady.
- *
- * If you start the MinGW version, and then launch the Cygwin version,
- * the MinGW clock becomes more erratic. Not entirely sure why.
- *
- * (There's a lot of stuff here; it's really just a usleep() call with
- * a bunch of instrumentation.)
- */
- if (sleepTime > 0) {
-#if defined(MONITOR_USLEEP)
- struct timeval before, after;
- long long actual;
-
- gettimeofday(&before, NULL);
- usleep((long) sleepTime);
- gettimeofday(&after, NULL);
-
- /* check usleep() accuracy; default Linux threads are pretty sloppy */
- actual = android::DurationTimer::subtractTimevals(&after, &before);
- if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ ||
- (long) actual > sleepTime + 20000 /*(sleepTime/10)*/)
- {
- LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime,
- (long) actual);
- }
-#else
-#ifdef HAVE_WIN32_THREADS
- Sleep( sleepTime/1000 );
-#else
- usleep((long) sleepTime);
-#endif
-#endif
- }
-
- //printf("slept %d\n", sleepTime);
-
- if (overSlept)
- return 1; // close enough
- else
- return 0;
-}
-
-
/*
* ===========================================================================
* DurationTimer
diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp
deleted file mode 100644
index 96f9fc4..0000000
--- a/libs/utils/ZipEntry.cpp
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-//
-// Access to entries in a Zip archive.
-//
-
-#define LOG_TAG "zip"
-
-#include <utils/ZipEntry.h>
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-
-using namespace android;
-
-/*
- * Initialize a new ZipEntry structure from a FILE* positioned at a
- * CentralDirectoryEntry.
- *
- * On exit, the file pointer will be at the start of the next CDE or
- * at the EOCD.
- */
-status_t ZipEntry::initFromCDE(FILE* fp)
-{
- status_t result;
- long posn;
- bool hasDD;
-
- //LOGV("initFromCDE ---\n");
-
- /* read the CDE */
- result = mCDE.read(fp);
- if (result != NO_ERROR) {
- LOGD("mCDE.read failed\n");
- return result;
- }
-
- //mCDE.dump();
-
- /* using the info in the CDE, go load up the LFH */
- posn = ftell(fp);
- if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
- LOGD("local header seek failed (%ld)\n",
- mCDE.mLocalHeaderRelOffset);
- return UNKNOWN_ERROR;
- }
-
- result = mLFH.read(fp);
- if (result != NO_ERROR) {
- LOGD("mLFH.read failed\n");
- return result;
- }
-
- if (fseek(fp, posn, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- //mLFH.dump();
-
- /*
- * We *might* need to read the Data Descriptor at this point and
- * integrate it into the LFH. If this bit is set, the CRC-32,
- * compressed size, and uncompressed size will be zero. In practice
- * these seem to be rare.
- */
- hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
- if (hasDD) {
- // do something clever
- //LOGD("+++ has data descriptor\n");
- }
-
- /*
- * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
- * flag is set, because the LFH is incomplete. (Not a problem, since we
- * prefer the CDE values.)
- */
- if (!hasDD && !compareHeaders()) {
- LOGW("WARNING: header mismatch\n");
- // keep going?
- }
-
- /*
- * If the mVersionToExtract is greater than 20, we may have an
- * issue unpacking the record -- could be encrypted, compressed
- * with something we don't support, or use Zip64 extensions. We
- * can defer worrying about that to when we're extracting data.
- */
-
- return NO_ERROR;
-}
-
-/*
- * Initialize a new entry. Pass in the file name and an optional comment.
- *
- * Initializes the CDE and the LFH.
- */
-void ZipEntry::initNew(const char* fileName, const char* comment)
-{
- assert(fileName != NULL && *fileName != '\0'); // name required
-
- /* most fields are properly initialized by constructor */
- mCDE.mVersionMadeBy = kDefaultMadeBy;
- mCDE.mVersionToExtract = kDefaultVersion;
- mCDE.mCompressionMethod = kCompressStored;
- mCDE.mFileNameLength = strlen(fileName);
- if (comment != NULL)
- mCDE.mFileCommentLength = strlen(comment);
- mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
-
- if (mCDE.mFileNameLength > 0) {
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
- strcpy((char*) mCDE.mFileName, fileName);
- }
- if (mCDE.mFileCommentLength > 0) {
- /* TODO: stop assuming null-terminated ASCII here? */
- mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
- strcpy((char*) mCDE.mFileComment, comment);
- }
-
- copyCDEtoLFH();
-}
-
-/*
- * Initialize a new entry, starting with the ZipEntry from a different
- * archive.
- *
- * Initializes the CDE and the LFH.
- */
-status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
- const ZipEntry* pEntry)
-{
- /*
- * Copy everything in the CDE over, then fix up the hairy bits.
- */
- memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
-
- if (mCDE.mFileNameLength > 0) {
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
- if (mCDE.mFileName == NULL)
- return NO_MEMORY;
- strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
- }
- if (mCDE.mFileCommentLength > 0) {
- mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
- if (mCDE.mFileComment == NULL)
- return NO_MEMORY;
- strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
- }
- if (mCDE.mExtraFieldLength > 0) {
- /* we null-terminate this, though it may not be a string */
- mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
- if (mCDE.mExtraField == NULL)
- return NO_MEMORY;
- memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
- mCDE.mExtraFieldLength+1);
- }
-
- /* construct the LFH from the CDE */
- copyCDEtoLFH();
-
- /*
- * The LFH "extra" field is independent of the CDE "extra", so we
- * handle it here.
- */
- assert(mLFH.mExtraField == NULL);
- mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
- if (mLFH.mExtraFieldLength > 0) {
- mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
- if (mLFH.mExtraField == NULL)
- return NO_MEMORY;
- memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
- mLFH.mExtraFieldLength+1);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Insert pad bytes in the LFH by tweaking the "extra" field. This will
- * potentially confuse something that put "extra" data in here earlier,
- * but I can't find an actual problem.
- */
-status_t ZipEntry::addPadding(int padding)
-{
- if (padding <= 0)
- return INVALID_OPERATION;
-
- //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
- // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
-
- if (mLFH.mExtraFieldLength > 0) {
- /* extend existing field */
- unsigned char* newExtra;
-
- newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
- if (newExtra == NULL)
- return NO_MEMORY;
- memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
- memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
-
- delete[] mLFH.mExtraField;
- mLFH.mExtraField = newExtra;
- mLFH.mExtraFieldLength += padding;
- } else {
- /* create new field */
- mLFH.mExtraField = new unsigned char[padding];
- memset(mLFH.mExtraField, 0, padding);
- mLFH.mExtraFieldLength = padding;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Set the fields in the LFH equal to the corresponding fields in the CDE.
- *
- * This does not touch the LFH "extra" field.
- */
-void ZipEntry::copyCDEtoLFH(void)
-{
- mLFH.mVersionToExtract = mCDE.mVersionToExtract;
- mLFH.mGPBitFlag = mCDE.mGPBitFlag;
- mLFH.mCompressionMethod = mCDE.mCompressionMethod;
- mLFH.mLastModFileTime = mCDE.mLastModFileTime;
- mLFH.mLastModFileDate = mCDE.mLastModFileDate;
- mLFH.mCRC32 = mCDE.mCRC32;
- mLFH.mCompressedSize = mCDE.mCompressedSize;
- mLFH.mUncompressedSize = mCDE.mUncompressedSize;
- mLFH.mFileNameLength = mCDE.mFileNameLength;
- // the "extra field" is independent
-
- delete[] mLFH.mFileName;
- if (mLFH.mFileNameLength > 0) {
- mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
- strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
- } else {
- mLFH.mFileName = NULL;
- }
-}
-
-/*
- * Set some information about a file after we add it.
- */
-void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod)
-{
- mCDE.mCompressionMethod = compressionMethod;
- mCDE.mCRC32 = crc32;
- mCDE.mCompressedSize = compLen;
- mCDE.mUncompressedSize = uncompLen;
- mCDE.mCompressionMethod = compressionMethod;
- if (compressionMethod == kCompressDeflated) {
- mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
- }
- copyCDEtoLFH();
-}
-
-/*
- * See if the data in mCDE and mLFH match up. This is mostly useful for
- * debugging these classes, but it can be used to identify damaged
- * archives.
- *
- * Returns "false" if they differ.
- */
-bool ZipEntry::compareHeaders(void) const
-{
- if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
- LOGV("cmp: VersionToExtract\n");
- return false;
- }
- if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
- LOGV("cmp: GPBitFlag\n");
- return false;
- }
- if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
- LOGV("cmp: CompressionMethod\n");
- return false;
- }
- if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
- LOGV("cmp: LastModFileTime\n");
- return false;
- }
- if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
- LOGV("cmp: LastModFileDate\n");
- return false;
- }
- if (mCDE.mCRC32 != mLFH.mCRC32) {
- LOGV("cmp: CRC32\n");
- return false;
- }
- if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
- LOGV("cmp: CompressedSize\n");
- return false;
- }
- if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
- LOGV("cmp: UncompressedSize\n");
- return false;
- }
- if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
- LOGV("cmp: FileNameLength\n");
- return false;
- }
-#if 0 // this seems to be used for padding, not real data
- if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
- LOGV("cmp: ExtraFieldLength\n");
- return false;
- }
-#endif
- if (mCDE.mFileName != NULL) {
- if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
- LOGV("cmp: FileName\n");
- return false;
- }
- }
-
- return true;
-}
-
-
-/*
- * Convert the DOS date/time stamp into a UNIX time stamp.
- */
-time_t ZipEntry::getModWhen(void) const
-{
- struct tm parts;
-
- parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
- parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
- parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
- parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
- parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
- parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
- parts.tm_wday = parts.tm_yday = 0;
- parts.tm_isdst = -1; // DST info "not available"
-
- return mktime(&parts);
-}
-
-/*
- * Set the CDE/LFH timestamp from UNIX time.
- */
-void ZipEntry::setModWhen(time_t when)
-{
-#ifdef HAVE_LOCALTIME_R
- struct tm tmResult;
-#endif
- time_t even;
- unsigned short zdate, ztime;
-
- struct tm* ptm;
-
- /* round up to an even number of seconds */
- even = (time_t)(((unsigned long)(when) + 1) & (~1));
-
- /* expand */
-#ifdef HAVE_LOCALTIME_R
- ptm = localtime_r(&even, &tmResult);
-#else
- ptm = localtime(&even);
-#endif
-
- int year;
- year = ptm->tm_year;
- if (year < 80)
- year = 80;
-
- zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
- ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
-
- mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
- mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::LocalFileHeader
- * ===========================================================================
- */
-
-/*
- * Read a local file header.
- *
- * On entry, "fp" points to the signature at the start of the header.
- * On exit, "fp" points to the start of data.
- */
-status_t ZipEntry::LocalFileHeader::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kLFHLen];
-
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
-
- if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
-
- // TODO: validate sizes
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* grab extra field */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a local file header.
- */
-status_t ZipEntry::LocalFileHeader::write(FILE* fp)
-{
- unsigned char buf[kLFHLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x0e], mCRC32);
- ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
-
- if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Dump the contents of a LocalFileHeader object.
- */
-void ZipEntry::LocalFileHeader::dump(void) const
-{
- LOGD(" LocalFileHeader contents:\n");
- LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u\n",
- mFileNameLength, mExtraFieldLength);
- if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::CentralDirEntry
- * ===========================================================================
- */
-
-/*
- * Read the central dir entry that appears next in the file.
- *
- * On entry, "fp" should be positioned on the signature bytes for the
- * entry. On exit, "fp" will point at the signature word for the next
- * entry or for the EOCD.
- */
-status_t ZipEntry::CentralDirEntry::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kCDELen];
-
- /* no re-use */
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
- assert(mFileComment == NULL);
-
- if (fread(buf, 1, kCDELen, fp) != kCDELen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("Whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
- mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
- mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
- mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
- mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
- mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
-
- // TODO: validate sizes and offsets
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* read "extra field" */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-
- /* grab comment, if any */
- if (mFileCommentLength != 0) {
- mFileComment = new unsigned char[mFileCommentLength+1];
- if (mFileComment == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileComment[mFileCommentLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a central dir entry.
- */
-status_t ZipEntry::CentralDirEntry::write(FILE* fp)
-{
- unsigned char buf[kCDELen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
- ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x10], mCRC32);
- ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
- ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
- ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
- ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
- ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
- ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
-
- if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- /* write comment */
- if (mFileCommentLength != 0) {
- if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of a CentralDirEntry object.
- */
-void ZipEntry::CentralDirEntry::dump(void) const
-{
- LOGD(" CentralDirEntry contents:\n");
- LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
- mFileNameLength, mExtraFieldLength, mFileCommentLength);
- LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
- mDiskNumberStart, mInternalAttrs, mExternalAttrs,
- mLocalHeaderRelOffset);
-
- if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
- if (mFileComment != NULL)
- LOGD(" comment: '%s'\n", mFileComment);
-}
-
diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp
deleted file mode 100644
index 6f27d17..0000000
--- a/libs/utils/ZipFile.cpp
+++ /dev/null
@@ -1,1296 +0,0 @@
-/*
- * Copyright (C) 2006 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.
- */
-
-//
-// Access to Zip archives.
-//
-
-#define LOG_TAG "zip"
-
-#include <utils/ZipFile.h>
-#include <utils/ZipUtils.h>
-#include <utils/Log.h>
-
-#include <zlib.h>
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-#include <memory.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <assert.h>
-
-using namespace android;
-
-/*
- * Some environments require the "b", some choke on it.
- */
-#define FILE_OPEN_RO "rb"
-#define FILE_OPEN_RW "r+b"
-#define FILE_OPEN_RW_CREATE "w+b"
-
-/* should live somewhere else? */
-static status_t errnoToStatus(int err)
-{
- if (err == ENOENT)
- return NAME_NOT_FOUND;
- else if (err == EACCES)
- return PERMISSION_DENIED;
- else
- return UNKNOWN_ERROR;
-}
-
-/*
- * Open a file and parse its guts.
- */
-status_t ZipFile::open(const char* zipFileName, int flags)
-{
- bool newArchive = false;
-
- assert(mZipFp == NULL); // no reopen
-
- if ((flags & kOpenTruncate))
- flags |= kOpenCreate; // trunc implies create
-
- if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
- return INVALID_OPERATION; // not both
- if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
- return INVALID_OPERATION; // not neither
- if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
- return INVALID_OPERATION; // create requires write
-
- if (flags & kOpenTruncate) {
- newArchive = true;
- } else {
- newArchive = (access(zipFileName, F_OK) != 0);
- if (!(flags & kOpenCreate) && newArchive) {
- /* not creating, must already exist */
- LOGD("File %s does not exist", zipFileName);
- return NAME_NOT_FOUND;
- }
- }
-
- /* open the file */
- const char* openflags;
- if (flags & kOpenReadWrite) {
- if (newArchive)
- openflags = FILE_OPEN_RW_CREATE;
- else
- openflags = FILE_OPEN_RW;
- } else {
- openflags = FILE_OPEN_RO;
- }
- mZipFp = fopen(zipFileName, openflags);
- if (mZipFp == NULL) {
- int err = errno;
- LOGD("fopen failed: %d\n", err);
- return errnoToStatus(err);
- }
-
- status_t result;
- if (!newArchive) {
- /*
- * Load the central directory. If that fails, then this probably
- * isn't a Zip archive.
- */
- result = readCentralDir();
- } else {
- /*
- * Newly-created. The EndOfCentralDir constructor actually
- * sets everything to be the way we want it (all zeroes). We
- * set mNeedCDRewrite so that we create *something* if the
- * caller doesn't add any files. (We could also just unlink
- * the file if it's brand new and nothing was added, but that's
- * probably doing more than we really should -- the user might
- * have a need for empty zip files.)
- */
- mNeedCDRewrite = true;
- result = NO_ERROR;
- }
-
- if (flags & kOpenReadOnly)
- mReadOnly = true;
- else
- assert(!mReadOnly);
-
- return result;
-}
-
-/*
- * Return the Nth entry in the archive.
- */
-ZipEntry* ZipFile::getEntryByIndex(int idx) const
-{
- if (idx < 0 || idx >= (int) mEntries.size())
- return NULL;
-
- return mEntries[idx];
-}
-
-/*
- * Find an entry by name.
- */
-ZipEntry* ZipFile::getEntryByName(const char* fileName) const
-{
- /*
- * Do a stupid linear string-compare search.
- *
- * There are various ways to speed this up, especially since it's rare
- * to intermingle changes to the archive with "get by name" calls. We
- * don't want to sort the mEntries vector itself, however, because
- * it's used to recreate the Central Directory.
- *
- * (Hash table works, parallel list of pointers in sorted order is good.)
- */
- int idx;
-
- for (idx = mEntries.size()-1; idx >= 0; idx--) {
- ZipEntry* pEntry = mEntries[idx];
- if (!pEntry->getDeleted() &&
- strcmp(fileName, pEntry->getFileName()) == 0)
- {
- return pEntry;
- }
- }
-
- return NULL;
-}
-
-/*
- * Empty the mEntries vector.
- */
-void ZipFile::discardEntries(void)
-{
- int count = mEntries.size();
-
- while (--count >= 0)
- delete mEntries[count];
-
- mEntries.clear();
-}
-
-
-/*
- * Find the central directory and read the contents.
- *
- * The fun thing about ZIP archives is that they may or may not be
- * readable from start to end. In some cases, notably for archives
- * that were written to stdout, the only length information is in the
- * central directory at the end of the file.
- *
- * Of course, the central directory can be followed by a variable-length
- * comment field, so we have to scan through it backwards. The comment
- * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
- * itself, plus apparently sometimes people throw random junk on the end
- * just for the fun of it.
- *
- * This is all a little wobbly. If the wrong value ends up in the EOCD
- * area, we're hosed. This appears to be the way that everbody handles
- * it though, so we're in pretty good company if this fails.
- */
-status_t ZipFile::readCentralDir(void)
-{
- status_t result = NO_ERROR;
- unsigned char* buf = NULL;
- off_t fileLength, seekStart;
- long readAmount;
- int i;
-
- fseek(mZipFp, 0, SEEK_END);
- fileLength = ftell(mZipFp);
- rewind(mZipFp);
-
- /* too small to be a ZIP archive? */
- if (fileLength < EndOfCentralDir::kEOCDLen) {
- LOGD("Length is %ld -- too small\n", (long)fileLength);
- result = INVALID_OPERATION;
- goto bail;
- }
-
- buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
- if (buf == NULL) {
- LOGD("Failure allocating %d bytes for EOCD search",
- EndOfCentralDir::kMaxEOCDSearch);
- result = NO_MEMORY;
- goto bail;
- }
-
- if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
- seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
- readAmount = EndOfCentralDir::kMaxEOCDSearch;
- } else {
- seekStart = 0;
- readAmount = (long) fileLength;
- }
- if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
- LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* read the last part of the file into the buffer */
- if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
- LOGD("short file? wanted %ld\n", readAmount);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* find the end-of-central-dir magic */
- for (i = readAmount - 4; i >= 0; i--) {
- if (buf[i] == 0x50 &&
- ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
- {
- LOGV("+++ Found EOCD at buf+%d\n", i);
- break;
- }
- }
- if (i < 0) {
- LOGD("EOCD not found, not Zip\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /* extract eocd values */
- result = mEOCD.readBuf(buf + i, readAmount - i);
- if (result != NO_ERROR) {
- LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
- goto bail;
- }
- //mEOCD.dump();
-
- if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
- mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
- {
- LOGD("Archive spanning not supported\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /*
- * So far so good. "mCentralDirSize" is the size in bytes of the
- * central directory, so we can just seek back that far to find it.
- * We can also seek forward mCentralDirOffset bytes from the
- * start of the file.
- *
- * We're not guaranteed to have the rest of the central dir in the
- * buffer, nor are we guaranteed that the central dir will have any
- * sort of convenient size. We need to skip to the start of it and
- * read the header, then the other goodies.
- *
- * The only thing we really need right now is the file comment, which
- * we're hoping to preserve.
- */
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- LOGD("Failure seeking to central dir offset %ld\n",
- mEOCD.mCentralDirOffset);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Loop through and read the central dir entries.
- */
- LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
- int entry;
- for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
- ZipEntry* pEntry = new ZipEntry;
-
- result = pEntry->initFromCDE(mZipFp);
- if (result != NO_ERROR) {
- LOGD("initFromCDE failed\n");
- delete pEntry;
- goto bail;
- }
-
- mEntries.add(pEntry);
- }
-
-
- /*
- * If all went well, we should now be back at the EOCD.
- */
- {
- unsigned char checkBuf[4];
- if (fread(checkBuf, 1, 4, mZipFp) != 4) {
- LOGD("EOCD check read failed\n");
- result = INVALID_OPERATION;
- goto bail;
- }
- if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
- LOGD("EOCD read check failed\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
- LOGV("+++ EOCD read check passed\n");
- }
-
-bail:
- delete[] buf;
- return result;
-}
-
-
-/*
- * Add a new file to the archive.
- *
- * This requires creating and populating a ZipEntry structure, and copying
- * the data into the file at the appropriate position. The "appropriate
- * position" is the current location of the central directory, which we
- * casually overwrite (we can put it back later).
- *
- * If we were concerned about safety, we would want to make all changes
- * in a temp file and then overwrite the original after everything was
- * safely written. Not really a concern for us.
- */
-status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result = NO_ERROR;
- long lfhPosn, startPosn, endPosn, uncompressedLen;
- FILE* inputFp = NULL;
- unsigned long crc;
- time_t modWhen;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- assert(compressionMethod == ZipEntry::kCompressDeflated ||
- compressionMethod == ZipEntry::kCompressStored);
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- /* make sure it doesn't already exist */
- if (getEntryByName(storageName) != NULL)
- return ALREADY_EXISTS;
-
- if (!data) {
- inputFp = fopen(fileName, FILE_OPEN_RO);
- if (inputFp == NULL)
- return errnoToStatus(errno);
- }
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- pEntry->initNew(storageName, NULL);
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH, even though it's still mostly blank. We need it
- * as a place-holder. In theory the LFH isn't necessary, but in
- * practice some utilities demand it.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
- startPosn = ftell(mZipFp);
-
- /*
- * Copy the data in, possibly compressing it as we go.
- */
- if (sourceType == ZipEntry::kCompressStored) {
- if (compressionMethod == ZipEntry::kCompressDeflated) {
- bool failed = false;
- result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
- if (result != NO_ERROR) {
- LOGD("compression failed, storing\n");
- failed = true;
- } else {
- /*
- * Make sure it has compressed "enough". This probably ought
- * to be set through an API call, but I don't expect our
- * criteria to change over time.
- */
- long src = inputFp ? ftell(inputFp) : size;
- long dst = ftell(mZipFp) - startPosn;
- if (dst + (dst / 10) > src) {
- LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
- src, dst);
- failed = true;
- }
- }
-
- if (failed) {
- compressionMethod = ZipEntry::kCompressStored;
- if (inputFp) rewind(inputFp);
- fseek(mZipFp, startPosn, SEEK_SET);
- /* fall through to kCompressStored case */
- }
- }
- /* handle "no compression" request, or failed compression from above */
- if (compressionMethod == ZipEntry::kCompressStored) {
- if (inputFp) {
- result = copyFpToFp(mZipFp, inputFp, &crc);
- } else {
- result = copyDataToFp(mZipFp, data, size, &crc);
- }
- if (result != NO_ERROR) {
- // don't need to truncate; happens in CDE rewrite
- LOGD("failed copying data in\n");
- goto bail;
- }
- }
-
- // currently seeked to end of file
- uncompressedLen = inputFp ? ftell(inputFp) : size;
- } else if (sourceType == ZipEntry::kCompressDeflated) {
- /* we should support uncompressed-from-compressed, but it's not
- * important right now */
- assert(compressionMethod == ZipEntry::kCompressDeflated);
-
- bool scanResult;
- int method;
- long compressedLen;
-
- scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
- &compressedLen, &crc);
- if (!scanResult || method != ZipEntry::kCompressDeflated) {
- LOGD("this isn't a deflated gzip file?");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
- if (result != NO_ERROR) {
- LOGD("failed copying gzip data in\n");
- goto bail;
- }
- } else {
- assert(false);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * We could write the "Data Descriptor", but there doesn't seem to
- * be any point since we're going to go back and write the LFH.
- *
- * Update file offsets.
- */
- endPosn = ftell(mZipFp); // seeked to end of compressed data
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
- compressionMethod);
- modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
- pEntry->setModWhen(modWhen);
- pEntry->setLFHOffset(lfhPosn);
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Go back and write the LFH.
- */
- if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Add pEntry to the list.
- */
- mEntries.add(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
-bail:
- if (inputFp != NULL)
- fclose(inputFp);
- delete pEntry;
- return result;
-}
-
-/*
- * Add an entry by copying it from another zip file. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
-status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- int padding, ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result;
- long lfhPosn, endPosn;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- if (pEntry == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
- if (result != NO_ERROR)
- goto bail;
- if (padding != 0) {
- result = pEntry->addPadding(padding);
- if (result != NO_ERROR)
- goto bail;
- }
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH. Since we're not recompressing the data, we already
- * have all of the fields filled out.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Copy the data over.
- *
- * If the "has data descriptor" flag is set, we want to copy the DD
- * fields as well. This is a fixed-size area immediately following
- * the data.
- */
- if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- off_t copyLen;
- copyLen = pSourceEntry->getCompressedLen();
- if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
- copyLen += ZipEntry::kDataDescriptorLen;
-
- if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
- != NO_ERROR)
- {
- LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Update file offsets.
- */
- endPosn = ftell(mZipFp);
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Add pEntry to the list.
- */
- mEntries.add(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
- result = NO_ERROR;
-
-bail:
- delete pEntry;
- return result;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data.
- */
-status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (1) {
- count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
- if (ferror(srcFp) || ferror(dstFp))
- return errnoToStatus(errno);
- if (count == 0)
- break;
-
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "dstFp" will be seeked immediately past the data.
- */
-status_t ZipFile::copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- size_t count;
-
- *pCRC32 = crc32(0L, Z_NULL, 0);
- if (size > 0) {
- *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
- if (fwrite(data, 1, size, dstFp) != size) {
- LOGD("fwrite %d bytes failed\n", (int) size);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy some of the bytes in "src" to "dst".
- *
- * If "pCRC32" is NULL, the CRC will not be computed.
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data just written.
- */
-status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (length) {
- long readSize;
-
- readSize = sizeof(tmpBuf);
- if (readSize > length)
- readSize = length;
-
- count = fread(tmpBuf, 1, readSize, srcFp);
- if ((long) count != readSize) { // error or unexpected EOF
- LOGD("fread %d bytes failed\n", (int) readSize);
- return UNKNOWN_ERROR;
- }
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
-
- length -= readSize;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Compress all of the data in "srcFp" and write it to "dstFp".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the compressed data.
- */
-status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- status_t result = NO_ERROR;
- const size_t kBufSize = 32768;
- unsigned char* inBuf = NULL;
- unsigned char* outBuf = NULL;
- z_stream zstream;
- bool atEof = false; // no feof() aviailable yet
- unsigned long crc;
- int zerr;
-
- /*
- * Create an input buffer and an output buffer.
- */
- inBuf = new unsigned char[kBufSize];
- outBuf = new unsigned char[kBufSize];
- if (inBuf == NULL || outBuf == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- /*
- * Initialize the zlib stream.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- zstream.data_type = Z_UNKNOWN;
-
- zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
- Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
- if (zerr != Z_OK) {
- result = UNKNOWN_ERROR;
- if (zerr == Z_VERSION_ERROR) {
- LOGE("Installed zlib is not compatible with linked version (%s)\n",
- ZLIB_VERSION);
- } else {
- LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
- }
- goto bail;
- }
-
- crc = crc32(0L, Z_NULL, 0);
-
- /*
- * Loop while we have data.
- */
- do {
- size_t getSize;
- int flush;
-
- /* only read if the input buffer is empty */
- if (zstream.avail_in == 0 && !atEof) {
- LOGV("+++ reading %d bytes\n", (int)kBufSize);
- if (data) {
- getSize = size > kBufSize ? kBufSize : size;
- memcpy(inBuf, data, getSize);
- data = ((const char*)data) + getSize;
- size -= getSize;
- } else {
- getSize = fread(inBuf, 1, kBufSize, srcFp);
- if (ferror(srcFp)) {
- LOGD("deflate read failed (errno=%d)\n", errno);
- goto z_bail;
- }
- }
- if (getSize < kBufSize) {
- LOGV("+++ got %d bytes, EOF reached\n",
- (int)getSize);
- atEof = true;
- }
-
- crc = crc32(crc, inBuf, getSize);
-
- zstream.next_in = inBuf;
- zstream.avail_in = getSize;
- }
-
- if (atEof)
- flush = Z_FINISH; /* tell zlib that we're done */
- else
- flush = Z_NO_FLUSH; /* more to come! */
-
- zerr = deflate(&zstream, flush);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
- result = UNKNOWN_ERROR;
- goto z_bail;
- }
-
- /* write when we're full or when we're done */
- if (zstream.avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
- {
- LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
- if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
- (size_t)(zstream.next_out - outBuf))
- {
- LOGD("write %d failed in deflate\n",
- (int) (zstream.next_out - outBuf));
- goto z_bail;
- }
-
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
-
- *pCRC32 = crc;
-
-z_bail:
- deflateEnd(&zstream); /* free up any allocated structures */
-
-bail:
- delete[] inBuf;
- delete[] outBuf;
-
- return result;
-}
-
-/*
- * Mark an entry as deleted.
- *
- * We will eventually need to crunch the file down, but if several files
- * are being removed (perhaps as part of an "update" process) we can make
- * things considerably faster by deferring the removal to "flush" time.
- */
-status_t ZipFile::remove(ZipEntry* pEntry)
-{
- /*
- * Should verify that pEntry is actually part of this archive, and
- * not some stray ZipEntry from a different file.
- */
-
- /* mark entry as deleted, and mark archive as dirty */
- pEntry->setDeleted();
- mNeedCDRewrite = true;
- return NO_ERROR;
-}
-
-/*
- * Flush any pending writes.
- *
- * In particular, this will crunch out deleted entries, and write the
- * Central Directory and EOCD if we have stomped on them.
- */
-status_t ZipFile::flush(void)
-{
- status_t result = NO_ERROR;
- long eocdPosn;
- int i, count;
-
- if (mReadOnly)
- return INVALID_OPERATION;
- if (!mNeedCDRewrite)
- return NO_ERROR;
-
- assert(mZipFp != NULL);
-
- result = crunchArchive();
- if (result != NO_ERROR)
- return result;
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- count = mEntries.size();
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- pEntry->mCDE.write(mZipFp);
- }
-
- eocdPosn = ftell(mZipFp);
- mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
-
- mEOCD.write(mZipFp);
-
- /*
- * If we had some stuff bloat up during compression and get replaced
- * with plain files, or if we deleted some entries, there's a lot
- * of wasted space at the end of the file. Remove it now.
- */
- if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
- LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
- // not fatal
- }
-
- /* should we clear the "newly added" flag in all entries now? */
-
- mNeedCDRewrite = false;
- return NO_ERROR;
-}
-
-/*
- * Crunch deleted files out of an archive by shifting the later files down.
- *
- * Because we're not using a temp file, we do the operation inside the
- * current file.
- */
-status_t ZipFile::crunchArchive(void)
-{
- status_t result = NO_ERROR;
- int i, count;
- long delCount, adjust;
-
-#if 0
- printf("CONTENTS:\n");
- for (i = 0; i < (int) mEntries.size(); i++) {
- printf(" %d: lfhOff=%ld del=%d\n",
- i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
- }
- printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
-#endif
-
- /*
- * Roll through the set of files, shifting them as appropriate. We
- * could probably get a slight performance improvement by sliding
- * multiple files down at once (because we could use larger reads
- * when operating on batches of small files), but it's not that useful.
- */
- count = mEntries.size();
- delCount = adjust = 0;
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- long span;
-
- if (pEntry->getLFHOffset() != 0) {
- long nextOffset;
-
- /* Get the length of this entry by finding the offset
- * of the next entry. Directory entries don't have
- * file offsets, so we need to find the next non-directory
- * entry.
- */
- nextOffset = 0;
- for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
- nextOffset = mEntries[ii]->getLFHOffset();
- if (nextOffset == 0)
- nextOffset = mEOCD.mCentralDirOffset;
- span = nextOffset - pEntry->getLFHOffset();
-
- assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
- } else {
- /* This is a directory entry. It doesn't have
- * any actual file contents, so there's no need to
- * move anything.
- */
- span = 0;
- }
-
- //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
- // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
-
- if (pEntry->getDeleted()) {
- adjust += span;
- delCount++;
-
- delete pEntry;
- mEntries.removeAt(i);
-
- /* adjust loop control */
- count--;
- i--;
- } else if (span != 0 && adjust > 0) {
- /* shuffle this entry back */
- //printf("+++ Shuffling '%s' back %ld\n",
- // pEntry->getFileName(), adjust);
- result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
- pEntry->getLFHOffset(), span);
- if (result != NO_ERROR) {
- /* this is why you use a temp file */
- LOGE("error during crunch - archive is toast\n");
- return result;
- }
-
- pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
- }
- }
-
- /*
- * Fix EOCD info. We have to wait until the end to do some of this
- * because we use mCentralDirOffset to determine "span" for the
- * last entry.
- */
- mEOCD.mCentralDirOffset -= adjust;
- mEOCD.mNumEntries -= delCount;
- mEOCD.mTotalNumEntries -= delCount;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
-
- assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
- assert(mEOCD.mNumEntries == count);
-
- return result;
-}
-
-/*
- * Works like memmove(), but on pieces of a file.
- */
-status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
-{
- if (dst == src || n <= 0)
- return NO_ERROR;
-
- unsigned char readBuf[32768];
-
- if (dst < src) {
- /* shift stuff toward start of file; must read from start */
- while (n != 0) {
- size_t getSize = sizeof(readBuf);
- if (getSize > n)
- getSize = n;
-
- if (fseek(fp, (long) src, SEEK_SET) != 0) {
- LOGD("filemove src seek %ld failed\n", (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fread(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove read %ld off=%ld failed\n",
- (long) getSize, (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fseek(fp, (long) dst, SEEK_SET) != 0) {
- LOGD("filemove dst seek %ld failed\n", (long) dst);
- return UNKNOWN_ERROR;
- }
-
- if (fwrite(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove write %ld off=%ld failed\n",
- (long) getSize, (long) dst);
- return UNKNOWN_ERROR;
- }
-
- src += getSize;
- dst += getSize;
- n -= getSize;
- }
- } else {
- /* shift stuff toward end of file; must read from end */
- assert(false); // write this someday, maybe
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Get the modification time from a file descriptor.
- */
-time_t ZipFile::getModTime(int fd)
-{
- struct stat sb;
-
- if (fstat(fd, &sb) < 0) {
- LOGD("HEY: fstat on fd %d failed\n", fd);
- return (time_t) -1;
- }
-
- return sb.st_mtime;
-}
-
-
-#if 0 /* this is a bad idea */
-/*
- * Get a copy of the Zip file descriptor.
- *
- * We don't allow this if the file was opened read-write because we tend
- * to leave the file contents in an uncertain state between calls to
- * flush(). The duplicated file descriptor should only be valid for reads.
- */
-int ZipFile::getZipFd(void) const
-{
- if (!mReadOnly)
- return INVALID_OPERATION;
- assert(mZipFp != NULL);
-
- int fd;
- fd = dup(fileno(mZipFp));
- if (fd < 0) {
- LOGD("didn't work, errno=%d\n", errno);
- }
-
- return fd;
-}
-#endif
-
-
-#if 0
-/*
- * Expand data.
- */
-bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
-{
- return false;
-}
-#endif
-
-// free the memory when you're done
-void* ZipFile::uncompress(const ZipEntry* entry)
-{
- size_t unlen = entry->getUncompressedLen();
- size_t clen = entry->getCompressedLen();
-
- void* buf = malloc(unlen);
- if (buf == NULL) {
- return NULL;
- }
-
- fseek(mZipFp, 0, SEEK_SET);
-
- off_t offset = entry->getFileOffset();
- if (fseek(mZipFp, offset, SEEK_SET) != 0) {
- goto bail;
- }
-
- switch (entry->getCompressionMethod())
- {
- case ZipEntry::kCompressStored: {
- ssize_t amt = fread(buf, 1, unlen, mZipFp);
- if (amt != (ssize_t)unlen) {
- goto bail;
- }
-#if 0
- printf("data...\n");
- const unsigned char* p = (unsigned char*)buf;
- const unsigned char* end = p+unlen;
- for (int i=0; i<32 && p < end; i++) {
- printf("0x%08x ", (int)(offset+(i*0x10)));
- for (int j=0; j<0x10 && p < end; j++) {
- printf(" %02x", *p);
- p++;
- }
- printf("\n");
- }
-#endif
-
- }
- break;
- case ZipEntry::kCompressDeflated: {
- if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
- goto bail;
- }
- }
- break;
- default:
- goto bail;
- }
- return buf;
-
-bail:
- free(buf);
- return NULL;
-}
-
-
-/*
- * ===========================================================================
- * ZipFile::EndOfCentralDir
- * ===========================================================================
- */
-
-/*
- * Read the end-of-central-dir fields.
- *
- * "buf" should be positioned at the EOCD signature, and should contain
- * the entire EOCD area including the comment.
- */
-status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
-{
- /* don't allow re-use */
- assert(mComment == NULL);
-
- if (len < kEOCDLen) {
- /* looks like ZIP file got truncated */
- LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
- kEOCDLen, len);
- return INVALID_OPERATION;
- }
-
- /* this should probably be an assert() */
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
- return UNKNOWN_ERROR;
-
- mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
- mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
- mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
- mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
- mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
- mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
- mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
-
- // TODO: validate mCentralDirOffset
-
- if (mCommentLen > 0) {
- if (kEOCDLen + mCommentLen > len) {
- LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
- kEOCDLen, mCommentLen, len);
- return UNKNOWN_ERROR;
- }
- mComment = new unsigned char[mCommentLen];
- memcpy(mComment, buf + kEOCDLen, mCommentLen);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Write an end-of-central-directory section.
- */
-status_t ZipFile::EndOfCentralDir::write(FILE* fp)
-{
- unsigned char buf[kEOCDLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
- ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
- ZipEntry::putShortLE(&buf[0x08], mNumEntries);
- ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
- ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
- ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
- ZipEntry::putShortLE(&buf[0x14], mCommentLen);
-
- if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
- return UNKNOWN_ERROR;
- if (mCommentLen > 0) {
- assert(mComment != NULL);
- if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of an EndOfCentralDir object.
- */
-void ZipFile::EndOfCentralDir::dump(void) const
-{
- LOGD(" EndOfCentralDir contents:\n");
- LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
- mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
- LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
- mCentralDirSize, mCentralDirOffset, mCommentLen);
-}
-
diff --git a/libs/utils/executablepath_darwin.cpp b/libs/utils/executablepath_darwin.cpp
deleted file mode 100644
index 2e3c3a0..0000000
--- a/libs/utils/executablepath_darwin.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/executablepath.h>
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-void executablepath(char s[PATH_MAX])
-{
- ProcessSerialNumber psn;
- GetCurrentProcess(&psn);
- CFDictionaryRef dict;
- dict = ProcessInformationCopyDictionary(&psn, 0xffffffff);
- CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict,
- CFSTR("CFBundleExecutable"));
- CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8);
-}
-
diff --git a/libs/utils/executablepath_linux.cpp b/libs/utils/executablepath_linux.cpp
deleted file mode 100644
index b8d2a3d..0000000
--- a/libs/utils/executablepath_linux.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <utils/executablepath.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <limits.h>
-#include <stdio.h>
-
-void executablepath(char exe[PATH_MAX])
-{
- char proc[100];
- sprintf(proc, "/proc/%d/exe", getpid());
-
- int err = readlink(proc, exe, PATH_MAX);
-}
-
diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c
deleted file mode 100644
index ab48c69..0000000
--- a/libs/utils/futex_synchro.c
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <limits.h>
-
-#include <sys/time.h>
-#include <sched.h>
-
-#include <errno.h>
-
-#include <private/utils/futex_synchro.h>
-
-
-// This futex glue code is need on desktop linux, but is already part of bionic.
-#if !defined(HAVE_FUTEX_WRAPPERS)
-
-#include <unistd.h>
-#include <sys/syscall.h>
-typedef unsigned int u32;
-#define asmlinkage
-#define __user
-#include <linux/futex.h>
-#include <utils/Atomic.h>
-
-
-int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3)
-{
- int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3);
- return err == 0 ? 0 : -errno;
-}
-
-int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout)
-{
- return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0);
-}
-
-int __futex_wake(volatile void *ftx, int count)
-{
- return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0);
-}
-
-int __atomic_cmpxchg(int old, int _new, volatile int *ptr)
-{
- return android_atomic_cmpxchg(old, _new, ptr);
-}
-
-int __atomic_swap(int _new, volatile int *ptr)
-{
- return android_atomic_swap(_new, ptr);
-}
-
-int __atomic_dec(volatile int *ptr)
-{
- return android_atomic_dec(ptr);
-}
-
-#else // !defined(__arm__)
-
-int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout);
-int __futex_wake(volatile void *ftx, int count);
-
-int __atomic_cmpxchg(int old, int _new, volatile int *ptr);
-int __atomic_swap(int _new, volatile int *ptr);
-int __atomic_dec(volatile int *ptr);
-
-#endif // !defined(HAVE_FUTEX_WRAPPERS)
-
-
-// lock states
-//
-// 0: unlocked
-// 1: locked, no waiters
-// 2: locked, maybe waiters
-
-void futex_mutex_init(futex_mutex_t *m)
-{
- m->value = 0;
-}
-
-int futex_mutex_lock(futex_mutex_t *m, unsigned msec)
-{
- if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
- return 0;
- }
- if(msec == FUTEX_WAIT_INFINITE) {
- while(__atomic_swap(2, &m->value) != 0) {
- __futex_wait(&m->value, 2, 0);
- }
- } else {
- struct timespec ts;
- ts.tv_sec = msec / 1000;
- ts.tv_nsec = (msec % 1000) * 1000000;
- while(__atomic_swap(2, &m->value) != 0) {
- if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) {
- return -1;
- }
- }
- }
- return 0;
-}
-
-int futex_mutex_trylock(futex_mutex_t *m)
-{
- if(__atomic_cmpxchg(0, 1, &m->value) == 0) {
- return 0;
- }
- return -1;
-}
-
-void futex_mutex_unlock(futex_mutex_t *m)
-{
- if(__atomic_dec(&m->value) != 1) {
- m->value = 0;
- __futex_wake(&m->value, 1);
- }
-}
-
-/* XXX *technically* there is a race condition that could allow
- * XXX a signal to be missed. If thread A is preempted in _wait()
- * XXX after unlocking the mutex and before waiting, and if other
- * XXX threads call signal or broadcast UINT_MAX times (exactly),
- * XXX before thread A is scheduled again and calls futex_wait(),
- * XXX then the signal will be lost.
- */
-
-void futex_cond_init(futex_cond_t *c)
-{
- c->value = 0;
-}
-
-int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec)
-{
- if(msec == FUTEX_WAIT_INFINITE){
- int oldvalue = c->value;
- futex_mutex_unlock(m);
- __futex_wait(&c->value, oldvalue, 0);
- futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
- return 0;
- } else {
- int oldvalue = c->value;
- struct timespec ts;
- ts.tv_sec = msec / 1000;
- ts.tv_nsec = (msec % 1000) * 1000000;
- futex_mutex_unlock(m);
- const int err = __futex_wait(&c->value, oldvalue, &ts);
- futex_mutex_lock(m, FUTEX_WAIT_INFINITE);
- return err;
- }
-}
-
-void futex_cond_signal(futex_cond_t *c)
-{
- __atomic_dec(&c->value);
- __futex_wake(&c->value, 1);
-}
-
-void futex_cond_broadcast(futex_cond_t *c)
-{
- __atomic_dec(&c->value);
- __futex_wake(&c->value, INT_MAX);
-}
-
diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp
deleted file mode 100644
index 656e46f..0000000
--- a/libs/utils/ported.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Ports of standard functions that don't exist on a specific platform.
-//
-// Note these are NOT in the "android" namespace.
-//
-#include <utils/ported.h>
-
-#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP)
-# include <sys/time.h>
-# include <windows.h>
-#endif
-
-
-#if defined(NEED_GETTIMEOFDAY)
-/*
- * Replacement gettimeofday() for Windows environments (primarily MinGW).
- *
- * Ignores "tz".
- */
-int gettimeofday(struct timeval* ptv, struct timezone* tz)
-{
- long long nsTime; // time in 100ns units since Jan 1 1601
- FILETIME ft;
-
- if (tz != NULL) {
- // oh well
- }
-
- ::GetSystemTimeAsFileTime(&ft);
- nsTime = (long long) ft.dwHighDateTime << 32 |
- (long long) ft.dwLowDateTime;
- // convert to time in usec since Jan 1 1970
- ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL);
- ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL);
-
- return 0;
-}
-#endif
-
-#if defined(NEED_USLEEP)
-//
-// Replacement usleep for Windows environments (primarily MinGW).
-//
-void usleep(unsigned long usec)
-{
- // Win32 API function Sleep() takes milliseconds
- ::Sleep((usec + 500) / 1000);
-}
-#endif
-
-#if 0 //defined(NEED_PIPE)
-//
-// Replacement pipe() command for MinGW
-//
-// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the
-// SecurityAttributes argument to CreatePipe(). This means the handles
-// aren't inherited when a new process is created. The examples I've seen
-// use it, possibly because there's a lot of junk going on behind the
-// scenes. (I'm assuming "process" and "thread" are different here, so
-// we should be okay spinning up a thread.) The recommended practice is
-// to dup() the descriptor you want the child to have.
-//
-// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O.
-// You can't use select() either, since that only works on sockets. The
-// Windows API calls that are useful here all operate on a HANDLE, not
-// an integer file descriptor, and I don't think you can get there from
-// here. The "named pipe" stuff is insane.
-//
-int pipe(int filedes[2])
-{
- return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT);
-}
-#endif
-
-#if defined(NEED_SETENV)
-/*
- * MinGW lacks these. For now, just stub them out so the code compiles.
- */
-int setenv(const char* name, const char* value, int overwrite)
-{
- return 0;
-}
-void unsetenv(const char* name)
-{
-}
-char* getenv(const char* name)
-{
- return NULL;
-}
-#endif
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index 53e46b7..2ce1273 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -36,11 +36,11 @@ import java.util.List;
* coordinate into a (partial) address. The amount of detail in a
* reverse geocoded location description may vary, for example one
* might contain the full street address of the closest building, while
- * another might contain only a city name and postal code.
+ * another might contain only a city name and postal code.
*
* The Geocoder class requires a backend service that is not included in
- * the core android framework. The Geocoder query methods will return an
- * empty list if there no backend service in the platform.
+ * the core android framework. The Geocoder query methods will return an
+ * empty list if there no backend service in the platform.
*/
public final class Geocoder {
private static final String TAG = "Geocoder";
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 2cda7fa..ce69ac1 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -115,6 +115,18 @@ public final class GpsStatus {
void onGpsStatusChanged(int event);
}
+ /**
+ * Used for receiving NMEA sentences from the GPS.
+ * NMEA 0183 is a standard for communicating with marine electronic devices
+ * and is a common method for receiving data from a GPS, typically over a serial port.
+ * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+ * You can implement this interface and call {@link LocationManager#addNmeaListener}
+ * to receive NMEA data from the GPS engine.
+ */
+ public interface NmeaListener {
+ void onNmeaReceived(long timestamp, String nmea);
+ }
+
GpsStatus() {
for (int i = 0; i < mSatellites.length; i++) {
mSatellites[i] = new GpsSatellite(i + 1);
diff --git a/location/java/android/location/IGpsStatusListener.aidl b/location/java/android/location/IGpsStatusListener.aidl
index 5dc0fe8..62b1c6b 100644
--- a/location/java/android/location/IGpsStatusListener.aidl
+++ b/location/java/android/location/IGpsStatusListener.aidl
@@ -29,4 +29,5 @@ oneway interface IGpsStatusListener
void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs,
in float[] elevations, in float[] azimuths,
int ephemerisMask, int almanacMask, int usedInFixMask);
+ void onNmeaReceived(long timestamp, String nmea);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index caf9516..b6c59d6 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -83,4 +83,7 @@ interface ILocationManager
/* for installing external Location Providers */
void installLocationProvider(String name, ILocationProvider provider);
void installGeocodeProvider(IGeocodeProvider provider);
+
+ // for NI support
+ boolean sendNiResponse(int notifId, int userResponse);
}
diff --git a/location/java/android/location/INetInitiatedListener.aidl b/location/java/android/location/INetInitiatedListener.aidl
new file mode 100755
index 0000000..f2f5a32
--- /dev/null
+++ b/location/java/android/location/INetInitiatedListener.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.location;
+
+/**
+ * {@hide}
+ */
+interface INetInitiatedListener
+{
+ boolean sendNiResponse(int notifId, int userResponse);
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 86ea66f..94ced22 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -51,6 +51,8 @@ public class LocationManager {
private ILocationManager mService;
private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
+ private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
+ new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
private final GpsStatus mGpsStatus = new GpsStatus();
/**
@@ -68,7 +70,7 @@ public class LocationManager {
* satellites. Depending on conditions, this provider may take a while to return
* a location fix.
*
- * Requires the permission android.permissions.ACCESS_FINE_LOCATION.
+ * Requires the permission android.permission.ACCESS_FINE_LOCATION.
*
* <p> The extras Bundle for the GPS location provider can contain the
* following key/value pairs:
@@ -103,9 +105,6 @@ public class LocationManager {
*/
public static final String KEY_LOCATION_CHANGED = "location";
- /** @hide */
- public static final String SYSTEM_DIR = "/data/system/location";
-
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap<LocationListener,ListenerTransport> mListeners =
new HashMap<LocationListener,ListenerTransport>();
@@ -1123,49 +1122,103 @@ public class LocationManager {
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
private final GpsStatus.Listener mListener;
+ private final GpsStatus.NmeaListener mNmeaListener;
+
+ // This must not equal any of the GpsStatus event IDs
+ private static final int NMEA_RECEIVED = 1000;
+
+ private class Nmea {
+ long mTimestamp;
+ String mNmea;
+
+ Nmea(long timestamp, String nmea) {
+ mTimestamp = timestamp;
+ mNmea = nmea;
+ }
+ }
+ private ArrayList<Nmea> mNmeaBuffer;
GpsStatusListenerTransport(GpsStatus.Listener listener) {
mListener = listener;
+ mNmeaListener = null;
+ }
+
+ GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
+ mNmeaListener = listener;
+ mListener = null;
+ mNmeaBuffer = new ArrayList<Nmea>();
}
public void onGpsStarted() {
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_STARTED;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STARTED;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onGpsStopped() {
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_STOPPED;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STOPPED;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onFirstFix(int ttff) {
- mGpsStatus.setTimeToFirstFix(ttff);
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
- mGpsHandler.sendMessage(msg);
+ if (mListener != null) {
+ mGpsStatus.setTimeToFirstFix(ttff);
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
+ mGpsHandler.sendMessage(msg);
+ }
}
public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
float[] elevations, float[] azimuths, int ephemerisMask,
int almanacMask, int usedInFixMask) {
- mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
- ephemerisMask, almanacMask, usedInFixMask);
+ if (mListener != null) {
+ mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
+ ephemerisMask, almanacMask, usedInFixMask);
+
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
+ // remove any SV status messages already in the queue
+ mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ mGpsHandler.sendMessage(msg);
+ }
+ }
- Message msg = Message.obtain();
- msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
- // remove any SV status messages already in the queue
- mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
- mGpsHandler.sendMessage(msg);
+ public void onNmeaReceived(long timestamp, String nmea) {
+ if (mNmeaListener != null) {
+ synchronized (mNmeaBuffer) {
+ mNmeaBuffer.add(new Nmea(timestamp, nmea));
+ }
+ Message msg = Message.obtain();
+ msg.what = NMEA_RECEIVED;
+ // remove any NMEA_RECEIVED messages already in the queue
+ mGpsHandler.removeMessages(NMEA_RECEIVED);
+ mGpsHandler.sendMessage(msg);
+ }
}
private final Handler mGpsHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- // synchronize on mGpsStatus to ensure the data is copied atomically.
- synchronized(mGpsStatus) {
- mListener.onGpsStatusChanged(msg.what);
+ if (msg.what == NMEA_RECEIVED) {
+ synchronized (mNmeaBuffer) {
+ int length = mNmeaBuffer.size();
+ for (int i = 0; i < length; i++) {
+ Nmea nmea = mNmeaBuffer.get(i);
+ mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
+ }
+ mNmeaBuffer.clear();
+ }
+ } else {
+ // synchronize on mGpsStatus to ensure the data is copied atomically.
+ synchronized(mGpsStatus) {
+ mListener.onGpsStatusChanged(msg.what);
+ }
}
}
};
@@ -1217,6 +1270,52 @@ public class LocationManager {
}
}
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {#link GpsStatus.NmeaListener} object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
+ boolean result;
+
+ if (mNmeaListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
+ result = mService.addGpsStatusListener(transport);
+ if (result) {
+ mNmeaListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
+ result = false;
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes an NMEA listener.
+ *
+ * @param listener a {#link GpsStatus.NmeaListener} object to remove
+ */
+ public void removeNmeaListener(GpsStatus.NmeaListener listener) {
+ try {
+ GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
+ if (transport != null) {
+ mService.removeGpsStatusListener(transport);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
+ }
+ }
+
/**
* Retrieves information about the current status of the GPS engine.
* This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
@@ -1315,4 +1414,20 @@ public class LocationManager {
Log.e(TAG, "RemoteException in reportLocation: ", e);
}
}
+
+ /**
+ * Used by NetInitiatedActivity to report user response
+ * for network initiated GPS fix requests.
+ *
+ * {@hide}
+ */
+ public boolean sendNiResponse(int notifId, int userResponse) {
+ try {
+ return mService.sendNiResponse(notifId, userResponse);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in sendNiResponse: ", e);
+ return false;
+ }
+ }
+
}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 4a51e31..bfa0671 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -27,6 +27,7 @@ import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
import android.location.ILocationProvider;
+import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
@@ -38,6 +39,7 @@ import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Config;
import android.util.Log;
import android.util.SparseIntArray;
@@ -45,14 +47,18 @@ import android.util.SparseIntArray;
import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.StringBufferInputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Properties;
+import java.util.Map.Entry;
/**
* A GPS implementation of LocationProvider used by LocationManager.
@@ -183,8 +189,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// number of fixes we have received since we started navigating
private int mFixCount;
- private int mPositionMode = GPS_POSITION_MODE_STANDALONE;
-
// true if we started navigation
private boolean mStarted;
@@ -198,6 +202,10 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// properties loaded from PROPERTIES_FILE
private Properties mProperties;
private String mNtpServer;
+ private String mSuplServerHost;
+ private int mSuplServerPort;
+ private String mC2KServerHost;
+ private int mC2KServerPort;
private final Context mContext;
private final ILocationManager mLocationManager;
@@ -211,6 +219,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private String mAGpsApn;
private int mAGpsDataConnectionState;
private final ConnectivityManager mConnMgr;
+ private final GpsNetInitiatedHandler mNIHandler;
// Wakelocks
private final static String WAKELOCK_KEY = "GpsLocationProvider";
@@ -321,6 +330,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
public GpsLocationProvider(Context context, ILocationManager locationManager) {
mContext = context;
mLocationManager = locationManager;
+ mNIHandler= new GpsNetInitiatedHandler(context, this);
// Create a wake lock
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -349,27 +359,21 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
stream.close();
mNtpServer = mProperties.getProperty("NTP_SERVER", null);
- String host = mProperties.getProperty("SUPL_HOST");
+ mSuplServerHost = mProperties.getProperty("SUPL_HOST");
String portString = mProperties.getProperty("SUPL_PORT");
- if (host != null && portString != null) {
+ if (mSuplServerHost != null && portString != null) {
try {
- int port = Integer.parseInt(portString);
- native_set_agps_server(AGPS_TYPE_SUPL, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mSuplServerPort = Integer.parseInt(portString);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
}
}
- host = mProperties.getProperty("C2K_HOST");
+ mC2KServerHost = mProperties.getProperty("C2K_HOST");
portString = mProperties.getProperty("C2K_PORT");
- if (host != null && portString != null) {
+ if (mC2KServerHost != null && portString != null) {
try {
- int port = Integer.parseInt(portString);
- native_set_agps_server(AGPS_TYPE_C2K, host, port);
- // use MS-Based position mode if SUPL support is enabled
- mPositionMode = GPS_POSITION_MODE_MS_BASED;
+ mC2KServerPort = Integer.parseInt(portString);
} catch (NumberFormatException e) {
Log.e(TAG, "unable to parse C2K_PORT: " + portString);
}
@@ -499,6 +503,13 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
mEnabled = native_init();
if (mEnabled) {
+ if (mSuplServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
+ }
+ if (mC2KServerHost != null) {
+ native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
+ }
+
// run event listener thread while we are enabled
mEventThread = new GpsEventThread();
mEventThread.start();
@@ -722,7 +733,15 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
mStarted = true;
- if (!native_start(mPositionMode, false, mFixInterval)) {
+ int positionMode;
+ if (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
+ positionMode = GPS_POSITION_MODE_MS_BASED;
+ } else {
+ positionMode = GPS_POSITION_MODE_STANDALONE;
+ }
+
+ if (!native_start(positionMode, false, mFixInterval)) {
mStarted = false;
Log.e(TAG, "native_start failed in startNavigating()");
return;
@@ -1002,6 +1021,32 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
}
}
+ /**
+ * called from native code to report NMEA data received
+ */
+ private void reportNmea(int index, long timestamp) {
+ synchronized(mListeners) {
+ int size = mListeners.size();
+ if (size > 0) {
+ // don't bother creating the String if we have no listeners
+ int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length);
+ String nmea = new String(mNmeaBuffer, 0, length);
+
+ for (int i = 0; i < size; i++) {
+ Listener listener = mListeners.get(i);
+ try {
+ listener.mListener.onNmeaReceived(timestamp, nmea);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportNmea");
+ mListeners.remove(listener);
+ // adjust for size of list changing
+ size--;
+ }
+ }
+ }
+ }
+ }
+
private void xtraDownloadRequest() {
if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
if (mNetworkThread != null) {
@@ -1009,6 +1054,96 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
}
}
+ //=============================================================
+ // NI Client support
+ //=============================================================
+ private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
+ // Sends a response for an NI reqeust to HAL.
+ public boolean sendNiResponse(int notificationId, int userResponse)
+ {
+ // TODO Add Permission check
+
+ StringBuilder extrasBuf = new StringBuilder();
+
+ if (Config.LOGD) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
+ ", response: " + userResponse);
+
+ native_send_ni_response(notificationId, userResponse);
+
+ return true;
+ }
+ };
+
+ public INetInitiatedListener getNetInitiatedListener() {
+ return mNetInitiatedListener;
+ }
+
+ // Called by JNI function to report an NI request.
+ @SuppressWarnings("deprecation")
+ public void reportNiNotification(
+ int notificationId,
+ int niType,
+ int notifyFlags,
+ int timeout,
+ int defaultResponse,
+ String requestorId,
+ String text,
+ int requestorIdEncoding,
+ int textEncoding,
+ String extras // Encoded extra data
+ )
+ {
+ Log.i(TAG, "reportNiNotification: entered");
+ Log.i(TAG, "notificationId: " + notificationId +
+ ", niType: " + niType +
+ ", notifyFlags: " + notifyFlags +
+ ", timeout: " + timeout +
+ ", defaultResponse: " + defaultResponse);
+
+ Log.i(TAG, "requestorId: " + requestorId +
+ ", text: " + text +
+ ", requestorIdEncoding: " + requestorIdEncoding +
+ ", textEncoding: " + textEncoding);
+
+ GpsNiNotification notification = new GpsNiNotification();
+
+ notification.notificationId = notificationId;
+ notification.niType = niType;
+ notification.needNotify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_NOTIFY) != 0;
+ notification.needVerify = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_NEED_VERIFY) != 0;
+ notification.privacyOverride = (notifyFlags & GpsNetInitiatedHandler.GPS_NI_PRIVACY_OVERRIDE) != 0;
+ notification.timeout = timeout;
+ notification.defaultResponse = defaultResponse;
+ notification.requestorId = requestorId;
+ notification.text = text;
+ notification.requestorIdEncoding = requestorIdEncoding;
+ notification.textEncoding = textEncoding;
+
+ // Process extras, assuming the format is
+ // one of more lines of "key = value"
+ Bundle bundle = new Bundle();
+
+ if (extras == null) extras = "";
+ Properties extraProp = new Properties();
+
+ try {
+ extraProp.load(new StringBufferInputStream(extras));
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "reportNiNotification cannot parse extras data: " + extras);
+ }
+
+ for (Entry<Object, Object> ent : extraProp.entrySet())
+ {
+ bundle.putString((String) ent.getKey(), (String) ent.getValue());
+ }
+
+ notification.extras = bundle;
+
+ mNIHandler.handleNiNotification(notification);
+ }
+
private class GpsEventThread extends Thread {
public GpsEventThread() {
@@ -1182,6 +1317,8 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private float mSvAzimuths[] = new float[MAX_SVS];
private int mSvMasks[] = new int[3];
private int mSvCount;
+ // preallocated to avoid memory allocation in reportNmea()
+ private byte[] mNmeaBuffer = new byte[120];
static { class_init_native(); }
private static native void class_init_native();
@@ -1199,6 +1336,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] svs, float[] snrs,
float[] elevations, float[] azimuths, int[] masks);
+ private native int native_read_nmea(int index, byte[] buffer, int bufferSize);
private native void native_inject_location(double latitude, double longitude, float accuracy);
// XTRA Support
@@ -1211,4 +1349,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
private native void native_agps_data_conn_closed();
private native void native_agps_data_conn_failed();
private native void native_set_agps_server(int type, String hostname, int port);
+
+ // Network-initiated (NI) Support
+ private native void native_send_ni_response(int notificationId, int userResponse);
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
new file mode 100755
index 0000000..a5466d1
--- /dev/null
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.location;
+
+import java.io.UnsupportedEncodingException;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A GPS Network-initiated Handler class used by LocationManager.
+ *
+ * {@hide}
+ */
+public class GpsNetInitiatedHandler {
+
+ private static final String TAG = "GpsNetInitiatedHandler";
+
+ private static final boolean DEBUG = true;
+ private static final boolean VERBOSE = false;
+
+ // NI verify activity for bringing up UI (not used yet)
+ public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
+
+ // string constants for defining data fields in NI Intent
+ public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
+ public static final String NI_INTENT_KEY_TITLE = "title";
+ public static final String NI_INTENT_KEY_MESSAGE = "message";
+ public static final String NI_INTENT_KEY_TIMEOUT = "timeout";
+ public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp";
+
+ // the extra command to send NI response to GpsLocationProvider
+ public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response";
+
+ // the extra command parameter names in the Bundle
+ public static final String NI_EXTRA_CMD_NOTIF_ID = "notif_id";
+ public static final String NI_EXTRA_CMD_RESPONSE = "response";
+
+ // these need to match GpsNiType constants in gps_ni.h
+ public static final int GPS_NI_TYPE_VOICE = 1;
+ public static final int GPS_NI_TYPE_UMTS_SUPL = 2;
+ public static final int GPS_NI_TYPE_UMTS_CTRL_PLANE = 3;
+
+ // these need to match GpsUserResponseType constants in gps_ni.h
+ public static final int GPS_NI_RESPONSE_ACCEPT = 1;
+ public static final int GPS_NI_RESPONSE_DENY = 2;
+ public static final int GPS_NI_RESPONSE_NORESP = 3;
+
+ // these need to match GpsNiNotifyFlags constants in gps_ni.h
+ public static final int GPS_NI_NEED_NOTIFY = 0x0001;
+ public static final int GPS_NI_NEED_VERIFY = 0x0002;
+ public static final int GPS_NI_PRIVACY_OVERRIDE = 0x0004;
+
+ // these need to match GpsNiEncodingType in gps_ni.h
+ public static final int GPS_ENC_NONE = 0;
+ public static final int GPS_ENC_SUPL_GSM_DEFAULT = 1;
+ public static final int GPS_ENC_SUPL_UTF8 = 2;
+ public static final int GPS_ENC_SUPL_UCS2 = 3;
+ public static final int GPS_ENC_UNKNOWN = -1;
+
+ private final Context mContext;
+
+ // parent gps location provider
+ private final GpsLocationProvider mGpsLocationProvider;
+
+ // configuration of notificaiton behavior
+ private boolean mPlaySounds = false;
+ private boolean visible = true;
+ private boolean mPopupImmediately = true;
+
+ // Set to true if string from HAL is encoded as Hex, e.g., "3F0039"
+ static private boolean mIsHexInput = true;
+
+ public static class GpsNiNotification
+ {
+ int notificationId;
+ int niType;
+ boolean needNotify;
+ boolean needVerify;
+ boolean privacyOverride;
+ int timeout;
+ int defaultResponse;
+ String requestorId;
+ String text;
+ int requestorIdEncoding;
+ int textEncoding;
+ Bundle extras;
+ };
+
+ public static class GpsNiResponse {
+ /* User reponse, one of the values in GpsUserResponseType */
+ int userResponse;
+ /* Optional extra data to pass with the user response */
+ Bundle extras;
+ };
+
+ /**
+ * The notification that is shown when a network-initiated notification
+ * (and verification) event is received.
+ * <p>
+ * This is lazily created, so use {@link #setNINotification()}.
+ */
+ private Notification mNiNotification;
+
+ public GpsNetInitiatedHandler(Context context, GpsLocationProvider gpsLocationProvider) {
+ mContext = context;
+ mGpsLocationProvider = gpsLocationProvider;
+ }
+
+ // Handles NI events from HAL
+ public void handleNiNotification(GpsNiNotification notif)
+ {
+ if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId
+ + " requestorId: " + notif.requestorId + " text: " + notif.text);
+
+ // Notify and verify with immediate pop-up
+ if (notif.needNotify && notif.needVerify && mPopupImmediately)
+ {
+ // Popup the dialog box now
+ openNiDialog(notif);
+ }
+
+ // Notify only, or delayed pop-up (change mPopupImmediately to FALSE)
+ if (notif.needNotify && !notif.needVerify ||
+ notif.needNotify && notif.needVerify && !mPopupImmediately)
+ {
+ // Show the notification
+
+ // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened
+ // when the user opens the notification message
+
+ setNiNotification(notif);
+ }
+
+ // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override.
+ if ( notif.needNotify && !notif.needVerify ||
+ !notif.needNotify && !notif.needVerify ||
+ notif.privacyOverride)
+ {
+ try {
+ mGpsLocationProvider.getNetInitiatedListener().sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT);
+ }
+ catch (RemoteException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // A note about timeout
+ // According to the protocol, in the need_notify and need_verify case,
+ // a default response should be sent when time out.
+ //
+ // In some GPS hardware, the GPS driver (under HAL) can handle the timeout case
+ // and this class GpsNetInitiatedHandler does not need to do anything.
+ //
+ // However, the UI should at least close the dialog when timeout. Further,
+ // for more general handling, timeout response should be added to the Handler here.
+ //
+ }
+
+ // Sets the NI notification.
+ private synchronized void setNiNotification(GpsNiNotification notif) {
+ NotificationManager notificationManager = (NotificationManager) mContext
+ .getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ String title = getNotifTitle(notif);
+ String message = getNotifMessage(notif);
+
+ if (DEBUG) Log.d(TAG, "setNiNotification, notifyId: " + notif.notificationId +
+ ", title: " + title +
+ ", message: " + message);
+
+ // Construct Notification
+ if (mNiNotification == null) {
+ mNiNotification = new Notification();
+ mNiNotification.icon = com.android.internal.R.drawable.stat_sys_gps_on; /* Change notification icon here */
+ mNiNotification.when = 0;
+ }
+
+ if (mPlaySounds) {
+ mNiNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mNiNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mNiNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mNiNotification.tickerText = getNotifTicker(notif);
+
+ // if not to popup dialog immediately, pending intent will open the dialog
+ Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent();
+ PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+ mNiNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ if (visible) {
+ notificationManager.notify(notif.notificationId, mNiNotification);
+ } else {
+ notificationManager.cancel(notif.notificationId);
+ }
+ }
+
+ // Opens the notification dialog and waits for user input
+ private void openNiDialog(GpsNiNotification notif)
+ {
+ Intent intent = getDlgIntent(notif);
+
+ if (DEBUG) Log.d(TAG, "openNiDialog, notifyId: " + notif.notificationId +
+ ", requestorId: " + notif.requestorId +
+ ", text: " + notif.text);
+
+ mContext.startActivity(intent);
+ }
+
+ // Construct the intent for bringing up the dialog activity, which shows the
+ // notification and takes user input
+ private Intent getDlgIntent(GpsNiNotification notif)
+ {
+ Intent intent = new Intent();
+ String title = getDialogTitle(notif);
+ String message = getDialogMessage(notif);
+
+ // directly bring up the NI activity
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class);
+
+ // put data in the intent
+ intent.putExtra(NI_INTENT_KEY_NOTIF_ID, notif.notificationId);
+ intent.putExtra(NI_INTENT_KEY_TITLE, title);
+ intent.putExtra(NI_INTENT_KEY_MESSAGE, message);
+ intent.putExtra(NI_INTENT_KEY_TIMEOUT, notif.timeout);
+ intent.putExtra(NI_INTENT_KEY_DEFAULT_RESPONSE, notif.defaultResponse);
+
+ if (DEBUG) Log.d(TAG, "generateIntent, title: " + title + ", message: " + message +
+ ", timeout: " + notif.timeout);
+
+ return intent;
+ }
+
+ // Converts a string (or Hex string) to a char array
+ static byte[] stringToByteArray(String original, boolean isHex)
+ {
+ int length = isHex ? original.length() / 2 : original.length();
+ byte[] output = new byte[length];
+ int i;
+
+ if (isHex)
+ {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) Integer.parseInt(original.substring(i*2, i*2+2), 16);
+ }
+ }
+ else {
+ for (i = 0; i < length; i++)
+ {
+ output[i] = (byte) original.charAt(i);
+ }
+ }
+
+ return output;
+ }
+
+ /**
+ * Unpacks an byte array containing 7-bit packed characters into a String.
+ *
+ * @param input a 7-bit packed char array
+ * @return the unpacked String
+ */
+ static String decodeGSMPackedString(byte[] input)
+ {
+ final char CHAR_CR = 0x0D;
+ int nStridx = 0;
+ int nPckidx = 0;
+ int num_bytes = input.length;
+ int cPrev = 0;
+ int cCurr = 0;
+ byte nShift;
+ byte nextChar;
+ byte[] stringBuf = new byte[input.length * 2];
+ String result = "";
+
+ while(nPckidx < num_bytes)
+ {
+ nShift = (byte) (nStridx & 0x07);
+ cCurr = input[nPckidx++];
+ if (cCurr < 0) cCurr += 256;
+
+ /* A 7-bit character can be split at the most between two bytes of packed
+ ** data.
+ */
+ nextChar = (byte) (( (cCurr << nShift) | (cPrev >> (8-nShift)) ) & 0x7F);
+ stringBuf[nStridx++] = nextChar;
+
+ /* Special case where the whole of the next 7-bit character fits inside
+ ** the current byte of packed data.
+ */
+ if(nShift == 6)
+ {
+ /* If the next 7-bit character is a CR (0x0D) and it is the last
+ ** character, then it indicates a padding character. Drop it.
+ */
+ if (nPckidx == num_bytes || (cCurr >> 1) == CHAR_CR)
+ {
+ break;
+ }
+
+ nextChar = (byte) (cCurr >> 1);
+ stringBuf[nStridx++] = nextChar;
+ }
+
+ cPrev = cCurr;
+ }
+
+ try{
+ result = new String(stringBuf, 0, nStridx, "US-ASCII");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+
+ return result;
+ }
+
+ static String decodeUTF8String(byte[] input)
+ {
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-8");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
+ }
+
+ static String decodeUCS2String(byte[] input)
+ {
+ String decoded = "";
+ try {
+ decoded = new String(input, "UTF-16");
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ Log.e(TAG, e.getMessage());
+ }
+ return decoded;
+ }
+
+ /** Decode NI string
+ *
+ * @param original The text string to be decoded
+ * @param isHex Specifies whether the content of the string has been encoded as a Hex string. Encoding
+ * a string as Hex can allow zeros inside the coded text.
+ * @param coding Specifies the coding scheme of the string, such as GSM, UTF8, UCS2, etc. This coding scheme
+ * needs to match those used passed to HAL from the native GPS driver. Decoding is done according
+ * to the <code> coding </code>, after a Hex string is decoded. Generally, if the
+ * notification strings don't need further decoding, <code> coding </code> encoding can be
+ * set to -1, and <code> isHex </code> can be false.
+ * @return the decoded string
+ */
+ static private String decodeString(String original, boolean isHex, int coding)
+ {
+ String decoded = original;
+ byte[] input = stringToByteArray(original, isHex);
+
+ switch (coding) {
+ case GPS_ENC_NONE:
+ decoded = original;
+ break;
+
+ case GPS_ENC_SUPL_GSM_DEFAULT:
+ decoded = decodeGSMPackedString(input);
+ break;
+
+ case GPS_ENC_SUPL_UTF8:
+ decoded = decodeUTF8String(input);
+ break;
+
+ case GPS_ENC_SUPL_UCS2:
+ decoded = decodeUCS2String(input);
+ break;
+
+ case GPS_ENC_UNKNOWN:
+ decoded = original;
+ break;
+
+ default:
+ Log.e(TAG, "Unknown encoding " + coding + " for NI text " + original);
+ break;
+ }
+ return decoded;
+ }
+
+ // change this to configure notification display
+ static private String getNotifTicker(GpsNiNotification notif)
+ {
+ String ticker = String.format("Position request! ReqId: [%s] ClientName: [%s]",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return ticker;
+ }
+
+ // change this to configure notification display
+ static private String getNotifTitle(GpsNiNotification notif)
+ {
+ String title = String.format("Position Request");
+ return title;
+ }
+
+ // change this to configure notification display
+ static private String getNotifMessage(GpsNiNotification notif)
+ {
+ String message = String.format(
+ "NI Request received from [%s] for client [%s]!",
+ decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding),
+ decodeString(notif.text, mIsHexInput, notif.textEncoding));
+ return message;
+ }
+
+ // change this to configure dialog display (for verification)
+ static public String getDialogTitle(GpsNiNotification notif)
+ {
+ return getNotifTitle(notif);
+ }
+
+ // change this to configure dialog display (for verification)
+ static private String getDialogMessage(GpsNiNotification notif)
+ {
+ return getNotifMessage(notif);
+ }
+
+}
diff --git a/location/java/com/android/internal/location/GpsXtraDownloader.java b/location/java/com/android/internal/location/GpsXtraDownloader.java
index 2a8be57..33ebce7 100644
--- a/location/java/com/android/internal/location/GpsXtraDownloader.java
+++ b/location/java/com/android/internal/location/GpsXtraDownloader.java
@@ -64,6 +64,7 @@ public class GpsXtraDownloader {
if (count == 0) {
Log.e(TAG, "No XTRA servers were specified in the GPS configuration");
+ return;
} else {
mXtraServers = new String[count];
count = 0;
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 0732b61..b3aae72 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -37,15 +37,61 @@ public class AudioFormat {
public static final int ENCODING_PCM_8BIT = 3; // accessed by native code
/** Invalid audio channel configuration */
- public static final int CHANNEL_CONFIGURATION_INVALID = 0;
+ /** @deprecated use CHANNEL_INVALID instead */
+ @Deprecated public static final int CHANNEL_CONFIGURATION_INVALID = 0;
/** Default audio channel configuration */
- public static final int CHANNEL_CONFIGURATION_DEFAULT = 1;
+ /** @deprecated use CHANNEL_OUT_DEFAULT or CHANNEL_IN_DEFAULT instead */
+ @Deprecated public static final int CHANNEL_CONFIGURATION_DEFAULT = 1;
/** Mono audio configuration */
- public static final int CHANNEL_CONFIGURATION_MONO = 2;
+ /** @deprecated use CHANNEL_OUT_MONO or CHANNEL_IN_MONO instead */
+ @Deprecated public static final int CHANNEL_CONFIGURATION_MONO = 2;
/** Stereo (2 channel) audio configuration */
- public static final int CHANNEL_CONFIGURATION_STEREO = 3;
+ /** @deprecated use CHANNEL_OUT_STEREO or CHANNEL_IN_STEREO instead */
+ @Deprecated public static final int CHANNEL_CONFIGURATION_STEREO = 3;
-}
+ /** Invalid audio channel mask */
+ public static final int CHANNEL_INVALID = 0;
+ /** Default audio channel mask */
+ public static final int CHANNEL_OUT_DEFAULT = 1;
+ // Channel mask definitions must be kept in sync with native values in include/media/AudioSystem.h
+ public static final int CHANNEL_OUT_FRONT_LEFT = 0x4;
+ public static final int CHANNEL_OUT_FRONT_RIGHT = 0x8;
+ public static final int CHANNEL_OUT_FRONT_CENTER = 0x10;
+ public static final int CHANNEL_OUT_LOW_FREQUENCY = 0x20;
+ public static final int CHANNEL_OUT_BACK_LEFT = 0x40;
+ public static final int CHANNEL_OUT_BACK_RIGHT = 0x80;
+ public static final int CHANNEL_OUT_FRONT_LEFT_OF_CENTER = 0x100;
+ public static final int CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x200;
+ public static final int CHANNEL_OUT_BACK_CENTER = 0x400;
+ public static final int CHANNEL_OUT_MONO = CHANNEL_OUT_FRONT_LEFT;
+ public static final int CHANNEL_OUT_STEREO = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT);
+ public static final int CHANNEL_OUT_QUAD = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
+ public static final int CHANNEL_OUT_SURROUND = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER);
+ public static final int CHANNEL_OUT_5POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT);
+ public static final int CHANNEL_OUT_7POINT1 = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
+ CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY | CHANNEL_OUT_BACK_LEFT | CHANNEL_OUT_BACK_RIGHT |
+ CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER);
+ public static final int CHANNEL_IN_DEFAULT = 1;
+ public static final int CHANNEL_IN_LEFT = 0x4;
+ public static final int CHANNEL_IN_RIGHT = 0x8;
+ public static final int CHANNEL_IN_FRONT = 0x10;
+ public static final int CHANNEL_IN_BACK = 0x20;
+ public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;
+ public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;
+ public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;
+ public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;
+ public static final int CHANNEL_IN_PRESSURE = 0x400;
+ public static final int CHANNEL_IN_X_AXIS = 0x800;
+ public static final int CHANNEL_IN_Y_AXIS = 0x1000;
+ public static final int CHANNEL_IN_Z_AXIS = 0x2000;
+ public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;
+ public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;
+ public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
+ public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index a65a417..1dd644b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -140,33 +140,31 @@ public class AudioManager {
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide The audio stream for phone calls when connected to bluetooth */
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
+ /** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
+ public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
+ /** The audio stream for DTMF Tones */
+ public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
+ /** @hide The audio stream for text to speech (TTS) */
+ public static final int STREAM_TTS = AudioSystem.STREAM_TTS;
/** Number of audio streams */
/**
* @deprecated Use AudioSystem.getNumStreamTypes() instead
*/
- public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
+ @Deprecated public static final int NUM_STREAMS = AudioSystem.NUM_STREAMS;
- /** @hide Maximum volume index values for audio streams */
- public static final int[] MAX_STREAM_VOLUME = new int[] {
- 6, // STREAM_VOICE_CALL
- 8, // STREAM_SYSTEM
- 8, // STREAM_RING
- 16, // STREAM_MUSIC
- 8, // STREAM_ALARM
- 8, // STREAM_NOTIFICATION
- 16, // STREAM_BLUETOOTH_SCO
- };
-
/** @hide Default volume index values for audio streams */
public static final int[] DEFAULT_STREAM_VOLUME = new int[] {
4, // STREAM_VOICE_CALL
- 5, // STREAM_SYSTEM
+ 7, // STREAM_SYSTEM
5, // STREAM_RING
11, // STREAM_MUSIC
6, // STREAM_ALARM
5, // STREAM_NOTIFICATION
- 7 // STREAM_BLUETOOTH_SCO
+ 7, // STREAM_BLUETOOTH_SCO
+ 7, // STREAM_SYSTEM_ENFORCED
+ 11, // STREAM_DTMF
+ 11 // STREAM_TTS
};
/**
@@ -637,9 +635,12 @@ public class AudioManager {
* <var>false</var> to turn it off
*/
public void setSpeakerphoneOn(boolean on){
- // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
- // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
- setRoutingP(MODE_INVALID, on ? ROUTE_SPEAKER: 0, ROUTE_SPEAKER);
+ IAudioService service = getService();
+ try {
+ service.setSpeakerphoneOn(on);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setSpeakerphoneOn", e);
+ }
}
/**
@@ -648,41 +649,52 @@ public class AudioManager {
* @return true if speakerphone is on, false if it's off
*/
public boolean isSpeakerphoneOn() {
- return (getRoutingP(MODE_IN_CALL) & ROUTE_SPEAKER) == 0 ? false : true;
+ IAudioService service = getService();
+ try {
+ return service.isSpeakerphoneOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in isSpeakerphoneOn", e);
+ return false;
+ }
}
/**
- * Sets audio routing to the Bluetooth headset on or off.
+ * Request use of Bluetooth SCO headset for communications.
*
- * @param on set <var>true</var> to route SCO (voice) audio to/from Bluetooth
- * headset; <var>false</var> to route audio to/from phone earpiece
+ * @param on set <var>true</var> to use bluetooth SCO for communications;
+ * <var>false</var> to not use bluetooth SCO for communications
*/
public void setBluetoothScoOn(boolean on){
- // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
- // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
- setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_SCO: 0, ROUTE_BLUETOOTH_SCO);
+ IAudioService service = getService();
+ try {
+ service.setBluetoothScoOn(on);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setBluetoothScoOn", e);
+ }
}
/**
- * Checks whether audio routing to the Bluetooth headset is on or off.
+ * Checks whether communications use Bluetooth SCO.
*
- * @return true if SCO audio is being routed to/from Bluetooth headset;
+ * @return true if SCO is used for communications;
* false if otherwise
*/
public boolean isBluetoothScoOn() {
- return (getRoutingP(MODE_IN_CALL) & ROUTE_BLUETOOTH_SCO) == 0 ? false : true;
+ IAudioService service = getService();
+ try {
+ return service.isBluetoothScoOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in isBluetoothScoOn", e);
+ return false;
+ }
}
/**
- * Sets A2DP audio routing to the Bluetooth headset on or off.
- *
* @param on set <var>true</var> to route A2DP audio to/from Bluetooth
* headset; <var>false</var> disable A2DP audio
+ * @deprecated Do not use.
*/
- public void setBluetoothA2dpOn(boolean on){
- // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
- // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
- setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
+ @Deprecated public void setBluetoothA2dpOn(boolean on){
}
/**
@@ -692,7 +704,12 @@ public class AudioManager {
* false if otherwise
*/
public boolean isBluetoothA2dpOn() {
- return (getRoutingP(MODE_NORMAL) & ROUTE_BLUETOOTH_A2DP) == 0 ? false : true;
+ if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,"")
+ == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
+ return false;
+ } else {
+ return true;
+ }
}
/**
@@ -700,12 +717,9 @@ public class AudioManager {
*
* @param on set <var>true</var> to route audio to/from wired
* headset; <var>false</var> disable wired headset audio
- * @hide
+ * @deprecated Do not use.
*/
- public void setWiredHeadsetOn(boolean on){
- // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
- // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
- setRoutingP(MODE_INVALID, on ? ROUTE_HEADSET: 0, ROUTE_HEADSET);
+ @Deprecated public void setWiredHeadsetOn(boolean on){
}
/**
@@ -713,10 +727,14 @@ public class AudioManager {
*
* @return true if audio is being routed to/from wired headset;
* false if otherwise
- * @hide
*/
public boolean isWiredHeadsetOn() {
- return (getRoutingP(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true;
+ if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,"")
+ == AudioSystem.DEVICE_STATE_UNAVAILABLE) {
+ return false;
+ } else {
+ return true;
+ }
}
/**
@@ -726,12 +744,7 @@ public class AudioManager {
* <var>false</var> to turn mute off
*/
public void setMicrophoneMute(boolean on){
- IAudioService service = getService();
- try {
- service.setMicrophoneMute(on);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setMicrophoneMute", e);
- }
+ AudioSystem.muteMicrophone(on);
}
/**
@@ -740,13 +753,7 @@ public class AudioManager {
* @return true if microphone is muted, false if it's not
*/
public boolean isMicrophoneMute() {
- IAudioService service = getService();
- try {
- return service.isMicrophoneMute();
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in isMicrophoneMute", e);
- return false;
- }
+ return AudioSystem.isMicrophoneMuted();
}
/**
@@ -809,32 +816,46 @@ public class AudioManager {
/* Routing bits for setRouting/getRouting API */
/**
* Routing audio output to earpiece
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE;
+ @Deprecated public static final int ROUTE_EARPIECE = AudioSystem.ROUTE_EARPIECE;
/**
- * Routing audio output to spaker
+ * Routing audio output to speaker
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER;
+ @Deprecated public static final int ROUTE_SPEAKER = AudioSystem.ROUTE_SPEAKER;
/**
* @deprecated use {@link #ROUTE_BLUETOOTH_SCO}
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
@Deprecated public static final int ROUTE_BLUETOOTH = AudioSystem.ROUTE_BLUETOOTH_SCO;
/**
* Routing audio output to bluetooth SCO
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO;
+ @Deprecated public static final int ROUTE_BLUETOOTH_SCO = AudioSystem.ROUTE_BLUETOOTH_SCO;
/**
* Routing audio output to headset
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET;
+ @Deprecated public static final int ROUTE_HEADSET = AudioSystem.ROUTE_HEADSET;
/**
* Routing audio output to bluetooth A2DP
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ @Deprecated public static final int ROUTE_BLUETOOTH_A2DP = AudioSystem.ROUTE_BLUETOOTH_A2DP;
/**
* Used for mask parameter of {@link #setRouting(int,int,int)}.
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn() methods instead.
*/
- public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL;
+ @Deprecated public static final int ROUTE_ALL = AudioSystem.ROUTE_ALL;
/**
* Sets the audio routing for a specified mode
@@ -846,16 +867,10 @@ public class AudioManager {
* ROUTE_xxx types. Unset bits indicate the route should be left unchanged
*
* @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
- * setBluetoothScoOn(), setBluetoothA2dpOn() and setWiredHeadsetOn() methods instead.
+ * setBluetoothScoOn() methods instead.
*/
-
+ @Deprecated
public void setRouting(int mode, int routes, int mask) {
- IAudioService service = getService();
- try {
- service.setRouting(mode, routes, mask);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setRouting", e);
- }
}
/**
@@ -869,13 +884,7 @@ public class AudioManager {
*/
@Deprecated
public int getRouting(int mode) {
- IAudioService service = getService();
- try {
- return service.getRouting(mode);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in getRouting", e);
- return -1;
- }
+ return -1;
}
/**
@@ -884,13 +893,7 @@ public class AudioManager {
* @return true if any music tracks are active.
*/
public boolean isMusicActive() {
- IAudioService service = getService();
- try {
- return service.isMusicActive();
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in isMusicActive", e);
- return false;
- }
+ return AudioSystem.isMusicActive();
}
/*
@@ -906,14 +909,32 @@ public class AudioManager {
*/
/**
* @hide
+ * @deprecated Use {@link #setPrameters(String)} instead
*/
- public void setParameter(String key, String value) {
- IAudioService service = getService();
- try {
- service.setParameter(key, value);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setParameter", e);
- }
+ @Deprecated public void setParameter(String key, String value) {
+ setParameters(key+"="+value);
+ }
+
+ /**
+ * Sets a variable number of parameter values to audio hardware.
+ *
+ * @param keyValuePairs list of parameters key value pairs in the form:
+ * key1=value1;key2=value2;...
+ *
+ */
+ public void setParameters(String keyValuePairs) {
+ AudioSystem.setParameters(keyValuePairs);
+ }
+
+ /**
+ * Sets a varaible number of parameter values to audio hardware.
+ *
+ * @param keys list of parameters
+ * @return list of parameters key value pairs in the form:
+ * key1=value1;key2=value2;...
+ */
+ public String getParameters(String keys) {
+ return AudioSystem.getParameters(keys);
}
/* Sound effect identifiers */
@@ -1082,31 +1103,4 @@ public class AudioManager {
* {@hide}
*/
private IBinder mICallBack = new Binder();
-
- /**
- * {@hide}
- */
- private void setRoutingP(int mode, int routes, int mask) {
- IAudioService service = getService();
- try {
- service.setRouting(mode, routes, mask);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in setRouting", e);
- }
- }
-
-
- /**
- * {@hide}
- */
- private int getRoutingP(int mode) {
- IAudioService service = getService();
- try {
- return service.getRouting(mode);
- } catch (RemoteException e) {
- Log.e(TAG, "Dead object in getRouting", e);
- return -1;
- }
- }
-
}
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4d1535f..7a47157 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -86,7 +86,7 @@ public class AudioRecord
public static final int ERROR_INVALID_OPERATION = -3;
private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT = -16;
- private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELCOUNT = -17;
+ private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK = -17;
private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT = -18;
private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE = -19;
private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED = -20;
@@ -133,9 +133,13 @@ public class AudioRecord
*/
private int mChannelCount = 1;
/**
+ * The audio channel mask
+ */
+ private int mChannels = AudioFormat.CHANNEL_IN_MONO;
+ /**
* The current audio channel configuration
*/
- private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ private int mChannelConfiguration = AudioFormat.CHANNEL_IN_MONO;
/**
* The encoding of the audio samples.
* @see AudioFormat#ENCODING_PCM_8BIT
@@ -193,8 +197,8 @@ public class AudioRecord
* @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
* not limited to) 44100, 22050 and 11025.
* @param channelConfig describes the configuration of the audio channels.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
- * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}
+ * See {@link AudioFormat#CHANNEL_IN_MONO} and
+ * {@link AudioFormat#CHANNEL_IN_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -224,7 +228,7 @@ public class AudioRecord
//TODO: update native initialization when information about hardware init failure
// due to capture device already open is available.
int initResult = native_setup( new WeakReference<AudioRecord>(this),
- mRecordSource, mSampleRate, mChannelCount, mAudioFormat, mNativeBufferSizeInBytes);
+ mRecordSource, mSampleRate, mChannels, mAudioFormat, mNativeBufferSizeInBytes);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing native AudioRecord object.");
return; // with mState == STATE_UNINITIALIZED
@@ -239,6 +243,7 @@ public class AudioRecord
// postconditions:
// mRecordSource is valid
// mChannelCount is valid
+ // mChannels is valid
// mAudioFormat is valid
// mSampleRate is valid
private void audioParamCheck(int audioSource, int sampleRateInHz,
@@ -264,20 +269,25 @@ public class AudioRecord
//--------------
// channel config
+ mChannelConfiguration = channelConfig;
+
switch (channelConfig) {
- case AudioFormat.CHANNEL_CONFIGURATION_DEFAULT:
+ case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
+ case AudioFormat.CHANNEL_IN_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
mChannelCount = 1;
- mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ mChannels = AudioFormat.CHANNEL_IN_MONO;
break;
+ case AudioFormat.CHANNEL_IN_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
mChannelCount = 2;
- mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ mChannels = AudioFormat.CHANNEL_IN_STEREO;
break;
default:
mChannelCount = 0;
- mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_INVALID;
- throw (new IllegalArgumentException("Unsupported channel configuration."));
+ mChannels = AudioFormat.CHANNEL_INVALID;
+ mChannelConfiguration = AudioFormat.CHANNEL_INVALID;
+ throw (new IllegalArgumentException("Unsupported channel configuration."));
}
//--------------
@@ -368,8 +378,8 @@ public class AudioRecord
/**
* Returns the configured channel configuration.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO}
- * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}.
+ * See {@link AudioFormat#CHANNEL_IN_MONO}
+ * and {@link AudioFormat#CHANNEL_IN_STEREO}.
*/
public int getChannelConfiguration() {
return mChannelConfiguration;
@@ -425,8 +435,8 @@ public class AudioRecord
* will be polled for new data.
* @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
- * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}
+ * See {@link AudioFormat#CHANNEL_IN_MONO} and
+ * {@link AudioFormat#CHANNEL_IN_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT}.
* @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the
@@ -438,14 +448,16 @@ public class AudioRecord
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch(channelConfig) {
- case AudioFormat.CHANNEL_CONFIGURATION_DEFAULT:
+ case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
+ case AudioFormat.CHANNEL_IN_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
+ case AudioFormat.CHANNEL_IN_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;
break;
- case AudioFormat.CHANNEL_CONFIGURATION_INVALID:
+ case AudioFormat.CHANNEL_INVALID:
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return AudioRecord.ERROR_BAD_VALUE;
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 58c04f3..665d353 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -17,9 +17,17 @@
package android.media;
import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothHeadset;
+
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.media.MediaPlayer.OnCompletionListener;
@@ -36,11 +44,16 @@ import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;
import android.view.VolumePanel;
+import android.os.SystemProperties;
import com.android.internal.telephony.ITelephony;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
/**
@@ -94,16 +107,10 @@ public class AudioService extends IAudioService.Stub {
/** @see VolumeStreamState */
private VolumeStreamState[] mStreamStates;
private SettingsObserver mSettingsObserver;
-
- private boolean mMicMute;
+
private int mMode;
- private int[] mRoutes = new int[AudioSystem.NUM_MODES];
private Object mSettingsLock = new Object();
private boolean mMediaServerOk;
- private boolean mSpeakerIsOn;
- private boolean mBluetoothScoIsConnected;
- private boolean mHeadsetIsConnected;
- private boolean mBluetoothA2dpIsConnected;
private SoundPool mSoundPool;
private Object mSoundEffectsLock = new Object();
@@ -135,6 +142,36 @@ public class AudioService extends IAudioService.Stub {
{4, -1} // FX_FOCUS_RETURN
};
+ /** @hide Maximum volume index values for audio streams */
+ private int[] MAX_STREAM_VOLUME = new int[] {
+ 5, // STREAM_VOICE_CALL
+ 7, // STREAM_SYSTEM
+ 7, // STREAM_RING
+ 15, // STREAM_MUSIC
+ 7, // STREAM_ALARM
+ 7, // STREAM_NOTIFICATION
+ 15, // STREAM_BLUETOOTH_SCO
+ 7, // STREAM_SYSTEM_ENFORCED
+ 15, // STREAM_DTMF
+ 15 // STREAM_TTS
+ };
+ /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
+ * of another stream: This avoids multiplying the volume settings for hidden
+ * stream types that follow other stream behavior for volume settings
+ * NOTE: do not create loops in aliases! */
+ private int[] STREAM_VOLUME_ALIAS = new int[] {
+ AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
+ AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
+ AudioSystem.STREAM_RING, // STREAM_RING
+ AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
+ AudioSystem.STREAM_ALARM, // STREAM_ALARM
+ AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION
+ AudioSystem.STREAM_VOICE_CALL, // STREAM_BLUETOOTH_SCO
+ AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED
+ AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
+ AudioSystem.STREAM_MUSIC // STREAM_TTS
+ };
+
private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
public void onError(int error) {
switch (error) {
@@ -178,6 +215,27 @@ public class AudioService extends IAudioService.Stub {
*/
private int mVibrateSetting;
+ /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
+ private int mNotificationsUseRingVolume;
+
+ // Broadcast receiver for device connections intent broadcasts
+ private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
+
+ //TODO: use common definitions with HeadsetObserver
+ private static final int BIT_HEADSET = (1 << 0);
+ private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+ private static final int BIT_TTY = (1 << 2);
+ private static final int BIT_FM_HEADSET = (1 << 3);
+ private static final int BIT_FM_SPEAKER = (1 << 4);
+
+ private int mHeadsetState;
+
+ // Devices currently connected
+ private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
+
+ // Forced device usage for communications
+ private int mForcedUseForComm;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -186,20 +244,31 @@ public class AudioService extends IAudioService.Stub {
public AudioService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
+
+ // Intialized volume
+ MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
+ "ro.config.vc_call_vol_steps",
+ MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
+
mVolumePanel = new VolumePanel(context, this);
mSettingsObserver = new SettingsObserver();
-
+ mMode = AudioSystem.MODE_NORMAL;
+ mHeadsetState = 0;
+ mForcedUseForComm = AudioSystem.FORCE_NONE;
createAudioSystemThread();
- createStreamStates();
readPersistedSettings();
- readAudioSettings();
+ createStreamStates();
mMediaServerOk = true;
AudioSystem.setErrorCallback(mAudioSystemCallback);
loadSoundEffects();
- mSpeakerIsOn = false;
- mBluetoothScoIsConnected = false;
- mHeadsetIsConnected = false;
- mBluetoothA2dpIsConnected = false;
+
+ // Register for device connection intent broadcasts.
+ IntentFilter intentFilter =
+ new IntentFilter(Intent.ACTION_HEADSET_PLUG);
+ intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
+ intentFilter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
+ context.registerReceiver(mReceiver, intentFilter);
+
}
private void createAudioSystemThread() {
@@ -223,63 +292,23 @@ public class AudioService extends IAudioService.Stub {
}
private void createStreamStates() {
- final int[] volumeLevelsPhone =
- createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]);
- final int[] volumeLevelsCoarse =
- createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]);
- final int[] volumeLevelsFine =
- createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
- final int[] volumeLevelsBtPhone =
- createVolumeLevels(0,
- AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);
-
int numStreamTypes = AudioSystem.getNumStreamTypes();
VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
for (int i = 0; i < numStreamTypes; i++) {
- final int[] levels;
-
- switch (i) {
-
- case AudioSystem.STREAM_MUSIC:
- levels = volumeLevelsFine;
- break;
-
- case AudioSystem.STREAM_VOICE_CALL:
- levels = volumeLevelsPhone;
- break;
-
- case AudioSystem.STREAM_BLUETOOTH_SCO:
- levels = volumeLevelsBtPhone;
- break;
-
- default:
- levels = volumeLevelsCoarse;
- break;
- }
-
- if (i == AudioSystem.STREAM_BLUETOOTH_SCO) {
- streams[i] = new VolumeStreamState(AudioManager.DEFAULT_STREAM_VOLUME[i], i,levels);
- } else {
- streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels);
- }
- }
- }
-
- private static int[] createVolumeLevels(int offset, int numlevels) {
- double curve = 1.0f; // 1.4f
- int [] volumes = new int[numlevels + offset];
- for (int i = 0; i < offset; i++) {
- volumes[i] = 0;
+ streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
}
- double val = 0;
- double max = Math.pow(numlevels - 1, curve);
- for (int i = 0; i < numlevels; i++) {
- val = Math.pow(i, curve) / max;
- volumes[offset + i] = (int) (val * 100.0f);
+ // Correct stream index values for streams with aliases
+ for (int i = 0; i < numStreamTypes; i++) {
+ if (STREAM_VOLUME_ALIAS[i] != i) {
+ int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
+ streams[i].mIndex = streams[i].getValidIndex(index);
+ setStreamVolumeIndex(i, index);
+ index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
+ streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
+ }
}
- return volumes;
}
private void readPersistedSettings() {
@@ -291,12 +320,19 @@ public class AudioService extends IAudioService.Stub {
mRingerModeAffectedStreams = Settings.System.getInt(cr,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- ((1 << AudioManager.STREAM_RING)|(1 << AudioManager.STREAM_NOTIFICATION)|(1 << AudioManager.STREAM_SYSTEM)));
+ ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
mMuteAffectedStreams = System.getInt(cr,
System.MUTE_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
+ mNotificationsUseRingVolume = System.getInt(cr,
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
+
+ if (mNotificationsUseRingVolume == 1) {
+ STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+ }
// Each stream will read its own persisted settings
// Broadcast the sticky intent
@@ -307,25 +343,13 @@ public class AudioService extends IAudioService.Stub {
broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
}
- private void readAudioSettings() {
- synchronized (mSettingsLock) {
- mMicMute = AudioSystem.isMicrophoneMuted();
- mMode = AudioSystem.getMode();
- for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
- mRoutes[mode] = AudioSystem.getRouting(mode);
- }
- }
+ private void setStreamVolumeIndex(int stream, int index) {
+ AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
}
- private void applyAudioSettings() {
- synchronized (mSettingsLock) {
- AudioSystem.muteMicrophone(mMicMute);
- AudioSystem.setMode(mMode);
- for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
- AudioSystem.setRouting(mode, mRoutes[mode], AudioSystem.ROUTE_ALL);
- }
- }
- }
+ private int rescaleIndex(int index, int srcStream, int dstStream) {
+ return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
+ }
///////////////////////////////////////////////////////////////////////////
// IPC methods
@@ -354,44 +378,26 @@ public class AudioService extends IAudioService.Stub {
ensureValidDirection(direction);
ensureValidStreamType(streamType);
- boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
- Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
- if (notificationsUseRingVolume && streamType == AudioManager.STREAM_NOTIFICATION) {
- // Redirect the volume change to the ring stream
- streamType = AudioManager.STREAM_RING;
- }
- VolumeStreamState streamState = mStreamStates[streamType];
+ VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
final int oldIndex = streamState.mIndex;
boolean adjustVolume = true;
// If either the client forces allowing ringer modes for this adjustment,
// or the stream type is one that is affected by ringer modes
if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
- || streamType == AudioManager.STREAM_RING) {
+ || streamType == AudioSystem.STREAM_RING) {
// Check if the ringer mode changes with this volume adjustment. If
// it does, it will handle adjusting the volume, so we won't below
adjustVolume = checkForRingerModeChange(oldIndex, direction);
}
if (adjustVolume && streamState.adjustIndex(direction)) {
-
- boolean alsoUpdateNotificationVolume = notificationsUseRingVolume &&
- streamType == AudioManager.STREAM_RING;
- if (alsoUpdateNotificationVolume) {
- mStreamStates[AudioManager.STREAM_NOTIFICATION].adjustIndex(direction);
- }
-
// Post message to set system volume (it in turn will post a message
// to persist). Do not change volume if stream is muted.
if (streamState.muteCount() == 0) {
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
+ sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
streamState, 0);
-
- if (alsoUpdateNotificationVolume) {
- sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, AudioManager.STREAM_NOTIFICATION,
- SENDMSG_NOOP, 0, 0, mStreamStates[AudioManager.STREAM_NOTIFICATION], 0);
- }
}
}
@@ -404,9 +410,8 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#setStreamVolume(int, int, int) */
public void setStreamVolume(int streamType, int index, int flags) {
ensureValidStreamType(streamType);
- syncRingerAndNotificationStreamVolume(streamType, index, false);
-
- setStreamVolumeInt(streamType, index, false, true);
+ index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
// UI, etc.
mVolumePanel.postVolumeChanged(streamType, flags);
@@ -420,37 +425,12 @@ public class AudioService extends IAudioService.Stub {
intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType));
// Currently, sending the intent only when the stream is BLUETOOTH_SCO
- if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
+ if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
mContext.sendBroadcast(intent);
}
}
/**
- * Sync the STREAM_RING and STREAM_NOTIFICATION volumes if mandated by the
- * value in Settings.
- *
- * @param streamType Type of the stream
- * @param index Volume index for the stream
- * @param force If true, set the volume even if the current and desired
- * volume as same
- */
- private void syncRingerAndNotificationStreamVolume(int streamType, int index, boolean force) {
- boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
- Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
- if (notificationsUseRingVolume) {
- if (streamType == AudioManager.STREAM_NOTIFICATION) {
- // Redirect the volume change to the ring stream
- streamType = AudioManager.STREAM_RING;
- }
- if (streamType == AudioManager.STREAM_RING) {
- // One-off to sync notification volume to ringer volume
- setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force, true);
- }
- }
- }
-
-
- /**
* Sets the stream state's index, and posts a message to set system volume.
* This will not call out to the UI. Assumes a valid stream type.
*
@@ -491,13 +471,13 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#getStreamVolume(int) */
public int getStreamVolume(int streamType) {
ensureValidStreamType(streamType);
- return mStreamStates[streamType].mIndex;
+ return (mStreamStates[streamType].mIndex + 5) / 10;
}
/** @see AudioManager#getStreamMaxVolume(int) */
public int getStreamMaxVolume(int streamType) {
ensureValidStreamType(streamType);
- return mStreamStates[streamType].getMaxIndex();
+ return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
}
/** @see AudioManager#getRingerMode() */
@@ -507,11 +487,12 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#setRingerMode(int) */
public void setRingerMode(int ringerMode) {
- if (ringerMode != mRingerMode) {
- setRingerModeInt(ringerMode, true);
-
- // Send sticky broadcast
- broadcastRingerMode();
+ synchronized (mSettingsLock) {
+ if (ringerMode != mRingerMode) {
+ setRingerModeInt(ringerMode, true);
+ // Send sticky broadcast
+ broadcastRingerMode();
+ }
}
}
@@ -541,7 +522,7 @@ public class AudioService extends IAudioService.Stub {
}
}
}
-
+
// Post a persist ringer mode msg
if (persist) {
sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
@@ -606,39 +587,28 @@ public class AudioService extends IAudioService.Stub {
return existingValue;
}
- /** @see AudioManager#setMicrophoneMute(boolean) */
- public void setMicrophoneMute(boolean on) {
- if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
- return;
- }
- synchronized (mSettingsLock) {
- if (on != mMicMute) {
- AudioSystem.muteMicrophone(on);
- mMicMute = on;
- }
- }
- }
-
- /** @see AudioManager#isMicrophoneMute() */
- public boolean isMicrophoneMute() {
- return mMicMute;
- }
-
/** @see AudioManager#setMode(int) */
public void setMode(int mode) {
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
+
+ if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
+ return;
+ }
+
synchronized (mSettingsLock) {
+ if (mode == AudioSystem.MODE_CURRENT) {
+ mode = mMode;
+ }
if (mode != mMode) {
- if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) {
+ if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
mMode = mode;
}
}
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int index = mStreamStates[streamType].mIndex;
- syncRingerAndNotificationStreamVolume(streamType, index, true);
- setStreamVolumeInt(streamType, index, true, true);
+ int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
+ setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
}
}
@@ -647,187 +617,6 @@ public class AudioService extends IAudioService.Stub {
return mMode;
}
- /** @see AudioManager#setRouting(int, int, int) */
- public void setRouting(int mode, int routes, int mask) {
- int incallMask = 0;
- int ringtoneMask = 0;
- int normalMask = 0;
-
- if (!checkAudioSettingsPermission("setRouting()")) {
- return;
- }
- synchronized (mSettingsLock) {
- // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
- // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods:
- // setWiredHeadsetOn(), setBluetoothA2dpOn(), setBluetoothScoOn() and setSpeakerphoneOn().
- // If applications are using AudioManager.setRouting() that is now deprecated, the routing
- // command will be ignored.
- if (mode == AudioSystem.MODE_INVALID) {
- switch (mask) {
- case AudioSystem.ROUTE_SPEAKER:
- // handle setSpeakerphoneOn()
- if (routes != 0 && !mSpeakerIsOn) {
- mSpeakerIsOn = true;
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
- incallMask = AudioSystem.ROUTE_ALL;
- } else if (routes == 0 && mSpeakerIsOn) {
- mSpeakerIsOn = false;
- if (mBluetoothScoIsConnected) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
- } else if (mHeadsetIsConnected) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
- } else {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
- }
- incallMask = AudioSystem.ROUTE_ALL;
- }
- break;
-
- case AudioSystem.ROUTE_BLUETOOTH_SCO:
- // handle setBluetoothScoOn()
- if (routes != 0 && !mBluetoothScoIsConnected) {
- mBluetoothScoIsConnected = true;
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
- mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_BLUETOOTH_SCO;
- mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_BLUETOOTH_SCO;
- incallMask = AudioSystem.ROUTE_ALL;
- // A2DP has higher priority than SCO headset, so headset connect/disconnect events
- // should not affect A2DP routing
- ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- } else if (routes == 0 && mBluetoothScoIsConnected) {
- mBluetoothScoIsConnected = false;
- if (mHeadsetIsConnected) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
- mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
- mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_HEADSET;
- } else {
- if (mSpeakerIsOn) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
- } else {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
- }
- mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_SPEAKER;
- mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_SPEAKER;
- }
- incallMask = AudioSystem.ROUTE_ALL;
- // A2DP has higher priority than SCO headset, so headset connect/disconnect events
- // should not affect A2DP routing
- ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- }
- break;
-
- case AudioSystem.ROUTE_HEADSET:
- // handle setWiredHeadsetOn()
- if (routes != 0 && !mHeadsetIsConnected) {
- mHeadsetIsConnected = true;
- // do not act upon headset connection if bluetooth SCO is connected to match phone app behavior
- if (!mBluetoothScoIsConnected) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
- mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
- mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_HEADSET;
- incallMask = AudioSystem.ROUTE_ALL;
- // A2DP has higher priority than wired headset, so headset connect/disconnect events
- // should not affect A2DP routing
- ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- }
- } else if (routes == 0 && mHeadsetIsConnected) {
- mHeadsetIsConnected = false;
- // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior
- if (!mBluetoothScoIsConnected) {
- if (mSpeakerIsOn) {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
- } else {
- mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
- }
- mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_SPEAKER;
- mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
- AudioSystem.ROUTE_SPEAKER;
-
- incallMask = AudioSystem.ROUTE_ALL;
- // A2DP has higher priority than wired headset, so headset connect/disconnect events
- // should not affect A2DP routing
- ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- }
- }
- break;
-
- case AudioSystem.ROUTE_BLUETOOTH_A2DP:
- // handle setBluetoothA2dpOn()
- if (routes != 0 && !mBluetoothA2dpIsConnected) {
- mBluetoothA2dpIsConnected = true;
- mRoutes[AudioSystem.MODE_RINGTONE] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
- mRoutes[AudioSystem.MODE_NORMAL] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
- // the audio flinger chooses A2DP as a higher priority,
- // so there is no need to disable other routes.
- ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
- } else if (routes == 0 && mBluetoothA2dpIsConnected) {
- mBluetoothA2dpIsConnected = false;
- mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
- // the audio flinger chooses A2DP as a higher priority,
- // so there is no need to disable other routes.
- ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
- normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
- }
- break;
- }
-
- // incallMask is != 0 means we must apply ne routing to MODE_IN_CALL mode
- if (incallMask != 0) {
- AudioSystem.setRouting(AudioSystem.MODE_IN_CALL,
- mRoutes[AudioSystem.MODE_IN_CALL],
- incallMask);
- }
- // ringtoneMask is != 0 means we must apply ne routing to MODE_RINGTONE mode
- if (ringtoneMask != 0) {
- AudioSystem.setRouting(AudioSystem.MODE_RINGTONE,
- mRoutes[AudioSystem.MODE_RINGTONE],
- ringtoneMask);
- }
- // normalMask is != 0 means we must apply ne routing to MODE_NORMAL mode
- if (normalMask != 0) {
- AudioSystem.setRouting(AudioSystem.MODE_NORMAL,
- mRoutes[AudioSystem.MODE_NORMAL],
- normalMask);
- }
-
- int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int index = mStreamStates[streamType].mIndex;
- syncRingerAndNotificationStreamVolume(streamType, index, true);
- setStreamVolumeInt(streamType, index, true, true);
- }
- }
- }
-
- /** @see AudioManager#getRouting(int) */
- public int getRouting(int mode) {
- return mRoutes[mode];
- }
-
- /** @see AudioManager#isMusicActive() */
- public boolean isMusicActive() {
- return AudioSystem.isMusicActive();
- }
-
- /** @see AudioManager#setParameter(String, String) */
- public void setParameter(String key, String value) {
- AudioSystem.setParameter(key, value);
- }
-
/** @see AudioManager#playSoundEffect(int) */
public void playSoundEffect(int effectType) {
sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
@@ -926,18 +715,29 @@ public class AudioService extends IAudioService.Stub {
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
VolumeStreamState streamState = mStreamStates[streamType];
- // there is no volume setting for STREAM_BLUETOOTH_SCO
- if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
- String settingName = System.VOLUME_SETTINGS[streamType];
- String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
-
- streamState.mIndex = streamState.getValidIndex(Settings.System.getInt(mContentResolver,
- settingName,
- AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
- streamState.mLastAudibleIndex = streamState.getValidIndex(Settings.System.getInt(mContentResolver,
- lastAudibleSettingName,
- streamState.mIndex > 0 ? streamState.mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
+ String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
+ String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
+ int index = Settings.System.getInt(mContentResolver,
+ settingName,
+ AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
+ if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
+ index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
+ } else {
+ index *= 10;
+ }
+ streamState.mIndex = streamState.getValidIndex(index);
+
+ index = (index + 5) / 10;
+ index = Settings.System.getInt(mContentResolver,
+ lastAudibleSettingName,
+ (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
+ if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
+ index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
+ } else {
+ index *= 10;
}
+ streamState.mLastAudibleIndex = streamState.getValidIndex(index);
+
// unmute stream that whas muted but is not affect by mute anymore
if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
int size = streamState.mDeathHandlers.size();
@@ -948,7 +748,7 @@ public class AudioService extends IAudioService.Stub {
}
// apply stream volume
if (streamState.muteCount() == 0) {
- AudioSystem.setVolume(streamType, streamState.mVolumes[streamState.mIndex]);
+ setStreamVolumeIndex(streamType, streamState.mIndex);
}
}
@@ -956,6 +756,54 @@ public class AudioService extends IAudioService.Stub {
setRingerModeInt(getRingerMode(), false);
}
+ /** @see AudioManager#setSpeakerphoneOn() */
+ public void setSpeakerphoneOn(boolean on){
+ if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
+ return;
+ }
+ if (on) {
+ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
+ mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
+ } else {
+ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+ mForcedUseForComm = AudioSystem.FORCE_NONE;
+ }
+ }
+
+ /** @see AudioManager#isSpeakerphoneOn() */
+ public boolean isSpeakerphoneOn() {
+ if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /** @see AudioManager#setBluetoothScoOn() */
+ public void setBluetoothScoOn(boolean on){
+ if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
+ return;
+ }
+ if (on) {
+ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
+ AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
+ mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
+ } else {
+ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
+ AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
+ mForcedUseForComm = AudioSystem.FORCE_NONE;
+ }
+ }
+
+ /** @see AudioManager#isBluetoothScoOn() */
+ public boolean isBluetoothScoOn() {
+ if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////////
@@ -969,7 +817,7 @@ public class AudioService extends IAudioService.Stub {
boolean adjustVolumeIndex = true;
int newRingerMode = mRingerMode;
- if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1
+ if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && (oldIndex + 5) / 10 == 1
&& direction == AudioManager.ADJUST_LOWER) {
newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
} else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
@@ -1026,7 +874,7 @@ public class AudioService extends IAudioService.Stub {
Log.w(TAG, "Couldn't connect to phone service", e);
}
- if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) {
+ if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
// Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
return AudioSystem.STREAM_BLUETOOTH_SCO;
} else if (isOffhook) {
@@ -1110,47 +958,36 @@ public class AudioService extends IAudioService.Stub {
private final String mLastAudibleVolumeIndexSettingName;
private final int mStreamType;
- private final int[] mVolumes;
+ private int mIndexMax;
private int mIndex;
private int mLastAudibleIndex;
private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
- private VolumeStreamState(String settingName, int streamType, int[] volumes) {
+ private VolumeStreamState(String settingName, int streamType) {
mVolumeIndexSettingName = settingName;
mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
mStreamType = streamType;
- mVolumes = volumes;
final ContentResolver cr = mContentResolver;
- mIndex = getValidIndex(Settings.System.getInt(cr, mVolumeIndexSettingName, AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
- mLastAudibleIndex = getValidIndex(Settings.System.getInt(cr,
- mLastAudibleVolumeIndexSettingName, mIndex > 0 ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
-
- AudioSystem.setVolume(streamType, volumes[mIndex]);
- mDeathHandlers = new ArrayList<VolumeDeathHandler>();
- }
-
- /**
- * Constructor to be used when there is no setting associated with the VolumeStreamState.
- *
- * @param defaultVolume Default volume of the stream to use.
- * @param streamType Type of the stream.
- * @param volumes Volumes levels associated with this stream.
- */
- private VolumeStreamState(int defaultVolume, int streamType, int[] volumes) {
- mVolumeIndexSettingName = null;
- mLastAudibleVolumeIndexSettingName = null;
- mIndex = mLastAudibleIndex = defaultVolume;
- mStreamType = streamType;
- mVolumes = volumes;
- AudioSystem.setVolume(mStreamType, defaultVolume);
+ mIndexMax = MAX_STREAM_VOLUME[streamType];
+ mIndex = Settings.System.getInt(cr,
+ mVolumeIndexSettingName,
+ AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
+ mLastAudibleIndex = Settings.System.getInt(cr,
+ mLastAudibleVolumeIndexSettingName,
+ (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
+ AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
+ mIndexMax *= 10;
+ mIndex = getValidIndex(10 * mIndex);
+ mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
+ setStreamVolumeIndex(streamType, mIndex);
mDeathHandlers = new ArrayList<VolumeDeathHandler>();
}
public boolean adjustIndex(int deltaIndex) {
- return setIndex(mIndex + deltaIndex, true);
+ return setIndex(mIndex + deltaIndex * 10, true);
}
public boolean setIndex(int index, boolean lastAudible) {
@@ -1161,6 +998,13 @@ public class AudioService extends IAudioService.Stub {
if (lastAudible) {
mLastAudibleIndex = mIndex;
}
+ // Apply change to all streams using this one as alias
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
+ mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
+ }
+ }
return true;
} else {
return false;
@@ -1168,7 +1012,7 @@ public class AudioService extends IAudioService.Stub {
}
public int getMaxIndex() {
- return mVolumes.length - 1;
+ return mIndexMax;
}
public void mute(IBinder cb, boolean state) {
@@ -1183,8 +1027,8 @@ public class AudioService extends IAudioService.Stub {
private int getValidIndex(int index) {
if (index < 0) {
return 0;
- } else if (index >= mVolumes.length) {
- return mVolumes.length - 1;
+ } else if (index > mIndexMax) {
+ return mIndexMax;
}
return index;
@@ -1318,8 +1162,16 @@ public class AudioService extends IAudioService.Stub {
private void setSystemVolume(VolumeStreamState streamState) {
// Adjust volume
- AudioSystem
- .setVolume(streamState.mStreamType, streamState.mVolumes[streamState.mIndex]);
+ setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
+
+ // Apply change to all streams using this one as alias
+ int numStreamTypes = AudioSystem.getNumStreamTypes();
+ for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
+ if (streamType != streamState.mStreamType &&
+ STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
+ setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
+ }
+ }
// Post a persist volume msg
sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
@@ -1327,12 +1179,10 @@ public class AudioService extends IAudioService.Stub {
}
private void persistVolume(VolumeStreamState streamState) {
- if (streamState.mStreamType != AudioManager.STREAM_BLUETOOTH_SCO) {
- System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
- streamState.mIndex);
- System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
- streamState.mLastAudibleIndex);
- }
+ System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
+ (streamState.mIndex + 5)/ 10);
+ System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
+ (streamState.mLastAudibleIndex + 5) / 10);
}
private void persistRingerMode() {
@@ -1421,25 +1271,45 @@ public class AudioService extends IAudioService.Stub {
Log.e(TAG, "Media server died.");
// Force creation of new IAudioflinger interface
mMediaServerOk = false;
- AudioSystem.getMode();
+ AudioSystem.isMusicActive();
break;
case MSG_MEDIA_SERVER_STARTED:
Log.e(TAG, "Media server started.");
- // Restore audio routing and stream volumes
- applyAudioSettings();
+ // Restore device connection states
+ Set set = mConnectedDevices.entrySet();
+ Iterator i = set.iterator();
+ while(i.hasNext()){
+ Map.Entry device = (Map.Entry)i.next();
+ AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ (String)device.getValue());
+ }
+
+ // Restore call state
+ AudioSystem.setPhoneState(mMode);
+
+ // Restore forced usage for communcations and record
+ AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
+ AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
+
+ // Restore stream volumes
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
- int volume;
+ int index;
VolumeStreamState streamState = mStreamStates[streamType];
+ AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
if (streamState.muteCount() == 0) {
- volume = streamState.mVolumes[streamState.mIndex];
+ index = streamState.mIndex;
} else {
- volume = streamState.mVolumes[0];
+ index = 0;
}
- AudioSystem.setVolume(streamType, volume);
+ setStreamVolumeIndex(streamType, index);
}
- setRingerMode(mRingerMode);
+
+ // Restore ringer mode
+ setRingerModeInt(getRingerMode(), false);
+
mMediaServerOk = true;
break;
@@ -1451,28 +1321,189 @@ public class AudioService extends IAudioService.Stub {
}
private class SettingsObserver extends ContentObserver {
-
+
SettingsObserver() {
super(new Handler());
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
+ mContentResolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
-
- mRingerModeAffectedStreams = Settings.System.getInt(mContentResolver,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- 0);
+ synchronized (mSettingsLock) {
+ int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ 0);
+ if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
+ /*
+ * Ensure all stream types that should be affected by ringer mode
+ * are in the proper state.
+ */
+ mRingerModeAffectedStreams = ringerModeAffectedStreams;
+ setRingerModeInt(getRingerMode(), false);
+ }
- /*
- * Ensure all stream types that should be affected by ringer mode
- * are in the proper state.
- */
- setRingerModeInt(getRingerMode(), false);
+ int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
+ 1);
+ if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
+ mNotificationsUseRingVolume = notificationsUseRingVolume;
+ if (mNotificationsUseRingVolume == 1) {
+ STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+ } else {
+ STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
+ // Persist notification volume volume as it was not persisted while aliased to ring volume
+ sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
+ SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], PERSIST_DELAY);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Receiver for misc intent broadcasts the Phone app cares about.
+ */
+ private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+ int state = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
+ BluetoothA2dp.STATE_DISCONNECTED);
+ BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothIntent.DEVICE);
+ String address = btDevice.getAddress();
+ boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
+ ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
+
+ if (isConnected &&
+ state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ address);
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+ } else if (!isConnected &&
+ (state == BluetoothA2dp.STATE_CONNECTED ||
+ state == BluetoothA2dp.STATE_PLAYING)) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ address);
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
+ address);
+ }
+ } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
+ int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
+ BluetoothHeadset.STATE_ERROR);
+ int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
+ BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothIntent.DEVICE);
+ String address = null;
+ int btClass = BluetoothClass.ERROR;
+ if (btDevice != null) {
+ address = btDevice.getAddress();
+ btClass = btDevice.getBluetoothClass();
+ if (BluetoothClass.Device.Major.getDeviceMajor(btClass) ==
+ BluetoothClass.Device.Major.AUDIO_VIDEO) {
+ switch (BluetoothClass.Device.getDevice(btClass)) {
+ case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
+ device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+ break;
+ case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
+ device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ boolean isConnected = (mConnectedDevices.containsKey(device) &&
+ ((String)mConnectedDevices.get(device)).equals(address));
+
+ if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ address);
+ mConnectedDevices.remove(device);
+ } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
+ AudioSystem.setDeviceConnectionState(device,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ address);
+ mConnectedDevices.put(new Integer(device), address);
+ }
+ } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
+ int state = intent.getIntExtra("state", 0);
+ if ((state & BIT_HEADSET) == 0 &&
+ (mHeadsetState & BIT_HEADSET) != 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
+ } else if ((state & BIT_HEADSET) != 0 &&
+ (mHeadsetState & BIT_HEADSET) == 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
+ }
+ if ((state & BIT_HEADSET_NO_MIC) == 0 &&
+ (mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
+ } else if ((state & BIT_HEADSET_NO_MIC) != 0 &&
+ (mHeadsetState & BIT_HEADSET_NO_MIC) == 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
+ }
+ if ((state & BIT_TTY) == 0 &&
+ (mHeadsetState & BIT_TTY) != 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_TTY);
+ } else if ((state & BIT_TTY) != 0 &&
+ (mHeadsetState & BIT_TTY) == 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_TTY), "");
+ }
+ if ((state & BIT_FM_HEADSET) == 0 &&
+ (mHeadsetState & BIT_FM_HEADSET) != 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_HEADPHONE);
+ } else if ((state & BIT_FM_HEADSET) != 0 &&
+ (mHeadsetState & BIT_FM_HEADSET) == 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_HEADPHONE), "");
+ }
+ if ((state & BIT_FM_SPEAKER) == 0 &&
+ (mHeadsetState & BIT_FM_SPEAKER) != 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ "");
+ mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_SPEAKER);
+ } else if ((state & BIT_FM_SPEAKER) != 0 &&
+ (mHeadsetState & BIT_FM_SPEAKER) == 0) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ "");
+ mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_SPEAKER), "");
+ }
+ mHeadsetState = state;
+ }
}
-
}
-
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 5917ab9..dbf6d9d 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -45,38 +45,21 @@ public class AudioSystem
public static final int STREAM_NOTIFICATION = 5;
/* @hide The audio stream for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
+ /* @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
+ public static final int STREAM_SYSTEM_ENFORCED = 7;
+ /* @hide The audio stream for DTMF tones */
+ public static final int STREAM_DTMF = 8;
+ /* @hide The audio stream for text to speech (TTS) */
+ public static final int STREAM_TTS = 9;
/**
* @deprecated Use {@link #numStreamTypes() instead}
*/
public static final int NUM_STREAMS = 5;
// Expose only the getter method publicly so we can change it in the future
- private static final int NUM_STREAM_TYPES = 7;
+ private static final int NUM_STREAM_TYPES = 10;
public static final int getNumStreamTypes() { return NUM_STREAM_TYPES; }
- /* max and min volume levels */
- /* Maximum volume setting, for use with setVolume(int,int) */
- public static final int MAX_VOLUME = 100;
- /* Minimum volume setting, for use with setVolume(int,int) */
- public static final int MIN_VOLUME = 0;
-
- /*
- * Sets the volume of a specified audio stream.
- *
- * param type the stream type to set the volume of (e.g. STREAM_MUSIC)
- * param volume the volume level to set (0-100)
- * return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR
- */
- public static native int setVolume(int type, int volume);
-
- /*
- * Returns the volume of a specified audio stream.
- *
- * param type the stream type to get the volume of (e.g. STREAM_MUSIC)
- * return the current volume (0-100)
- */
- public static native int getVolume(int type);
-
/*
* Sets the microphone mute on or off.
*
@@ -101,17 +84,23 @@ public class AudioSystem
* it can route the audio appropriately.
* return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR
*/
- public static native int setMode(int mode);
-
+ /** @deprecated use {@link #setPhoneState(int)} */
+ public static int setMode(int mode) {
+ return AUDIO_STATUS_ERROR;
+ }
/*
* Returns the current audio mode.
*
* return the current audio mode (NORMAL, RINGTONE, or IN_CALL).
* Returns the current current audio state from the HAL.
+ *
*/
- public static native int getMode();
+ /** @deprecated Do not use. */
+ public static int getMode() {
+ return MODE_INVALID;
+ }
- /* modes for setMode/getMode/setRoute/getRoute */
+ /* modes for setPhoneState */
public static final int MODE_INVALID = -2;
public static final int MODE_CURRENT = -1;
public static final int MODE_NORMAL = 0;
@@ -121,15 +110,20 @@ public class AudioSystem
/* Routing bits for setRouting/getRouting API */
- public static final int ROUTE_EARPIECE = (1 << 0);
- public static final int ROUTE_SPEAKER = (1 << 1);
-
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_EARPIECE = (1 << 0);
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_SPEAKER = (1 << 1);
/** @deprecated use {@link #ROUTE_BLUETOOTH_SCO} */
@Deprecated public static final int ROUTE_BLUETOOTH = (1 << 2);
- public static final int ROUTE_BLUETOOTH_SCO = (1 << 2);
- public static final int ROUTE_HEADSET = (1 << 3);
- public static final int ROUTE_BLUETOOTH_A2DP = (1 << 4);
- public static final int ROUTE_ALL = 0xFFFFFFFF;
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_BLUETOOTH_SCO = (1 << 2);
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_HEADSET = (1 << 3);
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_BLUETOOTH_A2DP = (1 << 4);
+ /** @deprecated */
+ @Deprecated public static final int ROUTE_ALL = 0xFFFFFFFF;
/*
* Sets the audio routing for a specified mode
@@ -141,7 +135,10 @@ public class AudioSystem
* ROUTE_xxx types. Unset bits indicate the route should be left unchanged
* return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR
*/
- public static native int setRouting(int mode, int routes, int mask);
+ /** @deprecated use {@link #setDeviceConnectionState(int,int,String)} */
+ public static int setRouting(int mode, int routes, int mask) {
+ return AUDIO_STATUS_ERROR;
+ }
/*
* Returns the current audio routing bit vector for a specified mode.
@@ -150,7 +147,10 @@ public class AudioSystem
* return an audio route bit vector that can be compared with ROUTE_xxx
* bits
*/
- public static native int getRouting(int mode);
+ /** @deprecated use {@link #getDeviceConnectionState(int,String)} */
+ public static int getRouting(int mode) {
+ return 0;
+ }
/*
* Checks whether any music is active.
@@ -160,17 +160,23 @@ public class AudioSystem
public static native boolean isMusicActive();
/*
- * Sets a generic audio configuration parameter. The use of these parameters
+ * Sets a group generic audio configuration parameters. The use of these parameters
* are platform dependant, see libaudio
*
- * ** Temporary interface - DO NOT USE
- *
- * TODO: Replace with a more generic key:value get/set mechanism
+ * param keyValuePairs list of parameters key value pairs in the form:
+ * key1=value1;key2=value2;...
+ */
+ public static native int setParameters(String keyValuePairs);
+
+ /*
+ * Gets a group generic audio configuration parameters. The use of these parameters
+ * are platform dependant, see libaudio
*
- * param key name of parameter to set. Must not be null.
- * param value value of parameter. Must not be null.
+ * param keys list of parameters
+ * return value: list of parameters key value pairs in the form:
+ * key1=value1;key2=value2;...
*/
- public static native void setParameter(String key, String value);
+ public static native String getParameters(String keys);
/*
private final static String TAG = "audio";
@@ -220,4 +226,68 @@ public class AudioSystem
mErrorCallback.onError(error);
}
}
+
+ /*
+ * AudioPolicyService methods
+ */
+
+ // output devices
+ public static final int DEVICE_OUT_EARPIECE = 0x1;
+ public static final int DEVICE_OUT_SPEAKER = 0x2;
+ public static final int DEVICE_OUT_WIRED_HEADSET = 0x4;
+ public static final int DEVICE_OUT_WIRED_HEADPHONE = 0x8;
+ public static final int DEVICE_OUT_BLUETOOTH_SCO = 0x10;
+ public static final int DEVICE_OUT_BLUETOOTH_SCO_HEADSET = 0x20;
+ public static final int DEVICE_OUT_BLUETOOTH_SCO_CARKIT = 0x40;
+ public static final int DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
+ public static final int DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
+ public static final int DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
+ public static final int DEVICE_OUT_AUX_DIGITAL = 0x400;
+ public static final int DEVICE_OUT_FM_HEADPHONE = 0x800;
+ public static final int DEVICE_OUT_FM_SPEAKER = 0x1000;
+ public static final int DEVICE_OUT_TTY = 0x2000;
+ public static final int DEVICE_OUT_DEFAULT = 0x8000;
+ // input devices
+ public static final int DEVICE_IN_COMMUNICATION = 0x10000;
+ public static final int DEVICE_IN_AMBIENT = 0x20000;
+ public static final int DEVICE_IN_BUILTIN_MIC1 = 0x40000;
+ public static final int DEVICE_IN_BUILTIN_MIC2 = 0x80000;
+ public static final int DEVICE_IN_MIC_ARRAY = 0x100000;
+ public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x200000;
+ public static final int DEVICE_IN_WIRED_HEADSET = 0x400000;
+ public static final int DEVICE_IN_AUX_DIGITAL = 0x800000;
+ public static final int DEVICE_IN_DEFAULT = 0x80000000;
+
+ // device states
+ public static final int DEVICE_STATE_UNAVAILABLE = 0;
+ public static final int DEVICE_STATE_AVAILABLE = 1;
+
+ // phone state
+ public static final int PHONE_STATE_OFFCALL = 0;
+ public static final int PHONE_STATE_RINGING = 1;
+ public static final int PHONE_STATE_INCALL = 2;
+
+ // config for setForceUse
+ public static final int FORCE_NONE = 0;
+ public static final int FORCE_SPEAKER = 1;
+ public static final int FORCE_HEADPHONES = 2;
+ public static final int FORCE_BT_SCO = 3;
+ public static final int FORCE_BT_A2DP = 4;
+ public static final int FORCE_WIRED_ACCESSORY = 5;
+ public static final int FORCE_DEFAULT = FORCE_NONE;
+
+ // usage for serForceUse
+ public static final int FOR_COMMUNICATION = 0;
+ public static final int FOR_MEDIA = 1;
+ public static final int FOR_RECORD = 2;
+
+ public static native int setDeviceConnectionState(int device, int state, String device_address);
+ public static native int getDeviceConnectionState(int device, String device_address);
+ public static native int setPhoneState(int state);
+ public static native int setRingerMode(int mode, int mask);
+ public static native int setForceUse(int usage, int config);
+ public static native int getForceUse(int usage);
+ public static native int initStreamVolume(int stream, int indexMin, int indexMax);
+ public static native int setStreamVolumeIndex(int stream, int index);
+ public static native int getStreamVolumeIndex(int stream);
}
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5f1be9d..1e8d72f 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -120,7 +120,7 @@ public class AudioTrack
public static final int ERROR_INVALID_OPERATION = -3;
private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -16;
- private static final int ERROR_NATIVESETUP_INVALIDCHANNELCOUNT = -17;
+ private static final int ERROR_NATIVESETUP_INVALIDCHANNELMASK = -17;
private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -18;
private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -19;
private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -20;
@@ -181,10 +181,15 @@ public class AudioTrack
*/
private int mSampleRate = 22050;
/**
- * The number of input audio channels (1 is mono, 2 is stereo).
+ * The number of audio output channels (1 is mono, 2 is stereo).
*/
private int mChannelCount = 1;
/**
+ * The audio channel mask.
+ */
+ private int mChannels = AudioFormat.CHANNEL_OUT_MONO;
+
+ /**
* The type of the audio stream to play. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
@@ -198,7 +203,7 @@ public class AudioTrack
/**
* The current audio channel configuration.
*/
- private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ private int mChannelConfiguration = AudioFormat.CHANNEL_OUT_MONO;
/**
* The encoding of the audio samples.
* @see AudioFormat#ENCODING_PCM_8BIT
@@ -235,8 +240,8 @@ public class AudioTrack
* @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
* not limited to) 44100, 22050 and 11025.
* @param channelConfig describes the configuration of the audio channels.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
- * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}
+ * See {@link AudioFormat#CHANNEL_OUT_MONO} and
+ * {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -266,7 +271,7 @@ public class AudioTrack
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this),
- mStreamType, mSampleRate, mChannelCount, mAudioFormat,
+ mStreamType, mSampleRate, mChannels, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
@@ -286,6 +291,7 @@ public class AudioTrack
// postconditions:
// mStreamType is valid
// mChannelCount is valid
+ // mChannels is valid
// mAudioFormat is valid
// mSampleRate is valid
// mDataLoadMode is valid
@@ -298,7 +304,8 @@ public class AudioTrack
&& (streamType != AudioManager.STREAM_RING) && (streamType != AudioManager.STREAM_SYSTEM)
&& (streamType != AudioManager.STREAM_VOICE_CALL)
&& (streamType != AudioManager.STREAM_NOTIFICATION)
- && (streamType != AudioManager.STREAM_BLUETOOTH_SCO)) {
+ && (streamType != AudioManager.STREAM_BLUETOOTH_SCO)
+ && (streamType != AudioManager.STREAM_DTMF)) {
throw (new IllegalArgumentException("Invalid stream type."));
} else {
mStreamType = streamType;
@@ -315,18 +322,23 @@ public class AudioTrack
//--------------
// channel config
+ mChannelConfiguration = channelConfig;
+
switch (channelConfig) {
- case AudioFormat.CHANNEL_CONFIGURATION_DEFAULT:
+ case AudioFormat.CHANNEL_OUT_DEFAULT: //AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
+ case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
mChannelCount = 1;
- mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ mChannels = AudioFormat.CHANNEL_OUT_MONO;
break;
+ case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
mChannelCount = 2;
- mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ mChannels = AudioFormat.CHANNEL_OUT_STEREO;
break;
default:
mChannelCount = 0;
+ mChannels = AudioFormat.CHANNEL_INVALID;
mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_INVALID;
throw(new IllegalArgumentException("Unsupported channel configuration."));
}
@@ -452,8 +464,8 @@ public class AudioTrack
/**
* Returns the configured channel configuration.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO}
- * and {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}.
+ * See {@link AudioFormat#CHANNEL_OUT_MONO}
+ * and {@link AudioFormat#CHANNEL_OUT_STEREO}.
*/
public int getChannelConfiguration() {
return mChannelConfiguration;
@@ -531,8 +543,8 @@ public class AudioTrack
* the expected frequency at which the buffer will be refilled with additional data to play.
* @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
- * See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
- * {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}
+ * See {@link AudioFormat#CHANNEL_OUT_MONO} and
+ * {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -544,9 +556,11 @@ public class AudioTrack
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch(channelConfig) {
+ case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
+ case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;
break;
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6d7c0ae..aba40b3 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -18,6 +18,9 @@ package android.media;
import android.util.Log;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@@ -59,7 +62,7 @@ public class ExifInterface {
// The Exif tag names
public static final String TAG_ORIENTATION = "Orientation";
- public static final String TAG_DATE_TIME_ORIGINAL = "DateTimeOriginal";
+ public static final String TAG_DATETIME = "DateTime";
public static final String TAG_MAKE = "Make";
public static final String TAG_MODEL = "Model";
public static final String TAG_FLASH = "Flash";
@@ -321,6 +324,29 @@ public class ExifInterface {
return latlng;
}
+ private static SimpleDateFormat sFormatter =
+ new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+
+ // Returns number of milliseconds since Jan. 1, 1970, midnight GMT.
+ // Returns -1 if the date time information if not available.
+ public static long getDateTime(HashMap<String, String> exifData) {
+ if (exifData == null) {
+ return -1;
+ }
+
+ String dateTimeString = exifData.get(ExifInterface.TAG_DATETIME);
+ if (dateTimeString == null) return -1;
+
+ ParsePosition pos = new ParsePosition(0);
+ try {
+ Date date = sFormatter.parse(dateTimeString, pos);
+ if (date == null) return -1;
+ return date.getTime();
+ } catch (IllegalArgumentException ex) {
+ return -1;
+ }
+ }
+
public static float convertRationalLatLonToFloat(
String rationalString, String ref) {
try {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 9a8264f..d3d2d29 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -29,9 +29,9 @@ interface IAudioService {
void setStreamVolume(int streamType, int index, int flags);
- void setStreamSolo(int streamType, boolean state, IBinder cb);
+ void setStreamSolo(int streamType, boolean state, IBinder cb);
- void setStreamMute(int streamType, boolean state, IBinder cb);
+ void setStreamMute(int streamType, boolean state, IBinder cb);
int getStreamVolume(int streamType);
@@ -46,23 +46,11 @@ interface IAudioService {
int getVibrateSetting(int vibrateType);
boolean shouldVibrate(int vibrateType);
-
- void setMicrophoneMute(boolean on);
-
- boolean isMicrophoneMute();
void setMode(int mode);
int getMode();
- void setRouting(int mode, int routes, int mask);
-
- int getRouting(int mode);
-
- boolean isMusicActive();
-
- void setParameter(String key, String value);
-
oneway void playSoundEffect(int effectType);
oneway void playSoundEffectVolume(int effectType, float volume);
@@ -72,4 +60,12 @@ interface IAudioService {
oneway void unloadSoundEffects();
oneway void reloadAudioSettings();
+
+ void setSpeakerphoneOn(boolean on);
+
+ boolean isSpeakerphoneOn();
+
+ void setBluetoothScoOn(boolean on);
+
+ boolean isBluetoothScoOn();
}
diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java
index d75d81d..1570db4 100644
--- a/media/java/android/media/JetPlayer.java
+++ b/media/java/android/media/JetPlayer.java
@@ -89,7 +89,7 @@ public class JetPlayer
// Jet rendering audio parameters
private static final int JET_OUTPUT_RATE = 22050; // _SAMPLE_RATE_22050 in Android.mk
private static final int JET_OUTPUT_CHANNEL_CONFIG =
- AudioFormat.CHANNEL_CONFIGURATION_STEREO; // NUM_OUTPUT_CHANNELS=2 in Android.mk
+ AudioFormat.CHANNEL_OUT_STEREO; // NUM_OUTPUT_CHANNELS=2 in Android.mk
//--------------------------------------------
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 8be11df..03ffc67 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -41,8 +41,9 @@ public class MediaFile {
public static final int FILE_TYPE_AWB = 5;
public static final int FILE_TYPE_WMA = 6;
public static final int FILE_TYPE_OGG = 7;
+ public static final int FILE_TYPE_AAC = 8;
private static final int FIRST_AUDIO_FILE_TYPE = FILE_TYPE_MP3;
- private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_OGG;
+ private static final int LAST_AUDIO_FILE_TYPE = FILE_TYPE_AAC;
// MIDI file types
public static final int FILE_TYPE_MID = 11;
@@ -57,8 +58,9 @@ public class MediaFile {
public static final int FILE_TYPE_3GPP = 23;
public static final int FILE_TYPE_3GPP2 = 24;
public static final int FILE_TYPE_WMV = 25;
+ public static final int FILE_TYPE_ASF = 26;
private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
- private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_WMV;
+ private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_ASF;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -104,6 +106,7 @@ public class MediaFile {
addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
addFileType("OGA", FILE_TYPE_OGG, "application/ogg");
+ addFileType("AAC", FILE_TYPE_AAC, "audio/aac");
addFileType("MID", FILE_TYPE_MID, "audio/midi");
addFileType("MIDI", FILE_TYPE_MID, "audio/midi");
@@ -121,6 +124,7 @@ public class MediaFile {
addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
+ addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg");
addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg");
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 3a49a5f..cecf4f8 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -35,6 +35,7 @@ public class MediaMetadataRetriever
{
static {
System.loadLibrary("media_jni");
+ native_init();
}
// The field below is accessed by native methods
@@ -211,7 +212,8 @@ public class MediaMetadataRetriever
* allocated internally.
*/
public native void release();
- private native void native_setup();
+ private native void native_setup();
+ private static native void native_init();
private native final void native_finalize();
@@ -252,5 +254,6 @@ public class MediaMetadataRetriever
public static final int METADATA_KEY_VIDEO_FORMAT = 18;
public static final int METADATA_KEY_VIDEO_HEIGHT = 19;
public static final int METADATA_KEY_VIDEO_WIDTH = 20;
+ public static final int METADATA_KEY_WRITER = 21;
// Add more here...
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 3b46d69..6a9a9bd 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -23,6 +23,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.util.Log;
@@ -33,7 +34,7 @@ import android.media.AudioManager;
import java.io.FileDescriptor;
import java.io.IOException;
-
+import java.util.Set;
import java.lang.ref.WeakReference;
/**
@@ -430,11 +431,49 @@ import java.lang.ref.WeakReference;
*/
public class MediaPlayer
{
+ /**
+ Constant to retrieve only the new metadata since the last
+ call.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean METADATA_UPDATE_ONLY = true;
+
+ /**
+ Constant to retrieve all the metadata.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean METADATA_ALL = false;
+
+ /**
+ Constant to enable the metadata filter during retrieval.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean APPLY_METADATA_FILTER = true;
+
+ /**
+ Constant to disable the metadata filter during retrieval.
+ // FIXME: unhide.
+ // FIXME: add link to getMetadata(boolean, boolean)
+ {@hide}
+ */
+ public static final boolean BYPASS_METADATA_FILTER = false;
+
static {
System.loadLibrary("media_jni");
+ native_init();
}
private final static String TAG = "MediaPlayer";
+ // Name of the remote interface for the media player. Must be kept
+ // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE
+ // macro invocation in IMediaPlayer.cpp
+ private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";
private int mNativeContext; // accessed by native methods
private int mListenerContext; // accessed by native methods
@@ -475,6 +514,43 @@ public class MediaPlayer
private native void _setVideoSurface();
/**
+ * Create a request parcel which can be routed to the native media
+ * player using {@link #invoke(Parcel, Parcel)}. The Parcel
+ * returned has the proper InterfaceToken set. The caller should
+ * not overwrite that token, i.e it can only append data to the
+ * Parcel.
+ *
+ * @return A parcel suitable to hold a request for the native
+ * player.
+ */
+ public Parcel newRequest() {
+ Parcel parcel = Parcel.obtain();
+ parcel.writeInterfaceToken(IMEDIA_PLAYER);
+ return parcel;
+ }
+
+ /**
+ * Invoke a generic method on the native player using opaque
+ * parcels for the request and reply. Both payloads' format is a
+ * convention between the java caller and the native player.
+ * Must be called after setDataSource to make sure a native player
+ * exists.
+ *
+ * @param request Parcel with the data for the extension. The
+ * caller must use {@link #newRequest()} to get one.
+ *
+ * @param reply Output parcel with the data returned by the
+ * native player.
+ *
+ * @return The status code see utils/Errors.h
+ */
+ public int invoke(Parcel request, Parcel reply) {
+ int retcode = native_invoke(request, reply);
+ reply.setDataPosition(0);
+ return retcode;
+ }
+
+ /**
* Sets the SurfaceHolder to use for displaying the video portion of the media.
* This call is optional. Not calling it when playing back a video will
* result in only the audio track being played.
@@ -838,6 +914,89 @@ public class MediaPlayer
public native int getDuration();
/**
+ * Gets the media metadata.
+ *
+ * @param update_only controls whether the full set of available
+ * metadata is returned or just the set that changed since the
+ * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
+ * #METADATA_ALL}.
+ *
+ * @param apply_filter if true only metadata that matches the
+ * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
+ * #BYPASS_METADATA_FILTER}.
+ *
+ * @return The metadata, possibly empty. null if an error occured.
+ // FIXME: unhide.
+ * {@hide}
+ */
+ public Metadata getMetadata(final boolean update_only,
+ final boolean apply_filter) {
+ Parcel reply = Parcel.obtain();
+ Metadata data = new Metadata();
+
+ if (!native_getMetadata(update_only, apply_filter, reply)) {
+ reply.recycle();
+ return null;
+ }
+
+ // Metadata takes over the parcel, don't recycle it unless
+ // there is an error.
+ if (!data.parse(reply)) {
+ reply.recycle();
+ return null;
+ }
+ return data;
+ }
+
+ /**
+ * Set a filter for the metadata update notification and update
+ * retrieval. The caller provides 2 set of metadata keys, allowed
+ * and blocked. The blocked set always takes precedence over the
+ * allowed one.
+ * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
+ * shorthands to allow/block all or no metadata.
+ *
+ * By default, there is no filter set.
+ *
+ * @param allow Is the set of metadata the client is interested
+ * in receiving new notifications for.
+ * @param block Is the set of metadata the client is not interested
+ * in receiving new notifications for.
+ * @return The call status code.
+ *
+ // FIXME: unhide.
+ * {@hide}
+ */
+ public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
+ // Do our serialization manually instead of calling
+ // Parcel.writeArray since the sets are made of the same type
+ // we avoid paying the price of calling writeValue (used by
+ // writeArray) which burns an extra int per element to encode
+ // the type.
+ Parcel request = newRequest();
+
+ // The parcel starts already with an interface token. There
+ // are 2 filters. Each one starts with a 4bytes number to
+ // store the len followed by a number of int (4 bytes as well)
+ // representing the metadata type.
+ int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
+
+ if (request.dataCapacity() < capacity) {
+ request.setDataCapacity(capacity);
+ }
+
+ request.writeInt(allow.size());
+ for(Integer t: allow) {
+ request.writeInt(t);
+ }
+ request.writeInt(block.size());
+ for(Integer t: block) {
+ request.writeInt(t);
+ }
+ return native_setMetadataFilter(request);
+ }
+
+ /**
* Releases resources associated with this MediaPlayer object.
* It is considered good practice to call this method when you're
* done using the MediaPlayer.
@@ -915,8 +1074,46 @@ public class MediaPlayer
*/
public native Bitmap getFrameAt(int msec) throws IllegalStateException;
+ /**
+ * @param request Parcel destinated to the media player. The
+ * Interface token must be set to the IMediaPlayer
+ * one to be routed correctly through the system.
+ * @param reply[out] Parcel that will contain the reply.
+ * @return The status code.
+ */
+ private native final int native_invoke(Parcel request, Parcel reply);
+
+
+ /**
+ * @param update_only If true fetch only the set of metadata that have
+ * changed since the last invocation of getMetadata.
+ * The set is built using the unfiltered
+ * notifications the native player sent to the
+ * MediaPlayerService during that period of
+ * time. If false, all the metadatas are considered.
+ * @param apply_filter If true, once the metadata set has been built based on
+ * the value update_only, the current filter is applied.
+ * @param reply[out] On return contains the serialized
+ * metadata. Valid only if the call was successful.
+ * @return The status code.
+ */
+ private native final boolean native_getMetadata(boolean update_only,
+ boolean apply_filter,
+ Parcel reply);
+
+ /**
+ * @param request Parcel with the 2 serialized lists of allowed
+ * metadata types followed by the one to be
+ * dropped. Each list starts with an integer
+ * indicating the number of metadata type elements.
+ * @return The status code.
+ */
+ private native final int native_setMetadataFilter(Parcel request);
+
+ private static native final void native_init();
private native final void native_setup(Object mediaplayer_this);
private native final void native_finalize();
+
@Override
protected void finalize() { native_finalize(); }
@@ -1254,6 +1451,11 @@ public class MediaPlayer
*/
public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
+ /** A new set of metadata is available.
+ * @see android.media.MediaPlayer.OnInfoListener
+ */
+ public static final int MEDIA_INFO_METADATA_UPDATE = 802;
+
/**
* Interface definition of a callback to be invoked to communicate some
* info and/or warning about the media or its playback.
@@ -1270,6 +1472,7 @@ public class MediaPlayer
* <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
* <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
* <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
+ * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
* </ul>
* @param extra an extra code, specific to the info. Typically
* implementation dependant.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index be4b489..9bb00c6 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -57,6 +57,7 @@ public class MediaRecorder
{
static {
System.loadLibrary("media_jni");
+ native_init();
}
private final static String TAG = "MediaRecorder";
@@ -167,7 +168,7 @@ public class MediaRecorder
/** The following formats are audio only .aac or .amr formats **/
/** @deprecated Deprecated in favor of AMR_NB */
- /** @todo change link when AMR_NB is exposed. Deprecated in favor of {@link MediaRecorder.OutputFormat#AMR_NB} */
+ /** TODO: change link when AMR_NB is exposed. Deprecated in favor of MediaRecorder.OutputFormat.AMR_NB */
public static final int RAW_AMR = 3;
/** @hide AMR NB file format */
public static final int AMR_NB = 3;
@@ -655,6 +656,8 @@ public class MediaRecorder
*/
public native void release();
+ private static native final void native_init();
+
private native final void native_setup(Object mediarecorder_this) throws IllegalStateException;
private native final void native_finalize();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 8db874a..f6d30e0 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -99,6 +99,7 @@ public class MediaScanner
{
static {
System.loadLibrary("media_jni");
+ native_init();
}
private final static String TAG = "MediaScanner";
@@ -307,10 +308,14 @@ public class MediaScanner
private boolean mDefaultRingtoneSet;
/** Whether the scanner has set a default sound for the notification ringtone. */
private boolean mDefaultNotificationSet;
+ /** Whether the scanner has set a default sound for the alarm ringtone. */
+ private boolean mDefaultAlarmSet;
/** The filename for the default sound for the ringer ringtone. */
private String mDefaultRingtoneFilename;
/** The filename for the default sound for the notification ringtone. */
private String mDefaultNotificationFilename;
+ /** The filename for the default sound for the alarm ringtone. */
+ private String mDefaultAlarmAlertFilename;
/**
* The prefix for system properties that define the default sound for
* ringtones. Concatenate the name of the setting from Settings
@@ -369,6 +374,8 @@ public class MediaScanner
+ Settings.System.RINGTONE);
mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ Settings.System.NOTIFICATION_SOUND);
+ mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
+ + Settings.System.ALARM_ALERT);
}
private MyMediaScannerClient mClient = new MyMediaScannerClient();
@@ -389,6 +396,7 @@ public class MediaScanner
private String mPath;
private long mLastModified;
private long mFileSize;
+ private String mWriter;
public FileCacheEntry beginFile(String path, String mimeType, long lastModified, long fileSize) {
@@ -472,6 +480,7 @@ public class MediaScanner
mDuration = 0;
mPath = path;
mLastModified = lastModified;
+ mWriter = null;
return entry;
}
@@ -484,6 +493,22 @@ public class MediaScanner
doScanFile(path, mimeType, lastModified, fileSize, false);
}
+ private boolean isMetadataSupported(int fileType) {
+ if (mFileType == MediaFile.FILE_TYPE_MP3 ||
+ mFileType == MediaFile.FILE_TYPE_MP4 ||
+ mFileType == MediaFile.FILE_TYPE_M4A ||
+ mFileType == MediaFile.FILE_TYPE_3GPP ||
+ mFileType == MediaFile.FILE_TYPE_3GPP2 ||
+ mFileType == MediaFile.FILE_TYPE_OGG ||
+ mFileType == MediaFile.FILE_TYPE_AAC ||
+ mFileType == MediaFile.FILE_TYPE_MID ||
+ mFileType == MediaFile.FILE_TYPE_WMA) {
+ // we only extract metadata from MP3, M4A, OGG, MID, AAC and WMA files.
+ // check MP4 files, to determine if they contain only audio.
+ return true;
+ }
+ return false;
+ }
public Uri doScanFile(String path, String mimeType, long lastModified, long fileSize, boolean scanAlways) {
Uri result = null;
// long t1 = System.currentTimeMillis();
@@ -499,16 +524,7 @@ public class MediaScanner
boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
(!ringtones && !notifications && !alarms && !podcasts);
- if (mFileType == MediaFile.FILE_TYPE_MP3 ||
- mFileType == MediaFile.FILE_TYPE_MP4 ||
- mFileType == MediaFile.FILE_TYPE_M4A ||
- mFileType == MediaFile.FILE_TYPE_3GPP ||
- mFileType == MediaFile.FILE_TYPE_3GPP2 ||
- mFileType == MediaFile.FILE_TYPE_OGG ||
- mFileType == MediaFile.FILE_TYPE_MID ||
- mFileType == MediaFile.FILE_TYPE_WMA) {
- // we only extract metadata from MP3, M4A, OGG, MID and WMA files.
- // check MP4 files, to determine if they contain only audio.
+ if( isMetadataSupported(mFileType) ) {
processFile(path, mimeType, this);
} else if (MediaFile.isImageFileType(mFileType)) {
// we used to compute the width and height but it's not worth it
@@ -586,6 +602,8 @@ public class MediaScanner
mTrack = (num * 1000) + (mTrack % 1000);
} else if (name.equalsIgnoreCase("duration")) {
mDuration = parseSubstring(value, 0, 0);
+ } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
+ mWriter = value.trim();
}
}
@@ -709,6 +727,11 @@ public class MediaScanner
values.put(Images.Media.LATITUDE, latlng[0]);
values.put(Images.Media.LONGITUDE, latlng[1]);
}
+
+ long time = ExifInterface.getDateTime(exifData);
+ if (time != -1) {
+ values.put(Images.Media.DATE_TAKEN, time);
+ }
}
}
@@ -779,6 +802,12 @@ public class MediaScanner
setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
mDefaultRingtoneSet = true;
}
+ } else if (alarms && !mDefaultAlarmSet) {
+ if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
+ doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
+ setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
+ mDefaultAlarmSet = true;
+ }
}
return result;
@@ -803,6 +832,22 @@ public class MediaScanner
}
}
+ public void addNoMediaFolder(String path) {
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Images.ImageColumns.DATA, "");
+ String [] pathSpec = new String[] {path + '%'};
+ try {
+ mMediaProvider.update(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values,
+ MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec);
+ mMediaProvider.update(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values,
+ MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec);
+ mMediaProvider.update(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values,
+ MediaStore.Images.ImageColumns.DATA + " LIKE ?", pathSpec);
+ } catch (RemoteException e) {
+ throw new RuntimeException();
+ }
+ }
+
}; // end of anonymous MediaScannerClient instance
private void prescan(String filePath) throws RemoteException {
@@ -1412,6 +1457,7 @@ public class MediaScanner
public native byte[] extractAlbumArt(FileDescriptor fd);
+ private static native final void native_init();
private native final void native_setup();
private native final void native_finalize();
@Override
diff --git a/media/java/android/media/MediaScannerClient.java b/media/java/android/media/MediaScannerClient.java
index cf1a8da..258c3b4 100644
--- a/media/java/android/media/MediaScannerClient.java
+++ b/media/java/android/media/MediaScannerClient.java
@@ -25,11 +25,13 @@ public interface MediaScannerClient
public void scanFile(String path, String mimeType, long lastModified, long fileSize);
+ public void addNoMediaFolder(String path);
+
/**
* Called by native code to return metadata extracted from media files.
*/
public void handleStringTag(String name, String value);
-
+
/**
* Called by native code to return mime type extracted from DRM content.
*/
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
new file mode 100644
index 0000000..bd25da2
--- /dev/null
+++ b/media/java/android/media/Metadata.java
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.TimeZone;
+
+
+/**
+ Class to hold the media's metadata. Metadata are used
+ for human consumption and can be embedded in the media (e.g
+ shoutcast) or available from an external source. The source can be
+ local (e.g thumbnail stored in the DB) or remote (e.g caption
+ server).
+
+ Metadata is like a Bundle. It is sparse and each key can occur at
+ most once. The key is an integer and the value is the actual metadata.
+
+ The caller is expected to know the type of the metadata and call
+ the right get* method to fetch its value.
+
+ // FIXME: unhide.
+ {@hide}
+ */
+public class Metadata
+{
+ // The metadata are keyed using integers rather than more heavy
+ // weight strings. We considered using Bundle to ship the metadata
+ // between the native layer and the java layer but dropped that
+ // option since keeping in sync a native implementation of Bundle
+ // and the java one would be too burdensome. Besides Bundle uses
+ // String for its keys.
+ // The key range [0 8192) is reserved for the system.
+ //
+ // We manually serialize the data in Parcels. For large memory
+ // blob (bitmaps, raw pictures) we use MemoryFile which allow the
+ // client to make the data purge-able once it is done with it.
+ //
+
+ public static final int ANY = 0; // Never used for metadata returned, only for filtering.
+ // Keep in sync with kAny in MediaPlayerService.cpp
+
+ // TODO: Should we use numbers compatible with the metadata retriever?
+ public static final int TITLE = 1; // String
+ public static final int COMMENT = 2; // String
+ public static final int COPYRIGHT = 3; // String
+ public static final int ALBUM = 4; // String
+ public static final int ARTIST = 5; // String
+ public static final int AUTHOR = 6; // String
+ public static final int COMPOSER = 7; // String
+ public static final int GENRE = 8; // String
+ public static final int DATE = 9; // Date
+ public static final int DURATION = 10; // Integer(millisec)
+ public static final int CD_TRACK_NUM = 11; // Integer 1-based
+ public static final int CD_TRACK_MAX = 12; // Integer
+ public static final int RATING = 13; // String
+ public static final int ALBUM_ART = 14; // byte[]
+ public static final int VIDEO_FRAME = 15; // Bitmap
+ public static final int CAPTION = 16; // TimedText
+
+ public static final int BIT_RATE = 17; // Integer, Aggregate rate of
+ // all the streams in bps.
+
+ public static final int AUDIO_BIT_RATE = 18; // Integer, bps
+ public static final int VIDEO_BIT_RATE = 19; // Integer, bps
+ public static final int AUDIO_SAMPLE_RATE = 20; // Integer, Hz
+ public static final int VIDEO_FRAME_RATE = 21; // Integer, Hz
+
+ // See RFC2046 and RFC4281.
+ public static final int MIME_TYPE = 22; // String
+ public static final int AUDIO_CODEC = 23; // String
+ public static final int VIDEO_CODEC = 24; // String
+
+ public static final int VIDEO_HEIGHT = 25; // Integer
+ public static final int VIDEO_WIDTH = 26; // Integer
+ public static final int NUM_TRACKS = 27; // Integer
+ public static final int DRM_CRIPPLED = 28; // Boolean
+
+ // Playback capabilities.
+ public static final int PAUSE_AVAILABLE = 29; // Boolean
+ public static final int SEEK_BACKWARD_AVAILABLE = 30; // Boolean
+ public static final int SEEK_FORWARD_AVAILABLE = 31; // Boolean
+
+ private static final int LAST_SYSTEM = 31;
+ private static final int FIRST_CUSTOM = 8192;
+
+ // Shorthands to set the MediaPlayer's metadata filter.
+ public static final Set<Integer> MATCH_NONE = Collections.EMPTY_SET;
+ public static final Set<Integer> MATCH_ALL = Collections.singleton(ANY);
+
+ public static final int STRING_VAL = 1;
+ public static final int INTEGER_VAL = 2;
+ public static final int BOOLEAN_VAL = 3;
+ public static final int LONG_VAL = 4;
+ public static final int DOUBLE_VAL = 5;
+ public static final int TIMED_TEXT_VAL = 6;
+ public static final int DATE_VAL = 7;
+ public static final int BYTE_ARRAY_VAL = 8;
+ // FIXME: misses a type for shared heap is missing (MemoryFile).
+ // FIXME: misses a type for bitmaps.
+ private static final int LAST_TYPE = 8;
+
+ private static final String TAG = "media.Metadata";
+ private static final int kInt32Size = 4;
+ private static final int kMetaHeaderSize = 2 * kInt32Size; // size + marker
+ private static final int kRecordHeaderSize = 3 * kInt32Size; // size + id + type
+
+ private static final int kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+
+ // After a successful parsing, set the parcel with the serialized metadata.
+ private Parcel mParcel;
+
+ // Map to associate a Metadata key (e.g TITLE) with the offset of
+ // the record's payload in the parcel.
+ // Used to look up if a key was present too.
+ // Key: Metadata ID
+ // Value: Offset of the metadata type field in the record.
+ private final HashMap<Integer, Integer> mKeyToPosMap =
+ new HashMap<Integer, Integer>();
+
+ /**
+ * Helper class to hold a triple (time, duration, text). Can be used to
+ * implement caption.
+ */
+ public class TimedText {
+ private Date mTime;
+ private int mDuration; // millisec
+ private String mText;
+
+ public TimedText(Date time, int duration, String text) {
+ mTime = time;
+ mDuration = duration;
+ mText = text;
+ }
+
+ public String toString() {
+ StringBuilder res = new StringBuilder(80);
+ res.append(mTime).append("-").append(mDuration)
+ .append(":").append(mText);
+ return res.toString();
+ }
+ }
+
+ public Metadata() { }
+
+ /**
+ * Go over all the records, collecting metadata keys and records'
+ * type field offset in the Parcel. These are stored in
+ * mKeyToPosMap for latter retrieval.
+ * Format of a metadata record:
+ <pre>
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | record size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata key | // TITLE
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata type | // STRING_VAL
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | .... metadata payload .... |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ </pre>
+ * @param parcel With the serialized records.
+ * @param bytesLeft How many bytes in the parcel should be processed.
+ * @return false if an error occurred during parsing.
+ */
+ private boolean scanAllRecords(Parcel parcel, int bytesLeft) {
+ int recCount = 0;
+ boolean error = false;
+
+ mKeyToPosMap.clear();
+ while (bytesLeft > kRecordHeaderSize) {
+ final int start = parcel.dataPosition();
+ // Check the size.
+ final int size = parcel.readInt();
+
+ if (size <= kRecordHeaderSize) { // at least 1 byte should be present.
+ Log.e(TAG, "Record is too short");
+ error = true;
+ break;
+ }
+
+ // Check the metadata key.
+ final int metadataId = parcel.readInt();
+ if (!checkMetadataId(metadataId)) {
+ error = true;
+ break;
+ }
+
+ // Store the record offset which points to the type
+ // field so we can later on read/unmarshall the record
+ // payload.
+ if (mKeyToPosMap.containsKey(metadataId)) {
+ Log.e(TAG, "Duplicate metadata ID found");
+ error = true;
+ break;
+ }
+
+ mKeyToPosMap.put(metadataId, parcel.dataPosition());
+
+ // Check the metadata type.
+ final int metadataType = parcel.readInt();
+ if (metadataType <= 0 || metadataType > LAST_TYPE) {
+ Log.e(TAG, "Invalid metadata type " + metadataType);
+ error = true;
+ break;
+ }
+
+ // Skip to the next one.
+ parcel.setDataPosition(start + size);
+ bytesLeft -= size;
+ ++recCount;
+ }
+
+ if (0 != bytesLeft || error) {
+ Log.e(TAG, "Ran out of data or error on record " + recCount);
+ mKeyToPosMap.clear();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Check a parcel containing metadata is well formed. The header
+ * is checked as well as the individual records format. However, the
+ * data inside the record is not checked because we do lazy access
+ * (we check/unmarshall only data the user asks for.)
+ *
+ * Format of a metadata parcel:
+ <pre>
+ 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | metadata total size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | 'M' | 'E' | 'T' | 'A' |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | |
+ | .... metadata records .... |
+ | |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ </pre>
+ *
+ * @param parcel With the serialized data. Metadata keeps a
+ * reference on it to access it later on. The caller
+ * should not modify the parcel after this call (and
+ * not call recycle on it.)
+ * @return false if an error occurred.
+ */
+ public boolean parse(Parcel parcel) {
+ if (parcel.dataAvail() < kMetaHeaderSize) {
+ Log.e(TAG, "Not enough data " + parcel.dataAvail());
+ return false;
+ }
+
+ final int pin = parcel.dataPosition(); // to roll back in case of errors.
+ final int size = parcel.readInt();
+
+ // The extra kInt32Size below is to account for the int32 'size' just read.
+ if (parcel.dataAvail() + kInt32Size < size || size < kMetaHeaderSize) {
+ Log.e(TAG, "Bad size " + size + " avail " + parcel.dataAvail() + " position " + pin);
+ parcel.setDataPosition(pin);
+ return false;
+ }
+
+ // Checks if the 'M' 'E' 'T' 'A' marker is present.
+ final int kShouldBeMetaMarker = parcel.readInt();
+ if (kShouldBeMetaMarker != kMetaMarker ) {
+ Log.e(TAG, "Marker missing " + Integer.toHexString(kShouldBeMetaMarker));
+ parcel.setDataPosition(pin);
+ return false;
+ }
+
+ // Scan the records to collect metadata ids and offsets.
+ if (!scanAllRecords(parcel, size - kMetaHeaderSize)) {
+ parcel.setDataPosition(pin);
+ return false;
+ }
+ mParcel = parcel;
+ return true;
+ }
+
+ /**
+ * @return The set of metadata ID found.
+ */
+ public Set<Integer> keySet() {
+ return mKeyToPosMap.keySet();
+ }
+
+ /**
+ * @return true if a value is present for the given key.
+ */
+ public boolean has(final int metadataId) {
+ if (!checkMetadataId(metadataId)) {
+ throw new IllegalArgumentException("Invalid key: " + metadataId);
+ }
+ return mKeyToPosMap.containsKey(metadataId);
+ }
+
+ // Accessors.
+ // Caller must make sure the key is present using the {@code has}
+ // method otherwise a RuntimeException will occur.
+
+ public String getString(final int key) {
+ checkType(key, STRING_VAL);
+ return mParcel.readString();
+ }
+
+ public int getInt(final int key) {
+ checkType(key, INTEGER_VAL);
+ return mParcel.readInt();
+ }
+
+ public boolean getBoolean(final int key) {
+ checkType(key, BOOLEAN_VAL);
+ return mParcel.readInt() == 1;
+ }
+
+ public long getLong(final int key) {
+ checkType(key, LONG_VAL);
+ return mParcel.readLong();
+ }
+
+ public double getDouble(final int key) {
+ checkType(key, DOUBLE_VAL);
+ return mParcel.readDouble();
+ }
+
+ public byte[] getByteArray(final int key) {
+ checkType(key, BYTE_ARRAY_VAL);
+ return mParcel.createByteArray();
+ }
+
+ public Date getDate(final int key) {
+ checkType(key, DATE_VAL);
+ final long timeSinceEpoch = mParcel.readLong();
+ final String timeZone = mParcel.readString();
+
+ if (timeZone.length() == 0) {
+ return new Date(timeSinceEpoch);
+ } else {
+ TimeZone tz = TimeZone.getTimeZone(timeZone);
+ Calendar cal = Calendar.getInstance(tz);
+
+ cal.setTimeInMillis(timeSinceEpoch);
+ return cal.getTime();
+ }
+ }
+
+ public TimedText getTimedText(final int key) {
+ checkType(key, TIMED_TEXT_VAL);
+ final Date startTime = new Date(mParcel.readLong()); // epoch
+ final int duration = mParcel.readInt(); // millisec
+
+ return new TimedText(startTime,
+ duration,
+ mParcel.readString());
+ }
+
+ // @return the last available system metadata id. Ids are
+ // 1-indexed.
+ public static int lastSytemId() { return LAST_SYSTEM; }
+
+ // @return the first available cutom metadata id.
+ public static int firstCustomId() { return FIRST_CUSTOM; }
+
+ // @return the last value of known type. Types are 1-indexed.
+ public static int lastType() { return LAST_TYPE; }
+
+ // Check val is either a system id or a custom one.
+ // @param val Metadata key to test.
+ // @return true if it is in a valid range.
+ private boolean checkMetadataId(final int val) {
+ if (val <= ANY || (LAST_SYSTEM < val && val < FIRST_CUSTOM)) {
+ Log.e(TAG, "Invalid metadata ID " + val);
+ return false;
+ }
+ return true;
+ }
+
+ // Check the type of the data match what is expected.
+ private void checkType(final int key, final int expectedType) {
+ final int pos = mKeyToPosMap.get(key);
+
+ mParcel.setDataPosition(pos);
+
+ final int type = mParcel.readInt();
+ if (type != expectedType) {
+ throw new IllegalStateException("Wrong type " + expectedType + " but got " + type);
+ }
+ }
+}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 42edae6..8481410 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -122,8 +122,9 @@ public class RingtoneManager {
* current ringtone, which will be used to show a checkmark next to the item
* for this {@link Uri}. If showing an item for "Default" (@see
* {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also be one of
- * {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI} to have the "Default" item
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" item
* checked.
*
* @see #ACTION_RINGTONE_PICKER
@@ -134,8 +135,9 @@ public class RingtoneManager {
/**
* Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
* ringtone to play when the user attempts to preview the "Default"
- * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI} to have the "Default" point to
+ * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" point to
* the current sound for the given default sound type. If you are showing a
* ringtone picker for some other type of sound, you are free to provide any
* {@link Uri} here.
@@ -163,8 +165,9 @@ public class RingtoneManager {
* <p>
* It will be one of:
* <li> the picked ringtone,
- * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI} if the default was chosen,
+ * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen,
* <li> null if the "Silent" item was picked.
*
* @see #ACTION_RINGTONE_PICKER
@@ -602,21 +605,6 @@ public class RingtoneManager {
Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
}
- // Ringtone doesn't exist, use the fallback ringtone.
- try {
- AssetFileDescriptor afd = context.getResources().openRawResourceFd(
- com.android.internal.R.raw.fallbackring);
- if (afd != null) {
- Ringtone r = new Ringtone(context);
- r.open(afd);
- afd.close();
- return r;
- }
- } catch (Exception ex) {
- }
-
- // we should never get here
- Log.e(TAG, "unable to find a usable ringtone");
return null;
}
@@ -627,15 +615,16 @@ public class RingtoneManager {
*
* @param context A context used for querying.
* @param type The type whose default sound should be returned. One of
- * {@link #TYPE_RINGTONE} or {@link #TYPE_NOTIFICATION}.
+ * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
+ * {@link #TYPE_ALARM}.
* @return A {@link Uri} pointing to the default sound for the sound type.
* @see #setActualDefaultRingtoneUri(Context, int, Uri)
*/
public static Uri getActualDefaultRingtoneUri(Context context, int type) {
String setting = getSettingForType(type);
if (setting == null) return null;
- final String uriString = Settings.System.getString(context.getContentResolver(), setting);
- return uriString != null ? Uri.parse(uriString) : getValidRingtoneUri(context);
+ final String uriString = Settings.System.getString(context.getContentResolver(), setting);
+ return uriString != null ? Uri.parse(uriString) : null;
}
/**
@@ -643,14 +632,16 @@ public class RingtoneManager {
*
* @param context A context used for querying.
* @param type The type whose default sound should be set. One of
- * {@link #TYPE_RINGTONE} or {@link #TYPE_NOTIFICATION}.
+ * {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
+ * {@link #TYPE_ALARM}.
* @param ringtoneUri A {@link Uri} pointing to the default sound to set.
* @see #getActualDefaultRingtoneUri(Context, int)
*/
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
String setting = getSettingForType(type);
if (setting == null) return;
- Settings.System.putString(context.getContentResolver(), setting, ringtoneUri.toString());
+ Settings.System.putString(context.getContentResolver(), setting,
+ ringtoneUri != null ? ringtoneUri.toString() : null);
}
private static String getSettingForType(int type) {
@@ -658,6 +649,8 @@ public class RingtoneManager {
return Settings.System.RINGTONE;
} else if ((type & TYPE_NOTIFICATION) != 0) {
return Settings.System.NOTIFICATION_SOUND;
+ } else if ((type & TYPE_ALARM) != 0) {
+ return Settings.System.ALARM_ALERT;
} else {
return null;
}
@@ -677,8 +670,9 @@ public class RingtoneManager {
* Returns the type of a default {@link Uri}.
*
* @param defaultRingtoneUri The default {@link Uri}. For example,
- * {@link System#DEFAULT_RINGTONE_URI} or
- * {@link System#DEFAULT_NOTIFICATION_URI}.
+ * {@link System#DEFAULT_RINGTONE_URI},
+ * {@link System#DEFAULT_NOTIFICATION_URI}, or
+ * {@link System#DEFAULT_ALARM_ALERT_URI}.
* @return The type of the defaultRingtoneUri, or -1.
*/
public static int getDefaultType(Uri defaultRingtoneUri) {
@@ -688,6 +682,8 @@ public class RingtoneManager {
return TYPE_RINGTONE;
} else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) {
return TYPE_NOTIFICATION;
+ } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_ALARM_ALERT_URI)) {
+ return TYPE_ALARM;
} else {
return -1;
}
@@ -707,6 +703,8 @@ public class RingtoneManager {
return Settings.System.DEFAULT_RINGTONE_URI;
} else if ((type & TYPE_NOTIFICATION) != 0) {
return Settings.System.DEFAULT_NOTIFICATION_URI;
+ } else if ((type & TYPE_ALARM) != 0) {
+ return Settings.System.DEFAULT_ALARM_ALERT_URI;
} else {
return null;
}
diff --git a/media/java/android/media/ToneGenerator.java b/media/java/android/media/ToneGenerator.java
index e5ee9a3..d4ae80f 100644
--- a/media/java/android/media/ToneGenerator.java
+++ b/media/java/android/media/ToneGenerator.java
@@ -724,9 +724,9 @@ public class ToneGenerator
public static final int TONE_CDMA_SIGNAL_OFF = 98;
/** Maximum volume, for use with {@link #ToneGenerator(int,int)} */
- public static final int MAX_VOLUME = AudioSystem.MAX_VOLUME;
+ public static final int MAX_VOLUME = 100;
/** Minimum volume setting, for use with {@link #ToneGenerator(int,int)} */
- public static final int MIN_VOLUME = AudioSystem.MIN_VOLUME;
+ public static final int MIN_VOLUME = 0;
/**
@@ -744,7 +744,7 @@ public class ToneGenerator
* This method starts the playback of a tone of the specified type.
* only one tone can play at a time: if a tone is playing while this method is called,
* this tone is stopped and replaced by the one requested.
- * @param toneType The type of tone generate chosen from the following list:
+ * @param toneType The type of tone generated chosen from the following list:
* <ul>
* <li>{@link #TONE_DTMF_0}
* <li>{@link #TONE_DTMF_1}
@@ -846,7 +846,18 @@ public class ToneGenerator
* </ul>
* @see #ToneGenerator(int, int)
*/
- public native boolean startTone(int toneType);
+ public boolean startTone(int toneType) {
+ return startTone(toneType, -1);
+ }
+
+ /**
+ * This method starts the playback of a tone of the specified type for the specified duration.
+ * @param toneType The type of tone generated @see #startTone(int).
+ * @param durationMs The tone duration in milliseconds. If the tone is limited in time by definition,
+ * the actual duration will be the minimum of durationMs and the defined tone duration. Setting durationMs to -1,
+ * is equivalent to calling #startTone(int).
+ */
+ public native boolean startTone(int toneType, int durationMs);
/**
* This method stops the tone currently playing playback.
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 8ee0cbd..49a82e6 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -12,20 +12,20 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libopencore_player \
- libopencore_author \
libomx_amrenc_sharedlibrary \
libandroid_runtime \
libnativehelper \
- libcutils \
libutils \
+ libbinder \
libmedia \
- libsgl \
+ libskia \
libui
LOCAL_STATIC_LIBRARIES :=
LOCAL_C_INCLUDES += \
external/tremor/Tremor \
+ frameworks/base/core/jni \
$(PV_INCLUDES) \
$(JNI_H_INCLUDE) \
$(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp
index 51cb6c7..c4dd07e 100644
--- a/media/jni/android_media_AmrInputStream.cpp
+++ b/media/jni/android_media_AmrInputStream.cpp
@@ -169,13 +169,6 @@ static JNINativeMethod gMethods[] = {
int register_android_media_AmrInputStream(JNIEnv *env)
{
const char* const kClassPathName = "android/media/AmrInputStream";
- jclass clazz;
-
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find %s", kClassPathName);
- return -1;
- }
return AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 4624a18..49f8cdd 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -40,6 +40,7 @@ struct fields_t {
static fields_t fields;
static Mutex sLock;
+static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
static void process_media_retriever_call(JNIEnv *env, status_t opStatus, const char* exception, const char *message)
{
@@ -269,6 +270,36 @@ static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jo
android_media_MediaMetadataRetriever_release(env, thiz);
}
+// This function gets a field ID, which in turn causes class initialization.
+// It is called from a static block in MediaMetadataRetriever, which won't run until the
+// first time an instance of this class is used.
+static void android_media_MediaMetadataRetriever_native_init(JNIEnv *env)
+{
+ jclass clazz = env->FindClass(kClassPathName);
+ if (clazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaMetadataRetriever");
+ return;
+ }
+
+ fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (fields.context == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaMetadataRetriever.mNativeContext");
+ return;
+ }
+
+ fields.bitmapClazz = env->FindClass("android/graphics/Bitmap");
+ if (fields.bitmapClazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/graphics/Bitmap");
+ return;
+ }
+
+ fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[B)V");
+ if (fields.bitmapConstructor == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
+ return;
+ }
+}
+
static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
{
LOGV("native_setup");
@@ -292,36 +323,13 @@ static JNINativeMethod nativeMethods[] = {
{"release", "()V", (void *)android_media_MediaMetadataRetriever_release},
{"native_finalize", "()V", (void *)android_media_MediaMetadataRetriever_native_finalize},
{"native_setup", "()V", (void *)android_media_MediaMetadataRetriever_native_setup},
+ {"native_init", "()V", (void *)android_media_MediaMetadataRetriever_native_init},
};
-// Register native mehtods with Android runtime environment
+// This function only registers the native methods, and is called from
+// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaMetadataRetriever(JNIEnv *env)
{
- static const char* const kClassPathName = "android/media/MediaMetadataRetriever";
- jclass clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find class: %s", kClassPathName);
- return -1;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.context == NULL) {
- LOGE("Can't find MediaMetadataRetriever.mNativeContext");
- return -1;
- }
-
- fields.bitmapClazz = env->FindClass("android/graphics/Bitmap");
- if (fields.bitmapClazz == NULL) {
- LOGE("Bitmap class is not found");
- return -1;
- }
-
- fields.bitmapConstructor = env->GetMethodID(fields.bitmapClazz, "<init>", "(IZ[B)V");
- if (fields.bitmapConstructor == NULL) {
- LOGE("Bitmap constructor is not found");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods
- (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
+ (env, kClassPathName, nativeMethods, NELEM(nativeMethods));
}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 6317fe2..df98de5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -20,6 +20,7 @@
#include "utils/Log.h"
#include <media/mediaplayer.h>
+#include <media/MediaPlayerInterface.h>
#include <stdio.h>
#include <assert.h>
#include <limits.h>
@@ -30,6 +31,8 @@
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "utils/Errors.h" // for status_t
+#include "android_util_Binder.h"
+#include <binder/Parcel.h>
// ----------------------------------------------------------------------------
@@ -98,10 +101,9 @@ void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2)
// ----------------------------------------------------------------------------
-static sp<Surface> get_surface(JNIEnv* env, jobject clazz)
+static Surface* get_surface(JNIEnv* env, jobject clazz)
{
- Surface* const p = (Surface*)env->GetIntField(clazz, fields.surface_native);
- return sp<Surface>(p);
+ return (Surface*)env->GetIntField(clazz, fields.surface_native);
}
static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
@@ -202,7 +204,7 @@ static void setVideoSurface(const sp<MediaPlayer>& mp, JNIEnv *env, jobject thiz
{
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
- const sp<Surface>& native_surface = get_surface(env, surface);
+ const sp<Surface> native_surface = get_surface(env, surface);
LOGV("prepare: surface=%p (id=%d)",
native_surface.get(), native_surface->ID());
mp->setVideoSurface(native_surface);
@@ -242,7 +244,7 @@ android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
}
jobject surface = env->GetObjectField(thiz, fields.surface);
if (surface != NULL) {
- const sp<Surface>& native_surface = get_surface(env, surface);
+ const sp<Surface> native_surface = get_surface(env, surface);
LOGV("prepareAsync: surface=%p (id=%d)",
native_surface.get(), native_surface->ID());
mp->setVideoSurface(native_surface);
@@ -442,6 +444,119 @@ android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
return NULL;
}
+
+// Sends the request and reply parcels to the media player via the
+// binder interface.
+static jint
+android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
+ jobject java_request, jobject java_reply)
+{
+ sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
+ if (media_player == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return UNKNOWN_ERROR;
+ }
+
+
+ Parcel *request = parcelForJavaObject(env, java_request);
+ Parcel *reply = parcelForJavaObject(env, java_reply);
+
+ // Don't use process_media_player_call which use the async loop to
+ // report errors, instead returns the status.
+ return media_player->invoke(*request, reply);
+}
+
+// Sends the new filter to the client.
+static jint
+android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
+{
+ sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
+ if (media_player == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return UNKNOWN_ERROR;
+ }
+
+ Parcel *filter = parcelForJavaObject(env, request);
+
+ if (filter == NULL ) {
+ jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
+ return UNKNOWN_ERROR;
+ }
+
+ return media_player->setMetadataFilter(*filter);
+}
+
+static jboolean
+android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
+ jboolean apply_filter, jobject reply)
+{
+ sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
+ if (media_player == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return false;
+ }
+
+ Parcel *metadata = parcelForJavaObject(env, reply);
+
+ if (metadata == NULL ) {
+ jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
+ return false;
+ }
+
+ metadata->freeData();
+ // On return metadata is positioned at the beginning of the
+ // metadata. Note however that the parcel actually starts with the
+ // return code so you should not rewind the parcel using
+ // setDataPosition(0).
+ return media_player->getMetadata(update_only, apply_filter, metadata) == OK;
+}
+
+// This function gets some field IDs, which in turn causes class initialization.
+// It is called from a static block in MediaPlayer, which won't run until the
+// first time an instance of this class is used.
+static void
+android_media_MediaPlayer_native_init(JNIEnv *env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/media/MediaPlayer");
+ if (clazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaPlayer");
+ return;
+ }
+
+ fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (fields.context == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mNativeContext");
+ return;
+ }
+
+ fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.post_event == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.postEventFromNative");
+ return;
+ }
+
+ fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
+ if (fields.surface == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaPlayer.mSurface");
+ return;
+ }
+
+ jclass surface = env->FindClass("android/view/Surface");
+ if (surface == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
+ return;
+ }
+
+ fields.surface_native = env->GetFieldID(surface, "mSurface", "I");
+ if (fields.surface_native == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface");
+ return;
+ }
+}
+
static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
@@ -503,53 +618,19 @@ static JNINativeMethod gMethods[] = {
{"isLooping", "()Z", (void *)android_media_MediaPlayer_isLooping},
{"setVolume", "(FF)V", (void *)android_media_MediaPlayer_setVolume},
{"getFrameAt", "(I)Landroid/graphics/Bitmap;", (void *)android_media_MediaPlayer_getFrameAt},
+ {"native_invoke", "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
+ {"native_setMetadataFilter", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_setMetadataFilter},
+ {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_getMetadata},
+ {"native_init", "()V", (void *)android_media_MediaPlayer_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaPlayer_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaPlayer_native_finalize},
};
static const char* const kClassPathName = "android/media/MediaPlayer";
+// This function only registers the native methods
static int register_android_media_MediaPlayer(JNIEnv *env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/media/MediaPlayer");
- if (clazz == NULL) {
- LOGE("Can't find android/media/MediaPlayer");
- return -1;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.context == NULL) {
- LOGE("Can't find MediaPlayer.mNativeContext");
- return -1;
- }
-
- fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
- if (fields.post_event == NULL) {
- LOGE("Can't find MediaPlayer.postEventFromNative");
- return -1;
- }
-
- fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
- if (fields.surface == NULL) {
- LOGE("Can't find MediaPlayer.mSurface");
- return -1;
- }
-
- jclass surface = env->FindClass("android/view/Surface");
- if (surface == NULL) {
- LOGE("Can't find android/view/Surface");
- return -1;
- }
-
- fields.surface_native = env->GetFieldID(surface, "mSurface", "I");
- if (fields.surface_native == NULL) {
- LOGE("Can't find Surface fields");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 0273a5a..cad65b3 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -276,7 +276,7 @@ static void
android_media_MediaRecorder_setVideoFrameRate(JNIEnv *env, jobject thiz, jint rate)
{
LOGV("setVideoFrameRate(%d)", rate);
- if (rate <= 0 || rate > MEDIA_RECORDER_MAX_FRAME_RATE) {
+ if (rate <= 0) {
jniThrowException(env, "java/lang/IllegalArgumentException", "invalid frame rate");
return;
}
@@ -371,6 +371,53 @@ android_media_MediaRecorder_release(JNIEnv *env, jobject thiz)
}
}
+// This function gets some field IDs, which in turn causes class initialization.
+// It is called from a static block in MediaRecorder, which won't run until the
+// first time an instance of this class is used.
+static void
+android_media_MediaRecorder_native_init(JNIEnv *env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/media/MediaRecorder");
+ if (clazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaRecorder");
+ return;
+ }
+
+ fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (fields.context == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mNativeContext");
+ return;
+ }
+
+ fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
+ if (fields.surface == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaRecorder.mSurface");
+ return;
+ }
+
+ jclass surface = env->FindClass("android/view/Surface");
+ if (surface == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/view/Surface");
+ return;
+ }
+
+ fields.surface_native = env->GetFieldID(surface, "mSurface", "I");
+ if (fields.surface_native == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find Surface.mSurface");
+ return;
+ }
+
+ fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
+ "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+ if (fields.post_event == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "MediaRecorder.postEventFromNative");
+ return;
+ }
+}
+
+
static void
android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{
@@ -418,55 +465,19 @@ static JNINativeMethod gMethods[] = {
{"getMaxAmplitude", "()I", (void *)android_media_MediaRecorder_native_getMaxAmplitude},
{"start", "()V", (void *)android_media_MediaRecorder_start},
{"stop", "()V", (void *)android_media_MediaRecorder_stop},
- {"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
+ {"native_reset", "()V", (void *)android_media_MediaRecorder_native_reset},
{"release", "()V", (void *)android_media_MediaRecorder_release},
+ {"native_init", "()V", (void *)android_media_MediaRecorder_native_init},
{"native_setup", "(Ljava/lang/Object;)V", (void *)android_media_MediaRecorder_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
};
static const char* const kClassPathName = "android/media/MediaRecorder";
+// This function only registers the native methods, and is called from
+// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaRecorder(JNIEnv *env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/media/MediaRecorder");
- if (clazz == NULL) {
- LOGE("Can't find android/media/MediaRecorder");
- return -1;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.context == NULL) {
- LOGE("Can't find MediaRecorder.mNativeContext");
- return -1;
- }
-
- fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");
- if (fields.surface == NULL) {
- LOGE("Can't find MediaRecorder.mSurface");
- return -1;
- }
-
- jclass surface = env->FindClass("android/view/Surface");
- if (surface == NULL) {
- LOGE("Can't find android/view/Surface");
- return -1;
- }
-
- fields.surface_native = env->GetFieldID(surface, "mSurface", "I");
- if (fields.surface_native == NULL) {
- LOGE("Can't find Surface fields");
- return -1;
- }
-
- fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
- if (fields.post_event == NULL) {
- LOGE("Can't find MediaRecorder.postEventFromNative");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaRecorder", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index 8764a70..6a5404e 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -65,6 +65,8 @@ public:
"(Ljava/lang/String;Ljava/lang/String;)V");
mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
"(Ljava/lang/String;)V");
+ mAddNoMediaFolderMethodID = env->GetMethodID(mediaScannerClientInterface, "addNoMediaFolder",
+ "(Ljava/lang/String;)V");
}
}
@@ -111,12 +113,26 @@ public:
return (!mEnv->ExceptionCheck());
}
+ // returns true if it succeeded, false if an exception occured in the Java code
+ virtual bool addNoMediaFolder(const char* path)
+ {
+ jstring pathStr;
+ if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
+
+ mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr);
+
+ mEnv->DeleteLocalRef(pathStr);
+ return (!mEnv->ExceptionCheck());
+ }
+
+
private:
JNIEnv *mEnv;
jobject mClient;
jmethodID mScanFileMethodID;
jmethodID mHandleStringTagMethodID;
jmethodID mSetMimeTypeMethodID;
+ jmethodID mAddNoMediaFolderMethodID;
};
@@ -241,6 +257,27 @@ done:
return array;
}
+// This function gets a field ID, which in turn causes class initialization.
+// It is called from a static block in MediaScanner, which won't run until the
+// first time an instance of this class is used.
+static void
+android_media_MediaScanner_native_init(JNIEnv *env)
+{
+ jclass clazz;
+
+ clazz = env->FindClass("android/media/MediaScanner");
+ if (clazz == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaScanner");
+ return;
+ }
+
+ fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (fields.context == NULL) {
+ jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaScanner.mNativeContext");
+ return;
+ }
+}
+
static void
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
{
@@ -275,28 +312,17 @@ static JNINativeMethod gMethods[] = {
(void *)android_media_MediaScanner_processFile},
{"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale},
{"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt},
+ {"native_init", "()V", (void *)android_media_MediaScanner_native_init},
{"native_setup", "()V", (void *)android_media_MediaScanner_native_setup},
{"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize},
};
static const char* const kClassPathName = "android/media/MediaScanner";
+// This function only registers the native methods, and is called from
+// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaScanner(JNIEnv *env)
{
- jclass clazz;
-
- clazz = env->FindClass("android/media/MediaScanner");
- if (clazz == NULL) {
- LOGE("Can't find android/media/MediaScanner");
- return -1;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
- if (fields.context == NULL) {
- LOGE("Can't find MediaScanner.mNativeContext");
- return -1;
- }
-
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaScanner", gMethods, NELEM(gMethods));
}
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
index 0247cdb..f248557 100644
--- a/media/jni/android_media_ResampleInputStream.cpp
+++ b/media/jni/android_media_ResampleInputStream.cpp
@@ -128,13 +128,6 @@ static JNINativeMethod gMethods[] = {
int register_android_media_ResampleInputStream(JNIEnv *env)
{
const char* const kClassPathName = "android/media/ResampleInputStream";
- jclass clazz;
-
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find %s", kClassPathName);
- return -1;
- }
return AndroidRuntime::registerNativeMethods(env,
kClassPathName, gMethods, NELEM(gMethods));
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 374ddeb..9ff2e24 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -9,6 +9,7 @@ LOCAL_SRC_FILES:= \
LOCAL_SHARED_LIBRARIES := \
libcutils \
libutils \
+ libbinder \
libandroid_runtime \
libnativehelper \
libmedia
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 00a121b..b17e31b 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -524,13 +524,14 @@ void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftV
// wrong audio audio buffer size (mAudioBufferSize)
unsigned long toggle = mToggle ^ 1;
void *userData = (void *)((unsigned long)this | toggle);
+ uint32_t channels = (numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO;
#ifdef USE_SHARED_MEM_BUFFER
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- numChannels, sample->getIMemory(), 0, callback, userData);
+ channels, sample->getIMemory(), 0, callback, userData);
#else
newTrack = new AudioTrack(streamType, sampleRate, sample->format(),
- numChannels, frameCount, 0, callback, userData, bufferFrames);
+ channels, frameCount, 0, callback, userData, bufferFrames);
#endif
if (newTrack->initCheck() != NO_ERROR) {
LOGE("Error creating AudioTrack");
diff --git a/media/libdrm/mobile2/include/rights/RoManager.h b/media/libdrm/mobile2/include/rights/RoManager.h
index cf398b3..71e9eef 100644
--- a/media/libdrm/mobile2/include/rights/RoManager.h
+++ b/media/libdrm/mobile2/include/rights/RoManager.h
@@ -64,12 +64,6 @@ public:
vector<Ro*> getAllRo();
/**
- * Get the private key of the device.
- * @return the private key.
- */
- const string& getDevicePrivateKey() const;
-
- /**
* Get ro which contained rights of specific content.
* @param contentID the specific id of content.
* @return NULL if not fount otherwise the related ro.
diff --git a/media/libdrm/mobile2/src/rights/RoManager.cpp b/media/libdrm/mobile2/src/rights/RoManager.cpp
index 848c2ba..a115d21 100644
--- a/media/libdrm/mobile2/src/rights/RoManager.cpp
+++ b/media/libdrm/mobile2/src/rights/RoManager.cpp
@@ -121,9 +121,3 @@ bool RoManager::checkRoInCache(const string& roID)
return true;
}
-/** see RoManager.h */
-const string& RoManager::getDevicePrivateKey() const
-{
- string pk;
- return pk;
-}
diff --git a/media/libdrm/mobile2/src/util/domcore/NodeIterator.cpp b/media/libdrm/mobile2/src/util/domcore/NodeIterator.cpp
index f076cda..fe13669 100644
--- a/media/libdrm/mobile2/src/util/domcore/NodeIterator.cpp
+++ b/media/libdrm/mobile2/src/util/domcore/NodeIterator.cpp
@@ -88,7 +88,7 @@ NodeImpl* NodeIterator::findPreviousOrderNode(NodeImpl* node)
node = node->getLastChild();
} else {
if (node == scopeNode)
- node == NULL;
+ node = NULL;
else
node = node->getParentNode();
}
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 8020da2..9d442c3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -2,31 +2,34 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- AudioTrack.cpp \
- IAudioFlinger.cpp \
- IAudioFlingerClient.cpp \
- IAudioTrack.cpp \
- IAudioRecord.cpp \
- AudioRecord.cpp \
- AudioSystem.cpp \
- mediaplayer.cpp \
- IMediaPlayerService.cpp \
- IMediaPlayerClient.cpp \
- IMediaPlayer.cpp \
- IMediaRecorder.cpp \
- mediarecorder.cpp \
- IMediaMetadataRetriever.cpp \
- mediametadataretriever.cpp \
- ToneGenerator.cpp \
- JetPlayer.cpp
+ AudioTrack.cpp \
+ IAudioFlinger.cpp \
+ IAudioFlingerClient.cpp \
+ IAudioTrack.cpp \
+ IAudioRecord.cpp \
+ AudioRecord.cpp \
+ AudioSystem.cpp \
+ mediaplayer.cpp \
+ IMediaPlayerService.cpp \
+ IMediaPlayerClient.cpp \
+ IMediaPlayer.cpp \
+ IMediaRecorder.cpp \
+ Metadata.cpp \
+ mediarecorder.cpp \
+ IMediaMetadataRetriever.cpp \
+ mediametadataretriever.cpp \
+ ToneGenerator.cpp \
+ JetPlayer.cpp \
+ IOMX.cpp \
+ IAudioPolicyService.cpp
LOCAL_SHARED_LIBRARIES := \
- libui libcutils libutils libsonivox
+ libui libcutils libutils libbinder libsonivox
LOCAL_MODULE:= libmedia
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
-LOCAL_LDLIBS += -ldl
+LOCAL_LDLIBS += -ldl -lpthread
endif
ifneq ($(TARGET_SIMULATOR),true)
@@ -34,6 +37,7 @@ LOCAL_SHARED_LIBRARIES += libdl
endif
LOCAL_C_INCLUDES := \
- $(call include-path-for, graphics corecg)
+ $(call include-path-for, graphics corecg) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index e56efbb..5e35564 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -28,12 +28,13 @@
#include <media/AudioSystem.h>
#include <media/AudioRecord.h>
+#include <media/mediarecorder.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/MemoryDealer.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
#include <cutils/atomic.h>
@@ -45,7 +46,7 @@ namespace android {
// ---------------------------------------------------------------------------
AudioRecord::AudioRecord()
- : mStatus(NO_INIT)
+ : mStatus(NO_INIT), mInput(0)
{
}
@@ -53,15 +54,15 @@ AudioRecord::AudioRecord(
int inputSource,
uint32_t sampleRate,
int format,
- int channelCount,
+ uint32_t channels,
int frameCount,
uint32_t flags,
callback_t cbf,
void* user,
int notificationFrames)
- : mStatus(NO_INIT)
+ : mStatus(NO_INIT), mInput(0)
{
- mStatus = set(inputSource, sampleRate, format, channelCount,
+ mStatus = set(inputSource, sampleRate, format, channels,
frameCount, flags, cbf, user, notificationFrames);
}
@@ -78,6 +79,7 @@ AudioRecord::~AudioRecord()
}
mAudioRecord.clear();
IPCThreadState::self()->flushCommands();
+ AudioSystem::releaseInput(mInput);
}
}
@@ -85,7 +87,7 @@ status_t AudioRecord::set(
int inputSource,
uint32_t sampleRate,
int format,
- int channelCount,
+ uint32_t channels,
int frameCount,
uint32_t flags,
callback_t cbf,
@@ -94,7 +96,7 @@ status_t AudioRecord::set(
bool threadCanCallJava)
{
- LOGV("set(): sampleRate %d, channelCount %d, frameCount %d",sampleRate, channelCount, frameCount);
+ LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
if (mAudioRecord != 0) {
return INVALID_OPERATION;
}
@@ -104,8 +106,8 @@ status_t AudioRecord::set(
return NO_INIT;
}
- if (inputSource == DEFAULT_INPUT) {
- inputSource = MIC_INPUT;
+ if (inputSource == AUDIO_SOURCE_DEFAULT) {
+ inputSource = AUDIO_SOURCE_MIC;
}
if (sampleRate == 0) {
@@ -115,15 +117,21 @@ status_t AudioRecord::set(
if (format == 0) {
format = AudioSystem::PCM_16_BIT;
}
- if (channelCount == 0) {
- channelCount = 1;
+ // validate parameters
+ if (!AudioSystem::isValidFormat(format)) {
+ LOGE("Invalid format");
+ return BAD_VALUE;
}
- // validate parameters
- if (format != AudioSystem::PCM_16_BIT) {
+ if (!AudioSystem::isInputChannel(channels)) {
return BAD_VALUE;
}
- if (channelCount != 1 && channelCount != 2) {
+ int channelCount = AudioSystem::popCount(channels);
+
+ mInput = AudioSystem::getInput(inputSource,
+ sampleRate, format, channels, (AudioSystem::audio_in_acoustics)flags);
+ if (mInput == 0) {
+ LOGE("Could not get audio output for stream type %d", inputSource);
return BAD_VALUE;
}
@@ -132,14 +140,22 @@ status_t AudioRecord::set(
if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &inputBuffSizeInBytes)
!= NO_ERROR) {
LOGE("AudioSystem could not query the input buffer size.");
- return NO_INIT;
+ return NO_INIT;
}
+
if (inputBuffSizeInBytes == 0) {
LOGE("Recording parameters are not supported: sampleRate %d, channelCount %d, format %d",
sampleRate, channelCount, format);
return BAD_VALUE;
}
+
int frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
+ if (AudioSystem::isLinearPCM(format)) {
+ frameSizeInBytes = channelCount * (format == AudioSystem::PCM_16_BIT ? sizeof(int16_t) : sizeof(int8_t));
+ } else {
+ frameSizeInBytes = sizeof(int8_t);
+ }
+
// We use 2* size of input buffer for ping pong use of record buffer.
int minFrameCount = 2 * inputBuffSizeInBytes / frameSizeInBytes;
@@ -157,11 +173,11 @@ status_t AudioRecord::set(
// open record channel
status_t status;
- sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), inputSource,
+ sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), mInput,
sampleRate, format,
channelCount,
frameCount,
- ((uint16_t)flags) << 16,
+ ((uint16_t)flags) << 16,
&status);
if (record == 0) {
LOGE("AudioFlinger could not create record track, status: %d", status);
@@ -188,7 +204,7 @@ status_t AudioRecord::set(
mFormat = format;
// Update buffer size in case it has been limited by AudioFlinger during track creation
mFrameCount = mCblk->frameCount;
- mChannelCount = channelCount;
+ mChannelCount = (uint8_t)channelCount;
mActive = 0;
mCbf = cbf;
mNotificationFrames = notificationFrames;
@@ -234,7 +250,11 @@ uint32_t AudioRecord::frameCount() const
int AudioRecord::frameSize() const
{
- return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ } else {
+ return sizeof(uint8_t);
+ }
}
int AudioRecord::inputSource() const
@@ -262,15 +282,18 @@ status_t AudioRecord::start()
}
if (android_atomic_or(1, &mActive) == 0) {
- mNewPosition = mCblk->user + mUpdatePeriod;
- mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
- mCblk->waitTimeMs = 0;
- if (t != 0) {
- t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
- } else {
- setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
+ ret = AudioSystem::startInput(mInput);
+ if (ret == NO_ERROR) {
+ mNewPosition = mCblk->user + mUpdatePeriod;
+ mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+ mCblk->waitTimeMs = 0;
+ if (t != 0) {
+ t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
+ } else {
+ setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
+ }
+ ret = mAudioRecord->start();
}
- ret = mAudioRecord->start();
}
if (t != 0) {
@@ -301,6 +324,7 @@ status_t AudioRecord::stop()
} else {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
}
+ AudioSystem::stopInput(mInput);
}
if (t != 0) {
@@ -421,7 +445,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
"this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
cblk->waitTimeMs = 0;
-
+
if (framesReq > framesReady) {
framesReq = framesReady;
}
@@ -437,7 +461,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
audioBuffer->channelCount= mChannelCount;
audioBuffer->format = mFormat;
audioBuffer->frameCount = framesReq;
- audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t);
+ audioBuffer->size = framesReq*cblk->frameSize;
audioBuffer->raw = (int8_t*)cblk->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
@@ -468,7 +492,7 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize)
do {
- audioBuffer.frameCount = userSize/mChannelCount/sizeof(int16_t);
+ audioBuffer.frameCount = userSize/frameSize();
// Calling obtainBuffer() with a negative wait count causes
// an (almost) infinite wait time.
@@ -519,8 +543,8 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
do {
audioBuffer.frameCount = frames;
- // Calling obtainBuffer() with a wait count of 1
- // limits wait time to WAIT_PERIOD_MS. This prevents from being
+ // Calling obtainBuffer() with a wait count of 1
+ // limits wait time to WAIT_PERIOD_MS. This prevents from being
// stuck here not being able to handle timed events (position, markers).
status_t err = obtainBuffer(&audioBuffer, 1);
if (err < NO_ERROR) {
@@ -548,14 +572,14 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
if (readSize > reqSize) readSize = reqSize;
audioBuffer.size = readSize;
- audioBuffer.frameCount = readSize/mChannelCount/sizeof(int16_t);
+ audioBuffer.frameCount = readSize/frameSize();
frames -= audioBuffer.frameCount;
releaseBuffer(&audioBuffer);
} while (frames);
-
+
// Manage overrun callback
if (mActive && (mCblk->framesAvailable_l() == 0)) {
LOGV("Overrun user: %x, server: %x, flowControlFlag %d", mCblk->user, mCblk->server, mCblk->flowControlFlag);
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index a21a7a4..bd1b2d7 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -18,10 +18,20 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <media/AudioSystem.h>
+#include <media/IAudioPolicyService.h>
#include <math.h>
+// ----------------------------------------------------------------------------
+// the sim build doesn't have gettid
+
+#ifndef HAVE_GETTID
+# define gettid getpid
+#endif
+
+// ----------------------------------------------------------------------------
+
namespace android {
// client singleton for AudioFlinger binder interface
@@ -30,10 +40,9 @@ sp<IAudioFlinger> AudioSystem::gAudioFlinger;
sp<AudioSystem::AudioFlingerClient> AudioSystem::gAudioFlingerClient;
audio_error_callback AudioSystem::gAudioErrorCallback = NULL;
// Cached values
-int AudioSystem::gOutSamplingRate[NUM_AUDIO_OUTPUT_TYPES];
-int AudioSystem::gOutFrameCount[NUM_AUDIO_OUTPUT_TYPES];
-uint32_t AudioSystem::gOutLatency[NUM_AUDIO_OUTPUT_TYPES];
-bool AudioSystem::gA2dpEnabled;
+DefaultKeyedVector<int, audio_io_handle_t> AudioSystem::gStreamOutputMap(0);
+DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSystem::gOutputs(0);
+
// Cached values for recording queries
uint32_t AudioSystem::gPrevInSamplingRate = 16000;
int AudioSystem::gPrevInFormat = AudioSystem::PCM_16_BIT;
@@ -65,42 +74,10 @@ const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
binder->linkToDeath(gAudioFlingerClient);
gAudioFlinger = interface_cast<IAudioFlinger>(binder);
gAudioFlinger->registerClient(gAudioFlingerClient);
- // Cache frequently accessed parameters
- for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) {
- gOutFrameCount[output] = (int)gAudioFlinger->frameCount(output);
- gOutSamplingRate[output] = (int)gAudioFlinger->sampleRate(output);
- gOutLatency[output] = gAudioFlinger->latency(output);
- }
- gA2dpEnabled = gAudioFlinger->isA2dpEnabled();
}
LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?");
- return gAudioFlinger;
-}
-// routing helper functions
-status_t AudioSystem::speakerphone(bool state) {
- uint32_t routes = state ? ROUTE_SPEAKER : ROUTE_EARPIECE;
- return setRouting(MODE_IN_CALL, routes, ROUTE_ALL);
-}
-
-status_t AudioSystem::isSpeakerphoneOn(bool* state) {
- uint32_t routes = 0;
- status_t s = getRouting(MODE_IN_CALL, &routes);
- *state = !!(routes & ROUTE_SPEAKER);
- return s;
-}
-
-status_t AudioSystem::bluetoothSco(bool state) {
- uint32_t mask = ROUTE_BLUETOOTH_SCO;
- uint32_t routes = state ? mask : ROUTE_EARPIECE;
- return setRouting(MODE_IN_CALL, routes, ROUTE_ALL);
-}
-
-status_t AudioSystem::isBluetoothScoOn(bool* state) {
- uint32_t routes = 0;
- status_t s = getRouting(MODE_IN_CALL, &routes);
- *state = !!(routes & ROUTE_BLUETOOTH_SCO);
- return s;
+ return gAudioFlinger;
}
status_t AudioSystem::muteMicrophone(bool state) {
@@ -148,12 +125,12 @@ status_t AudioSystem::getMasterMute(bool* mute)
return NO_ERROR;
}
-status_t AudioSystem::setStreamVolume(int stream, float value)
+status_t AudioSystem::setStreamVolume(int stream, float value, int output)
{
if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- af->setStreamVolume(stream, value);
+ af->setStreamVolume(stream, value, output);
return NO_ERROR;
}
@@ -166,12 +143,12 @@ status_t AudioSystem::setStreamMute(int stream, bool mute)
return NO_ERROR;
}
-status_t AudioSystem::getStreamVolume(int stream, float* volume)
+status_t AudioSystem::getStreamVolume(int stream, float* volume, int output)
{
if (uint32_t(stream) >= NUM_STREAM_TYPES) return BAD_VALUE;
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- *volume = af->streamVolume(stream);
+ *volume = af->streamVolume(stream, output);
return NO_ERROR;
}
@@ -192,43 +169,28 @@ status_t AudioSystem::setMode(int mode)
return af->setMode(mode);
}
-status_t AudioSystem::getMode(int* mode)
-{
+
+status_t AudioSystem::isMusicActive(bool* state) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- *mode = af->getMode();
+ *state = af->isMusicActive();
return NO_ERROR;
}
-status_t AudioSystem::setRouting(int mode, uint32_t routes, uint32_t mask)
-{
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- return af->setRouting(mode, routes, mask);
-}
-status_t AudioSystem::getRouting(int mode, uint32_t* routes)
-{
+status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
- uint32_t r = af->getRouting(mode);
- *routes = r;
- return NO_ERROR;
+ return af->setParameters(ioHandle, keyValuePairs);
}
-status_t AudioSystem::isMusicActive(bool* state) {
+String8 AudioSystem::getParameters(audio_io_handle_t ioHandle, const String8& keys) {
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- *state = af->isMusicActive();
- return NO_ERROR;
-}
+ String8 result = String8("");
+ if (af == 0) return result;
-// Temporary interface, do not use
-// TODO: Replace with a more generic key:value get/set mechanism
-status_t AudioSystem::setParameter(const char* key, const char* value) {
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- return af->setParameter(key, value);
+ result = af->getParameters(ioHandle, keys);
+ return result;
}
// convert volume steps to natural log scale
@@ -257,55 +219,108 @@ int AudioSystem::logToLinear(float volume)
status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType)
{
- int output = getOutput(streamType);
-
- if (output == NUM_AUDIO_OUTPUT_TYPES) return PERMISSION_DENIED;
+ OutputDescriptor *outputDesc;
+ audio_io_handle_t output;
+
+ if (streamType == DEFAULT) {
+ streamType = MUSIC;
+ }
+
+ output = getOutput((stream_type)streamType);
+ if (output == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ gLock.lock();
+ outputDesc = AudioSystem::gOutputs.valueFor(output);
+ if (outputDesc == 0) {
+ LOGV("getOutputSamplingRate() no output descriptor for output %d in gOutputs", output);
+ gLock.unlock();
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ *samplingRate = af->sampleRate(output);
+ } else {
+ LOGV("getOutputSamplingRate() reading from output desc");
+ *samplingRate = outputDesc->samplingRate;
+ gLock.unlock();
+ }
+
+ LOGV("getOutputSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, *samplingRate);
- // gOutSamplingRate[] is updated by getOutput() which calls get_audio_flinger()
- LOGV("getOutputSamplingRate() streamType %d, output %d, sampling rate %d", streamType, output, gOutSamplingRate[output]);
-
- *samplingRate = gOutSamplingRate[output];
-
return NO_ERROR;
}
status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType)
{
- int output = getOutput(streamType);
+ OutputDescriptor *outputDesc;
+ audio_io_handle_t output;
- if (output == NUM_AUDIO_OUTPUT_TYPES) return PERMISSION_DENIED;
+ if (streamType == DEFAULT) {
+ streamType = MUSIC;
+ }
- // gOutFrameCount[] is updated by getOutput() which calls get_audio_flinger()
- LOGV("getOutputFrameCount() streamType %d, output %d, frame count %d", streamType, output, gOutFrameCount[output]);
+ output = getOutput((stream_type)streamType);
+ if (output == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ gLock.lock();
+ outputDesc = AudioSystem::gOutputs.valueFor(output);
+ if (outputDesc == 0) {
+ gLock.unlock();
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ *frameCount = af->frameCount(output);
+ } else {
+ *frameCount = outputDesc->frameCount;
+ gLock.unlock();
+ }
+
+ LOGV("getOutputFrameCount() streamType %d, output %d, frameCount %d", streamType, output, *frameCount);
- *frameCount = gOutFrameCount[output];
-
return NO_ERROR;
}
status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
{
- int output = getOutput(streamType);
+ OutputDescriptor *outputDesc;
+ audio_io_handle_t output;
- if (output == NUM_AUDIO_OUTPUT_TYPES) return PERMISSION_DENIED;
+ if (streamType == DEFAULT) {
+ streamType = MUSIC;
+ }
- // gOutLatency[] is updated by getOutput() which calls get_audio_flinger()
- LOGV("getOutputLatency() streamType %d, output %d, latency %d", streamType, output, gOutLatency[output]);
+ output = getOutput((stream_type)streamType);
+ if (output == 0) {
+ return PERMISSION_DENIED;
+ }
+
+ gLock.lock();
+ outputDesc = AudioSystem::gOutputs.valueFor(output);
+ if (outputDesc == 0) {
+ gLock.unlock();
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ *latency = af->latency(output);
+ } else {
+ *latency = outputDesc->latency;
+ gLock.unlock();
+ }
+
+ LOGV("getOutputLatency() streamType %d, output %d, latency %d", streamType, output, *latency);
- *latency = gOutLatency[output];
-
return NO_ERROR;
}
-status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
+status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int channelCount,
size_t* buffSize)
{
// Do we have a stale gInBufferSize or are we requesting the input buffer size for new values
- if ((gInBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
+ if ((gInBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)
|| (channelCount != gPrevInChannelCount)) {
// save the request params
gPrevInSamplingRate = sampleRate;
- gPrevInFormat = format;
+ gPrevInFormat = format;
gPrevInChannelCount = channelCount;
gInBuffSize = 0;
@@ -314,24 +329,18 @@ status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, int format, int ch
return PERMISSION_DENIED;
}
gInBuffSize = af->getInputBufferSize(sampleRate, format, channelCount);
- }
+ }
*buffSize = gInBuffSize;
-
+
return NO_ERROR;
}
// ---------------------------------------------------------------------------
-void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
+void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
Mutex::Autolock _l(AudioSystem::gLock);
- AudioSystem::gAudioFlinger.clear();
- for (int output = 0; output < NUM_AUDIO_OUTPUT_TYPES; output++) {
- gOutFrameCount[output] = 0;
- gOutSamplingRate[output] = 0;
- gOutLatency[output] = 0;
- }
- AudioSystem::gInBuffSize = 0;
+ AudioSystem::gAudioFlinger.clear();
if (gAudioErrorCallback) {
gAudioErrorCallback(DEAD_OBJECT);
@@ -339,33 +348,82 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
LOGW("AudioFlinger server died!");
}
-void AudioSystem::AudioFlingerClient::a2dpEnabledChanged(bool enabled) {
- gA2dpEnabled = enabled;
- LOGV("AudioFlinger A2DP enabled status changed! %d", enabled);
-}
+void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, int ioHandle, void *param2) {
+ LOGV("ioConfigChanged() event %d", event);
+ OutputDescriptor *desc;
+ uint32_t stream;
+
+ if (ioHandle == 0) return;
-void AudioSystem::setErrorCallback(audio_error_callback cb) {
Mutex::Autolock _l(AudioSystem::gLock);
- gAudioErrorCallback = cb;
-}
-int AudioSystem::getOutput(int streamType)
-{
- // make sure that gA2dpEnabled is valid by calling get_audio_flinger() which in turn
- // will call gAudioFlinger->isA2dpEnabled()
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return NUM_AUDIO_OUTPUT_TYPES;
+ switch (event) {
+ case STREAM_CONFIG_CHANGED:
+ if (param2 == 0) break;
+ stream = *(uint32_t *)param2;
+ LOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
+ if (gStreamOutputMap.indexOfKey(stream) >= 0) {
+ gStreamOutputMap.replaceValueFor(stream, ioHandle);
+ }
+ break;
+ case OUTPUT_OPENED: {
+ if (gOutputs.indexOfKey(ioHandle) >= 0) {
+ LOGV("ioConfigChanged() opening already existing output! %d", ioHandle);
+ break;
+ }
+ if (param2 == 0) break;
+ desc = (OutputDescriptor *)param2;
+
+ OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
+ gOutputs.add(ioHandle, outputDesc);
+ LOGV("ioConfigChanged() new output samplingRate %d, format %d channels %d frameCount %d latency %d",
+ outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency);
+ } break;
+ case OUTPUT_CLOSED: {
+ if (gOutputs.indexOfKey(ioHandle) < 0) {
+ LOGW("ioConfigChanged() closing unknow output! %d", ioHandle);
+ break;
+ }
+ LOGV("ioConfigChanged() output %d closed", ioHandle);
+
+ gOutputs.removeItem(ioHandle);
+ for (int i = gStreamOutputMap.size() - 1; i >= 0 ; i--) {
+ if (gStreamOutputMap.valueAt(i) == ioHandle) {
+ gStreamOutputMap.removeItemsAt(i);
+ }
+ }
+ } break;
+
+ case OUTPUT_CONFIG_CHANGED: {
+ int index = gOutputs.indexOfKey(ioHandle);
+ if (index < 0) {
+ LOGW("ioConfigChanged() modifying unknow output! %d", ioHandle);
+ break;
+ }
+ if (param2 == 0) break;
+ desc = (OutputDescriptor *)param2;
+
+ LOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
+ ioHandle, desc->samplingRate, desc->format,
+ desc->channels, desc->frameCount, desc->latency);
+ OutputDescriptor *outputDesc = gOutputs.valueAt(index);
+ delete outputDesc;
+ outputDesc = new OutputDescriptor(*desc);
+ gOutputs.replaceValueFor(ioHandle, outputDesc);
+ } break;
+ case INPUT_OPENED:
+ case INPUT_CLOSED:
+ case INPUT_CONFIG_CHANGED:
+ break;
- if (streamType == DEFAULT) {
- streamType = MUSIC;
- }
- if (gA2dpEnabled && routedToA2dpOutput(streamType)) {
- return AUDIO_OUTPUT_A2DP;
- } else {
- return AUDIO_OUTPUT_HARDWARE;
}
}
+void AudioSystem::setErrorCallback(audio_error_callback cb) {
+ Mutex::Autolock _l(gLock);
+ gAudioErrorCallback = cb;
+}
+
bool AudioSystem::routedToA2dpOutput(int streamType) {
switch(streamType) {
case MUSIC:
@@ -379,6 +437,461 @@ bool AudioSystem::routedToA2dpOutput(int streamType) {
}
+// client singleton for AudioPolicyService binder interface
+sp<IAudioPolicyService> AudioSystem::gAudioPolicyService;
+sp<AudioSystem::AudioPolicyServiceClient> AudioSystem::gAudioPolicyServiceClient;
+
+
+// establish binder interface to AudioFlinger service
+const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service()
+{
+ gLock.lock();
+ if (gAudioPolicyService.get() == 0) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ do {
+ binder = sm->getService(String16("media.audio_policy"));
+ if (binder != 0)
+ break;
+ LOGW("AudioPolicyService not published, waiting...");
+ usleep(500000); // 0.5 s
+ } while(true);
+ if (gAudioPolicyServiceClient == NULL) {
+ gAudioPolicyServiceClient = new AudioPolicyServiceClient();
+ }
+ binder->linkToDeath(gAudioPolicyServiceClient);
+ gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
+ gLock.unlock();
+ } else {
+ gLock.unlock();
+ }
+ return gAudioPolicyService;
+}
+
+status_t AudioSystem::setDeviceConnectionState(audio_devices device,
+ device_connection_state state,
+ const char *device_address)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+
+ return aps->setDeviceConnectionState(device, state, device_address);
+}
+
+AudioSystem::device_connection_state AudioSystem::getDeviceConnectionState(audio_devices device,
+ const char *device_address)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return DEVICE_STATE_UNAVAILABLE;
+
+ return aps->getDeviceConnectionState(device, device_address);
+}
+
+status_t AudioSystem::setPhoneState(int state)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+
+ return aps->setPhoneState(state);
+}
+
+status_t AudioSystem::setRingerMode(uint32_t mode, uint32_t mask)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->setRingerMode(mode, mask);
+}
+
+status_t AudioSystem::setForceUse(force_use usage, forced_config config)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->setForceUse(usage, config);
+}
+
+AudioSystem::forced_config AudioSystem::getForceUse(force_use usage)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return FORCE_NONE;
+ return aps->getForceUse(usage);
+}
+
+
+audio_io_handle_t AudioSystem::getOutput(stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ output_flags flags)
+{
+ audio_io_handle_t output = 0;
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) {
+ Mutex::Autolock _l(gLock);
+ output = AudioSystem::gStreamOutputMap.valueFor(stream);
+ LOGV_IF((output != 0), "getOutput() read %d from cache for stream %d", output, stream);
+ }
+ if (output == 0) {
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return 0;
+ output = aps->getOutput(stream, samplingRate, format, channels, flags);
+ if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) == 0) {
+ Mutex::Autolock _l(gLock);
+ AudioSystem::gStreamOutputMap.add(stream, output);
+ }
+ }
+ return output;
+}
+
+status_t AudioSystem::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->startOutput(output, stream);
+}
+
+status_t AudioSystem::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->stopOutput(output, stream);
+}
+
+void AudioSystem::releaseOutput(audio_io_handle_t output)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return;
+ aps->releaseOutput(output);
+}
+
+audio_io_handle_t AudioSystem::getInput(int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ audio_in_acoustics acoustics)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return 0;
+ return aps->getInput(inputSource, samplingRate, format, channels, acoustics);
+}
+status_t AudioSystem::startInput(audio_io_handle_t input)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->startInput(input);
+}
+
+status_t AudioSystem::stopInput(audio_io_handle_t input)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->stopInput(input);
+}
+
+void AudioSystem::releaseInput(audio_io_handle_t input)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return;
+ aps->releaseInput(input);
+}
+
+status_t AudioSystem::initStreamVolume(stream_type stream,
+ int indexMin,
+ int indexMax)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->initStreamVolume(stream, indexMin, indexMax);
+}
+
+status_t AudioSystem::setStreamVolumeIndex(stream_type stream, int index)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->setStreamVolumeIndex(stream, index);
+}
+
+status_t AudioSystem::getStreamVolumeIndex(stream_type stream, int *index)
+{
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+ if (aps == 0) return PERMISSION_DENIED;
+ return aps->getStreamVolumeIndex(stream, index);
+}
+
+// ---------------------------------------------------------------------------
+
+void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) {
+ Mutex::Autolock _l(AudioSystem::gLock);
+ AudioSystem::gAudioPolicyService.clear();
+
+ LOGW("AudioPolicyService server died!");
+}
+
+// ---------------------------------------------------------------------------
+
+
+// use emulated popcount optimization
+// http://www.df.lth.se/~john_e/gems/gem002d.html
+uint32_t AudioSystem::popCount(uint32_t u)
+{
+ u = ((u&0x55555555) + ((u>>1)&0x55555555));
+ u = ((u&0x33333333) + ((u>>2)&0x33333333));
+ u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+ u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+ u = ( u&0x0000ffff) + (u>>16);
+ return u;
+}
+
+bool AudioSystem::isOutputDevice(audio_devices device)
+{
+ if ((popCount(device) == 1 ) &&
+ ((device & ~AudioSystem::DEVICE_OUT_ALL) == 0)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isInputDevice(audio_devices device)
+{
+ if ((popCount(device) == 1 ) &&
+ ((device & ~AudioSystem::DEVICE_IN_ALL) == 0)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isA2dpDevice(audio_devices device)
+{
+ if ((popCount(device) == 1 ) &&
+ (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isBluetoothScoDevice(audio_devices device)
+{
+ if ((popCount(device) == 1 ) &&
+ (device & (AudioSystem::DEVICE_OUT_BLUETOOTH_SCO |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_HEADSET |
+ AudioSystem::DEVICE_OUT_BLUETOOTH_SCO_CARKIT))) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isLowVisibility(stream_type stream)
+{
+ if (stream == AudioSystem::SYSTEM || stream == AudioSystem::NOTIFICATION) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isInputChannel(uint32_t channel)
+{
+ if ((channel & ~AudioSystem::CHANNEL_IN_ALL) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isOutputChannel(uint32_t channel)
+{
+ if ((channel & ~AudioSystem::CHANNEL_OUT_ALL) == 0) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool AudioSystem::isValidFormat(uint32_t format)
+{
+ switch (format & MAIN_FORMAT_MASK) {
+ case PCM:
+ case MP3:
+ case AMR_NB:
+ case AMR_WB:
+ case AAC:
+ case HE_AAC_V1:
+ case HE_AAC_V2:
+ case VORBIS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool AudioSystem::isLinearPCM(uint32_t format)
+{
+ switch (format) {
+ case PCM_16_BIT:
+ case PCM_8_BIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//------------------------- AudioParameter class implementation ---------------
+
+const char *AudioParameter::keyRouting = "routing";
+const char *AudioParameter::keySamplingRate = "sampling_rate";
+const char *AudioParameter::keyFormat = "format";
+const char *AudioParameter::keyChannels = "channels";
+const char *AudioParameter::keyFrameCount = "frame_count";
+
+AudioParameter::AudioParameter(const String8& keyValuePairs)
+{
+ char *str = new char[keyValuePairs.length()+1];
+ mKeyValuePairs = keyValuePairs;
+
+ strcpy(str, keyValuePairs.string());
+ char *pair = strtok(str, ";");
+ while (pair != NULL) {
+ if (strlen(pair) != 0) {
+ size_t eqIdx = strcspn(pair, "=");
+ String8 key = String8(pair, eqIdx);
+ String8 value;
+ if (eqIdx == strlen(pair)) {
+ value = String8("");
+ } else {
+ value = String8(pair + eqIdx + 1);
+ }
+ if (mParameters.indexOfKey(key) < 0) {
+ mParameters.add(key, value);
+ } else {
+ mParameters.replaceValueFor(key, value);
+ }
+ } else {
+ LOGV("AudioParameter() cstor empty key value pair");
+ }
+ pair = strtok(NULL, ";");
+ }
+
+ delete[] str;
+}
+
+AudioParameter::~AudioParameter()
+{
+ mParameters.clear();
+}
+
+String8 AudioParameter::toString()
+{
+ String8 str = String8("");
+
+ size_t size = mParameters.size();
+ for (size_t i = 0; i < size; i++) {
+ str += mParameters.keyAt(i);
+ str += "=";
+ str += mParameters.valueAt(i);
+ if (i < (size - 1)) str += ";";
+ }
+ return str;
+}
+
+status_t AudioParameter::add(const String8& key, const String8& value)
+{
+ if (mParameters.indexOfKey(key) < 0) {
+ mParameters.add(key, value);
+ return NO_ERROR;
+ } else {
+ mParameters.replaceValueFor(key, value);
+ return ALREADY_EXISTS;
+ }
+}
+
+status_t AudioParameter::addInt(const String8& key, const int value)
+{
+ char str[12];
+ if (snprintf(str, 12, "%d", value) > 0) {
+ String8 str8 = String8(str);
+ return add(key, str8);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::addFloat(const String8& key, const float value)
+{
+ char str[23];
+ if (snprintf(str, 23, "%.10f", value) > 0) {
+ String8 str8 = String8(str);
+ return add(key, str8);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::remove(const String8& key)
+{
+ if (mParameters.indexOfKey(key) >= 0) {
+ mParameters.removeItem(key);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::get(const String8& key, String8& value)
+{
+ if (mParameters.indexOfKey(key) >= 0) {
+ value = mParameters.valueFor(key);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioParameter::getInt(const String8& key, int& value)
+{
+ String8 str8;
+ status_t result = get(key, str8);
+ value = 0;
+ if (result == NO_ERROR) {
+ int val;
+ if (sscanf(str8.string(), "%d", &val) == 1) {
+ value = val;
+ } else {
+ result = INVALID_OPERATION;
+ }
+ }
+ return result;
+}
+
+status_t AudioParameter::getFloat(const String8& key, float& value)
+{
+ String8 str8;
+ status_t result = get(key, str8);
+ value = 0;
+ if (result == NO_ERROR) {
+ float val;
+ if (sscanf(str8.string(), "%f", &val) == 1) {
+ value = val;
+ } else {
+ result = INVALID_OPERATION;
+ }
+ }
+ return result;
+}
+
+status_t AudioParameter::getAt(size_t index, String8& key, String8& value)
+{
+ if (mParameters.size() > index) {
+ key = mParameters.keyAt(index);
+ value = mParameters.valueAt(index);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
}; // namespace android
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b2c067b..4b9d272 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -32,9 +32,9 @@
#include <media/AudioTrack.h>
#include <utils/Log.h>
-#include <utils/MemoryDealer.h>
-#include <utils/Parcel.h>
-#include <utils/IPCThreadState.h>
+#include <binder/MemoryDealer.h>
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
#include <utils/Timers.h>
#include <cutils/atomic.h>
@@ -54,7 +54,7 @@ AudioTrack::AudioTrack(
int streamType,
uint32_t sampleRate,
int format,
- int channelCount,
+ int channels,
int frameCount,
uint32_t flags,
callback_t cbf,
@@ -62,7 +62,7 @@ AudioTrack::AudioTrack(
int notificationFrames)
: mStatus(NO_INIT)
{
- mStatus = set(streamType, sampleRate, format, channelCount,
+ mStatus = set(streamType, sampleRate, format, channels,
frameCount, flags, cbf, user, notificationFrames, 0);
}
@@ -70,7 +70,7 @@ AudioTrack::AudioTrack(
int streamType,
uint32_t sampleRate,
int format,
- int channelCount,
+ int channels,
const sp<IMemory>& sharedBuffer,
uint32_t flags,
callback_t cbf,
@@ -78,7 +78,7 @@ AudioTrack::AudioTrack(
int notificationFrames)
: mStatus(NO_INIT)
{
- mStatus = set(streamType, sampleRate, format, channelCount,
+ mStatus = set(streamType, sampleRate, format, channels,
0, flags, cbf, user, notificationFrames, sharedBuffer);
}
@@ -97,6 +97,7 @@ AudioTrack::~AudioTrack()
}
mAudioTrack.clear();
IPCThreadState::self()->flushCommands();
+ AudioSystem::releaseOutput(getOutput());
}
}
@@ -104,7 +105,7 @@ status_t AudioTrack::set(
int streamType,
uint32_t sampleRate,
int format,
- int channelCount,
+ int channels,
int frameCount,
uint32_t flags,
callback_t cbf,
@@ -150,63 +151,84 @@ status_t AudioTrack::set(
if (format == 0) {
format = AudioSystem::PCM_16_BIT;
}
- if (channelCount == 0) {
- channelCount = 2;
+ if (channels == 0) {
+ channels = AudioSystem::CHANNEL_OUT_STEREO;
}
// validate parameters
- if (((format != AudioSystem::PCM_8_BIT) || sharedBuffer != 0) &&
- (format != AudioSystem::PCM_16_BIT)) {
+ if (!AudioSystem::isValidFormat(format)) {
LOGE("Invalid format");
return BAD_VALUE;
}
- if (channelCount != 1 && channelCount != 2) {
- LOGE("Invalid channel number");
+
+ // force direct flag if format is not linear PCM
+ if (!AudioSystem::isLinearPCM(format)) {
+ flags |= AudioSystem::OUTPUT_FLAG_DIRECT;
+ }
+
+ if (!AudioSystem::isOutputChannel(channels)) {
+ LOGE("Invalid channel mask");
return BAD_VALUE;
}
+ uint32_t channelCount = AudioSystem::popCount(channels);
- // Ensure that buffer depth covers at least audio hardware latency
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
- if (minBufCount < 2) minBufCount = 2;
+ audio_io_handle_t output = AudioSystem::getOutput((AudioSystem::stream_type)streamType,
+ sampleRate, format, channels, (AudioSystem::output_flags)flags);
- // When playing from shared buffer, playback will start even if last audioflinger
- // block is partly filled.
- if (sharedBuffer != 0 && minBufCount > 1) {
- minBufCount--;
+ if (output == 0) {
+ LOGE("Could not get audio output for stream type %d", streamType);
+ return BAD_VALUE;
}
- int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate;
-
- if (sharedBuffer == 0) {
- if (frameCount == 0) {
- frameCount = minFrameCount;
- }
- if (notificationFrames == 0) {
- notificationFrames = frameCount/2;
- }
- // Make sure that application is notified with sufficient margin
- // before underrun
- if (notificationFrames > frameCount/2) {
- notificationFrames = frameCount/2;
+ if (!AudioSystem::isLinearPCM(format)) {
+ if (sharedBuffer != 0) {
+ frameCount = sharedBuffer->size();
}
} else {
- // Ensure that buffer alignment matches channelcount
- if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
- LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
- return BAD_VALUE;
- }
- frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t);
- }
+ // Ensure that buffer depth covers at least audio hardware latency
+ uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
+ if (minBufCount < 2) minBufCount = 2;
+
+ int minFrameCount = (afFrameCount*sampleRate*minBufCount)/afSampleRate;
- if (frameCount < minFrameCount) {
- LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount);
- return BAD_VALUE;
+ if (sharedBuffer == 0) {
+ if (frameCount == 0) {
+ frameCount = minFrameCount;
+ }
+ if (notificationFrames == 0) {
+ notificationFrames = frameCount/2;
+ }
+ // Make sure that application is notified with sufficient margin
+ // before underrun
+ if (notificationFrames > frameCount/2) {
+ notificationFrames = frameCount/2;
+ }
+ if (frameCount < minFrameCount) {
+ LOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount);
+ return BAD_VALUE;
+ }
+ } else {
+ // Ensure that buffer alignment matches channelcount
+ if (((uint32_t)sharedBuffer->pointer() & (channelCount | 1)) != 0) {
+ LOGE("Invalid buffer alignement: address %p, channelCount %d", sharedBuffer->pointer(), channelCount);
+ return BAD_VALUE;
+ }
+ frameCount = sharedBuffer->size()/channelCount/sizeof(int16_t);
+ }
}
// create the track
status_t status;
sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),
- streamType, sampleRate, format, channelCount, frameCount, flags, sharedBuffer, &status);
+ streamType,
+ sampleRate,
+ format,
+ channelCount,
+ frameCount,
+ ((uint16_t)flags) << 16,
+ sharedBuffer,
+ output,
+ &status);
if (track == 0) {
LOGE("AudioFlinger could not create track, status: %d", status);
@@ -245,6 +267,7 @@ status_t AudioTrack::set(
mVolume[RIGHT] = 1.0f;
mStreamType = streamType;
mFormat = format;
+ mChannels = channels;
mChannelCount = channelCount;
mSharedBuffer = sharedBuffer;
mMuted = false;
@@ -259,6 +282,7 @@ status_t AudioTrack::set(
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
+ mFlags = flags;
return NO_ERROR;
}
@@ -297,7 +321,11 @@ uint32_t AudioTrack::frameCount() const
int AudioTrack::frameSize() const
{
- return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ return channelCount()*((format() == AudioSystem::PCM_8_BIT) ? sizeof(uint8_t) : sizeof(int16_t));
+ } else {
+ return sizeof(uint8_t);
+ }
}
sp<IMemory>& AudioTrack::sharedBuffer()
@@ -323,6 +351,7 @@ void AudioTrack::start()
}
if (android_atomic_or(1, &mActive) == 0) {
+ AudioSystem::startOutput(getOutput(), (AudioSystem::stream_type)mStreamType);
mNewPosition = mCblk->server + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
@@ -367,6 +396,7 @@ void AudioTrack::stop()
} else {
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
}
+ AudioSystem::stopOutput(getOutput(), (AudioSystem::stream_type)mStreamType);
}
if (t != 0) {
@@ -382,12 +412,12 @@ bool AudioTrack::stopped() const
void AudioTrack::flush()
{
LOGV("flush");
-
+
// clear playback marker and periodic update counter
mMarkerPosition = 0;
mMarkerReached = false;
mUpdatePeriod = 0;
-
+
if (!mActive) {
mAudioTrack->flush();
@@ -403,6 +433,7 @@ void AudioTrack::pause()
if (android_atomic_and(~1, &mActive) == 1) {
mActive = 0;
mAudioTrack->pause();
+ AudioSystem::stopOutput(getOutput(), (AudioSystem::stream_type)mStreamType);
}
}
@@ -455,7 +486,6 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount
{
audio_track_cblk_t* cblk = mCblk;
-
Mutex::Autolock _l(cblk->lock);
if (loopCount == 0) {
@@ -476,7 +506,7 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount
LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d",
loopStart, loopEnd, mFrameCount);
return BAD_VALUE;
- }
+ }
cblk->loopStart = loopStart;
cblk->loopEnd = loopEnd;
@@ -555,7 +585,7 @@ status_t AudioTrack::setPosition(uint32_t position)
mCblk->server = position;
mCblk->forceReady = 1;
-
+
return NO_ERROR;
}
@@ -571,7 +601,7 @@ status_t AudioTrack::getPosition(uint32_t *position)
status_t AudioTrack::reload()
{
if (!stopped()) return INVALID_OPERATION;
-
+
flush();
mCblk->stepUser(mFrameCount);
@@ -579,6 +609,12 @@ status_t AudioTrack::reload()
return NO_ERROR;
}
+audio_io_handle_t AudioTrack::getOutput()
+{
+ return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType,
+ mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
+}
+
// -------------------------------------------------------------------------
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
@@ -608,7 +644,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
return WOULD_BLOCK;
timeout = 0;
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- if (__builtin_expect(result!=NO_ERROR, false)) {
+ if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += waitTimeMs;
if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
// timing out when a loop has been set and we have already written upto loop end
@@ -616,7 +652,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
if (cblk->user < cblk->loopEnd) {
LOGW( "obtainBuffer timed out (is the CPU pegged?) %p "
"user=%08x, server=%08x", this, cblk->user, cblk->server);
- //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
+ //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
cblk->lock.unlock();
mAudioTrack->start();
cblk->lock.lock();
@@ -624,7 +660,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
}
cblk->waitTimeMs = 0;
}
-
+
if (--waitCount == 0) {
return TIMED_OUT;
}
@@ -636,7 +672,7 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
}
cblk->waitTimeMs = 0;
-
+
if (framesReq > framesAvail) {
framesReq = framesAvail;
}
@@ -653,12 +689,16 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
"but didn't need to be locked. We recovered, but "
"this shouldn't happen (user=%08x, server=%08x)", cblk->user, cblk->server);
- audioBuffer->flags = mMuted ? Buffer::MUTE : 0;
- audioBuffer->channelCount= mChannelCount;
- audioBuffer->format = AudioSystem::PCM_16_BIT;
- audioBuffer->frameCount = framesReq;
- audioBuffer->size = framesReq*mChannelCount*sizeof(int16_t);
- audioBuffer->raw = (int8_t *)cblk->buffer(u);
+ audioBuffer->flags = mMuted ? Buffer::MUTE : 0;
+ audioBuffer->channelCount = mChannelCount;
+ audioBuffer->frameCount = framesReq;
+ audioBuffer->size = framesReq * cblk->frameSize;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ audioBuffer->format = AudioSystem::PCM_16_BIT;
+ } else {
+ audioBuffer->format = mFormat;
+ }
+ audioBuffer->raw = (int8_t *)cblk->buffer(u);
active = mActive;
return active ? status_t(NO_ERROR) : status_t(STOPPED);
}
@@ -690,10 +730,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
Buffer audioBuffer;
do {
- audioBuffer.frameCount = userSize/mChannelCount;
- if (mFormat == AudioSystem::PCM_16_BIT) {
- audioBuffer.frameCount >>= 1;
- }
+ audioBuffer.frameCount = userSize/frameSize();
+
// Calling obtainBuffer() with a negative wait count causes
// an (almost) infinite wait time.
status_t err = obtainBuffer(&audioBuffer, -1);
@@ -705,7 +743,8 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
}
size_t toWrite;
- if (mFormat == AudioSystem::PCM_8_BIT) {
+
+ if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
// Divide capacity by 2 to take expansion into account
toWrite = audioBuffer.size>>1;
// 8 to 16 bit conversion
@@ -714,7 +753,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
while(count--) {
*dst++ = (int16_t)(*src++^0x80) << 8;
}
- }else {
+ } else {
toWrite = audioBuffer.size;
memcpy(audioBuffer.i8, src, toWrite);
src += toWrite;
@@ -742,13 +781,13 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
if (mCblk->flowControlFlag == 0) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
if (mCblk->server == mCblk->frameCount) {
- mCbf(EVENT_BUFFER_END, mUserData, 0);
+ mCbf(EVENT_BUFFER_END, mUserData, 0);
}
mCblk->flowControlFlag = 1;
if (mSharedBuffer != 0) return false;
}
}
-
+
// Manage loop end callback
while (mLoopCount > mCblk->loopCount) {
int loopCount = -1;
@@ -767,7 +806,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
}
// Manage new position callback
- if(mUpdatePeriod > 0) {
+ if (mUpdatePeriod > 0) {
while (mCblk->server >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
@@ -784,10 +823,10 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
do {
audioBuffer.frameCount = frames;
-
- // Calling obtainBuffer() with a wait count of 1
- // limits wait time to WAIT_PERIOD_MS. This prevents from being
- // stuck here not being able to handle timed events (position, markers, loops).
+
+ // Calling obtainBuffer() with a wait count of 1
+ // limits wait time to WAIT_PERIOD_MS. This prevents from being
+ // stuck here not being able to handle timed events (position, markers, loops).
status_t err = obtainBuffer(&audioBuffer, 1);
if (err < NO_ERROR) {
if (err != TIMED_OUT) {
@@ -801,7 +840,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
// Divide buffer size by 2 to take into account the expansion
// due to 8 to 16 bit conversion: the callback must fill only half
// of the destination buffer
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
audioBuffer.size >>= 1;
}
@@ -820,7 +859,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
}
if (writtenSize > reqSize) writtenSize = reqSize;
- if (mFormat == AudioSystem::PCM_8_BIT) {
+ if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {
// 8 to 16 bit conversion
const int8_t *src = audioBuffer.i8 + writtenSize-1;
int count = writtenSize;
@@ -832,7 +871,11 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
}
audioBuffer.size = writtenSize;
- audioBuffer.frameCount = writtenSize/mChannelCount/sizeof(int16_t);
+ // NOTE: mCblk->frameSize is not equal to AudioTrack::frameSize() for
+ // 8 bit PCM data: in this case, mCblk->frameSize is based on a sampel size of
+ // 16 bit.
+ audioBuffer.frameCount = writtenSize/mCblk->frameSize;
+
frames -= audioBuffer.frameCount;
releaseBuffer(&audioBuffer);
@@ -891,7 +934,7 @@ void AudioTrack::AudioTrackThread::onFirstRef()
// =========================================================================
audio_track_cblk_t::audio_track_cblk_t()
- : user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
+ : lock(Mutex::SHARED), user(0), server(0), userBase(0), serverBase(0), buffers(0), frameCount(0),
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0), flowControlFlag(1), forceReady(0)
{
}
@@ -949,7 +992,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount)
// we switch to normal obtainBuffer() timeout period
if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
bufferTimeoutMs = MAX_RUN_TIMEOUT_MS - 1;
- }
+ }
// It is possible that we receive a flush()
// while the mixer is processing a block: in this case,
// stepServer() is called After the flush() has reset u & s and
@@ -981,7 +1024,7 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount)
void* audio_track_cblk_t::buffer(uint32_t offset) const
{
- return (int16_t *)this->buffers + (offset-userBase)*this->channels;
+ return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
}
uint32_t audio_track_cblk_t::framesAvailable()
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index eeaa54f..fc39a46 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -16,12 +16,13 @@
*/
#define LOG_TAG "IAudioFlinger"
+//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <media/IAudioFlinger.h>
@@ -44,17 +45,21 @@ enum {
STREAM_VOLUME,
STREAM_MUTE,
SET_MODE,
- GET_MODE,
- SET_ROUTING,
- GET_ROUTING,
SET_MIC_MUTE,
GET_MIC_MUTE,
IS_MUSIC_ACTIVE,
- SET_PARAMETER,
+ SET_PARAMETERS,
+ GET_PARAMETERS,
REGISTER_CLIENT,
GET_INPUTBUFFERSIZE,
- WAKE_UP,
- IS_A2DP_ENABLED
+ OPEN_OUTPUT,
+ OPEN_DUPLICATE_OUTPUT,
+ CLOSE_OUTPUT,
+ SUSPEND_OUTPUT,
+ RESTORE_OUTPUT,
+ OPEN_INPUT,
+ CLOSE_INPUT,
+ SET_STREAM_OUTPUT
};
class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -74,6 +79,7 @@ public:
int frameCount,
uint32_t flags,
const sp<IMemory>& sharedBuffer,
+ int output,
status_t *status)
{
Parcel data, reply;
@@ -86,6 +92,7 @@ public:
data.writeInt32(frameCount);
data.writeInt32(flags);
data.writeStrongBinder(sharedBuffer->asBinder());
+ data.writeInt32(output);
status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
if (lStatus != NO_ERROR) {
LOGE("createTrack error: %s", strerror(-lStatus));
@@ -99,7 +106,7 @@ public:
virtual sp<IAudioRecord> openRecord(
pid_t pid,
- int inputSource,
+ int input,
uint32_t sampleRate,
int format,
int channelCount,
@@ -110,7 +117,7 @@ public:
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(pid);
- data.writeInt32(inputSource);
+ data.writeInt32(input);
data.writeInt32(sampleRate);
data.writeInt32(format);
data.writeInt32(channelCount);
@@ -203,12 +210,13 @@ public:
return reply.readInt32();
}
- virtual status_t setStreamVolume(int stream, float value)
+ virtual status_t setStreamVolume(int stream, float value, int output)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(stream);
data.writeFloat(value);
+ data.writeInt32(output);
remote()->transact(SET_STREAM_VOLUME, data, &reply);
return reply.readInt32();
}
@@ -223,11 +231,12 @@ public:
return reply.readInt32();
}
- virtual float streamVolume(int stream) const
+ virtual float streamVolume(int stream, int output) const
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(stream);
+ data.writeInt32(output);
remote()->transact(STREAM_VOLUME, data, &reply);
return reply.readFloat();
}
@@ -241,111 +250,201 @@ public:
return reply.readInt32();
}
- virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask)
+ virtual status_t setMode(int mode)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32(mode);
- data.writeInt32(routes);
- data.writeInt32(mask);
- remote()->transact(SET_ROUTING, data, &reply);
+ remote()->transact(SET_MODE, data, &reply);
return reply.readInt32();
}
- virtual uint32_t getRouting(int mode) const
+ virtual status_t setMicMute(bool state)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(mode);
- remote()->transact(GET_ROUTING, data, &reply);
+ data.writeInt32(state);
+ remote()->transact(SET_MIC_MUTE, data, &reply);
return reply.readInt32();
}
- virtual status_t setMode(int mode)
+ virtual bool getMicMute() const
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(mode);
- remote()->transact(SET_MODE, data, &reply);
+ remote()->transact(GET_MIC_MUTE, data, &reply);
return reply.readInt32();
}
- virtual int getMode() const
+ virtual bool isMusicActive() const
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(GET_MODE, data, &reply);
+ remote()->transact(IS_MUSIC_ACTIVE, data, &reply);
return reply.readInt32();
}
- virtual status_t setMicMute(bool state)
+ virtual status_t setParameters(int ioHandle, const String8& keyValuePairs)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(state);
- remote()->transact(SET_MIC_MUTE, data, &reply);
+ data.writeInt32(ioHandle);
+ data.writeString8(keyValuePairs);
+ remote()->transact(SET_PARAMETERS, data, &reply);
return reply.readInt32();
}
- virtual bool getMicMute() const
+ virtual String8 getParameters(int ioHandle, const String8& keys)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(GET_MIC_MUTE, data, &reply);
+ data.writeInt32(ioHandle);
+ data.writeString8(keys);
+ remote()->transact(GET_PARAMETERS, data, &reply);
+ return reply.readString8();
+ }
+
+ virtual void registerClient(const sp<IAudioFlingerClient>& client)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeStrongBinder(client->asBinder());
+ remote()->transact(REGISTER_CLIENT, data, &reply);
+ }
+
+ virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(sampleRate);
+ data.writeInt32(format);
+ data.writeInt32(channelCount);
+ remote()->transact(GET_INPUTBUFFERSIZE, data, &reply);
return reply.readInt32();
}
- virtual bool isMusicActive() const
+ virtual int openOutput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t *pLatencyMs,
+ uint32_t flags)
{
Parcel data, reply;
+ uint32_t devices = pDevices ? *pDevices : 0;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+ uint32_t latency = pLatencyMs ? *pLatencyMs : 0;
+
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(IS_MUSIC_ACTIVE, data, &reply);
+ data.writeInt32(devices);
+ data.writeInt32(samplingRate);
+ data.writeInt32(format);
+ data.writeInt32(channels);
+ data.writeInt32(latency);
+ data.writeInt32(flags);
+ remote()->transact(OPEN_OUTPUT, data, &reply);
+ int output = reply.readInt32();
+ LOGV("openOutput() returned output, %p", output);
+ devices = reply.readInt32();
+ if (pDevices) *pDevices = devices;
+ samplingRate = reply.readInt32();
+ if (pSamplingRate) *pSamplingRate = samplingRate;
+ format = reply.readInt32();
+ if (pFormat) *pFormat = format;
+ channels = reply.readInt32();
+ if (pChannels) *pChannels = channels;
+ latency = reply.readInt32();
+ if (pLatencyMs) *pLatencyMs = latency;
+ return output;
+ }
+
+ virtual int openDuplicateOutput(int output1, int output2)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(output1);
+ data.writeInt32(output2);
+ remote()->transact(OPEN_DUPLICATE_OUTPUT, data, &reply);
return reply.readInt32();
}
- virtual status_t setParameter(const char* key, const char* value)
+ virtual status_t closeOutput(int output)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeCString(key);
- data.writeCString(value);
- remote()->transact(SET_PARAMETER, data, &reply);
+ data.writeInt32(output);
+ remote()->transact(CLOSE_OUTPUT, data, &reply);
return reply.readInt32();
}
-
- virtual void registerClient(const sp<IAudioFlingerClient>& client)
+
+ virtual status_t suspendOutput(int output)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeStrongBinder(client->asBinder());
- remote()->transact(REGISTER_CLIENT, data, &reply);
+ data.writeInt32(output);
+ remote()->transact(SUSPEND_OUTPUT, data, &reply);
+ return reply.readInt32();
}
-
- virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount)
+
+ virtual status_t restoreOutput(int output)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- data.writeInt32(sampleRate);
- data.writeInt32(format);
- data.writeInt32(channelCount);
- remote()->transact(GET_INPUTBUFFERSIZE, data, &reply);
+ data.writeInt32(output);
+ remote()->transact(RESTORE_OUTPUT, data, &reply);
return reply.readInt32();
}
-
- virtual void wakeUp()
+
+ virtual int openInput(uint32_t *pDevices,
+ uint32_t *pSamplingRate,
+ uint32_t *pFormat,
+ uint32_t *pChannels,
+ uint32_t acoustics)
{
Parcel data, reply;
+ uint32_t devices = pDevices ? *pDevices : 0;
+ uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0;
+ uint32_t format = pFormat ? *pFormat : 0;
+ uint32_t channels = pChannels ? *pChannels : 0;
+
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(WAKE_UP, data, &reply, IBinder::FLAG_ONEWAY);
- return;
+ data.writeInt32(devices);
+ data.writeInt32(samplingRate);
+ data.writeInt32(format);
+ data.writeInt32(channels);
+ data.writeInt32(acoustics);
+ remote()->transact(OPEN_INPUT, data, &reply);
+ int input = reply.readInt32();
+ devices = reply.readInt32();
+ if (pDevices) *pDevices = devices;
+ samplingRate = reply.readInt32();
+ if (pSamplingRate) *pSamplingRate = samplingRate;
+ format = reply.readInt32();
+ if (pFormat) *pFormat = format;
+ channels = reply.readInt32();
+ if (pChannels) *pChannels = channels;
+ return input;
}
- virtual bool isA2dpEnabled() const
+ virtual status_t closeInput(int input)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
- remote()->transact(IS_A2DP_ENABLED, data, &reply);
- return (bool)reply.readInt32();
+ data.writeInt32(input);
+ remote()->transact(CLOSE_INPUT, data, &reply);
+ return reply.readInt32();
+ }
+
+ virtual status_t setStreamOutput(uint32_t stream, int output)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(stream);
+ data.writeInt32(output);
+ remote()->transact(SET_STREAM_OUTPUT, data, &reply);
+ return reply.readInt32();
}
};
@@ -353,12 +452,6 @@ IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnAudioFlinger::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -373,10 +466,11 @@ status_t BnAudioFlinger::onTransact(
size_t bufferCount = data.readInt32();
uint32_t flags = data.readInt32();
sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
+ int output = data.readInt32();
status_t status;
sp<IAudioTrack> track = createTrack(pid,
streamType, sampleRate, format,
- channelCount, bufferCount, flags, buffer, &status);
+ channelCount, bufferCount, flags, buffer, output, &status);
reply->writeInt32(status);
reply->writeStrongBinder(track->asBinder());
return NO_ERROR;
@@ -384,14 +478,14 @@ status_t BnAudioFlinger::onTransact(
case OPEN_RECORD: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
pid_t pid = data.readInt32();
- int inputSource = data.readInt32();
+ int input = data.readInt32();
uint32_t sampleRate = data.readInt32();
int format = data.readInt32();
int channelCount = data.readInt32();
size_t bufferCount = data.readInt32();
uint32_t flags = data.readInt32();
status_t status;
- sp<IAudioRecord> record = openRecord(pid, inputSource,
+ sp<IAudioRecord> record = openRecord(pid, input,
sampleRate, format, channelCount, bufferCount, flags, &status);
reply->writeInt32(status);
reply->writeStrongBinder(record->asBinder());
@@ -399,32 +493,27 @@ status_t BnAudioFlinger::onTransact(
} break;
case SAMPLE_RATE: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- int output = data.readInt32();
- reply->writeInt32( sampleRate(output) );
+ reply->writeInt32( sampleRate(data.readInt32()) );
return NO_ERROR;
} break;
case CHANNEL_COUNT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- int output = data.readInt32();
- reply->writeInt32( channelCount(output) );
+ reply->writeInt32( channelCount(data.readInt32()) );
return NO_ERROR;
} break;
case FORMAT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- int output = data.readInt32();
- reply->writeInt32( format(output) );
+ reply->writeInt32( format(data.readInt32()) );
return NO_ERROR;
} break;
case FRAME_COUNT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- int output = data.readInt32();
- reply->writeInt32( frameCount(output) );
+ reply->writeInt32( frameCount(data.readInt32()) );
return NO_ERROR;
} break;
case LATENCY: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- int output = data.readInt32();
- reply->writeInt32( latency(output) );
+ reply->writeInt32( latency(data.readInt32()) );
return NO_ERROR;
} break;
case SET_MASTER_VOLUME: {
@@ -450,7 +539,9 @@ status_t BnAudioFlinger::onTransact(
case SET_STREAM_VOLUME: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int stream = data.readInt32();
- reply->writeInt32( setStreamVolume(stream, data.readFloat()) );
+ float volume = data.readFloat();
+ int output = data.readInt32();
+ reply->writeInt32( setStreamVolume(stream, volume, output) );
return NO_ERROR;
} break;
case SET_STREAM_MUTE: {
@@ -462,7 +553,8 @@ status_t BnAudioFlinger::onTransact(
case STREAM_VOLUME: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int stream = data.readInt32();
- reply->writeFloat( streamVolume(stream) );
+ int output = data.readInt32();
+ reply->writeFloat( streamVolume(stream, output) );
return NO_ERROR;
} break;
case STREAM_MUTE: {
@@ -471,31 +563,12 @@ status_t BnAudioFlinger::onTransact(
reply->writeInt32( streamMute(stream) );
return NO_ERROR;
} break;
- case SET_ROUTING: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int mode = data.readInt32();
- uint32_t routes = data.readInt32();
- uint32_t mask = data.readInt32();
- reply->writeInt32( setRouting(mode, routes, mask) );
- return NO_ERROR;
- } break;
- case GET_ROUTING: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- int mode = data.readInt32();
- reply->writeInt32( getRouting(mode) );
- return NO_ERROR;
- } break;
case SET_MODE: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int mode = data.readInt32();
reply->writeInt32( setMode(mode) );
return NO_ERROR;
} break;
- case GET_MODE: {
- CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( getMode() );
- return NO_ERROR;
- } break;
case SET_MIC_MUTE: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
int state = data.readInt32();
@@ -512,13 +585,21 @@ status_t BnAudioFlinger::onTransact(
reply->writeInt32( isMusicActive() );
return NO_ERROR;
} break;
- case SET_PARAMETER: {
+ case SET_PARAMETERS: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- const char *key = data.readCString();
- const char *value = data.readCString();
- reply->writeInt32( setParameter(key, value) );
+ int ioHandle = data.readInt32();
+ String8 keyValuePairs(data.readString8());
+ reply->writeInt32(setParameters(ioHandle, keyValuePairs));
return NO_ERROR;
- } break;
+ } break;
+ case GET_PARAMETERS: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ int ioHandle = data.readInt32();
+ String8 keys(data.readString8());
+ reply->writeString8(getParameters(ioHandle, keys));
+ return NO_ERROR;
+ } break;
+
case REGISTER_CLIENT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient>(data.readStrongBinder());
@@ -533,14 +614,81 @@ status_t BnAudioFlinger::onTransact(
reply->writeInt32( getInputBufferSize(sampleRate, format, channelCount) );
return NO_ERROR;
} break;
- case WAKE_UP: {
+ case OPEN_OUTPUT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- wakeUp();
+ uint32_t devices = data.readInt32();
+ uint32_t samplingRate = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t channels = data.readInt32();
+ uint32_t latency = data.readInt32();
+ uint32_t flags = data.readInt32();
+ int output = openOutput(&devices,
+ &samplingRate,
+ &format,
+ &channels,
+ &latency,
+ flags);
+ LOGV("OPEN_OUTPUT output, %p", output);
+ reply->writeInt32(output);
+ reply->writeInt32(devices);
+ reply->writeInt32(samplingRate);
+ reply->writeInt32(format);
+ reply->writeInt32(channels);
+ reply->writeInt32(latency);
return NO_ERROR;
} break;
- case IS_A2DP_ENABLED: {
+ case OPEN_DUPLICATE_OUTPUT: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
- reply->writeInt32( (int)isA2dpEnabled() );
+ int output1 = data.readInt32();
+ int output2 = data.readInt32();
+ reply->writeInt32(openDuplicateOutput(output1, output2));
+ return NO_ERROR;
+ } break;
+ case CLOSE_OUTPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(closeOutput(data.readInt32()));
+ return NO_ERROR;
+ } break;
+ case SUSPEND_OUTPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(suspendOutput(data.readInt32()));
+ return NO_ERROR;
+ } break;
+ case RESTORE_OUTPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(restoreOutput(data.readInt32()));
+ return NO_ERROR;
+ } break;
+ case OPEN_INPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ uint32_t devices = data.readInt32();
+ uint32_t samplingRate = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t channels = data.readInt32();
+ uint32_t acoutics = data.readInt32();
+
+ int input = openInput(&devices,
+ &samplingRate,
+ &format,
+ &channels,
+ acoutics);
+ reply->writeInt32(input);
+ reply->writeInt32(devices);
+ reply->writeInt32(samplingRate);
+ reply->writeInt32(format);
+ reply->writeInt32(channels);
+ return NO_ERROR;
+ } break;
+ case CLOSE_INPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ reply->writeInt32(closeInput(data.readInt32()));
+ return NO_ERROR;
+ } break;
+ case SET_STREAM_OUTPUT: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ uint32_t stream = data.readInt32();
+ int output = data.readInt32();
+ reply->writeInt32(setStreamOutput(stream, output));
return NO_ERROR;
} break;
default:
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 9d00aef..3900de4 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -20,14 +20,15 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <media/IAudioFlingerClient.h>
+#include <media/AudioSystem.h>
namespace android {
enum {
- AUDIO_OUTPUT_CHANGED = IBinder::FIRST_CALL_TRANSACTION
+ IO_CONFIG_CHANGED = IBinder::FIRST_CALL_TRANSACTION
};
class BpAudioFlingerClient : public BpInterface<IAudioFlingerClient>
@@ -38,12 +39,25 @@ public:
{
}
- void a2dpEnabledChanged(bool enabled)
+ void ioConfigChanged(int event, int ioHandle, void *param2)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
- data.writeInt32((int)enabled);
- remote()->transact(AUDIO_OUTPUT_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
+ data.writeInt32(event);
+ data.writeInt32(ioHandle);
+ if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
+ uint32_t stream = *(uint32_t *)param2;
+ LOGV("ioConfigChanged stream %d", stream);
+ data.writeInt32(stream);
+ } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
+ AudioSystem::OutputDescriptor *desc = (AudioSystem::OutputDescriptor *)param2;
+ data.writeInt32(desc->samplingRate);
+ data.writeInt32(desc->format);
+ data.writeInt32(desc->channels);
+ data.writeInt32(desc->frameCount);
+ data.writeInt32(desc->latency);
+ }
+ remote()->transact(IO_CONFIG_CHANGED, data, &reply, IBinder::FLAG_ONEWAY);
}
};
@@ -51,20 +65,30 @@ IMPLEMENT_META_INTERFACE(AudioFlingerClient, "android.media.IAudioFlingerClient"
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnAudioFlingerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
- case AUDIO_OUTPUT_CHANGED: {
+ case IO_CONFIG_CHANGED: {
CHECK_INTERFACE(IAudioFlingerClient, data, reply);
- bool enabled = (bool)data.readInt32();
- a2dpEnabledChanged(enabled);
+ int event = data.readInt32();
+ int ioHandle = data.readInt32();
+ void *param2 = 0;
+ AudioSystem::OutputDescriptor desc;
+ uint32_t stream;
+ if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
+ stream = data.readInt32();
+ param2 = &stream;
+ LOGV("STREAM_CONFIG_CHANGED stream %d", stream);
+ } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
+ desc.samplingRate = data.readInt32();
+ desc.format = data.readInt32();
+ desc.channels = data.readInt32();
+ desc.frameCount = data.readInt32();
+ desc.latency = data.readInt32();
+ param2 = &desc;
+ }
+ ioConfigChanged(event, ioHandle, param2);
return NO_ERROR;
} break;
default:
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
new file mode 100644
index 0000000..18dd173
--- /dev/null
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -0,0 +1,413 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "IAudioPolicyService"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IAudioPolicyService.h>
+
+namespace android {
+
+enum {
+ SET_DEVICE_CONNECTION_STATE = IBinder::FIRST_CALL_TRANSACTION,
+ GET_DEVICE_CONNECTION_STATE,
+ SET_PHONE_STATE,
+ SET_RINGER_MODE,
+ SET_FORCE_USE,
+ GET_FORCE_USE,
+ GET_OUTPUT,
+ START_OUTPUT,
+ STOP_OUTPUT,
+ RELEASE_OUTPUT,
+ GET_INPUT,
+ START_INPUT,
+ STOP_INPUT,
+ RELEASE_INPUT,
+ INIT_STREAM_VOLUME,
+ SET_STREAM_VOLUME,
+ GET_STREAM_VOLUME
+};
+
+class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
+{
+public:
+ BpAudioPolicyService(const sp<IBinder>& impl)
+ : BpInterface<IAudioPolicyService>(impl)
+ {
+ }
+
+ virtual status_t setDeviceConnectionState(
+ AudioSystem::audio_devices device,
+ AudioSystem::device_connection_state state,
+ const char *device_address)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(device));
+ data.writeInt32(static_cast <uint32_t>(state));
+ data.writeCString(device_address);
+ remote()->transact(SET_DEVICE_CONNECTION_STATE, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual AudioSystem::device_connection_state getDeviceConnectionState(
+ AudioSystem::audio_devices device,
+ const char *device_address)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(device));
+ data.writeCString(device_address);
+ remote()->transact(GET_DEVICE_CONNECTION_STATE, data, &reply);
+ return static_cast <AudioSystem::device_connection_state>(reply.readInt32());
+ }
+
+ virtual status_t setPhoneState(int state)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(state);
+ remote()->transact(SET_PHONE_STATE, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t setRingerMode(uint32_t mode, uint32_t mask)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(mode);
+ data.writeInt32(mask);
+ remote()->transact(SET_RINGER_MODE, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(usage));
+ data.writeInt32(static_cast <uint32_t>(config));
+ remote()->transact(SET_FORCE_USE, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(usage));
+ remote()->transact(GET_FORCE_USE, data, &reply);
+ return static_cast <AudioSystem::forced_config> (reply.readInt32());
+ }
+
+ virtual audio_io_handle_t getOutput(
+ AudioSystem::stream_type stream,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::output_flags flags)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(stream));
+ data.writeInt32(samplingRate);
+ data.writeInt32(static_cast <uint32_t>(format));
+ data.writeInt32(channels);
+ data.writeInt32(static_cast <uint32_t>(flags));
+ remote()->transact(GET_OUTPUT, data, &reply);
+ return static_cast <audio_io_handle_t> (reply.readInt32());
+ }
+
+ virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(output);
+ data.writeInt32(stream);
+ remote()->transact(START_OUTPUT, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(output);
+ data.writeInt32(stream);
+ remote()->transact(STOP_OUTPUT, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual void releaseOutput(audio_io_handle_t output)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(output);
+ remote()->transact(RELEASE_OUTPUT, data, &reply);
+ }
+
+ virtual audio_io_handle_t getInput(
+ int inputSource,
+ uint32_t samplingRate,
+ uint32_t format,
+ uint32_t channels,
+ AudioSystem::audio_in_acoustics acoustics)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(inputSource);
+ data.writeInt32(samplingRate);
+ data.writeInt32(static_cast <uint32_t>(format));
+ data.writeInt32(channels);
+ data.writeInt32(static_cast <uint32_t>(acoustics));
+ remote()->transact(GET_INPUT, data, &reply);
+ return static_cast <audio_io_handle_t> (reply.readInt32());
+ }
+
+ virtual status_t startInput(audio_io_handle_t input)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(input);
+ remote()->transact(START_INPUT, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t stopInput(audio_io_handle_t input)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(input);
+ remote()->transact(STOP_INPUT, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual void releaseInput(audio_io_handle_t input)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(input);
+ remote()->transact(RELEASE_INPUT, data, &reply);
+ }
+
+ virtual status_t initStreamVolume(AudioSystem::stream_type stream,
+ int indexMin,
+ int indexMax)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(stream));
+ data.writeInt32(indexMin);
+ data.writeInt32(indexMax);
+ remote()->transact(INIT_STREAM_VOLUME, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(stream));
+ data.writeInt32(index);
+ remote()->transact(SET_STREAM_VOLUME, data, &reply);
+ return static_cast <status_t> (reply.readInt32());
+ }
+
+ virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeInt32(static_cast <uint32_t>(stream));
+ remote()->transact(GET_STREAM_VOLUME, data, &reply);
+ int lIndex = reply.readInt32();
+ if (index) *index = lIndex;
+ return static_cast <status_t> (reply.readInt32());
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
+
+// ----------------------------------------------------------------------
+
+
+status_t BnAudioPolicyService::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case SET_DEVICE_CONNECTION_STATE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::audio_devices device = static_cast <AudioSystem::audio_devices>(data.readInt32());
+ AudioSystem::device_connection_state state = static_cast <AudioSystem::device_connection_state>(data.readInt32());
+ const char *device_address = data.readCString();
+ reply->writeInt32(static_cast <uint32_t>(setDeviceConnectionState(device, state, device_address)));
+ return NO_ERROR;
+ } break;
+
+ case GET_DEVICE_CONNECTION_STATE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::audio_devices device = static_cast <AudioSystem::audio_devices>(data.readInt32());
+ const char *device_address = data.readCString();
+ reply->writeInt32(static_cast <uint32_t>(getDeviceConnectionState(device, device_address)));
+ return NO_ERROR;
+ } break;
+
+ case SET_PHONE_STATE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ reply->writeInt32(static_cast <uint32_t>(setPhoneState(data.readInt32())));
+ return NO_ERROR;
+ } break;
+
+ case SET_RINGER_MODE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ uint32_t mode = data.readInt32();
+ uint32_t mask = data.readInt32();
+ reply->writeInt32(static_cast <uint32_t>(setRingerMode(mode, mask)));
+ return NO_ERROR;
+ } break;
+
+ case SET_FORCE_USE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32());
+ AudioSystem::forced_config config = static_cast <AudioSystem::forced_config>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(setForceUse(usage, config)));
+ return NO_ERROR;
+ } break;
+
+ case GET_FORCE_USE: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::force_use usage = static_cast <AudioSystem::force_use>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(getForceUse(usage)));
+ return NO_ERROR;
+ } break;
+
+ case GET_OUTPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::stream_type stream = static_cast <AudioSystem::stream_type>(data.readInt32());
+ uint32_t samplingRate = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t channels = data.readInt32();
+ AudioSystem::output_flags flags = static_cast <AudioSystem::output_flags>(data.readInt32());
+
+ audio_io_handle_t output = getOutput(stream,
+ samplingRate,
+ format,
+ channels,
+ flags);
+ reply->writeInt32(static_cast <int>(output));
+ return NO_ERROR;
+ } break;
+
+ case START_OUTPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
+ uint32_t stream = data.readInt32();
+ reply->writeInt32(static_cast <uint32_t>(startOutput(output, (AudioSystem::stream_type)stream)));
+ return NO_ERROR;
+ } break;
+
+ case STOP_OUTPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
+ uint32_t stream = data.readInt32();
+ reply->writeInt32(static_cast <uint32_t>(stopOutput(output, (AudioSystem::stream_type)stream)));
+ return NO_ERROR;
+ } break;
+
+ case RELEASE_OUTPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());
+ releaseOutput(output);
+ return NO_ERROR;
+ } break;
+
+ case GET_INPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ int inputSource = data.readInt32();
+ uint32_t samplingRate = data.readInt32();
+ uint32_t format = data.readInt32();
+ uint32_t channels = data.readInt32();
+ AudioSystem::audio_in_acoustics acoustics = static_cast <AudioSystem::audio_in_acoustics>(data.readInt32());
+ audio_io_handle_t input = getInput(inputSource,
+ samplingRate,
+ format,
+ channels,
+ acoustics);
+ reply->writeInt32(static_cast <int>(input));
+ return NO_ERROR;
+ } break;
+
+ case START_INPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(startInput(input)));
+ return NO_ERROR;
+ } break;
+
+ case STOP_INPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
+ reply->writeInt32(static_cast <uint32_t>(stopInput(input)));
+ return NO_ERROR;
+ } break;
+
+ case RELEASE_INPUT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ audio_io_handle_t input = static_cast <audio_io_handle_t>(data.readInt32());
+ releaseInput(input);
+ return NO_ERROR;
+ } break;
+
+ case INIT_STREAM_VOLUME: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::stream_type stream = static_cast <AudioSystem::stream_type>(data.readInt32());
+ int indexMin = data.readInt32();
+ int indexMax = data.readInt32();
+ reply->writeInt32(static_cast <uint32_t>(initStreamVolume(stream, indexMin,indexMax)));
+ return NO_ERROR;
+ } break;
+
+ case SET_STREAM_VOLUME: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::stream_type stream = static_cast <AudioSystem::stream_type>(data.readInt32());
+ int index = data.readInt32();
+ reply->writeInt32(static_cast <uint32_t>(setStreamVolumeIndex(stream, index)));
+ return NO_ERROR;
+ } break;
+
+ case GET_STREAM_VOLUME: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ AudioSystem::stream_type stream = static_cast <AudioSystem::stream_type>(data.readInt32());
+ int index;
+ status_t status = getStreamVolumeIndex(stream, &index);
+ reply->writeInt32(index);
+ reply->writeInt32(static_cast <uint32_t>(status));
+ return NO_ERROR;
+ } break;
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libmedia/IAudioRecord.cpp b/media/libmedia/IAudioRecord.cpp
index 6e42dac..8fb5d3d 100644
--- a/media/libmedia/IAudioRecord.cpp
+++ b/media/libmedia/IAudioRecord.cpp
@@ -18,7 +18,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <media/IAudioRecord.h>
@@ -66,12 +66,6 @@ IMPLEMENT_META_INTERFACE(AudioRecord, "android.media.IAudioRecord");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnAudioRecord::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index abc202d..75b861b 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -18,7 +18,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <media/IAudioTrack.h>
@@ -91,12 +91,6 @@ IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnAudioTrack::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index 85b5944..397a55b 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -17,7 +17,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <SkBitmap.h>
#include <media/IMediaMetadataRetriever.h>
@@ -126,16 +126,10 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.hardware.IMediaMetadataRetriever");
+IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnMediaMetadataRetriever::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -215,4 +209,3 @@ status_t BnMediaMetadataRetriever::onTransact(
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index f18765a..5d9db10 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -18,7 +18,7 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <media/IMediaPlayer.h>
#include <ui/ISurface.h>
@@ -39,7 +39,10 @@ enum {
RESET,
SET_AUDIO_STREAM_TYPE,
SET_LOOPING,
- SET_VOLUME
+ SET_VOLUME,
+ INVOKE,
+ SET_METADATA_FILTER,
+ GET_METADATA,
};
class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -170,18 +173,38 @@ public:
remote()->transact(SET_VOLUME, data, &reply);
return reply.readInt32();
}
+
+ status_t invoke(const Parcel& request, Parcel *reply)
+ { // Avoid doing any extra copy. The interface descriptor should
+ // have been set by MediaPlayer.java.
+ return remote()->transact(INVOKE, request, reply);
+ }
+
+ status_t setMetadataFilter(const Parcel& request)
+ {
+ Parcel reply;
+ // Avoid doing any extra copy of the request. The interface
+ // descriptor should have been set by MediaPlayer.java.
+ remote()->transact(SET_METADATA_FILTER, request, &reply);
+ return reply.readInt32();
+ }
+
+ status_t getMetadata(bool update_only, bool apply_filter, Parcel *reply)
+ {
+ Parcel request;
+ request.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ // TODO: Burning 2 ints for 2 boolean. Should probably use flags in an int here.
+ request.writeInt32(update_only);
+ request.writeInt32(apply_filter);
+ remote()->transact(GET_METADATA, request, reply);
+ return reply->readInt32();
+ }
};
-IMPLEMENT_META_INTERFACE(MediaPlayer, "android.hardware.IMediaPlayer");
+IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnMediaPlayer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -266,6 +289,24 @@ status_t BnMediaPlayer::onTransact(
reply->writeInt32(setVolume(data.readFloat(), data.readFloat()));
return NO_ERROR;
} break;
+ case INVOKE: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ invoke(data, reply);
+ return NO_ERROR;
+ } break;
+ case SET_METADATA_FILTER: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ reply->writeInt32(setMetadataFilter(data));
+ return NO_ERROR;
+ } break;
+ case GET_METADATA: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+ const status_t retcode = getMetadata(data.readInt32(), data.readInt32(), reply);
+ reply->setDataPosition(0);
+ reply->writeInt32(retcode);
+ reply->setDataPosition(0);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
@@ -274,4 +315,3 @@ status_t BnMediaPlayer::onTransact(
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/media/libmedia/IMediaPlayerClient.cpp b/media/libmedia/IMediaPlayerClient.cpp
index 65022cd..bf51829 100644
--- a/media/libmedia/IMediaPlayerClient.cpp
+++ b/media/libmedia/IMediaPlayerClient.cpp
@@ -16,8 +16,8 @@
*/
#include <utils/RefBase.h>
-#include <utils/IInterface.h>
-#include <utils/Parcel.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
#include <media/IMediaPlayerClient.h>
@@ -46,16 +46,10 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.hardware.IMediaPlayerClient");
+IMPLEMENT_META_INTERFACE(MediaPlayerClient, "android.media.IMediaPlayerClient");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnMediaPlayerClient::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -74,4 +68,3 @@ status_t BnMediaPlayerClient::onTransact(
}
}; // namespace android
-
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 01cdb6c..8d2c360 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -17,11 +17,14 @@
#include <stdint.h>
#include <sys/types.h>
-#include <utils/Parcel.h>
-#include <utils/IMemory.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+
+#include <utils/Errors.h> // for status_t
namespace android {
@@ -32,6 +35,7 @@ enum {
DECODE_FD,
CREATE_MEDIA_RECORDER,
CREATE_METADATA_RETRIEVER,
+ CREATE_OMX,
};
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -109,18 +113,19 @@ public:
*pFormat = reply.readInt32();
return interface_cast<IMemory>(reply.readStrongBinder());
}
+
+ virtual sp<IOMX> createOMX() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+ remote()->transact(CREATE_OMX, data, &reply);
+ return interface_cast<IOMX>(reply.readStrongBinder());
+ }
};
-IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.hardware.IMediaPlayerService");
+IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -187,6 +192,12 @@ status_t BnMediaPlayerService::onTransact(
reply->writeStrongBinder(retriever->asBinder());
return NO_ERROR;
} break;
+ case CREATE_OMX: {
+ CHECK_INTERFACE(IMediaPlayerService, data, reply);
+ sp<IOMX> omx = createOMX();
+ reply->writeStrongBinder(omx->asBinder());
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 84d08c4..df7d301 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -18,7 +18,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "IMediaRecorder"
#include <utils/Log.h>
-#include <utils/Parcel.h>
+#include <binder/Parcel.h>
#include <ui/ISurface.h>
#include <ui/ICamera.h>
#include <media/IMediaPlayerClient.h>
@@ -264,16 +264,10 @@ public:
}
};
-IMPLEMENT_META_INTERFACE(MediaRecorder, "android.hardware.IMediaRecorder");
+IMPLEMENT_META_INTERFACE(MediaRecorder, "android.media.IMediaRecorder");
// ----------------------------------------------------------------------
-#define CHECK_INTERFACE(interface, data, reply) \
- do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
- LOGW("Call incorrectly routed to " #interface); \
- return PERMISSION_DENIED; \
- } } while (0)
-
status_t BnMediaRecorder::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
new file mode 100644
index 0000000..10bebd0
--- /dev/null
+++ b/media/libmedia/IOMX.cpp
@@ -0,0 +1,733 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IOMX"
+#include <utils/Log.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/IOMX.h>
+#include <ui/ISurface.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+enum {
+ CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+ LIST_NODES,
+ ALLOCATE_NODE,
+ FREE_NODE,
+ SEND_COMMAND,
+ GET_PARAMETER,
+ SET_PARAMETER,
+ GET_CONFIG,
+ SET_CONFIG,
+ USE_BUFFER,
+ ALLOC_BUFFER,
+ ALLOC_BUFFER_WITH_BACKUP,
+ FREE_BUFFER,
+ OBSERVE_NODE,
+ FILL_BUFFER,
+ EMPTY_BUFFER,
+ GET_EXTENSION_INDEX,
+ CREATE_RENDERER,
+ OBSERVER_ON_MSG,
+ RENDERER_RENDER,
+};
+
+sp<IOMXRenderer> IOMX::createRenderer(
+ const sp<Surface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight) {
+ return createRenderer(
+ surface->getISurface(),
+ componentName, colorFormat, encodedWidth, encodedHeight,
+ displayWidth, displayHeight);
+}
+
+class BpOMX : public BpInterface<IOMX> {
+public:
+ BpOMX(const sp<IBinder> &impl)
+ : BpInterface<IOMX>(impl) {
+ }
+
+ virtual status_t list_nodes(List<String8> *list) {
+ list->clear();
+
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ remote()->transact(LIST_NODES, data, &reply);
+
+ int32_t n = reply.readInt32();
+ for (int32_t i = 0; i < n; ++i) {
+ String8 s = reply.readString8();
+
+ list->push_back(s);
+ }
+
+ return OK;
+ }
+
+ virtual status_t allocate_node(const char *name, node_id *node) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeCString(name);
+ remote()->transact(ALLOCATE_NODE, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *node = (void*)reply.readIntPtr();
+ } else {
+ *node = 0;
+ }
+
+ return err;
+ }
+
+ virtual status_t free_node(node_id node) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ remote()->transact(FREE_NODE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(cmd);
+ data.writeInt32(param);
+ remote()->transact(SEND_COMMAND, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(GET_PARAMETER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ reply.read(params, size);
+
+ return OK;
+ }
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(SET_PARAMETER, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(GET_CONFIG, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ return err;
+ }
+
+ reply.read(params, size);
+
+ return OK;
+ }
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(index);
+ data.writeInt32(size);
+ data.write(params, size);
+ remote()->transact(SET_CONFIG, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ data.writeStrongBinder(params->asBinder());
+ remote()->transact(USE_BUFFER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = (void*)reply.readIntPtr();
+
+ return err;
+ }
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ data.writeInt32(size);
+ remote()->transact(ALLOC_BUFFER, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = (void*)reply.readIntPtr();
+
+ return err;
+ }
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ data.writeStrongBinder(params->asBinder());
+ remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err != OK) {
+ *buffer = 0;
+
+ return err;
+ }
+
+ *buffer = (void*)reply.readIntPtr();
+
+ return err;
+ }
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeInt32(port_index);
+ data.writeIntPtr((intptr_t)buffer);
+ remote()->transact(FREE_BUFFER, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeStrongBinder(observer->asBinder());
+ remote()->transact(OBSERVE_NODE, data, &reply);
+
+ return reply.readInt32();
+ }
+
+ virtual void fill_buffer(node_id node, buffer_id buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeIntPtr((intptr_t)buffer);
+ remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeIntPtr((intptr_t)buffer);
+ data.writeInt32(range_offset);
+ data.writeInt32(range_length);
+ data.writeInt32(flags);
+ data.writeInt64(timestamp);
+ remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)node);
+ data.writeCString(parameter_name);
+
+ remote()->transact(GET_EXTENSION_INDEX, data, &reply);
+
+ status_t err = reply.readInt32();
+ if (err == OK) {
+ *index = static_cast<OMX_INDEXTYPE>(reply.readInt32());
+ } else {
+ *index = OMX_IndexComponentStartUnused;
+ }
+
+ return err;
+ }
+
+ virtual sp<IOMXRenderer> createRenderer(
+ const sp<ISurface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+
+ data.writeStrongBinder(surface->asBinder());
+ data.writeCString(componentName);
+ data.writeInt32(colorFormat);
+ data.writeInt32(encodedWidth);
+ data.writeInt32(encodedHeight);
+ data.writeInt32(displayWidth);
+ data.writeInt32(displayHeight);
+
+ remote()->transact(CREATE_RENDERER, data, &reply);
+
+ return interface_cast<IOMXRenderer>(reply.readStrongBinder());
+ }
+};
+
+IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_INTERFACE(interface, data, reply) \
+ do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
+ LOGW("Call incorrectly routed to " #interface); \
+ return PERMISSION_DENIED; \
+ } } while (0)
+
+status_t BnOMX::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+ case LIST_NODES:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ List<String8> list;
+ list_nodes(&list);
+
+ reply->writeInt32(list.size());
+ for (List<String8>::iterator it = list.begin();
+ it != list.end(); ++it) {
+ reply->writeString8(*it);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOCATE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node;
+ status_t err = allocate_node(data.readCString(), &node);
+ reply->writeInt32(err);
+ if (err == OK) {
+ reply->writeIntPtr((intptr_t)node);
+ }
+
+ return NO_ERROR;
+ }
+
+ case FREE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+
+ reply->writeInt32(free_node(node));
+
+ return NO_ERROR;
+ }
+
+ case SEND_COMMAND:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+
+ OMX_COMMANDTYPE cmd =
+ static_cast<OMX_COMMANDTYPE>(data.readInt32());
+
+ OMX_S32 param = data.readInt32();
+ reply->writeInt32(send_command(node, cmd, param));
+
+ return NO_ERROR;
+ }
+
+ case GET_PARAMETER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+
+ // XXX I am not happy with this but Parcel::readInplace didn't work.
+ void *params = malloc(size);
+ data.read(params, size);
+
+ status_t err = get_parameter(node, index, params, size);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->write(params, size);
+ }
+
+ free(params);
+ params = NULL;
+
+ return NO_ERROR;
+ }
+
+ case SET_PARAMETER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+ void *params = const_cast<void *>(data.readInplace(size));
+
+ reply->writeInt32(set_parameter(node, index, params, size));
+
+ return NO_ERROR;
+ }
+
+ case GET_CONFIG:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+
+ // XXX I am not happy with this but Parcel::readInplace didn't work.
+ void *params = malloc(size);
+ data.read(params, size);
+
+ status_t err = get_config(node, index, params, size);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->write(params, size);
+ }
+
+ free(params);
+ params = NULL;
+
+ return NO_ERROR;
+ }
+
+ case SET_CONFIG:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+ size_t size = data.readInt32();
+ void *params = const_cast<void *>(data.readInplace(size));
+
+ reply->writeInt32(set_config(node, index, params, size));
+
+ return NO_ERROR;
+ }
+
+ case USE_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+ sp<IMemory> params =
+ interface_cast<IMemory>(data.readStrongBinder());
+
+ buffer_id buffer;
+ status_t err = use_buffer(node, port_index, params, &buffer);
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeIntPtr((intptr_t)buffer);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOC_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+ size_t size = data.readInt32();
+
+ buffer_id buffer;
+ status_t err = allocate_buffer(node, port_index, size, &buffer);
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeIntPtr((intptr_t)buffer);
+ }
+
+ return NO_ERROR;
+ }
+
+ case ALLOC_BUFFER_WITH_BACKUP:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+ sp<IMemory> params =
+ interface_cast<IMemory>(data.readStrongBinder());
+
+ buffer_id buffer;
+ status_t err = allocate_buffer_with_backup(
+ node, port_index, params, &buffer);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeIntPtr((intptr_t)buffer);
+ }
+
+ return NO_ERROR;
+ }
+
+ case FREE_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ OMX_U32 port_index = data.readInt32();
+ buffer_id buffer = (void*)data.readIntPtr();
+ reply->writeInt32(free_buffer(node, port_index, buffer));
+
+ return NO_ERROR;
+ }
+
+ case OBSERVE_NODE:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ sp<IOMXObserver> observer =
+ interface_cast<IOMXObserver>(data.readStrongBinder());
+ reply->writeInt32(observe_node(node, observer));
+
+ return NO_ERROR;
+ }
+
+ case FILL_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ buffer_id buffer = (void*)data.readIntPtr();
+ fill_buffer(node, buffer);
+
+ return NO_ERROR;
+ }
+
+ case EMPTY_BUFFER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ buffer_id buffer = (void*)data.readIntPtr();
+ OMX_U32 range_offset = data.readInt32();
+ OMX_U32 range_length = data.readInt32();
+ OMX_U32 flags = data.readInt32();
+ OMX_TICKS timestamp = data.readInt64();
+
+ empty_buffer(
+ node, buffer, range_offset, range_length,
+ flags, timestamp);
+
+ return NO_ERROR;
+ }
+
+ case GET_EXTENSION_INDEX:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ node_id node = (void*)data.readIntPtr();
+ const char *parameter_name = data.readCString();
+
+ OMX_INDEXTYPE index;
+ status_t err = get_extension_index(node, parameter_name, &index);
+
+ reply->writeInt32(err);
+
+ if (err == OK) {
+ reply->writeInt32(index);
+ }
+
+ return OK;
+ }
+
+ case CREATE_RENDERER:
+ {
+ CHECK_INTERFACE(IOMX, data, reply);
+
+ sp<ISurface> isurface =
+ interface_cast<ISurface>(data.readStrongBinder());
+
+ const char *componentName = data.readCString();
+
+ OMX_COLOR_FORMATTYPE colorFormat =
+ static_cast<OMX_COLOR_FORMATTYPE>(data.readInt32());
+
+ size_t encodedWidth = (size_t)data.readInt32();
+ size_t encodedHeight = (size_t)data.readInt32();
+ size_t displayWidth = (size_t)data.readInt32();
+ size_t displayHeight = (size_t)data.readInt32();
+
+ sp<IOMXRenderer> renderer =
+ createRenderer(isurface, componentName, colorFormat,
+ encodedWidth, encodedHeight,
+ displayWidth, displayHeight);
+
+ reply->writeStrongBinder(renderer->asBinder());
+
+ return OK;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BpOMXObserver : public BpInterface<IOMXObserver> {
+public:
+ BpOMXObserver(const sp<IBinder> &impl)
+ : BpInterface<IOMXObserver>(impl) {
+ }
+
+ virtual void on_message(const omx_message &msg) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
+ data.write(&msg, sizeof(msg));
+
+ remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(OMXObserver, "android.hardware.IOMXObserver");
+
+status_t BnOMXObserver::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+ case OBSERVER_ON_MSG:
+ {
+ CHECK_INTERFACE(IOMXObserver, data, reply);
+
+ omx_message msg;
+ data.read(&msg, sizeof(msg));
+
+ // XXX Could use readInplace maybe?
+ on_message(msg);
+
+ return NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BpOMXRenderer : public BpInterface<IOMXRenderer> {
+public:
+ BpOMXRenderer(const sp<IBinder> &impl)
+ : BpInterface<IOMXRenderer>(impl) {
+ }
+
+ virtual void render(IOMX::buffer_id buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IOMXRenderer::getInterfaceDescriptor());
+ data.writeIntPtr((intptr_t)buffer);
+
+ // NOTE: Do NOT make this a ONE_WAY call, it must be synchronous
+ // so that the caller knows when to recycle the buffer.
+ remote()->transact(RENDERER_RENDER, data, &reply);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(OMXRenderer, "android.hardware.IOMXRenderer");
+
+status_t BnOMXRenderer::onTransact(
+ uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+ switch (code) {
+ case RENDERER_RENDER:
+ {
+ CHECK_INTERFACE(IOMXRenderer, data, reply);
+
+ IOMX::buffer_id buffer = (void*)data.readIntPtr();
+
+ render(buffer);
+
+ return NO_ERROR;
+ }
+
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+} // namespace android
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 586aacb..ee9e1d8 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -99,7 +99,7 @@ int JetPlayer::init()
mAudioTrack->set(AudioSystem::MUSIC, //TODO parametrize this
pLibConfig->sampleRate,
1, // format = PCM 16bits per sample,
- pLibConfig->numChannels,
+ (pLibConfig->numChannels == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
mTrackBufferSize,
0);
diff --git a/media/libmedia/Metadata.cpp b/media/libmedia/Metadata.cpp
new file mode 100644
index 0000000..35ec6b3
--- /dev/null
+++ b/media/libmedia/Metadata.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "Metadata"
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include <media/Metadata.h>
+#include <binder/Parcel.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+// This file contains code to serialize Metadata triples (key, type,
+// value) into a parcel. The Parcel is destinated to be decoded by the
+// Metadata.java class.
+
+namespace {
+// All these constants below must be kept in sync with Metadata.java.
+enum MetadataId {
+ FIRST_SYSTEM_ID = 1,
+ LAST_SYSTEM_ID = 31,
+ FIRST_CUSTOM_ID = 8192
+};
+
+// Types
+enum Types {
+ STRING_VAL = 1,
+ INTEGER_VAL,
+ BOOLEAN_VAL,
+ LONG_VAL,
+ DOUBLE_VAL,
+ TIMED_TEXT_VAL,
+ DATE_VAL,
+ BYTE_ARRAY_VAL,
+};
+
+const size_t kRecordHeaderSize = 3 * sizeof(int32_t);
+const int32_t kMetaMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+
+} // anonymous namespace
+
+namespace android {
+namespace media {
+
+Metadata::Metadata(Parcel *p)
+ :mData(p),
+ mBegin(p->dataPosition()) { }
+
+Metadata::~Metadata() { }
+
+void Metadata::resetParcel()
+{
+ mData->setDataPosition(mBegin);
+}
+
+// Update the 4 bytes int at the beginning of the parcel which holds
+// the number of bytes written so far.
+void Metadata::updateLength()
+{
+ const size_t end = mData->dataPosition();
+
+ mData->setDataPosition(mBegin);
+ mData->writeInt32(end - mBegin);
+ mData->setDataPosition(end);
+}
+
+// Write the header. The java layer will look for the marker.
+bool Metadata::appendHeader()
+{
+ bool ok = true;
+
+ // Placeholder for the length of the metadata
+ ok = ok && mData->writeInt32(-1) == OK;
+ ok = ok && mData->writeInt32(kMetaMarker) == OK;
+ return ok;
+}
+
+bool Metadata::appendBool(int key, bool val)
+{
+ if (!checkKey(key)) {
+ return false;
+ }
+
+ const size_t begin = mData->dataPosition();
+ bool ok = true;
+
+ // 4 int32s: size, key, type, value.
+ ok = ok && mData->writeInt32(4 * sizeof(int32_t)) == OK;
+ ok = ok && mData->writeInt32(key) == OK;
+ ok = ok && mData->writeInt32(BOOLEAN_VAL) == OK;
+ ok = ok && mData->writeInt32(val ? 1 : 0) == OK;
+ if (!ok) {
+ mData->setDataPosition(begin);
+ }
+ return ok;
+}
+
+bool Metadata::appendInt32(int key, int32_t val)
+{
+ if (!checkKey(key)) {
+ return false;
+ }
+
+ const size_t begin = mData->dataPosition();
+ bool ok = true;
+
+ // 4 int32s: size, key, type, value.
+ ok = ok && mData->writeInt32(4 * sizeof(int32_t)) == OK;
+ ok = ok && mData->writeInt32(key) == OK;
+ ok = ok && mData->writeInt32(INTEGER_VAL) == OK;
+ ok = ok && mData->writeInt32(val) == OK;
+ if (!ok) {
+ mData->setDataPosition(begin);
+ }
+ return ok;
+}
+
+// Check the key (i.e metadata id) is valid if it is a system one.
+// Loop over all the exiting ones in the Parcel to check for duplicate
+// (not allowed).
+bool Metadata::checkKey(int key)
+{
+ if (key < FIRST_SYSTEM_ID ||
+ (LAST_SYSTEM_ID < key && key < FIRST_CUSTOM_ID)) {
+ LOGE("Bad key %d", key);
+ return false;
+ }
+ size_t curr = mData->dataPosition();
+ // Loop over the keys to check if it has been used already.
+ mData->setDataPosition(mBegin);
+
+ bool error = false;
+ size_t left = curr - mBegin;
+ while (left > 0) {
+ size_t pos = mData->dataPosition();
+ size_t size = mData->readInt32();
+ if (size < kRecordHeaderSize || size > left) {
+ error = true;
+ break;
+ }
+ if (mData->readInt32() == key) {
+ LOGE("Key exists already %d", key);
+ error = true;
+ break;
+ }
+ mData->setDataPosition(pos + size);
+ left -= size;
+ }
+ mData->setDataPosition(curr);
+ return !error;
+}
+
+} // namespace android::media
+} // namespace android
diff --git a/media/libmedia/ToneGenerator.cpp b/media/libmedia/ToneGenerator.cpp
index 5435da7..4008bfd 100644
--- a/media/libmedia/ToneGenerator.cpp
+++ b/media/libmedia/ToneGenerator.cpp
@@ -791,7 +791,6 @@ const unsigned char ToneGenerator::sToneMappingTable[NUM_REGIONS-1][NUM_SUP_TONE
// generators, instantiates output audio track.
//
// Input:
-// toneType: Type of tone generated (values in enum tone_type)
// streamType: Type of stream used for tone playback (enum AudioTrack::stream_type)
// volume: volume applied to tone (0.0 to 1.0)
//
@@ -869,13 +868,16 @@ ToneGenerator::~ToneGenerator() {
// Description: Starts tone playback.
//
// Input:
-// none
+// toneType: Type of tone generated (values in enum tone_type)
+// durationMs: The tone duration in milliseconds. If the tone is limited in time by definition,
+// the actual duration will be the minimum of durationMs and the defined tone duration.
+// Ommiting or setting durationMs to -1 does not limit tone duration.
//
// Output:
// none
//
////////////////////////////////////////////////////////////////////////////////
-bool ToneGenerator::startTone(int toneType) {
+bool ToneGenerator::startTone(int toneType, int durationMs) {
bool lResult = false;
if ((toneType < 0) || (toneType >= NUM_TONES))
@@ -896,6 +898,17 @@ bool ToneGenerator::startTone(int toneType) {
toneType = getToneForRegion(toneType);
mpNewToneDesc = &sToneDescriptors[toneType];
+ if (durationMs == -1) {
+ mMaxSmp = TONEGEN_INF;
+ } else {
+ if (durationMs > (int)(TONEGEN_INF / mSamplingRate)) {
+ mMaxSmp = (durationMs / 1000) * mSamplingRate;
+ } else {
+ mMaxSmp = (durationMs * mSamplingRate) / 1000;
+ }
+ LOGV("startTone, duration limited to %d ms", durationMs);
+ }
+
if (mState == TONE_INIT) {
if (prepareWave()) {
LOGV("Immediate start, time %d\n", (unsigned int)(systemTime()/1000000));
@@ -914,7 +927,7 @@ bool ToneGenerator::startTone(int toneType) {
}
}
} else {
- mState == TONE_IDLE;
+ mState = TONE_IDLE;
}
} else {
LOGV("Delayed start\n");
@@ -1001,7 +1014,7 @@ bool ToneGenerator::initAudioTrack() {
// Open audio track in mono, PCM 16bit, default sampling rate, default buffer size
mpAudioTrack
- = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, 1, 0, 0, audioCallback, this, 0);
+ = new AudioTrack(mStreamType, 0, AudioSystem::PCM_16_BIT, AudioSystem::CHANNEL_OUT_MONO, 0, 0, audioCallback, this, 0);
if (mpAudioTrack == 0) {
LOGE("AudioTrack allocation failed");
@@ -1102,11 +1115,17 @@ void ToneGenerator::audioCallback(int event, void* user, void *info) {
// Exit if tone sequence is over
- if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) {
+ if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0 ||
+ lpToneGen->mTotalSmp > lpToneGen->mMaxSmp) {
if (lpToneGen->mState == TONE_PLAYING) {
lpToneGen->mState = TONE_STOPPING;
}
- goto audioCallback_EndLoop;
+ if (lpToneDesc->segments[lpToneGen->mCurSegment].duration == 0) {
+ goto audioCallback_EndLoop;
+ }
+ // fade out before stopping if maximum duraiton reached
+ lWaveCmd = WaveGenerator::WAVEGEN_STOP;
+ lpToneGen->mNextSegSmp = TONEGEN_INF; // forced to skip state machine management below
}
if (lpToneGen->mTotalSmp > lpToneGen->mNextSegSmp) {
diff --git a/media/libmedia/mediametadataretriever.cpp b/media/libmedia/mediametadataretriever.cpp
index 09afc6c..d34a8ed 100644
--- a/media/libmedia/mediametadataretriever.cpp
+++ b/media/libmedia/mediametadataretriever.cpp
@@ -18,8 +18,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaMetadataRetriever"
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
#include <media/mediametadataretriever.h>
#include <media/IMediaPlayerService.h>
#include <utils/Log.h>
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 24e3e6f..aeb43c5 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -24,13 +24,13 @@
#include <unistd.h>
#include <fcntl.h>
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
#include <media/mediaplayer.h>
#include <media/AudioTrack.h>
-#include <utils/MemoryBase.h>
+#include <binder/MemoryBase.h>
namespace android {
@@ -196,12 +196,47 @@ status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
return err;
}
+status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
+{
+ Mutex::Autolock _l(mLock);
+ if ((mPlayer != NULL) && ( mCurrentState & MEDIA_PLAYER_INITIALIZED ))
+ {
+ LOGV("invoke %d", request.dataSize());
+ return mPlayer->invoke(request, reply);
+ }
+ LOGE("invoke failed: wrong state %X", mCurrentState);
+ return INVALID_OPERATION;
+}
+
+status_t MediaPlayer::setMetadataFilter(const Parcel& filter)
+{
+ LOGD("setMetadataFilter");
+ Mutex::Autolock lock(mLock);
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+ return mPlayer->setMetadataFilter(filter);
+}
+
+status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
+{
+ LOGD("getMetadata");
+ Mutex::Autolock lock(mLock);
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+ return mPlayer->getMetadata(update_only, apply_filter, metadata);
+}
+
status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
{
LOGV("setVideoSurface");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return NO_INIT;
- return mPlayer->setVideoSurface(surface->getISurface());
+ if (surface != NULL)
+ return mPlayer->setVideoSurface(surface->getISurface());
+ else
+ return mPlayer->setVideoSurface(NULL);
}
// must call with lock held
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 5093f0e..6b63931 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -20,7 +20,7 @@
#include <utils/Log.h>
#include <ui/Surface.h>
#include <media/mediarecorder.h>
-#include <utils/IServiceManager.h>
+#include <binder/IServiceManager.h>
#include <utils/String8.h>
#include <media/IMediaPlayerService.h>
#include <media/IMediaRecorder.h>
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index f7f2490..59ecde6 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -7,28 +7,41 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MediaRecorderClient.cpp \
- MediaPlayerService.cpp \
+ MediaRecorderClient.cpp \
+ MediaPlayerService.cpp \
MetadataRetrieverClient.cpp \
- VorbisPlayer.cpp \
+ StagefrightPlayer.cpp \
+ TestPlayerStub.cpp \
+ VorbisPlayer.cpp \
+ VorbisMetadataRetriever.cpp \
+ MidiMetadataRetriever.cpp \
MidiFile.cpp
ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
LOCAL_LDLIBS += -ldl -lpthread
endif
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- libvorbisidec \
- libsonivox \
- libopencore_player \
- libopencore_author \
- libmedia \
- libandroid_runtime
-
-LOCAL_C_INCLUDES := external/tremor/Tremor \
- $(call include-path-for, graphics corecg)
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libutils \
+ libbinder \
+ libvorbisidec \
+ libsonivox \
+ libopencore_player \
+ libopencore_author \
+ libmedia \
+ libandroid_runtime \
+ libstagefright \
+ libstagefright_omx
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SHARED_LIBRARIES += libdl
+endif
+
+LOCAL_C_INCLUDES := external/tremor/Tremor \
+ $(call include-path-for, graphics corecg) \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/frameworks/base/media/libstagefright/omx
LOCAL_MODULE:= libmediaplayerservice
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 31eecac..8998f10 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -27,18 +27,27 @@
#include <unistd.h>
#include <string.h>
+
#include <cutils/atomic.h>
+#include <cutils/properties.h> // for property_get
+
+#include <utils/misc.h>
#include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
+#include <utils/Errors.h> // for status_t
+#include <utils/String8.h>
+#include <utils/Vector.h>
#include <cutils/properties.h>
#include <media/MediaPlayerInterface.h>
#include <media/mediarecorder.h>
#include <media/MediaMetadataRetrieverInterface.h>
+#include <media/Metadata.h>
#include <media/AudioTrack.h>
#include "MediaRecorderClient.h"
@@ -48,6 +57,10 @@
#include "MidiFile.h"
#include "VorbisPlayer.h"
#include <media/PVPlayer.h>
+#include "TestPlayerStub.h"
+#include "StagefrightPlayer.h"
+
+#include <OMX.h>
/* desktop Linux needs a little help with gettid() */
#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -61,6 +74,111 @@ pid_t gettid() { return syscall(__NR_gettid);}
#undef __KERNEL__
#endif
+namespace {
+using android::media::Metadata;
+using android::status_t;
+using android::OK;
+using android::BAD_VALUE;
+using android::NOT_ENOUGH_DATA;
+using android::Parcel;
+
+// Max number of entries in the filter.
+const int kMaxFilterSize = 64; // I pulled that out of thin air.
+
+// FIXME: Move all the metadata related function in the Metadata.cpp
+
+
+// Unmarshall a filter from a Parcel.
+// Filter format in a parcel:
+//
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | number of entries (n) |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type 2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// ....
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | metadata type n |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// @param p Parcel that should start with a filter.
+// @param[out] filter On exit contains the list of metadata type to be
+// filtered.
+// @param[out] status On exit contains the status code to be returned.
+// @return true if the parcel starts with a valid filter.
+bool unmarshallFilter(const Parcel& p,
+ Metadata::Filter *filter,
+ status_t *status)
+{
+ int32_t val;
+ if (p.readInt32(&val) != OK)
+ {
+ LOGE("Failed to read filter's length");
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ if( val > kMaxFilterSize || val < 0)
+ {
+ LOGE("Invalid filter len %d", val);
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ const size_t num = val;
+
+ filter->clear();
+ filter->setCapacity(num);
+
+ size_t size = num * sizeof(Metadata::Type);
+
+
+ if (p.dataAvail() < size)
+ {
+ LOGE("Filter too short expected %d but got %d", size, p.dataAvail());
+ *status = NOT_ENOUGH_DATA;
+ return false;
+ }
+
+ const Metadata::Type *data =
+ static_cast<const Metadata::Type*>(p.readInplace(size));
+
+ if (NULL == data)
+ {
+ LOGE("Filter had no data");
+ *status = BAD_VALUE;
+ return false;
+ }
+
+ // TODO: The stl impl of vector would be more efficient here
+ // because it degenerates into a memcpy on pod types. Try to
+ // replace later or use stl::set.
+ for (size_t i = 0; i < num; ++i)
+ {
+ filter->add(*data);
+ ++data;
+ }
+ *status = OK;
+ return true;
+}
+
+// @param filter Of metadata type.
+// @param val To be searched.
+// @return true if a match was found.
+bool findMetadata(const Metadata::Filter& filter, const int32_t val)
+{
+ // Deal with empty and ANY right away
+ if (filter.isEmpty()) return false;
+ if (filter[0] == Metadata::kAny) return true;
+
+ return filter.indexOf(val) >= 0;
+}
+
+} // anonymous namespace
+
namespace android {
@@ -105,7 +223,11 @@ MediaPlayerService::~MediaPlayerService()
sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
{
+#ifndef NO_OPENCORE
sp<MediaRecorderClient> recorder = new MediaRecorderClient(pid);
+#else
+ sp<MediaRecorderClient> recorder = NULL;
+#endif
LOGV("Create new media recorder client from pid %d", pid);
return recorder;
}
@@ -151,6 +273,10 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie
return c;
}
+sp<IOMX> MediaPlayerService::createOMX() {
+ return new OMX;
+}
+
status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
{
const size_t SIZE = 256;
@@ -457,6 +583,7 @@ void MediaPlayerService::Client::disconnect()
p = mPlayer;
}
mClient.clear();
+
mPlayer.clear();
// clear the notification to prevent callbacks to dead client
@@ -474,7 +601,17 @@ void MediaPlayerService::Client::disconnect()
IPCThreadState::self()->flushCommands();
}
-static player_type getPlayerType(int fd, int64_t offset, int64_t length)
+static player_type getDefaultPlayerType() {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("media.stagefright.enable-player", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
+ return STAGEFRIGHT_PLAYER;
+ }
+
+ return PV_PLAYER;
+}
+
+player_type getPlayerType(int fd, int64_t offset, int64_t length)
{
char buf[20];
lseek(fd, offset, SEEK_SET);
@@ -504,12 +641,14 @@ static player_type getPlayerType(int fd, int64_t offset, int64_t length)
EAS_Shutdown(easdata);
}
- // Fall through to PV
- return PV_PLAYER;
+ return getDefaultPlayerType();
}
-static player_type getPlayerType(const char* url)
+player_type getPlayerType(const char* url)
{
+ if (TestPlayerStub::canBeUsed(url)) {
+ return TEST_PLAYER;
+ }
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
@@ -523,8 +662,7 @@ static player_type getPlayerType(const char* url)
}
}
- // Fall through to PV
- return PV_PLAYER;
+ return getDefaultPlayerType();
}
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
@@ -532,10 +670,12 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
{
sp<MediaPlayerBase> p;
switch (playerType) {
+#ifndef NO_OPENCORE
case PV_PLAYER:
LOGV(" create PVPlayer");
p = new PVPlayer();
break;
+#endif
case SONIVOX_PLAYER:
LOGV(" create MidiFile");
p = new MidiFile();
@@ -544,6 +684,14 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
LOGV(" create VorbisPlayer");
p = new VorbisPlayer();
break;
+ case STAGEFRIGHT_PLAYER:
+ LOGV(" create StagefrightPlayer");
+ p = new StagefrightPlayer;
+ break;
+ case TEST_PLAYER:
+ LOGV("Create Test Player stub");
+ p = new TestPlayerStub();
+ break;
}
if (p != NULL) {
if (p->initCheck() == NO_ERROR) {
@@ -608,7 +756,11 @@ status_t MediaPlayerService::Client::setDataSource(const char *url)
// now set data source
LOGV(" setDataSource");
mStatus = p->setDataSource(url);
- if (mStatus == NO_ERROR) mPlayer = p;
+ if (mStatus == NO_ERROR) {
+ mPlayer = p;
+ } else {
+ LOGE(" error: %d", mStatus);
+ }
return mStatus;
}
}
@@ -665,6 +817,73 @@ status_t MediaPlayerService::Client::setVideoSurface(const sp<ISurface>& surface
return p->setVideoSurface(surface);
}
+status_t MediaPlayerService::Client::invoke(const Parcel& request,
+ Parcel *reply)
+{
+ sp<MediaPlayerBase> p = getPlayer();
+ if (p == NULL) return UNKNOWN_ERROR;
+ return p->invoke(request, reply);
+}
+
+// This call doesn't need to access the native player.
+status_t MediaPlayerService::Client::setMetadataFilter(const Parcel& filter)
+{
+ status_t status;
+ media::Metadata::Filter allow, drop;
+
+ if (unmarshallFilter(filter, &allow, &status) &&
+ unmarshallFilter(filter, &drop, &status)) {
+ Mutex::Autolock lock(mLock);
+
+ mMetadataAllow = allow;
+ mMetadataDrop = drop;
+ }
+ return status;
+}
+
+status_t MediaPlayerService::Client::getMetadata(
+ bool update_only, bool apply_filter, Parcel *reply)
+{
+ sp<MediaPlayerBase> player = getPlayer();
+ if (player == 0) return UNKNOWN_ERROR;
+
+ status_t status;
+ // Placeholder for the return code, updated by the caller.
+ reply->writeInt32(-1);
+
+ media::Metadata::Filter ids;
+
+ // We don't block notifications while we fetch the data. We clear
+ // mMetadataUpdated first so we don't lose notifications happening
+ // during the rest of this call.
+ {
+ Mutex::Autolock lock(mLock);
+ if (update_only) {
+ ids = mMetadataUpdated;
+ }
+ mMetadataUpdated.clear();
+ }
+
+ media::Metadata metadata(reply);
+
+ metadata.appendHeader();
+ status = player->getMetadata(ids, reply);
+
+ if (status != OK) {
+ metadata.resetParcel();
+ LOGE("getMetadata failed %d", status);
+ return status;
+ }
+
+ // FIXME: Implement filtering on the result. Not critical since
+ // filtering takes place on the update notifications already. This
+ // would be when all the metadata are fetch and a filter is set.
+
+ // Everything is fine, update the metadata length.
+ metadata.updateLength();
+ return OK;
+}
+
status_t MediaPlayerService::Client::prepareAsync()
{
LOGV("[%d] prepareAsync", mConnId);
@@ -784,13 +1003,51 @@ status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolu
return NO_ERROR;
}
+
void MediaPlayerService::Client::notify(void* cookie, int msg, int ext1, int ext2)
{
Client* client = static_cast<Client*>(cookie);
+
+ if (MEDIA_INFO == msg &&
+ MEDIA_INFO_METADATA_UPDATE == ext1) {
+ const media::Metadata::Type metadata_type = ext2;
+
+ if(client->shouldDropMetadata(metadata_type)) {
+ return;
+ }
+
+ // Update the list of metadata that have changed. getMetadata
+ // also access mMetadataUpdated and clears it.
+ client->addNewMetadataUpdate(metadata_type);
+ }
LOGV("[%d] notify (%p, %d, %d, %d)", client->mConnId, cookie, msg, ext1, ext2);
client->mClient->notify(msg, ext1, ext2);
}
+
+bool MediaPlayerService::Client::shouldDropMetadata(media::Metadata::Type code) const
+{
+ Mutex::Autolock lock(mLock);
+
+ if (findMetadata(mMetadataDrop, code)) {
+ return true;
+ }
+
+ if (mMetadataAllow.isEmpty() || findMetadata(mMetadataAllow, code)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+
+void MediaPlayerService::Client::addNewMetadataUpdate(media::Metadata::Type metadata_type) {
+ Mutex::Autolock lock(mLock);
+ if (mMetadataUpdated.indexOf(metadata_type) < 0) {
+ mMetadataUpdated.add(metadata_type);
+ }
+}
+
#if CALLBACK_ANTAGONIZER
const int Antagonizer::interval = 10000; // 10 msecs
@@ -927,7 +1184,8 @@ Exit:
#undef LOG_TAG
#define LOG_TAG "AudioSink"
MediaPlayerService::AudioOutput::AudioOutput()
-{
+ : mCallback(NULL),
+ mCallbackCookie(NULL) {
mTrack = 0;
mStreamType = AudioSystem::MUSIC;
mLeftVolume = 1.0;
@@ -997,8 +1255,13 @@ float MediaPlayerService::AudioOutput::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioOutput::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ mCallback = cb;
+ mCallbackCookie = cookie;
+
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1019,7 +1282,27 @@ status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelC
}
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
- AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount);
+
+ AudioTrack *t;
+ if (mCallback != NULL) {
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ frameCount,
+ 0 /* flags */,
+ CallbackWrapper,
+ this);
+ } else {
+ t = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ (channelCount == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
+ frameCount);
+ }
+
if ((t == 0) || (t->initCheck() != NO_ERROR)) {
LOGE("Unable to create audio track");
delete t;
@@ -1045,6 +1328,8 @@ void MediaPlayerService::AudioOutput::start()
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
+ LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
//LOGV("write(%p, %u)", buffer, size);
if (mTrack) return mTrack->write(buffer, size);
return NO_INIT;
@@ -1085,6 +1370,20 @@ void MediaPlayerService::AudioOutput::setVolume(float left, float right)
}
}
+// static
+void MediaPlayerService::AudioOutput::CallbackWrapper(
+ int event, void *cookie, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioOutput *me = (AudioOutput *)cookie;
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+
+ (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie);
+}
+
#undef LOG_TAG
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const char* name) :
@@ -1105,8 +1404,14 @@ float MediaPlayerService::AudioCache::msecsPerFrame() const
return mMsecsPerFrame;
}
-status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioCache::open(
+ uint32_t sampleRate, int channelCount, int format, int bufferCount,
+ AudioCallback cb, void *cookie)
{
+ if (cb != NULL) {
+ return UNKNOWN_ERROR; // TODO: implement this.
+ }
+
LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
if (mHeap->getHeapID() < 0) return NO_INIT;
mSampleRate = sampleRate;
@@ -1171,4 +1476,4 @@ void MediaPlayerService::AudioCache::notify(void* cookie, int msg, int ext1, int
p->mSignal.signal();
}
-}; // namespace android
+} // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index f138886..a4be414 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -18,17 +18,23 @@
#ifndef ANDROID_MEDIAPLAYERSERVICE_H
#define ANDROID_MEDIAPLAYERSERVICE_H
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <utils/KeyedVector.h>
+#include <utils/Vector.h>
#include <ui/SurfaceComposerClient.h>
#include <media/IMediaPlayerService.h>
#include <media/MediaPlayerInterface.h>
+#include <media/Metadata.h>
namespace android {
class IMediaRecorder;
class IMediaMetadataRetriever;
+class IOMX;
#define CALLBACK_ANTAGONIZER 0
#if CALLBACK_ANTAGONIZER
@@ -69,7 +75,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const;
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4);
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount,
+ int format, int bufferCount,
+ AudioCallback cb, void *cookie);
+
virtual void start();
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop();
@@ -84,8 +95,12 @@ class MediaPlayerService : public BnMediaPlayerService
static int getMinBufferCount();
private:
static void setMinBufferCount();
+ static void CallbackWrapper(
+ int event, void *me, void *info);
AudioTrack* mTrack;
+ AudioCallback mCallback;
+ void * mCallbackCookie;
int mStreamType;
float mLeftVolume;
float mRightVolume;
@@ -113,7 +128,12 @@ class MediaPlayerService : public BnMediaPlayerService
virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
- virtual status_t open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1);
+
+ virtual status_t open(
+ uint32_t sampleRate, int channelCount, int format,
+ int bufferCount = 1,
+ AudioCallback cb = NULL, void *cookie = NULL);
+
virtual void start() {}
virtual ssize_t write(const void* buffer, size_t size);
virtual void stop() {}
@@ -140,7 +160,7 @@ class MediaPlayerService : public BnMediaPlayerService
sp<MemoryHeapBase> mHeap;
float mMsecsPerFrame;
uint16_t mChannelCount;
- uint16_t mFormat;
+ uint16_t mFormat;
ssize_t mFrameCount;
uint32_t mSampleRate;
uint32_t mSize;
@@ -160,11 +180,13 @@ public:
virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
+ virtual sp<IOMX> createOMX();
virtual status_t dump(int fd, const Vector<String16>& args);
void removeClient(wp<Client> client);
+
private:
class Client : public BnMediaPlayer {
@@ -184,6 +206,11 @@ private:
virtual status_t setAudioStreamType(int type);
virtual status_t setLooping(int loop);
virtual status_t setVolume(float leftVolume, float rightVolume);
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+ virtual status_t setMetadataFilter(const Parcel& filter);
+ virtual status_t getMetadata(bool update_only,
+ bool apply_filter,
+ Parcel *reply);
sp<MediaPlayerBase> createPlayer(player_type playerType);
status_t setDataSource(const char *url);
@@ -206,6 +233,18 @@ private:
sp<MediaPlayerBase> getPlayer() const { Mutex::Autolock lock(mLock); return mPlayer; }
+
+
+ // @param type Of the metadata to be tested.
+ // @return true if the metadata should be dropped according to
+ // the filters.
+ bool shouldDropMetadata(media::Metadata::Type type) const;
+
+ // Add a new element to the set of metadata updated. Noop if
+ // the element exists already.
+ // @param type Of the metadata to be recorded.
+ void addNewMetadataUpdate(media::Metadata::Type type);
+
mutable Mutex mLock;
sp<MediaPlayerBase> mPlayer;
sp<MediaPlayerService> mService;
@@ -215,6 +254,17 @@ private:
status_t mStatus;
bool mLoop;
int32_t mConnId;
+
+ // Metadata filters.
+ media::Metadata::Filter mMetadataAllow; // protected by mLock
+ media::Metadata::Filter mMetadataDrop; // protected by mLock
+
+ // Metadata updated. For each MEDIA_INFO_METADATA_UPDATE
+ // notification we try to update mMetadataUpdated which is a
+ // set: no duplicate.
+ // getMetadata clears this set.
+ media::Metadata::Filter mMetadataUpdated; // protected by mLock
+
#if CALLBACK_ANTAGONIZER
Antagonizer* mAntagonizer;
#endif
@@ -235,4 +285,3 @@ private:
}; // namespace android
#endif // ANDROID_MEDIAPLAYERSERVICE_H
-
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 8bc410c..e54f20d 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -25,10 +25,10 @@
#include <string.h>
#include <cutils/atomic.h>
#include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
-#include <utils/MemoryHeapBase.h>
-#include <utils/MemoryBase.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryBase.h>
#include <media/PVMediaRecorder.h>
#include <utils/String16.h>
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index a320bd5..b34421d 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -26,20 +26,23 @@
#include <string.h>
#include <cutils/atomic.h>
-#include <utils/MemoryDealer.h>
+#include <binder/MemoryDealer.h>
#include <android_runtime/ActivityManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
#include <media/MediaMetadataRetrieverInterface.h>
#include <media/MediaPlayerInterface.h>
#include <media/PVMetadataRetriever.h>
#include <private/media/VideoFrame.h>
-
+#include "VorbisMetadataRetriever.h"
+#include "MidiMetadataRetriever.h"
#include "MetadataRetrieverClient.h"
-
namespace android {
+extern player_type getPlayerType(const char* url);
+extern player_type getPlayerType(int fd, int64_t offset, int64_t length);
+
MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid)
{
LOGV("MetadataRetrieverClient constructor pid(%d)", pid);
@@ -49,7 +52,11 @@ MetadataRetrieverClient::MetadataRetrieverClient(pid_t pid)
mThumbnail = NULL;
mAlbumArt = NULL;
+#ifndef NO_OPENCORE
mRetriever = new PVMetadataRetriever();
+#else
+ mRetriever = NULL;
+#endif
if (mRetriever == NULL) {
LOGE("failed to initialize the retriever");
}
@@ -86,6 +93,36 @@ void MetadataRetrieverClient::disconnect()
IPCThreadState::self()->flushCommands();
}
+static sp<MediaMetadataRetrieverBase> createRetriever(player_type playerType)
+{
+ sp<MediaMetadataRetrieverBase> p;
+ switch (playerType) {
+#ifndef NO_OPENCORE
+ case PV_PLAYER:
+ LOGV("create pv metadata retriever");
+ p = new PVMetadataRetriever();
+ break;
+#endif
+ case VORBIS_PLAYER:
+ LOGV("create vorbis metadata retriever");
+ p = new VorbisMetadataRetriever();
+ break;
+ case SONIVOX_PLAYER:
+ LOGV("create midi metadata retriever");
+ p = new MidiMetadataRetriever();
+ break;
+ default:
+ // TODO:
+ // support for STAGEFRIGHT_PLAYER and TEST_PLAYER
+ LOGE("player type %d is not supported", playerType);
+ break;
+ }
+ if (p == NULL) {
+ LOGE("failed to create a retriever object");
+ }
+ return p;
+}
+
status_t MetadataRetrieverClient::setDataSource(const char *url)
{
LOGV("setDataSource(%s)", url);
@@ -93,11 +130,13 @@ status_t MetadataRetrieverClient::setDataSource(const char *url)
if (url == NULL) {
return UNKNOWN_ERROR;
}
- if (mRetriever == NULL) {
- LOGE("retriever is not initialized");
- return NO_INIT;
- }
- return mRetriever->setDataSource(url);
+ player_type playerType = getPlayerType(url);
+ LOGV("player type = %d", playerType);
+ sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+ if (p == NULL) return NO_INIT;
+ status_t ret = p->setDataSource(url);
+ if (ret == NO_ERROR) mRetriever = p;
+ return ret;
}
status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t length)
@@ -114,7 +153,7 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t
int ret = fstat(fd, &sb);
if (ret != 0) {
LOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));
- return UNKNOWN_ERROR;
+ return BAD_VALUE;
}
LOGV("st_dev = %llu", sb.st_dev);
LOGV("st_mode = %u", sb.st_mode);
@@ -125,13 +164,22 @@ status_t MetadataRetrieverClient::setDataSource(int fd, int64_t offset, int64_t
if (offset >= sb.st_size) {
LOGE("offset (%lld) bigger than file size (%llu)", offset, sb.st_size);
::close(fd);
- return UNKNOWN_ERROR;
+ return BAD_VALUE;
}
if (offset + length > sb.st_size) {
length = sb.st_size - offset;
- LOGE("calculated length = %lld", length);
+ LOGV("calculated length = %lld", length);
+ }
+
+ player_type playerType = getPlayerType(fd, offset, length);
+ LOGV("player type = %d", playerType);
+ sp<MediaMetadataRetrieverBase> p = createRetriever(playerType);
+ if (p == NULL) {
+ ::close(fd);
+ return NO_INIT;
}
- status_t status = mRetriever->setDataSource(fd, offset, length);
+ status_t status = p->setDataSource(fd, offset, length);
+ if (status == NO_ERROR) mRetriever = p;
::close(fd);
return status;
}
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.h b/media/libmediaplayerservice/MetadataRetrieverClient.h
index ce29c98..88d50bf 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.h
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.h
@@ -18,9 +18,12 @@
#ifndef ANDROID_MEDIAMETADATARETRIEVERSERVICE_H
#define ANDROID_MEDIAMETADATARETRIEVERSERVICE_H
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <utils/KeyedVector.h>
-#include <utils/IMemory.h>
+#include <binder/IMemory.h>
#include <media/MediaMetadataRetrieverInterface.h>
diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h
index 302f1cf..25d4a1b 100644
--- a/media/libmediaplayerservice/MidiFile.h
+++ b/media/libmediaplayerservice/MidiFile.h
@@ -46,6 +46,9 @@ public:
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return SONIVOX_PLAYER; }
+ virtual status_t invoke(const Parcel& request, Parcel *reply) {
+ return INVALID_OPERATION;
+ }
private:
status_t createOutputTrack();
@@ -74,4 +77,3 @@ private:
}; // namespace android
#endif // ANDROID_MIDIFILE_H
-
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.cpp b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
new file mode 100644
index 0000000..3795b7b
--- /dev/null
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.cpp
@@ -0,0 +1,91 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MidiMetadataRetriever"
+#include <utils/Log.h>
+
+#include "MidiMetadataRetriever.h"
+#include <media/mediametadataretriever.h>
+
+namespace android {
+
+static status_t ERROR_NOT_OPEN = -1;
+static status_t ERROR_OPEN_FAILED = -2;
+static status_t ERROR_EAS_FAILURE = -3;
+static status_t ERROR_ALLOCATE_FAILED = -4;
+
+void MidiMetadataRetriever::clearMetadataValues()
+{
+ LOGV("clearMetadataValues");
+ mMetadataValues[0][0] = '\0';
+}
+
+status_t MidiMetadataRetriever::setDataSource(const char *url)
+{
+ LOGV("setDataSource: %s", url? url: "NULL pointer");
+ Mutex::Autolock lock(mLock);
+ clearMetadataValues();
+ if (mMidiPlayer == 0) {
+ mMidiPlayer = new MidiFile();
+ }
+ return mMidiPlayer->setDataSource(url);
+}
+
+status_t MidiMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
+{
+ LOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length);
+ Mutex::Autolock lock(mLock);
+ clearMetadataValues();
+ if (mMidiPlayer == 0) {
+ mMidiPlayer = new MidiFile();
+ }
+ return mMidiPlayer->setDataSource(fd, offset, length);;
+}
+
+const char* MidiMetadataRetriever::extractMetadata(int keyCode)
+{
+ LOGV("extractMetdata: key(%d)", keyCode);
+ Mutex::Autolock lock(mLock);
+ if (mMidiPlayer == 0 || mMidiPlayer->initCheck() != NO_ERROR) {
+ LOGE("Midi player is not initialized yet");
+ return NULL;
+ }
+ switch (keyCode) {
+ case METADATA_KEY_DURATION:
+ {
+ if (mMetadataValues[0][0] == '\0') {
+ int duration = -1;
+ if (mMidiPlayer->getDuration(&duration) != NO_ERROR) {
+ LOGE("failed to get duration");
+ return NULL;
+ }
+ snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration);
+ }
+
+ LOGV("duration: %s ms", mMetadataValues[0]);
+ return mMetadataValues[0];
+ }
+ default:
+ LOGE("Unsupported key code (%d)", keyCode);
+ return NULL;
+ }
+ return NULL;
+}
+
+};
+
diff --git a/media/libmediaplayerservice/MidiMetadataRetriever.h b/media/libmediaplayerservice/MidiMetadataRetriever.h
new file mode 100644
index 0000000..73ff347
--- /dev/null
+++ b/media/libmediaplayerservice/MidiMetadataRetriever.h
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_MIDIMETADATARETRIEVER_H
+#define ANDROID_MIDIMETADATARETRIEVER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include "MidiFile.h"
+
+namespace android {
+
+class MidiMetadataRetriever : public MediaMetadataRetrieverInterface {
+public:
+ MidiMetadataRetriever() {}
+ ~MidiMetadataRetriever() {}
+
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual const char* extractMetadata(int keyCode);
+
+private:
+ static const uint32_t MAX_METADATA_STRING_LENGTH = 128;
+ void clearMetadataValues();
+
+ Mutex mLock;
+ sp<MidiFile> mMidiPlayer;
+ char mMetadataValues[1][MAX_METADATA_STRING_LENGTH];
+};
+
+}; // namespace android
+
+#endif // ANDROID_MIDIMETADATARETRIEVER_H
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
new file mode 100644
index 0000000..9a06d13
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -0,0 +1,208 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightPlayer"
+#include <utils/Log.h>
+
+#include "StagefrightPlayer.h"
+#include <media/stagefright/MediaPlayerImpl.h>
+
+namespace android {
+
+StagefrightPlayer::StagefrightPlayer()
+ : mPlayer(NULL) {
+ LOGV("StagefrightPlayer");
+}
+
+StagefrightPlayer::~StagefrightPlayer() {
+ LOGV("~StagefrightPlayer");
+ reset();
+ LOGV("~StagefrightPlayer done.");
+}
+
+status_t StagefrightPlayer::initCheck() {
+ LOGV("initCheck");
+ return OK;
+}
+
+status_t StagefrightPlayer::setDataSource(const char *url) {
+ LOGV("setDataSource('%s')", url);
+
+ reset();
+ mPlayer = new MediaPlayerImpl(url);
+
+ status_t err = mPlayer->initCheck();
+ if (err != OK) {
+ delete mPlayer;
+ mPlayer = NULL;
+ } else {
+ mPlayer->setAudioSink(mAudioSink);
+ }
+
+ return err;
+}
+
+status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+ LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+ reset();
+ mPlayer = new MediaPlayerImpl(fd, offset, length);
+
+ status_t err = mPlayer->initCheck();
+ if (err != OK) {
+ delete mPlayer;
+ mPlayer = NULL;
+ } else {
+ mPlayer->setAudioSink(mAudioSink);
+ }
+
+ return err;
+}
+
+status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
+ LOGV("setVideoSurface");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->setISurface(surface);
+
+ return OK;
+}
+
+status_t StagefrightPlayer::prepare() {
+ LOGV("prepare");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ sendEvent(
+ MEDIA_SET_VIDEO_SIZE,
+ mPlayer->getWidth(), mPlayer->getHeight());
+
+ return OK;
+}
+
+status_t StagefrightPlayer::prepareAsync() {
+ LOGV("prepareAsync");
+
+ status_t err = prepare();
+
+ if (err != OK) {
+ return err;
+ }
+
+ sendEvent(MEDIA_PREPARED);
+
+ return OK;
+}
+
+status_t StagefrightPlayer::start() {
+ LOGV("start");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->play();
+
+ return OK;
+}
+
+status_t StagefrightPlayer::stop() {
+ LOGV("stop");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ reset();
+
+ return OK;
+}
+
+status_t StagefrightPlayer::pause() {
+ LOGV("pause");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ mPlayer->pause();
+
+ return OK;
+}
+
+bool StagefrightPlayer::isPlaying() {
+ LOGV("isPlaying");
+ return mPlayer != NULL && mPlayer->isPlaying();
+}
+
+status_t StagefrightPlayer::seekTo(int msec) {
+ LOGV("seekTo");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ status_t err = mPlayer->seekTo((int64_t)msec * 1000);
+
+ sendEvent(MEDIA_SEEK_COMPLETE);
+
+ return err;
+}
+
+status_t StagefrightPlayer::getCurrentPosition(int *msec) {
+ LOGV("getCurrentPosition");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ *msec = mPlayer->getPosition() / 1000;
+ return OK;
+}
+
+status_t StagefrightPlayer::getDuration(int *msec) {
+ LOGV("getDuration");
+
+ if (mPlayer == NULL) {
+ return NO_INIT;
+ }
+
+ *msec = mPlayer->getDuration() / 1000;
+ return OK;
+}
+
+status_t StagefrightPlayer::reset() {
+ LOGV("reset");
+
+ delete mPlayer;
+ mPlayer = NULL;
+
+ return OK;
+}
+
+status_t StagefrightPlayer::setLooping(int loop) {
+ LOGV("setLooping");
+ return UNKNOWN_ERROR;
+}
+
+player_type StagefrightPlayer::playerType() {
+ LOGV("playerType");
+ return STAGEFRIGHT_PLAYER;
+}
+
+status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
+void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
+ MediaPlayerInterface::setAudioSink(audioSink);
+
+ if (mPlayer != NULL) {
+ mPlayer->setAudioSink(audioSink);
+ }
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
new file mode 100644
index 0000000..f214872
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -0,0 +1,60 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_STAGEFRIGHTPLAYER_H
+#define ANDROID_STAGEFRIGHTPLAYER_H
+
+#include <media/MediaPlayerInterface.h>
+
+namespace android {
+
+class MediaPlayerImpl;
+
+class StagefrightPlayer : public MediaPlayerInterface {
+public:
+ StagefrightPlayer();
+ virtual ~StagefrightPlayer();
+
+ virtual status_t initCheck();
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setVideoSurface(const sp<ISurface> &surface);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t invoke(const Parcel &request, Parcel *reply);
+ virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+private:
+ MediaPlayerImpl *mPlayer;
+
+ StagefrightPlayer(const StagefrightPlayer &);
+ StagefrightPlayer &operator=(const StagefrightPlayer &);
+};
+
+} // namespace android
+
+#endif // ANDROID_STAGEFRIGHTPLAYER_H
diff --git a/media/libmediaplayerservice/TestPlayerStub.cpp b/media/libmediaplayerservice/TestPlayerStub.cpp
new file mode 100644
index 0000000..8627708
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TestPlayerStub"
+#include "utils/Log.h"
+
+#include "TestPlayerStub.h"
+
+#include <dlfcn.h> // for dlopen/dlclose
+#include <stdlib.h>
+#include <string.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h> // for status_t
+
+#include "media/MediaPlayerInterface.h"
+
+
+namespace {
+using android::status_t;
+using android::MediaPlayerBase;
+
+const char *kTestUrlScheme = "test:";
+const char *kUrlParam = "url=";
+
+const char *kBuildTypePropName = "ro.build.type";
+const char *kEngBuild = "eng";
+const char *kTestBuild = "test";
+
+// @return true if the current build is 'eng' or 'test'.
+bool isTestBuild()
+{
+ char prop[PROPERTY_VALUE_MAX] = { '\0', };
+
+ property_get(kBuildTypePropName, prop, '\0');
+ return strcmp(prop, kEngBuild) == 0 || strcmp(prop, kTestBuild) == 0;
+}
+
+// @return true if the url scheme is 'test:'
+bool isTestUrl(const char *url)
+{
+ return url && strncmp(url, kTestUrlScheme, strlen(kTestUrlScheme)) == 0;
+}
+
+} // anonymous namespace
+
+namespace android {
+
+TestPlayerStub::TestPlayerStub()
+ :mUrl(NULL), mFilename(NULL), mContentUrl(NULL),
+ mHandle(NULL), mNewPlayer(NULL), mDeletePlayer(NULL),
+ mPlayer(NULL) { }
+
+TestPlayerStub::~TestPlayerStub()
+{
+ resetInternal();
+}
+
+status_t TestPlayerStub::initCheck()
+{
+ return isTestBuild() ? OK : INVALID_OPERATION;
+}
+
+// Parse mUrl to get:
+// * The library to be dlopened.
+// * The url to be passed to the real setDataSource impl.
+//
+// mUrl is expected to be in following format:
+//
+// test:<name of the .so>?url=<url for setDataSource>
+//
+// The value of the url parameter is treated as a string (no
+// unescaping of illegal charaters).
+status_t TestPlayerStub::parseUrl()
+{
+ if (strlen(mUrl) < strlen(kTestUrlScheme)) {
+ resetInternal();
+ return BAD_VALUE;
+ }
+
+ char *i = mUrl + strlen(kTestUrlScheme);
+
+ mFilename = i;
+
+ while (*i != '\0' && *i != '?') {
+ ++i;
+ }
+
+ if (*i == '\0' || strncmp(i + 1, kUrlParam, strlen(kUrlParam)) != 0) {
+ resetInternal();
+ return BAD_VALUE;
+ }
+ *i = '\0'; // replace '?' to nul-terminate mFilename
+
+ mContentUrl = i + 1 + strlen(kUrlParam);
+ return OK;
+}
+
+// Load the dynamic library.
+// Create the test player.
+// Call setDataSource on the test player with the url in param.
+status_t TestPlayerStub::setDataSource(const char *url)
+{
+ if (!isTestUrl(url) || NULL != mHandle) {
+ return INVALID_OPERATION;
+ }
+
+ mUrl = strdup(url);
+
+ status_t status = parseUrl();
+
+ if (OK != status) {
+ resetInternal();
+ return status;
+ }
+
+ ::dlerror(); // Clears any pending error.
+
+ // Load the test player from the url. dlopen will fail if the lib
+ // is not there. dls are under /system/lib
+ // None of the entry points should be NULL.
+ mHandle = ::dlopen(mFilename, RTLD_NOW | RTLD_GLOBAL);
+ if (!mHandle) {
+ LOGE("dlopen failed: %s", ::dlerror());
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ // Load the 2 entry points to create and delete instances.
+ const char *err;
+ mNewPlayer = reinterpret_cast<NEW_PLAYER>(dlsym(mHandle,
+ "newPlayer"));
+ err = ::dlerror();
+ if (err || mNewPlayer == NULL) {
+ // if err is NULL the string <null> is inserted in the logs =>
+ // mNewPlayer was NULL.
+ LOGE("dlsym for newPlayer failed %s", err);
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ mDeletePlayer = reinterpret_cast<DELETE_PLAYER>(dlsym(mHandle,
+ "deletePlayer"));
+ err = ::dlerror();
+ if (err || mDeletePlayer == NULL) {
+ LOGE("dlsym for deletePlayer failed %s", err);
+ resetInternal();
+ return UNKNOWN_ERROR;
+ }
+
+ mPlayer = (*mNewPlayer)();
+ return mPlayer->setDataSource(mContentUrl);
+}
+
+// Internal cleanup.
+status_t TestPlayerStub::resetInternal()
+{
+ if(mUrl) {
+ free(mUrl);
+ mUrl = NULL;
+ }
+ mFilename = NULL;
+ mContentUrl = NULL;
+
+ if (mPlayer) {
+ LOG_ASSERT(mDeletePlayer != NULL);
+ (*mDeletePlayer)(mPlayer);
+ mPlayer = NULL;
+ }
+
+ if (mHandle) {
+ ::dlclose(mHandle);
+ mHandle = NULL;
+ }
+ return OK;
+}
+
+/* static */ bool TestPlayerStub::canBeUsed(const char *url)
+{
+ return isTestBuild() && isTestUrl(url);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/TestPlayerStub.h b/media/libmediaplayerservice/TestPlayerStub.h
new file mode 100644
index 0000000..80d53a8
--- /dev/null
+++ b/media/libmediaplayerservice/TestPlayerStub.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+#define ANDROID_FRAMEWORKS_BASE_MEDIA_LIBMEDIAPLAYERSERVICE_TESTPLAYERSTUB_H__
+
+#include <media/MediaPlayerInterface.h>
+#include <utils/Errors.h>
+
+namespace android {
+class MediaPlayerBase; // in media/MediaPlayerInterface.h
+
+// Wrapper around a test media player that gets dynamically loaded.
+//
+// The URL passed to setDataSource has this format:
+//
+// test:<name of the .so>?url=<url for the real setDataSource impl.>
+//
+// e.g:
+// test:invoke_test_media_player.so?url=http://youtube.com/
+// test:invoke_test_media_player.so?url=speedtest
+//
+// TestPlayerStub::setDataSource loads the library in the test url. 2
+// entry points with C linkage are expected. One to create the test
+// player and one to destroy it.
+//
+// extern "C" android::MediaPlayerBase* newPlayer();
+// extern "C" android::status_t deletePlayer(android::MediaPlayerBase *p);
+//
+// Once the test player has been loaded, its setDataSource
+// implementation is called with the value of the 'url' parameter.
+//
+// typical usage in a java test:
+// ============================
+//
+// MediaPlayer p = new MediaPlayer();
+// p.setDataSource("test:invoke_mock_media_player.so?url=http://youtube.com");
+// p.prepare();
+// ...
+// p.release();
+
+class TestPlayerStub : public MediaPlayerInterface {
+ public:
+ typedef MediaPlayerBase* (*NEW_PLAYER)();
+ typedef status_t (*DELETE_PLAYER)(MediaPlayerBase *);
+
+ TestPlayerStub();
+ virtual ~TestPlayerStub();
+
+ // Called right after the constructor. Check if the current build
+ // allows test players.
+ virtual status_t initCheck();
+
+ // @param url Should be a test url. See class comment.
+ virtual status_t setDataSource(const char* url);
+
+ // Test player for a file descriptor source is not supported.
+ virtual status_t setDataSource(int, int64_t, int64_t) {
+ return INVALID_OPERATION;
+ }
+
+
+ // All the methods below wrap the mPlayer instance.
+ virtual status_t setVideoSurface(const android::sp<android::ISurface>& s) {
+ return mPlayer->setVideoSurface(s);
+ }
+ virtual status_t prepare() {return mPlayer->prepare();}
+ virtual status_t prepareAsync() {return mPlayer->prepareAsync();}
+ virtual status_t start() {return mPlayer->start();}
+ virtual status_t stop() {return mPlayer->stop();}
+ virtual status_t pause() {return mPlayer->pause();}
+ virtual bool isPlaying() {return mPlayer->isPlaying();}
+ virtual status_t seekTo(int msec) {return mPlayer->seekTo(msec);}
+ virtual status_t getCurrentPosition(int *p) {
+ return mPlayer->getCurrentPosition(p);
+ }
+ virtual status_t getDuration(int *d) {return mPlayer->getDuration(d);}
+ virtual status_t reset() {return mPlayer->reset();}
+ virtual status_t setLooping(int b) {return mPlayer->setLooping(b);}
+ virtual player_type playerType() {return mPlayer->playerType();}
+ virtual status_t invoke(const android::Parcel& in, android::Parcel *out) {
+ return mPlayer->invoke(in, out);
+ }
+
+
+ // @return true if the current build is 'eng' or 'test' and the
+ // url's scheme is 'test:'
+ static bool canBeUsed(const char *url);
+
+ private:
+ // Release the player, dlclose the library.
+ status_t resetInternal();
+ status_t parseUrl();
+
+ char *mUrl; // test:foo.so?url=http://bar
+ char *mFilename; // foo.so
+ char *mContentUrl; // http://bar
+ void *mHandle; // returned by dlopen
+ NEW_PLAYER mNewPlayer;
+ DELETE_PLAYER mDeletePlayer;
+ MediaPlayerBase *mPlayer; // wrapped player
+};
+
+} // namespace android
+
+#endif
diff --git a/media/libmediaplayerservice/VorbisMetadataRetriever.cpp b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
new file mode 100644
index 0000000..e981678
--- /dev/null
+++ b/media/libmediaplayerservice/VorbisMetadataRetriever.cpp
@@ -0,0 +1,86 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VorbisMetadataRetriever"
+#include <utils/Log.h>
+
+#include "VorbisMetadataRetriever.h"
+#include <media/mediametadataretriever.h>
+#
+
+namespace android {
+
+void VorbisMetadataRetriever::clearMetadataValues()
+{
+ LOGV("cleearMetadataValues");
+ mMetadataValues[0][0] = '\0';
+}
+
+status_t VorbisMetadataRetriever::setDataSource(const char *url)
+{
+ LOGV("setDataSource: url(%s)", url? url: "NULL pointer");
+ Mutex::Autolock lock(mLock);
+ clearMetadataValues();
+ if (mVorbisPlayer == 0) {
+ mVorbisPlayer = new VorbisPlayer();
+ }
+ return mVorbisPlayer->setDataSource(url);
+}
+
+status_t VorbisMetadataRetriever::setDataSource(int fd, int64_t offset, int64_t length)
+{
+ LOGV("setDataSource: fd(%d), offset(%lld), and length(%lld)", fd, offset, length);
+ Mutex::Autolock lock(mLock);
+ clearMetadataValues();
+ if (mVorbisPlayer == 0) {
+ mVorbisPlayer = new VorbisPlayer();
+ }
+ return mVorbisPlayer->setDataSource(fd, offset, length);
+}
+
+const char* VorbisMetadataRetriever::extractMetadata(int keyCode)
+{
+ LOGV("extractMetadata: key(%d)", keyCode);
+ Mutex::Autolock lock(mLock);
+ if (mVorbisPlayer == 0 || mVorbisPlayer->initCheck() != NO_ERROR) {
+ LOGE("no vorbis player is initialized yet");
+ return NULL;
+ }
+ switch (keyCode) {
+ case METADATA_KEY_DURATION:
+ {
+ if (mMetadataValues[0][0] == '\0') {
+ int duration = -1;
+ if (mVorbisPlayer->getDuration(&duration) != NO_ERROR) {
+ LOGE("failed to get duration");
+ return NULL;
+ }
+ snprintf(mMetadataValues[0], MAX_METADATA_STRING_LENGTH, "%d", duration);
+ }
+ LOGV("duration: %s ms", mMetadataValues[0]);
+ return mMetadataValues[0];
+ }
+ default:
+ LOGE("Unsupported key code (%d)", keyCode);
+ return NULL;
+ }
+ return NULL;
+}
+
+};
+
diff --git a/media/libmediaplayerservice/VorbisMetadataRetriever.h b/media/libmediaplayerservice/VorbisMetadataRetriever.h
new file mode 100644
index 0000000..1c57fe3
--- /dev/null
+++ b/media/libmediaplayerservice/VorbisMetadataRetriever.h
@@ -0,0 +1,49 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_VORBISMETADATARETRIEVER_H
+#define ANDROID_VORBISMETADATARETRIEVER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <media/MediaMetadataRetrieverInterface.h>
+
+#include "VorbisPlayer.h"
+
+namespace android {
+
+class VorbisMetadataRetriever : public MediaMetadataRetrieverInterface {
+public:
+ VorbisMetadataRetriever() {}
+ ~VorbisMetadataRetriever() {}
+
+ virtual status_t setDataSource(const char *url);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual const char* extractMetadata(int keyCode);
+
+private:
+ static const uint32_t MAX_METADATA_STRING_LENGTH = 128;
+ void clearMetadataValues();
+
+ Mutex mLock;
+ sp<VorbisPlayer> mVorbisPlayer;
+ char mMetadataValues[1][MAX_METADATA_STRING_LENGTH];
+};
+
+}; // namespace android
+
+#endif // ANDROID_VORBISMETADATARETRIEVER_H
diff --git a/media/libmediaplayerservice/VorbisPlayer.h b/media/libmediaplayerservice/VorbisPlayer.h
index c30dc1b..4024654 100644
--- a/media/libmediaplayerservice/VorbisPlayer.h
+++ b/media/libmediaplayerservice/VorbisPlayer.h
@@ -53,6 +53,7 @@ public:
virtual status_t reset();
virtual status_t setLooping(int loop);
virtual player_type playerType() { return VORBIS_PLAYER; }
+ virtual status_t invoke(const Parcel& request, Parcel *reply) {return INVALID_OPERATION;}
private:
status_t setdatasource(const char *path, int fd, int64_t offset, int64_t length);
@@ -88,4 +89,3 @@ private:
}; // namespace android
#endif // ANDROID_VORBISPLAYER_H
-
diff --git a/media/libstagefright/AMRExtractor.cpp b/media/libstagefright/AMRExtractor.cpp
new file mode 100644
index 0000000..8d85ce2
--- /dev/null
+++ b/media/libstagefright/AMRExtractor.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMRExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/AMRExtractor.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class AMRSource : public MediaSource {
+public:
+ AMRSource(const sp<DataSource> &source, bool isWide);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~AMRSource();
+
+private:
+ sp<DataSource> mDataSource;
+ bool mIsWide;
+
+ off_t mOffset;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+ MediaBufferGroup *mGroup;
+
+ AMRSource(const AMRSource &);
+ AMRSource &operator=(const AMRSource &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+AMRExtractor::AMRExtractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mInitCheck(NO_INIT) {
+ String8 mimeType;
+ float confidence;
+ if (SniffAMR(mDataSource, &mimeType, &confidence)) {
+ mInitCheck = OK;
+ mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
+ }
+}
+
+AMRExtractor::~AMRExtractor() {
+}
+
+size_t AMRExtractor::countTracks() {
+ return mInitCheck == OK ? 1 : 0;
+}
+
+sp<MediaSource> AMRExtractor::getTrack(size_t index) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return new AMRSource(mDataSource, mIsWide);
+}
+
+sp<MetaData> AMRExtractor::getTrackMetaData(size_t index) {
+ if (mInitCheck != OK || index != 0) {
+ return NULL;
+ }
+
+ return makeAMRFormat(mIsWide);
+}
+
+// static
+sp<MetaData> AMRExtractor::makeAMRFormat(bool isWide) {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(
+ kKeyMIMEType, isWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
+ : MEDIA_MIMETYPE_AUDIO_AMR_NB);
+
+ meta->setInt32(kKeyChannelCount, 1);
+ meta->setInt32(kKeySampleRate, isWide ? 16000 : 8000);
+
+ return meta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+AMRSource::AMRSource(const sp<DataSource> &source, bool isWide)
+ : mDataSource(source),
+ mIsWide(isWide),
+ mOffset(mIsWide ? 9 : 6),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL) {
+}
+
+AMRSource::~AMRSource() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t AMRSource::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mOffset = mIsWide ? 9 : 6;
+ mCurrentTimeUs = 0;
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(128));
+ mStarted = true;
+
+ return OK;
+}
+
+status_t AMRSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ return OK;
+}
+
+sp<MetaData> AMRSource::getFormat() {
+ return AMRExtractor::makeAMRFormat(mIsWide);
+}
+
+status_t AMRSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ uint8_t header;
+ ssize_t n = mDataSource->read_at(mOffset, &header, 1);
+
+ if (n < 1) {
+ return ERROR_IO;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ if (header & 0x83) {
+ // Padding bits must be 0.
+
+ return ERROR_MALFORMED;
+ }
+
+ unsigned FT = (header >> 3) & 0x0f;
+
+ if (FT > 8 || (!mIsWide && FT > 7)) {
+ return ERROR_MALFORMED;
+ }
+
+ static const size_t kFrameSizeNB[8] = {
+ 95, 103, 118, 134, 148, 159, 204, 244
+ };
+ static const size_t kFrameSizeWB[9] = {
+ 132, 177, 253, 285, 317, 365, 397, 461, 477
+ };
+
+ size_t frameSize = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+ // Round up bits to bytes and add 1 for the header byte.
+ frameSize = (frameSize + 7) / 8 + 1;
+
+ n = mDataSource->read_at(mOffset, buffer->data(), frameSize);
+
+ if (n != (ssize_t)frameSize) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ buffer->set_range(0, frameSize);
+ buffer->meta_data()->setInt32(
+ kKeyTimeUnits, (mCurrentTimeUs + 500) / 1000);
+ buffer->meta_data()->setInt32(
+ kKeyTimeScale, 1000);
+
+ mOffset += frameSize;
+ mCurrentTimeUs += 20000; // Each frame is 20ms
+
+ *out = buffer;
+
+ return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool SniffAMR(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ char header[9];
+
+ if (source->read_at(0, header, sizeof(header)) != sizeof(header)) {
+ return false;
+ }
+
+ if (!memcmp(header, "#!AMR\n", 6)) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ *confidence = 0.5;
+
+ return true;
+ } else if (!memcmp(header, "#!AMR-WB\n", 9)) {
+ *mimeType = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ *confidence = 0.5;
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
new file mode 100644
index 0000000..a4f2c79
--- /dev/null
+++ b/media/libstagefright/Android.mk
@@ -0,0 +1,56 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ AMRExtractor.cpp \
+ CachingDataSource.cpp \
+ DataSource.cpp \
+ FileSource.cpp \
+ HTTPDataSource.cpp \
+ HTTPStream.cpp \
+ MP3Extractor.cpp \
+ MPEG4Extractor.cpp \
+ MPEG4Writer.cpp \
+ MediaBuffer.cpp \
+ MediaBufferGroup.cpp \
+ MediaDefs.cpp \
+ MediaExtractor.cpp \
+ MediaPlayerImpl.cpp \
+ MediaSource.cpp \
+ MetaData.cpp \
+ MmapSource.cpp \
+ OMXCodec.cpp \
+ SampleTable.cpp \
+ ShoutcastSource.cpp \
+ TimeSource.cpp \
+ TimedEventQueue.cpp \
+ Utils.cpp \
+ AudioPlayer.cpp \
+ ESDS.cpp \
+ OMXClient.cpp \
+ string.cpp
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+ $(TOP)/external/opencore/android
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libcutils \
+ libui
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
new file mode 100644
index 0000000..319488e
--- /dev/null
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AudioPlayer"
+#include <utils/Log.h>
+
+#include <media/AudioTrack.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
+ : mAudioTrack(NULL),
+ mInputBuffer(NULL),
+ mSampleRate(0),
+ mLatencyUs(0),
+ mFrameSize(0),
+ mNumFramesPlayed(0),
+ mPositionTimeMediaUs(-1),
+ mPositionTimeRealUs(-1),
+ mSeeking(false),
+ mStarted(false),
+ mAudioSink(audioSink) {
+}
+
+AudioPlayer::~AudioPlayer() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+void AudioPlayer::setSource(const sp<MediaSource> &source) {
+ CHECK_EQ(mSource, NULL);
+ mSource = source;
+}
+
+void AudioPlayer::start() {
+ CHECK(!mStarted);
+ CHECK(mSource != NULL);
+
+ status_t err = mSource->start();
+ CHECK_EQ(err, OK);
+
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+ CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW));
+
+ success = format->findInt32(kKeySampleRate, &mSampleRate);
+ CHECK(success);
+
+ int32_t numChannels;
+ success = format->findInt32(kKeyChannelCount, &numChannels);
+ CHECK(success);
+
+ if (mAudioSink.get() != NULL) {
+ status_t err = mAudioSink->open(
+ mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &AudioPlayer::AudioSinkCallback, this);
+ CHECK_EQ(err, OK);
+
+ mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+ mFrameSize = mAudioSink->frameSize();
+
+ mAudioSink->start();
+ } else {
+ mAudioTrack = new AudioTrack(
+ AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
+ (numChannels == 2)
+ ? AudioSystem::CHANNEL_OUT_STEREO
+ : AudioSystem::CHANNEL_OUT_MONO,
+ 8192, 0, &AudioCallback, this, 0);
+
+ CHECK_EQ(mAudioTrack->initCheck(), OK);
+
+ mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+ mFrameSize = mAudioTrack->frameSize();
+
+ mAudioTrack->start();
+ }
+
+ mStarted = true;
+}
+
+void AudioPlayer::pause() {
+ CHECK(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->pause();
+ } else {
+ mAudioTrack->stop();
+ }
+}
+
+void AudioPlayer::resume() {
+ CHECK(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->start();
+ } else {
+ mAudioTrack->start();
+ }
+}
+
+void AudioPlayer::stop() {
+ CHECK(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->stop();
+ } else {
+ mAudioTrack->stop();
+
+ delete mAudioTrack;
+ mAudioTrack = NULL;
+ }
+
+ // Make sure to release any buffer we hold onto so that the
+ // source is able to stop().
+ if (mInputBuffer != NULL) {
+ LOGI("AudioPlayer releasing input buffer.");
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSource->stop();
+
+ mNumFramesPlayed = 0;
+ mPositionTimeMediaUs = -1;
+ mPositionTimeRealUs = -1;
+ mSeeking = false;
+ mStarted = false;
+}
+
+// static
+void AudioPlayer::AudioCallback(int event, void *user, void *info) {
+ static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
+}
+
+// static
+void AudioPlayer::AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie) {
+ AudioPlayer *me = (AudioPlayer *)cookie;
+
+ me->fillBuffer(buffer, size);
+}
+
+void AudioPlayer::AudioCallback(int event, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ fillBuffer(buffer->raw, buffer->size);
+}
+
+void AudioPlayer::fillBuffer(void *data, size_t size) {
+ if (mNumFramesPlayed == 0) {
+ LOGI("AudioCallback");
+ }
+
+ size_t size_done = 0;
+ size_t size_remaining = size;
+ while (size_remaining > 0) {
+ MediaSource::ReadOptions options;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+
+ if (mInputBuffer != NULL) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+ mSeeking = false;
+ }
+ }
+
+ if (mInputBuffer == NULL) {
+ status_t err = mSource->read(&mInputBuffer, &options);
+
+ CHECK((err == OK && mInputBuffer != NULL)
+ || (err != OK && mInputBuffer == NULL));
+
+ if (err != OK) {
+ memset((char *)data + size_done, 0, size_remaining);
+ break;
+ }
+
+ int32_t units, scale;
+ bool success =
+ mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ success = success &&
+ mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ CHECK(success);
+
+ Mutex::Autolock autoLock(mLock);
+ mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+
+ mPositionTimeRealUs =
+ ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+ / mSampleRate;
+ }
+
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ continue;
+ }
+
+ size_t copy = size_remaining;
+ if (copy > mInputBuffer->range_length()) {
+ copy = mInputBuffer->range_length();
+ }
+
+ memcpy((char *)data + size_done,
+ (const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
+ copy);
+
+ mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
+ mInputBuffer->range_length() - copy);
+
+ size_done += copy;
+ size_remaining -= copy;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ mNumFramesPlayed += size / mFrameSize;
+}
+
+int64_t AudioPlayer::getRealTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ return getRealTimeUsLocked();
+}
+
+int64_t AudioPlayer::getRealTimeUsLocked() const {
+ return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
+}
+
+int64_t AudioPlayer::getMediaTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+
+ return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
+}
+
+bool AudioPlayer::getMediaTimeMapping(
+ int64_t *realtime_us, int64_t *mediatime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ *realtime_us = mPositionTimeRealUs;
+ *mediatime_us = mPositionTimeMediaUs;
+
+ return mPositionTimeRealUs != -1 || mPositionTimeMediaUs != -1;
+}
+
+status_t AudioPlayer::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+
+ return OK;
+}
+
+}
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
new file mode 100644
index 0000000..fd00576
--- /dev/null
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+CachingDataSource::CachingDataSource(
+ const sp<DataSource> &source, size_t pageSize, int numPages)
+ : mSource(source),
+ mData(malloc(pageSize * numPages)),
+ mPageSize(pageSize),
+ mFirst(NULL),
+ mLast(NULL) {
+ for (int i = 0; i < numPages; ++i) {
+ Page *page = new Page;
+ page->mPrev = mLast;
+ page->mNext = NULL;
+
+ if (mLast == NULL) {
+ mFirst = page;
+ } else {
+ mLast->mNext = page;
+ }
+
+ mLast = page;
+
+ page->mOffset = -1;
+ page->mLength = 0;
+ page->mData = (char *)mData + mPageSize * i;
+ }
+}
+
+CachingDataSource::~CachingDataSource() {
+ Page *page = mFirst;
+ while (page != NULL) {
+ Page *next = page->mNext;
+ delete page;
+ page = next;
+ }
+ mFirst = mLast = NULL;
+
+ free(mData);
+ mData = NULL;
+}
+
+status_t CachingDataSource::InitCheck() const {
+ return OK;
+}
+
+ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ size_t total = 0;
+ while (size > 0) {
+ Page *page = mFirst;
+ while (page != NULL) {
+ if (page->mOffset >= 0 && offset >= page->mOffset
+ && offset < page->mOffset + (off_t)page->mLength) {
+ break;
+ }
+ page = page->mNext;
+ }
+
+ if (page == NULL) {
+ page = allocate_page();
+ page->mOffset = offset - offset % mPageSize;
+ ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize);
+ if (n < 0) {
+ page->mLength = 0;
+ } else {
+ page->mLength = (size_t)n;
+ }
+ mFirst->mPrev = page;
+ page->mNext = mFirst;
+ page->mPrev = NULL;
+ mFirst = page;
+
+ if (n < 0) {
+ return n;
+ }
+
+ if (offset >= page->mOffset + (off_t)page->mLength) {
+ break;
+ }
+ } else {
+ // Move "page" to the front in LRU order.
+ if (page->mNext != NULL) {
+ page->mNext->mPrev = page->mPrev;
+ } else {
+ mLast = page->mPrev;
+ }
+
+ if (page->mPrev != NULL) {
+ page->mPrev->mNext = page->mNext;
+ } else {
+ mFirst = page->mNext;
+ }
+
+ mFirst->mPrev = page;
+ page->mNext = mFirst;
+ page->mPrev = NULL;
+ mFirst = page;
+ }
+
+ size_t copy = page->mLength - (offset - page->mOffset);
+ if (copy > size) {
+ copy = size;
+ }
+ memcpy(data,(const char *)page->mData + (offset - page->mOffset),
+ copy);
+
+ total += copy;
+
+ if (page->mLength < mPageSize) {
+ // This was the final page. There is no more data beyond it.
+ break;
+ }
+
+ offset += copy;
+ size -= copy;
+ data = (char *)data + copy;
+ }
+
+ return total;
+}
+
+CachingDataSource::Page *CachingDataSource::allocate_page() {
+ // The last page is the least recently used, i.e. oldest.
+
+ Page *page = mLast;
+
+ page->mPrev->mNext = NULL;
+ mLast = page->mPrev;
+ page->mPrev = NULL;
+
+ return page;
+}
+
+} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
new file mode 100644
index 0000000..596ab67
--- /dev/null
+++ b/media/libstagefright/CameraSource.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/time.h>
+
+#include <OMX_Component.h>
+
+#include <binder/IServiceManager.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <ui/ICameraClient.h>
+#include <ui/ICameraService.h>
+#include <ui/Overlay.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class CameraBuffer : public MediaBuffer {
+public:
+ CameraBuffer(const sp<IMemory> &frame)
+ : MediaBuffer(frame->pointer(), frame->size()),
+ mFrame(frame) {
+ }
+
+ sp<IMemory> releaseFrame() {
+ sp<IMemory> frame = mFrame;
+ mFrame.clear();
+ return frame;
+ }
+
+private:
+ sp<IMemory> mFrame;
+};
+
+class CameraSourceClient : public BnCameraClient {
+public:
+ CameraSourceClient()
+ : mSource(NULL) {
+ }
+
+ virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+ CHECK(mSource != NULL);
+ mSource->notifyCallback(msgType, ext1, ext2);
+ }
+
+ virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {
+ CHECK(mSource != NULL);
+ mSource->dataCallback(msgType, data);
+ }
+
+ void setCameraSource(CameraSource *source) {
+ mSource = source;
+ }
+
+private:
+ CameraSource *mSource;
+};
+
+class DummySurface : public BnSurface {
+public:
+ DummySurface() {}
+
+ virtual status_t registerBuffers(const BufferHeap &buffers) {
+ return OK;
+ }
+
+ virtual void postBuffer(ssize_t offset) {
+ }
+
+ virtual void unregisterBuffers() {
+ }
+
+ virtual sp<OverlayRef> createOverlay(
+ uint32_t w, uint32_t h, int32_t format) {
+ return NULL;
+ }
+};
+
+// static
+CameraSource *CameraSource::Create() {
+ sp<IServiceManager> sm = defaultServiceManager();
+
+ sp<ICameraService> service =
+ interface_cast<ICameraService>(
+ sm->getService(String16("media.camera")));
+
+ sp<CameraSourceClient> client = new CameraSourceClient;
+ sp<ICamera> camera = service->connect(client);
+
+ CameraSource *source = new CameraSource(camera, client);
+ client->setCameraSource(source);
+
+ return source;
+}
+
+CameraSource::CameraSource(
+ const sp<ICamera> &camera, const sp<ICameraClient> &client)
+ : mCamera(camera),
+ mCameraClient(client),
+ mNumFrames(0),
+ mStarted(false) {
+ printf("params: \"%s\"\n", mCamera->getParameters().string());
+}
+
+CameraSource::~CameraSource() {
+ if (mStarted) {
+ stop();
+ }
+
+ mCamera->disconnect();
+}
+
+status_t CameraSource::start(MetaData *) {
+ CHECK(!mStarted);
+
+ status_t err = mCamera->lock();
+ CHECK_EQ(err, OK);
+
+ err = mCamera->setPreviewDisplay(new DummySurface);
+ CHECK_EQ(err, OK);
+ mCamera->setPreviewCallbackFlag(1);
+ mCamera->startPreview();
+ CHECK_EQ(err, OK);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t CameraSource::stop() {
+ CHECK(mStarted);
+
+ mCamera->stopPreview();
+ mCamera->unlock();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> CameraSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
+ meta->setInt32(kKeyWidth, 480);
+ meta->setInt32(kKeyHeight, 320);
+
+ return meta;
+}
+
+status_t CameraSource::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ CHECK(mStarted);
+
+ *buffer = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ sp<IMemory> frame;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ while (mFrames.empty()) {
+ mFrameAvailableCondition.wait(mLock);
+ }
+
+ frame = *mFrames.begin();
+ mFrames.erase(mFrames.begin());
+ }
+
+ int count = mNumFrames++;
+
+ *buffer = new CameraBuffer(frame);
+
+ (*buffer)->meta_data()->clear();
+ (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
+ (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
+
+ (*buffer)->add_ref();
+ (*buffer)->setObserver(this);
+
+ return OK;
+}
+
+void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+ printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2);
+}
+
+void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
+ Mutex::Autolock autoLock(mLock);
+
+ mFrames.push_back(data);
+ mFrameAvailableCondition.signal();
+}
+
+void CameraSource::signalBufferReturned(MediaBuffer *_buffer) {
+ CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer);
+
+ mCamera->releaseRecordingFrame(buffer->releaseFrame());
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ buffer = NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
new file mode 100644
index 0000000..daac539
--- /dev/null
+++ b/media/libstagefright/DataSource.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/AMRExtractor.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+bool DataSource::getUInt16(off_t offset, uint16_t *x) {
+ *x = 0;
+
+ uint8_t byte[2];
+ if (read_at(offset, byte, 2) != 2) {
+ return false;
+ }
+
+ *x = (byte[0] << 8) | byte[1];
+
+ return true;
+}
+
+status_t DataSource::getSize(off_t *size) {
+ *size = 0;
+
+ return ERROR_UNSUPPORTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Mutex DataSource::gSnifferMutex;
+List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+bool DataSource::sniff(String8 *mimeType, float *confidence) {
+ *mimeType = "";
+ *confidence = 0.0f;
+
+ Mutex::Autolock autoLock(gSnifferMutex);
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ String8 newMimeType;
+ float newConfidence;
+ if ((*it)(this, &newMimeType, &newConfidence)) {
+ if (newConfidence > *confidence) {
+ *mimeType = newMimeType;
+ *confidence = newConfidence;
+ }
+ }
+ }
+
+ return *confidence > 0.0;
+}
+
+// static
+void DataSource::RegisterSniffer(SnifferFunc func) {
+ Mutex::Autolock autoLock(gSnifferMutex);
+
+ for (List<SnifferFunc>::iterator it = gSniffers.begin();
+ it != gSniffers.end(); ++it) {
+ if (*it == func) {
+ return;
+ }
+ }
+
+ gSniffers.push_back(func);
+}
+
+// static
+void DataSource::RegisterDefaultSniffers() {
+ RegisterSniffer(SniffMP3);
+ RegisterSniffer(SniffMPEG4);
+ RegisterSniffer(SniffAMR);
+}
+
+} // namespace android
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
new file mode 100644
index 0000000..53b92a0
--- /dev/null
+++ b/media/libstagefright/ESDS.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/ESDS.h>
+
+#include <string.h>
+
+namespace android {
+
+ESDS::ESDS(const void *data, size_t size)
+ : mData(new uint8_t[size]),
+ mSize(size),
+ mInitCheck(NO_INIT),
+ mDecoderSpecificOffset(0),
+ mDecoderSpecificLength(0) {
+ memcpy(mData, data, size);
+
+ mInitCheck = parse();
+}
+
+ESDS::~ESDS() {
+ delete[] mData;
+ mData = NULL;
+}
+
+status_t ESDS::InitCheck() const {
+ return mInitCheck;
+}
+
+status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
+ if (mInitCheck != OK) {
+ return mInitCheck;
+ }
+
+ *data = &mData[mDecoderSpecificOffset];
+ *size = mDecoderSpecificLength;
+
+ return OK;
+}
+
+status_t ESDS::skipDescriptorHeader(
+ size_t offset, size_t size,
+ uint8_t *tag, size_t *data_offset, size_t *data_size) const {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ *tag = mData[offset++];
+ --size;
+
+ *data_size = 0;
+ bool more;
+ do {
+ if (size == 0) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t x = mData[offset++];
+ --size;
+
+ *data_size = (*data_size << 7) | (x & 0x7f);
+ more = (x & 0x80) != 0;
+ }
+ while (more);
+
+ if (*data_size > size) {
+ return ERROR_MALFORMED;
+ }
+
+ *data_offset = offset;
+
+ return OK;
+}
+
+status_t ESDS::parse() {
+ uint8_t tag;
+ size_t data_offset;
+ size_t data_size;
+ status_t err =
+ skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_ESDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ return parseESDescriptor(data_offset, data_size);
+}
+
+status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
+ if (size < 3) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 2; // skip ES_ID
+ size -= 2;
+
+ unsigned streamDependenceFlag = mData[offset] & 0x80;
+ unsigned URL_Flag = mData[offset] & 0x40;
+ unsigned OCRstreamFlag = mData[offset] & 0x20;
+
+ ++offset;
+ --size;
+
+ if (streamDependenceFlag) {
+ offset += 2;
+ size -= 2;
+ }
+
+ if (URL_Flag) {
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+ unsigned URLlength = mData[offset];
+ offset += URLlength + 1;
+ size -= URLlength + 1;
+ }
+
+ if (OCRstreamFlag) {
+ offset += 2;
+ size -= 2;
+ }
+
+ if (offset >= size) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderConfigDescriptor) {
+ return ERROR_MALFORMED;
+ }
+
+ err = parseDecoderConfigDescriptor(sub_offset, sub_size);
+
+ return err;
+}
+
+status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+ if (size < 13) {
+ return ERROR_MALFORMED;
+ }
+
+ offset += 13;
+ size -= 13;
+
+ if (size == 0) {
+ mDecoderSpecificOffset = 0;
+ mDecoderSpecificLength = 0;
+ return OK;
+ }
+
+ uint8_t tag;
+ size_t sub_offset, sub_size;
+ status_t err = skipDescriptorHeader(
+ offset, size, &tag, &sub_offset, &sub_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (tag != kTag_DecoderSpecificInfo) {
+ return ERROR_MALFORMED;
+ }
+
+ mDecoderSpecificOffset = sub_offset;
+ mDecoderSpecificLength = sub_size;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
new file mode 100644
index 0000000..f6b90b2
--- /dev/null
+++ b/media/libstagefright/FileSource.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+FileSource::FileSource(const char *filename)
+ : mFile(fopen(filename, "rb")) {
+}
+
+FileSource::~FileSource() {
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+status_t FileSource::InitCheck() const {
+ return mFile != NULL ? OK : NO_INIT;
+}
+
+ssize_t FileSource::read_at(off_t offset, void *data, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ int err = fseeko(mFile, offset, SEEK_SET);
+ CHECK(err != -1);
+
+ ssize_t result = fread(data, 1, size, mFile);
+
+ return result;
+}
+
+} // namespace android
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
new file mode 100644
index 0000000..698223b
--- /dev/null
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+HTTPDataSource::HTTPDataSource(const char *uri)
+ : mHost(NULL),
+ mPort(0),
+ mPath(NULL),
+ mBuffer(malloc(kBufferSize)),
+ mBufferLength(0),
+ mBufferOffset(0) {
+ CHECK(!strncasecmp("http://", uri, 7));
+
+ string host;
+ string path;
+ int port;
+
+ char *slash = strchr(uri + 7, '/');
+ if (slash == NULL) {
+ host = uri + 7;
+ path = "/";
+ } else {
+ host = string(uri + 7, slash - (uri + 7));
+ path = slash;
+ }
+
+ char *colon = strchr(host.c_str(), ':');
+ if (colon == NULL) {
+ port = 80;
+ } else {
+ char *end;
+ long tmp = strtol(colon + 1, &end, 10);
+ CHECK(end > colon + 1);
+ CHECK(tmp > 0 && tmp < 65536);
+ port = tmp;
+
+ host = string(host, 0, colon - host.c_str());
+ }
+
+ LOGI("Connecting to host '%s', port %d, path '%s'",
+ host.c_str(), port, path.c_str());
+
+ mHost = strdup(host.c_str());
+ mPort = port;
+ mPath = strdup(path.c_str());
+
+ status_t err = mHttp.connect(mHost, mPort);
+ CHECK_EQ(err, OK);
+}
+
+HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
+ : mHost(strdup(host)),
+ mPort(port),
+ mPath(strdup(path)),
+ mBuffer(malloc(kBufferSize)),
+ mBufferLength(0),
+ mBufferOffset(0) {
+ status_t err = mHttp.connect(mHost, mPort);
+ CHECK_EQ(err, OK);
+}
+
+HTTPDataSource::~HTTPDataSource() {
+ mHttp.disconnect();
+
+ free(mBuffer);
+ mBuffer = NULL;
+
+ free(mPath);
+ mPath = NULL;
+}
+
+ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
+ if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
+ size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
+
+ size_t copy = num_bytes_available;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
+
+ return copy;
+ }
+
+ mBufferOffset = offset;
+ mBufferLength = 0;
+
+ char host[128];
+ sprintf(host, "Host: %s\r\n", mHost);
+
+ char range[128];
+ sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
+ mBufferOffset, mBufferOffset + kBufferSize - 1);
+
+ int http_status;
+
+ status_t err;
+ int attempt = 1;
+ for (;;) {
+ if ((err = mHttp.send("GET ")) != OK
+ || (err = mHttp.send(mPath)) != OK
+ || (err = mHttp.send(" HTTP/1.1\r\n")) != OK
+ || (err = mHttp.send(host)) != OK
+ || (err = mHttp.send(range)) != OK
+ || (err = mHttp.send("\r\n")) != OK
+ || (err = mHttp.receive_header(&http_status)) != OK) {
+
+ if (attempt == 3) {
+ return err;
+ }
+
+ mHttp.connect(mHost, mPort);
+ ++attempt;
+ } else {
+ break;
+ }
+ }
+
+ if ((http_status / 100) != 2) {
+ return UNKNOWN_ERROR;
+ }
+
+ string value;
+ if (!mHttp.find_header_value("Content-Length", &value)) {
+ return UNKNOWN_ERROR;
+ }
+
+ char *end;
+ unsigned long contentLength = strtoul(value.c_str(), &end, 10);
+
+ ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
+
+ if (num_bytes_received <= 0) {
+ return num_bytes_received;
+ }
+
+ mBufferLength = (size_t)num_bytes_received;
+
+ size_t copy = mBufferLength;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data, mBuffer, copy);
+
+ return copy;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
new file mode 100644
index 0000000..6af7df9
--- /dev/null
+++ b/media/libstagefright/HTTPStream.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+// static
+const char *HTTPStream::kStatusKey = ":status:";
+
+HTTPStream::HTTPStream()
+ : mState(READY),
+ mSocket(-1) {
+}
+
+HTTPStream::~HTTPStream() {
+ disconnect();
+}
+
+status_t HTTPStream::connect(const char *server, int port) {
+ status_t err = OK;
+
+ if (mState == CONNECTED) {
+ return ERROR_ALREADY_CONNECTED;
+ }
+
+ CHECK_EQ(mSocket, -1);
+ mSocket = socket(AF_INET, SOCK_STREAM, 0);
+
+ if (mSocket < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ struct hostent *ent = gethostbyname(server);
+ if (ent == NULL) {
+ err = ERROR_UNKNOWN_HOST;
+ goto exit1;
+ }
+
+ struct sockaddr_in addr;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+ memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+
+ if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ err = ERROR_CANNOT_CONNECT;
+ goto exit1;
+ }
+
+ mState = CONNECTED;
+
+ return OK;
+
+exit1:
+ close(mSocket);
+ mSocket = -1;
+
+ return err;
+}
+
+status_t HTTPStream::disconnect() {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ CHECK(mSocket >= 0);
+ close(mSocket);
+ mSocket = -1;
+
+ mState = READY;
+
+ return OK;
+}
+
+status_t HTTPStream::send(const char *data, size_t size) {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ while (size > 0) {
+ ssize_t n = ::send(mSocket, data, size, 0);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ size -= (size_t)n;
+ data += (size_t)n;
+ }
+
+ return OK;
+}
+
+status_t HTTPStream::send(const char *data) {
+ return send(data, strlen(data));
+}
+
+status_t HTTPStream::receive_line(char *line, size_t size) {
+ if (mState != CONNECTED) {
+ return ERROR_NOT_CONNECTED;
+ }
+
+ bool saw_CR = false;
+ size_t length = 0;
+
+ for (;;) {
+ char c;
+ ssize_t n = recv(mSocket, &c, 1, 0);
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ if (saw_CR && c == '\n') {
+ // We have a complete line.
+
+ line[length - 1] = '\0';
+ return OK;
+ }
+
+ saw_CR = (c == '\r');
+
+ CHECK(length + 1 < size);
+ line[length++] = c;
+ }
+}
+
+status_t HTTPStream::receive_header(int *http_status) {
+ *http_status = -1;
+ mHeaders.clear();
+
+ char line[1024];
+ status_t err = receive_line(line, sizeof(line));
+ if (err != OK) {
+ return err;
+ }
+
+ mHeaders.add(string(kStatusKey), string(line));
+
+ char *spacePos = strchr(line, ' ');
+ if (spacePos == NULL) {
+ // Malformed response?
+ return UNKNOWN_ERROR;
+ }
+
+ char *status_start = spacePos + 1;
+ char *status_end = status_start;
+ while (isdigit(*status_end)) {
+ ++status_end;
+ }
+
+ if (status_end == status_start) {
+ // Malformed response, status missing?
+ return UNKNOWN_ERROR;
+ }
+
+ memmove(line, status_start, status_end - status_start);
+ line[status_end - status_start] = '\0';
+
+ long tmp = strtol(line, NULL, 10);
+ if (tmp < 0 || tmp > 999) {
+ return UNKNOWN_ERROR;
+ }
+
+ *http_status = (int)tmp;
+
+ for (;;) {
+ err = receive_line(line, sizeof(line));
+ if (err != OK) {
+ return err;
+ }
+
+ if (*line == '\0') {
+ // Empty line signals the end of the header.
+ break;
+ }
+
+ // puts(line);
+
+ char *colonPos = strchr(line, ':');
+ if (colonPos == NULL) {
+ mHeaders.add(string(line), string());
+ } else {
+ char *end_of_key = colonPos;
+ while (end_of_key > line && isspace(end_of_key[-1])) {
+ --end_of_key;
+ }
+
+ char *start_of_value = colonPos + 1;
+ while (isspace(*start_of_value)) {
+ ++start_of_value;
+ }
+
+ *end_of_key = '\0';
+
+ mHeaders.add(string(line), string(start_of_value));
+ }
+ }
+
+ return OK;
+}
+
+ssize_t HTTPStream::receive(void *data, size_t size) {
+ size_t total = 0;
+ while (total < size) {
+ ssize_t n = recv(mSocket, (char *)data + total, size - total, 0);
+
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+
+ disconnect();
+ return ERROR_IO;
+ } else if (n == 0) {
+ disconnect();
+
+ return ERROR_CONNECTION_LOST;
+ }
+
+ total += (size_t)n;
+ }
+
+ return (ssize_t)total;
+}
+
+bool HTTPStream::find_header_value(const string &key, string *value) const {
+ ssize_t index = mHeaders.indexOfKey(key);
+ if (index < 0) {
+ value->clear();
+ return false;
+ }
+
+ *value = mHeaders.valueAt(index);
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
new file mode 100644
index 0000000..7fd699f
--- /dev/null
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MP3Extractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static bool get_mp3_frame_size(
+ uint32_t header, size_t *frame_size,
+ int *out_sampling_rate = NULL, int *out_channels = NULL,
+ int *out_bitrate = NULL) {
+ *frame_size = 0;
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = 0;
+ }
+
+ if (out_channels) {
+ *out_channels = 0;
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = 0;
+ }
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+
+ unsigned version = (header >> 19) & 3;
+
+ if (version == 0x01) {
+ return false;
+ }
+
+ unsigned layer = (header >> 17) & 3;
+
+ if (layer == 0x00) {
+ return false;
+ }
+
+ unsigned protection = (header >> 16) & 1;
+
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ // Disallow "free" bitrate.
+ return false;
+ }
+
+ unsigned sampling_rate_index = (header >> 10) & 3;
+
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+
+ static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) {
+ // layer I
+
+ static const int kBitrateV1[] = {
+ 32, 64, 96, 128, 160, 192, 224, 256,
+ 288, 320, 352, 384, 416, 448
+ };
+
+ static const int kBitrateV2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 144, 160, 176, 192, 224, 256
+ };
+
+ int bitrate =
+ (version == 3 /* V1 */)
+ ? kBitrateV1[bitrate_index - 1]
+ : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ } else {
+ // layer II or III
+
+ static const int kBitrateV1L2[] = {
+ 32, 48, 56, 64, 80, 96, 112, 128,
+ 160, 192, 224, 256, 320, 384
+ };
+
+ static const int kBitrateV1L3[] = {
+ 32, 40, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320
+ };
+
+ static const int kBitrateV2[] = {
+ 8, 16, 24, 32, 40, 48, 56, 64,
+ 80, 96, 112, 128, 144, 160
+ };
+
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate = (layer == 2 /* L2 */)
+ ? kBitrateV1L2[bitrate_index - 1]
+ : kBitrateV1L3[bitrate_index - 1];
+ } else {
+ // V2 (or 2.5)
+
+ bitrate = kBitrateV2[bitrate_index - 1];
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static bool Resync(
+ const sp<DataSource> &source, uint32_t match_header,
+ off_t *inout_pos, uint32_t *out_header) {
+ // Everything must match except for
+ // protection, bitrate, padding, private bits and mode extension.
+ const uint32_t kMask = 0xfffe0ccf;
+
+ const size_t kMaxFrameSize = 4096;
+ uint8_t *buffer = new uint8_t[kMaxFrameSize];
+
+ off_t pos = *inout_pos - kMaxFrameSize;
+ size_t buffer_offset = kMaxFrameSize;
+ size_t buffer_length = kMaxFrameSize;
+ bool valid = false;
+ do {
+ if (buffer_offset + 3 >= buffer_length) {
+ if (buffer_length < kMaxFrameSize) {
+ break;
+ }
+
+ pos += buffer_offset;
+
+ if (pos >= *inout_pos + 128 * 1024) {
+ // Don't scan forever.
+ LOGV("giving up at offset %ld", pos);
+ break;
+ }
+
+ memmove(buffer, &buffer[buffer_offset], buffer_length - buffer_offset);
+ buffer_length = buffer_length - buffer_offset;
+ buffer_offset = 0;
+
+ ssize_t n = source->read_at(
+ pos, &buffer[buffer_length], kMaxFrameSize - buffer_length);
+
+ if (n <= 0) {
+ break;
+ }
+
+ buffer_length += (size_t)n;
+
+ continue;
+ }
+
+ uint32_t header = U32_AT(&buffer[buffer_offset]);
+
+ if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
+ ++buffer_offset;
+ continue;
+ }
+
+ size_t frame_size;
+ int sample_rate, num_channels, bitrate;
+ if (!get_mp3_frame_size(header, &frame_size,
+ &sample_rate, &num_channels, &bitrate)) {
+ ++buffer_offset;
+ continue;
+ }
+
+ LOGV("found possible 1st frame at %ld", pos + buffer_offset);
+
+ // We found what looks like a valid frame,
+ // now find its successors.
+
+ off_t test_pos = pos + buffer_offset + frame_size;
+
+ valid = true;
+ for (int j = 0; j < 3; ++j) {
+ uint8_t tmp[4];
+ if (source->read_at(test_pos, tmp, 4) < 4) {
+ valid = false;
+ break;
+ }
+
+ uint32_t test_header = U32_AT(tmp);
+
+ LOGV("subsequent header is %08x", test_header);
+
+ if ((test_header & kMask) != (header & kMask)) {
+ valid = false;
+ break;
+ }
+
+ size_t test_frame_size;
+ if (!get_mp3_frame_size(test_header, &test_frame_size)) {
+ valid = false;
+ break;
+ }
+
+ LOGV("found subsequent frame #%d at %ld", j + 2, test_pos);
+
+ test_pos += test_frame_size;
+ }
+
+ if (valid) {
+ *inout_pos = pos + buffer_offset;
+
+ if (out_header != NULL) {
+ *out_header = header;
+ }
+ } else {
+ LOGV("no dice, no valid sequence of frames found.");
+ }
+
+ ++buffer_offset;
+
+ } while (!valid);
+
+ delete[] buffer;
+ buffer = NULL;
+
+ return valid;
+}
+
+class MP3Source : public MediaSource {
+public:
+ MP3Source(
+ const sp<MetaData> &meta, const sp<DataSource> &source,
+ off_t first_frame_pos, uint32_t fixed_header);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~MP3Source();
+
+private:
+ sp<MetaData> mMeta;
+ sp<DataSource> mDataSource;
+ off_t mFirstFramePos;
+ uint32_t mFixedHeader;
+ off_t mCurrentPos;
+ int64_t mCurrentTimeUs;
+ bool mStarted;
+
+ MediaBufferGroup *mGroup;
+
+ MP3Source(const MP3Source &);
+ MP3Source &operator=(const MP3Source &);
+};
+
+MP3Extractor::MP3Extractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mFirstFramePos(-1),
+ mFixedHeader(0) {
+ off_t pos = 0;
+ uint32_t header;
+ bool success = Resync(mDataSource, 0, &pos, &header);
+ CHECK(success);
+
+ if (success) {
+ mFirstFramePos = pos;
+ mFixedHeader = header;
+
+ size_t frame_size;
+ int sample_rate;
+ int num_channels;
+ int bitrate;
+ get_mp3_frame_size(
+ header, &frame_size, &sample_rate, &num_channels, &bitrate);
+
+ mMeta = new MetaData;
+
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ mMeta->setInt32(kKeySampleRate, sample_rate);
+ mMeta->setInt32(kKeyBitRate, bitrate);
+ mMeta->setInt32(kKeyChannelCount, num_channels);
+
+ off_t fileSize;
+ if (mDataSource->getSize(&fileSize) == OK) {
+ mMeta->setInt32(
+ kKeyDuration,
+ 8 * (fileSize - mFirstFramePos) / bitrate);
+ mMeta->setInt32(kKeyTimeScale, 1000);
+ }
+ }
+}
+
+MP3Extractor::~MP3Extractor() {
+}
+
+size_t MP3Extractor::countTracks() {
+ return (mFirstFramePos < 0) ? 0 : 1;
+}
+
+sp<MediaSource> MP3Extractor::getTrack(size_t index) {
+ if (mFirstFramePos < 0 || index != 0) {
+ return NULL;
+ }
+
+ return new MP3Source(
+ mMeta, mDataSource, mFirstFramePos, mFixedHeader);
+}
+
+sp<MetaData> MP3Extractor::getTrackMetaData(size_t index) {
+ if (mFirstFramePos < 0 || index != 0) {
+ return NULL;
+ }
+
+ return mMeta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MP3Source::MP3Source(
+ const sp<MetaData> &meta, const sp<DataSource> &source,
+ off_t first_frame_pos, uint32_t fixed_header)
+ : mMeta(meta),
+ mDataSource(source),
+ mFirstFramePos(first_frame_pos),
+ mFixedHeader(fixed_header),
+ mCurrentPos(0),
+ mCurrentTimeUs(0),
+ mStarted(false),
+ mGroup(NULL) {
+}
+
+MP3Source::~MP3Source() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t MP3Source::start(MetaData *) {
+ CHECK(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+
+ const size_t kMaxFrameSize = 32768;
+ mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+
+ mCurrentPos = mFirstFramePos;
+ mCurrentTimeUs = 0;
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MP3Source::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> MP3Source::getFormat() {
+ return mMeta;
+}
+
+status_t MP3Source::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+ int32_t bitrate;
+ if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+ // bitrate is in kbits/sec.
+ LOGI("no bitrate");
+
+ return ERROR_UNSUPPORTED;
+ }
+
+ mCurrentTimeUs = seekTimeUs;
+ mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 1000000 * 125;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ size_t frame_size;
+ for (;;) {
+ ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4);
+ if (n < 4) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ uint32_t header = U32_AT((const uint8_t *)buffer->data());
+
+ if (get_mp3_frame_size(header, &frame_size)) {
+ break;
+ }
+
+ // Lost sync.
+ LOGW("lost sync!\n");
+
+ off_t pos = mCurrentPos;
+ if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
+ LOGE("Unable to resync. Signalling end of stream.");
+
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ mCurrentPos = pos;
+
+ // Try again with the new position.
+ }
+
+ CHECK(frame_size <= buffer->size());
+
+ ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size);
+ if (n < (ssize_t)frame_size) {
+ buffer->release();
+ buffer = NULL;
+
+ return ERROR_END_OF_STREAM;
+ }
+
+ buffer->set_range(0, frame_size);
+
+ buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
+ buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+
+ mCurrentPos += frame_size;
+ mCurrentTimeUs += 1152 * 1000000 / 44100;
+
+ *out = buffer;
+
+ return OK;
+}
+
+bool SniffMP3(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ off_t pos = 0;
+ uint32_t header;
+ if (!Resync(source, 0, &pos, &header)) {
+ return false;
+ }
+
+ *mimeType = MEDIA_MIMETYPE_AUDIO_MPEG;
+ *confidence = 0.3f;
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
new file mode 100644
index 0000000..9174d19
--- /dev/null
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MPEG4Extractor"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class MPEG4Source : public MediaSource {
+public:
+ // Caller retains ownership of both "dataSource" and "sampleTable".
+ MPEG4Source(const sp<MetaData> &format,
+ const sp<DataSource> &dataSource,
+ const sp<SampleTable> &sampleTable);
+
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+protected:
+ virtual ~MPEG4Source();
+
+private:
+ sp<MetaData> mFormat;
+ sp<DataSource> mDataSource;
+ int32_t mTimescale;
+ sp<SampleTable> mSampleTable;
+ uint32_t mCurrentSampleIndex;
+
+ bool mIsAVC;
+ bool mStarted;
+
+ MediaBufferGroup *mGroup;
+
+ MediaBuffer *mBuffer;
+
+ bool mWantsNALFragments;
+
+ uint8_t *mSrcBuffer;
+
+ MPEG4Source(const MPEG4Source &);
+ MPEG4Source &operator=(const MPEG4Source &);
+};
+
+static void hexdump(const void *_data, size_t size) {
+ const uint8_t *data = (const uint8_t *)_data;
+ size_t offset = 0;
+ while (offset < size) {
+ printf("0x%04x ", offset);
+
+ size_t n = size - offset;
+ if (n > 16) {
+ n = 16;
+ }
+
+ for (size_t i = 0; i < 16; ++i) {
+ if (i == 8) {
+ printf(" ");
+ }
+
+ if (offset + i < size) {
+ printf("%02x ", data[offset + i]);
+ } else {
+ printf(" ");
+ }
+ }
+
+ printf(" ");
+
+ for (size_t i = 0; i < n; ++i) {
+ if (isprint(data[offset + i])) {
+ printf("%c", data[offset + i]);
+ } else {
+ printf(".");
+ }
+ }
+
+ printf("\n");
+
+ offset += 16;
+ }
+}
+
+static const char *FourCC2MIME(uint32_t fourcc) {
+ switch (fourcc) {
+ case FOURCC('m', 'p', '4', 'a'):
+ return MEDIA_MIMETYPE_AUDIO_AAC;
+
+ case FOURCC('s', 'a', 'm', 'r'):
+ return MEDIA_MIMETYPE_AUDIO_AMR_NB;
+
+ case FOURCC('s', 'a', 'w', 'b'):
+ return MEDIA_MIMETYPE_AUDIO_AMR_WB;
+
+ case FOURCC('m', 'p', '4', 'v'):
+ return MEDIA_MIMETYPE_VIDEO_MPEG4;
+
+ case FOURCC('s', '2', '6', '3'):
+ return MEDIA_MIMETYPE_VIDEO_H263;
+
+ case FOURCC('a', 'v', 'c', '1'):
+ return MEDIA_MIMETYPE_VIDEO_AVC;
+
+ default:
+ CHECK(!"should not be here.");
+ return NULL;
+ }
+}
+
+MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)
+ : mDataSource(source),
+ mHaveMetadata(false),
+ mFirstTrack(NULL),
+ mLastTrack(NULL) {
+}
+
+MPEG4Extractor::~MPEG4Extractor() {
+ Track *track = mFirstTrack;
+ while (track) {
+ Track *next = track->next;
+
+ delete track;
+ track = next;
+ }
+ mFirstTrack = mLastTrack = NULL;
+}
+
+size_t MPEG4Extractor::countTracks() {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return 0;
+ }
+
+ size_t n = 0;
+ Track *track = mFirstTrack;
+ while (track) {
+ ++n;
+ track = track->next;
+ }
+
+ return n;
+}
+
+sp<MetaData> MPEG4Extractor::getTrackMetaData(size_t index) {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ if (track == NULL) {
+ return NULL;
+ }
+
+ return track->meta;
+}
+
+status_t MPEG4Extractor::readMetaData() {
+ if (mHaveMetadata) {
+ return OK;
+ }
+
+ off_t offset = 0;
+ status_t err;
+ while ((err = parseChunk(&offset, 0)) == OK) {
+ }
+
+ if (mHaveMetadata) {
+ return OK;
+ }
+
+ return err;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+ s[0] = x >> 24;
+ s[1] = (x >> 16) & 0xff;
+ s[2] = (x >> 8) & 0xff;
+ s[3] = x & 0xff;
+ s[4] = '\0';
+}
+
+status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
+ uint32_t hdr[2];
+ if (mDataSource->read_at(*offset, hdr, 8) < 8) {
+ return ERROR_IO;
+ }
+ uint64_t chunk_size = ntohl(hdr[0]);
+ uint32_t chunk_type = ntohl(hdr[1]);
+ off_t data_offset = *offset + 8;
+
+ if (chunk_size == 1) {
+ if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) {
+ return ERROR_IO;
+ }
+ chunk_size = ntoh64(chunk_size);
+ data_offset += 8;
+ }
+
+ char chunk[5];
+ MakeFourCCString(chunk_type, chunk);
+
+#if 0
+ static const char kWhitespace[] = " ";
+ const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
+ printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+
+ char buffer[256];
+ if (chunk_size <= sizeof(buffer)) {
+ if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) {
+ return ERROR_IO;
+ }
+
+ hexdump(buffer, chunk_size);
+ }
+#endif
+
+ off_t chunk_data_size = *offset + chunk_size - data_offset;
+
+ switch(chunk_type) {
+ case FOURCC('m', 'o', 'o', 'v'):
+ case FOURCC('t', 'r', 'a', 'k'):
+ case FOURCC('m', 'd', 'i', 'a'):
+ case FOURCC('m', 'i', 'n', 'f'):
+ case FOURCC('d', 'i', 'n', 'f'):
+ case FOURCC('s', 't', 'b', 'l'):
+ case FOURCC('m', 'v', 'e', 'x'):
+ case FOURCC('m', 'o', 'o', 'f'):
+ case FOURCC('t', 'r', 'a', 'f'):
+ case FOURCC('m', 'f', 'r', 'a'):
+ case FOURCC('s', 'k', 'i' ,'p'):
+ {
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset;
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+
+ if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+ mHaveMetadata = true;
+
+ return UNKNOWN_ERROR; // Return a dummy error.
+ }
+ break;
+ }
+
+ case FOURCC('t', 'k', 'h', 'd'):
+ {
+ CHECK(chunk_data_size >= 4);
+
+ uint8_t version;
+ if (mDataSource->read_at(data_offset, &version, 1) < 1) {
+ return ERROR_IO;
+ }
+
+ uint64_t ctime, mtime, duration;
+ int32_t id;
+ uint32_t width, height;
+
+ if (version == 1) {
+ if (chunk_data_size != 36 + 60) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[36 + 60];
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ ctime = U64_AT(&buffer[4]);
+ mtime = U64_AT(&buffer[12]);
+ id = U32_AT(&buffer[20]);
+ duration = U64_AT(&buffer[28]);
+ width = U32_AT(&buffer[88]);
+ height = U32_AT(&buffer[92]);
+ } else if (version == 0) {
+ if (chunk_data_size != 24 + 60) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[24 + 60];
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+ ctime = U32_AT(&buffer[4]);
+ mtime = U32_AT(&buffer[8]);
+ id = U32_AT(&buffer[12]);
+ duration = U32_AT(&buffer[20]);
+ width = U32_AT(&buffer[76]);
+ height = U32_AT(&buffer[80]);
+ }
+
+ Track *track = new Track;
+ track->next = NULL;
+ if (mLastTrack) {
+ mLastTrack->next = track;
+ } else {
+ mFirstTrack = track;
+ }
+ mLastTrack = track;
+
+ track->meta = new MetaData;
+ track->timescale = 0;
+ track->sampleTable = new SampleTable(mDataSource);
+ track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('m', 'd', 'h', 'd'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t version;
+ if (mDataSource->read_at(
+ data_offset, &version, sizeof(version))
+ < (ssize_t)sizeof(version)) {
+ return ERROR_IO;
+ }
+
+ off_t timescale_offset;
+
+ if (version == 1) {
+ timescale_offset = data_offset + 4 + 16;
+ } else if (version == 0) {
+ timescale_offset = data_offset + 4 + 8;
+ } else {
+ return ERROR_IO;
+ }
+
+ uint32_t timescale;
+ if (mDataSource->read_at(
+ timescale_offset, &timescale, sizeof(timescale))
+ < (ssize_t)sizeof(timescale)) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->timescale = ntohl(timescale);
+ mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
+
+ int64_t duration;
+ if (version == 1) {
+ if (mDataSource->read_at(
+ timescale_offset + 4, &duration, sizeof(duration))
+ < (ssize_t)sizeof(duration)) {
+ return ERROR_IO;
+ }
+ duration = ntoh64(duration);
+ } else {
+ int32_t duration32;
+ if (mDataSource->read_at(
+ timescale_offset + 4, &duration32, sizeof(duration32))
+ < (ssize_t)sizeof(duration32)) {
+ return ERROR_IO;
+ }
+ duration = ntohl(duration32);
+ }
+ mLastTrack->meta->setInt32(kKeyDuration, duration);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('h', 'd', 'l', 'r'):
+ {
+ if (chunk_data_size < 25) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[24];
+ if (mDataSource->read_at(data_offset, buffer, 24) < 24) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ if (U32_AT(&buffer[4]) != 0) {
+ // pre_defined should be 0.
+ return ERROR_MALFORMED;
+ }
+
+ mHandlerType = U32_AT(&buffer[8]);
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'd'):
+ {
+ if (chunk_data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8];
+ CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+ if (mDataSource->read_at(
+ data_offset, buffer, 8) < 8) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ uint32_t entry_count = U32_AT(&buffer[4]);
+
+ if (entry_count > 1) {
+ // For now we only support a single type of media per track.
+ return ERROR_UNSUPPORTED;
+ }
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + 8;
+ for (uint32_t i = 0; i < entry_count; ++i) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'a'):
+ case FOURCC('s', 'a', 'm', 'r'):
+ case FOURCC('s', 'a', 'w', 'b'):
+ {
+ if (mHandlerType != FOURCC('s', 'o', 'u', 'n')) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[8 + 20];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic AudioSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t num_channels = U16_AT(&buffer[16]);
+
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB,
+ FourCC2MIME(chunk_type))
+ || !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ FourCC2MIME(chunk_type))) {
+ // AMR audio is always mono.
+ num_channels = 1;
+ }
+
+ uint16_t sample_size = U16_AT(&buffer[18]);
+ uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+
+ printf("*** coding='%s' %d channels, size %d, rate %d\n",
+ chunk, num_channels, sample_size, sample_rate);
+
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+ mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+ break;
+ }
+
+ case FOURCC('m', 'p', '4', 'v'):
+ case FOURCC('s', '2', '6', '3'):
+ case FOURCC('a', 'v', 'c', '1'):
+ {
+ if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[78];
+ if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+ // Basic VideoSampleEntry size.
+ return ERROR_MALFORMED;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint16_t data_ref_index = U16_AT(&buffer[6]);
+ uint16_t width = U16_AT(&buffer[6 + 18]);
+ uint16_t height = U16_AT(&buffer[6 + 20]);
+
+ printf("*** coding='%s' width=%d height=%d\n",
+ chunk, width, height);
+
+ mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+ mLastTrack->meta->setInt32(kKeyWidth, width);
+ mLastTrack->meta->setInt32(kKeyHeight, height);
+
+ off_t stop_offset = *offset + chunk_size;
+ *offset = data_offset + sizeof(buffer);
+ while (*offset < stop_offset) {
+ status_t err = parseChunk(offset, depth + 1);
+ if (err != OK) {
+ return err;
+ }
+ }
+ CHECK_EQ(*offset, stop_offset);
+ break;
+ }
+
+ case FOURCC('s', 't', 'c', 'o'):
+ case FOURCC('c', 'o', '6', '4'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setChunkOffsetParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'c'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSampleToChunkParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 'z'):
+ case FOURCC('s', 't', 'z', '2'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSampleSizeParams(
+ chunk_type, data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 't', 's'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setTimeToSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('s', 't', 's', 's'):
+ {
+ status_t err =
+ mLastTrack->sampleTable->setSyncSampleParams(
+ data_offset, chunk_data_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('e', 's', 'd', 's'):
+ {
+ if (chunk_data_size < 4) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t buffer[256];
+ if (chunk_data_size > (off_t)sizeof(buffer)) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(buffer) != 0) {
+ // Should be version 0, flags 0.
+ return ERROR_MALFORMED;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ case FOURCC('a', 'v', 'c', 'C'):
+ {
+ char buffer[256];
+ if (chunk_data_size > (off_t)sizeof(buffer)) {
+ return ERROR_BUFFER_TOO_SMALL;
+ }
+
+ if (mDataSource->read_at(
+ data_offset, buffer, chunk_data_size) < chunk_data_size) {
+ return ERROR_IO;
+ }
+
+ mLastTrack->meta->setData(
+ kKeyAVCC, kTypeAVCC, buffer, chunk_data_size);
+
+ *offset += chunk_size;
+ break;
+ }
+
+ default:
+ {
+ *offset += chunk_size;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+sp<MediaSource> MPEG4Extractor::getTrack(size_t index) {
+ status_t err;
+ if ((err = readMetaData()) != OK) {
+ return NULL;
+ }
+
+ Track *track = mFirstTrack;
+ while (index > 0) {
+ if (track == NULL) {
+ return NULL;
+ }
+
+ track = track->next;
+ --index;
+ }
+
+ if (track == NULL) {
+ return NULL;
+ }
+
+ return new MPEG4Source(
+ track->meta, mDataSource, track->sampleTable);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Source::MPEG4Source(
+ const sp<MetaData> &format,
+ const sp<DataSource> &dataSource,
+ const sp<SampleTable> &sampleTable)
+ : mFormat(format),
+ mDataSource(dataSource),
+ mTimescale(0),
+ mSampleTable(sampleTable),
+ mCurrentSampleIndex(0),
+ mIsAVC(false),
+ mStarted(false),
+ mGroup(NULL),
+ mBuffer(NULL),
+ mWantsNALFragments(false),
+ mSrcBuffer(NULL) {
+ const char *mime;
+ bool success = mFormat->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
+ CHECK(success);
+
+ mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC);
+}
+
+MPEG4Source::~MPEG4Source() {
+ if (mStarted) {
+ stop();
+ }
+}
+
+status_t MPEG4Source::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ int32_t val;
+ if (params && params->findInt32(kKeyWantsNALFragments, &val)
+ && val != 0) {
+ mWantsNALFragments = true;
+ } else {
+ mWantsNALFragments = false;
+ }
+
+ mGroup = new MediaBufferGroup;
+
+ size_t max_size;
+ status_t err = mSampleTable->getMaxSampleSize(&max_size);
+ CHECK_EQ(err, OK);
+
+ // Assume that a given buffer only contains at most 10 fragments,
+ // each fragment originally prefixed with a 2 byte length will
+ // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
+ // and thus will grow by 2 bytes per fragment.
+ mGroup->add_buffer(new MediaBuffer(max_size + 10 * 2));
+
+ mSrcBuffer = new uint8_t[max_size];
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MPEG4Source::stop() {
+ CHECK(mStarted);
+
+ if (mBuffer != NULL) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+
+ delete[] mSrcBuffer;
+ mSrcBuffer = NULL;
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+ mCurrentSampleIndex = 0;
+
+ return OK;
+}
+
+sp<MetaData> MPEG4Source::getFormat() {
+ return mFormat;
+}
+
+status_t MPEG4Source::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ CHECK(mStarted);
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ uint32_t sampleIndex;
+ status_t err = mSampleTable->findClosestSample(
+ seekTimeUs * mTimescale / 1000000,
+ &sampleIndex, SampleTable::kSyncSample_Flag);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mCurrentSampleIndex = sampleIndex;
+ if (mBuffer != NULL) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+
+ // fall through
+ }
+
+ off_t offset;
+ size_t size;
+ uint32_t dts;
+ bool newBuffer = false;
+ if (mBuffer == NULL) {
+ newBuffer = true;
+
+ status_t err = mSampleTable->getSampleOffsetAndSize(
+ mCurrentSampleIndex, &offset, &size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = mGroup->acquire_buffer(&mBuffer);
+ if (err != OK) {
+ CHECK_EQ(mBuffer, NULL);
+ return err;
+ }
+ }
+
+ if (!mIsAVC || mWantsNALFragments) {
+ if (newBuffer) {
+ ssize_t num_bytes_read =
+ mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
+
+ if (num_bytes_read < (ssize_t)size) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ mBuffer->set_range(0, size);
+ mBuffer->meta_data()->clear();
+ mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+ mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ ++mCurrentSampleIndex;
+ }
+
+ if (!mIsAVC) {
+ *out = mBuffer;
+ mBuffer = NULL;
+
+ return OK;
+ }
+
+ // Each NAL unit is split up into its constituent fragments and
+ // each one of them returned in its own buffer.
+
+ CHECK(mBuffer->range_length() >= 2);
+
+ const uint8_t *src =
+ (const uint8_t *)mBuffer->data() + mBuffer->range_offset();
+
+ size_t nal_size = U16_AT(src);
+
+ CHECK(mBuffer->range_length() >= 2 + nal_size);
+
+ MediaBuffer *clone = mBuffer->clone();
+ clone->set_range(mBuffer->range_offset() + 2, nal_size);
+
+ mBuffer->set_range(
+ mBuffer->range_offset() + 2 + nal_size,
+ mBuffer->range_length() - 2 - nal_size);
+
+ if (mBuffer->range_length() == 0) {
+ mBuffer->release();
+ mBuffer = NULL;
+ }
+
+ *out = clone;
+
+ return OK;
+ } else {
+ // Whole NAL units are returned but each fragment is prefixed by
+ // the start code (0x00 00 00 01).
+
+ ssize_t num_bytes_read =
+ mDataSource->read_at(offset, mSrcBuffer, size);
+
+ if (num_bytes_read < (ssize_t)size) {
+ mBuffer->release();
+ mBuffer = NULL;
+
+ return ERROR_IO;
+ }
+
+ uint8_t *dstData = (uint8_t *)mBuffer->data();
+ size_t srcOffset = 0;
+ size_t dstOffset = 0;
+ while (srcOffset < size) {
+ CHECK(srcOffset + 1 < size);
+ size_t nalLength =
+ (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
+ CHECK(srcOffset + 1 + nalLength < size);
+ srcOffset += 2;
+
+ if (nalLength == 0) {
+ continue;
+ }
+
+ CHECK(dstOffset + 4 <= mBuffer->size());
+
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 0;
+ dstData[dstOffset++] = 1;
+ memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+ srcOffset += nalLength;
+ dstOffset += nalLength;
+ }
+
+ mBuffer->set_range(0, dstOffset);
+ mBuffer->meta_data()->clear();
+ mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+ mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+ ++mCurrentSampleIndex;
+
+ *out = mBuffer;
+ mBuffer = NULL;
+
+ return OK;
+ }
+}
+
+bool SniffMPEG4(
+ const sp<DataSource> &source, String8 *mimeType, float *confidence) {
+ uint8_t header[8];
+
+ ssize_t n = source->read_at(4, header, sizeof(header));
+ if (n < (ssize_t)sizeof(header)) {
+ return false;
+ }
+
+ if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+ || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) {
+ *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
+ *confidence = 0.1;
+
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
new file mode 100644
index 0000000..fa35768
--- /dev/null
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <pthread.h>
+
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+class MPEG4Writer::Track {
+public:
+ Track(MPEG4Writer *owner, const sp<MediaSource> &source);
+ ~Track();
+
+ status_t start();
+ void stop();
+ bool reachedEOS();
+
+ int64_t getDuration() const;
+ void writeTrackHeader(int32_t trackID);
+
+private:
+ MPEG4Writer *mOwner;
+ sp<MetaData> mMeta;
+ sp<MediaSource> mSource;
+ volatile bool mDone;
+
+ pthread_t mThread;
+
+ struct SampleInfo {
+ size_t size;
+ off_t offset;
+ int64_t timestamp;
+ };
+ List<SampleInfo> mSampleInfos;
+
+ void *mCodecSpecificData;
+ size_t mCodecSpecificDataSize;
+
+ bool mReachedEOS;
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ Track(const Track &);
+ Track &operator=(const Track &);
+};
+
+MPEG4Writer::MPEG4Writer(const char *filename)
+ : mFile(fopen(filename, "wb")),
+ mOffset(0),
+ mMdatOffset(0) {
+ CHECK(mFile != NULL);
+}
+
+MPEG4Writer::~MPEG4Writer() {
+ stop();
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ delete *it;
+ }
+ mTracks.clear();
+}
+
+void MPEG4Writer::addSource(const sp<MediaSource> &source) {
+ Track *track = new Track(this, source);
+ mTracks.push_back(track);
+}
+
+status_t MPEG4Writer::start() {
+ if (mFile == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ beginBox("ftyp");
+ writeFourcc("isom");
+ writeInt32(0);
+ writeFourcc("isom");
+ endBox();
+
+ mMdatOffset = mOffset;
+ write("\x00\x00\x00\x01mdat????????", 16);
+
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ status_t err = (*it)->start();
+
+ if (err != OK) {
+ for (List<Track *>::iterator it2 = mTracks.begin();
+ it2 != it; ++it2) {
+ (*it2)->stop();
+ }
+
+ return err;
+ }
+ }
+
+ return OK;
+}
+
+void MPEG4Writer::stop() {
+ if (mFile == NULL) {
+ return;
+ }
+
+ int64_t max_duration = 0;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ (*it)->stop();
+
+ int64_t duration = (*it)->getDuration();
+ if (duration > max_duration) {
+ max_duration = duration;
+ }
+ }
+
+ // Fix up the size of the 'mdat' chunk.
+ fseek(mFile, mMdatOffset + 8, SEEK_SET);
+ int64_t size = mOffset - mMdatOffset;
+ size = hton64(size);
+ fwrite(&size, 1, 8, mFile);
+ fseek(mFile, mOffset, SEEK_SET);
+
+ time_t now = time(NULL);
+
+ beginBox("moov");
+
+ beginBox("mvhd");
+ writeInt32(0); // version=0, flags=0
+ writeInt32(now); // creation time
+ writeInt32(now); // modification time
+ writeInt32(1000); // timescale
+ writeInt32(max_duration);
+ writeInt32(0x10000); // rate
+ writeInt16(0x100); // volume
+ writeInt16(0); // reserved
+ writeInt32(0); // reserved
+ writeInt32(0); // reserved
+ writeInt32(0x10000); // matrix
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0x10000);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0);
+ writeInt32(0x40000000);
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(0); // predefined
+ writeInt32(mTracks.size() + 1); // nextTrackID
+ endBox(); // mvhd
+
+ int32_t id = 1;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it, ++id) {
+ (*it)->writeTrackHeader(id);
+ }
+ endBox(); // moov
+
+ CHECK(mBoxes.empty());
+
+ fclose(mFile);
+ mFile = NULL;
+}
+
+off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ off_t old_offset = mOffset;
+
+ fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+ 1, buffer->range_length(), mFile);
+
+ mOffset += buffer->range_length();
+
+ return old_offset;
+}
+
+void MPEG4Writer::beginBox(const char *fourcc) {
+ CHECK_EQ(strlen(fourcc), 4);
+
+ mBoxes.push_back(mOffset);
+
+ writeInt32(0);
+ writeFourcc(fourcc);
+}
+
+void MPEG4Writer::endBox() {
+ CHECK(!mBoxes.empty());
+
+ off_t offset = *--mBoxes.end();
+ mBoxes.erase(--mBoxes.end());
+
+ fseek(mFile, offset, SEEK_SET);
+ writeInt32(mOffset - offset);
+ mOffset -= 4;
+ fseek(mFile, mOffset, SEEK_SET);
+}
+
+void MPEG4Writer::writeInt8(int8_t x) {
+ fwrite(&x, 1, 1, mFile);
+ ++mOffset;
+}
+
+void MPEG4Writer::writeInt16(int16_t x) {
+ x = htons(x);
+ fwrite(&x, 1, 2, mFile);
+ mOffset += 2;
+}
+
+void MPEG4Writer::writeInt32(int32_t x) {
+ x = htonl(x);
+ fwrite(&x, 1, 4, mFile);
+ mOffset += 4;
+}
+
+void MPEG4Writer::writeInt64(int64_t x) {
+ x = hton64(x);
+ fwrite(&x, 1, 8, mFile);
+ mOffset += 8;
+}
+
+void MPEG4Writer::writeCString(const char *s) {
+ size_t n = strlen(s);
+
+ fwrite(s, 1, n + 1, mFile);
+ mOffset += n + 1;
+}
+
+void MPEG4Writer::writeFourcc(const char *s) {
+ CHECK_EQ(strlen(s), 4);
+ fwrite(s, 1, 4, mFile);
+ mOffset += 4;
+}
+
+void MPEG4Writer::write(const void *data, size_t size) {
+ fwrite(data, 1, size, mFile);
+ mOffset += size;
+}
+
+bool MPEG4Writer::reachedEOS() {
+ bool allDone = true;
+ for (List<Track *>::iterator it = mTracks.begin();
+ it != mTracks.end(); ++it) {
+ if (!(*it)->reachedEOS()) {
+ allDone = false;
+ break;
+ }
+ }
+
+ return allDone;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Writer::Track::Track(
+ MPEG4Writer *owner, const sp<MediaSource> &source)
+ : mOwner(owner),
+ mMeta(source->getFormat()),
+ mSource(source),
+ mDone(false),
+ mCodecSpecificData(NULL),
+ mCodecSpecificDataSize(0),
+ mReachedEOS(false) {
+}
+
+MPEG4Writer::Track::~Track() {
+ stop();
+
+ if (mCodecSpecificData != NULL) {
+ free(mCodecSpecificData);
+ mCodecSpecificData = NULL;
+ }
+}
+
+status_t MPEG4Writer::Track::start() {
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ mDone = mReachedEOS = true;
+ return err;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mDone = false;
+ mReachedEOS = false;
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ return OK;
+}
+
+void MPEG4Writer::Track::stop() {
+ if (mDone) {
+ return;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ mSource->stop();
+}
+
+bool MPEG4Writer::Track::reachedEOS() {
+ return mReachedEOS;
+}
+
+// static
+void *MPEG4Writer::Track::ThreadWrapper(void *me) {
+ Track *track = static_cast<Track *>(me);
+
+ track->threadEntry();
+
+ return NULL;
+}
+
+void MPEG4Writer::Track::threadEntry() {
+ bool is_mpeg4 = false;
+ sp<MetaData> meta = mSource->getFormat();
+ const char *mime;
+ meta->findCString(kKeyMIMEType, &mime);
+ is_mpeg4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+ MediaBuffer *buffer;
+ while (!mDone && mSource->read(&buffer) == OK) {
+ if (buffer->range_length() == 0) {
+ buffer->release();
+ buffer = NULL;
+
+ continue;
+ }
+
+ if (mCodecSpecificData == NULL && is_mpeg4) {
+ const uint8_t *data =
+ (const uint8_t *)buffer->data() + buffer->range_offset();
+
+ const size_t size = buffer->range_length();
+
+ size_t offset = 0;
+ while (offset + 3 < size) {
+ if (data[offset] == 0x00 && data[offset + 1] == 0x00
+ && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
+ break;
+ }
+
+ ++offset;
+ }
+
+ // CHECK(offset + 3 < size);
+ if (offset + 3 >= size) {
+ // XXX assume the entire first chunk of data is the codec specific
+ // data.
+ offset = size;
+ }
+
+ mCodecSpecificDataSize = offset;
+ mCodecSpecificData = malloc(offset);
+ memcpy(mCodecSpecificData, data, offset);
+
+ buffer->set_range(buffer->range_offset() + offset, size - offset);
+ }
+
+ off_t offset = mOwner->addSample(buffer);
+
+ SampleInfo info;
+ info.size = buffer->range_length();
+ info.offset = offset;
+
+ int32_t units, scale;
+ bool success =
+ buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ CHECK(success);
+ success =
+ buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ CHECK(success);
+
+ info.timestamp = (int64_t)units * 1000 / scale;
+
+ mSampleInfos.push_back(info);
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ mReachedEOS = true;
+}
+
+int64_t MPEG4Writer::Track::getDuration() const {
+ return 10000; // XXX
+}
+
+void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
+ const char *mime;
+ bool success = mMeta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ bool is_audio = !strncasecmp(mime, "audio/", 6);
+
+ time_t now = time(NULL);
+
+ mOwner->beginBox("trak");
+
+ mOwner->beginBox("tkhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(trackID);
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(getDuration());
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // layer
+ mOwner->writeInt16(0); // alternate group
+ mOwner->writeInt16(is_audio ? 0x100 : 0); // volume
+ mOwner->writeInt16(0); // reserved
+
+ mOwner->writeInt32(0x10000); // matrix
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0x10000);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0x40000000);
+
+ if (is_audio) {
+ mOwner->writeInt32(0);
+ mOwner->writeInt32(0);
+ } else {
+ int32_t width, height;
+ bool success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+
+ mOwner->writeInt32(width);
+ mOwner->writeInt32(height);
+ }
+ mOwner->endBox(); // tkhd
+
+ mOwner->beginBox("mdia");
+
+ mOwner->beginBox("mdhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(now); // creation time
+ mOwner->writeInt32(now); // modification time
+ mOwner->writeInt32(1000); // timescale
+ mOwner->writeInt32(getDuration());
+ mOwner->writeInt16(0); // language code XXX
+ mOwner->writeInt16(0); // predefined
+ mOwner->endBox();
+
+ mOwner->beginBox("hdlr");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeFourcc(is_audio ? "soun" : "vide");
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeCString(""); // name
+ mOwner->endBox();
+
+ mOwner->beginBox("minf");
+
+ mOwner->beginBox("dinf");
+ mOwner->beginBox("dref");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1);
+ mOwner->beginBox("url ");
+ mOwner->writeInt32(1); // version=0, flags=1
+ mOwner->endBox(); // url
+ mOwner->endBox(); // dref
+ mOwner->endBox(); // dinf
+
+ if (is_audio) {
+ mOwner->beginBox("smhd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt16(0); // balance
+ mOwner->writeInt16(0); // reserved
+ mOwner->endBox();
+ } else {
+ mOwner->beginBox("vmhd");
+ mOwner->writeInt32(0x00000001); // version=0, flags=1
+ mOwner->writeInt16(0); // graphics mode
+ mOwner->writeInt16(0); // opcolor
+ mOwner->writeInt16(0);
+ mOwner->writeInt16(0);
+ mOwner->endBox();
+ }
+ mOwner->endBox(); // minf
+
+ mOwner->beginBox("stbl");
+
+ mOwner->beginBox("stsd");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(1); // entry count
+ if (is_audio) {
+ const char *fourcc = NULL;
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+ fourcc = "samr";
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ fourcc = "sawb";
+ } else {
+ LOGE("Unknown mime type '%s'.", mime);
+ CHECK(!"should not be here, unknown mime type.");
+ }
+
+ mOwner->beginBox(fourcc); // audio format
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(0); // data ref index
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(2); // channel count
+ mOwner->writeInt16(16); // sample size
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+
+ int32_t samplerate;
+ bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
+ CHECK(success);
+
+ mOwner->writeInt32(samplerate << 16);
+ mOwner->endBox();
+ } else {
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ mOwner->beginBox("mp4v");
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ mOwner->beginBox("s263");
+ } else {
+ LOGE("Unknown mime type '%s'.", mime);
+ CHECK(!"should not be here, unknown mime type.");
+ }
+
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt16(0); // data ref index
+ mOwner->writeInt16(0); // predefined
+ mOwner->writeInt16(0); // reserved
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+ mOwner->writeInt32(0); // predefined
+
+ int32_t width, height;
+ bool success = mMeta->findInt32(kKeyWidth, &width);
+ success = success && mMeta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+
+ mOwner->writeInt16(width);
+ mOwner->writeInt16(height);
+ mOwner->writeInt32(0x480000); // horiz resolution
+ mOwner->writeInt32(0x480000); // vert resolution
+ mOwner->writeInt32(0); // reserved
+ mOwner->writeInt16(1); // frame count
+ mOwner->write(" ", 32);
+ mOwner->writeInt16(0x18); // depth
+ mOwner->writeInt16(-1); // predefined
+
+ CHECK(23 + mCodecSpecificDataSize < 128);
+
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ mOwner->beginBox("esds");
+
+ mOwner->writeInt32(0); // version=0, flags=0
+
+ mOwner->writeInt8(0x03); // ES_DescrTag
+ mOwner->writeInt8(23 + mCodecSpecificDataSize);
+ mOwner->writeInt16(0x0000); // ES_ID
+ mOwner->writeInt8(0x1f);
+
+ mOwner->writeInt8(0x04); // DecoderConfigDescrTag
+ mOwner->writeInt8(15 + mCodecSpecificDataSize);
+ mOwner->writeInt8(0x20); // objectTypeIndication ISO/IEC 14492-2
+ mOwner->writeInt8(0x11); // streamType VisualStream
+
+ static const uint8_t kData[] = {
+ 0x01, 0x77, 0x00,
+ 0x00, 0x03, 0xe8, 0x00,
+ 0x00, 0x03, 0xe8, 0x00
+ };
+ mOwner->write(kData, sizeof(kData));
+
+ mOwner->writeInt8(0x05); // DecoderSpecificInfoTag
+
+ mOwner->writeInt8(mCodecSpecificDataSize);
+ mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+ static const uint8_t kData2[] = {
+ 0x06, // SLConfigDescriptorTag
+ 0x01,
+ 0x02
+ };
+ mOwner->write(kData2, sizeof(kData2));
+
+ mOwner->endBox(); // esds
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ mOwner->beginBox("d263");
+
+ mOwner->writeInt32(0); // vendor
+ mOwner->writeInt8(0); // decoder version
+ mOwner->writeInt8(10); // level: 10
+ mOwner->writeInt8(0); // profile: 0
+
+ mOwner->endBox(); // d263
+ }
+ mOwner->endBox(); // mp4v or s263
+ }
+ mOwner->endBox(); // stsd
+
+ mOwner->beginBox("stts");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size() - 1);
+
+ List<SampleInfo>::iterator it = mSampleInfos.begin();
+ int64_t last = (*it).timestamp;
+ ++it;
+ while (it != mSampleInfos.end()) {
+ mOwner->writeInt32(1);
+ mOwner->writeInt32((*it).timestamp - last);
+
+ last = (*it).timestamp;
+
+ ++it;
+ }
+ mOwner->endBox(); // stts
+
+ mOwner->beginBox("stsz");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(0); // default sample size
+ mOwner->writeInt32(mSampleInfos.size());
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it) {
+ mOwner->writeInt32((*it).size);
+ }
+ mOwner->endBox(); // stsz
+
+ mOwner->beginBox("stsc");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size());
+ int32_t n = 1;
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it, ++n) {
+ mOwner->writeInt32(n);
+ mOwner->writeInt32(1);
+ mOwner->writeInt32(1);
+ }
+ mOwner->endBox(); // stsc
+
+ mOwner->beginBox("co64");
+ mOwner->writeInt32(0); // version=0, flags=0
+ mOwner->writeInt32(mSampleInfos.size());
+ for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+ it != mSampleInfos.end(); ++it, ++n) {
+ mOwner->writeInt64((*it).offset);
+ }
+ mOwner->endBox(); // co64
+
+ mOwner->endBox(); // stbl
+ mOwner->endBox(); // mdia
+ mOwner->endBox(); // trak
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
new file mode 100644
index 0000000..f3c0e73
--- /dev/null
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBuffer"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+// XXX make this truly atomic.
+static int atomic_add(int *value, int delta) {
+ int prev_value = *value;
+ *value += delta;
+
+ return prev_value;
+}
+
+MediaBuffer::MediaBuffer(void *data, size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(data),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(size_t size)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(malloc(size)),
+ mSize(size),
+ mRangeOffset(0),
+ mRangeLength(size),
+ mOwnsData(true),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
+void MediaBuffer::release() {
+ if (mObserver == NULL) {
+ CHECK_EQ(mRefCount, 0);
+ delete this;
+ return;
+ }
+
+ int prevCount = atomic_add(&mRefCount, -1);
+ if (prevCount == 1) {
+ if (mObserver == NULL) {
+ delete this;
+ return;
+ }
+
+ mObserver->signalBufferReturned(this);
+ }
+ CHECK(prevCount > 0);
+}
+
+void MediaBuffer::claim() {
+ CHECK(mObserver != NULL);
+ CHECK_EQ(mRefCount, 1);
+
+ mRefCount = 0;
+}
+
+void MediaBuffer::add_ref() {
+ atomic_add(&mRefCount, 1);
+}
+
+void *MediaBuffer::data() const {
+ return mData;
+}
+
+size_t MediaBuffer::size() const {
+ return mSize;
+}
+
+size_t MediaBuffer::range_offset() const {
+ return mRangeOffset;
+}
+
+size_t MediaBuffer::range_length() const {
+ return mRangeLength;
+}
+
+void MediaBuffer::set_range(size_t offset, size_t length) {
+ if (offset < 0 || offset + length > mSize) {
+ LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+ }
+ CHECK(offset >= 0 && offset + length <= mSize);
+
+ mRangeOffset = offset;
+ mRangeLength = length;
+}
+
+sp<MetaData> MediaBuffer::meta_data() {
+ return mMetaData;
+}
+
+void MediaBuffer::reset() {
+ mMetaData->clear();
+ set_range(0, mSize);
+}
+
+MediaBuffer::~MediaBuffer() {
+ CHECK_EQ(mObserver, NULL);
+
+ if (mOwnsData && mData != NULL) {
+ free(mData);
+ mData = NULL;
+ }
+
+ if (mOriginal != NULL) {
+ mOriginal->release();
+ mOriginal = NULL;
+ }
+}
+
+void MediaBuffer::setObserver(MediaBufferObserver *observer) {
+ CHECK(observer == NULL || mObserver == NULL);
+ mObserver = observer;
+}
+
+void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
+ mNextBuffer = buffer;
+}
+
+MediaBuffer *MediaBuffer::nextBuffer() {
+ return mNextBuffer;
+}
+
+int MediaBuffer::refcount() const {
+ return mRefCount;
+}
+
+MediaBuffer *MediaBuffer::clone() {
+ MediaBuffer *buffer = new MediaBuffer(mData, mSize);
+ buffer->set_range(mRangeOffset, mRangeLength);
+ buffer->mMetaData = new MetaData(*mMetaData.get());
+
+ add_ref();
+ buffer->mOriginal = this;
+
+ return buffer;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
new file mode 100644
index 0000000..c8d05f4
--- /dev/null
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MediaBufferGroup"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+
+namespace android {
+
+MediaBufferGroup::MediaBufferGroup()
+ : mFirstBuffer(NULL),
+ mLastBuffer(NULL) {
+}
+
+MediaBufferGroup::~MediaBufferGroup() {
+ MediaBuffer *next;
+ for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+ buffer = next) {
+ next = buffer->nextBuffer();
+
+ CHECK_EQ(buffer->refcount(), 0);
+
+ buffer->setObserver(NULL);
+ buffer->release();
+ }
+}
+
+void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ buffer->setObserver(this);
+
+ if (mLastBuffer) {
+ mLastBuffer->setNextBuffer(buffer);
+ } else {
+ mFirstBuffer = buffer;
+ }
+
+ mLastBuffer = buffer;
+}
+
+status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
+ Mutex::Autolock autoLock(mLock);
+
+ for (;;) {
+ for (MediaBuffer *buffer = mFirstBuffer;
+ buffer != NULL; buffer = buffer->nextBuffer()) {
+ if (buffer->refcount() == 0) {
+ buffer->add_ref();
+ buffer->reset();
+
+ *out = buffer;
+ goto exit;
+ }
+ }
+
+ // All buffers are in use. Block until one of them is returned to us.
+ mCondition.wait(mLock);
+ }
+
+exit:
+ return OK;
+}
+
+void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+ Mutex::Autolock autoLock(mLock);
+ mCondition.signal();
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp
new file mode 100644
index 0000000..87b5b24
--- /dev/null
+++ b/media/libstagefright/MediaDefs.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaDefs.h>
+
+namespace android {
+
+const char *MEDIA_MIMETYPE_IMAGE_JPEG = "image/jpeg";
+
+const char *MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char *MEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char *MEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+const char *MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
+
+const char *MEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char *MEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg";
+const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw";
+
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
+
+} // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
new file mode 100644
index 0000000..8535f52
--- /dev/null
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/AMRExtractor.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// static
+sp<MediaExtractor> MediaExtractor::Create(
+ const sp<DataSource> &source, const char *mime) {
+ String8 tmp;
+ if (mime == NULL) {
+ float confidence;
+ if (!source->sniff(&tmp, &confidence)) {
+ LOGE("FAILED to autodetect media content.");
+
+ return NULL;
+ }
+
+ mime = tmp.string();
+ LOGI("Autodetected media content as '%s' with confidence %.2f",
+ mime, confidence);
+ }
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
+ || !strcasecmp(mime, "audio/mp4")) {
+ return new MPEG4Extractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ return new MP3Extractor(source);
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
+ || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
+ return new AMRExtractor(source);
+ }
+
+ return NULL;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
new file mode 100644
index 0000000..2e609e3
--- /dev/null
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaPlayerImpl"
+#include "utils/Log.h"
+
+#include <OMX_Component.h>
+
+#include <unistd.h>
+
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CachingDataSource.h>
+// #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/TimeSource.h>
+#include <ui/PixelFormat.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+MediaPlayerImpl::MediaPlayerImpl(const char *uri)
+ : mInitCheck(NO_INIT),
+ mTimeSource(NULL),
+ mAudioPlayer(NULL),
+ mVideoWidth(0),
+ mVideoHeight(0),
+ mVideoPosition(0),
+ mDuration(0),
+ mPlaying(false),
+ mPaused(false),
+ mSeeking(false) {
+ LOGI("MediaPlayerImpl(%s)", uri);
+ DataSource::RegisterDefaultSniffers();
+
+ status_t err = mClient.connect();
+ if (err != OK) {
+ LOGE("Failed to connect to OMXClient.");
+ return;
+ }
+
+ if (!strncasecmp("shoutcast://", uri, 12)) {
+ setAudioSource(makeShoutcastSource(uri));
+#if 0
+ } else if (!strncasecmp("camera:", uri, 7)) {
+ mVideoWidth = 480;
+ mVideoHeight = 320;
+ mVideoDecoder = CameraSource::Create();
+#endif
+ } else {
+ sp<DataSource> source;
+ if (!strncasecmp("file://", uri, 7)) {
+ source = new MmapSource(uri + 7);
+ } else if (!strncasecmp("http://", uri, 7)) {
+ source = new HTTPDataSource(uri);
+ source = new CachingDataSource(source, 64 * 1024, 10);
+ } else {
+ // Assume it's a filename.
+ source = new MmapSource(uri);
+ }
+
+ mExtractor = MediaExtractor::Create(source);
+
+ if (mExtractor == NULL) {
+ return;
+ }
+ }
+
+ init();
+
+ mInitCheck = OK;
+}
+
+MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
+ : mInitCheck(NO_INIT),
+ mTimeSource(NULL),
+ mAudioPlayer(NULL),
+ mVideoWidth(0),
+ mVideoHeight(0),
+ mVideoPosition(0),
+ mDuration(0),
+ mPlaying(false),
+ mPaused(false),
+ mSeeking(false) {
+ LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
+ DataSource::RegisterDefaultSniffers();
+
+ status_t err = mClient.connect();
+ if (err != OK) {
+ LOGE("Failed to connect to OMXClient.");
+ return;
+ }
+
+ mExtractor = MediaExtractor::Create(
+ new MmapSource(fd, offset, length));
+
+ if (mExtractor == NULL) {
+ return;
+ }
+
+ init();
+
+ mInitCheck = OK;
+}
+
+status_t MediaPlayerImpl::initCheck() const {
+ return mInitCheck;
+}
+
+MediaPlayerImpl::~MediaPlayerImpl() {
+ stop();
+ setSurface(NULL);
+
+ if (mInitCheck == OK) {
+ mClient.disconnect();
+ }
+
+ LOGV("~MediaPlayerImpl done.");
+}
+
+void MediaPlayerImpl::play() {
+ LOGI("play");
+
+ if (mPlaying) {
+ if (mPaused) {
+ if (mAudioSource != NULL) {
+ mAudioPlayer->resume();
+ }
+ mPaused = false;
+ }
+ return;
+ }
+
+ mPlaying = true;
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer = new AudioPlayer(mAudioSink);
+ mAudioPlayer->setSource(mAudioDecoder);
+ mAudioPlayer->start();
+ mTimeSource = mAudioPlayer;
+ } else {
+ mTimeSource = new SystemTimeSource;
+ }
+
+ if (mVideoDecoder != NULL) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mVideoThread, &attr, VideoWrapper, this);
+
+ pthread_attr_destroy(&attr);
+ }
+}
+
+void MediaPlayerImpl::pause() {
+ if (!mPlaying || mPaused) {
+ return;
+ }
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer->pause();
+ }
+
+ mPaused = true;
+}
+
+void MediaPlayerImpl::stop() {
+ if (!mPlaying) {
+ return;
+ }
+
+ mPlaying = false;
+
+ if (mVideoDecoder != NULL) {
+ void *dummy;
+ pthread_join(mVideoThread, &dummy);
+ }
+
+ if (mAudioSource != NULL) {
+ mAudioPlayer->stop();
+
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+ } else {
+ delete mTimeSource;
+ }
+
+ mTimeSource = NULL;
+}
+
+// static
+void *MediaPlayerImpl::VideoWrapper(void *me) {
+ ((MediaPlayerImpl *)me)->videoEntry();
+
+ return NULL;
+}
+
+void MediaPlayerImpl::videoEntry() {
+ bool firstFrame = true;
+ bool eof = false;
+
+ status_t err = mVideoDecoder->start();
+ CHECK_EQ(err, OK);
+
+ while (mPlaying) {
+ MediaBuffer *buffer;
+
+ MediaSource::ReadOptions options;
+ bool seeking = false;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mSeeking) {
+ LOGI("seek-options to %lld", mSeekTimeUs);
+ options.setSeekTo(mSeekTimeUs);
+
+ mSeeking = false;
+ seeking = true;
+ eof = false;
+ }
+ }
+
+ if (eof || mPaused) {
+ usleep(100000);
+ continue;
+ }
+
+ status_t err = mVideoDecoder->read(&buffer, &options);
+ CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
+
+ if (err == ERROR_END_OF_STREAM || err != OK) {
+ eof = true;
+ continue;
+ }
+
+ if (buffer->range_length() == 0) {
+ // The final buffer is empty.
+ buffer->release();
+ continue;
+ }
+
+ int32_t units, scale;
+ bool success =
+ buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+ CHECK(success);
+ success =
+ buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+ CHECK(success);
+
+ int64_t pts_us = (int64_t)units * 1000000 / scale;
+ {
+ Mutex::Autolock autoLock(mLock);
+ mVideoPosition = pts_us;
+
+ LOGV("now_video = %.2f secs (%lld ms)",
+ pts_us / 1E6, (pts_us + 500) / 1000);
+ }
+
+ if (seeking && mAudioPlayer != NULL) {
+ // Now that we know where exactly video seeked (taking sync-samples
+ // into account), we will seek the audio track to the same time.
+ mAudioPlayer->seekTo(pts_us);
+ }
+
+ if (firstFrame || seeking) {
+ mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
+ firstFrame = false;
+ }
+
+ displayOrDiscardFrame(buffer, pts_us);
+ }
+
+ mVideoDecoder->stop();
+}
+
+void MediaPlayerImpl::displayOrDiscardFrame(
+ MediaBuffer *buffer, int64_t pts_us) {
+ for (;;) {
+ if (!mPlaying || mPaused) {
+ buffer->release();
+ buffer = NULL;
+
+ return;
+ }
+
+ int64_t realtime_us, mediatime_us;
+ if (mAudioPlayer != NULL
+ && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
+ mTimeSourceDeltaUs = realtime_us - mediatime_us;
+ LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6);
+ }
+
+ int64_t now_us = mTimeSource->getRealTimeUs();
+ now_us -= mTimeSourceDeltaUs;
+
+ int64_t delay_us = pts_us - now_us;
+
+ if (delay_us < -15000) {
+ // We're late.
+
+ LOGI("we're late by %lld ms, dropping a frame\n",
+ -delay_us / 1000);
+
+ buffer->release();
+ buffer = NULL;
+ return;
+ } else if (delay_us > 100000) {
+ LOGI("we're much too early (by %lld ms)\n",
+ delay_us / 1000);
+ usleep(100000);
+ continue;
+ } else if (delay_us > 0) {
+ usleep(delay_us);
+ }
+
+ break;
+ }
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mVideoRenderer.get() != NULL) {
+ sendFrameToISurface(buffer);
+ }
+ }
+
+ buffer->release();
+ buffer = NULL;
+}
+
+void MediaPlayerImpl::init() {
+ if (mExtractor != NULL) {
+ size_t num_tracks = mExtractor->countTracks();
+
+ mDuration = 0;
+
+ for (size_t i = 0; i < num_tracks; ++i) {
+ const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+ CHECK(meta != NULL);
+
+ const char *mime;
+ if (!meta->findCString(kKeyMIMEType, &mime)) {
+ continue;
+ }
+
+ bool is_audio = false;
+ bool is_acceptable = false;
+ if (!strncasecmp(mime, "audio/", 6)) {
+ is_audio = true;
+ is_acceptable = (mAudioSource == NULL);
+ } else if (!strncasecmp(mime, "video/", 6)) {
+ is_acceptable = (mVideoSource == NULL);
+ }
+
+ if (!is_acceptable) {
+ continue;
+ }
+
+ sp<MediaSource> source = mExtractor->getTrack(i);
+
+ int32_t units, scale;
+ if (meta->findInt32(kKeyDuration, &units)
+ && meta->findInt32(kKeyTimeScale, &scale)) {
+ int64_t duration_us = (int64_t)units * 1000000 / scale;
+ if (duration_us > mDuration) {
+ mDuration = duration_us;
+ }
+ }
+
+ if (is_audio) {
+ setAudioSource(source);
+ } else {
+ setVideoSource(source);
+ }
+ }
+ }
+}
+
+void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) {
+ LOGI("setAudioSource");
+ mAudioSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ mAudioDecoder = OMXCodec::Create(
+ mClient.interface(), meta, false /* createEncoder */, source);
+}
+
+void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) {
+ LOGI("setVideoSource");
+ mVideoSource = source;
+
+ sp<MetaData> meta = source->getFormat();
+
+ bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
+ CHECK(success);
+
+ success = meta->findInt32(kKeyHeight, &mVideoHeight);
+ CHECK(success);
+
+ mVideoDecoder = OMXCodec::Create(
+ mClient.interface(), meta, false /* createEncoder */, source);
+
+ if (mISurface.get() != NULL || mSurface.get() != NULL) {
+ depopulateISurface();
+ populateISurface();
+ }
+}
+
+void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
+ LOGI("setSurface %p", surface.get());
+ Mutex::Autolock autoLock(mLock);
+
+ depopulateISurface();
+
+ mSurface = surface;
+ mISurface = NULL;
+
+ if (mSurface.get() != NULL) {
+ populateISurface();
+ }
+}
+
+void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
+ LOGI("setISurface %p", isurface.get());
+ Mutex::Autolock autoLock(mLock);
+
+ depopulateISurface();
+
+ mSurface = NULL;
+ mISurface = isurface;
+
+ if (mISurface.get() != NULL) {
+ populateISurface();
+ }
+}
+
+MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
+ if (strncasecmp(uri, "shoutcast://", 12)) {
+ return NULL;
+ }
+
+ string host;
+ string path;
+ int port;
+
+ char *slash = strchr(uri + 12, '/');
+ if (slash == NULL) {
+ host = uri + 12;
+ path = "/";
+ } else {
+ host = string(uri + 12, slash - (uri + 12));
+ path = slash;
+ }
+
+ char *colon = strchr(host.c_str(), ':');
+ if (colon == NULL) {
+ port = 80;
+ } else {
+ char *end;
+ long tmp = strtol(colon + 1, &end, 10);
+ CHECK(end > colon + 1);
+ CHECK(tmp > 0 && tmp < 65536);
+ port = tmp;
+
+ host = string(host, 0, colon - host.c_str());
+ }
+
+ LOGI("Connecting to host '%s', port %d, path '%s'",
+ host.c_str(), port, path.c_str());
+
+ HTTPStream *http = new HTTPStream;
+ int http_status;
+
+ for (;;) {
+ status_t err = http->connect(host.c_str(), port);
+ CHECK_EQ(err, OK);
+
+ err = http->send("GET ");
+ err = http->send(path.c_str());
+ err = http->send(" HTTP/1.1\r\n");
+ err = http->send("Host: ");
+ err = http->send(host.c_str());
+ err = http->send("\r\n");
+ err = http->send("Icy-MetaData: 1\r\n\r\n");
+
+ CHECK_EQ(OK, http->receive_header(&http_status));
+
+ if (http_status == 301 || http_status == 302) {
+ string location;
+ CHECK(http->find_header_value("Location", &location));
+
+ CHECK(string(location, 0, 7) == "http://");
+ location.erase(0, 7);
+ string::size_type slashPos = location.find('/');
+ if (slashPos == string::npos) {
+ slashPos = location.size();
+ location += '/';
+ }
+
+ http->disconnect();
+
+ LOGI("Redirecting to %s\n", location.c_str());
+
+ host = string(location, 0, slashPos);
+
+ string::size_type colonPos = host.find(':');
+ if (colonPos != string::npos) {
+ const char *start = host.c_str() + colonPos + 1;
+ char *end;
+ long tmp = strtol(start, &end, 10);
+ CHECK(end > start && (*end == '\0'));
+
+ port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
+ } else {
+ port = 80;
+ }
+
+ path = string(location, slashPos);
+
+ continue;
+ }
+
+ break;
+ }
+
+ if (http_status != 200) {
+ LOGE("Connection failed: http_status = %d", http_status);
+ return NULL;
+ }
+
+ MediaSource *source = new ShoutcastSource(http);
+
+ return source;
+}
+
+bool MediaPlayerImpl::isPlaying() const {
+ return mPlaying && !mPaused;
+}
+
+int64_t MediaPlayerImpl::getDuration() {
+ return mDuration;
+}
+
+int64_t MediaPlayerImpl::getPosition() {
+ int64_t position = 0;
+ if (mVideoSource != NULL) {
+ Mutex::Autolock autoLock(mLock);
+ position = mVideoPosition;
+ } else if (mAudioPlayer != NULL) {
+ position = mAudioPlayer->getMediaTimeUs();
+ }
+
+ return position;
+}
+
+status_t MediaPlayerImpl::seekTo(int64_t time) {
+ LOGI("seekTo %lld", time);
+
+ if (mPaused) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mVideoSource == NULL && mAudioPlayer != NULL) {
+ mAudioPlayer->seekTo(time);
+ } else {
+ Mutex::Autolock autoLock(mLock);
+ mSeekTimeUs = time;
+ mSeeking = true;
+ }
+
+ return OK;
+}
+
+void MediaPlayerImpl::populateISurface() {
+ if (mVideoSource == NULL) {
+ return;
+ }
+
+ sp<MetaData> meta = mVideoDecoder->getFormat();
+
+ int32_t format;
+ const char *component;
+ int32_t decodedWidth, decodedHeight;
+ bool success = meta->findInt32(kKeyColorFormat, &format);
+ success = success && meta->findCString(kKeyDecoderComponent, &component);
+ success = success && meta->findInt32(kKeyWidth, &decodedWidth);
+ success = success && meta->findInt32(kKeyHeight, &decodedHeight);
+ CHECK(success);
+
+ if (mSurface.get() != NULL) {
+ mVideoRenderer =
+ mClient.interface()->createRenderer(
+ mSurface, component,
+ (OMX_COLOR_FORMATTYPE)format,
+ decodedWidth, decodedHeight,
+ mVideoWidth, mVideoHeight);
+ } else {
+ mVideoRenderer =
+ mClient.interface()->createRenderer(
+ mISurface, component,
+ (OMX_COLOR_FORMATTYPE)format,
+ decodedWidth, decodedHeight,
+ mVideoWidth, mVideoHeight);
+ }
+}
+
+void MediaPlayerImpl::depopulateISurface() {
+ mVideoRenderer.clear();
+}
+
+void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
+ void *id;
+ if (buffer->meta_data()->findPointer(kKeyBufferID, &id)) {
+ mVideoRenderer->render((IOMX::buffer_id)id);
+ }
+}
+
+void MediaPlayerImpl::setAudioSink(
+ const sp<MediaPlayerBase::AudioSink> &audioSink) {
+ LOGI("setAudioSink %p", audioSink.get());
+ mAudioSink = audioSink;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
new file mode 100644
index 0000000..ec89b74
--- /dev/null
+++ b/media/libstagefright/MediaSource.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+MediaSource::MediaSource() {}
+
+MediaSource::~MediaSource() {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MediaSource::ReadOptions::ReadOptions() {
+ reset();
+}
+
+void MediaSource::ReadOptions::reset() {
+ mOptions = 0;
+ mSeekTimeUs = 0;
+ mLatenessUs = 0;
+}
+
+void MediaSource::ReadOptions::setSeekTo(int64_t time_us) {
+ mOptions |= kSeekTo_Option;
+ mSeekTimeUs = time_us;
+}
+
+void MediaSource::ReadOptions::clearSeekTo() {
+ mOptions &= ~kSeekTo_Option;
+ mSeekTimeUs = 0;
+}
+
+bool MediaSource::ReadOptions::getSeekTo(int64_t *time_us) const {
+ *time_us = mSeekTimeUs;
+ return (mOptions & kSeekTo_Option) != 0;
+}
+
+void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
+ mLatenessUs = lateness_us;
+}
+
+int64_t MediaSource::ReadOptions::getLateBy() const {
+ return mLatenessUs;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
new file mode 100644
index 0000000..6b067cb
--- /dev/null
+++ b/media/libstagefright/MetaData.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MetaData::MetaData() {
+}
+
+MetaData::MetaData(const MetaData &from)
+ : RefBase(),
+ mItems(from.mItems) {
+}
+
+MetaData::~MetaData() {
+ clear();
+}
+
+void MetaData::clear() {
+ mItems.clear();
+}
+
+bool MetaData::remove(uint32_t key) {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ mItems.removeItemsAt(i);
+
+ return true;
+}
+
+bool MetaData::setCString(uint32_t key, const char *value) {
+ return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaData::setInt32(uint32_t key, int32_t value) {
+ return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaData::setFloat(uint32_t key, float value) {
+ return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaData::setPointer(uint32_t key, void *value) {
+ return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaData::findCString(uint32_t key, const char **value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+ return false;
+ }
+
+ *value = (const char *)data;
+
+ return true;
+}
+
+bool MetaData::findInt32(uint32_t key, int32_t *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(int32_t *)data;
+
+ return true;
+}
+
+bool MetaData::findFloat(uint32_t key, float *value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(float *)data;
+
+ return true;
+}
+
+bool MetaData::findPointer(uint32_t key, void **value) {
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+ return false;
+ }
+
+ CHECK_EQ(size, sizeof(*value));
+
+ *value = *(void **)data;
+
+ return true;
+}
+
+bool MetaData::setData(
+ uint32_t key, uint32_t type, const void *data, size_t size) {
+ bool overwrote_existing = true;
+
+ ssize_t i = mItems.indexOfKey(key);
+ if (i < 0) {
+ typed_data item;
+ i = mItems.add(key, item);
+
+ overwrote_existing = false;
+ }
+
+ typed_data &item = mItems.editValueAt(i);
+
+ item.setData(type, data, size);
+
+ return overwrote_existing;
+}
+
+bool MetaData::findData(uint32_t key, uint32_t *type,
+ const void **data, size_t *size) const {
+ ssize_t i = mItems.indexOfKey(key);
+
+ if (i < 0) {
+ return false;
+ }
+
+ const typed_data &item = mItems.valueAt(i);
+
+ item.getData(type, data, size);
+
+ return true;
+}
+
+MetaData::typed_data::typed_data()
+ : mType(0),
+ mSize(0) {
+}
+
+MetaData::typed_data::~typed_data() {
+ clear();
+}
+
+MetaData::typed_data::typed_data(const typed_data &from)
+ : mType(from.mType),
+ mSize(0) {
+ allocateStorage(from.mSize);
+ memcpy(storage(), from.storage(), mSize);
+}
+
+MetaData::typed_data &MetaData::typed_data::operator=(
+ const MetaData::typed_data &from) {
+ if (this != &from) {
+ clear();
+ mType = from.mType;
+ allocateStorage(from.mSize);
+ memcpy(storage(), from.storage(), mSize);
+ }
+
+ return *this;
+}
+
+void MetaData::typed_data::clear() {
+ freeStorage();
+
+ mType = 0;
+}
+
+void MetaData::typed_data::setData(
+ uint32_t type, const void *data, size_t size) {
+ clear();
+
+ mType = type;
+ allocateStorage(size);
+ memcpy(storage(), data, size);
+}
+
+void MetaData::typed_data::getData(
+ uint32_t *type, const void **data, size_t *size) const {
+ *type = mType;
+ *size = mSize;
+ *data = storage();
+}
+
+void MetaData::typed_data::allocateStorage(size_t size) {
+ mSize = size;
+
+ if (usesReservoir()) {
+ return;
+ }
+
+ u.ext_data = malloc(mSize);
+}
+
+void MetaData::typed_data::freeStorage() {
+ if (!usesReservoir()) {
+ if (u.ext_data) {
+ free(u.ext_data);
+ }
+ }
+
+ mSize = 0;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
new file mode 100644
index 0000000..47d95f9
--- /dev/null
+++ b/media/libstagefright/MmapSource.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MmapSource"
+#include <utils/Log.h>
+
+#include <sys/mman.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MmapSource.h>
+
+namespace android {
+
+MmapSource::MmapSource(const char *filename)
+ : mFd(open(filename, O_RDONLY)),
+ mBase(NULL),
+ mSize(0) {
+ LOGV("MmapSource '%s'", filename);
+ CHECK(mFd >= 0);
+
+ off_t size = lseek(mFd, 0, SEEK_END);
+ mSize = (size_t)size;
+
+ mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
+
+ if (mBase == (void *)-1) {
+ mBase = NULL;
+
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
+ : mFd(fd),
+ mBase(NULL),
+ mSize(length) {
+ LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
+ CHECK(fd >= 0);
+
+ mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
+
+ if (mBase == (void *)-1) {
+ mBase = NULL;
+
+ close(mFd);
+ mFd = -1;
+ }
+
+}
+
+MmapSource::~MmapSource() {
+ if (mFd != -1) {
+ munmap(mBase, mSize);
+ mBase = NULL;
+ mSize = 0;
+
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t MmapSource::InitCheck() const {
+ return mFd == -1 ? NO_INIT : OK;
+}
+
+ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) {
+ LOGV("read_at offset:%ld data:%p size:%d", offset, data, size);
+ CHECK(offset >= 0);
+
+ size_t avail = 0;
+ if (offset >= 0 && offset < (off_t)mSize) {
+ avail = mSize - offset;
+ }
+
+ if (size > avail) {
+ size = avail;
+ }
+
+ memcpy(data, (const uint8_t *)mBase + offset, size);
+
+ return (ssize_t)size;
+}
+
+status_t MmapSource::getSize(off_t *size) {
+ *size = mSize;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
new file mode 100644
index 0000000..dba7a2a
--- /dev/null
+++ b/media/libstagefright/OMXClient.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXClient"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+
+OMXClient::OMXClient() {
+}
+
+status_t OMXClient::connect() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+ CHECK(service.get() != NULL);
+
+ mOMX = service->createOMX();
+ CHECK(mOMX.get() != NULL);
+
+ return OK;
+}
+
+void OMXClient::disconnect() {
+}
+
+} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
new file mode 100644
index 0000000..a964d17
--- /dev/null
+++ b/media/libstagefright/OMXCodec.cpp
@@ -0,0 +1,2455 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMXCodec"
+#include <utils/Log.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/MemoryDealer.h>
+#include <binder/ProcessState.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
+#include <utils/Vector.h>
+
+#include <OMX_Audio.h>
+#include <OMX_Component.h>
+
+namespace android {
+
+struct CodecInfo {
+ const char *mime;
+ const char *codec;
+};
+
+static const CodecInfo kDecoderInfo[] = {
+ { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
+};
+
+static const CodecInfo kEncoderInfo[] = {
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrencnb" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcenc" },
+};
+
+#define CODEC_LOGI(x, ...) LOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
+#define CODEC_LOGV(x, ...) LOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
+
+struct OMXCodecObserver : public BnOMXObserver {
+ OMXCodecObserver(const wp<OMXCodec> &target)
+ : mTarget(target) {
+ }
+
+ // from IOMXObserver
+ virtual void on_message(const omx_message &msg) {
+ sp<OMXCodec> codec = mTarget.promote();
+
+ if (codec.get() != NULL) {
+ codec->on_message(msg);
+ }
+ }
+
+protected:
+ virtual ~OMXCodecObserver() {}
+
+private:
+ wp<OMXCodec> mTarget;
+
+ OMXCodecObserver(const OMXCodecObserver &);
+ OMXCodecObserver &operator=(const OMXCodecObserver &);
+};
+
+static const char *GetCodec(const CodecInfo *info, size_t numInfos,
+ const char *mime, int index) {
+ CHECK(index >= 0);
+ for(size_t i = 0; i < numInfos; ++i) {
+ if (!strcasecmp(mime, info[i].mime)) {
+ if (index == 0) {
+ return info[i].codec;
+ }
+
+ --index;
+ }
+ }
+
+ return NULL;
+}
+
+enum {
+ kAVCProfileBaseline = 0x42,
+ kAVCProfileMain = 0x4d,
+ kAVCProfileExtended = 0x58,
+ kAVCProfileHigh = 0x64,
+ kAVCProfileHigh10 = 0x6e,
+ kAVCProfileHigh422 = 0x7a,
+ kAVCProfileHigh444 = 0xf4,
+ kAVCProfileCAVLC444Intra = 0x2c
+};
+
+static const char *AVCProfileToString(uint8_t profile) {
+ switch (profile) {
+ case kAVCProfileBaseline:
+ return "Baseline";
+ case kAVCProfileMain:
+ return "Main";
+ case kAVCProfileExtended:
+ return "Extended";
+ case kAVCProfileHigh:
+ return "High";
+ case kAVCProfileHigh10:
+ return "High 10";
+ case kAVCProfileHigh422:
+ return "High 422";
+ case kAVCProfileHigh444:
+ return "High 444";
+ case kAVCProfileCAVLC444Intra:
+ return "CAVLC 444 Intra";
+ default: return "Unknown";
+ }
+}
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+// static
+sp<OMXCodec> OMXCodec::Create(
+ const sp<IOMX> &omx,
+ const sp<MetaData> &meta, bool createEncoder,
+ const sp<MediaSource> &source,
+ const char *matchComponentName) {
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+
+ const char *componentName = NULL;
+ IOMX::node_id node = 0;
+ for (int index = 0;; ++index) {
+ if (createEncoder) {
+ componentName = GetCodec(
+ kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+ mime, index);
+ } else {
+ componentName = GetCodec(
+ kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+ mime, index);
+ }
+
+ if (!componentName) {
+ return NULL;
+ }
+
+ // If a specific codec is requested, skip the non-matching ones.
+ if (matchComponentName && strcmp(componentName, matchComponentName)) {
+ continue;
+ }
+
+ LOGV("Attempting to allocate OMX node '%s'", componentName);
+
+ status_t err = omx->allocate_node(componentName, &node);
+ if (err == OK) {
+ LOGI("Successfully allocated OMX node '%s'", componentName);
+ break;
+ }
+ }
+
+ uint32_t quirks = 0;
+ if (!strcmp(componentName, "OMX.PV.avcdec")) {
+ quirks |= kWantsNALFragments;
+ }
+ if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
+ quirks |= kNeedsFlushBeforeDisable;
+ }
+ if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
+ quirks |= kNeedsFlushBeforeDisable;
+ quirks |= kRequiresFlushCompleteEmulation;
+
+ // The following is currently necessary for proper shutdown
+ // behaviour, but NOT enabled by default in order to make the
+ // bug reproducible...
+ // quirks |= kRequiresFlushBeforeShutdown;
+ }
+ if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
+ quirks |= kRequiresLoadedToIdleAfterAllocation;
+ quirks |= kRequiresAllocateBufferOnInputPorts;
+ }
+ if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
+ // XXX Required on P....on only.
+ quirks |= kRequiresAllocateBufferOnOutputPorts;
+ }
+
+ sp<OMXCodec> codec = new OMXCodec(
+ omx, node, quirks, createEncoder, mime, componentName,
+ source);
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ printf("found codec-specific data of size %d\n",
+ codec_specific_data_size);
+
+ codec->addCodecSpecificData(
+ codec_specific_data, codec_specific_data_size);
+ } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ printf("found avcc of size %d\n", size);
+
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ(ptr[0], 1); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ codec->addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ codec->addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ LOGI("AVC profile = %d (%s), level = %d",
+ (int)profile, AVCProfileToString(profile), (int)level / 10);
+
+ if (!strcmp(componentName, "OMX.TI.Video.Decoder")
+ && (profile != kAVCProfileBaseline || level > 39)) {
+ // This stream exceeds the decoder's capabilities.
+
+ LOGE("Profile and/or level exceed the decoder's capabilities.");
+ return NULL;
+ }
+ }
+
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, mime)) {
+ codec->setAMRFormat();
+ }
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)) {
+ codec->setAMRWBFormat();
+ }
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) {
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ codec->setAACFormat(numChannels, sampleRate);
+ }
+ if (!strncasecmp(mime, "video/", 6)) {
+ int32_t width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+ CHECK(success);
+
+ if (createEncoder) {
+ codec->setVideoInputFormat(mime, width, height);
+ } else {
+ codec->setVideoOutputFormat(mime, width, height);
+ }
+ }
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_IMAGE_JPEG)
+ && !strcmp(componentName, "OMX.TI.JPEG.decode")) {
+ OMX_COLOR_FORMATTYPE format =
+ OMX_COLOR_Format32bitARGB8888;
+ // OMX_COLOR_FormatYUV420PackedPlanar;
+ // OMX_COLOR_FormatCbYCrY;
+ // OMX_COLOR_FormatYUV411Planar;
+
+ int32_t width, height;
+ bool success = meta->findInt32(kKeyWidth, &width);
+ success = success && meta->findInt32(kKeyHeight, &height);
+
+ int32_t compressedSize;
+ success = success && meta->findInt32(
+ kKeyMaxInputSize, &compressedSize);
+
+ CHECK(success);
+ CHECK(compressedSize > 0);
+
+ codec->setImageOutputFormat(format, width, height);
+ codec->setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
+ }
+
+ int32_t maxInputSize;
+ if (createEncoder && meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ codec->setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
+ }
+
+ if (!strcmp(componentName, "OMX.TI.AMR.encode")
+ || !strcmp(componentName, "OMX.TI.WBAMR.encode")) {
+ codec->setMinBufferSize(kPortIndexOutput, 8192); // XXX
+ }
+
+ codec->initOutputFormat(meta);
+
+ return codec;
+}
+
+void OMXCodec::setMinBufferSize(OMX_U32 portIndex, OMX_U32 size) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ if (def.nBufferSize < size) {
+ def.nBufferSize = size;
+
+ }
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+status_t OMXCodec::setVideoPortFormatType(
+ OMX_U32 portIndex,
+ OMX_VIDEO_CODINGTYPE compressionFormat,
+ OMX_COLOR_FORMATTYPE colorFormat) {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+ format.nPortIndex = portIndex;
+ format.nIndex = 0;
+ bool found = false;
+
+ OMX_U32 index = 0;
+ for (;;) {
+ format.nIndex = index;
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+
+ if (err != OK) {
+ return err;
+ }
+
+ // The following assertion is violated by TI's video decoder.
+ // CHECK_EQ(format.nIndex, index);
+
+#if 1
+ CODEC_LOGI("portIndex: %ld, index: %ld, eCompressionFormat=%d eColorFormat=%d",
+ portIndex,
+ index, format.eCompressionFormat, format.eColorFormat);
+#endif
+
+ if (!strcmp("OMX.TI.Video.encoder", mComponentName)) {
+ if (portIndex == kPortIndexInput
+ && colorFormat == format.eColorFormat) {
+ // eCompressionFormat does not seem right.
+ found = true;
+ break;
+ }
+ if (portIndex == kPortIndexOutput
+ && compressionFormat == format.eCompressionFormat) {
+ // eColorFormat does not seem right.
+ found = true;
+ break;
+ }
+ }
+
+ if (format.eCompressionFormat == compressionFormat
+ && format.eColorFormat == colorFormat) {
+ found = true;
+ break;
+ }
+
+ ++index;
+ }
+
+ if (!found) {
+ return UNKNOWN_ERROR;
+ }
+
+ CODEC_LOGI("found a match.");
+ status_t err = mOMX->set_parameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+
+ return err;
+}
+
+void OMXCodec::setVideoInputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height) {
+ CODEC_LOGI("setVideoInputFormat width=%ld, height=%ld", width, height);
+
+ OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ compressionFormat = OMX_VIDEO_CodingAVC;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ compressionFormat = OMX_VIDEO_CodingMPEG4;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ compressionFormat = OMX_VIDEO_CodingH263;
+ } else {
+ LOGE("Not a supported video mime type: %s", mime);
+ CHECK(!"Should not be here. Not a supported video mime type.");
+ }
+
+ OMX_COLOR_FORMATTYPE colorFormat =
+ 0 ? OMX_COLOR_FormatYCbYCr : OMX_COLOR_FormatCbYCrY;
+
+ if (!strncmp("OMX.qcom.video.encoder.", mComponentName, 23)) {
+ colorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
+ }
+
+ setVideoPortFormatType(
+ kPortIndexInput, OMX_VIDEO_CodingUnused,
+ colorFormat);
+
+ setVideoPortFormatType(
+ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ def.nBufferSize = (width * height * 2); // (width * height * 3) / 2;
+ CODEC_LOGI("Setting nBufferSize = %ld", def.nBufferSize);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ video_def->eColorFormat = colorFormat;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::setVideoOutputFormat(
+ const char *mime, OMX_U32 width, OMX_U32 height) {
+ CODEC_LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+ OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
+ compressionFormat = OMX_VIDEO_CodingAVC;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
+ compressionFormat = OMX_VIDEO_CodingMPEG4;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
+ compressionFormat = OMX_VIDEO_CodingH263;
+ } else {
+ LOGE("Not a supported video mime type: %s", mime);
+ CHECK(!"Should not be here. Not a supported video mime type.");
+ }
+
+ setVideoPortFormatType(
+ kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+#if 1
+ {
+ OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+ format.nPortIndex = kPortIndexOutput;
+ format.nIndex = 0;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+ CHECK_EQ(err, OK);
+ CHECK_EQ(format.eCompressionFormat, OMX_VIDEO_CodingUnused);
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+ || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+ || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+ || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamVideoPortFormat,
+ &format, sizeof(format));
+ CHECK_EQ(err, OK);
+ }
+#endif
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+
+#if 1
+ // XXX Need a (much) better heuristic to compute input buffer sizes.
+ const size_t X = 64 * 1024;
+ if (def.nBufferSize < X) {
+ def.nBufferSize = X;
+ }
+#endif
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ ////////////////////////////////////////////////////////////////////////////
+
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ CHECK_EQ(def.eDomain, OMX_PortDomainVideo);
+
+#if 0
+ def.nBufferSize =
+ (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2; // YUV420
+#endif
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+
+OMXCodec::OMXCodec(
+ const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks,
+ bool isEncoder,
+ const char *mime,
+ const char *componentName,
+ const sp<MediaSource> &source)
+ : mOMX(omx),
+ mNode(node),
+ mQuirks(quirks),
+ mIsEncoder(isEncoder),
+ mMIME(strdup(mime)),
+ mComponentName(strdup(componentName)),
+ mSource(source),
+ mCodecSpecificDataIndex(0),
+ mState(LOADED),
+ mInitialBufferSubmit(true),
+ mSignalledEOS(false),
+ mNoMoreOutputData(false),
+ mSeekTimeUs(-1) {
+ mPortStatus[kPortIndexInput] = ENABLED;
+ mPortStatus[kPortIndexOutput] = ENABLED;
+
+ mObserver = new OMXCodecObserver(this);
+ mOMX->observe_node(mNode, mObserver);
+
+ setComponentRole();
+}
+
+// static
+void OMXCodec::setComponentRole(
+ const sp<IOMX> &omx, IOMX::node_id node, bool isEncoder,
+ const char *mime) {
+ struct MimeToRole {
+ const char *mime;
+ const char *decoderRole;
+ const char *encoderRole;
+ };
+
+ static const MimeToRole kMimeToRole[] = {
+ { MEDIA_MIMETYPE_AUDIO_MPEG,
+ "audio_decoder.mp3", "audio_encoder.mp3" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB,
+ "audio_decoder.amrnb", "audio_encoder.amrnb" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ "audio_decoder.amrwb", "audio_encoder.amrwb" },
+ { MEDIA_MIMETYPE_AUDIO_AAC,
+ "audio_decoder.aac", "audio_encoder.aac" },
+ { MEDIA_MIMETYPE_VIDEO_AVC,
+ "video_decoder.avc", "video_encoder.avc" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4,
+ "video_decoder.mpeg4", "video_encoder.mpeg4" },
+ { MEDIA_MIMETYPE_VIDEO_H263,
+ "video_decoder.h263", "video_encoder.h263" },
+ };
+
+ static const size_t kNumMimeToRole =
+ sizeof(kMimeToRole) / sizeof(kMimeToRole[0]);
+
+ size_t i;
+ for (i = 0; i < kNumMimeToRole; ++i) {
+ if (!strcasecmp(mime, kMimeToRole[i].mime)) {
+ break;
+ }
+ }
+
+ if (i == kNumMimeToRole) {
+ return;
+ }
+
+ const char *role =
+ isEncoder ? kMimeToRole[i].encoderRole
+ : kMimeToRole[i].decoderRole;
+
+ if (role != NULL) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
+
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
+
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+ status_t err = omx->set_parameter(
+ node, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
+
+ if (err != OK) {
+ LOGW("Failed to set standard component role '%s'.", role);
+ }
+ }
+}
+
+void OMXCodec::setComponentRole() {
+ setComponentRole(mOMX, mNode, mIsEncoder, mMIME);
+}
+
+OMXCodec::~OMXCodec() {
+ CHECK(mState == LOADED || mState == ERROR);
+
+ status_t err = mOMX->observe_node(mNode, NULL);
+ CHECK_EQ(err, OK);
+
+ err = mOMX->free_node(mNode);
+ CHECK_EQ(err, OK);
+
+ mNode = NULL;
+ setState(DEAD);
+
+ clearCodecSpecificData();
+
+ free(mComponentName);
+ mComponentName = NULL;
+
+ free(mMIME);
+ mMIME = NULL;
+}
+
+status_t OMXCodec::init() {
+ // mLock is held.
+
+ CHECK_EQ(mState, LOADED);
+
+ status_t err;
+ if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
+ err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+ setState(LOADED_TO_IDLE);
+ }
+
+ err = allocateBuffers();
+ CHECK_EQ(err, OK);
+
+ if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
+ err = mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+
+ setState(LOADED_TO_IDLE);
+ }
+
+ while (mState != EXECUTING && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ return mState == ERROR ? UNKNOWN_ERROR : OK;
+}
+
+// static
+bool OMXCodec::isIntermediateState(State state) {
+ return state == LOADED_TO_IDLE
+ || state == IDLE_TO_EXECUTING
+ || state == EXECUTING_TO_IDLE
+ || state == IDLE_TO_LOADED
+ || state == RECONFIGURING;
+}
+
+status_t OMXCodec::allocateBuffers() {
+ status_t err = allocateBuffersOnPort(kPortIndexInput);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return allocateBuffersOnPort(kPortIndexOutput);
+}
+
+status_t OMXCodec::allocateBuffersOnPort(OMX_U32 portIndex) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+ mDealer[portIndex] = new MemoryDealer(totalSize);
+
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+ CHECK(mem.get() != NULL);
+
+ IOMX::buffer_id buffer;
+ if (portIndex == kPortIndexInput
+ && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
+ err = mOMX->allocate_buffer_with_backup(
+ mNode, portIndex, mem, &buffer);
+ } else if (portIndex == kPortIndexOutput
+ && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
+ err = mOMX->allocate_buffer(
+ mNode, portIndex, def.nBufferSize, &buffer);
+ } else {
+ err = mOMX->use_buffer(mNode, portIndex, mem, &buffer);
+ }
+
+ if (err != OK) {
+ LOGE("allocate_buffer_with_backup failed");
+ return err;
+ }
+
+ BufferInfo info;
+ info.mBuffer = buffer;
+ info.mOwnedByComponent = false;
+ info.mMem = mem;
+ info.mMediaBuffer = NULL;
+
+ if (portIndex == kPortIndexOutput) {
+ info.mMediaBuffer = new MediaBuffer(mem->pointer(), mem->size());
+ info.mMediaBuffer->setObserver(this);
+ }
+
+ mPortBuffers[portIndex].push(info);
+
+ CODEC_LOGV("allocated buffer %p on %s port", buffer,
+ portIndex == kPortIndexInput ? "input" : "output");
+ }
+
+ dumpPortStatus(portIndex);
+
+ return OK;
+}
+
+void OMXCodec::on_message(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+
+ switch (msg.type) {
+ case omx_message::EVENT:
+ {
+ onEvent(
+ msg.u.event_data.event, msg.u.event_data.data1,
+ msg.u.event_data.data2);
+
+ break;
+ }
+
+ case omx_message::EMPTY_BUFFER_DONE:
+ {
+ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+
+ CODEC_LOGV("EMPTY_BUFFER_DONE(buffer: %p)", buffer);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ size_t i = 0;
+ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
+ ++i;
+ }
+
+ CHECK(i < buffers->size());
+ if (!(*buffers)[i].mOwnedByComponent) {
+ LOGW("We already own input buffer %p, yet received "
+ "an EMPTY_BUFFER_DONE.", buffer);
+ }
+
+ buffers->editItemAt(i).mOwnedByComponent = false;
+
+ if (mPortStatus[kPortIndexInput] == DISABLING) {
+ CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
+
+ status_t err =
+ mOMX->free_buffer(mNode, kPortIndexInput, buffer);
+ CHECK_EQ(err, OK);
+
+ buffers->removeAt(i);
+ } else if (mPortStatus[kPortIndexInput] != SHUTTING_DOWN) {
+ CHECK_EQ(mPortStatus[kPortIndexInput], ENABLED);
+ drainInputBuffer(&buffers->editItemAt(i));
+ }
+
+ break;
+ }
+
+ case omx_message::FILL_BUFFER_DONE:
+ {
+ IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+ OMX_U32 flags = msg.u.extended_buffer_data.flags;
+
+ CODEC_LOGV("FILL_BUFFER_DONE(buffer: %p, size: %ld, flags: 0x%08lx)",
+ buffer,
+ msg.u.extended_buffer_data.range_length,
+ flags);
+
+ CODEC_LOGV("FILL_BUFFER_DONE(timestamp: %lld us (%.2f secs))",
+ msg.u.extended_buffer_data.timestamp,
+ msg.u.extended_buffer_data.timestamp / 1E6);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ size_t i = 0;
+ while (i < buffers->size() && (*buffers)[i].mBuffer != buffer) {
+ ++i;
+ }
+
+ CHECK(i < buffers->size());
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (!info->mOwnedByComponent) {
+ LOGW("We already own output buffer %p, yet received "
+ "a FILL_BUFFER_DONE.", buffer);
+ }
+
+ info->mOwnedByComponent = false;
+
+ if (mPortStatus[kPortIndexOutput] == DISABLING) {
+ CODEC_LOGV("Port is disabled, freeing buffer %p", buffer);
+
+ status_t err =
+ mOMX->free_buffer(mNode, kPortIndexOutput, buffer);
+ CHECK_EQ(err, OK);
+
+ buffers->removeAt(i);
+ } else if (mPortStatus[kPortIndexOutput] == ENABLED
+ && (flags & OMX_BUFFERFLAG_EOS)) {
+ CODEC_LOGV("No more output data.");
+ mNoMoreOutputData = true;
+ mBufferFilled.signal();
+ } else if (mPortStatus[kPortIndexOutput] != SHUTTING_DOWN) {
+ CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+
+ MediaBuffer *buffer = info->mMediaBuffer;
+
+ buffer->set_range(
+ msg.u.extended_buffer_data.range_offset,
+ msg.u.extended_buffer_data.range_length);
+
+ buffer->meta_data()->clear();
+
+ buffer->meta_data()->setInt32(
+ kKeyTimeUnits,
+ (msg.u.extended_buffer_data.timestamp + 500) / 1000);
+
+ buffer->meta_data()->setInt32(
+ kKeyTimeScale, 1000);
+
+ if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
+ buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
+ }
+
+ buffer->meta_data()->setPointer(
+ kKeyPlatformPrivate,
+ msg.u.extended_buffer_data.platform_private);
+
+ buffer->meta_data()->setPointer(
+ kKeyBufferID,
+ msg.u.extended_buffer_data.buffer);
+
+ mFilledBuffers.push_back(i);
+ mBufferFilled.signal();
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+}
+
+void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+ switch (event) {
+ case OMX_EventCmdComplete:
+ {
+ onCmdComplete((OMX_COMMANDTYPE)data1, data2);
+ break;
+ }
+
+ case OMX_EventError:
+ {
+ LOGE("ERROR(%ld, %ld)", data1, data2);
+
+ setState(ERROR);
+ break;
+ }
+
+ case OMX_EventPortSettingsChanged:
+ {
+ onPortSettingsChanged(data1);
+ break;
+ }
+
+ case OMX_EventBufferFlag:
+ {
+ CODEC_LOGV("EVENT_BUFFER_FLAG(%ld)", data1);
+
+ if (data1 == kPortIndexOutput) {
+ mNoMoreOutputData = true;
+ }
+ break;
+ }
+
+ default:
+ {
+ CODEC_LOGV("EVENT(%d, %ld, %ld)", event, data1, data2);
+ break;
+ }
+ }
+}
+
+void OMXCodec::onCmdComplete(OMX_COMMANDTYPE cmd, OMX_U32 data) {
+ switch (cmd) {
+ case OMX_CommandStateSet:
+ {
+ onStateChange((OMX_STATETYPE)data);
+ break;
+ }
+
+ case OMX_CommandPortDisable:
+ {
+ OMX_U32 portIndex = data;
+ CODEC_LOGV("PORT_DISABLED(%ld)", portIndex);
+
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+ CHECK_EQ(mPortStatus[portIndex], DISABLING);
+ CHECK_EQ(mPortBuffers[portIndex].size(), 0);
+
+ mPortStatus[portIndex] = DISABLED;
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ enablePortAsync(portIndex);
+
+ status_t err = allocateBuffersOnPort(portIndex);
+ CHECK_EQ(err, OK);
+ }
+ break;
+ }
+
+ case OMX_CommandPortEnable:
+ {
+ OMX_U32 portIndex = data;
+ CODEC_LOGV("PORT_ENABLED(%ld)", portIndex);
+
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+ CHECK_EQ(mPortStatus[portIndex], ENABLING);
+
+ mPortStatus[portIndex] = ENABLED;
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ setState(EXECUTING);
+
+ fillOutputBuffers();
+ }
+ break;
+ }
+
+ case OMX_CommandFlush:
+ {
+ OMX_U32 portIndex = data;
+
+ CODEC_LOGV("FLUSH_DONE(%ld)", portIndex);
+
+ CHECK_EQ(mPortStatus[portIndex], SHUTTING_DOWN);
+ mPortStatus[portIndex] = ENABLED;
+
+ CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
+ mPortBuffers[portIndex].size());
+
+ if (mState == RECONFIGURING) {
+ CHECK_EQ(portIndex, kPortIndexOutput);
+
+ disablePortAsync(portIndex);
+ } else if (mState == EXECUTING_TO_IDLE) {
+ if (mPortStatus[kPortIndexInput] == ENABLED
+ && mPortStatus[kPortIndexOutput] == ENABLED) {
+ CODEC_LOGV("Finished flushing both ports, now completing "
+ "transition from EXECUTING to IDLE.");
+
+ mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
+ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+ }
+ } else {
+ // We're flushing both ports in preparation for seeking.
+
+ if (mPortStatus[kPortIndexInput] == ENABLED
+ && mPortStatus[kPortIndexOutput] == ENABLED) {
+ CODEC_LOGV("Finished flushing both ports, now continuing from"
+ " seek-time.");
+
+ drainInputBuffers();
+ fillOutputBuffers();
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CODEC_LOGV("CMD_COMPLETE(%d, %ld)", cmd, data);
+ break;
+ }
+ }
+}
+
+void OMXCodec::onStateChange(OMX_STATETYPE newState) {
+ switch (newState) {
+ case OMX_StateIdle:
+ {
+ CODEC_LOGV("Now Idle.");
+ if (mState == LOADED_TO_IDLE) {
+ status_t err = mOMX->send_command(
+ mNode, OMX_CommandStateSet, OMX_StateExecuting);
+
+ CHECK_EQ(err, OK);
+
+ setState(IDLE_TO_EXECUTING);
+ } else {
+ CHECK_EQ(mState, EXECUTING_TO_IDLE);
+
+ CHECK_EQ(
+ countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
+ mPortBuffers[kPortIndexInput].size());
+
+ CHECK_EQ(
+ countBuffersWeOwn(mPortBuffers[kPortIndexOutput]),
+ mPortBuffers[kPortIndexOutput].size());
+
+ status_t err = mOMX->send_command(
+ mNode, OMX_CommandStateSet, OMX_StateLoaded);
+
+ CHECK_EQ(err, OK);
+
+ err = freeBuffersOnPort(kPortIndexInput);
+ CHECK_EQ(err, OK);
+
+ err = freeBuffersOnPort(kPortIndexOutput);
+ CHECK_EQ(err, OK);
+
+ mPortStatus[kPortIndexInput] = ENABLED;
+ mPortStatus[kPortIndexOutput] = ENABLED;
+
+ setState(IDLE_TO_LOADED);
+ }
+ break;
+ }
+
+ case OMX_StateExecuting:
+ {
+ CHECK_EQ(mState, IDLE_TO_EXECUTING);
+
+ CODEC_LOGV("Now Executing.");
+
+ setState(EXECUTING);
+
+ // Buffers will be submitted to the component in the first
+ // call to OMXCodec::read as mInitialBufferSubmit is true at
+ // this point. This ensures that this on_message call returns,
+ // releases the lock and ::init can notice the state change and
+ // itself return.
+ break;
+ }
+
+ case OMX_StateLoaded:
+ {
+ CHECK_EQ(mState, IDLE_TO_LOADED);
+
+ CODEC_LOGV("Now Loaded.");
+
+ setState(LOADED);
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+}
+
+// static
+size_t OMXCodec::countBuffersWeOwn(const Vector<BufferInfo> &buffers) {
+ size_t n = 0;
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ if (!buffers[i].mOwnedByComponent) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+status_t OMXCodec::freeBuffersOnPort(
+ OMX_U32 portIndex, bool onlyThoseWeOwn) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ status_t stickyErr = OK;
+
+ for (size_t i = buffers->size(); i-- > 0;) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (onlyThoseWeOwn && info->mOwnedByComponent) {
+ continue;
+ }
+
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ status_t err =
+ mOMX->free_buffer(mNode, portIndex, info->mBuffer);
+
+ if (err != OK) {
+ stickyErr = err;
+ }
+
+ if (info->mMediaBuffer != NULL) {
+ info->mMediaBuffer->setObserver(NULL);
+
+ // Make sure nobody but us owns this buffer at this point.
+ CHECK_EQ(info->mMediaBuffer->refcount(), 0);
+
+ info->mMediaBuffer->release();
+ }
+
+ buffers->removeAt(i);
+ }
+
+ CHECK(onlyThoseWeOwn || buffers->isEmpty());
+
+ return stickyErr;
+}
+
+void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) {
+ CODEC_LOGV("PORT_SETTINGS_CHANGED(%ld)", portIndex);
+
+ CHECK_EQ(mState, EXECUTING);
+ CHECK_EQ(portIndex, kPortIndexOutput);
+ setState(RECONFIGURING);
+
+ if (mQuirks & kNeedsFlushBeforeDisable) {
+ if (!flushPortAsync(portIndex)) {
+ onCmdComplete(OMX_CommandFlush, portIndex);
+ }
+ } else {
+ disablePortAsync(portIndex);
+ }
+}
+
+bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING
+ || mState == EXECUTING_TO_IDLE);
+
+ CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.",
+ portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
+ mPortBuffers[portIndex].size());
+
+ CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ mPortStatus[portIndex] = SHUTTING_DOWN;
+
+ if ((mQuirks & kRequiresFlushCompleteEmulation)
+ && countBuffersWeOwn(mPortBuffers[portIndex])
+ == mPortBuffers[portIndex].size()) {
+ // No flush is necessary and this component fails to send a
+ // flush-complete event in this case.
+
+ return false;
+ }
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandFlush, portIndex);
+ CHECK_EQ(err, OK);
+
+ return true;
+}
+
+void OMXCodec::disablePortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ CHECK_EQ(mPortStatus[portIndex], ENABLED);
+ mPortStatus[portIndex] = DISABLING;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortDisable, portIndex);
+ CHECK_EQ(err, OK);
+
+ freeBuffersOnPort(portIndex, true);
+}
+
+void OMXCodec::enablePortAsync(OMX_U32 portIndex) {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ CHECK_EQ(mPortStatus[portIndex], DISABLED);
+ mPortStatus[portIndex] = ENABLING;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandPortEnable, portIndex);
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::fillOutputBuffers() {
+ CHECK_EQ(mState, EXECUTING);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ fillOutputBuffer(&buffers->editItemAt(i));
+ }
+}
+
+void OMXCodec::drainInputBuffers() {
+ CHECK(mState == EXECUTING || mState == RECONFIGURING);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ drainInputBuffer(&buffers->editItemAt(i));
+ }
+}
+
+void OMXCodec::drainInputBuffer(BufferInfo *info) {
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ if (mSignalledEOS) {
+ return;
+ }
+
+ if (mCodecSpecificDataIndex < mCodecSpecificData.size()) {
+ const CodecSpecificData *specific =
+ mCodecSpecificData[mCodecSpecificDataIndex];
+
+ size_t size = specific->mSize;
+
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mMIME)
+ && !(mQuirks & kWantsNALFragments)) {
+ static const uint8_t kNALStartCode[4] =
+ { 0x00, 0x00, 0x00, 0x01 };
+
+ CHECK(info->mMem->size() >= specific->mSize + 4);
+
+ size += 4;
+
+ memcpy(info->mMem->pointer(), kNALStartCode, 4);
+ memcpy((uint8_t *)info->mMem->pointer() + 4,
+ specific->mData, specific->mSize);
+ } else {
+ CHECK(info->mMem->size() >= specific->mSize);
+ memcpy(info->mMem->pointer(), specific->mData, specific->mSize);
+ }
+
+ mOMX->empty_buffer(
+ mNode, info->mBuffer, 0, size,
+ OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
+ 0);
+
+ info->mOwnedByComponent = true;
+
+ ++mCodecSpecificDataIndex;
+ return;
+ }
+
+ MediaBuffer *srcBuffer;
+ status_t err;
+ if (mSeekTimeUs >= 0) {
+ MediaSource::ReadOptions options;
+ options.setSeekTo(mSeekTimeUs);
+ mSeekTimeUs = -1;
+
+ err = mSource->read(&srcBuffer, &options);
+ } else {
+ err = mSource->read(&srcBuffer);
+ }
+
+ OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+ OMX_TICKS timestamp = 0;
+ size_t srcLength = 0;
+
+ if (err != OK) {
+ CODEC_LOGV("signalling end of input stream.");
+ flags |= OMX_BUFFERFLAG_EOS;
+
+ mSignalledEOS = true;
+ } else {
+ srcLength = srcBuffer->range_length();
+
+ if (info->mMem->size() < srcLength) {
+ LOGE("info->mMem->size() = %d, srcLength = %d",
+ info->mMem->size(), srcLength);
+ }
+ CHECK(info->mMem->size() >= srcLength);
+ memcpy(info->mMem->pointer(),
+ (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(),
+ srcLength);
+
+ int32_t units, scale;
+ if (srcBuffer->meta_data()->findInt32(kKeyTimeUnits, &units)
+ && srcBuffer->meta_data()->findInt32(kKeyTimeScale, &scale)) {
+ timestamp = ((OMX_TICKS)units * 1000000) / scale;
+
+ CODEC_LOGV("Calling empty_buffer on buffer %p (length %d)",
+ info->mBuffer, srcLength);
+ CODEC_LOGV("Calling empty_buffer with timestamp %lld us (%.2f secs)",
+ timestamp, timestamp / 1E6);
+ }
+ }
+
+ mOMX->empty_buffer(
+ mNode, info->mBuffer, 0, srcLength,
+ flags, timestamp);
+
+ info->mOwnedByComponent = true;
+
+ if (srcBuffer != NULL) {
+ srcBuffer->release();
+ srcBuffer = NULL;
+ }
+}
+
+void OMXCodec::fillOutputBuffer(BufferInfo *info) {
+ CHECK_EQ(info->mOwnedByComponent, false);
+
+ if (mNoMoreOutputData) {
+ CODEC_LOGV("There is no more output data available, not "
+ "calling fillOutputBuffer");
+ return;
+ }
+
+ CODEC_LOGV("Calling fill_buffer on buffer %p", info->mBuffer);
+ mOMX->fill_buffer(mNode, info->mBuffer);
+
+ info->mOwnedByComponent = true;
+}
+
+void OMXCodec::drainInputBuffer(IOMX::buffer_id buffer) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ if ((*buffers)[i].mBuffer == buffer) {
+ drainInputBuffer(&buffers->editItemAt(i));
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+void OMXCodec::fillOutputBuffer(IOMX::buffer_id buffer) {
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ if ((*buffers)[i].mBuffer == buffer) {
+ fillOutputBuffer(&buffers->editItemAt(i));
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+void OMXCodec::setState(State newState) {
+ mState = newState;
+ mAsyncCompletion.signal();
+
+ // This may cause some spurious wakeups but is necessary to
+ // unblock the reader if we enter ERROR state.
+ mBufferFilled.signal();
+}
+
+void OMXCodec::setRawAudioFormat(
+ OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels) {
+ OMX_AUDIO_PARAM_PCMMODETYPE pcmParams;
+ InitOMXParams(&pcmParams);
+ pcmParams.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+
+ CHECK_EQ(err, OK);
+
+ pcmParams.nChannels = numChannels;
+ pcmParams.eNumData = OMX_NumericalDataSigned;
+ pcmParams.bInterleaved = OMX_TRUE;
+ pcmParams.nBitPerSample = 16;
+ pcmParams.nSamplingRate = sampleRate;
+ pcmParams.ePCMMode = OMX_AUDIO_PCMModeLinear;
+
+ if (numChannels == 1) {
+ pcmParams.eChannelMapping[0] = OMX_AUDIO_ChannelCF;
+ } else {
+ CHECK_EQ(numChannels, 2);
+
+ pcmParams.eChannelMapping[0] = OMX_AUDIO_ChannelLF;
+ pcmParams.eChannelMapping[1] = OMX_AUDIO_ChannelRF;
+ }
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamAudioPcm, &pcmParams, sizeof(pcmParams));
+
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::setAMRFormat() {
+ if (!mIsEncoder) {
+ OMX_AUDIO_PARAM_AMRTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+
+ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ }
+
+ ////////////////////////
+
+ if (mIsEncoder) {
+ sp<MetaData> format = mSource->getFormat();
+ int32_t sampleRate;
+ int32_t numChannels;
+ CHECK(format->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(format->findInt32(kKeyChannelCount, &numChannels));
+
+ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ }
+}
+
+void OMXCodec::setAMRWBFormat() {
+ if (!mIsEncoder) {
+ OMX_AUDIO_PARAM_AMRTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err =
+ mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+
+ CHECK_EQ(err, OK);
+
+ def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = OMX_AUDIO_AMRBandModeWB0;
+
+ err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+ }
+
+ ////////////////////////
+
+ if (mIsEncoder) {
+ sp<MetaData> format = mSource->getFormat();
+ int32_t sampleRate;
+ int32_t numChannels;
+ CHECK(format->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(format->findInt32(kKeyChannelCount, &numChannels));
+
+ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ }
+}
+
+void OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate) {
+ if (mIsEncoder) {
+ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ } else {
+ OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+ CHECK_EQ(err, OK);
+
+ profile.nChannels = numChannels;
+ profile.nSampleRate = sampleRate;
+ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+ CHECK_EQ(err, OK);
+ }
+}
+
+void OMXCodec::setImageOutputFormat(
+ OMX_COLOR_FORMATTYPE format, OMX_U32 width, OMX_U32 height) {
+ CODEC_LOGV("setImageOutputFormat(%ld, %ld)", width, height);
+
+#if 0
+ OMX_INDEXTYPE index;
+ status_t err = mOMX->get_extension_index(
+ mNode, "OMX.TI.JPEG.decode.Config.OutputColorFormat", &index);
+ CHECK_EQ(err, OK);
+
+ err = mOMX->set_config(mNode, index, &format, sizeof(format));
+ CHECK_EQ(err, OK);
+#endif
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+
+ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+ imageDef->eColorFormat = format;
+ imageDef->nFrameWidth = width;
+ imageDef->nFrameHeight = height;
+
+ switch (format) {
+ case OMX_COLOR_FormatYUV420PackedPlanar:
+ case OMX_COLOR_FormatYUV411Planar:
+ {
+ def.nBufferSize = (width * height * 3) / 2;
+ break;
+ }
+
+ case OMX_COLOR_FormatCbYCrY:
+ {
+ def.nBufferSize = width * height * 2;
+ break;
+ }
+
+ case OMX_COLOR_Format32bitARGB8888:
+ {
+ def.nBufferSize = width * height * 4;
+ break;
+ }
+
+ case OMX_COLOR_Format16bitARGB4444:
+ case OMX_COLOR_Format16bitARGB1555:
+ case OMX_COLOR_Format16bitRGB565:
+ case OMX_COLOR_Format16bitBGR565:
+ {
+ def.nBufferSize = width * height * 2;
+ break;
+ }
+
+ default:
+ CHECK(!"Should not be here. Unknown color format.");
+ break;
+ }
+
+ def.nBufferCountActual = def.nBufferCountMin;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::setJPEGInputFormat(
+ OMX_U32 width, OMX_U32 height, OMX_U32 compressedSize) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexInput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(def.eDomain, OMX_PortDomainImage);
+ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingJPEG);
+ imageDef->nFrameWidth = width;
+ imageDef->nFrameHeight = height;
+
+ def.nBufferSize = compressedSize;
+ def.nBufferCountActual = def.nBufferCountMin;
+
+ err = mOMX->set_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+}
+
+void OMXCodec::addCodecSpecificData(const void *data, size_t size) {
+ CodecSpecificData *specific =
+ (CodecSpecificData *)malloc(sizeof(CodecSpecificData) + size - 1);
+
+ specific->mSize = size;
+ memcpy(specific->mData, data, size);
+
+ mCodecSpecificData.push(specific);
+}
+
+void OMXCodec::clearCodecSpecificData() {
+ for (size_t i = 0; i < mCodecSpecificData.size(); ++i) {
+ free(mCodecSpecificData.editItemAt(i));
+ }
+ mCodecSpecificData.clear();
+ mCodecSpecificDataIndex = 0;
+}
+
+status_t OMXCodec::start(MetaData *) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != LOADED) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> params = new MetaData;
+ if (mQuirks & kWantsNALFragments) {
+ params->setInt32(kKeyWantsNALFragments, true);
+ }
+ status_t err = mSource->start(params.get());
+
+ if (err != OK) {
+ return err;
+ }
+
+ mCodecSpecificDataIndex = 0;
+ mInitialBufferSubmit = true;
+ mSignalledEOS = false;
+ mNoMoreOutputData = false;
+ mSeekTimeUs = -1;
+ mFilledBuffers.clear();
+
+ return init();
+}
+
+status_t OMXCodec::stop() {
+ CODEC_LOGV("stop");
+
+ Mutex::Autolock autoLock(mLock);
+
+ while (isIntermediateState(mState)) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ switch (mState) {
+ case LOADED:
+ case ERROR:
+ break;
+
+ case EXECUTING:
+ {
+ setState(EXECUTING_TO_IDLE);
+
+ if (mQuirks & kRequiresFlushBeforeShutdown) {
+ CODEC_LOGV("This component requires a flush before transitioning "
+ "from EXECUTING to IDLE...");
+
+ bool emulateInputFlushCompletion =
+ !flushPortAsync(kPortIndexInput);
+
+ bool emulateOutputFlushCompletion =
+ !flushPortAsync(kPortIndexOutput);
+
+ if (emulateInputFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexInput);
+ }
+
+ if (emulateOutputFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
+ }
+ } else {
+ mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
+ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
+
+ status_t err =
+ mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+ CHECK_EQ(err, OK);
+ }
+
+ while (mState != LOADED && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here.");
+ break;
+ }
+ }
+
+ mSource->stop();
+
+ return OK;
+}
+
+sp<MetaData> OMXCodec::getFormat() {
+ return mOutputFormat;
+}
+
+status_t OMXCodec::read(
+ MediaBuffer **buffer, const ReadOptions *options) {
+ *buffer = NULL;
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mState != EXECUTING && mState != RECONFIGURING) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mInitialBufferSubmit) {
+ mInitialBufferSubmit = false;
+
+ drainInputBuffers();
+
+ if (mState == EXECUTING) {
+ // Otherwise mState == RECONFIGURING and this code will trigger
+ // after the output port is reenabled.
+ fillOutputBuffers();
+ }
+ }
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ CODEC_LOGV("seeking to %lld us (%.2f secs)", seekTimeUs, seekTimeUs / 1E6);
+
+ mSignalledEOS = false;
+ mNoMoreOutputData = false;
+
+ CHECK(seekTimeUs >= 0);
+ mSeekTimeUs = seekTimeUs;
+
+ mFilledBuffers.clear();
+
+ CHECK_EQ(mState, EXECUTING);
+
+ bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput);
+ bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput);
+
+ if (emulateInputFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexInput);
+ }
+
+ if (emulateOutputFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
+ }
+ }
+
+ while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
+ mBufferFilled.wait(mLock);
+ }
+
+ if (mState == ERROR) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mFilledBuffers.empty()) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ size_t index = *mFilledBuffers.begin();
+ mFilledBuffers.erase(mFilledBuffers.begin());
+
+ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+ info->mMediaBuffer->add_ref();
+ *buffer = info->mMediaBuffer;
+
+ return OK;
+}
+
+void OMXCodec::signalBufferReturned(MediaBuffer *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mMediaBuffer == buffer) {
+ CHECK_EQ(mPortStatus[kPortIndexOutput], ENABLED);
+ fillOutputBuffer(info);
+ return;
+ }
+ }
+
+ CHECK(!"should not be here.");
+}
+
+static const char *imageCompressionFormatString(OMX_IMAGE_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_IMAGE_CodingUnused",
+ "OMX_IMAGE_CodingAutoDetect",
+ "OMX_IMAGE_CodingJPEG",
+ "OMX_IMAGE_CodingJPEG2K",
+ "OMX_IMAGE_CodingEXIF",
+ "OMX_IMAGE_CodingTIFF",
+ "OMX_IMAGE_CodingGIF",
+ "OMX_IMAGE_CodingPNG",
+ "OMX_IMAGE_CodingLZW",
+ "OMX_IMAGE_CodingBMP",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *colorFormatString(OMX_COLOR_FORMATTYPE type) {
+ static const char *kNames[] = {
+ "OMX_COLOR_FormatUnused",
+ "OMX_COLOR_FormatMonochrome",
+ "OMX_COLOR_Format8bitRGB332",
+ "OMX_COLOR_Format12bitRGB444",
+ "OMX_COLOR_Format16bitARGB4444",
+ "OMX_COLOR_Format16bitARGB1555",
+ "OMX_COLOR_Format16bitRGB565",
+ "OMX_COLOR_Format16bitBGR565",
+ "OMX_COLOR_Format18bitRGB666",
+ "OMX_COLOR_Format18bitARGB1665",
+ "OMX_COLOR_Format19bitARGB1666",
+ "OMX_COLOR_Format24bitRGB888",
+ "OMX_COLOR_Format24bitBGR888",
+ "OMX_COLOR_Format24bitARGB1887",
+ "OMX_COLOR_Format25bitARGB1888",
+ "OMX_COLOR_Format32bitBGRA8888",
+ "OMX_COLOR_Format32bitARGB8888",
+ "OMX_COLOR_FormatYUV411Planar",
+ "OMX_COLOR_FormatYUV411PackedPlanar",
+ "OMX_COLOR_FormatYUV420Planar",
+ "OMX_COLOR_FormatYUV420PackedPlanar",
+ "OMX_COLOR_FormatYUV420SemiPlanar",
+ "OMX_COLOR_FormatYUV422Planar",
+ "OMX_COLOR_FormatYUV422PackedPlanar",
+ "OMX_COLOR_FormatYUV422SemiPlanar",
+ "OMX_COLOR_FormatYCbYCr",
+ "OMX_COLOR_FormatYCrYCb",
+ "OMX_COLOR_FormatCbYCrY",
+ "OMX_COLOR_FormatCrYCbY",
+ "OMX_COLOR_FormatYUV444Interleaved",
+ "OMX_COLOR_FormatRawBayer8bit",
+ "OMX_COLOR_FormatRawBayer10bit",
+ "OMX_COLOR_FormatRawBayer8bitcompressed",
+ "OMX_COLOR_FormatL2",
+ "OMX_COLOR_FormatL4",
+ "OMX_COLOR_FormatL8",
+ "OMX_COLOR_FormatL16",
+ "OMX_COLOR_FormatL24",
+ "OMX_COLOR_FormatL32",
+ "OMX_COLOR_FormatYUV420PackedSemiPlanar",
+ "OMX_COLOR_FormatYUV422PackedSemiPlanar",
+ "OMX_COLOR_Format18BitBGR666",
+ "OMX_COLOR_Format24BitARGB6666",
+ "OMX_COLOR_Format24BitABGR6666",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ if (type == OMX_QCOM_COLOR_FormatYVU420SemiPlanar) {
+ return "OMX_QCOM_COLOR_FormatYVU420SemiPlanar";
+ } else if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *videoCompressionFormatString(OMX_VIDEO_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_VIDEO_CodingUnused",
+ "OMX_VIDEO_CodingAutoDetect",
+ "OMX_VIDEO_CodingMPEG2",
+ "OMX_VIDEO_CodingH263",
+ "OMX_VIDEO_CodingMPEG4",
+ "OMX_VIDEO_CodingWMV",
+ "OMX_VIDEO_CodingRV",
+ "OMX_VIDEO_CodingAVC",
+ "OMX_VIDEO_CodingMJPEG",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *audioCodingTypeString(OMX_AUDIO_CODINGTYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_CodingUnused",
+ "OMX_AUDIO_CodingAutoDetect",
+ "OMX_AUDIO_CodingPCM",
+ "OMX_AUDIO_CodingADPCM",
+ "OMX_AUDIO_CodingAMR",
+ "OMX_AUDIO_CodingGSMFR",
+ "OMX_AUDIO_CodingGSMEFR",
+ "OMX_AUDIO_CodingGSMHR",
+ "OMX_AUDIO_CodingPDCFR",
+ "OMX_AUDIO_CodingPDCEFR",
+ "OMX_AUDIO_CodingPDCHR",
+ "OMX_AUDIO_CodingTDMAFR",
+ "OMX_AUDIO_CodingTDMAEFR",
+ "OMX_AUDIO_CodingQCELP8",
+ "OMX_AUDIO_CodingQCELP13",
+ "OMX_AUDIO_CodingEVRC",
+ "OMX_AUDIO_CodingSMV",
+ "OMX_AUDIO_CodingG711",
+ "OMX_AUDIO_CodingG723",
+ "OMX_AUDIO_CodingG726",
+ "OMX_AUDIO_CodingG729",
+ "OMX_AUDIO_CodingAAC",
+ "OMX_AUDIO_CodingMP3",
+ "OMX_AUDIO_CodingSBC",
+ "OMX_AUDIO_CodingVORBIS",
+ "OMX_AUDIO_CodingWMA",
+ "OMX_AUDIO_CodingRA",
+ "OMX_AUDIO_CodingMIDI",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *audioPCMModeString(OMX_AUDIO_PCMMODETYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_PCMModeLinear",
+ "OMX_AUDIO_PCMModeALaw",
+ "OMX_AUDIO_PCMModeMULaw",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *amrBandModeString(OMX_AUDIO_AMRBANDMODETYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_AMRBandModeUnused",
+ "OMX_AUDIO_AMRBandModeNB0",
+ "OMX_AUDIO_AMRBandModeNB1",
+ "OMX_AUDIO_AMRBandModeNB2",
+ "OMX_AUDIO_AMRBandModeNB3",
+ "OMX_AUDIO_AMRBandModeNB4",
+ "OMX_AUDIO_AMRBandModeNB5",
+ "OMX_AUDIO_AMRBandModeNB6",
+ "OMX_AUDIO_AMRBandModeNB7",
+ "OMX_AUDIO_AMRBandModeWB0",
+ "OMX_AUDIO_AMRBandModeWB1",
+ "OMX_AUDIO_AMRBandModeWB2",
+ "OMX_AUDIO_AMRBandModeWB3",
+ "OMX_AUDIO_AMRBandModeWB4",
+ "OMX_AUDIO_AMRBandModeWB5",
+ "OMX_AUDIO_AMRBandModeWB6",
+ "OMX_AUDIO_AMRBandModeWB7",
+ "OMX_AUDIO_AMRBandModeWB8",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+static const char *amrFrameFormatString(OMX_AUDIO_AMRFRAMEFORMATTYPE type) {
+ static const char *kNames[] = {
+ "OMX_AUDIO_AMRFrameFormatConformance",
+ "OMX_AUDIO_AMRFrameFormatIF1",
+ "OMX_AUDIO_AMRFrameFormatIF2",
+ "OMX_AUDIO_AMRFrameFormatFSF",
+ "OMX_AUDIO_AMRFrameFormatRTPPayload",
+ "OMX_AUDIO_AMRFrameFormatITU",
+ };
+
+ size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
+
+ if (type < 0 || (size_t)type >= numNames) {
+ return "UNKNOWN";
+ } else {
+ return kNames[type];
+ }
+}
+
+void OMXCodec::dumpPortStatus(OMX_U32 portIndex) {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ printf("%s Port = {\n", portIndex == kPortIndexInput ? "Input" : "Output");
+
+ CHECK((portIndex == kPortIndexInput && def.eDir == OMX_DirInput)
+ || (portIndex == kPortIndexOutput && def.eDir == OMX_DirOutput));
+
+ printf(" nBufferCountActual = %ld\n", def.nBufferCountActual);
+ printf(" nBufferCountMin = %ld\n", def.nBufferCountMin);
+ printf(" nBufferSize = %ld\n", def.nBufferSize);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainImage:
+ {
+ const OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+
+ printf("\n");
+ printf(" // Image\n");
+ printf(" nFrameWidth = %ld\n", imageDef->nFrameWidth);
+ printf(" nFrameHeight = %ld\n", imageDef->nFrameHeight);
+ printf(" nStride = %ld\n", imageDef->nStride);
+
+ printf(" eCompressionFormat = %s\n",
+ imageCompressionFormatString(imageDef->eCompressionFormat));
+
+ printf(" eColorFormat = %s\n",
+ colorFormatString(imageDef->eColorFormat));
+
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
+
+ printf("\n");
+ printf(" // Video\n");
+ printf(" nFrameWidth = %ld\n", videoDef->nFrameWidth);
+ printf(" nFrameHeight = %ld\n", videoDef->nFrameHeight);
+ printf(" nStride = %ld\n", videoDef->nStride);
+
+ printf(" eCompressionFormat = %s\n",
+ videoCompressionFormatString(videoDef->eCompressionFormat));
+
+ printf(" eColorFormat = %s\n",
+ colorFormatString(videoDef->eColorFormat));
+
+ break;
+ }
+
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
+
+ printf("\n");
+ printf(" // Audio\n");
+ printf(" eEncoding = %s\n",
+ audioCodingTypeString(audioDef->eEncoding));
+
+ if (audioDef->eEncoding == OMX_AUDIO_CodingPCM) {
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ InitOMXParams(&params);
+ params.nPortIndex = portIndex;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+ CHECK_EQ(err, OK);
+
+ printf(" nSamplingRate = %ld\n", params.nSamplingRate);
+ printf(" nChannels = %ld\n", params.nChannels);
+ printf(" bInterleaved = %d\n", params.bInterleaved);
+ printf(" nBitPerSample = %ld\n", params.nBitPerSample);
+
+ printf(" eNumData = %s\n",
+ params.eNumData == OMX_NumericalDataSigned
+ ? "signed" : "unsigned");
+
+ printf(" ePCMMode = %s\n", audioPCMModeString(params.ePCMMode));
+ } else if (audioDef->eEncoding == OMX_AUDIO_CodingAMR) {
+ OMX_AUDIO_PARAM_AMRTYPE amr;
+ InitOMXParams(&amr);
+ amr.nPortIndex = portIndex;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
+ CHECK_EQ(err, OK);
+
+ printf(" nChannels = %ld\n", amr.nChannels);
+ printf(" eAMRBandMode = %s\n",
+ amrBandModeString(amr.eAMRBandMode));
+ printf(" eAMRFrameFormat = %s\n",
+ amrFrameFormatString(amr.eAMRFrameFormat));
+ }
+
+ break;
+ }
+
+ default:
+ {
+ printf(" // Unknown\n");
+ break;
+ }
+ }
+
+ printf("}\n");
+}
+
+void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) {
+ mOutputFormat = new MetaData;
+ mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->get_parameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ CHECK_EQ(err, OK);
+
+ switch (def.eDomain) {
+ case OMX_PortDomainImage:
+ {
+ OMX_IMAGE_PORTDEFINITIONTYPE *imageDef = &def.format.image;
+ CHECK_EQ(imageDef->eCompressionFormat, OMX_IMAGE_CodingUnused);
+
+ mOutputFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ mOutputFormat->setInt32(kKeyColorFormat, imageDef->eColorFormat);
+ mOutputFormat->setInt32(kKeyWidth, imageDef->nFrameWidth);
+ mOutputFormat->setInt32(kKeyHeight, imageDef->nFrameHeight);
+ break;
+ }
+
+ case OMX_PortDomainAudio:
+ {
+ OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+
+ if (audio_def->eEncoding == OMX_AUDIO_CodingPCM) {
+ OMX_AUDIO_PARAM_PCMMODETYPE params;
+ InitOMXParams(&params);
+ params.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(params.eNumData, OMX_NumericalDataSigned);
+ CHECK_EQ(params.nBitPerSample, 16);
+ CHECK_EQ(params.ePCMMode, OMX_AUDIO_PCMModeLinear);
+
+ int32_t numChannels, sampleRate;
+ inputFormat->findInt32(kKeyChannelCount, &numChannels);
+ inputFormat->findInt32(kKeySampleRate, &sampleRate);
+
+ if ((OMX_U32)numChannels != params.nChannels) {
+ LOGW("Codec outputs a different number of channels than "
+ "the input stream contains.");
+ }
+
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+
+ // Use the codec-advertised number of channels, as some
+ // codecs appear to output stereo even if the input data is
+ // mono.
+ mOutputFormat->setInt32(kKeyChannelCount, params.nChannels);
+
+ // The codec-reported sampleRate is not reliable...
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ } else if (audio_def->eEncoding == OMX_AUDIO_CodingAMR) {
+ OMX_AUDIO_PARAM_AMRTYPE amr;
+ InitOMXParams(&amr);
+ amr.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->get_parameter(
+ mNode, OMX_IndexParamAudioAmr, &amr, sizeof(amr));
+ CHECK_EQ(err, OK);
+
+ CHECK_EQ(amr.nChannels, 1);
+ mOutputFormat->setInt32(kKeyChannelCount, 1);
+
+ if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeNB0
+ && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeNB7) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB);
+ mOutputFormat->setInt32(kKeySampleRate, 8000);
+ } else if (amr.eAMRBandMode >= OMX_AUDIO_AMRBandModeWB0
+ && amr.eAMRBandMode <= OMX_AUDIO_AMRBandModeWB8) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB);
+ mOutputFormat->setInt32(kKeySampleRate, 16000);
+ } else {
+ CHECK(!"Unknown AMR band mode.");
+ }
+ } else if (audio_def->eEncoding == OMX_AUDIO_CodingAAC) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ } else {
+ CHECK(!"Should not be here. Unknown audio encoding.");
+ }
+ break;
+ }
+
+ case OMX_PortDomainVideo:
+ {
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW);
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+ } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+ } else {
+ CHECK(!"Unknown compression format.");
+ }
+
+ if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
+ // This component appears to be lying to me.
+ mOutputFormat->setInt32(
+ kKeyWidth, (video_def->nFrameWidth + 15) & -16);
+ mOutputFormat->setInt32(
+ kKeyHeight, (video_def->nFrameHeight + 15) & -16);
+ } else {
+ mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
+ mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
+ }
+
+ mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+ break;
+ }
+
+ default:
+ {
+ CHECK(!"should not be here, neither audio nor video.");
+ break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+status_t QueryCodecs(
+ const sp<IOMX> &omx,
+ const char *mime, bool queryDecoders,
+ Vector<CodecCapabilities> *results) {
+ results->clear();
+
+ for (int index = 0;; ++index) {
+ const char *componentName;
+
+ if (!queryDecoders) {
+ componentName = GetCodec(
+ kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+ mime, index);
+ } else {
+ componentName = GetCodec(
+ kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+ mime, index);
+ }
+
+ if (!componentName) {
+ return OK;
+ }
+
+ IOMX::node_id node;
+ status_t err = omx->allocate_node(componentName, &node);
+
+ if (err != OK) {
+ continue;
+ }
+
+ OMXCodec::setComponentRole(omx, node, queryDecoders, mime);
+
+ results->push();
+ CodecCapabilities *caps = &results->editItemAt(results->size() - 1);
+ caps->mComponentName = componentName;
+
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE param;
+ InitOMXParams(&param);
+
+ param.nPortIndex = queryDecoders ? 0 : 1;
+
+ for (param.nProfileIndex = 0;; ++param.nProfileIndex) {
+ err = omx->get_parameter(
+ node, OMX_IndexParamVideoProfileLevelQuerySupported,
+ &param, sizeof(param));
+
+ if (err != OK) {
+ break;
+ }
+
+ CodecProfileLevel profileLevel;
+ profileLevel.mProfile = param.eProfile;
+ profileLevel.mLevel = param.eLevel;
+
+ caps->mProfileLevels.push(profileLevel);
+ }
+
+ CHECK_EQ(omx->free_node(node), OK);
+ }
+}
+
+} // namespace android
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
new file mode 100644
index 0000000..8efa7c7
--- /dev/null
+++ b/media/libstagefright/SampleTable.cpp
@@ -0,0 +1,578 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SampleTable"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+SampleTable::SampleTable(const sp<DataSource> &source)
+ : mDataSource(source),
+ mChunkOffsetOffset(-1),
+ mChunkOffsetType(0),
+ mNumChunkOffsets(0),
+ mSampleToChunkOffset(-1),
+ mNumSampleToChunkOffsets(0),
+ mSampleSizeOffset(-1),
+ mSampleSizeFieldSize(0),
+ mDefaultSampleSize(0),
+ mNumSampleSizes(0),
+ mTimeToSampleCount(0),
+ mTimeToSample(NULL),
+ mSyncSampleOffset(-1),
+ mNumSyncSamples(0) {
+}
+
+SampleTable::~SampleTable() {
+ delete[] mTimeToSample;
+ mTimeToSample = NULL;
+}
+
+status_t SampleTable::setChunkOffsetParams(
+ uint32_t type, off_t data_offset, off_t data_size) {
+ if (mChunkOffsetOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64);
+
+ mChunkOffsetOffset = data_offset;
+ mChunkOffsetType = type;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumChunkOffsets = U32_AT(&header[4]);
+
+ if (mChunkOffsetType == kChunkOffsetType32) {
+ if (data_size < 8 + mNumChunkOffsets * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if (data_size < 8 + mNumChunkOffsets * 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleToChunkParams(
+ off_t data_offset, off_t data_size) {
+ if (mSampleToChunkOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ mSampleToChunkOffset = data_offset;
+
+ if (data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSampleToChunkOffsets = U32_AT(&header[4]);
+
+ if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
+ return ERROR_MALFORMED;
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSampleSizeParams(
+ uint32_t type, off_t data_offset, off_t data_size) {
+ if (mSampleSizeOffset >= 0) {
+ return ERROR_MALFORMED;
+ }
+
+ CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
+
+ mSampleSizeOffset = data_offset;
+
+ if (data_size < 12) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[12];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mDefaultSampleSize = U32_AT(&header[4]);
+ mNumSampleSizes = U32_AT(&header[8]);
+
+ if (type == kSampleSizeType32) {
+ mSampleSizeFieldSize = 32;
+
+ if (mDefaultSampleSize != 0) {
+ return OK;
+ }
+
+ if (data_size < 12 + mNumSampleSizes * 4) {
+ return ERROR_MALFORMED;
+ }
+ } else {
+ if ((mDefaultSampleSize & 0xffffff00) != 0) {
+ // The high 24 bits are reserved and must be 0.
+ return ERROR_MALFORMED;
+ }
+
+ mSampleSizeFieldSize = mDefaultSampleSize & 0xf;
+ mDefaultSampleSize = 0;
+
+ if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
+ && mSampleSizeFieldSize != 16) {
+ return ERROR_MALFORMED;
+ }
+
+ if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
+ return ERROR_MALFORMED;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setTimeToSampleParams(
+ off_t data_offset, off_t data_size) {
+ if (mTimeToSample != NULL || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mTimeToSampleCount = U32_AT(&header[4]);
+ mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+
+ size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
+ if (mDataSource->read_at(
+ data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
+ return ERROR_IO;
+ }
+
+ for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+ mTimeToSample[i] = ntohl(mTimeToSample[i]);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
+ if (mSyncSampleOffset >= 0 || data_size < 8) {
+ return ERROR_MALFORMED;
+ }
+
+ mSyncSampleOffset = data_offset;
+
+ uint8_t header[8];
+ if (mDataSource->read_at(
+ data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+ return ERROR_IO;
+ }
+
+ if (U32_AT(header) != 0) {
+ // Expected version = 0, flags = 0.
+ return ERROR_MALFORMED;
+ }
+
+ mNumSyncSamples = U32_AT(&header[4]);
+
+ if (mNumSyncSamples < 2) {
+ LOGW("Table of sync samples is empty or has only a single entry!");
+ }
+ return OK;
+}
+
+uint32_t SampleTable::countChunkOffsets() const {
+ return mNumChunkOffsets;
+}
+
+status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
+ *offset = 0;
+
+ if (mChunkOffsetOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (chunk_index >= mNumChunkOffsets) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mChunkOffsetType == kChunkOffsetType32) {
+ uint32_t offset32;
+
+ if (mDataSource->read_at(
+ mChunkOffsetOffset + 8 + 4 * chunk_index,
+ &offset32,
+ sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntohl(offset32);
+ } else {
+ CHECK_EQ(mChunkOffsetType, kChunkOffsetType64);
+
+ uint64_t offset64;
+ if (mDataSource->read_at(
+ mChunkOffsetOffset + 8 + 8 * chunk_index,
+ &offset64,
+ sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+ return ERROR_IO;
+ }
+
+ *offset = ntoh64(offset64);
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getChunkForSample(
+ uint32_t sample_index,
+ uint32_t *chunk_index,
+ uint32_t *chunk_relative_sample_index,
+ uint32_t *desc_index) {
+ *chunk_index = 0;
+ *chunk_relative_sample_index = 0;
+ *desc_index = 0;
+
+ if (mSampleToChunkOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (sample_index >= countSamples()) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ uint32_t first_chunk = 0;
+ uint32_t samples_per_chunk = 0;
+ uint32_t chunk_desc_index = 0;
+
+ uint32_t index = 0;
+ while (index < mNumSampleToChunkOffsets) {
+ uint8_t buffer[12];
+ if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12,
+ buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+ return ERROR_IO;
+ }
+
+ uint32_t stop_chunk = U32_AT(buffer);
+ if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
+ break;
+ }
+
+ sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
+ first_chunk = stop_chunk;
+ samples_per_chunk = U32_AT(&buffer[4]);
+ chunk_desc_index = U32_AT(&buffer[8]);
+
+ ++index;
+ }
+
+ *chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
+ *chunk_relative_sample_index = sample_index % samples_per_chunk;
+ *desc_index = chunk_desc_index;
+
+ return OK;
+}
+
+uint32_t SampleTable::countSamples() const {
+ return mNumSampleSizes;
+}
+
+status_t SampleTable::getSampleSize(
+ uint32_t sample_index, size_t *sample_size) {
+ *sample_size = 0;
+
+ if (mSampleSizeOffset < 0) {
+ return ERROR_MALFORMED;
+ }
+
+ if (sample_index >= mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ if (mDefaultSampleSize > 0) {
+ *sample_size = mDefaultSampleSize;
+ return OK;
+ }
+
+ switch (mSampleSizeFieldSize) {
+ case 32:
+ {
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + 4 * sample_index,
+ sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = ntohl(*sample_size);
+ break;
+ }
+
+ case 16:
+ {
+ uint16_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + 2 * sample_index,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = ntohs(x);
+ break;
+ }
+
+ case 8:
+ {
+ uint8_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + sample_index,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = x;
+ break;
+ }
+
+ default:
+ {
+ CHECK_EQ(mSampleSizeFieldSize, 4);
+
+ uint8_t x;
+ if (mDataSource->read_at(
+ mSampleSizeOffset + 12 + sample_index / 2,
+ &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+ return ERROR_IO;
+ }
+
+ *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
+ break;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getSampleOffsetAndSize(
+ uint32_t sample_index, off_t *offset, size_t *size) {
+ Mutex::Autolock autoLock(mLock);
+
+ *offset = 0;
+ *size = 0;
+
+ uint32_t chunk_index;
+ uint32_t chunk_relative_sample_index;
+ uint32_t desc_index;
+ status_t err = getChunkForSample(
+ sample_index, &chunk_index, &chunk_relative_sample_index,
+ &desc_index);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = getChunkOffset(chunk_index, offset);
+
+ if (err != OK) {
+ return err;
+ }
+
+ for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
+ size_t sample_size;
+ err = getSampleSize(sample_index - j - 1, &sample_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *offset += sample_size;
+ }
+
+ err = getSampleSize(sample_index, size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getMaxSampleSize(size_t *max_size) {
+ Mutex::Autolock autoLock(mLock);
+
+ *max_size = 0;
+
+ for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
+ size_t sample_size;
+ status_t err = getSampleSize(i, &sample_size);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (sample_size > *max_size) {
+ *max_size = sample_size;
+ }
+ }
+
+ return OK;
+}
+
+status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
+ // XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (sample_index >= mNumSampleSizes) {
+ return ERROR_OUT_OF_RANGE;
+ }
+
+ uint32_t cur_sample = 0;
+ *time = 0;
+ for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+ uint32_t n = mTimeToSample[2 * i];
+ uint32_t delta = mTimeToSample[2 * i + 1];
+
+ if (sample_index < cur_sample + n) {
+ *time += delta * (sample_index - cur_sample);
+
+ return OK;
+ }
+
+ *time += delta * n;
+ cur_sample += n;
+ }
+
+ return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSample(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ Mutex::Autolock autoLock(mLock);
+
+ uint32_t cur_sample = 0;
+ uint32_t time = 0;
+ for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+ uint32_t n = mTimeToSample[2 * i];
+ uint32_t delta = mTimeToSample[2 * i + 1];
+
+ if (req_time < time + n * delta) {
+ int j = (req_time - time) / delta;
+
+ *sample_index = cur_sample + j;
+
+ if (flags & kSyncSample_Flag) {
+ return findClosestSyncSample(*sample_index, sample_index);
+ }
+
+ return OK;
+ }
+
+ time += delta * n;
+ cur_sample += n;
+ }
+
+ return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSyncSample(
+ uint32_t start_sample_index, uint32_t *sample_index) {
+ *sample_index = 0;
+
+ if (mSyncSampleOffset < 0) {
+ // All samples are sync-samples.
+ *sample_index = start_sample_index;
+ return OK;
+ }
+
+ uint32_t x;
+ uint32_t left = 0;
+ uint32_t right = mNumSyncSamples;
+ while (left < right) {
+ uint32_t mid = (left + right) / 2;
+ if (mDataSource->read_at(
+ mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
+ return ERROR_IO;
+ }
+
+ x = ntohl(x);
+
+ if (x < (start_sample_index + 1)) {
+ left = mid + 1;
+ } else if (x > (start_sample_index + 1)) {
+ right = mid;
+ } else {
+ break;
+ }
+ }
+
+ *sample_index = x - 1;
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
new file mode 100644
index 0000000..8e8f4fa
--- /dev/null
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+ShoutcastSource::ShoutcastSource(HTTPStream *http)
+ : mHttp(http),
+ mMetaDataOffset(0),
+ mBytesUntilMetaData(0),
+ mGroup(NULL),
+ mStarted(false) {
+ string metaint;
+ if (mHttp->find_header_value("icy-metaint", &metaint)) {
+ char *end;
+ const char *start = metaint.c_str();
+ mMetaDataOffset = strtol(start, &end, 10);
+ CHECK(end > start && *end == '\0');
+ CHECK(mMetaDataOffset > 0);
+
+ mBytesUntilMetaData = mMetaDataOffset;
+ }
+}
+
+ShoutcastSource::~ShoutcastSource() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mHttp;
+ mHttp = NULL;
+}
+
+status_t ShoutcastSource::start(MetaData *) {
+ CHECK(!mStarted);
+
+ mGroup = new MediaBufferGroup;
+ mGroup->add_buffer(new MediaBuffer(4096)); // XXX
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t ShoutcastSource::stop() {
+ CHECK(mStarted);
+
+ delete mGroup;
+ mGroup = NULL;
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> ShoutcastSource::getFormat() {
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ meta->setInt32(kKeySampleRate, 44100);
+ meta->setInt32(kKeyChannelCount, 2); // XXX
+
+ return meta;
+}
+
+status_t ShoutcastSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ CHECK(mStarted);
+
+ *out = NULL;
+
+ int64_t seekTimeUs;
+ if (options && options->getSeekTo(&seekTimeUs)) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ MediaBuffer *buffer;
+ status_t err = mGroup->acquire_buffer(&buffer);
+ if (err != OK) {
+ return err;
+ }
+
+ *out = buffer;
+
+ size_t num_bytes = buffer->size();
+ if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) {
+ num_bytes = mBytesUntilMetaData;
+ }
+
+ ssize_t n = mHttp->receive(buffer->data(), num_bytes);
+
+ if (n <= 0) {
+ return (status_t)n;
+ }
+
+ buffer->set_range(0, n);
+
+ mBytesUntilMetaData -= (size_t)n;
+
+ if (mBytesUntilMetaData == 0) {
+ unsigned char num_16_byte_blocks = 0;
+ n = mHttp->receive((char *)&num_16_byte_blocks, 1);
+ CHECK_EQ(n, 1);
+
+ char meta[255 * 16];
+ size_t meta_size = num_16_byte_blocks * 16;
+ size_t meta_length = 0;
+ while (meta_length < meta_size) {
+ n = mHttp->receive(&meta[meta_length], meta_size - meta_length);
+ if (n <= 0) {
+ return (status_t)n;
+ }
+
+ meta_length += (size_t) n;
+ }
+
+ while (meta_length > 0 && meta[meta_length - 1] == '\0') {
+ --meta_length;
+ }
+
+ if (meta_length > 0) {
+ // Technically we should probably attach this meta data to the
+ // next buffer. XXX
+ buffer->meta_data()->setData('shou', 'shou', meta, meta_length);
+ }
+
+ mBytesUntilMetaData = mMetaDataOffset;
+ }
+
+ return OK;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/TimeSource.cpp b/media/libstagefright/TimeSource.cpp
new file mode 100644
index 0000000..d987fbf
--- /dev/null
+++ b/media/libstagefright/TimeSource.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <sys/time.h>
+
+#include <media/stagefright/TimeSource.h>
+
+namespace android {
+
+SystemTimeSource::SystemTimeSource()
+ : mStartTimeUs(GetSystemTimeUs()) {
+}
+
+int64_t SystemTimeSource::getRealTimeUs() {
+ return GetSystemTimeUs() - mStartTimeUs;
+}
+
+// static
+int64_t SystemTimeSource::GetSystemTimeUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
new file mode 100644
index 0000000..3d85f75
--- /dev/null
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#define LOG_TAG "TimedEventQueue"
+#include <utils/Log.h>
+
+#include <sys/time.h>
+
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/TimedEventQueue.h>
+
+namespace android {
+
+TimedEventQueue::TimedEventQueue()
+ : mRunning(false),
+ mStopped(false) {
+}
+
+TimedEventQueue::~TimedEventQueue() {
+ stop();
+}
+
+void TimedEventQueue::start() {
+ if (mRunning) {
+ return;
+ }
+
+ mStopped = false;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+
+ mRunning = true;
+}
+
+void TimedEventQueue::stop(bool flush) {
+ if (!mRunning) {
+ return;
+ }
+
+ if (flush) {
+ postEventToBack(new StopEvent);
+ } else {
+ postTimedEvent(new StopEvent, INT64_MIN);
+ }
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ mQueue.clear();
+
+ mRunning = false;
+}
+
+void TimedEventQueue::postEvent(const sp<Event> &event) {
+ // Reserve an earlier timeslot an INT64_MIN to be able to post
+ // the StopEvent to the absolute head of the queue.
+ postTimedEvent(event, INT64_MIN + 1);
+}
+
+void TimedEventQueue::postEventToBack(const sp<Event> &event) {
+ postTimedEvent(event, INT64_MAX);
+}
+
+void TimedEventQueue::postEventWithDelay(
+ const sp<Event> &event, int64_t delay_us) {
+ CHECK(delay_us >= 0);
+ postTimedEvent(event, getRealTimeUs() + delay_us);
+}
+
+void TimedEventQueue::postTimedEvent(
+ const sp<Event> &event, int64_t realtime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ List<QueueItem>::iterator it = mQueue.begin();
+ while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
+ ++it;
+ }
+
+ QueueItem item;
+ item.event = event;
+ item.realtime_us = realtime_us;
+
+ if (it == mQueue.begin()) {
+ mQueueHeadChangedCondition.signal();
+ }
+
+ mQueue.insert(it, item);
+
+ mQueueNotEmptyCondition.signal();
+}
+
+bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
+ Mutex::Autolock autoLock(mLock);
+
+ List<QueueItem>::iterator it = mQueue.begin();
+ while (it != mQueue.end() && (*it).event != event) {
+ ++it;
+ }
+
+ if (it == mQueue.end()) {
+ return false;
+ }
+
+ if (it == mQueue.begin()) {
+ mQueueHeadChangedCondition.signal();
+ }
+
+ mQueue.erase(it);
+
+ return true;
+}
+
+// static
+int64_t TimedEventQueue::getRealTimeUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+// static
+void *TimedEventQueue::ThreadWrapper(void *me) {
+ static_cast<TimedEventQueue *>(me)->threadEntry();
+
+ return NULL;
+}
+
+void TimedEventQueue::threadEntry() {
+ for (;;) {
+ int64_t now_us;
+ sp<Event> event;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mStopped) {
+ break;
+ }
+
+ while (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(mLock);
+ }
+
+ List<QueueItem>::iterator it;
+ for (;;) {
+ it = mQueue.begin();
+
+ now_us = getRealTimeUs();
+ int64_t when_us = (*it).realtime_us;
+
+ int64_t delay_us;
+ if (when_us < 0 || when_us == INT64_MAX) {
+ delay_us = 0;
+ } else {
+ delay_us = when_us - now_us;
+ }
+
+ if (delay_us <= 0) {
+ break;
+ }
+
+ status_t err = mQueueHeadChangedCondition.waitRelative(
+ mLock, delay_us * 1000);
+
+ if (err == -ETIMEDOUT) {
+ now_us = getRealTimeUs();
+ break;
+ }
+ }
+
+ event = (*it).event;
+ mQueue.erase(it);
+ }
+
+ // Fire event with the lock NOT held.
+ event->fire(this, now_us);
+ }
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
new file mode 100644
index 0000000..2720f93
--- /dev/null
+++ b/media/libstagefright/Utils.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+uint16_t U16_AT(const uint8_t *ptr) {
+ return ptr[0] << 8 | ptr[1];
+}
+
+uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+uint64_t U64_AT(const uint8_t *ptr) {
+ return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
+}
+
+// XXX warning: these won't work on big-endian host.
+uint64_t ntoh64(uint64_t x) {
+ return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
+}
+
+uint64_t hton64(uint64_t x) {
+ return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
new file mode 100644
index 0000000..77e42be
--- /dev/null
+++ b/media/libstagefright/omx/Android.mk
@@ -0,0 +1,33 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Set up the OpenCore variables.
+include external/opencore/Config.mk
+LOCAL_C_INCLUDES := $(PV_INCLUDES)
+LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
+
+LOCAL_C_INCLUDES += $(TOP)/hardware/ti/omap3/liboverlay
+
+LOCAL_SRC_FILES:= \
+ OMX.cpp \
+ QComHardwareRenderer.cpp \
+ SoftwareRenderer.cpp \
+ TIHardwareRenderer.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libmedia \
+ libutils \
+ libui \
+ libcutils \
+ libopencore_common
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+ LOCAL_LDLIBS += -lpthread
+endif
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright_omx
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
new file mode 100644
index 0000000..1e59b52
--- /dev/null
+++ b/media/libstagefright/omx/OMX.cpp
@@ -0,0 +1,639 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "OMX"
+#include <utils/Log.h>
+
+#include <sys/socket.h>
+
+#include "OMX.h"
+#include "OMXRenderer.h"
+
+#include "pv_omxcore.h"
+
+#include <binder/IMemory.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <media/stagefright/TIHardwareRenderer.h>
+#include <media/stagefright/VideoRenderer.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+class NodeMeta {
+public:
+ NodeMeta(OMX *owner)
+ : mOwner(owner),
+ mHandle(NULL) {
+ }
+
+ OMX *owner() const {
+ return mOwner;
+ }
+
+ void setHandle(OMX_HANDLETYPE handle) {
+ CHECK_EQ(mHandle, NULL);
+ mHandle = handle;
+ }
+
+ OMX_HANDLETYPE handle() const {
+ return mHandle;
+ }
+
+ void setObserver(const sp<IOMXObserver> &observer) {
+ mObserver = observer;
+ }
+
+ sp<IOMXObserver> observer() {
+ return mObserver;
+ }
+
+private:
+ OMX *mOwner;
+ OMX_HANDLETYPE mHandle;
+ sp<IOMXObserver> mObserver;
+
+ NodeMeta(const NodeMeta &);
+ NodeMeta &operator=(const NodeMeta &);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct OMX::CallbackDispatcher : public RefBase {
+ CallbackDispatcher();
+
+ void post(const omx_message &msg);
+
+protected:
+ virtual ~CallbackDispatcher();
+
+private:
+ Mutex mLock;
+ bool mDone;
+ Condition mQueueChanged;
+ List<omx_message> mQueue;
+
+ pthread_t mThread;
+
+ void dispatch(const omx_message &msg);
+
+ static void *ThreadWrapper(void *me);
+ void threadEntry();
+
+ CallbackDispatcher(const CallbackDispatcher &);
+ CallbackDispatcher &operator=(const CallbackDispatcher &);
+};
+
+OMX::CallbackDispatcher::CallbackDispatcher()
+ : mDone(false) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+}
+
+OMX::CallbackDispatcher::~CallbackDispatcher() {
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ mDone = true;
+ mQueueChanged.signal();
+ }
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+}
+
+void OMX::CallbackDispatcher::post(const omx_message &msg) {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(msg);
+ mQueueChanged.signal();
+}
+
+void OMX::CallbackDispatcher::dispatch(const omx_message &msg) {
+ NodeMeta *meta = static_cast<NodeMeta *>(msg.node);
+
+ sp<IOMXObserver> observer = meta->observer();
+ if (observer.get() != NULL) {
+ observer->on_message(msg);
+ }
+}
+
+// static
+void *OMX::CallbackDispatcher::ThreadWrapper(void *me) {
+ static_cast<CallbackDispatcher *>(me)->threadEntry();
+
+ return NULL;
+}
+
+void OMX::CallbackDispatcher::threadEntry() {
+ for (;;) {
+ omx_message msg;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ while (!mDone && mQueue.empty()) {
+ mQueueChanged.wait(mLock);
+ }
+
+ if (mDone) {
+ break;
+ }
+
+ msg = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+ }
+
+ dispatch(msg);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BufferMeta {
+public:
+ BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
+ : mOwner(owner),
+ mMem(mem),
+ mIsBackup(is_backup) {
+ }
+
+ BufferMeta(OMX *owner, size_t size)
+ : mOwner(owner),
+ mSize(size),
+ mIsBackup(false) {
+ }
+
+ void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->pBuffer + header->nOffset,
+ header->nFilledLen);
+ }
+
+ void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
+ if (!mIsBackup) {
+ return;
+ }
+
+ memcpy(header->pBuffer + header->nOffset,
+ (const OMX_U8 *)mMem->pointer() + header->nOffset,
+ header->nFilledLen);
+ }
+
+private:
+ OMX *mOwner;
+ sp<IMemory> mMem;
+ size_t mSize;
+ bool mIsBackup;
+
+ BufferMeta(const BufferMeta &);
+ BufferMeta &operator=(const BufferMeta &);
+};
+
+// static
+OMX_CALLBACKTYPE OMX::kCallbacks = {
+ &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
+};
+
+// static
+OMX_ERRORTYPE OMX::OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnEmptyBufferDone(meta, pBuffer);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+ NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+ return meta->owner()->OnFillBufferDone(meta, pBuffer);
+}
+
+OMX::OMX()
+ : mDispatcher(new CallbackDispatcher) {
+}
+
+status_t OMX::list_nodes(List<String8> *list) {
+ OMX_MasterInit(); // XXX Put this somewhere else.
+
+ list->clear();
+
+ OMX_U32 index = 0;
+ char componentName[256];
+ while (OMX_MasterComponentNameEnum(componentName, sizeof(componentName), index)
+ == OMX_ErrorNone) {
+ list->push_back(String8(componentName));
+
+ ++index;
+ }
+
+ return OK;
+}
+
+status_t OMX::allocate_node(const char *name, node_id *node) {
+ Mutex::Autolock autoLock(mLock);
+
+ *node = 0;
+
+ OMX_MasterInit(); // XXX Put this somewhere else.
+
+ NodeMeta *meta = new NodeMeta(this);
+
+ OMX_HANDLETYPE handle;
+ OMX_ERRORTYPE err = OMX_MasterGetHandle(
+ &handle, const_cast<char *>(name), meta, &kCallbacks);
+
+ if (err != OMX_ErrorNone) {
+ LOGE("FAILED to allocate omx component '%s'", name);
+
+ delete meta;
+ meta = NULL;
+
+ return UNKNOWN_ERROR;
+ }
+
+ meta->setHandle(handle);
+
+ *node = meta;
+
+ return OK;
+}
+
+status_t OMX::free_node(node_id node) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle());
+
+ delete meta;
+ meta = NULL;
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_SetParameter(meta->handle(), index, const_cast<void *>(params));
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err = OMX_GetConfig(meta->handle(), index, params);
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size) {
+ Mutex::Autolock autoLock(mLock);
+
+ NodeMeta *meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_SetConfig(meta->handle(), index, const_cast<void *>(params));
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, params);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta,
+ params->size(), static_cast<OMX_U8 *>(params->pointer()));
+
+ if (err != OMX_ErrorNone) {
+ LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, size);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_AllocateBuffer(node_meta->handle(), &header, port_index,
+ buffer_meta, size);
+
+ if (err != OMX_ErrorNone) {
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer) {
+ Mutex::Autolock autoLock(mLock);
+
+ BufferMeta *buffer_meta = new BufferMeta(this, params, true);
+
+ OMX_BUFFERHEADERTYPE *header;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_AllocateBuffer(
+ node_meta->handle(), &header, port_index, buffer_meta,
+ params->size());
+
+ if (err != OMX_ErrorNone) {
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ *buffer = 0;
+ return UNKNOWN_ERROR;
+ }
+
+ *buffer = header;
+
+ return OK;
+}
+
+status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+ OMX_ERRORTYPE err =
+ OMX_FreeBuffer(node_meta->handle(), port_index, header);
+
+ delete buffer_meta;
+ buffer_meta = NULL;
+
+ return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+OMX_ERRORTYPE OMX::OnEvent(
+ NodeMeta *meta,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData) {
+ LOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
+
+ omx_message msg;
+ msg.type = omx_message::EVENT;
+ msg.node = meta;
+ msg.u.event_data.event = eEvent;
+ msg.u.event_data.data1 = nData1;
+ msg.u.event_data.data2 = nData2;
+
+ mDispatcher->post(msg);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ LOGV("OnEmptyBufferDone buffer=%p", pBuffer);
+
+ omx_message msg;
+ msg.type = omx_message::EMPTY_BUFFER_DONE;
+ msg.node = meta;
+ msg.u.buffer_data.buffer = pBuffer;
+
+ mDispatcher->post(msg);
+
+ return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ LOGV("OnFillBufferDone buffer=%p", pBuffer);
+ BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate);
+ buffer_meta->CopyFromOMX(pBuffer);
+
+ omx_message msg;
+ msg.type = omx_message::FILL_BUFFER_DONE;
+ msg.node = meta;
+ msg.u.extended_buffer_data.buffer = pBuffer;
+ msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
+ msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
+ msg.u.extended_buffer_data.flags = pBuffer->nFlags;
+ msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
+ msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
+
+ mDispatcher->post(msg);
+
+ return OMX_ErrorNone;
+}
+
+status_t OMX::observe_node(
+ node_id node, const sp<IOMXObserver> &observer) {
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ node_meta->setObserver(observer);
+
+ return OK;
+}
+
+void OMX::fill_buffer(node_id node, buffer_id buffer) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = 0;
+ header->nOffset = 0;
+ header->nFlags = 0;
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_FillThisBuffer(node_meta->handle(), header);
+ CHECK_EQ(err, OMX_ErrorNone);
+}
+
+void OMX::empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+ header->nFilledLen = range_length;
+ header->nOffset = range_offset;
+ header->nFlags = flags;
+ header->nTimeStamp = timestamp;
+
+ BufferMeta *buffer_meta =
+ static_cast<BufferMeta *>(header->pAppPrivate);
+ buffer_meta->CopyToOMX(header);
+
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_EmptyThisBuffer(node_meta->handle(), header);
+ CHECK_EQ(err, OMX_ErrorNone);
+}
+
+status_t OMX::get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index) {
+ NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+ OMX_ERRORTYPE err =
+ OMX_GetExtensionIndex(
+ node_meta->handle(),
+ const_cast<char *>(parameter_name), index);
+
+ return err == OMX_ErrorNone ? OK : UNKNOWN_ERROR;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+sp<IOMXRenderer> OMX::createRenderer(
+ const sp<ISurface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight) {
+ VideoRenderer *impl = NULL;
+
+ static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+ if (colorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
+ && !strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
+ LOGW("Using QComHardwareRenderer.");
+ impl =
+ new QComHardwareRenderer(
+ surface,
+ displayWidth, displayHeight,
+ encodedWidth, encodedHeight);
+ } else if (colorFormat == OMX_COLOR_FormatCbYCrY
+ && !strcmp(componentName, "OMX.TI.Video.Decoder")) {
+ LOGW("Using TIHardwareRenderer.");
+ impl =
+ new TIHardwareRenderer(
+ surface,
+ displayWidth, displayHeight,
+ encodedWidth, encodedHeight);
+ } else {
+ LOGW("Using software renderer.");
+ impl = new SoftwareRenderer(
+ surface,
+ displayWidth, displayHeight,
+ encodedWidth, encodedHeight);
+ }
+
+ return new OMXRenderer(impl);
+}
+
+OMXRenderer::OMXRenderer(VideoRenderer *impl)
+ : mImpl(impl) {
+}
+
+OMXRenderer::~OMXRenderer() {
+ delete mImpl;
+ mImpl = NULL;
+}
+
+void OMXRenderer::render(IOMX::buffer_id buffer) {
+ OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+
+ mImpl->render(
+ header->pBuffer + header->nOffset,
+ header->nFilledLen,
+ header->pPlatformPrivate);
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/omx/OMX.h
new file mode 100644
index 0000000..6325f79
--- /dev/null
+++ b/media/libstagefright/omx/OMX.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OMX_H_
+#define ANDROID_OMX_H_
+
+#include <media/IOMX.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class NodeMeta;
+
+class OMX : public BnOMX {
+public:
+ OMX();
+
+ virtual status_t list_nodes(List<String8> *list);
+
+ virtual status_t allocate_node(const char *name, node_id *node);
+ virtual status_t free_node(node_id node);
+
+ virtual status_t send_command(
+ node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+ virtual status_t get_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t set_parameter(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t get_config(
+ node_id node, OMX_INDEXTYPE index,
+ void *params, size_t size);
+
+ virtual status_t set_config(
+ node_id node, OMX_INDEXTYPE index,
+ const void *params, size_t size);
+
+ virtual status_t use_buffer(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t allocate_buffer(
+ node_id node, OMX_U32 port_index, size_t size,
+ buffer_id *buffer);
+
+ virtual status_t allocate_buffer_with_backup(
+ node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+ buffer_id *buffer);
+
+ virtual status_t free_buffer(
+ node_id node, OMX_U32 port_index, buffer_id buffer);
+
+ virtual status_t observe_node(
+ node_id node, const sp<IOMXObserver> &observer);
+
+ virtual void fill_buffer(node_id node, buffer_id buffer);
+
+ virtual void empty_buffer(
+ node_id node,
+ buffer_id buffer,
+ OMX_U32 range_offset, OMX_U32 range_length,
+ OMX_U32 flags, OMX_TICKS timestamp);
+
+ virtual status_t get_extension_index(
+ node_id node,
+ const char *parameter_name,
+ OMX_INDEXTYPE *index);
+
+ virtual sp<IOMXRenderer> createRenderer(
+ const sp<ISurface> &surface,
+ const char *componentName,
+ OMX_COLOR_FORMATTYPE colorFormat,
+ size_t encodedWidth, size_t encodedHeight,
+ size_t displayWidth, size_t displayHeight);
+
+private:
+ static OMX_CALLBACKTYPE kCallbacks;
+
+ Mutex mLock;
+
+ struct CallbackDispatcher;
+ sp<CallbackDispatcher> mDispatcher;
+
+ static OMX_ERRORTYPE OnEvent(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+
+ static OMX_ERRORTYPE OnEmptyBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+ static OMX_ERRORTYPE OnFillBufferDone(
+ OMX_IN OMX_HANDLETYPE hComponent,
+ OMX_IN OMX_PTR pAppData,
+ OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+ OMX_ERRORTYPE OnEvent(
+ NodeMeta *meta,
+ OMX_IN OMX_EVENTTYPE eEvent,
+ OMX_IN OMX_U32 nData1,
+ OMX_IN OMX_U32 nData2,
+ OMX_IN OMX_PTR pEventData);
+
+ OMX_ERRORTYPE OnEmptyBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ OMX_ERRORTYPE OnFillBufferDone(
+ NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+ OMX(const OMX &);
+ OMX &operator=(const OMX &);
+};
+
+} // namespace android
+
+#endif // ANDROID_OMX_H_
diff --git a/media/libstagefright/omx/OMXRenderer.h b/media/libstagefright/omx/OMXRenderer.h
new file mode 100644
index 0000000..4d194ce
--- /dev/null
+++ b/media/libstagefright/omx/OMXRenderer.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OMX_RENDERER_H_
+
+#define OMX_RENDERER_H_
+
+#include <media/IOMX.h>
+
+namespace android {
+
+class VideoRenderer;
+
+class OMXRenderer : public BnOMXRenderer {
+public:
+ // Assumes ownership of "impl".
+ OMXRenderer(VideoRenderer *impl);
+ virtual ~OMXRenderer();
+
+ virtual void render(IOMX::buffer_id buffer);
+
+private:
+ VideoRenderer *mImpl;
+
+ OMXRenderer(const OMXRenderer &);
+ OMXRenderer &operator=(const OMXRenderer &);
+};
+
+} // namespace android
+
+#endif // OMX_RENDERER_H_
diff --git a/media/libstagefright/omx/QComHardwareRenderer.cpp b/media/libstagefright/omx/QComHardwareRenderer.cpp
new file mode 100644
index 0000000..e9930be
--- /dev/null
+++ b/media/libstagefright/omx/QComHardwareRenderer.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef struct PLATFORM_PRIVATE_ENTRY
+{
+ /* Entry type */
+ uint32_t type;
+
+ /* Pointer to platform specific entry */
+ void *entry;
+
+} PLATFORM_PRIVATE_ENTRY;
+
+typedef struct PLATFORM_PRIVATE_LIST
+{
+ /* Number of entries */
+ uint32_t nEntries;
+
+ /* Pointer to array of platform specific entries *
+ * Contiguous block of PLATFORM_PRIVATE_ENTRY elements */
+ PLATFORM_PRIVATE_ENTRY *entryList;
+
+} PLATFORM_PRIVATE_LIST;
+
+// data structures for tunneling buffers
+typedef struct PLATFORM_PRIVATE_PMEM_INFO
+{
+ /* pmem file descriptor */
+ uint32_t pmem_fd;
+ uint32_t offset;
+
+} PLATFORM_PRIVATE_PMEM_INFO;
+
+#define PLATFORM_PRIVATE_PMEM 1
+
+QComHardwareRenderer::QComHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mISurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight),
+ mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
+ CHECK(mISurface.get() != NULL);
+ CHECK(mDecodedWidth > 0);
+ CHECK(mDecodedHeight > 0);
+}
+
+QComHardwareRenderer::~QComHardwareRenderer() {
+ mISurface->unregisterBuffers();
+}
+
+void QComHardwareRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ size_t offset;
+ if (!getOffset(platformPrivate, &offset)) {
+ return;
+ }
+
+ mISurface->postBuffer(offset);
+}
+
+bool QComHardwareRenderer::getOffset(void *platformPrivate, size_t *offset) {
+ *offset = 0;
+
+ PLATFORM_PRIVATE_LIST *list = (PLATFORM_PRIVATE_LIST *)platformPrivate;
+ for (uint32_t i = 0; i < list->nEntries; ++i) {
+ if (list->entryList[i].type != PLATFORM_PRIVATE_PMEM) {
+ continue;
+ }
+
+ PLATFORM_PRIVATE_PMEM_INFO *info =
+ (PLATFORM_PRIVATE_PMEM_INFO *)list->entryList[i].entry;
+
+ if (info != NULL) {
+ if (mMemoryHeap.get() == NULL) {
+ publishBuffers(info->pmem_fd);
+ }
+
+ if (mMemoryHeap.get() == NULL) {
+ return false;
+ }
+
+ *offset = info->offset;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QComHardwareRenderer::publishBuffers(uint32_t pmem_fd) {
+ sp<MemoryHeapBase> master =
+ reinterpret_cast<MemoryHeapBase *>(pmem_fd);
+
+ master->setDevice("/dev/pmem");
+
+ mMemoryHeap = new MemoryHeapPmem(master, 0);
+ mMemoryHeap->slap();
+
+ ISurface::BufferHeap bufferHeap(
+ mDisplayWidth, mDisplayHeight,
+ mDecodedWidth, mDecodedHeight,
+ PIXEL_FORMAT_YCbCr_420_SP,
+ mMemoryHeap);
+
+ status_t err = mISurface->registerBuffers(bufferHeap);
+ CHECK_EQ(err, OK);
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/SoftwareRenderer.cpp b/media/libstagefright/omx/SoftwareRenderer.cpp
new file mode 100644
index 0000000..da97d55
--- /dev/null
+++ b/media/libstagefright/omx/SoftwareRenderer.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "SoftwareRenderer"
+#include <utils/Log.h>
+
+#include <binder/MemoryHeapBase.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+#define QCOM_YUV 0
+
+SoftwareRenderer::SoftwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mISurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight),
+ mFrameSize(mDecodedWidth * mDecodedHeight * 2), // RGB565
+ mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
+ mIndex(0) {
+ CHECK(mISurface.get() != NULL);
+ CHECK(mDecodedWidth > 0);
+ CHECK(mDecodedHeight > 0);
+ CHECK(mMemoryHeap->heapID() >= 0);
+
+ ISurface::BufferHeap bufferHeap(
+ mDisplayWidth, mDisplayHeight,
+ mDecodedWidth, mDecodedHeight,
+ PIXEL_FORMAT_RGB_565,
+ mMemoryHeap);
+
+ status_t err = mISurface->registerBuffers(bufferHeap);
+ CHECK_EQ(err, OK);
+}
+
+SoftwareRenderer::~SoftwareRenderer() {
+ mISurface->unregisterBuffers();
+}
+
+void SoftwareRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ if (size != (mDecodedHeight * mDecodedWidth * 3) / 2) {
+ LOGE("size is %d, expected %d",
+ size, (mDecodedHeight * mDecodedWidth * 3) / 2);
+ }
+ CHECK(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
+
+ static const signed kClipMin = -278;
+ static const signed kClipMax = 535;
+ static uint8_t kClip[kClipMax - kClipMin + 1];
+ static uint8_t *kAdjustedClip = &kClip[-kClipMin];
+
+ static bool clipInitialized = false;
+
+ if (!clipInitialized) {
+ for (signed i = kClipMin; i <= kClipMax; ++i) {
+ kClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+ }
+ clipInitialized = true;
+ }
+
+ size_t offset = mIndex * mFrameSize;
+
+ void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
+
+ uint32_t *dst_ptr = (uint32_t *)dst;
+
+ const uint8_t *src_y = (const uint8_t *)data;
+
+ const uint8_t *src_u =
+ (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
+
+#if !QCOM_YUV
+ const uint8_t *src_v =
+ (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
+#endif
+
+ for (size_t y = 0; y < mDecodedHeight; ++y) {
+ for (size_t x = 0; x < mDecodedWidth; x += 2) {
+ // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
+ // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
+ // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
+
+ // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
+ // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
+ // R = .................. + 409/256 * (V - 128)
+
+ // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
+ // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
+ // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
+
+ // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
+ // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
+ // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
+
+ // clip range -278 .. 535
+
+ signed y1 = (signed)src_y[x] - 16;
+ signed y2 = (signed)src_y[x + 1] - 16;
+
+#if QCOM_YUV
+ signed u = (signed)src_u[x & ~1] - 128;
+ signed v = (signed)src_u[(x & ~1) + 1] - 128;
+#else
+ signed u = (signed)src_u[x / 2] - 128;
+ signed v = (signed)src_v[x / 2] - 128;
+#endif
+
+ signed u_b = u * 517;
+ signed u_g = -u * 100;
+ signed v_g = -v * 208;
+ signed v_r = v * 409;
+
+ signed tmp1 = y1 * 298;
+ signed b1 = (tmp1 + u_b) / 256;
+ signed g1 = (tmp1 + v_g + u_g) / 256;
+ signed r1 = (tmp1 + v_r) / 256;
+
+ signed tmp2 = y2 * 298;
+ signed b2 = (tmp2 + u_b) / 256;
+ signed g2 = (tmp2 + v_g + u_g) / 256;
+ signed r2 = (tmp2 + v_r) / 256;
+
+ uint32_t rgb1 =
+ ((kAdjustedClip[r1] >> 3) << 11)
+ | ((kAdjustedClip[g1] >> 2) << 5)
+ | (kAdjustedClip[b1] >> 3);
+
+ uint32_t rgb2 =
+ ((kAdjustedClip[r2] >> 3) << 11)
+ | ((kAdjustedClip[g2] >> 2) << 5)
+ | (kAdjustedClip[b2] >> 3);
+
+ dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+ }
+
+ src_y += mDecodedWidth;
+
+ if (y & 1) {
+#if QCOM_YUV
+ src_u += mDecodedWidth;
+#else
+ src_u += mDecodedWidth / 2;
+ src_v += mDecodedWidth / 2;
+#endif
+ }
+
+ dst_ptr += mDecodedWidth / 2;
+ }
+
+ mISurface->postBuffer(offset);
+ mIndex = 1 - mIndex;
+}
+
+} // namespace android
diff --git a/media/libstagefright/omx/TIHardwareRenderer.cpp b/media/libstagefright/omx/TIHardwareRenderer.cpp
new file mode 100644
index 0000000..ebade4a
--- /dev/null
+++ b/media/libstagefright/omx/TIHardwareRenderer.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TIHardwareRenderer"
+#include <utils/Log.h>
+
+#include <media/stagefright/TIHardwareRenderer.h>
+#include <media/stagefright/MediaDebug.h>
+#include <ui/ISurface.h>
+#include <ui/Overlay.h>
+
+#include "v4l2_utils.h"
+
+#define CACHEABLE_BUFFERS 0x1
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TIHardwareRenderer::TIHardwareRenderer(
+ const sp<ISurface> &surface,
+ size_t displayWidth, size_t displayHeight,
+ size_t decodedWidth, size_t decodedHeight)
+ : mISurface(surface),
+ mDisplayWidth(displayWidth),
+ mDisplayHeight(displayHeight),
+ mDecodedWidth(decodedWidth),
+ mDecodedHeight(decodedHeight),
+ mFrameSize(mDecodedWidth * mDecodedHeight * 2),
+ mIsFirstFrame(true),
+ mIndex(0) {
+ CHECK(mISurface.get() != NULL);
+ CHECK(mDecodedWidth > 0);
+ CHECK(mDecodedHeight > 0);
+
+ sp<OverlayRef> ref = mISurface->createOverlay(
+ mDisplayWidth, mDisplayHeight, OVERLAY_FORMAT_CbYCrY_422_I);
+
+ if (ref.get() == NULL) {
+ LOGE("Unable to create the overlay!");
+ return;
+ }
+
+ mOverlay = new Overlay(ref);
+ mOverlay->setParameter(CACHEABLE_BUFFERS, 0);
+
+ for (size_t i = 0; i < (size_t)mOverlay->getBufferCount(); ++i) {
+ mapping_data_t *data =
+ (mapping_data_t *)mOverlay->getBufferAddress((void *)i);
+
+ mOverlayAddresses.push(data->ptr);
+ }
+}
+
+TIHardwareRenderer::~TIHardwareRenderer() {
+ if (mOverlay.get() != NULL) {
+ mOverlay->destroy();
+ mOverlay.clear();
+
+ // XXX apparently destroying an overlay is an asynchronous process...
+ sleep(1);
+ }
+}
+
+void TIHardwareRenderer::render(
+ const void *data, size_t size, void *platformPrivate) {
+ // CHECK_EQ(size, mFrameSize);
+
+ if (mOverlay.get() == NULL) {
+ return;
+ }
+
+#if 0
+ size_t i = 0;
+ for (; i < mOverlayAddresses.size(); ++i) {
+ if (mOverlayAddresses[i] == data) {
+ break;
+ }
+
+ if (mIsFirstFrame) {
+ LOGI("overlay buffer #%d: %p", i, mOverlayAddresses[i]);
+ }
+ }
+
+ if (i == mOverlayAddresses.size()) {
+ LOGE("No suitable overlay buffer found.");
+ return;
+ }
+
+ mOverlay->queueBuffer((void *)i);
+
+ overlay_buffer_t overlay_buffer;
+ if (!mIsFirstFrame) {
+ CHECK_EQ(mOverlay->dequeueBuffer(&overlay_buffer), OK);
+ } else {
+ mIsFirstFrame = false;
+ }
+#else
+ memcpy(mOverlayAddresses[mIndex], data, size);
+
+ mOverlay->queueBuffer((void *)mIndex);
+
+ if (++mIndex == mOverlayAddresses.size()) {
+ mIndex = 0;
+ }
+
+ overlay_buffer_t overlay_buffer;
+ if (!mIsFirstFrame) {
+ CHECK_EQ(mOverlay->dequeueBuffer(&overlay_buffer), OK);
+ } else {
+ mIsFirstFrame = false;
+ }
+#endif
+}
+
+} // namespace android
+
diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp
new file mode 100644
index 0000000..5b16784
--- /dev/null
+++ b/media/libstagefright/string.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <media/stagefright/string.h>
+
+namespace android {
+
+// static
+string::size_type string::npos = (string::size_type)-1;
+
+string::string() {
+}
+
+string::string(const char *s, size_t length)
+ : mString(s, length) {
+}
+
+string::string(const string &from, size_type start, size_type length)
+ : mString(from.c_str() + start, length) {
+}
+
+string::string(const char *s)
+ : mString(s) {
+}
+
+const char *string::c_str() const {
+ return mString.string();
+}
+
+string::size_type string::size() const {
+ return mString.length();
+}
+
+void string::clear() {
+ mString = String8();
+}
+
+string::size_type string::find(char c) const {
+ char s[2];
+ s[0] = c;
+ s[1] = '\0';
+
+ ssize_t index = mString.find(s);
+
+ return index < 0 ? npos : (size_type)index;
+}
+
+bool string::operator<(const string &other) const {
+ return mString < other.mString;
+}
+
+bool string::operator==(const string &other) const {
+ return mString == other.mString;
+}
+
+string &string::operator+=(char c) {
+ mString.append(&c, 1);
+
+ return *this;
+}
+
+void string::erase(size_t from, size_t length) {
+ String8 s(mString.string(), from);
+ s.append(mString.string() + from + length);
+
+ mString = s;
+}
+
+} // namespace android
+
diff --git a/media/mediaserver/Android.mk b/media/mediaserver/Android.mk
index c681698..a92cea8 100644
--- a/media/mediaserver/Android.mk
+++ b/media/mediaserver/Android.mk
@@ -8,7 +8,8 @@ LOCAL_SHARED_LIBRARIES := \
libaudioflinger \
libcameraservice \
libmediaplayerservice \
- libutils
+ libutils \
+ libbinder
base := $(LOCAL_PATH)/../..
diff --git a/media/mediaserver/main_mediaserver.cpp b/media/mediaserver/main_mediaserver.cpp
index 6954b63..7094cfa 100644
--- a/media/mediaserver/main_mediaserver.cpp
+++ b/media/mediaserver/main_mediaserver.cpp
@@ -20,14 +20,15 @@
#include <unistd.h>
#include <grp.h>
-#include <utils/IPCThreadState.h>
-#include <utils/ProcessState.h>
-#include <utils/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
#include <utils/Log.h>
#include <AudioFlinger.h>
#include <CameraService.h>
#include <MediaPlayerService.h>
+#include <AudioPolicyService.h>
#include <private/android_filesystem_config.h>
using namespace android;
@@ -40,6 +41,7 @@ int main(int argc, char** argv)
AudioFlinger::instantiate();
MediaPlayerService::instantiate();
CameraService::instantiate();
+ AudioPolicyService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
diff --git a/media/sdutils/sdutil.cpp b/media/sdutils/sdutil.cpp
index 06120f5..fe11878 100644
--- a/media/sdutils/sdutil.cpp
+++ b/media/sdutils/sdutil.cpp
@@ -15,8 +15,8 @@
*/
#include <hardware_legacy/IMountService.h>
-#include <utils/BpBinder.h>
-#include <utils/IServiceManager.h>
+#include <binder/BpBinder.h>
+#include <binder/IServiceManager.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/media/tests/MediaFrameworkTest/res/drawable-hdpi/icon.png b/media/tests/MediaFrameworkTest/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..a02138e
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/drawable/icon.png b/media/tests/MediaFrameworkTest/res/drawable-mdpi/icon.png
index 64e3601..64e3601 100644
--- a/media/tests/MediaFrameworkTest/res/drawable/icon.png
+++ b/media/tests/MediaFrameworkTest/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
index c25e476..cbd1ff8 100644
--- a/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
+++ b/media/tests/MediaFrameworkTest/res/layout/surface_view.xml
@@ -21,13 +21,12 @@
<FrameLayout
android:layout_width="fill_parent"
- android:layout_height="0px"
- android:layout_weight="1">
+ android:layout_height="fill_parent">
<SurfaceView
android:id="@+id/surface_view"
- android:layout_width="320dip"
- android:layout_height="240dip"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
android:layout_centerInParent="true"
/>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index e65cf41..5e830a8 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -69,10 +69,6 @@ public class MediaFrameworkTest extends Activity {
setContentView(R.layout.surface_view);
mSurfaceView = (SurfaceView)findViewById(R.id.surface_view);
ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
- lp.width = 320;
- lp.height = 240;
- mSurfaceView.setLayoutParams(lp);
- mSurfaceView.getHolder().setFixedSize(320, 240);
mSurfaceView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
//Get the midi fd
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
index 6edc2cc..2a4e9a0 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java
@@ -23,6 +23,7 @@ import com.android.mediaframeworktest.functional.MediaMimeTest;
import com.android.mediaframeworktest.functional.MediaPlayerApiTest;
import com.android.mediaframeworktest.functional.MediaRecorderTest;
import com.android.mediaframeworktest.functional.SimTonesTest;
+import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest;
import junit.framework.TestSuite;
@@ -32,7 +33,7 @@ import android.test.InstrumentationTestSuite;
/**
* Instrumentation Test Runner for all MediaPlayer tests.
- *
+ *
* Running all tests:
*
* adb shell am instrument \
@@ -52,6 +53,7 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaRecorderTest.class);
suite.addTestSuite(MediaAudioTrackTest.class);
suite.addTestSuite(MediaMimeTest.class);
+ suite.addTestSuite(MediaPlayerInvokeTest.class);
return suite;
}
@@ -60,5 +62,3 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner {
return MediaFrameworkTestRunner.class.getClassLoader();
}
}
-
-
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
index 81d59da..a203adc 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkUnitTestRunner.java
@@ -24,16 +24,16 @@ import junit.framework.TestSuite;
/**
* Instrumentation Test Runner for all media framework unit tests.
- *
+ *
* Make sure that MediaFrameworkUnitTestRunner has been added to
* AndroidManifest.xml file, and then "make -j4 mediaframeworktest; adb sync"
* to build and upload mediaframeworktest to the phone or emulator.
- *
+ *
* Example on running all unit tests for a single class:
* adb shell am instrument -e class \
- * com.android.mediaframeworktest.unit.MediaMetadataRetrieverUnitTest \
+ * com.android.mediaframeworktest.unit.MediaMetadataRetrieverUnitTest \
* -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
- *
+ *
* Example on running all unit tests for the media framework:
* adb shell am instrument \
* -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
@@ -54,12 +54,12 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
public ClassLoader getLoader() {
return MediaFrameworkUnitTestRunner.class.getClassLoader();
}
-
+
// Running all unit tests checking the state machine may be time-consuming.
private void addMediaMetadataRetrieverStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaMetadataRetrieverTest.class);
}
-
+
// Running all unit tests checking the state machine may be time-consuming.
private void addMediaRecorderStateUnitTests(TestSuite suite) {
suite.addTestSuite(MediaRecorderPrepareStateUnitTest.class);
@@ -87,5 +87,6 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
suite.addTestSuite(MediaPlayerSetLoopingStateUnitTest.class);
suite.addTestSuite(MediaPlayerSetAudioStreamTypeStateUnitTest.class);
suite.addTestSuite(MediaPlayerSetVolumeStateUnitTest.class);
+ suite.addTestSuite(MediaPlayerMetadataParserTest.class);
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index e76967d..45a5e53 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -372,81 +372,81 @@ public class MediaNames {
public static final String META_DATA_MP3 [][] = {
{"/sdcard/media_api/metaDataTestMedias/MP3/ID3V1_ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist",
"ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues",
- "ID3V2.3 Title", "1234", "295", "1"},
+ "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/ID3V2.mp3", "1/10", "ID3V2.3 Album", "ID3V2.3 Artist",
"ID3V2.3 Lyricist", "ID3V2.3 Composer", null, "Blues",
- "ID3V2.3 Title", "1234", "287", "1"},
+ "ID3V2.3 Title", "1234", "287", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/ID3V1.mp3", "1", "test ID3V1 Album", "test ID3V1 Artist",
- null, null, null, "255", "test ID3V1 Title", "1234", "231332", "1"},
+ null, null, null, "255", "test ID3V1 Title", "1234", "231332", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V1.mp3" , null, null, null,
- null, null, null, null, null, null, "231330", "1"},
+ null, null, null, null, null, null, "231330", "1", null},
//The corrupted TALB field in id3v2 would not switch to id3v1 tag automatically
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TALB.mp3", "01", null, "ID3V2.3 Artist",
"ID3V2.3 Lyricist", "ID3V2.3 Composer", null,
- "Blues", "ID3V2.3 Title", "1234", "295", "1"},
+ "Blues", "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM.mp3", "01", "ID3V2.3 Album",
"ID3V2.3 Artist", "ID3V2.3 Lyricist", null, null,
- "Blues", "ID3V2.3 Title", "1234", "295", "1"},
+ "Blues", "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TCOM_2.mp3", "01", "ID3V2.3 Album",
- "ID3V2.3 Artist", null, null, null, "Blues", "ID3V2.3 Title", "1234", "295", "1"},
+ "ID3V2.3 Artist", null, null, null, "Blues", "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK.mp3", "dd", "ID3V2.3 Album",
"ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null,
- "Blues", "ID3V2.3 Title", "1234", "295", "1"},
+ "Blues", "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TRCK_2.mp3", "01", "ID3V2.3 Album",
- "ID3V2.3 Artist", null, null, null, "255", "ID3V2.3 Title", "1234", "295", "1"},
+ "ID3V2.3 Artist", null, null, null, "255", "ID3V2.3 Title", "1234", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER.mp3", "01", "ID3V2.3 Album",
- "ID3V2.3 Artist", null, null, null, null, "ID3V2.3 Title", "9999", "295", "1"},
+ "ID3V2.3 Artist", null, null, null, null, "ID3V2.3 Title", "9999", "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TYER_2.mp3", "01", "ID3V2.3 Album",
"ID3V2.3 Artist", "ID3V2.3 Lyricist", "ID3V2.3 Composer", null,
- "Blues", "ID3V2.3 Title", null, "295", "1"},
+ "Blues", "ID3V2.3 Title", null, "295", "1", null},
{"/sdcard/media_api/metaDataTestMedias/MP3/Corrupted_ID3V2_TIT.mp3", null, null, null,
- null, null, null, null, null, null, "295", "1"}
+ null, null, null, null, null, null, "295", "1", null}
};
public static final String META_DATA_OTHERS [][] = {
{"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null,
null, null, "20080309T002415.000Z", null,
- null, null, "1404928", "2"},
+ null, null, "1404928", "2", null},
{"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null,
null, null, null, null,
- null, null, "126540", "1"},
+ null, null, "126540", "1", null},
{"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null,
null, null, null, null,
- null, null, "231180", "1"},
- {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", null, "Suspended Animation",
+ null, null, "231180", "1", null},
+ {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation",
"John Petrucci", null, null, "20070510T125223.000Z",
- null, null, "2005", "231180", "1"},
+ "13", "Jaws Of Life", "2005", "19815424", "1", "m4a composer"},
{"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null,
null, null, null, "20051220T202015.000Z",
- null, null, null, "3771392", "2"},
+ null, null, null, "85500", "2", null},
{"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation",
"John Petrucci", null, null, "20070510T125223.000Z",
- null, null, "2005", "231180", "1"},
- {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", null, "mp4 album Kung Fu Panda",
+ null, null, "2005", "231180", "1", null},
+ {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", "2/0", "mp4 album Kung Fu Panda",
"mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z",
- "41", "Kung Fu Panda", "2008", "5667840", "2"},
+ "41", "Kung Fu Panda", "2008", "77113", "2", "mp4 composer"},
{"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation",
"John Petrucci", null, null, "20070510T125223.000Z",
- null, null, "2005", "231180", "1"},
+ null, null, "2005", "231180", "1", null},
{"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg",
null, "Suspended Animation", "John Petrucci",
- null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1"},
+ null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1", null},
{"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null,
null, null, null, null,
- null, null, null, "224000", "1"},
+ null, null, null, "224000", "1", null},
{"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal",
"Alien Crime Syndicate", "Alien Crime Syndicate",
"wma 9 Composer", "20040521T175729.483Z",
- "Rock", "Run for the Money", "2004", "134479", "1"},
+ "Rock", "Run for the Money", "2004", "134479", "1", null},
{"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album",
"wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z",
- "Acid Jazz", "wma 10 Title", "2010", "126574", "1"},
+ "Acid Jazz", "wma 10 Title", "2010", "126574", "1", null},
{"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album",
null, "wmv 9 Artist ", null, "20051122T155247.540Z",
- null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2"},
+ null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2", null},
{"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album",
null, "Hallau Shoots & Company", null, "20020226T170045.891Z",
- null, "CODEC Shootout", "1986", "43709", "2"}
+ null, "CODEC Shootout", "1986", "43709", "2", null}
};
//output recorded video
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java
index aefedc3..cea3a5a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioTrackTest.java
@@ -140,7 +140,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM,
- AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
AudioTrack.STATE_INITIALIZED);
assertTrue("testConstructorMono16MusicStream: " + res.mResultLog, res.mResult);
@@ -153,7 +153,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
AudioTrack.STATE_INITIALIZED);
assertTrue("testConstructorStereo16MusicStream: " + res.mResultLog, res.mResult);
@@ -166,7 +166,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC,
- AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
AudioTrack.STATE_NO_STATIC_DATA);
assertTrue("testConstructorMono16MusicStatic: " + res.mResultLog, res.mResult);
@@ -179,7 +179,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
AudioTrack.STATE_NO_STATIC_DATA);
assertTrue("testConstructorStereo16MusicStatic: " + res.mResultLog, res.mResult);
@@ -196,7 +196,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM,
- AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_8BIT,
AudioTrack.STATE_INITIALIZED);
assertTrue("testConstructorMono8MusicStream: " + res.mResultLog, res.mResult);
@@ -208,7 +208,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STREAM,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_8BIT,
AudioTrack.STATE_INITIALIZED);
assertTrue("testConstructorStereo8MusicStream: " + res.mResultLog, res.mResult);
@@ -220,7 +220,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC,
- AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_8BIT,
AudioTrack.STATE_NO_STATIC_DATA);
assertTrue("testConstructorMono8MusicStatic: " + res.mResultLog, res.mResult);
@@ -232,7 +232,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
TestResults res = constructorTestMultiSampleRate(
AudioManager.STREAM_MUSIC, AudioTrack.MODE_STATIC,
- AudioFormat.CHANNEL_CONFIGURATION_STEREO, AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_8BIT,
AudioTrack.STATE_NO_STATIC_DATA);
assertTrue("testConstructorStereo8MusicStatic: " + res.mResultLog, res.mResult);
@@ -248,15 +248,15 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
public void testConstructorStreamType() throws Exception {
// constants for test
final int TYPE_TEST_SR = 22050;
- final int TYPE_TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TYPE_TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TYPE_TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TYPE_TEST_MODE = AudioTrack.MODE_STREAM;
final int[] STREAM_TYPES = { AudioManager.STREAM_ALARM, AudioManager.STREAM_BLUETOOTH_SCO,
AudioManager.STREAM_MUSIC, AudioManager.STREAM_NOTIFICATION,
AudioManager.STREAM_RING, AudioManager.STREAM_SYSTEM,
- AudioManager.STREAM_VOICE_CALL };
+ AudioManager.STREAM_VOICE_CALL, AudioManager.STREAM_DTMF, };
final String[] STREAM_NAMES = { "STREAM_ALARM", "STREAM_BLUETOOTH_SCO", "STREAM_MUSIC",
- "STREAM_NOTIFICATION", "STREAM_RING", "STREAM_SYSTEM", "STREAM_VOICE_CALL" };
+ "STREAM_NOTIFICATION", "STREAM_RING", "STREAM_SYSTEM", "STREAM_VOICE_CALL", "STREAM_DTMF" };
boolean localTestRes = true;
AudioTrack track = null;
@@ -303,7 +303,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionAfterInit";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -324,7 +324,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionIncrease";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -352,7 +352,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionAfterFlush";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -382,7 +382,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionAfterStop";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -413,7 +413,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testPlaybackHeadPositionAfterPause";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -448,7 +448,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetStereoVolumeMax";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -474,7 +474,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetStereoVolumeMin";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -500,7 +500,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetStereoVolumeMid";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -526,7 +526,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackRate";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -552,7 +552,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackRateZero";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -574,7 +574,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackRateTwiceOutputSR";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -601,7 +601,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetGetPlaybackRate";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -628,7 +628,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackRateUninit";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -655,7 +655,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackHeadPositionPlaying";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -682,7 +682,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackHeadPositionStopped";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -710,7 +710,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackHeadPositionPaused";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -738,7 +738,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetPlaybackHeadPositionTooFar";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -770,7 +770,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsStream";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -794,7 +794,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsStartAfterEnd";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -818,7 +818,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsSuccess";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -842,7 +842,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsLoopTooLong";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -868,7 +868,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsStartTooFar";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -896,7 +896,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testSetLoopPointsEndTooFar";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STATIC;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -929,7 +929,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByteOffsetTooBig";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -953,7 +953,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShortOffsetTooBig";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -977,7 +977,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByteSizeTooBig";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1001,7 +1001,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShortSizeTooBig";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1025,7 +1025,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByteNegativeOffset";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1049,7 +1049,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShortNegativeOffset";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1073,7 +1073,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByteNegativeSize";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1097,7 +1097,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShortNegativeSize";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1121,7 +1121,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByte";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1145,7 +1145,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShort";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1169,7 +1169,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteByte8bit";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_8BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1193,7 +1193,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constants for test
final String TEST_NAME = "testWriteShort8bit";
final int TEST_SR = 22050;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_8BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1221,7 +1221,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constant for test
final String TEST_NAME = "testGetMinBufferSizeTooLowSR";
final int TEST_SR = 3999;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_8BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
@@ -1238,7 +1238,7 @@ public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaF
// constant for testg
final String TEST_NAME = "testGetMinBufferSizeTooHighSR";
final int TEST_SR = 48001;
- final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
+ final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_8BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index 3715913..1bf4958 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -36,7 +36,7 @@ public class MediaMetadataTest extends AndroidTestCase {
FILE_PATH,CD_TRACK, ALBUM,
ARTIST, AUTHOR, COMPOSER,
DATE, GENRE, TITLE,
- YEAR, DURATION, NUM_TRACKS
+ YEAR, DURATION, NUM_TRACKS, WRITER
}
public static enum MP3_TEST_FILE{
@@ -130,8 +130,6 @@ public class MediaMetadataTest extends AndroidTestCase {
validateMetatData(non_mp3_test_file.AMRWB.ordinal(), MediaNames.META_DATA_OTHERS);
}
- //Bug# 1440173 - skip this test case now
- @Suppress
@MediumTest
public static void testM4A1_Metadata() throws Exception {
validateMetatData(non_mp3_test_file.M4A1.ordinal(), MediaNames.META_DATA_OTHERS);
@@ -254,6 +252,10 @@ public class MediaMetadataTest extends AndroidTestCase {
Log.v(TAG, "Track : "+ value);
assertEquals(TAG,meta_data_file[fileIndex][meta.NUM_TRACKS.ordinal()], value);
+ value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_WRITER);
+ Log.v(TAG, "Writer : "+ value);
+ assertEquals(TAG,meta_data_file[fileIndex][meta.WRITER.ordinal()], value);
+
retriever.release();
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index ea42f53..30e2d6c 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -435,9 +435,16 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra
@LargeTest
public void testLocalMp3PrepareAsyncCallback() throws Exception {
boolean onPrepareSuccess =
- CodecTest.prepareAsyncCallback(MediaNames.VIDEO_H263_AMR, false);
+ CodecTest.prepareAsyncCallback(MediaNames.MP3CBR, false);
assertTrue("LocalMp3prepareAsyncCallback", onPrepareSuccess);
}
+
+ @LargeTest
+ public void testLocalH263AMRPrepareAsyncCallback() throws Exception {
+ boolean onPrepareSuccess =
+ CodecTest.prepareAsyncCallback(MediaNames.VIDEO_H263_AMR, false);
+ assertTrue("testLocalH263AMRPrepareAsyncCallback", onPrepareSuccess);
+ }
@LargeTest
public void testStreamPrepareAsyncCallback() throws Exception {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
new file mode 100644
index 0000000..ab8b311
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerInvokeTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.functional;
+
+import com.android.mediaframeworktest.MediaFrameworkTest;
+import com.android.mediaframeworktest.MediaNames;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import android.media.MediaPlayer;
+import android.os.Parcel;
+
+import java.util.Calendar;
+import java.util.Random;
+
+// Tests for the invoke method in the MediaPlayer.
+public class MediaPlayerInvokeTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+ private static final String TAG = "MediaPlayerInvokeTest";
+ private MediaPlayer mPlayer;
+ private Random rnd;
+
+ public MediaPlayerInvokeTest() {
+ super("com.android.mediaframeworktest", MediaFrameworkTest.class);
+ rnd = new Random(Calendar.getInstance().getTimeInMillis());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPlayer = new MediaPlayer();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mPlayer.release();
+ super.tearDown();
+ }
+
+ // Generate a random number, sends it to the ping test player.
+ @MediumTest
+ public void testPing() throws Exception {
+ mPlayer.setDataSource("test:invoke_mock_media_player.so?url=ping");
+
+ Parcel request = mPlayer.newRequest();
+ Parcel reply = Parcel.obtain();
+
+ int val = rnd.nextInt();
+ request.writeInt(val);
+ assertEquals(0, mPlayer.invoke(request, reply));
+ assertEquals(val, reply.readInt());
+ }
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 442c35b..01c0920 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -47,7 +47,7 @@ import android.media.MediaMetadataRetriever;
*/
public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<MediaFrameworkTest> {
- private String TAG = "MediaFrameworkPerformance";
+ private String TAG = "MediaPlayerPerformance";
private SQLiteDatabase mDB;
private SurfaceHolder mSurfaceHolder = null;
@@ -76,9 +76,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi
public void createDB() {
mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
- mDB.execSQL("CREATE TABLE perfdata (_id INTEGER PRIMARY KEY," +
+ mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," +
"file TEXT," + "setdatatime LONG," + "preparetime LONG," +
"playtime LONG" + ");");
+ //clean the table before adding new data
+ mDB.execSQL("DELETE FROM perfdata");
}
public void audioPlaybackStartupTime(String[] testFile) {
@@ -137,6 +139,10 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase<Medi
audioPlaybackStartupTime(MediaNames.MP3FILES);
audioPlaybackStartupTime(MediaNames.AACFILES);
+ //close the database after all transactions
+ if (mDB.isOpen()) {
+ mDB.close();
+ }
}
public void wmametadatautility(String[] testFile) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
new file mode 100644
index 0000000..38f598a
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaPlayerMetadataParserTest.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mediaframeworktest.unit;
+import android.media.Metadata;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/*
+ * Check the Java layer that parses serialized metadata in Parcel
+ * works as expected.
+ *
+ */
+
+public class MediaPlayerMetadataParserTest extends AndroidTestCase {
+ private static final String TAG = "MediaPlayerMetadataTest";
+ private static final int kMarker = 0x4d455441; // 'M' 'E' 'T' 'A'
+ private static final int kHeaderSize = 8;
+
+ private Metadata mMetadata = null;
+ private Parcel mParcel = null;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mMetadata = new Metadata();
+ mParcel = Parcel.obtain();
+
+ resetParcel();
+ }
+
+ // Check parsing of the parcel fails. Make sure the parser rewind
+ // the parcel properly.
+ private void assertParseFail() throws Exception {
+ mParcel.setDataPosition(0);
+ assertFalse(mMetadata.parse(mParcel));
+ assertEquals(0, mParcel.dataPosition());
+ }
+
+ // Check parsing of the parcel is successful.
+ private void assertParse() throws Exception {
+ mParcel.setDataPosition(0);
+ assertTrue(mMetadata.parse(mParcel));
+ }
+
+ // Write the number of bytes from the start of the parcel to the
+ // current position at the beginning of the parcel (offset 0).
+ private void adjustSize() {
+ adjustSize(0);
+ }
+
+ // Write the number of bytes from the offset to the current
+ // position at position pointed by offset.
+ private void adjustSize(int offset) {
+ final int pos = mParcel.dataPosition();
+
+ mParcel.setDataPosition(offset);
+ mParcel.writeInt(pos - offset);
+ mParcel.setDataPosition(pos);
+ }
+
+ // Rewind the parcel and insert the header.
+ private void resetParcel() {
+ mParcel.setDataPosition(0);
+ // Most tests will use a properly formed parcel with a size
+ // and the meta marker so we add them by default.
+ mParcel.writeInt(-1); // Placeholder for the size
+ mParcel.writeInt(kMarker);
+ }
+
+ // ----------------------------------------------------------------------
+ // START OF THE TESTS
+
+
+ // There should be at least 8 bytes in the parcel, 4 for the size
+ // and 4 for the 'M' 'E' 'T' 'A' marker.
+ @SmallTest
+ public void testMissingSizeAndMarker() throws Exception {
+ for (int i = 0; i < kHeaderSize; ++i) {
+ mParcel.setDataPosition(0);
+ mParcel.setDataSize(i);
+
+ assertEquals(i, mParcel.dataAvail());
+ assertParseFail();
+ }
+ }
+
+ // There should be at least 'size' bytes in the parcel.
+ @SmallTest
+ public void testMissingData() throws Exception {
+ final int size = 20;
+
+ mParcel.writeInt(size);
+ mParcel.setDataSize(size - 1);
+ assertParseFail();
+ }
+
+ // Empty parcel is fine
+ @SmallTest
+ public void testEmptyIsOk() throws Exception {
+ adjustSize();
+ assertParse();
+ }
+
+ // ----------------------------------------------------------------------
+ // RECORDS
+ // ----------------------------------------------------------------------
+
+ // A record header should be at least 12 bytes long
+ @SmallTest
+ public void testRecordMissingId() throws Exception {
+ mParcel.writeInt(13); // record length
+ // misses metadata id and metadata type.
+ adjustSize();
+ assertParseFail();
+ }
+
+ @SmallTest
+ public void testRecordMissingType() throws Exception {
+ mParcel.writeInt(13); // record length lies
+ mParcel.writeInt(Metadata.TITLE);
+ // misses metadata type
+ adjustSize();
+ assertParseFail();
+ }
+
+ @SmallTest
+ public void testRecordWithZeroPayload() throws Exception {
+ mParcel.writeInt(0);
+ adjustSize();
+ assertParseFail();
+ }
+
+ // A record cannot be empty.
+ @SmallTest
+ public void testRecordMissingPayload() throws Exception {
+ mParcel.writeInt(12);
+ mParcel.writeInt(Metadata.TITLE);
+ mParcel.writeInt(Metadata.STRING_VAL);
+ // misses payload
+ adjustSize();
+ assertParseFail();
+ }
+
+ // Check records can be found.
+ @SmallTest
+ public void testRecordsFound() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ writeStringRecord(Metadata.firstCustomId(), "custom");
+ adjustSize();
+ assertParse();
+ assertTrue(mMetadata.has(Metadata.TITLE));
+ assertTrue(mMetadata.has(Metadata.GENRE));
+ assertTrue(mMetadata.has(Metadata.firstCustomId()));
+ assertFalse(mMetadata.has(Metadata.DRM_CRIPPLED));
+ assertEquals(3, mMetadata.keySet().size());
+ }
+
+ // Detects bad metadata type
+ @SmallTest
+ public void testBadMetadataType() throws Exception {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(Metadata.TITLE);
+ mParcel.writeInt(0); // Invalid type.
+ mParcel.writeString("dummy");
+ adjustSize(start);
+
+ adjustSize();
+ assertParseFail();
+ }
+
+ // Check a Metadata instance can be reused, i.e the parse method
+ // wipes out the existing states/keys.
+ @SmallTest
+ public void testParseClearState() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ writeStringRecord(Metadata.firstCustomId(), "custom");
+ adjustSize();
+ assertParse();
+
+ resetParcel();
+ writeStringRecord(Metadata.MIME_TYPE, "audio/mpg");
+ adjustSize();
+ assertParse();
+
+ // Only the mime type metadata should be present.
+ assertEquals(1, mMetadata.keySet().size());
+ assertTrue(mMetadata.has(Metadata.MIME_TYPE));
+
+ assertFalse(mMetadata.has(Metadata.TITLE));
+ assertFalse(mMetadata.has(Metadata.GENRE));
+ assertFalse(mMetadata.has(Metadata.firstCustomId()));
+ }
+
+ // ----------------------------------------------------------------------
+ // GETTERS
+ // ----------------------------------------------------------------------
+
+ // getString
+ @SmallTest
+ public void testGetString() throws Exception {
+ writeStringRecord(Metadata.TITLE, "a title");
+ writeStringRecord(Metadata.GENRE, "comedy");
+ adjustSize();
+ assertParse();
+
+ assertEquals("a title", mMetadata.getString(Metadata.TITLE));
+ assertEquals("comedy", mMetadata.getString(Metadata.GENRE));
+ }
+
+ // get an empty string.
+ @SmallTest
+ public void testGetEmptyString() throws Exception {
+ writeStringRecord(Metadata.TITLE, "");
+ adjustSize();
+ assertParse();
+
+ assertEquals("", mMetadata.getString(Metadata.TITLE));
+ }
+
+ // get a string when a NULL value was in the parcel
+ @SmallTest
+ public void testGetNullString() throws Exception {
+ writeStringRecord(Metadata.TITLE, null);
+ adjustSize();
+ assertParse();
+
+ assertEquals(null, mMetadata.getString(Metadata.TITLE));
+ }
+
+ // get a string when an integer is actually present
+ @SmallTest
+ public void testWrongType() throws Exception {
+ writeIntRecord(Metadata.DURATION, 5);
+ adjustSize();
+ assertParse();
+
+ try {
+ mMetadata.getString(Metadata.DURATION);
+ } catch (IllegalStateException ise) {
+ return;
+ }
+ fail("Exception was not thrown");
+ }
+
+ // getInt
+ @SmallTest
+ public void testGetInt() throws Exception {
+ writeIntRecord(Metadata.CD_TRACK_NUM, 1);
+ adjustSize();
+ assertParse();
+
+ assertEquals(1, mMetadata.getInt(Metadata.CD_TRACK_NUM));
+ }
+
+ // getBoolean
+ @SmallTest
+ public void testGetBoolean() throws Exception {
+ writeBooleanRecord(Metadata.DRM_CRIPPLED, true);
+ adjustSize();
+ assertParse();
+
+ assertEquals(true, mMetadata.getBoolean(Metadata.DRM_CRIPPLED));
+ }
+
+ // getLong
+ @SmallTest
+ public void testGetLong() throws Exception {
+ writeLongRecord(Metadata.DURATION, 1L);
+ adjustSize();
+ assertParse();
+
+ assertEquals(1L, mMetadata.getLong(Metadata.DURATION));
+ }
+
+ // getDouble
+ @SmallTest
+ public void testGetDouble() throws Exception {
+ writeDoubleRecord(Metadata.VIDEO_FRAME_RATE, 29.97);
+ adjustSize();
+ assertParse();
+
+ assertEquals(29.97, mMetadata.getDouble(Metadata.VIDEO_FRAME_RATE));
+ }
+
+ // getByteArray
+ @SmallTest
+ public void testGetByteArray() throws Exception {
+ byte data[] = new byte[]{1,2,3,4,5};
+
+ writeByteArrayRecord(Metadata.ALBUM_ART, data);
+ adjustSize();
+ assertParse();
+
+ byte res[] = mMetadata.getByteArray(Metadata.ALBUM_ART);
+ for (int i = 0; i < data.length; ++i) {
+ assertEquals(data[i], res[i]);
+ }
+ }
+
+ // getDate
+ @SmallTest
+ public void testGetDate() throws Exception {
+ writeDateRecord(Metadata.DATE, 0, "PST");
+ adjustSize();
+ assertParse();
+
+ assertEquals(new Date(0), mMetadata.getDate(Metadata.DATE));
+ }
+
+ // getTimedText
+ @SmallTest
+ public void testGetTimedText() throws Exception {
+ Date now = Calendar.getInstance().getTime();
+ writeTimedTextRecord(Metadata.CAPTION, now.getTime(),
+ 10, "Some caption");
+ adjustSize();
+ assertParse();
+
+ Metadata.TimedText caption = mMetadata.getTimedText(Metadata.CAPTION);
+ assertEquals("" + now + "-" + 10 + ":Some caption", caption.toString());
+ }
+
+ // ----------------------------------------------------------------------
+ // HELPERS TO APPEND RECORDS
+ // ----------------------------------------------------------------------
+
+ // Insert a string record at the current position.
+ private void writeStringRecord(int metadataId, String val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.STRING_VAL);
+ mParcel.writeString(val);
+ adjustSize(start);
+ }
+
+ // Insert an int record at the current position.
+ private void writeIntRecord(int metadataId, int val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.INTEGER_VAL);
+ mParcel.writeInt(val);
+ adjustSize(start);
+ }
+
+ // Insert a boolean record at the current position.
+ private void writeBooleanRecord(int metadataId, boolean val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.BOOLEAN_VAL);
+ mParcel.writeInt(val ? 1 : 0);
+ adjustSize(start);
+ }
+
+ // Insert a Long record at the current position.
+ private void writeLongRecord(int metadataId, long val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.LONG_VAL);
+ mParcel.writeLong(val);
+ adjustSize(start);
+ }
+
+ // Insert a Double record at the current position.
+ private void writeDoubleRecord(int metadataId, double val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.DOUBLE_VAL);
+ mParcel.writeDouble(val);
+ adjustSize(start);
+ }
+
+ // Insert a ByteArray record at the current position.
+ private void writeByteArrayRecord(int metadataId, byte[] val) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.BYTE_ARRAY_VAL);
+ mParcel.writeByteArray(val);
+ adjustSize(start);
+ }
+
+ // Insert a Date record at the current position.
+ private void writeDateRecord(int metadataId, long time, String tz) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.DATE_VAL);
+ mParcel.writeLong(time);
+ mParcel.writeString(tz);
+ adjustSize(start);
+ }
+
+ // Insert a TimedText record at the current position.
+ private void writeTimedTextRecord(int metadataId, long begin,
+ int duration, String text) {
+ final int start = mParcel.dataPosition();
+ mParcel.writeInt(-1); // Placeholder for the length
+ mParcel.writeInt(metadataId);
+ mParcel.writeInt(Metadata.TIMED_TEXT_VAL);
+ mParcel.writeLong(begin);
+ mParcel.writeInt(duration);
+ mParcel.writeString(text);
+ adjustSize(start);
+ }
+}
diff --git a/media/tests/players/Android.mk b/media/tests/players/Android.mk
new file mode 100644
index 0000000..eb50a51
--- /dev/null
+++ b/media/tests/players/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= invoke_mock_media_player.cpp
+
+LOCAL_SHARED_LIBRARIES:= \
+ libbinder \
+ libutils
+
+LOCAL_MODULE:= invoke_mock_media_player
+LOCAL_MODULE_TAGS := test eng
+LOCAL_PRELINK_MODULE:= false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/tests/players/README b/media/tests/players/README
new file mode 100644
index 0000000..edf9bd6
--- /dev/null
+++ b/media/tests/players/README
@@ -0,0 +1,8 @@
+Native test players for system tests.
+
+For functional/system/performance tests, a native test player can be used.
+This directory contains the sources of such players.
+The class TestPlayerStub uses the dynamic loader to load any of them.
+
+
+
diff --git a/media/tests/players/invoke_mock_media_player.cpp b/media/tests/players/invoke_mock_media_player.cpp
new file mode 100644
index 0000000..77bb5b2
--- /dev/null
+++ b/media/tests/players/invoke_mock_media_player.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "TestPlayerStub"
+#include "utils/Log.h"
+
+#include <string.h>
+
+#include <binder/Parcel.h>
+#include <media/MediaPlayerInterface.h>
+#include <utils/Errors.h>
+
+using android::INVALID_OPERATION;
+using android::ISurface;
+using android::MediaPlayerBase;
+using android::OK;
+using android::Parcel;
+using android::SortedVector;
+using android::TEST_PLAYER;
+using android::UNKNOWN_ERROR;
+using android::player_type;
+using android::sp;
+using android::status_t;
+
+// This file contains a test player that is loaded via the
+// TestPlayerStub class. The player contains various implementation
+// of the invoke method that java tests can use.
+
+namespace {
+const char *kPing = "ping";
+
+class Player: public MediaPlayerBase
+{
+ public:
+ enum TestType {TEST_UNKNOWN, PING};
+ Player() {}
+ virtual ~Player() {}
+
+ virtual status_t initCheck() {return OK;}
+ virtual bool hardwareOutput() {return true;}
+
+ virtual status_t setDataSource(const char *url) {
+ LOGV("setDataSource %s", url);
+ mTest = TEST_UNKNOWN;
+ if (strncmp(url, kPing, strlen(kPing)) == 0) {
+ mTest = PING;
+ }
+ return OK;
+ }
+
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length) {return OK;}
+ virtual status_t setVideoSurface(const sp<ISurface>& surface) {return OK;}
+ virtual status_t prepare() {return OK;}
+ virtual status_t prepareAsync() {return OK;}
+ virtual status_t start() {return OK;}
+ virtual status_t stop() {return OK;}
+ virtual status_t pause() {return OK;}
+ virtual bool isPlaying() {return true;}
+ virtual status_t seekTo(int msec) {return OK;}
+ virtual status_t getCurrentPosition(int *msec) {return OK;}
+ virtual status_t getDuration(int *msec) {return OK;}
+ virtual status_t reset() {return OK;}
+ virtual status_t setLooping(int loop) {return OK;}
+ virtual player_type playerType() {return TEST_PLAYER;}
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+
+ private:
+ // Take a request, copy it to the reply.
+ void ping(const Parcel& request, Parcel *reply);
+
+ status_t mStatus;
+ TestType mTest;
+};
+
+status_t Player::invoke(const Parcel& request, Parcel *reply)
+{
+ switch (mTest) {
+ case PING:
+ ping(request, reply);
+ break;
+ default: mStatus = UNKNOWN_ERROR;
+ }
+ return mStatus;
+}
+
+void Player::ping(const Parcel& request, Parcel *reply)
+{
+ const size_t len = request.dataAvail();
+
+ reply->setData(static_cast<const uint8_t*>(request.readInplace(len)), len);
+ mStatus = OK;
+}
+
+}
+
+extern "C" android::MediaPlayerBase* newPlayer()
+{
+ LOGD("New invoke test player");
+ return new Player();
+}
+
+extern "C" android::status_t deletePlayer(android::MediaPlayerBase *player)
+{
+ LOGD("Delete invoke test player");
+ delete player;
+ return OK;
+}
diff --git a/obex/Android.mk b/obex/Android.mk
new file mode 100644
index 0000000..fbfe9be
--- /dev/null
+++ b/obex/Android.mk
@@ -0,0 +1,9 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE:= javax.obex
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/obex/javax/obex/ApplicationParameter.java b/obex/javax/obex/ApplicationParameter.java
new file mode 100644
index 0000000..a62210f
--- /dev/null
+++ b/obex/javax/obex/ApplicationParameter.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * @hide
+ */
+public final class ApplicationParameter {
+
+ private byte[] mArray;
+
+ private int mLength;
+
+ private int mMaxLength = 1000;
+
+ public static class TRIPLET_TAGID {
+ public static final byte ORDER_TAGID = 0x01;
+
+ public static final byte SEARCH_VALUE_TAGID = 0x02;
+
+ public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
+
+ // if equals to "0", PSE only reply number of contacts
+ public static final byte MAXLISTCOUNT_TAGID = 0x04;
+
+ public static final byte LISTSTARTOFFSET_TAGID = 0x05;
+
+ public static final byte FILTER_TAGID = 0x06;
+
+ public static final byte FORMAT_TAGID = 0x07;
+
+ // only used if max list count = 0
+ public static final byte PHONEBOOKSIZE_TAGID = 0x08;
+
+ // only used in "mch" in response
+ public static final byte NEWMISSEDCALLS_TAGID = 0x09;
+ }
+
+ public static class TRIPLET_VALUE {
+ public static class ORDER {
+ public static final byte ORDER_BY_INDEX = 0x00;
+
+ public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
+
+ public static final byte ORDER_BY_PHONETIC = 0x02;
+ }
+
+ public static class SEARCHATTRIBUTE {
+ public static final byte SEARCH_BY_NAME = 0x00;
+
+ public static final byte SEARCH_BY_NUMBER = 0x01;
+
+ public static final byte SEARCH_BY_SOUND = 0x02;
+ }
+
+ public static class FORMAT {
+ public static final byte VCARD_VERSION_21 = 0x00;
+
+ public static final byte VCARD_VERSION_30 = 0x01;
+ }
+ }
+
+ public static class TRIPLET_LENGTH {
+ public static final byte ORDER_LENGTH = 1;
+
+ public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
+
+ public static final byte MAXLISTCOUNT_LENGTH = 2;
+
+ public static final byte LISTSTARTOFFSET_LENGTH = 2;
+
+ public static final byte FILTER_LENGTH = 8;
+
+ public static final byte FORMAT_LENGTH = 1;
+
+ public static final byte PHONEBOOKSIZE_LENGTH = 2;
+
+ public static final byte NEWMISSEDCALLS_LENGTH = 1;
+ }
+
+ public ApplicationParameter() {
+ mArray = new byte[mMaxLength];
+ mLength = 0;
+ }
+
+ public void addAPPHeader(byte tag, byte len, byte[] value) {
+ if ((mLength + len + 2) > mMaxLength) {
+ byte[] array_tmp = new byte[mLength + 4 * len];
+ System.arraycopy(mArray, 0, array_tmp, 0, mLength);
+ mArray = array_tmp;
+ mMaxLength = mLength + 4 * len;
+ }
+ mArray[mLength++] = tag;
+ mArray[mLength++] = len;
+ System.arraycopy(value, 0, mArray, mLength, len);
+ mLength += len;
+ }
+
+ public byte[] getAPPparam() {
+ byte[] para = new byte[mLength];
+ System.arraycopy(mArray, 0, para, 0, mLength);
+ return para;
+ }
+}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
new file mode 100644
index 0000000..ec226fb
--- /dev/null
+++ b/obex/javax/obex/Authenticator.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This interface provides a way to respond to authentication challenge and
+ * authentication response headers. When a client or server receives an
+ * authentication challenge or authentication response header, the
+ * <code>onAuthenticationChallenge()</code> or
+ * <code>onAuthenticationResponse()</code> will be called, respectively, by the
+ * implementation.
+ * <P>
+ * For more information on how the authentication procedure works in OBEX,
+ * please review the IrOBEX specification at <A
+ * HREF="http://www.irda.org">http://www.irda.org</A>.
+ * <P>
+ * <STRONG>Authentication Challenges</STRONG>
+ * <P>
+ * When a client or server receives an authentication challenge header, the
+ * <code>onAuthenticationChallenge()</code> method will be invoked by the OBEX
+ * API implementation. The application will then return the user name (if
+ * needed) and password via a <code>PasswordAuthentication</code> object. The
+ * password in this object is not sent in the authentication response. Instead,
+ * the 16-byte challenge received in the authentication challenge is combined
+ * with the password returned from the <code>onAuthenticationChallenge()</code>
+ * method and passed through the MD5 hash algorithm. The resulting value is sent
+ * in the authentication response along with the user name if it was provided.
+ * <P>
+ * <STRONG>Authentication Responses</STRONG>
+ * <P>
+ * When a client or server receives an authentication response header, the
+ * <code>onAuthenticationResponse()</code> method is invoked by the API
+ * implementation with the user name received in the authentication response
+ * header. (The user name will be <code>null</code> if no user name was provided
+ * in the authentication response header.) The application must determine the
+ * correct password. This value should be returned from the
+ * <code>onAuthenticationResponse()</code> method. If the authentication request
+ * should fail without the implementation checking the password,
+ * <code>null</code> should be returned by the application. (This is needed for
+ * reasons like not recognizing the user name, etc.) If the returned value is
+ * not <code>null</code>, the OBEX API implementation will combine the password
+ * returned from the <code>onAuthenticationResponse()</code> method and
+ * challenge sent via the authentication challenge, apply the MD5 hash
+ * algorithm, and compare the result to the response hash received in the
+ * authentication response header. If the values are not equal, an
+ * <code>IOException</code> will be thrown if the client requested
+ * authentication. If the server requested authentication, the
+ * <code>onAuthenticationFailure()</code> method will be called on the
+ * <code>ServerRequestHandler</code> that failed authentication. The connection
+ * is <B>not</B> closed if authentication failed.
+ * @hide
+ */
+public interface Authenticator {
+
+ /**
+ * Called when a client or a server receives an authentication challenge
+ * header. It should respond to the challenge with a
+ * <code>PasswordAuthentication</code> that contains the correct user name
+ * and password for the challenge.
+ * @param description the description of which user name and password should
+ * be used; if no description is provided in the authentication
+ * challenge or the description is encoded in an encoding scheme that
+ * is not supported, an empty string will be provided
+ * @param isUserIdRequired <code>true</code> if the user ID is required;
+ * <code>false</code> if the user ID is not required
+ * @param isFullAccess <code>true</code> if full access to the server will
+ * be granted; <code>false</code> if read only access will be granted
+ * @return a <code>PasswordAuthentication</code> object containing the user
+ * name and password used for authentication
+ */
+ PasswordAuthentication onAuthenticationChallenge(String description, boolean isUserIdRequired,
+ boolean isFullAccess);
+
+ /**
+ * Called when a client or server receives an authentication response
+ * header. This method will provide the user name and expect the correct
+ * password to be returned.
+ * @param userName the user name provided in the authentication response; may
+ * be <code>null</code>
+ * @return the correct password for the user name provided; if
+ * <code>null</code> is returned then the authentication request
+ * failed
+ */
+ byte[] onAuthenticationResponse(byte[] userName);
+}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
new file mode 100644
index 0000000..022ad4f
--- /dev/null
+++ b/obex/javax/obex/BaseStream.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * This interface defines the methods needed by a parent that uses the
+ * PrivateInputStream and PrivateOutputStream objects defined in this package.
+ * @hide
+ */
+public interface BaseStream {
+
+ /**
+ * Verifies that this object is still open.
+ * @throws IOException if the object is closed
+ */
+ void ensureOpen() throws IOException;
+
+ /**
+ * Verifies that additional information may be sent. In other words, the
+ * operation is not done.
+ * @throws IOException if the operation is completed
+ */
+ void ensureNotDone() throws IOException;
+
+ /**
+ * Continues the operation since there is no data to read.
+ * @param sendEmpty <code>true</code> if the operation should send an empty
+ * packet or not send anything if there is no data to send
+ * @param inStream <code>true</code> if the stream is input stream or is
+ * output stream
+ * @return <code>true</code> if the operation was completed;
+ * <code>false</code> if no operation took place
+ * @throws IOException if an IO error occurs
+ */
+ boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
+
+ /**
+ * Called when the output or input stream is closed.
+ * @param inStream <code>true</code> if the input stream is closed;
+ * <code>false</code> if the output stream is closed
+ * @throws IOException if an IO error occurs
+ */
+ void streamClosed(boolean inStream) throws IOException;
+}
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
new file mode 100644
index 0000000..65663b1
--- /dev/null
+++ b/obex/javax/obex/ClientOperation.java
@@ -0,0 +1,720 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the <code>Operation</code> interface. It will read and
+ * write data via puts and gets.
+ * @hide
+ */
+public final class ClientOperation implements Operation, BaseStream {
+
+ private ClientSession mParent;
+
+ private boolean mInputOpen;
+
+ private PrivateInputStream mPrivateInput;
+
+ private boolean mPrivateInputOpen;
+
+ private PrivateOutputStream mPrivateOutput;
+
+ private boolean mPrivateOutputOpen;
+
+ private String mExceptionMessage;
+
+ private int mMaxPacketSize;
+
+ private boolean mOperationDone;
+
+ private boolean mGetOperation;
+
+ private HeaderSet mRequestHeader;
+
+ private HeaderSet mReplyHeader;
+
+ private boolean mEndOfBodySent;
+
+ /**
+ * Creates new OperationImpl to read and write data to a server
+ * @param maxSize the maximum packet size
+ * @param p the parent to this object
+ * @param type <code>true</code> if this is a get request;
+ * <code>false</code. if this is a put request
+ * @param header the header to set in the initial request
+ * @throws IOException if the an IO error occurred
+ */
+ public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
+ throws IOException {
+
+ mParent = p;
+ mEndOfBodySent = false;
+ mInputOpen = true;
+ mOperationDone = false;
+ mMaxPacketSize = maxSize;
+ mGetOperation = type;
+
+ mPrivateInputOpen = false;
+ mPrivateOutputOpen = false;
+ mPrivateInput = null;
+ mPrivateOutput = null;
+
+ mReplyHeader = new HeaderSet();
+
+ mRequestHeader = new HeaderSet();
+
+ int[] headerList = header.getHeaderList();
+
+ if (headerList != null) {
+
+ for (int i = 0; i < headerList.length; i++) {
+ mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
+ }
+ }
+
+ if ((header).mAuthChall != null) {
+ mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
+ System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
+ (header).mAuthChall.length);
+ }
+
+ if ((header).mAuthResp != null) {
+ mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
+ System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
+ (header).mAuthResp.length);
+
+ }
+ }
+
+ /**
+ * Sends an ABORT message to the server. By calling this method, the
+ * corresponding input and output streams will be closed along with this
+ * object.
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server called this method
+ */
+ public synchronized void abort() throws IOException {
+ ensureOpen();
+ //no compatible with sun-ri
+ if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ throw new IOException("Operation has already ended");
+ }
+
+ mExceptionMessage = "Operation aborted";
+ if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ mOperationDone = true;
+ /*
+ * Since we are not sending any headers or returning any headers then
+ * we just need to write and read the same bytes
+ */
+ mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null);
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
+ throw new IOException("Invalid response code from server");
+ }
+
+ mExceptionMessage = null;
+ }
+
+ close();
+ }
+
+ /**
+ * Retrieves the response code retrieved from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> interface.
+ * @return the response code retrieved from the server
+ * @throws IOException if an error occurred in the transport layer during
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet</code> in a <code>ClientSession</code>
+ * object
+ */
+ public synchronized int getResponseCode() throws IOException {
+ //avoid dup validateConnection
+ if ((mReplyHeader.responseCode == -1)
+ || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ validateConnection();
+ }
+
+ return mReplyHeader.responseCode;
+ }
+
+ /**
+ * This method will always return <code>null</code>
+ * @return <code>null</code>
+ */
+ public String getEncoding() {
+ return null;
+ }
+
+ /**
+ * Returns the type of content that the resource connected to is providing.
+ * E.g. if the connection is via HTTP, then the value of the content-type
+ * header field is returned.
+ * @return the content type of the resource that the URL references, or
+ * <code>null</code> if not known
+ */
+ public String getType() {
+ try {
+ return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the length of the content which is being provided. E.g. if the
+ * connection is via HTTP, then the value of the content-length header field
+ * is returned.
+ * @return the content length of the resource that this connection's URL
+ * references, or -1 if the content length is not known
+ */
+ public long getLength() {
+ try {
+ Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
+
+ if (temp == null) {
+ return -1;
+ } else {
+ return temp.longValue();
+ }
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Open and return an input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ */
+ public InputStream openInputStream() throws IOException {
+
+ ensureOpen();
+
+ if (mPrivateInputOpen)
+ throw new IOException("no more input streams available");
+ if (mGetOperation) {
+ // send the GET request here
+ validateConnection();
+ } else {
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ }
+
+ mPrivateInputOpen = true;
+
+ return mPrivateInput;
+ }
+
+ /**
+ * Open and return a data input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ */
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ /**
+ * Open and return an output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public OutputStream openOutputStream() throws IOException {
+
+ ensureOpen();
+ ensureNotDone();
+
+ if (mPrivateOutputOpen)
+ throw new IOException("no more output streams available");
+
+ if (mPrivateOutput == null) {
+ // there are 3 bytes operation headers and 3 bytes body headers //
+ mPrivateOutput = new PrivateOutputStream(this, mMaxPacketSize - 6);
+ }
+
+ mPrivateOutputOpen = true;
+
+ return mPrivateOutput;
+ }
+
+ public int getMaxPacketSize() {
+ return mMaxPacketSize - 6;
+ }
+
+ /**
+ * Open and return a data output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ /**
+ * Closes the connection and ends the transaction
+ * @throws IOException if the operation has already ended or is closed
+ */
+ public void close() throws IOException {
+ mInputOpen = false;
+ mPrivateInputOpen = false;
+ mPrivateOutputOpen = false;
+ mParent.setRequestInactive();
+ }
+
+ /**
+ * Returns the headers that have been received during the operation.
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
+ * @return the headers received during this <code>Operation</code>
+ * @throws IOException if this <code>Operation</code> has been closed
+ */
+ public HeaderSet getReceivedHeader() throws IOException {
+ ensureOpen();
+
+ return mReplyHeader;
+ }
+
+ /**
+ * Specifies the headers that should be sent in the next OBEX message that
+ * is sent.
+ * @param headers the headers to send in the next message
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
+ * @throws IllegalArgumentException if <code>headers</code> was not created
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ * @throws NullPointerException if <code>headers</code> is <code>null</code>
+ */
+ public void sendHeaders(HeaderSet headers) throws IOException {
+ ensureOpen();
+ if (mOperationDone) {
+ throw new IOException("Operation has already exchanged all data");
+ }
+
+ if (headers == null) {
+ throw new IOException("Headers may not be null");
+ }
+
+ int[] headerList = headers.getHeaderList();
+ if (headerList != null) {
+ for (int i = 0; i < headerList.length; i++) {
+ mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
+ }
+ }
+ }
+
+ /**
+ * Verifies that additional information may be sent. In other words, the
+ * operation is not done.
+ * @throws IOException if the operation is completed
+ */
+ public void ensureNotDone() throws IOException {
+ if (mOperationDone) {
+ throw new IOException("Operation has completed");
+ }
+ }
+
+ /**
+ * Verifies that the connection is open and no exceptions should be thrown.
+ * @throws IOException if an exception needs to be thrown
+ */
+ public void ensureOpen() throws IOException {
+ mParent.ensureOpen();
+
+ if (mExceptionMessage != null) {
+ throw new IOException(mExceptionMessage);
+ }
+ if (!mInputOpen) {
+ throw new IOException("Operation has already ended");
+ }
+ }
+
+ /**
+ * Verifies that the connection is open and the proper data has been read.
+ * @throws IOException if an IO error occurs
+ */
+ private void validateConnection() throws IOException {
+ ensureOpen();
+
+ // to sure only one privateInput object exist.
+ if (mPrivateInput == null) {
+ startProcessing();
+ }
+ }
+
+ /**
+ * Sends a request to the client of the specified type
+ * @param opCode the request code to send to the client
+ * @return <code>true</code> if there is more data to send;
+ * <code>false</code> if there is no more data to send
+ * @throws IOException if an IO error occurs
+ */
+ private boolean sendRequest(int opCode) throws IOException {
+ boolean returnValue = false;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int bodyLength = -1;
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
+ if (mPrivateOutput != null) {
+ bodyLength = mPrivateOutput.size();
+ }
+
+ /*
+ * Determine if there is space to add a body request. At present
+ * this method checks to see if there is room for at least a 17
+ * byte body header. This number needs to be at least 6 so that
+ * there is room for the header ID and length and the reply ID and
+ * length, but it is a waste of resources if we can't send much of
+ * the body.
+ */
+ if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketSize) {
+ int end = 0;
+ int start = 0;
+ // split & send the headerArray in multiple packets.
+
+ while (end != headerArray.length) {
+ //split the headerArray
+ end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
+ - ObexHelper.BASE_PACKET_LENGTH);
+ // can not split
+ if (end == -1) {
+ mOperationDone = true;
+ abort();
+ mExceptionMessage = "Header larger then can be sent in a packet";
+ mInputOpen = false;
+
+ if (mPrivateInput != null) {
+ mPrivateInput.close();
+ }
+
+ if (mPrivateOutput != null) {
+ mPrivateOutput.close();
+ }
+ throw new IOException("OBEX Packet exceeds max packet size");
+ }
+
+ byte[] sendHeader = new byte[end - start];
+ System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+ if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) {
+ return false;
+ }
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ return false;
+ }
+
+ start = end;
+ }
+
+ if (bodyLength > 0) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ out.write(headerArray);
+ }
+
+ if (bodyLength > 0) {
+ /*
+ * Determine if we can send the whole body or just part of
+ * the body. Remember that there is the 3 bytes for the
+ * response message and 3 bytes for the header ID and length
+ */
+ if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
+ returnValue = true;
+
+ bodyLength = mMaxPacketSize - headerArray.length - 6;
+ }
+
+ byte[] body = mPrivateOutput.readBytes(bodyLength);
+
+ /*
+ * Since this is a put request if the final bit is set or
+ * the output stream is closed we need to send the 0x49
+ * (End of Body) otherwise, we need to send 0x48 (Body)
+ */
+ if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
+ && ((opCode & 0x80) != 0)) {
+ out.write(0x49);
+ mEndOfBodySent = true;
+ } else {
+ out.write(0x48);
+ }
+
+ bodyLength += 3;
+ out.write((byte)(bodyLength >> 8));
+ out.write((byte)bodyLength);
+
+ if (body != null) {
+ out.write(body);
+ }
+ }
+
+ if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
+ // only 0x82 or 0x83 can send 0x49
+ if ((opCode & 0x80) == 0) {
+ out.write(0x48);
+ } else {
+ out.write(0x49);
+ mEndOfBodySent = true;
+
+ }
+
+ bodyLength = 3;
+ out.write((byte)(bodyLength >> 8));
+ out.write((byte)bodyLength);
+ }
+
+ if (out.size() == 0) {
+ if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) {
+ return false;
+ }
+ return returnValue;
+ }
+ if ((out.size() > 0)
+ && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) {
+ return false;
+ }
+
+ // send all of the output data in 0x48,
+ // send 0x49 with empty body
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
+ returnValue = true;
+
+ return returnValue;
+ }
+
+ /**
+ * This method starts the processing thread results. It will send the
+ * initial request. If the response takes more then one packet, a thread
+ * will be started to handle additional requests
+ * @throws IOException if an IO error occurs
+ */
+ private synchronized void startProcessing() throws IOException {
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ boolean more = true;
+
+ if (mGetOperation) {
+ if (!mOperationDone) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(0x03);
+ }
+
+ if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+ }
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
+ } else {
+
+ if (!mOperationDone) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(0x02);
+
+ }
+ }
+
+ if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(0x82, null, mReplyHeader, mPrivateInput);
+ }
+
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
+ }
+
+ /**
+ * Continues the operation since there is no data to read.
+ * @param sendEmpty <code>true</code> if the operation should send an empty
+ * packet or not send anything if there is no data to send
+ * @param inStream <code>true</code> if the stream is input stream or is
+ * output stream
+ * @throws IOException if an IO error occurs
+ */
+ public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+ throws IOException {
+
+ if (mGetOperation) {
+ if ((inStream) && (!mOperationDone)) {
+ // to deal with inputstream in get operation
+ mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+ /*
+ * Determine if that was not the last packet in the operation
+ */
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+
+ return true;
+
+ } else if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in get operation
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ sendRequest(0x03);
+ return true;
+
+ } else if (mOperationDone) {
+ return false;
+ }
+
+ } else {
+ if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in put operation
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+ sendRequest(0x02);
+ return true;
+ } else if ((inStream) && (!mOperationDone)) {
+ // How to deal with inputstream in put operation ?
+ return false;
+
+ } else if (mOperationDone) {
+ return false;
+ }
+
+ }
+ return false;
+ }
+
+ /**
+ * Called when the output or input stream is closed.
+ * @param inStream <code>true</code> if the input stream is closed;
+ * <code>false</code> if the output stream is closed
+ * @throws IOException if an IO error occurs
+ */
+ public void streamClosed(boolean inStream) throws IOException {
+ if (!mGetOperation) {
+ if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in put operation
+
+ boolean more = true;
+
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+ if (headerArray.length <= 0)
+ more = false;
+ }
+ // If have not sent any data so send all now
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(0x02);
+ }
+
+ /*
+ * According to the IrOBEX specification, after the final put, you
+ * only have a single reply to send. so we don't need the while
+ * loop.
+ */
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+
+ sendRequest(0x82);
+ }
+ mOperationDone = true;
+ } else if ((inStream) && (mOperationDone)) {
+ // how to deal with input stream in put stream ?
+ mOperationDone = true;
+ }
+ } else {
+ if ((inStream) && (!mOperationDone)) {
+
+ // to deal with inputstream in get operation
+ // Have not sent any data so send it all now
+
+ if (mReplyHeader.responseCode == -1) {
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ }
+
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ if (!sendRequest(0x83)) {
+ break;
+ }
+ }
+ while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+ }
+ mOperationDone = true;
+ } else if ((!inStream) && (!mOperationDone)) {
+ // to deal with outputstream in get operation
+ // part of the data may have been sent in continueOperation.
+
+ boolean more = true;
+
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
+ byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
+ if (headerArray.length <= 0)
+ more = false;
+ }
+
+ if (mPrivateInput == null) {
+ mPrivateInput = new PrivateInputStream(this);
+ }
+ if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
+ more = false;
+
+ mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+ while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+ more = sendRequest(0x03);
+ }
+ sendRequest(0x83);
+ // parent.sendRequest(0x83, null, replyHeaders, privateInput);
+ if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
+ mOperationDone = true;
+ }
+ }
+ }
+ }
+}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
new file mode 100644
index 0000000..0935383
--- /dev/null
+++ b/obex/javax/obex/ClientSession.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class in an implementation of the OBEX ClientSession.
+ * @hide
+ */
+public final class ClientSession extends ObexSession {
+
+ private boolean mOpen;
+
+ // Determines if an OBEX layer connection has been established
+ private boolean mObexConnected;
+
+ private byte[] mConnectionId = null;
+
+ /*
+ * The max Packet size must be at least 256 according to the OBEX
+ * specification.
+ */
+ private int maxPacketSize = 256;
+
+ private boolean mRequestActive;
+
+ private final InputStream mInput;
+
+ private final OutputStream mOutput;
+
+ public ClientSession(final ObexTransport trans) throws IOException {
+ mInput = trans.openInputStream();
+ mOutput = trans.openOutputStream();
+ mOpen = true;
+ mRequestActive = false;
+ }
+
+ public HeaderSet connect(final HeaderSet header) throws IOException {
+ ensureOpen();
+ if (mObexConnected) {
+ throw new IOException("Already connected to server");
+ }
+ setRequestActive();
+
+ int totalLength = 4;
+ byte[] head = null;
+
+ // Determine the header byte array
+ if (header != null) {
+ if (header.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+ }
+ head = ObexHelper.createHeader(header, false);
+ totalLength += head.length;
+ }
+ /*
+ * Write the OBEX CONNECT packet to the server.
+ * Byte 0: 0x80
+ * Byte 1&2: Connect Packet Length
+ * Byte 3: OBEX Version Number (Presently, 0x10)
+ * Byte 4: Flags (For TCP 0x00)
+ * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
+ * Byte 7 to n: headers
+ */
+ byte[] requestPacket = new byte[totalLength];
+ // We just need to start at byte 3 since the sendRequest() method will
+ // handle the length and 0x80.
+ requestPacket[0] = (byte)0x10;
+ requestPacket[1] = (byte)0x00;
+ requestPacket[2] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
+ requestPacket[3] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+ if (head != null) {
+ System.arraycopy(head, 0, requestPacket, 4, head.length);
+ }
+
+ // check with local max packet size
+ if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+ throw new IOException("Packet size exceeds max packet size");
+ }
+
+ HeaderSet returnHeaderSet = new HeaderSet();
+ sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null);
+
+ /*
+ * Read the response from the OBEX server.
+ * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
+ * Byte 1&2: Packet Length
+ * Byte 3: OBEX Version Number
+ * Byte 4: Flags3
+ * Byte 5&6: Max OBEX packet Length
+ * Byte 7 to n: Optional HeaderSet
+ */
+ if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
+ mObexConnected = true;
+ }
+ setRequestInactive();
+
+ return returnHeaderSet;
+ }
+
+ public Operation get(HeaderSet header) throws IOException {
+
+ if (!mObexConnected) {
+ throw new IOException("Not connected to the server");
+ }
+ setRequestActive();
+
+ ensureOpen();
+
+ HeaderSet head;
+ if (header == null) {
+ head = new HeaderSet();
+ } else {
+ head = header;
+ if (head.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
+ }
+ }
+ // Add the connection ID if one exists
+ if (mConnectionId != null) {
+ head.mConnectionID = new byte[4];
+ System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
+ }
+
+ return new ClientOperation(maxPacketSize, this, head, true);
+ }
+
+ /**
+ * 0xCB Connection Id an identifier used for OBEX connection multiplexing
+ */
+ public void setConnectionID(long id) {
+ if ((id < 0) || (id > 0xFFFFFFFFL)) {
+ throw new IllegalArgumentException("Connection ID is not in a valid range");
+ }
+ mConnectionId = ObexHelper.convertToByteArray(id);
+ }
+
+ public HeaderSet delete(HeaderSet header) throws IOException {
+
+ Operation op = put(header);
+ op.getResponseCode();
+ HeaderSet returnValue = op.getReceivedHeader();
+ op.close();
+
+ return returnValue;
+ }
+
+ public HeaderSet disconnect(HeaderSet header) throws IOException {
+ if (!mObexConnected) {
+ throw new IOException("Not connected to the server");
+ }
+ setRequestActive();
+
+ ensureOpen();
+ // Determine the header byte array
+ byte[] head = null;
+ if (header != null) {
+ if (header.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
+ }
+ // Add the connection ID if one exists
+ if (mConnectionId != null) {
+ header.mConnectionID = new byte[4];
+ System.arraycopy(mConnectionId, 0, header.mConnectionID, 0, 4);
+ }
+ head = ObexHelper.createHeader(header, false);
+
+ if ((head.length + 3) > maxPacketSize) {
+ throw new IOException("Packet size exceeds max packet size");
+ }
+ } else {
+ // Add the connection ID if one exists
+ if (mConnectionId != null) {
+ head = new byte[5];
+ head[0] = (byte)HeaderSet.CONNECTION_ID;
+ System.arraycopy(mConnectionId, 0, head, 1, 4);
+ }
+ }
+
+ HeaderSet returnHeaderSet = new HeaderSet();
+ sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null);
+
+ /*
+ * An OBEX DISCONNECT reply from the server:
+ * Byte 1: Response code
+ * Bytes 2 & 3: packet size
+ * Bytes 4 & up: headers
+ */
+
+ /* response code , and header are ignored
+ * */
+
+ synchronized (this) {
+ mObexConnected = false;
+ setRequestInactive();
+ }
+
+ return returnHeaderSet;
+ }
+
+ public long getConnectionID() {
+
+ if (mConnectionId == null) {
+ return -1;
+ }
+ return ObexHelper.convertToLong(mConnectionId);
+ }
+
+ public Operation put(HeaderSet header) throws IOException {
+ if (!mObexConnected) {
+ throw new IOException("Not connected to the server");
+ }
+ setRequestActive();
+
+ ensureOpen();
+ HeaderSet head;
+ if (header == null) {
+ head = new HeaderSet();
+ } else {
+ head = header;
+ // when auth is initiated by client ,save the digest
+ if (head.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
+ }
+ }
+
+ // Add the connection ID if one exists
+ if (mConnectionId != null) {
+
+ head.mConnectionID = new byte[4];
+ System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
+ }
+
+ return new ClientOperation(maxPacketSize, this, head, false);
+ }
+
+ public void setAuthenticator(Authenticator auth) throws IOException {
+ if (auth == null) {
+ throw new IOException("Authenticator may not be null");
+ }
+ mAuthenticator = auth;
+ }
+
+ public HeaderSet setPath(HeaderSet header, boolean backup, boolean create) throws IOException {
+ if (!mObexConnected) {
+ throw new IOException("Not connected to the server");
+ }
+ setRequestActive();
+ ensureOpen();
+
+ int totalLength = 2;
+ byte[] head = null;
+ HeaderSet headset;
+ if (header == null) {
+ headset = new HeaderSet();
+ } else {
+ headset = header;
+ if (headset.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
+ }
+ }
+
+ // when auth is initiated by client ,save the digest
+ if (headset.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
+ }
+
+ // Add the connection ID if one exists
+ if (mConnectionId != null) {
+ headset.mConnectionID = new byte[4];
+ System.arraycopy(mConnectionId, 0, headset.mConnectionID, 0, 4);
+ }
+
+ head = ObexHelper.createHeader(headset, false);
+ totalLength += head.length;
+
+ if (totalLength > maxPacketSize) {
+ throw new IOException("Packet size exceeds max packet size");
+ }
+
+ int flags = 0;
+ /*
+ * The backup flag bit is bit 0 so if we add 1, this will set that bit
+ */
+ if (backup) {
+ flags++;
+ }
+ /*
+ * The create bit is bit 1 so if we or with 2 the bit will be set.
+ */
+ if (!create) {
+ flags |= 2;
+ }
+
+ /*
+ * An OBEX SETPATH packet to the server:
+ * Byte 1: 0x85
+ * Byte 2 & 3: packet size
+ * Byte 4: flags
+ * Byte 5: constants
+ * Byte 6 & up: headers
+ */
+ byte[] packet = new byte[totalLength];
+ packet[0] = (byte)flags;
+ packet[1] = (byte)0x00;
+ if (headset != null) {
+ System.arraycopy(head, 0, packet, 2, head.length);
+ }
+
+ HeaderSet returnHeaderSet = new HeaderSet();
+ sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null);
+
+ /*
+ * An OBEX SETPATH reply from the server:
+ * Byte 1: Response code
+ * Bytes 2 & 3: packet size
+ * Bytes 4 & up: headers
+ */
+
+ setRequestInactive();
+
+ return returnHeaderSet;
+ }
+
+ /**
+ * Verifies that the connection is open.
+ * @throws IOException if the connection is closed
+ */
+ public synchronized void ensureOpen() throws IOException {
+ if (!mOpen) {
+ throw new IOException("Connection closed");
+ }
+ }
+
+ /**
+ * Set request inactive. Allows Put and get operation objects to tell this
+ * object when they are done.
+ */
+ /*package*/synchronized void setRequestInactive() {
+ mRequestActive = false;
+ }
+
+ /**
+ * Set request to active.
+ * @throws IOException if already active
+ */
+ private synchronized void setRequestActive() throws IOException {
+ if (mRequestActive) {
+ throw new IOException("OBEX request is already being performed");
+ }
+ mRequestActive = true;
+ }
+
+ /**
+ * Sends a standard request to the client. It will then wait for the reply
+ * and update the header set object provided. If any authentication headers
+ * (i.e. authentication challenge or authentication response) are received,
+ * they will be processed.
+ * @param opCode the type of request to send to the client
+ * @param head the headers to send to the client
+ * @param header the header object to update with the response
+ * @param privateInput the input stream used by the Operation object; null
+ * if this is called on a CONNECT, SETPATH or DISCONNECT return
+ * <code>true</code> if the operation completed successfully;
+ * <code>false</code> if an authentication response failed to pass
+ * @throws IOException if an IO error occurs
+ */
+ public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
+ PrivateInputStream privateInput) throws IOException {
+ //check header length with local max size
+ if (head != null) {
+ if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+ throw new IOException("header too large ");
+ }
+ }
+
+ int bytesReceived;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write((byte)opCode);
+
+ // Determine if there are any headers to send
+ if (head == null) {
+ out.write(0x00);
+ out.write(0x03);
+ } else {
+ out.write((byte)((head.length + 3) >> 8));
+ out.write((byte)(head.length + 3));
+ out.write(head);
+ }
+
+ // Write the request to the output stream and flush the stream
+ mOutput.write(out.toByteArray());
+ mOutput.flush();
+
+ header.responseCode = mInput.read();
+
+ int length = ((mInput.read() << 8) | (mInput.read()));
+
+ if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+ throw new IOException("Packet received exceeds packet size limit");
+ }
+ if (length > ObexHelper.BASE_PACKET_LENGTH) {
+ byte[] data = null;
+ if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
+ @SuppressWarnings("unused")
+ int version = mInput.read();
+ @SuppressWarnings("unused")
+ int flags = mInput.read();
+ maxPacketSize = (mInput.read() << 8) + mInput.read();
+
+ //check with local max size
+ if (maxPacketSize > ObexHelper.MAX_PACKET_SIZE_INT) {
+ maxPacketSize = ObexHelper.MAX_PACKET_SIZE_INT;
+ }
+
+ if (length > 7) {
+ data = new byte[length - 7];
+
+ bytesReceived = mInput.read(data);
+ while (bytesReceived != (length - 7)) {
+ bytesReceived += mInput.read(data, bytesReceived, data.length
+ - bytesReceived);
+ }
+ } else {
+ return true;
+ }
+ } else {
+ data = new byte[length - 3];
+ bytesReceived = mInput.read(data);
+
+ while (bytesReceived != (length - 3)) {
+ bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+ }
+ if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
+ return true;
+ }
+ }
+
+ byte[] body = ObexHelper.updateHeaderSet(header, data);
+ if ((privateInput != null) && (body != null)) {
+ privateInput.writeBytes(body, 1);
+ }
+
+ if (header.mConnectionID != null) {
+ mConnectionId = new byte[4];
+ System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
+ }
+
+ if (header.mAuthResp != null) {
+ if (!handleAuthResp(header.mAuthResp)) {
+ setRequestInactive();
+ throw new IOException("Authentication Failed");
+ }
+ }
+
+ if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
+ && (header.mAuthChall != null)) {
+
+ if (handleAuthChall(header)) {
+ out.write((byte)HeaderSet.AUTH_RESPONSE);
+ out.write((byte)((header.mAuthResp.length + 3) >> 8));
+ out.write((byte)(header.mAuthResp.length + 3));
+ out.write(header.mAuthResp);
+ header.mAuthChall = null;
+ header.mAuthResp = null;
+
+ byte[] sendHeaders = new byte[out.size() - 3];
+ System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
+
+ return sendRequest(opCode, sendHeaders, header, privateInput);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void close() throws IOException {
+ mOpen = false;
+ mInput.close();
+ mOutput.close();
+ }
+}
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
new file mode 100644
index 0000000..8b457f6
--- /dev/null
+++ b/obex/javax/obex/HeaderSet.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.Random;
+
+/**
+ * This class implements the javax.obex.HeaderSet interface for OBEX over
+ * RFCOMM.
+ * @hide
+ */
+public final class HeaderSet {
+
+ /**
+ * Represents the OBEX Count header. This allows the connection statement to
+ * tell the server how many objects it plans to send or retrieve.
+ * <P>
+ * The value of <code>COUNT</code> is 0xC0 (192).
+ */
+ public static final int COUNT = 0xC0;
+
+ /**
+ * Represents the OBEX Name header. This specifies the name of the object.
+ * <P>
+ * The value of <code>NAME</code> is 0x01 (1).
+ */
+ public static final int NAME = 0x01;
+
+ /**
+ * Represents the OBEX Type header. This allows a request to specify the
+ * type of the object (e.g. text, html, binary, etc.).
+ * <P>
+ * The value of <code>TYPE</code> is 0x42 (66).
+ */
+ public static final int TYPE = 0x42;
+
+ /**
+ * Represents the OBEX Length header. This is the length of the object in
+ * bytes.
+ * <P>
+ * The value of <code>LENGTH</code> is 0xC3 (195).
+ */
+ public static final int LENGTH = 0xC3;
+
+ /**
+ * Represents the OBEX Time header using the ISO 8601 standards. This is the
+ * preferred time header.
+ * <P>
+ * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
+ */
+ public static final int TIME_ISO_8601 = 0x44;
+
+ /**
+ * Represents the OBEX Time header using the 4 byte representation. This is
+ * only included for backwards compatibility. It represents the number of
+ * seconds since January 1, 1970.
+ * <P>
+ * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
+ */
+ public static final int TIME_4_BYTE = 0xC4;
+
+ /**
+ * Represents the OBEX Description header. This is a text description of the
+ * object.
+ * <P>
+ * The value of <code>DESCRIPTION</code> is 0x05 (5).
+ */
+ public static final int DESCRIPTION = 0x05;
+
+ /**
+ * Represents the OBEX Target header. This is the name of the service an
+ * operation is targeted to.
+ * <P>
+ * The value of <code>TARGET</code> is 0x46 (70).
+ */
+ public static final int TARGET = 0x46;
+
+ /**
+ * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
+ * included in a request or reply.
+ * <P>
+ * The value of <code>HTTP</code> is 0x47 (71).
+ */
+ public static final int HTTP = 0x47;
+
+ /**
+ * Represents the OBEX BODY header.
+ * <P>
+ * The value of <code>BODY</code> is 0x48 (72).
+ */
+ public static final int BODY = 0x48;
+
+ /**
+ * Represents the OBEX End of BODY header.
+ * <P>
+ * The value of <code>BODY</code> is 0x49 (73).
+ */
+ public static final int END_OF_BODY = 0x49;
+
+ /**
+ * Represents the OBEX Who header. Identifies the OBEX application to
+ * determine if the two peers are talking to each other.
+ * <P>
+ * The value of <code>WHO</code> is 0x4A (74).
+ */
+ public static final int WHO = 0x4A;
+
+ /**
+ * Represents the OBEX Connection ID header. Identifies used for OBEX
+ * connection multiplexing.
+ * <P>
+ * The value of <code>CONNECTION_ID</code> is 0xCB (203).
+ */
+
+ public static final int CONNECTION_ID = 0xCB;
+
+ /**
+ * Represents the OBEX Application Parameter header. This header specifies
+ * additional application request and response information.
+ * <P>
+ * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
+ */
+ public static final int APPLICATION_PARAMETER = 0x4C;
+
+ /**
+ * Represents the OBEX authentication digest-challenge.
+ * <P>
+ * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
+ */
+ public static final int AUTH_CHALLENGE = 0x4D;
+
+ /**
+ * Represents the OBEX authentication digest-response.
+ * <P>
+ * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
+ */
+ public static final int AUTH_RESPONSE = 0x4E;
+
+ /**
+ * Represents the OBEX Object Class header. This header specifies the OBEX
+ * object class of the object.
+ * <P>
+ * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
+ */
+ public static final int OBJECT_CLASS = 0x4F;
+
+ private Long mCount; // 4 byte unsigned integer
+
+ private String mName; // null terminated Unicode text string
+
+ private String mType; // null terminated ASCII text string
+
+ private Long mLength; // 4 byte unsigend integer
+
+ private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
+
+ private Calendar mByteTime; // 4 byte unsigned integer
+
+ private String mDescription; // null terminated Unicode text String
+
+ private byte[] mTarget; // byte sequence
+
+ private byte[] mHttpHeader; // byte sequence
+
+ private byte[] mWho; // length prefixed byte sequence
+
+ private byte[] mAppParam; // byte sequence of the form tag length value
+
+ private byte[] mObjectClass; // byte sequence
+
+ private String[] mUnicodeUserDefined; //null terminated unicode string
+
+ private byte[][] mSequenceUserDefined; // byte sequence user defined
+
+ private Byte[] mByteUserDefined; // 1 byte
+
+ private Long[] mIntegerUserDefined; // 4 byte unsigned integer
+
+ private final Random mRandom;
+
+ /*package*/ byte[] nonce;
+
+ public byte[] mAuthChall; // The authentication challenge header
+
+ public byte[] mAuthResp; // The authentication response header
+
+ public byte[] mConnectionID; // THe connection ID
+
+ public int responseCode;
+
+ /**
+ * Creates new <code>HeaderSet</code> object.
+ * @param size the max packet size for this connection
+ */
+ public HeaderSet() {
+ mUnicodeUserDefined = new String[16];
+ mSequenceUserDefined = new byte[16][];
+ mByteUserDefined = new Byte[16];
+ mIntegerUserDefined = new Long[16];
+ responseCode = -1;
+ mRandom = new Random();
+ }
+
+ /**
+ * Sets the value of the header identifier to the value provided. The type
+ * of object must correspond to the Java type defined in the description of
+ * this interface. If <code>null</code> is passed as the
+ * <code>headerValue</code> then the header will be removed from the set of
+ * headers to include in the next request.
+ * @param headerID the identifier to include in the message
+ * @param headerValue the value of the header identifier
+ * @throws IllegalArgumentException if the header identifier provided is not
+ * one defined in this interface or a user-defined header; if the
+ * type of <code>headerValue</code> is not the correct Java type as
+ * defined in the description of this interface\
+ */
+ public void setHeader(int headerID, Object headerValue) {
+ long temp = -1;
+
+ switch (headerID) {
+ case COUNT:
+ if (!(headerValue instanceof Long)) {
+ if (headerValue == null) {
+ mCount = null;
+ break;
+ }
+ throw new IllegalArgumentException("Count must be a Long");
+ }
+ temp = ((Long)headerValue).longValue();
+ if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+ throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
+ }
+ mCount = (Long)headerValue;
+ break;
+ case NAME:
+ if ((headerValue != null) && (!(headerValue instanceof String))) {
+ throw new IllegalArgumentException("Name must be a String");
+ }
+ mName = (String)headerValue;
+ break;
+ case TYPE:
+ if ((headerValue != null) && (!(headerValue instanceof String))) {
+ throw new IllegalArgumentException("Type must be a String");
+ }
+ mType = (String)headerValue;
+ break;
+ case LENGTH:
+ if (!(headerValue instanceof Long)) {
+ if (headerValue == null) {
+ mLength = null;
+ break;
+ }
+ throw new IllegalArgumentException("Length must be a Long");
+ }
+ temp = ((Long)headerValue).longValue();
+ if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+ throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
+ }
+ mLength = (Long)headerValue;
+ break;
+ case TIME_ISO_8601:
+ if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+ throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
+ }
+ mIsoTime = (Calendar)headerValue;
+ break;
+ case TIME_4_BYTE:
+ if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
+ throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
+ }
+ mByteTime = (Calendar)headerValue;
+ break;
+ case DESCRIPTION:
+ if ((headerValue != null) && (!(headerValue instanceof String))) {
+ throw new IllegalArgumentException("Description must be a String");
+ }
+ mDescription = (String)headerValue;
+ break;
+ case TARGET:
+ if (headerValue == null) {
+ mTarget = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException("Target must be a byte array");
+ } else {
+ mTarget = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
+ }
+ }
+ break;
+ case HTTP:
+ if (headerValue == null) {
+ mHttpHeader = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException("HTTP must be a byte array");
+ } else {
+ mHttpHeader = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
+ }
+ }
+ break;
+ case WHO:
+ if (headerValue == null) {
+ mWho = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException("WHO must be a byte array");
+ } else {
+ mWho = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
+ }
+ }
+ break;
+ case OBJECT_CLASS:
+ if (headerValue == null) {
+ mObjectClass = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException("Object Class must be a byte array");
+ } else {
+ mObjectClass = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
+ }
+ }
+ break;
+ case APPLICATION_PARAMETER:
+ if (headerValue == null) {
+ mAppParam = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException(
+ "Application Parameter must be a byte array");
+ } else {
+ mAppParam = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
+ }
+ }
+ break;
+ default:
+ // Verify that it was not a Unicode String user Defined
+ if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+ if ((headerValue != null) && (!(headerValue instanceof String))) {
+ throw new IllegalArgumentException(
+ "Unicode String User Defined must be a String");
+ }
+ mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
+
+ break;
+ }
+ // Verify that it was not a byte sequence user defined value
+ if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+
+ if (headerValue == null) {
+ mSequenceUserDefined[headerID - 0x70] = null;
+ } else {
+ if (!(headerValue instanceof byte[])) {
+ throw new IllegalArgumentException(
+ "Byte Sequence User Defined must be a byte array");
+ } else {
+ mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
+ System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
+ 0, mSequenceUserDefined[headerID - 0x70].length);
+ }
+ }
+ break;
+ }
+ // Verify that it was not a Byte user Defined
+ if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+ if ((headerValue != null) && (!(headerValue instanceof Byte))) {
+ throw new IllegalArgumentException("ByteUser Defined must be a Byte");
+ }
+ mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
+
+ break;
+ }
+ // Verify that is was not the 4 byte unsigned integer user
+ // defined header
+ if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+ if (!(headerValue instanceof Long)) {
+ if (headerValue == null) {
+ mIntegerUserDefined[headerID - 0xF0] = null;
+ break;
+ }
+ throw new IllegalArgumentException("Integer User Defined must be a Long");
+ }
+ temp = ((Long)headerValue).longValue();
+ if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
+ throw new IllegalArgumentException(
+ "Integer User Defined must be between 0 and 0xFFFFFFFF");
+ }
+ mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
+ break;
+ }
+ throw new IllegalArgumentException("Invalid Header Identifier");
+ }
+ }
+
+ /**
+ * Retrieves the value of the header identifier provided. The type of the
+ * Object returned is defined in the description of this interface.
+ * @param headerID the header identifier whose value is to be returned
+ * @return the value of the header provided or <code>null</code> if the
+ * header identifier specified is not part of this
+ * <code>HeaderSet</code> object
+ * @throws IllegalArgumentException if the <code>headerID</code> is not one
+ * defined in this interface or any of the user-defined headers
+ * @throws IOException if an error occurred in the transport layer during
+ * the operation or if the connection has been closed
+ */
+ public Object getHeader(int headerID) throws IOException {
+
+ switch (headerID) {
+ case COUNT:
+ return mCount;
+ case NAME:
+ return mName;
+ case TYPE:
+ return mType;
+ case LENGTH:
+ return mLength;
+ case TIME_ISO_8601:
+ return mIsoTime;
+ case TIME_4_BYTE:
+ return mByteTime;
+ case DESCRIPTION:
+ return mDescription;
+ case TARGET:
+ return mTarget;
+ case HTTP:
+ return mHttpHeader;
+ case WHO:
+ return mWho;
+ case OBJECT_CLASS:
+ return mObjectClass;
+ case APPLICATION_PARAMETER:
+ return mAppParam;
+ default:
+ // Verify that it was not a Unicode String user Defined
+ if ((headerID >= 0x30) && (headerID <= 0x3F)) {
+ return mUnicodeUserDefined[headerID - 0x30];
+ }
+ // Verify that it was not a byte sequence user defined header
+ if ((headerID >= 0x70) && (headerID <= 0x7F)) {
+ return mSequenceUserDefined[headerID - 0x70];
+ }
+ // Verify that it was not a byte user defined header
+ if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
+ return mByteUserDefined[headerID - 0xB0];
+ }
+ // Verify that it was not a integer user defined header
+ if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
+ return mIntegerUserDefined[headerID - 0xF0];
+ }
+ throw new IllegalArgumentException("Invalid Header Identifier");
+ }
+ }
+
+ /**
+ * Retrieves the list of headers that may be retrieved via the
+ * <code>getHeader</code> method that will not return <code>null</code>. In
+ * other words, this method returns all the headers that are available in
+ * this object.
+ * @see #getHeader
+ * @return the array of headers that are set in this object or
+ * <code>null</code> if no headers are available
+ * @throws IOException if an error occurred in the transport layer during
+ * the operation or the connection has been closed
+ */
+ public int[] getHeaderList() throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ if (mCount != null) {
+ out.write(COUNT);
+ }
+ if (mName != null) {
+ out.write(NAME);
+ }
+ if (mType != null) {
+ out.write(TYPE);
+ }
+ if (mLength != null) {
+ out.write(LENGTH);
+ }
+ if (mIsoTime != null) {
+ out.write(TIME_ISO_8601);
+ }
+ if (mByteTime != null) {
+ out.write(TIME_4_BYTE);
+ }
+ if (mDescription != null) {
+ out.write(DESCRIPTION);
+ }
+ if (mTarget != null) {
+ out.write(TARGET);
+ }
+ if (mHttpHeader != null) {
+ out.write(HTTP);
+ }
+ if (mWho != null) {
+ out.write(WHO);
+ }
+ if (mAppParam != null) {
+ out.write(APPLICATION_PARAMETER);
+ }
+ if (mObjectClass != null) {
+ out.write(OBJECT_CLASS);
+ }
+
+ for (int i = 0x30; i < 0x40; i++) {
+ if (mUnicodeUserDefined[i - 0x30] != null) {
+ out.write(i);
+ }
+ }
+
+ for (int i = 0x70; i < 0x80; i++) {
+ if (mSequenceUserDefined[i - 0x70] != null) {
+ out.write(i);
+ }
+ }
+
+ for (int i = 0xB0; i < 0xC0; i++) {
+ if (mByteUserDefined[i - 0xB0] != null) {
+ out.write(i);
+ }
+ }
+
+ for (int i = 0xF0; i < 0x100; i++) {
+ if (mIntegerUserDefined[i - 0xF0] != null) {
+ out.write(i);
+ }
+ }
+
+ byte[] headers = out.toByteArray();
+ out.close();
+
+ if ((headers == null) || (headers.length == 0)) {
+ return null;
+ }
+
+ int[] result = new int[headers.length];
+ for (int i = 0; i < headers.length; i++) {
+ // Convert the byte to a positive integer. That is, an integer
+ // between 0 and 256.
+ result[i] = headers[i] & 0xFF;
+ }
+
+ return result;
+ }
+
+ /**
+ * Sets the authentication challenge header. The <code>realm</code> will be
+ * encoded based upon the default encoding scheme used by the implementation
+ * to encode strings. Therefore, the encoding scheme used to encode the
+ * <code>realm</code> is application dependent.
+ * @param realm a short description that describes what password to use; if
+ * <code>null</code> no realm will be sent in the authentication
+ * challenge header
+ * @param userID if <code>true</code>, a user ID is required in the reply;
+ * if <code>false</code>, no user ID is required
+ * @param access if <code>true</code> then full access will be granted if
+ * successful; if <code>false</code> then read-only access will be
+ * granted if successful
+ * @throws IOException
+ */
+ public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
+ throws IOException {
+
+ nonce = new byte[16];
+ for (int i = 0; i < 16; i++) {
+ nonce[i] = (byte)mRandom.nextInt();
+ }
+
+ mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
+ }
+
+ /**
+ * Returns the response code received from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> class.
+ * @see ResponseCodes
+ * @return the response code retrieved from the server
+ * @throws IOException if an error occurred in the transport layer during
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet()</code> in a <code>ClientSession</code>
+ * object; if this object was created by an OBEX server
+ */
+ public int getResponseCode() throws IOException {
+ if (responseCode == -1) {
+ throw new IOException("May not be called on a server");
+ } else {
+ return responseCode;
+ }
+ }
+}
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
new file mode 100644
index 0000000..1b66662
--- /dev/null
+++ b/obex/javax/obex/ObexHelper.java
@@ -0,0 +1,995 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import android.security.Md5MessageDigest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * This class defines a set of helper methods for the implementation of Obex.
+ * @hide
+ */
+public final class ObexHelper {
+
+ /**
+ * Defines the basic packet length used by OBEX. Every OBEX packet has the
+ * same basic format:<BR>
+ * Byte 0: Request or Response Code Byte 1&2: Length of the packet.
+ */
+ public static final int BASE_PACKET_LENGTH = 3;
+
+ /** Prevent object construction of helper class */
+ private ObexHelper() {
+ }
+
+ /**
+ * The maximum packet size for OBEX packets that this client can handle. At
+ * present, this must be changed for each port. TODO: The max packet size
+ * should be the Max incoming MTU minus TODO: L2CAP package headers and
+ * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
+ * LocalDevice.getProperty().
+ */
+ /*
+ * android note set as 0xFFFE to match remote MPS
+ */
+ public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
+
+ public static final int OBEX_OPCODE_CONNECT = 0x80;
+
+ public static final int OBEX_OPCODE_DISCONNECT = 0x81;
+
+ public static final int OBEX_OPCODE_PUT = 0x02;
+
+ public static final int OBEX_OPCODE_PUT_FINAL = 0x82;
+
+ public static final int OBEX_OPCODE_GET = 0x03;
+
+ public static final int OBEX_OPCODE_GET_FINAL = 0x83;
+
+ public static final int OBEX_OPCODE_RESERVED = 0x04;
+
+ public static final int OBEX_OPCODE_RESERVED_FINAL = 0x84;
+
+ public static final int OBEX_OPCODE_SETPATH = 0x85;
+
+ public static final int OBEX_OPCODE_ABORT = 0xFF;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ASCII = 0x00;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_1 = 0x01;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_2 = 0x02;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_3 = 0x03;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_4 = 0x04;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_5 = 0x05;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_6 = 0x06;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_7 = 0x07;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_8 = 0x08;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_9 = 0x09;
+
+ public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
+
+ /**
+ * Updates the HeaderSet with the headers received in the byte array
+ * provided. Invalid headers are ignored.
+ * <P>
+ * The first two bits of an OBEX Header specifies the type of object that is
+ * being sent. The table below specifies the meaning of the high bits.
+ * <TABLE>
+ * <TR>
+ * <TH>Bits 8 and 7</TH>
+ * <TH>Value</TH>
+ * <TH>Description</TH>
+ * </TR>
+ * <TR>
+ * <TD>00</TD>
+ * <TD>0x00</TD>
+ * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD>
+ * </TR>
+ * <TR>
+ * <TD>01</TD>
+ * <TD>0x40</TD>
+ * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD>
+ * </TR>
+ * <TR>
+ * <TD>10</TD>
+ * <TD>0x80</TD>
+ * <TD>1 byte quantity</TD>
+ * </TR>
+ * <TR>
+ * <TD>11</TD>
+ * <TD>0xC0</TD>
+ * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD>
+ * </TR>
+ * </TABLE>
+ * This method uses the information in this table to determine the type of
+ * Java object to create and passes that object with the full header to
+ * setHeader() to update the HeaderSet object. Invalid headers will cause an
+ * exception to be thrown. When it is thrown, it is ignored.
+ * @param header the HeaderSet to update
+ * @param headerArray the byte array containing headers
+ * @return the result of the last start body or end body header provided;
+ * the first byte in the result will specify if a body or end of
+ * body is received
+ * @throws IOException if an invalid header was found
+ */
+ public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
+ int index = 0;
+ int length = 0;
+ int headerID;
+ byte[] value = null;
+ byte[] body = null;
+ HeaderSet headerImpl = header;
+ try {
+ while (index < headerArray.length) {
+ headerID = 0xFF & headerArray[index];
+ switch (headerID & (0xC0)) {
+
+ /*
+ * 0x00 is a unicode null terminate string with the first
+ * two bytes after the header identifier being the length
+ */
+ case 0x00:
+ // Fall through
+ /*
+ * 0x40 is a byte sequence with the first
+ * two bytes after the header identifier being the length
+ */
+ case 0x40:
+ boolean trimTail = true;
+ index++;
+ length = 0xFF & headerArray[index];
+ length = length << 8;
+ index++;
+ length += 0xFF & headerArray[index];
+ length -= 3;
+ index++;
+ value = new byte[length];
+ System.arraycopy(headerArray, index, value, 0, length);
+ if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
+ trimTail = false;
+ }
+ switch (headerID) {
+ case HeaderSet.TYPE:
+ try {
+ // Remove trailing null
+ if (trimTail == false) {
+ headerImpl.setHeader(headerID, new String(value, 0,
+ value.length, "ISO8859_1"));
+ } else {
+ headerImpl.setHeader(headerID, new String(value, 0,
+ value.length - 1, "ISO8859_1"));
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw e;
+ }
+ break;
+
+ case HeaderSet.AUTH_CHALLENGE:
+ headerImpl.mAuthChall = new byte[length];
+ System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0,
+ length);
+ break;
+
+ case HeaderSet.AUTH_RESPONSE:
+ headerImpl.mAuthResp = new byte[length];
+ System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0,
+ length);
+ break;
+
+ case HeaderSet.BODY:
+ /* Fall Through */
+ case HeaderSet.END_OF_BODY:
+ body = new byte[length + 1];
+ body[0] = (byte)headerID;
+ System.arraycopy(headerArray, index, body, 1, length);
+ break;
+
+ case HeaderSet.TIME_ISO_8601:
+ try {
+ String dateString = new String(value, "ISO8859_1");
+ Calendar temp = Calendar.getInstance();
+ if ((dateString.length() == 16)
+ && (dateString.charAt(15) == 'Z')) {
+ temp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+ temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
+ 0, 4)));
+ temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
+ 4, 6)));
+ temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
+ .substring(6, 8)));
+ temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
+ .substring(9, 11)));
+ temp.set(Calendar.MINUTE, Integer.parseInt(dateString
+ .substring(11, 13)));
+ temp.set(Calendar.SECOND, Integer.parseInt(dateString
+ .substring(13, 15)));
+ headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
+ } catch (UnsupportedEncodingException e) {
+ throw e;
+ }
+ break;
+
+ default:
+ if ((headerID & 0xC0) == 0x00) {
+ headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
+ value, true));
+ } else {
+ headerImpl.setHeader(headerID, value);
+ }
+ }
+
+ index += length;
+ break;
+
+ /*
+ * 0x80 is a byte header. The only valid byte headers are
+ * the 16 user defined byte headers.
+ */
+ case 0x80:
+ index++;
+ try {
+ headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
+ } catch (Exception e) {
+ // Not a valid header so ignore
+ }
+ index++;
+ break;
+
+ /*
+ * 0xC0 is a 4 byte unsigned integer header and with the
+ * exception of TIME_4_BYTE will be converted to a Long
+ * and added.
+ */
+ case 0xC0:
+ index++;
+ value = new byte[4];
+ System.arraycopy(headerArray, index, value, 0, 4);
+ try {
+ if (headerID != HeaderSet.TIME_4_BYTE) {
+ // Determine if it is a connection ID. These
+ // need to be handled differently
+ if (headerID == HeaderSet.CONNECTION_ID) {
+ headerImpl.mConnectionID = new byte[4];
+ System.arraycopy(value, 0, headerImpl.mConnectionID, 0, 4);
+ } else {
+ headerImpl.setHeader(headerID, Long
+ .valueOf(convertToLong(value)));
+ }
+ } else {
+ Calendar temp = Calendar.getInstance();
+ temp.setTime(new Date(convertToLong(value) * 1000L));
+ headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
+ }
+ } catch (Exception e) {
+ // Not a valid header so ignore
+ throw new IOException("Header was not formatted properly");
+ }
+ index += 4;
+ break;
+ }
+
+ }
+ } catch (IOException e) {
+ throw new IOException("Header was not formatted properly");
+ }
+
+ return body;
+ }
+
+ /**
+ * Creates the header part of OBEX packet based on the header provided.
+ * TODO: Could use getHeaderList() to get the array of headers to include
+ * and then use the high two bits to determine the the type of the object
+ * and construct the byte array from that. This will make the size smaller.
+ * @param head the header used to construct the byte array
+ * @param nullOut <code>true</code> if the header should be set to
+ * <code>null</code> once it is added to the array or
+ * <code>false</code> if it should not be nulled out
+ * @return the header of an OBEX packet
+ */
+ public static byte[] createHeader(HeaderSet head, boolean nullOut) {
+ Long intHeader = null;
+ String stringHeader = null;
+ Calendar dateHeader = null;
+ Byte byteHeader = null;
+ StringBuffer buffer = null;
+ byte[] value = null;
+ byte[] result = null;
+ byte[] lengthArray = new byte[2];
+ int length;
+ HeaderSet headImpl = null;
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ headImpl = head;
+
+ try {
+ /*
+ * Determine if there is a connection ID to send. If there is,
+ * then it should be the first header in the packet.
+ */
+ if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
+
+ out.write((byte)HeaderSet.CONNECTION_ID);
+ out.write(headImpl.mConnectionID);
+ }
+
+ // Count Header
+ intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
+ if (intHeader != null) {
+ out.write((byte)HeaderSet.COUNT);
+ value = ObexHelper.convertToByteArray(intHeader.longValue());
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.COUNT, null);
+ }
+ }
+
+ // Name Header
+ stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
+ if (stringHeader != null) {
+ out.write((byte)HeaderSet.NAME);
+ value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(0xFF & (length >> 8));
+ lengthArray[1] = (byte)(0xFF & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.NAME, null);
+ }
+ }
+
+ // Type Header
+ stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
+ if (stringHeader != null) {
+ out.write((byte)HeaderSet.TYPE);
+ try {
+ value = stringHeader.getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ throw e;
+ }
+
+ length = value.length + 4;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ out.write(0x00);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.TYPE, null);
+ }
+ }
+
+ // Length Header
+ intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
+ if (intHeader != null) {
+ out.write((byte)HeaderSet.LENGTH);
+ value = ObexHelper.convertToByteArray(intHeader.longValue());
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.LENGTH, null);
+ }
+ }
+
+ // Time ISO Header
+ dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
+ if (dateHeader != null) {
+
+ /*
+ * The ISO Header should take the form YYYYMMDDTHHMMSSZ. The
+ * 'Z' will only be included if it is a UTC time.
+ */
+ buffer = new StringBuffer();
+ int temp = dateHeader.get(Calendar.YEAR);
+ for (int i = temp; i < 1000; i = i * 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+ temp = dateHeader.get(Calendar.MONTH);
+ if (temp < 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+ temp = dateHeader.get(Calendar.DAY_OF_MONTH);
+ if (temp < 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+ buffer.append("T");
+ temp = dateHeader.get(Calendar.HOUR_OF_DAY);
+ if (temp < 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+ temp = dateHeader.get(Calendar.MINUTE);
+ if (temp < 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+ temp = dateHeader.get(Calendar.SECOND);
+ if (temp < 10) {
+ buffer.append("0");
+ }
+ buffer.append(temp);
+
+ if (dateHeader.getTimeZone().getID().equals("UTC")) {
+ buffer.append("Z");
+ }
+
+ try {
+ value = buffer.toString().getBytes("ISO8859_1");
+ } catch (UnsupportedEncodingException e) {
+ throw e;
+ }
+
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(HeaderSet.TIME_ISO_8601);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
+ }
+ }
+
+ // Time 4 Byte Header
+ dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
+ if (dateHeader != null) {
+ out.write(HeaderSet.TIME_4_BYTE);
+
+ /*
+ * Need to call getTime() twice. The first call will return
+ * a java.util.Date object. The second call returns the number
+ * of milliseconds since January 1, 1970. We need to convert
+ * it to seconds since the TIME_4_BYTE expects the number of
+ * seconds since January 1, 1970.
+ */
+ value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
+ }
+ }
+
+ // Description Header
+ stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
+ if (stringHeader != null) {
+ out.write((byte)HeaderSet.DESCRIPTION);
+ value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.DESCRIPTION, null);
+ }
+ }
+
+ // Target Header
+ value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
+ if (value != null) {
+ out.write((byte)HeaderSet.TARGET);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.TARGET, null);
+ }
+ }
+
+ // HTTP Header
+ value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
+ if (value != null) {
+ out.write((byte)HeaderSet.HTTP);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.HTTP, null);
+ }
+ }
+
+ // Who Header
+ value = (byte[])headImpl.getHeader(HeaderSet.WHO);
+ if (value != null) {
+ out.write((byte)HeaderSet.WHO);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.WHO, null);
+ }
+ }
+
+ // Connection ID Header
+ value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
+ if (value != null) {
+ out.write((byte)HeaderSet.APPLICATION_PARAMETER);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
+ }
+ }
+
+ // Object Class Header
+ value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
+ if (value != null) {
+ out.write((byte)HeaderSet.OBJECT_CLASS);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
+ }
+ }
+
+ // Check User Defined Headers
+ for (int i = 0; i < 16; i++) {
+
+ //Unicode String Header
+ stringHeader = (String)headImpl.getHeader(i + 0x30);
+ if (stringHeader != null) {
+ out.write((byte)i + 0x30);
+ value = ObexHelper.convertToUnicodeByteArray(stringHeader);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(i + 0x30, null);
+ }
+ }
+
+ // Byte Sequence Header
+ value = (byte[])headImpl.getHeader(i + 0x70);
+ if (value != null) {
+ out.write((byte)i + 0x70);
+ length = value.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(value);
+ if (nullOut) {
+ headImpl.setHeader(i + 0x70, null);
+ }
+ }
+
+ // Byte Header
+ byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
+ if (byteHeader != null) {
+ out.write((byte)i + 0xB0);
+ out.write(byteHeader.byteValue());
+ if (nullOut) {
+ headImpl.setHeader(i + 0xB0, null);
+ }
+ }
+
+ // Integer header
+ intHeader = (Long)headImpl.getHeader(i + 0xF0);
+ if (intHeader != null) {
+ out.write((byte)i + 0xF0);
+ out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
+ if (nullOut) {
+ headImpl.setHeader(i + 0xF0, null);
+ }
+ }
+ }
+
+ // Add the authentication challenge header
+ if (headImpl.mAuthChall != null) {
+ out.write((byte)HeaderSet.AUTH_CHALLENGE);
+ length = headImpl.mAuthChall.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(headImpl.mAuthChall);
+ if (nullOut) {
+ headImpl.mAuthChall = null;
+ }
+ }
+
+ // Add the authentication response header
+ if (headImpl.mAuthResp != null) {
+ out.write((byte)HeaderSet.AUTH_RESPONSE);
+ length = headImpl.mAuthResp.length + 3;
+ lengthArray[0] = (byte)(255 & (length >> 8));
+ lengthArray[1] = (byte)(255 & length);
+ out.write(lengthArray);
+ out.write(headImpl.mAuthResp);
+ if (nullOut) {
+ headImpl.mAuthResp = null;
+ }
+ }
+
+ } catch (IOException e) {
+ } finally {
+ result = out.toByteArray();
+ try {
+ out.close();
+ } catch (Exception ex) {
+ }
+ }
+
+ return result;
+
+ }
+
+ /**
+ * Determines where the maximum divide is between headers. This method is
+ * used by put and get operations to separate headers to a size that meets
+ * the max packet size allowed.
+ * @param headerArray the headers to separate
+ * @param start the starting index to search
+ * @param maxSize the maximum size of a packet
+ * @return the index of the end of the header block to send or -1 if the
+ * header could not be divided because the header is too large
+ */
+ public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
+
+ int fullLength = 0;
+ int lastLength = -1;
+ int index = start;
+ int length = 0;
+
+ while ((fullLength < maxSize) && (index < headerArray.length)) {
+ int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
+ lastLength = fullLength;
+
+ switch (headerID & (0xC0)) {
+
+ case 0x00:
+ // Fall through
+ case 0x40:
+
+ index++;
+ length = (headerArray[index] < 0 ? headerArray[index] + 256
+ : headerArray[index]);
+ length = length << 8;
+ index++;
+ length += (headerArray[index] < 0 ? headerArray[index] + 256
+ : headerArray[index]);
+ length -= 3;
+ index++;
+ index += length;
+ fullLength += length + 3;
+ break;
+
+ case 0x80:
+
+ index++;
+ index++;
+ fullLength += 2;
+ break;
+
+ case 0xC0:
+
+ index += 5;
+ fullLength += 5;
+ break;
+
+ }
+
+ }
+
+ /*
+ * Determine if this is the last header or not
+ */
+ if (lastLength == 0) {
+ /*
+ * Since this is the last header, check to see if the size of this
+ * header is less then maxSize. If it is, return the length of the
+ * header, otherwise return -1. The length of the header is
+ * returned since it would be the start of the next header
+ */
+ if (fullLength < maxSize) {
+ return headerArray.length;
+ } else {
+ return -1;
+ }
+ } else {
+ return lastLength + start;
+ }
+ }
+
+ /**
+ * Converts the byte array to a long.
+ * @param b the byte array to convert to a long
+ * @return the byte array as a long
+ */
+ public static long convertToLong(byte[] b) {
+ long result = 0;
+ long value = 0;
+ long power = 0;
+
+ for (int i = (b.length - 1); i >= 0; i--) {
+ value = b[i];
+ if (value < 0) {
+ value += 256;
+ }
+
+ result = result | (value << power);
+ power += 8;
+ }
+
+ return result;
+ }
+
+ /**
+ * Converts the long to a 4 byte array. The long must be non negative.
+ * @param l the long to convert
+ * @return a byte array that is the same as the long
+ */
+ public static byte[] convertToByteArray(long l) {
+ byte[] b = new byte[4];
+
+ b[0] = (byte)(255 & (l >> 24));
+ b[1] = (byte)(255 & (l >> 16));
+ b[2] = (byte)(255 & (l >> 8));
+ b[3] = (byte)(255 & l);
+
+ return b;
+ }
+
+ /**
+ * Converts the String to a UNICODE byte array. It will also add the ending
+ * null characters to the end of the string.
+ * @param s the string to convert
+ * @return the unicode byte array of the string
+ */
+ public static byte[] convertToUnicodeByteArray(String s) {
+ if (s == null) {
+ return null;
+ }
+
+ char c[] = s.toCharArray();
+ byte[] result = new byte[(c.length * 2) + 2];
+ for (int i = 0; i < c.length; i++) {
+ result[(i * 2)] = (byte)(c[i] >> 8);
+ result[((i * 2) + 1)] = (byte)c[i];
+ }
+
+ // Add the UNICODE null character
+ result[result.length - 2] = 0;
+ result[result.length - 1] = 0;
+
+ return result;
+ }
+
+ /**
+ * Retrieves the value from the byte array for the tag value specified. The
+ * array should be of the form Tag - Length - Value triplet.
+ * @param tag the tag to retrieve from the byte array
+ * @param triplet the byte sequence containing the tag length value form
+ * @return the value of the specified tag
+ */
+ public static byte[] getTagValue(byte tag, byte[] triplet) {
+
+ int index = findTag(tag, triplet);
+ if (index == -1) {
+ return null;
+ }
+
+ index++;
+ int length = triplet[index] & 0xFF;
+
+ byte[] result = new byte[length];
+ index++;
+ System.arraycopy(triplet, index, result, 0, length);
+
+ return result;
+ }
+
+ /**
+ * Finds the index that starts the tag value pair in the byte array provide.
+ * @param tag the tag to look for
+ * @param value the byte array to search
+ * @return the starting index of the tag or -1 if the tag could not be found
+ */
+ public static int findTag(byte tag, byte[] value) {
+ int length = 0;
+
+ if (value == null) {
+ return -1;
+ }
+
+ int index = 0;
+
+ while ((index < value.length) && (value[index] != tag)) {
+ length = value[index + 1] & 0xFF;
+ index += length + 2;
+ }
+
+ if (index >= value.length) {
+ return -1;
+ }
+
+ return index;
+ }
+
+ /**
+ * Converts the byte array provided to a unicode string.
+ * @param b the byte array to convert to a string
+ * @param includesNull determine if the byte string provided contains the
+ * UNICODE null character at the end or not; if it does, it will be
+ * removed
+ * @return a Unicode string
+ * @throws IllegalArgumentException if the byte array has an odd length
+ */
+ public static String convertToUnicode(byte[] b, boolean includesNull) {
+ if (b == null || b.length == 0) {
+ return null;
+ }
+ int arrayLength = b.length;
+ if (!((arrayLength % 2) == 0)) {
+ throw new IllegalArgumentException("Byte array not of a valid form");
+ }
+ arrayLength = (arrayLength >> 1);
+ if (includesNull) {
+ arrayLength -= 1;
+ }
+
+ char[] c = new char[arrayLength];
+ for (int i = 0; i < arrayLength; i++) {
+ int upper = b[2 * i];
+ int lower = b[(2 * i) + 1];
+ if (upper < 0) {
+ upper += 256;
+ }
+ if (lower < 0) {
+ lower += 256;
+ }
+ // If upper and lower both equal 0, it should be the end of string.
+ // Ignore left bytes from array to avoid potential issues
+ if (upper == 0 && lower == 0) {
+ return new String(c, 0, i);
+ }
+
+ c[i] = (char)((upper << 8) | lower);
+ }
+
+ return new String(c);
+ }
+
+ /**
+ * Compute the MD5 hash of the byte array provided. Does not accumulate
+ * input.
+ * @param in the byte array to hash
+ * @return the MD5 hash of the byte array
+ */
+ public static byte[] computeMd5Hash(byte[] in) {
+ Md5MessageDigest md5 = new Md5MessageDigest();
+ return md5.digest(in);
+ }
+
+ /**
+ * Computes an authentication challenge header.
+ * @param nonce the challenge that will be provided to the peer; the
+ * challenge must be 16 bytes long
+ * @param realm a short description that describes what password to use
+ * @param access if <code>true</code> then full access will be granted if
+ * successful; if <code>false</code> then read only access will be
+ * granted if successful
+ * @param userID if <code>true</code>, a user ID is required in the reply;
+ * if <code>false</code>, no user ID is required
+ * @throws IllegalArgumentException if the challenge is not 16 bytes long;
+ * if the realm can not be encoded in less then 255 bytes
+ * @throws IOException if the encoding scheme ISO 8859-1 is not supported
+ */
+ public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
+ boolean userID) throws IOException {
+ byte[] authChall = null;
+
+ if (nonce.length != 16) {
+ throw new IllegalArgumentException("Nonce must be 16 bytes long");
+ }
+
+ /*
+ * The authentication challenge is a byte sequence of the following form
+ * byte 0: 0x00 - the tag for the challenge
+ * byte 1: 0x10 - the length of the challenge; must be 16
+ * byte 2-17: the authentication challenge
+ * byte 18: 0x01 - the options tag; this is optional in the spec, but
+ * we are going to include it in every message
+ * byte 19: 0x01 - length of the options; must be 1
+ * byte 20: the value of the options; bit 0 is set if user ID is
+ * required; bit 1 is set if access mode is read only
+ * byte 21: 0x02 - the tag for authentication realm; only included if
+ * an authentication realm is specified
+ * byte 22: the length of the authentication realm; only included if
+ * the authentication realm is specified
+ * byte 23: the encoding scheme of the authentication realm; we will use
+ * the ISO 8859-1 encoding scheme since it is part of the KVM
+ * byte 24 & up: the realm if one is specified.
+ */
+ if (realm == null) {
+ authChall = new byte[21];
+ } else {
+ if (realm.length() >= 255) {
+ throw new IllegalArgumentException("Realm must be less then 255 bytes");
+ }
+ authChall = new byte[24 + realm.length()];
+ authChall[21] = 0x02;
+ authChall[22] = (byte)(realm.length() + 1);
+ authChall[23] = 0x01; // ISO 8859-1 Encoding
+ System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
+ }
+
+ // Include the nonce field in the header
+ authChall[0] = 0x00;
+ authChall[1] = 0x10;
+ System.arraycopy(nonce, 0, authChall, 2, 16);
+
+ // Include the options header
+ authChall[18] = 0x01;
+ authChall[19] = 0x01;
+ authChall[20] = 0x00;
+
+ if (!access) {
+ authChall[20] = (byte)(authChall[20] | 0x02);
+ }
+ if (userID) {
+ authChall[20] = (byte)(authChall[20] | 0x01);
+ }
+
+ return authChall;
+ }
+}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
new file mode 100644
index 0000000..a7daeb5
--- /dev/null
+++ b/obex/javax/obex/ObexSession.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>ObexSession</code> interface characterizes the term
+ * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
+ * could be the server-side view of an OBEX connection, or the client-side view
+ * of the same connection, which is established by server's accepting of a
+ * client issued "CONNECT".
+ * <P>
+ * This interface serves as the common super class for
+ * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
+ * @hide
+ */
+public class ObexSession {
+
+ protected Authenticator mAuthenticator;
+
+ protected byte[] mChallengeDigest;
+
+ /**
+ * Called when the server received an authentication challenge header. This
+ * will cause the authenticator to handle the authentication challenge.
+ * @param header the header with the authentication challenge
+ * @return <code>true</code> if the last request should be resent;
+ * <code>false</code> if the last request should not be resent
+ * @throws IOException
+ */
+ public boolean handleAuthChall(HeaderSet header) throws IOException {
+ if (mAuthenticator == null) {
+ return false;
+ }
+
+ /*
+ * An authentication challenge is made up of one required and two
+ * optional tag length value triplets. The tag 0x00 is required to be in
+ * the authentication challenge and it represents the challenge digest
+ * that was received. The tag 0x01 is the options tag. This tag tracks
+ * if user ID is required and if full access will be granted. The tag
+ * 0x02 is the realm, which provides a description of which user name
+ * and password to use.
+ */
+ byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall);
+ byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall);
+ byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall);
+
+ String realm = null;
+ if (description != null) {
+ byte[] realmString = new byte[description.length - 1];
+ System.arraycopy(description, 1, realmString, 0, realmString.length);
+
+ switch (description[0] & 0xFF) {
+
+ case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII:
+ // ASCII encoding
+ // Fall through
+ case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1:
+ // ISO-8859-1 encoding
+ try {
+ realm = new String(realmString, "ISO8859_1");
+ } catch (Exception e) {
+ throw new IOException("Unsupported Encoding Scheme");
+ }
+ break;
+
+ case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE:
+ // UNICODE Encoding
+ realm = ObexHelper.convertToUnicode(realmString, false);
+ break;
+
+ default:
+ throw new IOException("Unsupported Encoding Scheme");
+ }
+ }
+
+ boolean isUserIDRequired = false;
+ boolean isFullAccess = true;
+ if (option != null) {
+ if ((option[0] & 0x01) != 0) {
+ isUserIDRequired = true;
+ }
+
+ if ((option[0] & 0x02) != 0) {
+ isFullAccess = false;
+ }
+ }
+
+ PasswordAuthentication result = null;
+ header.mAuthChall = null;
+
+ try {
+ result = mAuthenticator
+ .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
+ } catch (Exception e) {
+ return false;
+ }
+
+ /*
+ * If no password is provided then we not resent the request
+ */
+ if (result == null) {
+ return false;
+ }
+
+ byte[] password = result.getPassword();
+ if (password == null) {
+ return false;
+ }
+
+ byte[] userName = result.getUserName();
+
+ /*
+ * Create the authentication response header. It includes 1 required and
+ * 2 option tag length value triples. The required triple has a tag of
+ * 0x00 and is the response digest. The first optional tag is 0x01 and
+ * represents the user ID. If no user ID is provided, then no user ID
+ * will be sent. The second optional tag is 0x02 and is the challenge
+ * that was received. This will always be sent
+ */
+ if (userName != null) {
+ header.mAuthResp = new byte[38 + userName.length];
+ header.mAuthResp[36] = (byte)0x01;
+ header.mAuthResp[37] = (byte)userName.length;
+ System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length);
+ } else {
+ header.mAuthResp = new byte[36];
+ }
+
+ // Create the secret String
+ byte[] digest = new byte[challenge.length + password.length + 1];
+ System.arraycopy(challenge, 0, digest, 0, challenge.length);
+ // Insert colon between challenge and password
+ digest[challenge.length] = (byte)0x3A;
+ System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
+
+ // Add the Response Digest
+ header.mAuthResp[0] = (byte)0x00;
+ header.mAuthResp[1] = (byte)0x10;
+
+ System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16);
+
+ // Add the challenge
+ header.mAuthResp[18] = (byte)0x02;
+ header.mAuthResp[19] = (byte)0x10;
+ System.arraycopy(challenge, 0, header.mAuthResp, 20, 16);
+
+ return true;
+ }
+
+ /**
+ * Called when the server received an authentication response header. This
+ * will cause the authenticator to handle the authentication response.
+ * @param authResp the authentication response
+ * @return <code>true</code> if the response passed; <code>false</code> if
+ * the response failed
+ */
+ public boolean handleAuthResp(byte[] authResp) {
+ if (mAuthenticator == null) {
+ return false;
+ }
+ // get the correct password from the application
+ byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
+ (byte)0x01, authResp));
+ if (correctPassword == null) {
+ return false;
+ }
+
+ byte[] temp = new byte[correctPassword.length + 16];
+
+ System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
+ System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
+
+ byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
+ byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
+
+ // compare the MD5 hash array .
+ for (int i = 0; i < 16; i++) {
+ if (correctResponse[i] != actualResponse[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
new file mode 100644
index 0000000..445e267
--- /dev/null
+++ b/obex/javax/obex/ObexTransport.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>ObexTransport</code> interface defines the underlying transport
+ * connection which carries the OBEX protocol( such as TCP, RFCOMM device file
+ * exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
+ * platform, Irda). This interface provides an abstract layer to be used by the
+ * <code>ObexConnection</code>. Each kind of medium shall have its own
+ * implementation to wrap and follow the same interface.
+ * <P>
+ * See section 1.2.2 of IrDA Object Exchange Protocol specification.
+ * <P>
+ * Different kind of medium may have different construction - for example, the
+ * RFCOMM device file medium may be constructed from a file descriptor or simply
+ * a string while the TCP medium usually from a socket.
+ * @hide
+ */
+public interface ObexTransport {
+
+ void create() throws IOException;
+
+ void listen() throws IOException;
+
+ void close() throws IOException;
+
+ void connect() throws IOException;
+
+ void disconnect() throws IOException;
+
+ InputStream openInputStream() throws IOException;
+
+ OutputStream openOutputStream() throws IOException;
+
+ DataInputStream openDataInputStream() throws IOException;
+
+ DataOutputStream openDataOutputStream() throws IOException;
+
+}
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
new file mode 100644
index 0000000..20653f2
--- /dev/null
+++ b/obex/javax/obex/Operation.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>Operation</code> interface provides ways to manipulate a single
+ * OBEX PUT or GET operation. The implementation of this interface sends OBEX
+ * packets as they are built. If during the operation the peer in the operation
+ * ends the operation, an <code>IOException</code> is thrown on the next read
+ * from the input stream, write to the output stream, or call to
+ * <code>sendHeaders()</code>.
+ * <P>
+ * <STRONG>Definition of methods inherited from <code>ContentConnection</code>
+ * </STRONG>
+ * <P>
+ * <code>getEncoding()</code> will always return <code>null</code>. <BR>
+ * <code>getLength()</code> will return the length specified by the OBEX Length
+ * header or -1 if the OBEX Length header was not included. <BR>
+ * <code>getType()</code> will return the value specified in the OBEX Type
+ * header or <code>null</code> if the OBEX Type header was not included.<BR>
+ * <P>
+ * <STRONG>How Headers are Handled</STRONG>
+ * <P>
+ * As headers are received, they may be retrieved through the
+ * <code>getReceivedHeaders()</code> method. If new headers are set during the
+ * operation, the new headers will be sent during the next packet exchange.
+ * <P>
+ * <STRONG>PUT example</STRONG>
+ * <P>
+ * <PRE>
+ * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj) throws IOException {
+ * // Include the length header
+ * head.setHeader(head.LENGTH, new Long(obj.length));
+ * // Initiate the PUT request
+ * Operation op = conn.put(head);
+ * // Open the output stream to put the object to it
+ * DataOutputStream out = op.openDataOutputStream();
+ * // Send the object to the server
+ * out.write(obj);
+ * // End the transaction
+ * out.close();
+ * op.close();
+ * }
+ * </PRE>
+ * <P>
+ * <STRONG>GET example</STRONG>
+ * <P>
+ * <PRE>
+ * byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
+ * // Send the initial GET request to the server
+ * Operation op = conn.get(head);
+ * // Retrieve the length of the object being sent back
+ * int length = op.getLength();
+ * // Create space for the object
+ * byte[] obj = new byte[length];
+ * // Get the object from the input stream
+ * DataInputStream in = trans.openDataInputStream();
+ * in.read(obj);
+ * // End the transaction
+ * in.close();
+ * op.close();
+ * return obj;
+ * }
+ * </PRE>
+ *
+ * <H3>Client PUT Operation Flow</H3> For PUT operations, a call to
+ * <code>close()</code> the <code>OutputStream</code> returned from
+ * <code>openOutputStream()</code> or <code>openDataOutputStream()</code> will
+ * signal that the request is done. (In OBEX terms, the End-Of-Body header
+ * should be sent and the final bit in the request will be set.) At this point,
+ * the reply from the server may begin to be processed. A call to
+ * <code>getResponseCode()</code> will do an implicit close on the
+ * <code>OutputStream</code> and therefore signal that the request is done.
+ * <H3>Client GET Operation Flow</H3> For GET operation, a call to
+ * <code>openInputStream()</code> or <code>openDataInputStream()</code> will
+ * signal that the request is done. (In OBEX terms, the final bit in the request
+ * will be set.) A call to <code>getResponseCode()</code> will cause an implicit
+ * close on the <code>InputStream</code>. No further data may be read at this
+ * point.
+ * @hide
+ */
+public interface Operation {
+
+ /**
+ * Sends an ABORT message to the server. By calling this method, the
+ * corresponding input and output streams will be closed along with this
+ * object. No headers are sent in the abort request. This will end the
+ * operation since <code>close()</code> will be called by this method.
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server calls this method
+ */
+ void abort() throws IOException;
+
+ /**
+ * Returns the headers that have been received during the operation.
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
+ * @return the headers received during this <code>Operation</code>
+ * @throws IOException if this <code>Operation</code> has been closed
+ */
+ HeaderSet getReceivedHeader() throws IOException;
+
+ /**
+ * Specifies the headers that should be sent in the next OBEX message that
+ * is sent.
+ * @param headers the headers to send in the next message
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
+ * @throws IllegalArgumentException if <code>headers</code> was not created
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ * or <code>ClientSession.createHeaderSet()</code>
+ * @throws NullPointerException if <code>headers</code> if <code>null</code>
+ */
+ void sendHeaders(HeaderSet headers) throws IOException;
+
+ /**
+ * Returns the response code received from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> class.
+ * @see ResponseCodes
+ * @return the response code retrieved from the server
+ * @throws IOException if an error occurred in the transport layer during
+ * the transaction; if this object was created by an OBEX server
+ */
+ int getResponseCode() throws IOException;
+
+ String getEncoding();
+
+ long getLength();
+
+ String getType();
+
+ InputStream openInputStream() throws IOException;
+
+ DataInputStream openDataInputStream() throws IOException;
+
+ OutputStream openOutputStream() throws IOException;
+
+ DataOutputStream openDataOutputStream() throws IOException;
+
+ void close() throws IOException;
+
+ int getMaxPacketSize();
+}
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
new file mode 100644
index 0000000..326b1ff
--- /dev/null
+++ b/obex/javax/obex/PasswordAuthentication.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * This class holds user name and password combinations.
+ * @hide
+ */
+public final class PasswordAuthentication {
+
+ private byte[] mUserName;
+
+ private final byte[] mPassword;
+
+ /**
+ * Creates a new <code>PasswordAuthentication</code> with the user name and
+ * password provided.
+ * @param userName the user name to include; this may be <code>null</code>
+ * @param password the password to include in the response
+ * @throws NullPointerException if <code>password</code> is
+ * <code>null</code>
+ */
+ public PasswordAuthentication(final byte[] userName, final byte[] password) {
+ if (userName != null) {
+ mUserName = new byte[userName.length];
+ System.arraycopy(userName, 0, mUserName, 0, userName.length);
+ }
+
+ mPassword = new byte[password.length];
+ System.arraycopy(password, 0, mPassword, 0, password.length);
+ }
+
+ /**
+ * Retrieves the user name that was specified in the constructor. The user
+ * name may be <code>null</code>.
+ * @return the user name
+ */
+ public byte[] getUserName() {
+ return mUserName;
+ }
+
+ /**
+ * Retrieves the password.
+ * @return the password
+ */
+ public byte[] getPassword() {
+ return mPassword;
+ }
+}
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
new file mode 100644
index 0000000..5daee72
--- /dev/null
+++ b/obex/javax/obex/PrivateInputStream.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This object provides an input stream to the Operation objects used in this
+ * package.
+ * @hide
+ */
+public final class PrivateInputStream extends InputStream {
+
+ private BaseStream mParent;
+
+ private byte[] mData;
+
+ private int mIndex;
+
+ private boolean mOpen;
+
+ /**
+ * Creates an input stream for the <code>Operation</code> to read from
+ * @param p the connection this input stream is for
+ */
+ public PrivateInputStream(BaseStream p) {
+ mParent = p;
+ mData = new byte[0];
+ mIndex = 0;
+ mOpen = true;
+ }
+
+ /**
+ * Returns the number of bytes that can be read (or skipped over) from this
+ * input stream without blocking by the next caller of a method for this
+ * input stream. The next caller might be the same thread or or another
+ * thread.
+ * @return the number of bytes that can be read from this input stream
+ * without blocking
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public synchronized int available() throws IOException {
+ ensureOpen();
+ return mData.length - mIndex;
+ }
+
+ /**
+ * Reads the next byte of data from the input stream. The value byte is
+ * returned as an int in the range 0 to 255. If no byte is available because
+ * the end of the stream has been reached, the value -1 is returned. This
+ * method blocks until input data is available, the end of the stream is
+ * detected, or an exception is thrown.
+ * @return the byte read from the input stream or -1 if it reaches the end of
+ * stream
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public synchronized int read() throws IOException {
+ ensureOpen();
+ while (mData.length == mIndex) {
+ if (!mParent.continueOperation(true, true)) {
+ return -1;
+ }
+ }
+ return (mData[mIndex++] & 0xFF);
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public synchronized int read(byte[] b, int offset, int length) throws IOException {
+
+ if (b == null) {
+ throw new IOException("buffer is null");
+ }
+ if ((offset | length) < 0 || length > b.length - offset) {
+ throw new ArrayIndexOutOfBoundsException("index outof bound");
+ }
+ ensureOpen();
+
+ int currentDataLength = mData.length - mIndex;
+ int remainReadLength = length;
+ int offset1 = offset;
+ int result = 0;
+
+ while (currentDataLength <= remainReadLength) {
+ System.arraycopy(mData, mIndex, b, offset1, currentDataLength);
+ mIndex += currentDataLength;
+ offset1 += currentDataLength;
+ result += currentDataLength;
+ remainReadLength -= currentDataLength;
+
+ if (!mParent.continueOperation(true, true)) {
+ return result == 0 ? -1 : result;
+ }
+ currentDataLength = mData.length - mIndex;
+ }
+ if (remainReadLength > 0) {
+ System.arraycopy(mData, mIndex, b, offset1, remainReadLength);
+ mIndex += remainReadLength;
+ result += remainReadLength;
+ }
+ return result;
+ }
+
+ /**
+ * Allows the <code>OperationImpl</code> thread to add body data to the
+ * input stream.
+ * @param body the data to add to the stream
+ * @param start the start of the body to array to copy
+ */
+ public synchronized void writeBytes(byte[] body, int start) {
+
+ int length = (body.length - start) + (mData.length - mIndex);
+ byte[] temp = new byte[length];
+
+ System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex);
+ System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start);
+
+ mData = temp;
+ mIndex = 0;
+ notifyAll();
+ }
+
+ /**
+ * Verifies that this stream is open
+ * @throws IOException if the stream is not open
+ */
+ private void ensureOpen() throws IOException {
+ mParent.ensureOpen();
+ if (!mOpen) {
+ throw new IOException("Input stream is closed");
+ }
+ }
+
+ /**
+ * Closes the input stream. If the input stream is already closed, do
+ * nothing.
+ * @throws IOException this will never happen
+ */
+ @Override
+ public void close() throws IOException {
+ mOpen = false;
+ mParent.streamClosed(true);
+ }
+}
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
new file mode 100644
index 0000000..ca420af
--- /dev/null
+++ b/obex/javax/obex/PrivateOutputStream.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This object provides an output stream to the Operation objects used in this
+ * package.
+ * @hide
+ */
+public final class PrivateOutputStream extends OutputStream {
+
+ private BaseStream mParent;
+
+ private ByteArrayOutputStream mArray;
+
+ private boolean mOpen;
+
+ private int mMaxPacketSize;
+
+ /**
+ * Creates an empty <code>PrivateOutputStream</code> to write to.
+ * @param p the connection that this stream runs over
+ */
+ public PrivateOutputStream(BaseStream p, int maxSize) {
+ mParent = p;
+ mArray = new ByteArrayOutputStream();
+ mMaxPacketSize = maxSize;
+ mOpen = true;
+ }
+
+ /**
+ * Determines how many bytes have been written to the output stream.
+ * @return the number of bytes written to the output stream
+ */
+ public int size() {
+ return mArray.size();
+ }
+
+ /**
+ * Writes the specified byte to this output stream. The general contract for
+ * write is that one byte is written to the output stream. The byte to be
+ * written is the eight low-order bits of the argument b. The 24 high-order
+ * bits of b are ignored.
+ * @param b the byte to write
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ public synchronized void write(int b) throws IOException {
+ ensureOpen();
+ mParent.ensureNotDone();
+ mArray.write(b);
+ if (mArray.size() == mMaxPacketSize) {
+ mParent.continueOperation(true, false);
+ }
+ }
+
+ @Override
+ public void write(byte[] buffer) throws IOException {
+ write(buffer, 0, buffer.length);
+ }
+
+ @Override
+ public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
+ int offset1 = offset;
+ int remainLength = count;
+
+ if (buffer == null) {
+ throw new IOException("buffer is null");
+ }
+ if ((offset | count) < 0 || count > buffer.length - offset) {
+ throw new IndexOutOfBoundsException("index outof bound");
+ }
+
+ ensureOpen();
+ mParent.ensureNotDone();
+ if (count < mMaxPacketSize) {
+ mArray.write(buffer, offset, count);
+ } else {
+ while (remainLength >= mMaxPacketSize) {
+ mArray.write(buffer, offset1, mMaxPacketSize);
+ offset1 += mMaxPacketSize;
+ remainLength = count - offset1;
+ mParent.continueOperation(true, false);
+ }
+ if (remainLength > 0) {
+ mArray.write(buffer, offset1, remainLength);
+ }
+ }
+ }
+
+ /**
+ * Reads the bytes that have been written to this stream.
+ * @param size the size of the array to return
+ * @return the byte array that is written
+ */
+ public synchronized byte[] readBytes(int size) {
+ if (mArray.size() > 0) {
+ byte[] temp = mArray.toByteArray();
+ mArray.reset();
+ byte[] result = new byte[size];
+ System.arraycopy(temp, 0, result, 0, size);
+ if (temp.length != size) {
+ mArray.write(temp, size, temp.length - size);
+ }
+ return result;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Verifies that this stream is open
+ * @throws IOException if the stream is not open
+ */
+ private void ensureOpen() throws IOException {
+ mParent.ensureOpen();
+ if (!mOpen) {
+ throw new IOException("Output stream is closed");
+ }
+ }
+
+ /**
+ * Closes the output stream. If the input stream is already closed, do
+ * nothing.
+ * @throws IOException this will never happen
+ */
+ @Override
+ public void close() throws IOException {
+ mOpen = false;
+ mParent.streamClosed(false);
+ }
+
+ /**
+ * Determines if the connection is closed
+ * @return <code>true</code> if the connection is closed; <code>false</code>
+ * if the connection is open
+ */
+ public boolean isClosed() {
+ return !mOpen;
+ }
+}
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
new file mode 100644
index 0000000..a2b9a37
--- /dev/null
+++ b/obex/javax/obex/ResponseCodes.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ResponseCodes</code> class contains the list of valid response
+ * codes a server may send to a client.
+ * <P>
+ * <STRONG>IMPORTANT NOTE</STRONG>
+ * <P>
+ * The values in this interface represent the values defined in the IrOBEX
+ * specification, which is different with the HTTP specification.
+ * <P>
+ * <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
+ * further description since they are not defined in HTTP. The server will send
+ * an <code>OBEX_DATABASE_FULL</code> message when the client requests that
+ * something be placed into a database but the database is full (cannot take
+ * more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
+ * client wishes to access a database, database table, or database record that
+ * has been locked.
+ * @hide
+ */
+public final class ResponseCodes {
+
+ /**
+ * Defines the OBEX CONTINUE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
+ */
+ public static final int OBEX_HTTP_CONTINUE = 0x90;
+
+ /**
+ * Defines the OBEX SUCCESS response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
+ */
+ public static final int OBEX_HTTP_OK = 0xA0;
+
+ /**
+ * Defines the OBEX CREATED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
+ */
+ public static final int OBEX_HTTP_CREATED = 0xA1;
+
+ /**
+ * Defines the OBEX ACCEPTED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
+ */
+ public static final int OBEX_HTTP_ACCEPTED = 0xA2;
+
+ /**
+ * Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
+ */
+ public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
+
+ /**
+ * Defines the OBEX NO CONTENT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
+ */
+ public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
+
+ /**
+ * Defines the OBEX RESET CONTENT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
+ */
+ public static final int OBEX_HTTP_RESET = 0xA5;
+
+ /**
+ * Defines the OBEX PARTIAL CONTENT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
+ */
+ public static final int OBEX_HTTP_PARTIAL = 0xA6;
+
+ /**
+ * Defines the OBEX MULTIPLE_CHOICES response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
+ */
+ public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
+
+ /**
+ * Defines the OBEX MOVED PERMANENTLY response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
+ */
+ public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
+
+ /**
+ * Defines the OBEX MOVED TEMPORARILY response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
+ */
+ public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
+
+ /**
+ * Defines the OBEX SEE OTHER response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
+ */
+ public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
+
+ /**
+ * Defines the OBEX NOT MODIFIED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
+ */
+ public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
+
+ /**
+ * Defines the OBEX USE PROXY response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
+ */
+ public static final int OBEX_HTTP_USE_PROXY = 0xB5;
+
+ /**
+ * Defines the OBEX BAD REQUEST response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
+ */
+ public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
+
+ /**
+ * Defines the OBEX UNAUTHORIZED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
+ */
+ public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
+
+ /**
+ * Defines the OBEX PAYMENT REQUIRED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
+ */
+ public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
+
+ /**
+ * Defines the OBEX FORBIDDEN response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
+ */
+ public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
+
+ /**
+ * Defines the OBEX NOT FOUND response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
+ */
+ public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
+
+ /**
+ * Defines the OBEX METHOD NOT ALLOWED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
+ */
+ public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
+
+ /**
+ * Defines the OBEX NOT ACCEPTABLE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
+ */
+ public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
+
+ /**
+ * Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
+ */
+ public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
+
+ /**
+ * Defines the OBEX REQUEST TIME OUT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
+ */
+ public static final int OBEX_HTTP_TIMEOUT = 0xC8;
+
+ /**
+ * Defines the OBEX METHOD CONFLICT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
+ */
+ public static final int OBEX_HTTP_CONFLICT = 0xC9;
+
+ /**
+ * Defines the OBEX METHOD GONE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
+ */
+ public static final int OBEX_HTTP_GONE = 0xCA;
+
+ /**
+ * Defines the OBEX METHOD LENGTH REQUIRED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
+ */
+ public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
+
+ /**
+ * Defines the OBEX PRECONDITION FAILED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
+ */
+ public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
+
+ /**
+ * Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
+ */
+ public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
+
+ /**
+ * Defines the OBEX REQUESTED URL TOO LARGE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
+ */
+ public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
+
+ /**
+ * Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
+ */
+ public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
+
+ /**
+ * Defines the OBEX INTERNAL SERVER ERROR response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
+ */
+ public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
+
+ /**
+ * Defines the OBEX NOT IMPLEMENTED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
+ */
+ public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
+
+ /**
+ * Defines the OBEX BAD GATEWAY response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
+ */
+ public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
+
+ /**
+ * Defines the OBEX SERVICE UNAVAILABLE response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
+ */
+ public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
+
+ /**
+ * Defines the OBEX GATEWAY TIMEOUT response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
+ */
+ public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
+
+ /**
+ * Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
+ * <P>
+ * The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
+ */
+ public static final int OBEX_HTTP_VERSION = 0xD5;
+
+ /**
+ * Defines the OBEX DATABASE FULL response code.
+ * <P>
+ * The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
+ */
+ public static final int OBEX_DATABASE_FULL = 0xE0;
+
+ /**
+ * Defines the OBEX DATABASE LOCKED response code.
+ * <P>
+ * The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
+ */
+ public static final int OBEX_DATABASE_LOCKED = 0xE1;
+
+ /**
+ * Constructor does nothing.
+ */
+ private ResponseCodes() {
+ }
+}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
new file mode 100644
index 0000000..8710c64
--- /dev/null
+++ b/obex/javax/obex/ServerOperation.java
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.DataInputStream;
+import java.io.OutputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * This class implements the Operation interface for server side connections.
+ * <P>
+ * <STRONG>Request Codes</STRONG> There are four different request codes that
+ * are in this class. 0x02 is a PUT request that signals that the request is not
+ * complete and requires an additional OBEX packet. 0x82 is a PUT request that
+ * says that request is complete. In this case, the server can begin sending the
+ * response. The 0x03 is a GET request that signals that the request is not
+ * finished. When the server receives a 0x83, the client is signaling the server
+ * that it is done with its request. TODO: Extend the ClientOperation and reuse
+ * the methods defined TODO: in that class.
+ * @hide
+ */
+public final class ServerOperation implements Operation, BaseStream {
+
+ public boolean isAborted;
+
+ public HeaderSet requestHeader;
+
+ public HeaderSet replyHeader;
+
+ public boolean finalBitSet;
+
+ private InputStream mInput;
+
+ private ServerSession mParent;
+
+ private int mMaxPacketLength;
+
+ private int mResponseSize;
+
+ private boolean mClosed;
+
+ private boolean mGetOperation;
+
+ private PrivateInputStream mPrivateInput;
+
+ private PrivateOutputStream mPrivateOutput;
+
+ private boolean mPrivateOutputOpen;
+
+ private String mExceptionString;
+
+ private ServerRequestHandler mListener;
+
+ private boolean mRequestFinished;
+
+ private boolean mHasBody;
+
+ /**
+ * Creates new ServerOperation
+ * @param p the parent that created this object
+ * @param in the input stream to read from
+ * @param out the output stream to write to
+ * @param request the initial request that was received from the client
+ * @param maxSize the max packet size that the client will accept
+ * @param listen the listener that is responding to the request
+ * @throws IOException if an IO error occurs
+ */
+ public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
+ ServerRequestHandler listen) throws IOException {
+
+ isAborted = false;
+ mParent = p;
+ mInput = in;
+ mMaxPacketLength = maxSize;
+ mClosed = false;
+ requestHeader = new HeaderSet();
+ replyHeader = new HeaderSet();
+ mPrivateInput = new PrivateInputStream(this);
+ mResponseSize = 3;
+ mListener = listen;
+ mRequestFinished = false;
+ mPrivateOutputOpen = false;
+ mHasBody = false;
+ int bytesReceived;
+
+ /*
+ * Determine if this is a PUT request
+ */
+ if ((request == 0x02) || (request == 0x82)) {
+ /*
+ * It is a PUT request.
+ */
+ mGetOperation = false;
+ } else {
+ /*
+ * It is a GET request.
+ */
+ mGetOperation = true;
+ }
+
+ /*
+ * Determine if the final bit is set
+ */
+ if ((request & 0x80) == 0) {
+ finalBitSet = false;
+ } else {
+ finalBitSet = true;
+ mRequestFinished = true;
+ }
+
+ int length = in.read();
+ length = (length << 8) + in.read();
+
+ /*
+ * Determine if the packet length is larger than this device can receive
+ */
+ if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+ throw new IOException("Packet received was too large");
+ }
+
+ /*
+ * Determine if any headers were sent in the initial request
+ */
+ if (length > 3) {
+ byte[] data = new byte[length - 3];
+ bytesReceived = in.read(data);
+
+ while (bytesReceived != data.length) {
+ bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
+ }
+
+ byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
+
+ if (body != null) {
+ mHasBody = true;
+ }
+
+ if (requestHeader.mConnectionID != null) {
+ mListener.setConnectionId(ObexHelper.convertToLong(requestHeader.mConnectionID));
+ } else {
+ mListener.setConnectionId(0);
+ }
+
+ if (requestHeader.mAuthResp != null) {
+ if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
+ mExceptionString = "Authentication Failed";
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+ mClosed = true;
+ requestHeader.mAuthResp = null;
+ return;
+ }
+ }
+
+ if (requestHeader.mAuthChall != null) {
+ mParent.handleAuthChall(requestHeader);
+ // send the authResp to the client
+ replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
+ System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
+ replyHeader.mAuthResp.length);
+ requestHeader.mAuthResp = null;
+ requestHeader.mAuthChall = null;
+
+ }
+
+ if (body != null) {
+ mPrivateInput.writeBytes(body, 1);
+ } else {
+ while ((!mGetOperation) && (!finalBitSet)) {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ if (mPrivateInput.available() > 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ while ((!mGetOperation) && (!finalBitSet) && (mPrivateInput.available() == 0)) {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ if (mPrivateInput.available() > 0) {
+ break;
+ }
+ }
+
+ // wait for get request finished !!!!
+ while (mGetOperation && !finalBitSet) {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ }
+ if (finalBitSet && mGetOperation) {
+ mRequestFinished = true;
+ }
+ }
+
+ public boolean isValidBody() {
+ return mHasBody;
+ }
+
+ /**
+ * Determines if the operation should continue or should wait. If it should
+ * continue, this method will continue the operation.
+ * @param sendEmpty if <code>true</code> then this will continue the
+ * operation even if no headers will be sent; if <code>false</code>
+ * then this method will only continue the operation if there are
+ * headers to send
+ * @param inStream if<code>true</code> the stream is input stream, otherwise
+ * output stream
+ * @return <code>true</code> if the operation was completed;
+ * <code>false</code> if no operation took place
+ */
+ public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
+ throws IOException {
+ if (!mGetOperation) {
+ if (!finalBitSet) {
+ if (sendEmpty) {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ return true;
+ } else {
+ if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ } else {
+ return false;
+ }
+ } else {
+ sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ return true;
+ }
+ }
+
+ /**
+ * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
+ * will wait for a response from the client before ending.
+ * @param type the response code to send back to the client
+ * @return <code>true</code> if the final bit was not set on the reply;
+ * <code>false</code> if no reply was received because the operation
+ * ended, an abort was received, or the final bit was set in the
+ * reply
+ * @throws IOException if an IO error occurs
+ */
+ public synchronized boolean sendReply(int type) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int bytesReceived;
+
+ long id = mListener.getConnectionId();
+ if (id == -1) {
+ replyHeader.mConnectionID = null;
+ } else {
+ replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
+ }
+
+ byte[] headerArray = ObexHelper.createHeader(replyHeader, true);
+ int bodyLength = -1;
+ int orginalBodyLength = -1;
+
+ if (mPrivateOutput != null) {
+ bodyLength = mPrivateOutput.size();
+ orginalBodyLength = bodyLength;
+ }
+
+ if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
+
+ int end = 0;
+ int start = 0;
+
+ while (end != headerArray.length) {
+ end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
+ - ObexHelper.BASE_PACKET_LENGTH);
+ if (end == -1) {
+
+ mClosed = true;
+
+ if (mPrivateInput != null) {
+ mPrivateInput.close();
+ }
+
+ if (mPrivateOutput != null) {
+ mPrivateOutput.close();
+ }
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ throw new IOException("OBEX Packet exceeds max packet size");
+ }
+ byte[] sendHeader = new byte[end - start];
+ System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
+
+ mParent.sendResponse(type, sendHeader);
+ start = end;
+ }
+
+ if (bodyLength > 0) {
+ return true;
+ } else {
+ return false;
+ }
+
+ } else {
+ out.write(headerArray);
+ }
+
+ if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
+ if (bodyLength > 0) {
+ /*
+ * Determine if I can send the whole body or just part of
+ * the body. Remember that there is the 3 bytes for the
+ * response message and 3 bytes for the header ID and length
+ */
+ if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
+ bodyLength = mMaxPacketLength - headerArray.length - 6;
+ }
+
+ byte[] body = mPrivateOutput.readBytes(bodyLength);
+
+ /*
+ * Since this is a put request if the final bit is set or
+ * the output stream is closed we need to send the 0x49
+ * (End of Body) otherwise, we need to send 0x48 (Body)
+ */
+ if ((finalBitSet) || (mPrivateOutput.isClosed())) {
+ out.write(0x49);
+ } else {
+ out.write(0x48);
+ }
+
+ bodyLength += 3;
+ out.write((byte)(bodyLength >> 8));
+ out.write((byte)bodyLength);
+ out.write(body);
+ }
+ }
+
+ if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
+ out.write(0x49);
+ orginalBodyLength = 3;
+ out.write((byte)(orginalBodyLength >> 8));
+ out.write((byte)orginalBodyLength);
+
+ }
+
+ mResponseSize = 3;
+ mParent.sendResponse(type, out.toByteArray());
+
+ if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
+ int headerID = mInput.read();
+ int length = mInput.read();
+ length = (length << 8) + mInput.read();
+ if ((headerID != ObexHelper.OBEX_OPCODE_PUT)
+ && (headerID != ObexHelper.OBEX_OPCODE_PUT_FINAL)
+ && (headerID != ObexHelper.OBEX_OPCODE_GET)
+ && (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
+
+ if (length > 3) {
+ byte[] temp = new byte[length];
+ bytesReceived = mInput.read(temp);
+
+ while (bytesReceived != length) {
+ bytesReceived += mInput.read(temp, bytesReceived, length - bytesReceived);
+ }
+ }
+
+ /*
+ * Determine if an ABORT was sent as the reply
+ */
+ if (headerID == ObexHelper.OBEX_OPCODE_ABORT) {
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
+ mClosed = true;
+ isAborted = true;
+ mExceptionString = "Abort Received";
+ throw new IOException("Abort Received");
+ } else {
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
+ mClosed = true;
+ mExceptionString = "Bad Request Received";
+ throw new IOException("Bad Request Received");
+ }
+ } else {
+
+ if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)
+ || (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
+ finalBitSet = true;
+ }
+
+ /*
+ * Determine if the packet length is larger then this device can receive
+ */
+ if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+ throw new IOException("Packet received was too large");
+ }
+
+ /*
+ * Determine if any headers were sent in the initial request
+ */
+ if (length > 3) {
+ byte[] data = new byte[length - 3];
+ bytesReceived = mInput.read(data);
+
+ while (bytesReceived != data.length) {
+ bytesReceived += mInput.read(data, bytesReceived, data.length
+ - bytesReceived);
+ }
+ byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
+ if (body != null) {
+ mHasBody = true;
+ }
+ if (requestHeader.mConnectionID != null) {
+ mListener.setConnectionId(ObexHelper
+ .convertToLong(requestHeader.mConnectionID));
+ } else {
+ mListener.setConnectionId(1);
+ }
+
+ if (requestHeader.mAuthResp != null) {
+ if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
+ mExceptionString = "Authentication Failed";
+ mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+ mClosed = true;
+ requestHeader.mAuthResp = null;
+ return false;
+ }
+ requestHeader.mAuthResp = null;
+ }
+
+ if (requestHeader.mAuthChall != null) {
+ mParent.handleAuthChall(requestHeader);
+ // send the auhtResp to the client
+ replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
+ System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
+ replyHeader.mAuthResp.length);
+ requestHeader.mAuthResp = null;
+ requestHeader.mAuthChall = null;
+ }
+
+ if (body != null) {
+ mPrivateInput.writeBytes(body, 1);
+ }
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Sends an ABORT message to the server. By calling this method, the
+ * corresponding input and output streams will be closed along with this
+ * object.
+ * @throws IOException if the transaction has already ended or if an OBEX
+ * server called this method
+ */
+ public void abort() throws IOException {
+ throw new IOException("Called from a server");
+ }
+
+ /**
+ * Returns the headers that have been received during the operation.
+ * Modifying the object returned has no effect on the headers that are sent
+ * or retrieved.
+ * @return the headers received during this <code>Operation</code>
+ * @throws IOException if this <code>Operation</code> has been closed
+ */
+ public HeaderSet getReceivedHeader() throws IOException {
+ ensureOpen();
+ return requestHeader;
+ }
+
+ /**
+ * Specifies the headers that should be sent in the next OBEX message that
+ * is sent.
+ * @param headers the headers to send in the next message
+ * @throws IOException if this <code>Operation</code> has been closed or the
+ * transaction has ended and no further messages will be exchanged
+ * @throws IllegalArgumentException if <code>headers</code> was not created
+ * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
+ */
+ public void sendHeaders(HeaderSet headers) throws IOException {
+ ensureOpen();
+
+ if (headers == null) {
+ throw new IOException("Headers may not be null");
+ }
+
+ int[] headerList = headers.getHeaderList();
+ if (headerList != null) {
+ for (int i = 0; i < headerList.length; i++) {
+ replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
+ }
+
+ }
+ }
+
+ /**
+ * Retrieves the response code retrieved from the server. Response codes are
+ * defined in the <code>ResponseCodes</code> interface.
+ * @return the response code retrieved from the server
+ * @throws IOException if an error occurred in the transport layer during
+ * the transaction; if this method is called on a
+ * <code>HeaderSet</code> object created by calling
+ * <code>createHeaderSet</code> in a <code>ClientSession</code>
+ * object; if this is called from a server
+ */
+ public int getResponseCode() throws IOException {
+ throw new IOException("Called from a server");
+ }
+
+ /**
+ * Always returns <code>null</code>
+ * @return <code>null</code>
+ */
+ public String getEncoding() {
+ return null;
+ }
+
+ /**
+ * Returns the type of content that the resource connected to is providing.
+ * E.g. if the connection is via HTTP, then the value of the content-type
+ * header field is returned.
+ * @return the content type of the resource that the URL references, or
+ * <code>null</code> if not known
+ */
+ public String getType() {
+ try {
+ return (String)requestHeader.getHeader(HeaderSet.TYPE);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the length of the content which is being provided. E.g. if the
+ * connection is via HTTP, then the value of the content-length header field
+ * is returned.
+ * @return the content length of the resource that this connection's URL
+ * references, or -1 if the content length is not known
+ */
+ public long getLength() {
+ try {
+ Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
+
+ if (temp == null) {
+ return -1;
+ } else {
+ return temp.longValue();
+ }
+ } catch (IOException e) {
+ return -1;
+ }
+ }
+
+ public int getMaxPacketSize() {
+ return mMaxPacketLength - 6;
+ }
+
+ /**
+ * Open and return an input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ */
+ public InputStream openInputStream() throws IOException {
+ ensureOpen();
+ return mPrivateInput;
+ }
+
+ /**
+ * Open and return a data input stream for a connection.
+ * @return an input stream
+ * @throws IOException if an I/O error occurs
+ */
+ public DataInputStream openDataInputStream() throws IOException {
+ return new DataInputStream(openInputStream());
+ }
+
+ /**
+ * Open and return an output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public OutputStream openOutputStream() throws IOException {
+ ensureOpen();
+
+ if (mPrivateOutputOpen) {
+ throw new IOException("no more input streams available, stream already opened");
+ }
+
+ if (!mRequestFinished) {
+ throw new IOException("no output streams available ,request not finished");
+ }
+
+ if (mPrivateOutput == null) {
+ mPrivateOutput = new PrivateOutputStream(this, mMaxPacketLength - 6);
+ }
+ mPrivateOutputOpen = true;
+ return mPrivateOutput;
+ }
+
+ /**
+ * Open and return a data output stream for a connection.
+ * @return an output stream
+ * @throws IOException if an I/O error occurs
+ */
+ public DataOutputStream openDataOutputStream() throws IOException {
+ return new DataOutputStream(openOutputStream());
+ }
+
+ /**
+ * Closes the connection and ends the transaction
+ * @throws IOException if the operation has already ended or is closed
+ */
+ public void close() throws IOException {
+ ensureOpen();
+ mClosed = true;
+ }
+
+ /**
+ * Verifies that the connection is open and no exceptions should be thrown.
+ * @throws IOException if an exception needs to be thrown
+ */
+ public void ensureOpen() throws IOException {
+ if (mExceptionString != null) {
+ throw new IOException(mExceptionString);
+ }
+ if (mClosed) {
+ throw new IOException("Operation has already ended");
+ }
+ }
+
+ /**
+ * Verifies that additional information may be sent. In other words, the
+ * operation is not done.
+ * <P>
+ * Included to implement the BaseStream interface only. It does not do
+ * anything on the server side since the operation of the Operation object
+ * is not done until after the handler returns from its method.
+ * @throws IOException if the operation is completed
+ */
+ public void ensureNotDone() throws IOException {
+ }
+
+ /**
+ * Called when the output or input stream is closed. It does not do anything
+ * on the server side since the operation of the Operation object is not
+ * done until after the handler returns from its method.
+ * @param inStream <code>true</code> if the input stream is closed;
+ * <code>false</code> if the output stream is closed
+ * @throws IOException if an IO error occurs
+ */
+ public void streamClosed(boolean inStream) throws IOException {
+
+ }
+}
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
new file mode 100644
index 0000000..d93e5b6
--- /dev/null
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+/**
+ * The <code>ServerRequestHandler</code> class defines an event listener that
+ * will respond to OBEX requests made to the server.
+ * <P>
+ * The <code>onConnect()</code>, <code>onSetPath()</code>,
+ * <code>onDelete()</code>, <code>onGet()</code>, and <code>onPut()</code>
+ * methods may return any response code defined in the
+ * <code>ResponseCodes</code> class except for <code>OBEX_HTTP_CONTINUE</code>.
+ * If <code>OBEX_HTTP_CONTINUE</code> or a value not defined in the
+ * <code>ResponseCodes</code> class is returned, the server implementation will
+ * send an <code>OBEX_HTTP_INTERNAL_ERROR</code> response to the client.
+ * <P>
+ * <STRONG>Connection ID and Target Headers</STRONG>
+ * <P>
+ * According to the IrOBEX specification, a packet may not contain a Connection
+ * ID and Target header. Since the Connection ID header is managed by the
+ * implementation, it will not send a Connection ID header, if a Connection ID
+ * was specified, in a packet that has a Target header. In other words, if an
+ * application adds a Target header to a <code>HeaderSet</code> object used in
+ * an OBEX operation and a Connection ID was specified, no Connection ID will be
+ * sent in the packet containing the Target header.
+ * <P>
+ * <STRONG>CREATE-EMPTY Requests</STRONG>
+ * <P>
+ * A CREATE-EMPTY request allows clients to create empty objects on the server.
+ * When a CREATE-EMPTY request is received, the <code>onPut()</code> method will
+ * be called by the implementation. To differentiate between a normal PUT
+ * request and a CREATE-EMPTY request, an application must open the
+ * <code>InputStream</code> from the <code>Operation</code> object passed to the
+ * <code>onPut()</code> method. For a PUT request, the application will be able
+ * to read Body data from this <code>InputStream</code>. For a CREATE-EMPTY
+ * request, there will be no Body data to read. Therefore, a call to
+ * <code>InputStream.read()</code> will return -1.
+ * @hide
+ */
+public class ServerRequestHandler {
+
+ private long mConnectionId;
+
+ /**
+ * Creates a <code>ServerRequestHandler</code>.
+ */
+ protected ServerRequestHandler() {
+ /*
+ * A connection ID of -1 implies there is no conenction ID
+ */
+ mConnectionId = -1;
+ }
+
+ /**
+ * Sets the connection ID header to include in the reply packets.
+ * @param connectionId the connection ID to use; -1 if no connection ID
+ * should be sent
+ * @throws IllegalArgumentException if <code>id</code> is not in the range
+ * -1 to 2<sup>32</sup>-1
+ */
+ public void setConnectionId(final long connectionId) {
+ if ((connectionId < -1) || (connectionId > 0xFFFFFFFFL)) {
+ throw new IllegalArgumentException("Illegal Connection ID");
+ }
+ mConnectionId = connectionId;
+ }
+
+ /**
+ * Retrieves the connection ID that is being used in the present connection.
+ * This method will return -1 if no connection ID is being used.
+ * @return the connection id being used or -1 if no connection ID is being
+ * used
+ */
+ public long getConnectionId() {
+ return mConnectionId;
+ }
+
+ /**
+ * Called when a CONNECT request is received.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * <code>onConnect()</code> will always return an <code>OBEX_HTTP_OK</code>
+ * response code.
+ * <P>
+ * The headers received in the request can be retrieved from the
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
+ * @param request contains the headers sent by the client;
+ * <code>request</code> will never be <code>null</code>
+ * @param reply the headers that should be sent in the reply;
+ * <code>reply</code> will never be <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
+ */
+ public int onConnect(HeaderSet request, HeaderSet reply) {
+ return ResponseCodes.OBEX_HTTP_OK;
+ }
+
+ /**
+ * Called when a DISCONNECT request is received.
+ * <P>
+ * The headers received in the request can be retrieved from the
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
+ * @param request contains the headers sent by the client;
+ * <code>request</code> will never be <code>null</code>
+ * @param reply the headers that should be sent in the reply;
+ * <code>reply</code> will never be <code>null</code>
+ */
+ public void onDisconnect(HeaderSet request, HeaderSet reply) {
+ }
+
+ /**
+ * Called when a SETPATH request is received.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * <code>onSetPath()</code> will always return an
+ * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+ * <P>
+ * The headers received in the request can be retrieved from the
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
+ * @param request contains the headers sent by the client;
+ * <code>request</code> will never be <code>null</code>
+ * @param reply the headers that should be sent in the reply;
+ * <code>reply</code> will never be <code>null</code>
+ * @param backup <code>true</code> if the client requests that the server
+ * back up one directory before changing to the path described by
+ * <code>name</code>; <code>false</code> to apply the request to the
+ * present path
+ * @param create <code>true</code> if the path should be created if it does
+ * not already exist; <code>false</code> if the path should not be
+ * created if it does not exist and an error code should be returned
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
+ */
+ public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
+
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * Called when a DELETE request is received.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * <code>onDelete()</code> will always return an
+ * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+ * <P>
+ * The headers received in the request can be retrieved from the
+ * <code>request</code> argument. The headers that should be sent in the
+ * reply must be specified in the <code>reply</code> argument.
+ * @param request contains the headers sent by the client;
+ * <code>request</code> will never be <code>null</code>
+ * @param reply the headers that should be sent in the reply;
+ * <code>reply</code> will never be <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
+ */
+ public int onDelete(HeaderSet request, HeaderSet reply) {
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * Called when a PUT request is received.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * <code>onPut()</code> will always return an
+ * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+ * <P>
+ * If an ABORT request is received during the processing of a PUT request,
+ * <code>op</code> will be closed by the implementation.
+ * @param operation contains the headers sent by the client and allows new
+ * headers to be sent in the reply; <code>op</code> will never be
+ * <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
+ */
+ public int onPut(Operation operation) {
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * Called when a GET request is received.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * <code>onGet()</code> will always return an
+ * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
+ * <P>
+ * If an ABORT request is received during the processing of a GET request,
+ * <code>op</code> will be closed by the implementation.
+ * @param operation contains the headers sent by the client and allows new
+ * headers to be sent in the reply; <code>op</code> will never be
+ * <code>null</code>
+ * @return a response code defined in <code>ResponseCodes</code> that will
+ * be returned to the client; if an invalid response code is
+ * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
+ * will be used
+ */
+ public int onGet(Operation operation) {
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+
+ /**
+ * Called when this object attempts to authenticate a client and the
+ * authentication request fails because the response digest in the
+ * authentication response header was wrong.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * this method will do nothing.
+ * @param userName the user name returned in the authentication response;
+ * <code>null</code> if no user name was provided in the response
+ */
+ public void onAuthenticationFailure(byte[] userName) {
+ }
+
+ /**
+ * Called by ServerSession to update the status of current transaction
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * this method will do nothing.
+ */
+ public void updateStatus(String message) {
+ }
+
+ /**
+ * Called when session is closed.
+ * <P>
+ * If this method is not implemented by the class that extends this class,
+ * this method will do nothing.
+ */
+ public void onClose() {
+ }
+}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
new file mode 100644
index 0000000..675272d
--- /dev/null
+++ b/obex/javax/obex/ServerSession.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * This class in an implementation of the OBEX ServerSession.
+ * @hide
+ */
+public final class ServerSession extends ObexSession implements Runnable {
+
+ private static final String TAG = "Obex ServerSession";
+
+ private ObexTransport mTransport;
+
+ private InputStream mInput;
+
+ private OutputStream mOutput;
+
+ private ServerRequestHandler mListener;
+
+ private Thread mProcessThread;
+
+ private int mMaxPacketLength;
+
+ private boolean mClosed;
+
+ /**
+ * Creates new ServerSession.
+ * @param trans the connection to the client
+ * @param handler the event listener that will process requests
+ * @param auth the authenticator to use with this connection
+ * @throws IOException if an error occurred while opening the input and
+ * output streams
+ */
+ public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
+ throws IOException {
+ mAuthenticator = auth;
+ mTransport = trans;
+ mInput = mTransport.openInputStream();
+ mOutput = mTransport.openOutputStream();
+ mListener = handler;
+ mMaxPacketLength = 256;
+
+ mClosed = false;
+ mProcessThread = new Thread(this);
+ mProcessThread.start();
+ }
+
+ /**
+ * Processes requests made to the server and forwards them to the
+ * appropriate event listener.
+ */
+ public void run() {
+ try {
+
+ boolean done = false;
+ while (!done && !mClosed) {
+ int requestType = mInput.read();
+ switch (requestType) {
+ case ObexHelper.OBEX_OPCODE_CONNECT:
+ handleConnectRequest();
+ break;
+
+ case ObexHelper.OBEX_OPCODE_DISCONNECT:
+ handleDisconnectRequest();
+ done = true;
+ break;
+
+ case ObexHelper.OBEX_OPCODE_GET:
+ case ObexHelper.OBEX_OPCODE_GET_FINAL:
+ handleGetRequest(requestType);
+ break;
+
+ case ObexHelper.OBEX_OPCODE_PUT:
+ case ObexHelper.OBEX_OPCODE_PUT_FINAL:
+ handlePutRequest(requestType);
+ break;
+
+ case ObexHelper.OBEX_OPCODE_SETPATH:
+ handleSetPathRequest();
+ break;
+
+ case -1:
+ done = true;
+ break;
+
+ default:
+
+ /*
+ * Received a request type that is not recognized so I am
+ * just going to read the packet and send a not implemented
+ * to the client
+ */
+ int length = mInput.read();
+ length = (length << 8) + mInput.read();
+ for (int i = 3; i < length; i++) {
+ mInput.read();
+ }
+ sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
+ }
+ }
+
+ } catch (NullPointerException e) {
+ Log.d(TAG, e.toString());
+ } catch (Exception e) {
+ Log.d(TAG, e.toString());
+ }
+ close();
+ }
+
+ /**
+ * Handles a PUT request from a client. This method will provide a
+ * <code>ServerOperation</code> object to the request handler. The
+ * <code>ServerOperation</code> object will handle the rest of the request.
+ * It will also send replies and receive requests until the final reply
+ * should be sent. When the final reply should be sent, this method will get
+ * the response code to use and send the reply. The
+ * <code>ServerOperation</code> object will always reply with a
+ * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+ * needed.
+ * @param type the type of request received; either 0x02 or 0x82
+ * @throws IOException if an error occurred at the transport layer
+ */
+ private void handlePutRequest(int type) throws IOException {
+ ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
+ try {
+ int response = -1;
+
+ if ((op.finalBitSet) && !op.isValidBody()) {
+ response = validateResponseCode(mListener
+ .onDelete(op.requestHeader, op.replyHeader));
+ } else {
+ response = validateResponseCode(mListener.onPut(op));
+ }
+ if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
+ op.sendReply(response);
+ } else if (!op.isAborted) {
+ // wait for the final bit
+ while (!op.finalBitSet) {
+ op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
+ }
+ op.sendReply(response);
+ }
+ } catch (Exception e) {
+ /*To fix bugs in aborted cases,
+ *(client abort file transfer prior to the last packet which has the end of body header,
+ *internal error should not be sent because server has already replied with
+ *OK response in "sendReply")
+ */
+ if (!op.isAborted) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ }
+ }
+ }
+
+ /**
+ * Handles a GET request from a client. This method will provide a
+ * <code>ServerOperation</code> object to the request handler. The
+ * <code>ServerOperation</code> object will handle the rest of the request.
+ * It will also send replies and receive requests until the final reply
+ * should be sent. When the final reply should be sent, this method will get
+ * the response code to use and send the reply. The
+ * <code>ServerOperation</code> object will always reply with a
+ * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
+ * needed.
+ * @param type the type of request received; either 0x03 or 0x83
+ * @throws IOException if an error occurred at the transport layer
+ */
+ private void handleGetRequest(int type) throws IOException {
+ ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
+ try {
+ int response = validateResponseCode(mListener.onGet(op));
+
+ if (!op.isAborted) {
+ op.sendReply(response);
+ }
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ }
+ }
+
+ /**
+ * Send standard response.
+ * @param code the response code to send
+ * @param header the headers to include in the response
+ * @throws IOException if an IO error occurs
+ */
+ public void sendResponse(int code, byte[] header) throws IOException {
+ int totalLength = 3;
+ byte[] data = null;
+
+ if (header != null) {
+ totalLength += header.length;
+ data = new byte[totalLength];
+ data[0] = (byte)code;
+ data[1] = (byte)(totalLength >> 8);
+ data[2] = (byte)totalLength;
+ System.arraycopy(header, 0, data, 3, header.length);
+ } else {
+ data = new byte[totalLength];
+ data[0] = (byte)code;
+ data[1] = (byte)0x00;
+ data[2] = (byte)totalLength;
+ }
+ mOutput.write(data);
+ mOutput.flush();
+ }
+
+ /**
+ * Handles a SETPATH request from a client. This method will read the rest
+ * of the request from the client. Assuming the request is valid, it will
+ * create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server
+ * with the response code provided.
+ * @throws IOException if an error occurred at the transport layer
+ */
+ private void handleSetPathRequest() throws IOException {
+ int length;
+ int flags;
+ @SuppressWarnings("unused")
+ int constants;
+ int totalLength = 3;
+ byte[] head = null;
+ int code = -1;
+ int bytesReceived;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+
+ length = mInput.read();
+ length = (length << 8) + mInput.read();
+ flags = mInput.read();
+ constants = mInput.read();
+
+ if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 3;
+ } else {
+ if (length > 5) {
+ byte[] headers = new byte[length - 5];
+ bytesReceived = mInput.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += mInput.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ ObexHelper.updateHeaderSet(request, headers);
+
+ if (request.mConnectionID != null) {
+ mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
+ } else {
+ mListener.setConnectionId(-1);
+ }
+ // the Auth chan is initiated by the server, client sent back the authResp .
+ if (request.mAuthResp != null) {
+ if (!handleAuthResp(request.mAuthResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+ request.mAuthResp));
+ }
+ request.mAuthResp = null;
+ }
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+ // the Auth challenge is initiated by the client
+ // the server will send back the authResp to the client
+ if (request.mAuthChall != null) {
+ handleAuthChall(request);
+ reply.mAuthResp = new byte[request.mAuthResp.length];
+ System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
+ reply.mAuthResp.length);
+ request.mAuthChall = null;
+ request.mAuthResp = null;
+ }
+ boolean backup = false;
+ boolean create = true;
+ if (!((flags & 1) == 0)) {
+ backup = true;
+ }
+ if ((flags & 2) == 0) {
+ create = false;
+ }
+
+ try {
+ code = mListener.onSetPath(request, reply, backup, create);
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ return;
+ }
+
+ code = validateResponseCode(code);
+
+ if (reply.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
+ } else {
+ mChallengeDigest = null;
+ }
+
+ long id = mListener.getConnectionId();
+ if (id == -1) {
+ reply.mConnectionID = null;
+ } else {
+ reply.mConnectionID = ObexHelper.convertToByteArray(id);
+ }
+
+ head = ObexHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > mMaxPacketLength) {
+ totalLength = 3;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ // Compute Length of OBEX SETPATH packet
+ byte[] replyData = new byte[totalLength];
+ replyData[0] = (byte)code;
+ replyData[1] = (byte)(totalLength >> 8);
+ replyData[2] = (byte)totalLength;
+ if (head != null) {
+ System.arraycopy(head, 0, replyData, 3, head.length);
+ }
+ /*
+ * Write the OBEX SETPATH packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+ */
+ mOutput.write(replyData);
+ mOutput.flush();
+ }
+
+ /**
+ * Handles a disconnect request from a client. This method will read the
+ * rest of the request from the client. Assuming the request is valid, it
+ * will create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server.
+ * @throws IOException if an error occurred at the transport layer
+ */
+ private void handleDisconnectRequest() throws IOException {
+ int length;
+ int code = ResponseCodes.OBEX_HTTP_OK;
+ int totalLength = 3;
+ byte[] head = null;
+ int bytesReceived;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+
+ length = mInput.read();
+ length = (length << 8) + mInput.read();
+
+ if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 3;
+ } else {
+ if (length > 3) {
+ byte[] headers = new byte[length - 3];
+ bytesReceived = mInput.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += mInput.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ ObexHelper.updateHeaderSet(request, headers);
+ }
+
+ if (request.mConnectionID != null) {
+ mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
+ } else {
+ mListener.setConnectionId(1);
+ }
+
+ if (request.mAuthResp != null) {
+ if (!handleAuthResp(request.mAuthResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+ request.mAuthResp));
+ }
+ request.mAuthResp = null;
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+
+ if (request.mAuthChall != null) {
+ handleAuthChall(request);
+ request.mAuthChall = null;
+ }
+
+ try {
+ mListener.onDisconnect(request, reply);
+ } catch (Exception e) {
+ sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
+ return;
+ }
+
+ long id = mListener.getConnectionId();
+ if (id == -1) {
+ reply.mConnectionID = null;
+ } else {
+ reply.mConnectionID = ObexHelper.convertToByteArray(id);
+ }
+
+ head = ObexHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > mMaxPacketLength) {
+ totalLength = 3;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ }
+ }
+
+ // Compute Length of OBEX CONNECT packet
+ byte[] replyData;
+ if (head != null) {
+ replyData = new byte[3 + head.length];
+ } else {
+ replyData = new byte[3];
+ }
+ replyData[0] = (byte)code;
+ replyData[1] = (byte)(totalLength >> 8);
+ replyData[2] = (byte)totalLength;
+ if (head != null) {
+ System.arraycopy(head, 0, replyData, 3, head.length);
+ }
+ /*
+ * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3 to n: headers
+ */
+ mOutput.write(replyData);
+ mOutput.flush();
+ }
+
+ /**
+ * Handles a connect request from a client. This method will read the rest
+ * of the request from the client. Assuming the request is valid, it will
+ * create a <code>HeaderSet</code> object to pass to the
+ * <code>ServerRequestHandler</code> object. After the handler processes the
+ * request, this method will create a reply message to send to the server
+ * with the response code provided.
+ * @throws IOException if an error occurred at the transport layer
+ */
+ private void handleConnectRequest() throws IOException {
+ int packetLength;
+ @SuppressWarnings("unused")
+ int version;
+ @SuppressWarnings("unused")
+ int flags;
+ int totalLength = 7;
+ byte[] head = null;
+ int code = -1;
+ HeaderSet request = new HeaderSet();
+ HeaderSet reply = new HeaderSet();
+ int bytesReceived;
+
+ /*
+ * Read in the length of the OBEX packet, OBEX version, flags, and max
+ * packet length
+ */
+ packetLength = mInput.read();
+ packetLength = (packetLength << 8) + mInput.read();
+ version = mInput.read();
+ flags = mInput.read();
+ mMaxPacketLength = mInput.read();
+ mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
+
+ // should we check it?
+ if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+ mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
+ }
+
+ if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+ code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
+ totalLength = 7;
+ } else {
+ if (packetLength > 7) {
+ byte[] headers = new byte[packetLength - 7];
+ bytesReceived = mInput.read(headers);
+
+ while (bytesReceived != headers.length) {
+ bytesReceived += mInput.read(headers, bytesReceived, headers.length
+ - bytesReceived);
+ }
+
+ ObexHelper.updateHeaderSet(request, headers);
+ }
+
+ if (request.mConnectionID != null) {
+ mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
+ } else {
+ mListener.setConnectionId(1);
+ }
+
+ if (request.mAuthResp != null) {
+ if (!handleAuthResp(request.mAuthResp)) {
+ code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
+ mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
+ request.mAuthResp));
+ }
+ request.mAuthResp = null;
+ }
+
+ if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
+ if (request.mAuthChall != null) {
+ handleAuthChall(request);
+ reply.mAuthResp = new byte[request.mAuthResp.length];
+ System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
+ reply.mAuthResp.length);
+ request.mAuthChall = null;
+ request.mAuthResp = null;
+ }
+
+ try {
+ code = mListener.onConnect(request, reply);
+ code = validateResponseCode(code);
+
+ if (reply.nonce != null) {
+ mChallengeDigest = new byte[16];
+ System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
+ } else {
+ mChallengeDigest = null;
+ }
+ long id = mListener.getConnectionId();
+ if (id == -1) {
+ reply.mConnectionID = null;
+ } else {
+ reply.mConnectionID = ObexHelper.convertToByteArray(id);
+ }
+
+ head = ObexHelper.createHeader(reply, false);
+ totalLength += head.length;
+
+ if (totalLength > mMaxPacketLength) {
+ totalLength = 7;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ totalLength = 7;
+ head = null;
+ code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+ }
+ }
+
+ // Compute Length of OBEX CONNECT packet
+ byte[] length = ObexHelper.convertToByteArray(totalLength);
+
+ /*
+ * Write the OBEX CONNECT packet to the server. Byte 0: response code
+ * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
+ * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
+ * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
+ */
+ byte[] sendData = new byte[totalLength];
+ sendData[0] = (byte)code;
+ sendData[1] = length[2];
+ sendData[2] = length[3];
+ sendData[3] = (byte)0x10;
+ sendData[4] = (byte)0x00;
+ sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
+ sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+
+ if (head != null) {
+ System.arraycopy(head, 0, sendData, 7, head.length);
+ }
+
+ mOutput.write(sendData);
+ mOutput.flush();
+ }
+
+ /**
+ * Closes the server session - in detail close I/O streams and the
+ * underlying transport layer. Internal flag is also set so that later
+ * attempt to read/write will throw an exception.
+ */
+ public synchronized void close() {
+ if (mListener != null) {
+ mListener.onClose();
+ }
+ try {
+ mInput.close();
+ mOutput.close();
+ mTransport.close();
+ mClosed = true;
+ } catch (Exception e) {
+ }
+ mTransport = null;
+ mInput = null;
+ mOutput = null;
+ mListener = null;
+ }
+
+ /**
+ * Verifies that the response code is valid. If it is not valid, it will
+ * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
+ * @param code the response code to check
+ * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
+ * if <code>code</code> is not valid
+ */
+ private int validateResponseCode(int code) {
+
+ if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
+ && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
+ && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
+ && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
+ return code;
+ }
+ if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
+ && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
+ return code;
+ }
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+
+}
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
new file mode 100644
index 0000000..9836dd6
--- /dev/null
+++ b/obex/javax/obex/SessionNotifier.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * - Neither the name of the Motorola, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package javax.obex;
+
+import java.io.IOException;
+
+/**
+ * The <code>SessionNotifier</code> interface defines a connection notifier for
+ * server-side OBEX connections. When a <code>SessionNotifier</code> is created
+ * and calls <code>acceptAndOpen()</code>, it will begin listening for clients
+ * to create a connection at the transport layer. When the transport layer
+ * connection is received, the <code>acceptAndOpen()</code> method will return a
+ * <code>javax.microedition.io.Connection</code> that is the connection to the
+ * client. The <code>acceptAndOpen()</code> method also takes a
+ * <code>ServerRequestHandler</code> argument that will process the requests
+ * from the client that connects to the server.
+ * @hide
+ */
+public interface SessionNotifier {
+
+ /**
+ * Waits for a transport layer connection to be established and specifies
+ * the handler to handle the requests from the client. No authenticator is
+ * associated with this connection, therefore, it is implementation
+ * dependent as to how an authentication challenge and authentication
+ * response header will be received and processed.
+ * <P>
+ * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
+ * on a <code>SessionNotifier</code> object that does not have a
+ * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
+ * for this object will be added to the SDDB. This method requests the BCC
+ * to put the local device in connectable mode so that it will respond to
+ * connection attempts by clients.
+ * <P>
+ * The following checks are done to verify that the service record provided
+ * is valid. If any of these checks fail, then a
+ * <code>ServiceRegistrationException</code> is thrown.
+ * <UL>
+ * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
+ * attributes for a <code>btgoep</code> service record, must be present in
+ * the <code>ServiceRecord</code> associated with this notifier.
+ * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+ * <LI>The <code>ServiceRecord</code> associated with this notifier must not
+ * have changed the RFCOMM server channel number
+ * </UL>
+ * <P>
+ * This method will not ensure that <code>ServiceRecord</code> associated
+ * with this notifier is a completely valid service record. It is the
+ * responsibility of the application to ensure that the service record
+ * follows all of the applicable syntactic and semantic rules for service
+ * record correctness.
+ * @param handler the request handler that will respond to OBEX requests
+ * @return the connection to the client
+ * @throws IOException if an error occurs in the transport layer
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>
+ */
+ ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
+
+ /**
+ * Waits for a transport layer connection to be established and specifies
+ * the handler to handle the requests from the client and the
+ * <code>Authenticator</code> to use to respond to authentication challenge
+ * and authentication response headers.
+ * <P>
+ * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
+ * on a <code>SessionNotifier</code> object that does not have a
+ * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
+ * for this object will be added to the SDDB. This method requests the BCC
+ * to put the local device in connectable mode so that it will respond to
+ * connection attempts by clients.
+ * <P>
+ * The following checks are done to verify that the service record provided
+ * is valid. If any of these checks fail, then a
+ * <code>ServiceRegistrationException</code> is thrown.
+ * <UL>
+ * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
+ * attributes for a <code>btgoep</code> service record, must be present in
+ * the <code>ServiceRecord</code> associated with this notifier.
+ * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
+ * <LI>The <code>ServiceRecord</code> associated with this notifier must not
+ * have changed the RFCOMM server channel number
+ * </UL>
+ * <P>
+ * This method will not ensure that <code>ServiceRecord</code> associated
+ * with this notifier is a completely valid service record. It is the
+ * responsibility of the application to ensure that the service record
+ * follows all of the applicable syntactic and semantic rules for service
+ * record correctness.
+ * @param handler the request handler that will respond to OBEX requests
+ * @param auth the <code>Authenticator</code> to use with this connection;
+ * if <code>null</code> then no <code>Authenticator</code> will be
+ * used
+ * @return the connection to the client
+ * @throws IOException if an error occurs in the transport layer
+ * @throws NullPointerException if <code>handler</code> is <code>null</code>
+ */
+ ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth) throws IOException;
+}
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index 25cfcb8..545fd0e 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -131,6 +131,30 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLDESTROYIMAGEKHRPROC) (EGLDisplay dpy, EGL
/* Interfaces defined by EGL_KHR_image above */
#endif
+
+#ifndef EGL_ANDROID_image_native_buffer
+#define EGL_ANDROID_image_native_buffer 1
+struct android_native_buffer_t;
+#define EGL_NATIVE_BUFFER_ANDROID 0x3140 /* eglCreateImageKHR target */
+#endif
+
+#ifndef EGL_ANDROID_get_render_buffer
+#define EGL_ANDROID_get_render_buffer 1
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLClientBuffer EGLAPIENTRY eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw);
+#endif
+typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETRENDERBUFFERANDROIDPROC) (EGLDisplay dpy, EGLSurface draw);
+#endif
+
+#ifndef EGL_ANDROID_swap_rectangle
+#define EGL_ANDROID_swap_rectangle 1
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height);
+#endif
+
+
#ifdef __cplusplus
}
#endif
diff --git a/opengl/include/EGL/eglnatives.h b/opengl/include/EGL/eglnatives.h
deleted file mode 100644
index 21622dc..0000000
--- a/opengl/include/EGL/eglnatives.h
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_EGLNATIVES_H
-#define ANDROID_EGLNATIVES_H
-
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*****************************************************************************/
-
-/* flags returned from swapBuffer */
-#define EGL_NATIVES_FLAG_SIZE_CHANGED 0x00000001
-
-/* surface flags */
-#define EGL_NATIVES_FLAG_DESTROY_BACKBUFFER 0x00000001
-
-enum native_pixel_format_t
-{
- NATIVE_PIXEL_FORMAT_RGBA_8888 = 1,
- NATIVE_PIXEL_FORMAT_RGB_565 = 4,
- NATIVE_PIXEL_FORMAT_BGRA_8888 = 5,
- NATIVE_PIXEL_FORMAT_RGBA_5551 = 6,
- NATIVE_PIXEL_FORMAT_RGBA_4444 = 7,
- NATIVE_PIXEL_FORMAT_YCbCr_422_SP= 0x10,
- NATIVE_PIXEL_FORMAT_YCbCr_420_SP= 0x11,
-};
-
-enum native_memory_type_t
-{
- NATIVE_MEMORY_TYPE_PMEM = 0,
- NATIVE_MEMORY_TYPE_GPU = 1,
- NATIVE_MEMORY_TYPE_FB = 2,
- NATIVE_MEMORY_TYPE_HEAP = 128
-};
-
-
-struct egl_native_window_t
-{
- /*
- * magic must be set to 0x600913
- */
- uint32_t magic;
-
- /*
- * must be sizeof(egl_native_window_t)
- */
- uint32_t version;
-
- /*
- * ident is reserved for the Android platform
- */
- uint32_t ident;
-
- /*
- * width, height and stride of the window in pixels
- * Any of these value can be nul in which case GL commands are
- * accepted and processed as usual, but not rendering occurs.
- */
- int width; // w=h=0 is legal
- int height;
- int stride;
-
- /*
- * format of the native window (see ui/PixelFormat.h)
- */
- int format;
-
- /*
- * Offset of the bits in the VRAM
- */
- intptr_t offset;
-
- /*
- * flags describing some attributes of this surface
- * EGL_NATIVES_FLAG_DESTROY_BACKBUFFER: backbuffer not preserved after
- * eglSwapBuffers
- */
- uint32_t flags;
-
- /*
- * horizontal and vertical resolution in DPI
- */
- float xdpi;
- float ydpi;
-
- /*
- * refresh rate in frames per second (Hz)
- */
- float fps;
-
-
- /*
- * Base memory virtual address of the surface in the CPU side
- */
- intptr_t base;
-
- /*
- * Heap the offset above is based from
- */
- int fd;
-
- /*
- * Memory type the surface resides into
- */
- uint8_t memory_type;
-
- /*
- * Reserved for future use. MUST BE ZERO.
- */
- uint8_t reserved_pad[3];
- int reserved[8];
-
- /*
- * Vertical stride (only relevant with planar formats)
- */
-
- int vstride;
-
- /*
- * Hook called by EGL to hold a reference on this structure
- */
- void (*incRef)(struct egl_native_window_t* window);
-
- /*
- * Hook called by EGL to release a reference on this structure
- */
- void (*decRef)(struct egl_native_window_t* window);
-
- /*
- * Hook called by EGL to perform a page flip. This function
- * may update the size attributes above, in which case it returns
- * the EGL_NATIVES_FLAG_SIZE_CHANGED bit set.
- */
- uint32_t (*swapBuffers)(struct egl_native_window_t* window);
-
- /*
- * Reserved for future use. MUST BE ZERO.
- */
- void (*reserved_proc_0)(void);
-
- /*
- * Reserved for future use. MUST BE ZERO.
- */
- void (*reserved_proc_1)(void);
-
- /*
- * Reserved for future use. MUST BE ZERO.
- */
- void (*reserved_proc_2)(void);
-
-
- /*
- * Hook called by EGL when the native surface is associated to EGL
- * (eglCreateWindowSurface). Can be NULL.
- */
- void (*connect)(struct egl_native_window_t* window);
-
- /*
- * Hook called by EGL when eglDestroySurface is called. Can be NULL.
- */
- void (*disconnect)(struct egl_native_window_t* window);
-
- /*
- * Reserved for future use. MUST BE ZERO.
- */
- void (*reserved_proc[11])(void);
-
- /*
- * Some storage reserved for the oem driver.
- */
- intptr_t oem[4];
-};
-
-
-struct egl_native_pixmap_t
-{
- int32_t version; /* must be 32 */
- int32_t width;
- int32_t height;
- int32_t stride;
- uint8_t* data;
- uint8_t format;
- uint8_t rfu[3];
- union {
- uint32_t compressedFormat;
- int32_t vstride;
- };
- int32_t reserved;
-};
-
-/*****************************************************************************/
-
-/*
- * This a convenience function to create a NativeWindowType surface
- * that maps to the whole screen
- * This function is actually implemented in libui.so
- */
-
-struct egl_native_window_t* android_createDisplaySurface();
-
-/*****************************************************************************/
-
-
-/*
- * OEM's egl's library (libhgl.so) must imlement these hooks to allocate
- * the GPU memory they need
- */
-
-
-typedef struct
-{
- // for internal use
- void* user;
- // virtual address of this area
- void* base;
- // size of this area in bytes
- size_t size;
- // physical address of this area
- void* phys;
- // offset in this area available to the GPU
- size_t offset;
- // fd of this area
- int fd;
-} gpu_area_t;
-
-typedef struct
-{
- // area where GPU registers are mapped
- gpu_area_t regs;
- // number of extra areas (currently limited to 2)
- int32_t count;
- // extra GPU areas (currently limited to 2)
- gpu_area_t gpu[2];
-} request_gpu_t;
-
-
-typedef request_gpu_t* (*OEM_EGL_acquire_gpu_t)(void* user);
-typedef int (*OEM_EGL_release_gpu_t)(void* user, request_gpu_t* handle);
-typedef void (*register_gpu_t)
- (void* user, OEM_EGL_acquire_gpu_t, OEM_EGL_release_gpu_t);
-
-void oem_register_gpu(
- void* user,
- OEM_EGL_acquire_gpu_t acquire,
- OEM_EGL_release_gpu_t release);
-
-
-/*****************************************************************************/
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_EGLNATIVES_H */
diff --git a/opengl/include/EGL/eglplatform.h b/opengl/include/EGL/eglplatform.h
index ac00901..53e9e6116 100644
--- a/opengl/include/EGL/eglplatform.h
+++ b/opengl/include/EGL/eglplatform.h
@@ -89,9 +89,10 @@ typedef Window EGLNativeWindowType;
#elif defined(ANDROID)
-#include <EGL/eglnatives.h>
+struct android_native_window_t;
+struct egl_native_pixmap_t;
-typedef struct egl_native_window_t* EGLNativeWindowType;
+typedef struct android_native_window_t* EGLNativeWindowType;
typedef struct egl_native_pixmap_t* EGLNativePixmapType;
typedef void* EGLNativeDisplayType;
diff --git a/opengl/include/GLES/glplatform.h b/opengl/include/GLES/glplatform.h
index 0924cae..198e679 100644
--- a/opengl/include/GLES/glplatform.h
+++ b/opengl/include/GLES/glplatform.h
@@ -28,12 +28,6 @@
#define GL_APIENTRY KHRONOS_APIENTRY
-// XXX: this should probably not be here
-#define GL_DIRECT_TEXTURE_2D_QUALCOMM 0x7E80
-
-// XXX: not sure how this is intended to be used
-#define GL_GLEXT_PROTOTYPES
-
#endif
#endif /* __glplatform_h_ */
diff --git a/opengl/include/GLES2/gl2.h b/opengl/include/GLES2/gl2.h
new file mode 100644
index 0000000..0182a67
--- /dev/null
+++ b/opengl/include/GLES2/gl2.h
@@ -0,0 +1,620 @@
+#ifndef __gl2_h_
+#define __gl2_h_
+
+/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */
+
+#include <GLES2/gl2platform.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+/*-------------------------------------------------------------------------
+ * Data type definitions
+ *-----------------------------------------------------------------------*/
+
+typedef void GLvoid;
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLbitfield;
+typedef khronos_int8_t GLbyte;
+typedef short GLshort;
+typedef int GLint;
+typedef int GLsizei;
+typedef khronos_uint8_t GLubyte;
+typedef unsigned short GLushort;
+typedef unsigned int GLuint;
+typedef khronos_float_t GLfloat;
+typedef khronos_float_t GLclampf;
+typedef khronos_int32_t GLfixed;
+
+/* GL types for handling large vertex buffer objects */
+typedef khronos_intptr_t GLintptr;
+typedef khronos_ssize_t GLsizeiptr;
+
+/* OpenGL ES core versions */
+#define GL_ES_VERSION_2_0 1
+
+/* ClearBufferMask */
+#define GL_DEPTH_BUFFER_BIT 0x00000100
+#define GL_STENCIL_BUFFER_BIT 0x00000400
+#define GL_COLOR_BUFFER_BIT 0x00004000
+
+/* Boolean */
+#define GL_FALSE 0
+#define GL_TRUE 1
+
+/* BeginMode */
+#define GL_POINTS 0x0000
+#define GL_LINES 0x0001
+#define GL_LINE_LOOP 0x0002
+#define GL_LINE_STRIP 0x0003
+#define GL_TRIANGLES 0x0004
+#define GL_TRIANGLE_STRIP 0x0005
+#define GL_TRIANGLE_FAN 0x0006
+
+/* AlphaFunction (not supported in ES20) */
+/* GL_NEVER */
+/* GL_LESS */
+/* GL_EQUAL */
+/* GL_LEQUAL */
+/* GL_GREATER */
+/* GL_NOTEQUAL */
+/* GL_GEQUAL */
+/* GL_ALWAYS */
+
+/* BlendingFactorDest */
+#define GL_ZERO 0
+#define GL_ONE 1
+#define GL_SRC_COLOR 0x0300
+#define GL_ONE_MINUS_SRC_COLOR 0x0301
+#define GL_SRC_ALPHA 0x0302
+#define GL_ONE_MINUS_SRC_ALPHA 0x0303
+#define GL_DST_ALPHA 0x0304
+#define GL_ONE_MINUS_DST_ALPHA 0x0305
+
+/* BlendingFactorSrc */
+/* GL_ZERO */
+/* GL_ONE */
+#define GL_DST_COLOR 0x0306
+#define GL_ONE_MINUS_DST_COLOR 0x0307
+#define GL_SRC_ALPHA_SATURATE 0x0308
+/* GL_SRC_ALPHA */
+/* GL_ONE_MINUS_SRC_ALPHA */
+/* GL_DST_ALPHA */
+/* GL_ONE_MINUS_DST_ALPHA */
+
+/* BlendEquationSeparate */
+#define GL_FUNC_ADD 0x8006
+#define GL_BLEND_EQUATION 0x8009
+#define GL_BLEND_EQUATION_RGB 0x8009 /* same as BLEND_EQUATION */
+#define GL_BLEND_EQUATION_ALPHA 0x883D
+
+/* BlendSubtract */
+#define GL_FUNC_SUBTRACT 0x800A
+#define GL_FUNC_REVERSE_SUBTRACT 0x800B
+
+/* Separate Blend Functions */
+#define GL_BLEND_DST_RGB 0x80C8
+#define GL_BLEND_SRC_RGB 0x80C9
+#define GL_BLEND_DST_ALPHA 0x80CA
+#define GL_BLEND_SRC_ALPHA 0x80CB
+#define GL_CONSTANT_COLOR 0x8001
+#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
+#define GL_CONSTANT_ALPHA 0x8003
+#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
+#define GL_BLEND_COLOR 0x8005
+
+/* Buffer Objects */
+#define GL_ARRAY_BUFFER 0x8892
+#define GL_ELEMENT_ARRAY_BUFFER 0x8893
+#define GL_ARRAY_BUFFER_BINDING 0x8894
+#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895
+
+#define GL_STREAM_DRAW 0x88E0
+#define GL_STATIC_DRAW 0x88E4
+#define GL_DYNAMIC_DRAW 0x88E8
+
+#define GL_BUFFER_SIZE 0x8764
+#define GL_BUFFER_USAGE 0x8765
+
+#define GL_CURRENT_VERTEX_ATTRIB 0x8626
+
+/* CullFaceMode */
+#define GL_FRONT 0x0404
+#define GL_BACK 0x0405
+#define GL_FRONT_AND_BACK 0x0408
+
+/* DepthFunction */
+/* GL_NEVER */
+/* GL_LESS */
+/* GL_EQUAL */
+/* GL_LEQUAL */
+/* GL_GREATER */
+/* GL_NOTEQUAL */
+/* GL_GEQUAL */
+/* GL_ALWAYS */
+
+/* EnableCap */
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_CULL_FACE 0x0B44
+#define GL_BLEND 0x0BE2
+#define GL_DITHER 0x0BD0
+#define GL_STENCIL_TEST 0x0B90
+#define GL_DEPTH_TEST 0x0B71
+#define GL_SCISSOR_TEST 0x0C11
+#define GL_POLYGON_OFFSET_FILL 0x8037
+#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E
+#define GL_SAMPLE_COVERAGE 0x80A0
+
+/* ErrorCode */
+#define GL_NO_ERROR 0
+#define GL_INVALID_ENUM 0x0500
+#define GL_INVALID_VALUE 0x0501
+#define GL_INVALID_OPERATION 0x0502
+#define GL_OUT_OF_MEMORY 0x0505
+
+/* FrontFaceDirection */
+#define GL_CW 0x0900
+#define GL_CCW 0x0901
+
+/* GetPName */
+#define GL_LINE_WIDTH 0x0B21
+#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
+#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
+#define GL_CULL_FACE_MODE 0x0B45
+#define GL_FRONT_FACE 0x0B46
+#define GL_DEPTH_RANGE 0x0B70
+#define GL_DEPTH_WRITEMASK 0x0B72
+#define GL_DEPTH_CLEAR_VALUE 0x0B73
+#define GL_DEPTH_FUNC 0x0B74
+#define GL_STENCIL_CLEAR_VALUE 0x0B91
+#define GL_STENCIL_FUNC 0x0B92
+#define GL_STENCIL_FAIL 0x0B94
+#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95
+#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96
+#define GL_STENCIL_REF 0x0B97
+#define GL_STENCIL_VALUE_MASK 0x0B93
+#define GL_STENCIL_WRITEMASK 0x0B98
+#define GL_STENCIL_BACK_FUNC 0x8800
+#define GL_STENCIL_BACK_FAIL 0x8801
+#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802
+#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803
+#define GL_STENCIL_BACK_REF 0x8CA3
+#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4
+#define GL_STENCIL_BACK_WRITEMASK 0x8CA5
+#define GL_VIEWPORT 0x0BA2
+#define GL_SCISSOR_BOX 0x0C10
+/* GL_SCISSOR_TEST */
+#define GL_COLOR_CLEAR_VALUE 0x0C22
+#define GL_COLOR_WRITEMASK 0x0C23
+#define GL_UNPACK_ALIGNMENT 0x0CF5
+#define GL_PACK_ALIGNMENT 0x0D05
+#define GL_MAX_TEXTURE_SIZE 0x0D33
+#define GL_MAX_VIEWPORT_DIMS 0x0D3A
+#define GL_SUBPIXEL_BITS 0x0D50
+#define GL_RED_BITS 0x0D52
+#define GL_GREEN_BITS 0x0D53
+#define GL_BLUE_BITS 0x0D54
+#define GL_ALPHA_BITS 0x0D55
+#define GL_DEPTH_BITS 0x0D56
+#define GL_STENCIL_BITS 0x0D57
+#define GL_POLYGON_OFFSET_UNITS 0x2A00
+/* GL_POLYGON_OFFSET_FILL */
+#define GL_POLYGON_OFFSET_FACTOR 0x8038
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_SAMPLE_BUFFERS 0x80A8
+#define GL_SAMPLES 0x80A9
+#define GL_SAMPLE_COVERAGE_VALUE 0x80AA
+#define GL_SAMPLE_COVERAGE_INVERT 0x80AB
+
+/* GetTextureParameter */
+/* GL_TEXTURE_MAG_FILTER */
+/* GL_TEXTURE_MIN_FILTER */
+/* GL_TEXTURE_WRAP_S */
+/* GL_TEXTURE_WRAP_T */
+
+#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3
+
+/* HintMode */
+#define GL_DONT_CARE 0x1100
+#define GL_FASTEST 0x1101
+#define GL_NICEST 0x1102
+
+/* HintTarget */
+#define GL_GENERATE_MIPMAP_HINT 0x8192
+
+/* DataType */
+#define GL_BYTE 0x1400
+#define GL_UNSIGNED_BYTE 0x1401
+#define GL_SHORT 0x1402
+#define GL_UNSIGNED_SHORT 0x1403
+#define GL_INT 0x1404
+#define GL_UNSIGNED_INT 0x1405
+#define GL_FLOAT 0x1406
+#define GL_FIXED 0x140C
+
+/* PixelFormat */
+#define GL_DEPTH_COMPONENT 0x1902
+#define GL_ALPHA 0x1906
+#define GL_RGB 0x1907
+#define GL_RGBA 0x1908
+#define GL_LUMINANCE 0x1909
+#define GL_LUMINANCE_ALPHA 0x190A
+
+/* PixelType */
+/* GL_UNSIGNED_BYTE */
+#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
+#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
+#define GL_UNSIGNED_SHORT_5_6_5 0x8363
+
+/* Shaders */
+#define GL_FRAGMENT_SHADER 0x8B30
+#define GL_VERTEX_SHADER 0x8B31
+#define GL_MAX_VERTEX_ATTRIBS 0x8869
+#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB
+#define GL_MAX_VARYING_VECTORS 0x8DFC
+#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C
+#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872
+#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD
+#define GL_SHADER_TYPE 0x8B4F
+#define GL_DELETE_STATUS 0x8B80
+#define GL_LINK_STATUS 0x8B82
+#define GL_VALIDATE_STATUS 0x8B83
+#define GL_ATTACHED_SHADERS 0x8B85
+#define GL_ACTIVE_UNIFORMS 0x8B86
+#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87
+#define GL_ACTIVE_ATTRIBUTES 0x8B89
+#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
+#define GL_CURRENT_PROGRAM 0x8B8D
+
+/* StencilFunction */
+#define GL_NEVER 0x0200
+#define GL_LESS 0x0201
+#define GL_EQUAL 0x0202
+#define GL_LEQUAL 0x0203
+#define GL_GREATER 0x0204
+#define GL_NOTEQUAL 0x0205
+#define GL_GEQUAL 0x0206
+#define GL_ALWAYS 0x0207
+
+/* StencilOp */
+/* GL_ZERO */
+#define GL_KEEP 0x1E00
+#define GL_REPLACE 0x1E01
+#define GL_INCR 0x1E02
+#define GL_DECR 0x1E03
+#define GL_INVERT 0x150A
+#define GL_INCR_WRAP 0x8507
+#define GL_DECR_WRAP 0x8508
+
+/* StringName */
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+#define GL_EXTENSIONS 0x1F03
+
+/* TextureMagFilter */
+#define GL_NEAREST 0x2600
+#define GL_LINEAR 0x2601
+
+/* TextureMinFilter */
+/* GL_NEAREST */
+/* GL_LINEAR */
+#define GL_NEAREST_MIPMAP_NEAREST 0x2700
+#define GL_LINEAR_MIPMAP_NEAREST 0x2701
+#define GL_NEAREST_MIPMAP_LINEAR 0x2702
+#define GL_LINEAR_MIPMAP_LINEAR 0x2703
+
+/* TextureParameterName */
+#define GL_TEXTURE_MAG_FILTER 0x2800
+#define GL_TEXTURE_MIN_FILTER 0x2801
+#define GL_TEXTURE_WRAP_S 0x2802
+#define GL_TEXTURE_WRAP_T 0x2803
+
+/* TextureTarget */
+/* GL_TEXTURE_2D */
+#define GL_TEXTURE 0x1702
+
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
+#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
+#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A
+#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C
+
+/* TextureUnit */
+#define GL_TEXTURE0 0x84C0
+#define GL_TEXTURE1 0x84C1
+#define GL_TEXTURE2 0x84C2
+#define GL_TEXTURE3 0x84C3
+#define GL_TEXTURE4 0x84C4
+#define GL_TEXTURE5 0x84C5
+#define GL_TEXTURE6 0x84C6
+#define GL_TEXTURE7 0x84C7
+#define GL_TEXTURE8 0x84C8
+#define GL_TEXTURE9 0x84C9
+#define GL_TEXTURE10 0x84CA
+#define GL_TEXTURE11 0x84CB
+#define GL_TEXTURE12 0x84CC
+#define GL_TEXTURE13 0x84CD
+#define GL_TEXTURE14 0x84CE
+#define GL_TEXTURE15 0x84CF
+#define GL_TEXTURE16 0x84D0
+#define GL_TEXTURE17 0x84D1
+#define GL_TEXTURE18 0x84D2
+#define GL_TEXTURE19 0x84D3
+#define GL_TEXTURE20 0x84D4
+#define GL_TEXTURE21 0x84D5
+#define GL_TEXTURE22 0x84D6
+#define GL_TEXTURE23 0x84D7
+#define GL_TEXTURE24 0x84D8
+#define GL_TEXTURE25 0x84D9
+#define GL_TEXTURE26 0x84DA
+#define GL_TEXTURE27 0x84DB
+#define GL_TEXTURE28 0x84DC
+#define GL_TEXTURE29 0x84DD
+#define GL_TEXTURE30 0x84DE
+#define GL_TEXTURE31 0x84DF
+#define GL_ACTIVE_TEXTURE 0x84E0
+
+/* TextureWrapMode */
+#define GL_REPEAT 0x2901
+#define GL_CLAMP_TO_EDGE 0x812F
+#define GL_MIRRORED_REPEAT 0x8370
+
+/* Uniform Types */
+#define GL_FLOAT_VEC2 0x8B50
+#define GL_FLOAT_VEC3 0x8B51
+#define GL_FLOAT_VEC4 0x8B52
+#define GL_INT_VEC2 0x8B53
+#define GL_INT_VEC3 0x8B54
+#define GL_INT_VEC4 0x8B55
+#define GL_BOOL 0x8B56
+#define GL_BOOL_VEC2 0x8B57
+#define GL_BOOL_VEC3 0x8B58
+#define GL_BOOL_VEC4 0x8B59
+#define GL_FLOAT_MAT2 0x8B5A
+#define GL_FLOAT_MAT3 0x8B5B
+#define GL_FLOAT_MAT4 0x8B5C
+#define GL_SAMPLER_2D 0x8B5E
+#define GL_SAMPLER_CUBE 0x8B60
+
+/* Vertex Arrays */
+#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622
+#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623
+#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624
+#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625
+#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A
+#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645
+#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+
+/* Read Format */
+#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A
+#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+
+/* Shader Source */
+#define GL_COMPILE_STATUS 0x8B81
+#define GL_INFO_LOG_LENGTH 0x8B84
+#define GL_SHADER_SOURCE_LENGTH 0x8B88
+#define GL_SHADER_COMPILER 0x8DFA
+
+/* Shader Binary */
+#define GL_SHADER_BINARY_FORMATS 0x8DF8
+#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9
+
+/* Shader Precision-Specified Types */
+#define GL_LOW_FLOAT 0x8DF0
+#define GL_MEDIUM_FLOAT 0x8DF1
+#define GL_HIGH_FLOAT 0x8DF2
+#define GL_LOW_INT 0x8DF3
+#define GL_MEDIUM_INT 0x8DF4
+#define GL_HIGH_INT 0x8DF5
+
+/* Framebuffer Object. */
+#define GL_FRAMEBUFFER 0x8D40
+#define GL_RENDERBUFFER 0x8D41
+
+#define GL_RGBA4 0x8056
+#define GL_RGB5_A1 0x8057
+#define GL_RGB565 0x8D62
+#define GL_DEPTH_COMPONENT16 0x81A5
+#define GL_STENCIL_INDEX 0x1901
+#define GL_STENCIL_INDEX8 0x8D48
+
+#define GL_RENDERBUFFER_WIDTH 0x8D42
+#define GL_RENDERBUFFER_HEIGHT 0x8D43
+#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44
+#define GL_RENDERBUFFER_RED_SIZE 0x8D50
+#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51
+#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52
+#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53
+#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54
+#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
+
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0
+#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+
+#define GL_COLOR_ATTACHMENT0 0x8CE0
+#define GL_DEPTH_ATTACHMENT 0x8D00
+#define GL_STENCIL_ATTACHMENT 0x8D20
+
+#define GL_NONE 0
+
+#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
+#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6
+#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 0x8CD9
+#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD
+
+#define GL_FRAMEBUFFER_BINDING 0x8CA6
+#define GL_RENDERBUFFER_BINDING 0x8CA7
+#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
+
+#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506
+
+/*-------------------------------------------------------------------------
+ * GL core functions.
+ *-----------------------------------------------------------------------*/
+
+GL_APICALL void GL_APIENTRY glActiveTexture (GLenum texture);
+GL_APICALL void GL_APIENTRY glAttachShader (GLuint program, GLuint shader);
+GL_APICALL void GL_APIENTRY glBindAttribLocation (GLuint program, GLuint index, const char* name);
+GL_APICALL void GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer);
+GL_APICALL void GL_APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer);
+GL_APICALL void GL_APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer);
+GL_APICALL void GL_APIENTRY glBindTexture (GLenum target, GLuint texture);
+GL_APICALL void GL_APIENTRY glBlendColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+GL_APICALL void GL_APIENTRY glBlendEquation ( GLenum mode );
+GL_APICALL void GL_APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha);
+GL_APICALL void GL_APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor);
+GL_APICALL void GL_APIENTRY glBlendFuncSeparate (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
+GL_APICALL void GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void* data, GLenum usage);
+GL_APICALL void GL_APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
+GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus (GLenum target);
+GL_APICALL void GL_APIENTRY glClear (GLbitfield mask);
+GL_APICALL void GL_APIENTRY glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
+GL_APICALL void GL_APIENTRY glClearDepthf (GLclampf depth);
+GL_APICALL void GL_APIENTRY glClearStencil (GLint s);
+GL_APICALL void GL_APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
+GL_APICALL void GL_APIENTRY glCompileShader (GLuint shader);
+GL_APICALL void GL_APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
+GL_APICALL void GL_APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL GLuint GL_APIENTRY glCreateProgram (void);
+GL_APICALL GLuint GL_APIENTRY glCreateShader (GLenum type);
+GL_APICALL void GL_APIENTRY glCullFace (GLenum mode);
+GL_APICALL void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers);
+GL_APICALL void GL_APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint* framebuffers);
+GL_APICALL void GL_APIENTRY glDeleteProgram (GLuint program);
+GL_APICALL void GL_APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint* renderbuffers);
+GL_APICALL void GL_APIENTRY glDeleteShader (GLuint shader);
+GL_APICALL void GL_APIENTRY glDeleteTextures (GLsizei n, const GLuint* textures);
+GL_APICALL void GL_APIENTRY glDepthFunc (GLenum func);
+GL_APICALL void GL_APIENTRY glDepthMask (GLboolean flag);
+GL_APICALL void GL_APIENTRY glDepthRangef (GLclampf zNear, GLclampf zFar);
+GL_APICALL void GL_APIENTRY glDetachShader (GLuint program, GLuint shader);
+GL_APICALL void GL_APIENTRY glDisable (GLenum cap);
+GL_APICALL void GL_APIENTRY glDisableVertexAttribArray (GLuint index);
+GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
+GL_APICALL void GL_APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const void* indices);
+GL_APICALL void GL_APIENTRY glEnable (GLenum cap);
+GL_APICALL void GL_APIENTRY glEnableVertexAttribArray (GLuint index);
+GL_APICALL void GL_APIENTRY glFinish (void);
+GL_APICALL void GL_APIENTRY glFlush (void);
+GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+GL_APICALL void GL_APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+GL_APICALL void GL_APIENTRY glFrontFace (GLenum mode);
+GL_APICALL void GL_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers);
+GL_APICALL void GL_APIENTRY glGenerateMipmap (GLenum target);
+GL_APICALL void GL_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers);
+GL_APICALL void GL_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers);
+GL_APICALL void GL_APIENTRY glGenTextures (GLsizei n, GLuint* textures);
+GL_APICALL void GL_APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+GL_APICALL void GL_APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name);
+GL_APICALL void GL_APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders);
+GL_APICALL int GL_APIENTRY glGetAttribLocation (GLuint program, const char* name);
+GL_APICALL void GL_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params);
+GL_APICALL void GL_APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint* params);
+GL_APICALL GLenum GL_APIENTRY glGetError (void);
+GL_APICALL void GL_APIENTRY glGetFloatv (GLenum pname, GLfloat* params);
+GL_APICALL void GL_APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetIntegerv (GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufsize, GLsizei* length, char* infolog);
+GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog);
+GL_APICALL void GL_APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
+GL_APICALL void GL_APIENTRY glGetShaderSource (GLuint shader, GLsizei bufsize, GLsizei* length, char* source);
+GL_APICALL const GLubyte* GL_APIENTRY glGetString (GLenum name);
+GL_APICALL void GL_APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat* params);
+GL_APICALL void GL_APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat* params);
+GL_APICALL void GL_APIENTRY glGetUniformiv (GLuint program, GLint location, GLint* params);
+GL_APICALL int GL_APIENTRY glGetUniformLocation (GLuint program, const char* name);
+GL_APICALL void GL_APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat* params);
+GL_APICALL void GL_APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint* params);
+GL_APICALL void GL_APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void** pointer);
+GL_APICALL void GL_APIENTRY glHint (GLenum target, GLenum mode);
+GL_APICALL GLboolean GL_APIENTRY glIsBuffer (GLuint buffer);
+GL_APICALL GLboolean GL_APIENTRY glIsEnabled (GLenum cap);
+GL_APICALL GLboolean GL_APIENTRY glIsFramebuffer (GLuint framebuffer);
+GL_APICALL GLboolean GL_APIENTRY glIsProgram (GLuint program);
+GL_APICALL GLboolean GL_APIENTRY glIsRenderbuffer (GLuint renderbuffer);
+GL_APICALL GLboolean GL_APIENTRY glIsShader (GLuint shader);
+GL_APICALL GLboolean GL_APIENTRY glIsTexture (GLuint texture);
+GL_APICALL void GL_APIENTRY glLineWidth (GLfloat width);
+GL_APICALL void GL_APIENTRY glLinkProgram (GLuint program);
+GL_APICALL void GL_APIENTRY glPixelStorei (GLenum pname, GLint param);
+GL_APICALL void GL_APIENTRY glPolygonOffset (GLfloat factor, GLfloat units);
+GL_APICALL void GL_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
+GL_APICALL void GL_APIENTRY glReleaseShaderCompiler (void);
+GL_APICALL void GL_APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glSampleCoverage (GLclampf value, GLboolean invert);
+GL_APICALL void GL_APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glShaderBinary (GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length);
+GL_APICALL void GL_APIENTRY glShaderSource (GLuint shader, GLsizei count, const char** string, const GLint* length);
+GL_APICALL void GL_APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask);
+GL_APICALL void GL_APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask);
+GL_APICALL void GL_APIENTRY glStencilMask (GLuint mask);
+GL_APICALL void GL_APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask);
+GL_APICALL void GL_APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass);
+GL_APICALL void GL_APIENTRY glStencilOpSeparate (GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
+GL_APICALL void GL_APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+GL_APICALL void GL_APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param);
+GL_APICALL void GL_APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat* params);
+GL_APICALL void GL_APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param);
+GL_APICALL void GL_APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint* params);
+GL_APICALL void GL_APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
+GL_APICALL void GL_APIENTRY glUniform1f (GLint location, GLfloat x);
+GL_APICALL void GL_APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat* v);
+GL_APICALL void GL_APIENTRY glUniform1i (GLint location, GLint x);
+GL_APICALL void GL_APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint* v);
+GL_APICALL void GL_APIENTRY glUniform2f (GLint location, GLfloat x, GLfloat y);
+GL_APICALL void GL_APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat* v);
+GL_APICALL void GL_APIENTRY glUniform2i (GLint location, GLint x, GLint y);
+GL_APICALL void GL_APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint* v);
+GL_APICALL void GL_APIENTRY glUniform3f (GLint location, GLfloat x, GLfloat y, GLfloat z);
+GL_APICALL void GL_APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat* v);
+GL_APICALL void GL_APIENTRY glUniform3i (GLint location, GLint x, GLint y, GLint z);
+GL_APICALL void GL_APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint* v);
+GL_APICALL void GL_APIENTRY glUniform4f (GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+GL_APICALL void GL_APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat* v);
+GL_APICALL void GL_APIENTRY glUniform4i (GLint location, GLint x, GLint y, GLint z, GLint w);
+GL_APICALL void GL_APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint* v);
+GL_APICALL void GL_APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+GL_APICALL void GL_APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+GL_APICALL void GL_APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
+GL_APICALL void GL_APIENTRY glUseProgram (GLuint program);
+GL_APICALL void GL_APIENTRY glValidateProgram (GLuint program);
+GL_APICALL void GL_APIENTRY glVertexAttrib1f (GLuint indx, GLfloat x);
+GL_APICALL void GL_APIENTRY glVertexAttrib1fv (GLuint indx, const GLfloat* values);
+GL_APICALL void GL_APIENTRY glVertexAttrib2f (GLuint indx, GLfloat x, GLfloat y);
+GL_APICALL void GL_APIENTRY glVertexAttrib2fv (GLuint indx, const GLfloat* values);
+GL_APICALL void GL_APIENTRY glVertexAttrib3f (GLuint indx, GLfloat x, GLfloat y, GLfloat z);
+GL_APICALL void GL_APIENTRY glVertexAttrib3fv (GLuint indx, const GLfloat* values);
+GL_APICALL void GL_APIENTRY glVertexAttrib4f (GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+GL_APICALL void GL_APIENTRY glVertexAttrib4fv (GLuint indx, const GLfloat* values);
+GL_APICALL void GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr);
+GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __gl2_h_ */
diff --git a/opengl/include/GLES2/gl2ext.h b/opengl/include/GLES2/gl2ext.h
new file mode 100644
index 0000000..72f1ae7
--- /dev/null
+++ b/opengl/include/GLES2/gl2ext.h
@@ -0,0 +1,518 @@
+#ifndef __gl2ext_h_
+#define __gl2ext_h_
+
+/* $Revision: 8271 $ on $Date:: 2009-05-21 09:33:40 -0700 #$ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+#ifndef GL_APIENTRYP
+# define GL_APIENTRYP GL_APIENTRY*
+#endif
+
+/*------------------------------------------------------------------------*
+ * OES extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_OES_compressed_ETC1_RGB8_texture */
+#ifndef GL_OES_compressed_ETC1_RGB8_texture
+#define GL_ETC1_RGB8_OES 0x8D64
+#endif
+
+/* GL_OES_compressed_paletted_texture */
+#ifndef GL_OES_compressed_paletted_texture
+#define GL_PALETTE4_RGB8_OES 0x8B90
+#define GL_PALETTE4_RGBA8_OES 0x8B91
+#define GL_PALETTE4_R5_G6_B5_OES 0x8B92
+#define GL_PALETTE4_RGBA4_OES 0x8B93
+#define GL_PALETTE4_RGB5_A1_OES 0x8B94
+#define GL_PALETTE8_RGB8_OES 0x8B95
+#define GL_PALETTE8_RGBA8_OES 0x8B96
+#define GL_PALETTE8_R5_G6_B5_OES 0x8B97
+#define GL_PALETTE8_RGBA4_OES 0x8B98
+#define GL_PALETTE8_RGB5_A1_OES 0x8B99
+#endif
+
+/* GL_OES_depth24 */
+#ifndef GL_OES_depth24
+#define GL_DEPTH_COMPONENT24_OES 0x81A6
+#endif
+
+/* GL_OES_depth32 */
+#ifndef GL_OES_depth32
+#define GL_DEPTH_COMPONENT32_OES 0x81A7
+#endif
+
+/* GL_OES_depth_texture */
+/* No new tokens introduced by this extension. */
+
+/* GL_OES_EGL_image */
+#ifndef GL_OES_EGL_image
+typedef void* GLeglImageOES;
+#endif
+
+/* GL_OES_get_program_binary */
+#ifndef GL_OES_get_program_binary
+#define GL_PROGRAM_BINARY_LENGTH_OES 0x8741
+#define GL_NUM_PROGRAM_BINARY_FORMATS_OES 0x87FE
+#define GL_PROGRAM_BINARY_FORMATS_OES 0x87FF
+#endif
+
+/* GL_OES_mapbuffer */
+#ifndef GL_OES_mapbuffer
+#define GL_WRITE_ONLY_OES 0x88B9
+#define GL_BUFFER_ACCESS_OES 0x88BB
+#define GL_BUFFER_MAPPED_OES 0x88BC
+#define GL_BUFFER_MAP_POINTER_OES 0x88BD
+#endif
+
+/* GL_OES_packed_depth_stencil */
+#ifndef GL_OES_packed_depth_stencil
+#define GL_DEPTH_STENCIL_OES 0x84F9
+#define GL_UNSIGNED_INT_24_8_OES 0x84FA
+#define GL_DEPTH24_STENCIL8_OES 0x88F0
+#endif
+
+/* GL_OES_rgb8_rgba8 */
+#ifndef GL_OES_rgb8_rgba8
+#define GL_RGB8_OES 0x8051
+#define GL_RGBA8_OES 0x8058
+#endif
+
+/* GL_OES_standard_derivatives */
+#ifndef GL_OES_standard_derivatives
+#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES 0x8B8B
+#endif
+
+/* GL_OES_stencil1 */
+#ifndef GL_OES_stencil1
+#define GL_STENCIL_INDEX1_OES 0x8D46
+#endif
+
+/* GL_OES_stencil4 */
+#ifndef GL_OES_stencil4
+#define GL_STENCIL_INDEX4_OES 0x8D47
+#endif
+
+/* GL_OES_texture3D */
+#ifndef GL_OES_texture3D
+#define GL_TEXTURE_WRAP_R_OES 0x8072
+#define GL_TEXTURE_3D_OES 0x806F
+#define GL_TEXTURE_BINDING_3D_OES 0x806A
+#define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073
+#define GL_SAMPLER_3D_OES 0x8B5F
+#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES 0x8CD4
+#endif
+
+/* GL_OES_texture_half_float */
+#ifndef GL_OES_texture_half_float
+#define GL_HALF_FLOAT_OES 0x8D61
+#endif
+
+/* GL_OES_vertex_half_float */
+/* GL_HALF_FLOAT_OES defined in GL_OES_texture_half_float already. */
+
+/* GL_OES_vertex_type_10_10_10_2 */
+#ifndef GL_OES_vertex_type_10_10_10_2
+#define GL_UNSIGNED_INT_10_10_10_2_OES 0x8DF6
+#define GL_INT_10_10_10_2_OES 0x8DF7
+#endif
+
+/*------------------------------------------------------------------------*
+ * AMD extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_AMD_compressed_3DC_texture */
+#ifndef GL_AMD_compressed_3DC_texture
+#define GL_3DC_X_AMD 0x87F9
+#define GL_3DC_XY_AMD 0x87FA
+#endif
+
+/* GL_AMD_compressed_ATC_texture */
+#ifndef GL_AMD_compressed_ATC_texture
+#define GL_ATC_RGB_AMD 0x8C92
+#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93
+#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE
+#endif
+
+/* GL_AMD_program_binary_Z400 */
+#ifndef GL_AMD_program_binary_Z400
+#define GL_Z400_BINARY_AMD 0x8740
+#endif
+
+/* GL_AMD_performance_monitor */
+#ifndef GL_AMD_performance_monitor
+#define GL_COUNTER_TYPE_AMD 0x8BC0
+#define GL_COUNTER_RANGE_AMD 0x8BC1
+#define GL_UNSIGNED_INT64_AMD 0x8BC2
+#define GL_PERCENTAGE_AMD 0x8BC3
+#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4
+#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5
+#define GL_PERFMON_RESULT_AMD 0x8BC6
+#endif
+
+/*------------------------------------------------------------------------*
+ * EXT extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_EXT_texture_filter_anisotropic */
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#endif
+
+/* GL_EXT_texture_type_2_10_10_10_REV */
+#ifndef GL_EXT_texture_type_2_10_10_10_REV
+#define GL_UNSIGNED_INT_2_10_10_10_REV_EXT 0x8368
+#endif
+
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_BGRA 0x80E1
+#endif
+
+/*------------------------------------------------------------------------*
+ * IMG extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_IMG_read_format */
+#ifndef GL_IMG_read_format
+#define GL_BGRA 0x80E1
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
+#endif
+
+/* GL_IMG_texture_compression_pvrtc */
+#ifndef GL_IMG_texture_compression_pvrtc
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03
+#endif
+
+/*------------------------------------------------------------------------*
+ * NV extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_NV_fence */
+#ifndef GL_NV_fence
+#define GL_ALL_COMPLETED_NV 0x84F2
+#define GL_FENCE_STATUS_NV 0x84F3
+#define GL_FENCE_CONDITION_NV 0x84F4
+#endif
+
+/*------------------------------------------------------------------------*
+ * QCOM extension tokens
+ *------------------------------------------------------------------------*/
+
+/* GL_QCOM_driver_control */
+/* No new tokens introduced by this extension. */
+
+/* GL_QCOM_perfmon_global_mode */
+#ifndef GL_QCOM_perfmon_global_mode
+#define GL_PERFMON_GLOBAL_MODE_QCOM 0x8FA0
+#endif
+
+/*------------------------------------------------------------------------*
+ * End of extension tokens, start of corresponding extension functions
+ *------------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------------*
+ * OES extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_OES_compressed_ETC1_RGB8_texture */
+#ifndef GL_OES_compressed_ETC1_RGB8_texture
+#define GL_OES_compressed_ETC1_RGB8_texture 1
+#endif
+
+/* GL_OES_compressed_paletted_texture */
+#ifndef GL_OES_compressed_paletted_texture
+#define GL_OES_compressed_paletted_texture 1
+#endif
+
+/* GL_OES_EGL_image */
+#ifndef GL_OES_EGL_image
+#define GL_OES_EGL_image 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glEGLImageTargetTexture2DOES (GLenum target, GLeglImageOES image);
+GL_APICALL void GL_APIENTRY glEGLImageTargetRenderbufferStorageOES (GLenum target, GLeglImageOES image);
+#endif
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image);
+typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) (GLenum target, GLeglImageOES image);
+#endif
+
+/* GL_OES_depth24 */
+#ifndef GL_OES_depth24
+#define GL_OES_depth24 1
+#endif
+
+/* GL_OES_depth32 */
+#ifndef GL_OES_depth32
+#define GL_OES_depth32 1
+#endif
+
+/* GL_OES_depth_texture */
+#ifndef GL_OES_depth_texture
+#define GL_OES_depth_texture 1
+#endif
+
+/* GL_OES_element_index_uint */
+#ifndef GL_OES_element_index_uint
+#define GL_OES_element_index_uint 1
+#endif
+
+/* GL_OES_fbo_render_mipmap */
+#ifndef GL_OES_fbo_render_mipmap
+#define GL_OES_fbo_render_mipmap 1
+#endif
+
+/* GL_OES_fragment_precision_high */
+#ifndef GL_OES_fragment_precision_high
+#define GL_OES_fragment_precision_high 1
+#endif
+
+/* GL_OES_get_program_binary */
+#ifndef GL_OES_get_program_binary
+#define GL_OES_get_program_binary 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGetProgramBinaryOES (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
+GL_APICALL void GL_APIENTRY glProgramBinaryOES (GLuint program, GLenum binaryFormat, const void *binary, GLint length);
+#endif
+typedef void (GL_APIENTRYP PFNGLGETPROGRAMBINARYOESPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary);
+typedef void (GL_APIENTRYP PFNGLPROGRAMBINARYOESPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLint length);
+#endif
+
+/* GL_OES_mapbuffer */
+#ifndef GL_OES_mapbuffer
+#define GL_OES_mapbuffer 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void* GL_APIENTRY glMapBufferOES (GLenum target, GLenum access);
+GL_APICALL GLboolean GL_APIENTRY glUnmapBufferOES (GLenum target);
+GL_APICALL void GL_APIENTRY glGetBufferPointervOES (GLenum target, GLenum pname, void** params);
+#endif
+typedef void* (GL_APIENTRYP PFNGLMAPBUFFEROESPROC) (GLenum target, GLenum access);
+typedef GLboolean (GL_APIENTRYP PFNGLUNMAPBUFFEROESPROC) (GLenum target);
+typedef void (GL_APIENTRYP PFNGLGETBUFFERPOINTERVOESPROC) (GLenum target, GLenum pname, void** params);
+#endif
+
+/* GL_OES_packed_depth_stencil */
+#ifndef GL_OES_packed_depth_stencil
+#define GL_OES_packed_depth_stencil 1
+#endif
+
+/* GL_OES_rgb8_rgba8 */
+#ifndef GL_OES_rgb8_rgba8
+#define GL_OES_rgb8_rgba8 1
+#endif
+
+/* GL_OES_standard_derivatives */
+#ifndef GL_OES_standard_derivatives
+#define GL_OES_standard_derivatives 1
+#endif
+
+/* GL_OES_stencil1 */
+#ifndef GL_OES_stencil1
+#define GL_OES_stencil1 1
+#endif
+
+/* GL_OES_stencil4 */
+#ifndef GL_OES_stencil4
+#define GL_OES_stencil4 1
+#endif
+
+/* GL_OES_texture_3D */
+#ifndef GL_OES_texture_3D
+#define GL_OES_texture_3D 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels);
+GL_APICALL void GL_APIENTRY glTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
+GL_APICALL void GL_APIENTRY glCopyTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+GL_APICALL void GL_APIENTRY glCompressedTexImage3DOES (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glCompressedTexSubImage3DOES (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data);
+GL_APICALL void GL_APIENTRY glFramebufferTexture3DOES (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
+#endif
+typedef void (GL_APIENTRYP PFNGLTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
+typedef void (GL_APIENTRYP PFNGLTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels);
+typedef void (GL_APIENTRYP PFNGLCOPYTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
+typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DOESPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data);
+typedef void (GL_APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DOESPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data);
+typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
+#endif
+
+/* GL_OES_texture_float_linear */
+#ifndef GL_OES_texture_float_linear
+#define GL_OES_texture_float_linear 1
+#endif
+
+/* GL_OES_texture_half_float_linear */
+#ifndef GL_OES_texture_half_float_linear
+#define GL_OES_texture_half_float_linear 1
+#endif
+
+/* GL_OES_texture_float */
+#ifndef GL_OES_texture_float
+#define GL_OES_texture_float 1
+#endif
+
+/* GL_OES_texture_half_float */
+#ifndef GL_OES_texture_half_float
+#define GL_OES_texture_half_float 1
+#endif
+
+/* GL_OES_texture_npot */
+#ifndef GL_OES_texture_npot
+#define GL_OES_texture_npot 1
+#endif
+
+/* GL_OES_vertex_half_float */
+#ifndef GL_OES_vertex_half_float
+#define GL_OES_vertex_half_float 1
+#endif
+
+/* GL_OES_vertex_type_10_10_10_2 */
+#ifndef GL_OES_vertex_type_10_10_10_2
+#define GL_OES_vertex_type_10_10_10_2 1
+#endif
+
+/*------------------------------------------------------------------------*
+ * AMD extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_AMD_compressed_3DC_texture */
+#ifndef GL_AMD_compressed_3DC_texture
+#define GL_AMD_compressed_3DC_texture 1
+#endif
+
+/* GL_AMD_compressed_ATC_texture */
+#ifndef GL_AMD_compressed_ATC_texture
+#define GL_AMD_compressed_ATC_texture 1
+#endif
+
+/* GL_AMD_program_binary_Z400 */
+#ifndef GL_AMD_program_binary_Z400
+#define GL_AMD_program_binary_Z400 1
+#endif
+
+/* AMD_performance_monitor */
+#ifndef GL_AMD_performance_monitor
+#define GL_AMD_performance_monitor 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data);
+GL_APICALL void GL_APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors);
+GL_APICALL void GL_APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors);
+GL_APICALL void GL_APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList);
+GL_APICALL void GL_APIENTRY glBeginPerfMonitorAMD (GLuint monitor);
+GL_APICALL void GL_APIENTRY glEndPerfMonitorAMD (GLuint monitor);
+GL_APICALL void GL_APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten);
+#endif
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, char *groupString);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data);
+typedef void (GL_APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);
+typedef void (GL_APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors);
+typedef void (GL_APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList);
+typedef void (GL_APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor);
+typedef void (GL_APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor);
+typedef void (GL_APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten);
+#endif
+
+/*------------------------------------------------------------------------*
+ * EXT extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_EXT_texture_filter_anisotropic */
+#ifndef GL_EXT_texture_filter_anisotropic
+#define GL_EXT_texture_filter_anisotropic 1
+#endif
+
+/* GL_EXT_texture_type_2_10_10_10_REV */
+#ifndef GL_EXT_texture_type_2_10_10_10_REV
+#define GL_EXT_texture_type_2_10_10_10_REV 1
+#endif
+
+/* GL_EXT_texture_format_BGRA8888 */
+#ifndef GL_EXT_texture_format_BGRA8888
+#define GL_EXT_texture_format_BGRA8888 1
+#endif
+
+/*------------------------------------------------------------------------*
+ * IMG extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_IMG_read_format */
+#ifndef GL_IMG_read_format
+#define GL_IMG_read_format 1
+#endif
+
+/* GL_IMG_texture_compression_pvrtc */
+#ifndef GL_IMG_texture_compression_pvrtc
+#define GL_IMG_texture_compression_pvrtc 1
+#endif
+
+/*------------------------------------------------------------------------*
+ * NV extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_NV_fence */
+#ifndef GL_NV_fence
+#define GL_NV_fence 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences);
+GL_APICALL void GL_APIENTRY glGenFencesNV (GLsizei n, GLuint *fences);
+GL_APICALL GLboolean GL_APIENTRY glIsFenceNV (GLuint fence);
+GL_APICALL GLboolean GL_APIENTRY glTestFenceNV (GLuint fence);
+GL_APICALL void GL_APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params);
+GL_APICALL void GL_APIENTRY glFinishFenceNV (GLuint fence);
+GL_APICALL void GL_APIENTRY glSetFenceNV (GLuint fence, GLenum condition);
+#endif
+typedef void (GL_APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences);
+typedef void (GL_APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences);
+typedef GLboolean (GL_APIENTRYP PFNGLISFENCENVPROC) (GLuint fence);
+typedef GLboolean (GL_APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence);
+typedef void (GL_APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params);
+typedef void (GL_APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence);
+typedef void (GL_APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition);
+#endif
+
+/*------------------------------------------------------------------------*
+ * QCOM extension functions
+ *------------------------------------------------------------------------*/
+
+/* GL_QCOM_driver_control */
+#ifndef GL_QCOM_driver_control
+#define GL_QCOM_driver_control 1
+#ifdef GL_GLEXT_PROTOTYPES
+GL_APICALL void GL_APIENTRY glGetDriverControlsQCOM (GLint *num, GLsizei size, GLuint *driverControls);
+GL_APICALL void GL_APIENTRY glGetDriverControlStringQCOM (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString);
+GL_APICALL void GL_APIENTRY glEnableDriverControlQCOM (GLuint driverControl);
+GL_APICALL void GL_APIENTRY glDisableDriverControlQCOM (GLuint driverControl);
+#endif
+typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSQCOMPROC) (GLint *num, GLsizei size, GLuint *driverControls);
+typedef void (GL_APIENTRYP PFNGLGETDRIVERCONTROLSTRINGQCOMPROC) (GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString);
+typedef void (GL_APIENTRYP PFNGLENABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
+typedef void (GL_APIENTRYP PFNGLDISABLEDRIVERCONTROLQCOMPROC) (GLuint driverControl);
+#endif
+
+/* GL_QCOM_perfmon_global_mode */
+#ifndef GL_QCOM_perfmon_global_mode
+#define GL_QCOM_perfmon_global_mode 1
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __gl2ext_h_ */
diff --git a/opengl/include/GLES2/gl2platform.h b/opengl/include/GLES2/gl2platform.h
new file mode 100644
index 0000000..3e9036c
--- /dev/null
+++ b/opengl/include/GLES2/gl2platform.h
@@ -0,0 +1,29 @@
+#ifndef __gl2platform_h_
+#define __gl2platform_h_
+
+/* $Revision: 7173 $ on $Date:: 2009-01-09 11:18:21 -0800 #$ */
+
+/*
+ * This document is licensed under the SGI Free Software B License Version
+ * 2.0. For details, see http://oss.sgi.com/projects/FreeB/ .
+ */
+
+/* Platform-specific types and definitions for OpenGL ES 2.X gl2.h
+ * Last modified on 2008/12/19
+ *
+ * Adopters may modify khrplatform.h and this file to suit their platform.
+ * You are encouraged to submit all modifications to the Khronos group so that
+ * they can be included in future versions of this file. Please submit changes
+ * by sending them to the public Khronos Bugzilla (http://khronos.org/bugzilla)
+ * by filing a bug against product "OpenGL-ES" component "Registry".
+ */
+
+#include <KHR/khrplatform.h>
+
+#ifndef GL_APICALL
+#define GL_APICALL KHRONOS_APICALL
+#endif
+
+#define GL_APIENTRY KHRONOS_APIENTRY
+
+#endif /* __gl2platform_h_ */
diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java
index ccb27cc..022da0c 100644
--- a/opengl/java/android/opengl/GLSurfaceView.java
+++ b/opengl/java/android/opengl/GLSurfaceView.java
@@ -204,7 +204,6 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
// underlying surface is created and destroyed
SurfaceHolder holder = getHolder();
holder.addCallback(this);
- holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
}
/**
@@ -656,25 +655,27 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback
EGLConfig closestConfig = null;
int closestDistance = 1000;
for(EGLConfig config : configs) {
- int r = findConfigAttrib(egl, display, config,
- EGL10.EGL_RED_SIZE, 0);
- int g = findConfigAttrib(egl, display, config,
- EGL10.EGL_GREEN_SIZE, 0);
- int b = findConfigAttrib(egl, display, config,
- EGL10.EGL_BLUE_SIZE, 0);
- int a = findConfigAttrib(egl, display, config,
- EGL10.EGL_ALPHA_SIZE, 0);
int d = findConfigAttrib(egl, display, config,
EGL10.EGL_DEPTH_SIZE, 0);
int s = findConfigAttrib(egl, display, config,
EGL10.EGL_STENCIL_SIZE, 0);
- int distance = Math.abs(r - mRedSize)
- + Math.abs(g - mGreenSize)
- + Math.abs(b - mBlueSize) + Math.abs(a - mAlphaSize)
- + Math.abs(d - mDepthSize) + Math.abs(s - mStencilSize);
- if (distance < closestDistance) {
- closestDistance = distance;
- closestConfig = config;
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
}
}
return closestConfig;
diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk
index 3ce0414..2522656 100644
--- a/opengl/libagl/Android.mk
+++ b/opengl/libagl/Android.mk
@@ -6,6 +6,9 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+# Set to 1 to use gralloc and copybits
+LIBAGL_USE_GRALLOC_COPYBITS := 1
+
LOCAL_SRC_FILES:= \
egl.cpp \
state.cpp \
@@ -29,13 +32,23 @@ endif
ifneq ($(TARGET_SIMULATOR),true)
# we need to access the private Bionic header <bionic_tls.h>
- LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private
+ LOCAL_C_INCLUDES += bionic/libc/private
+endif
+
+ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1)
+ LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS
+ LOCAL_SRC_FILES += copybit.cpp
endif
-LOCAL_SHARED_LIBRARIES := libcutils libutils libpixelflinger
+LOCAL_CFLAGS += -DLOG_TAG=\"libagl\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_SHARED_LIBRARIES := libcutils libhardware libutils libpixelflinger
LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_LDLIBS := -lpthread -ldl
-LOCAL_MODULE:= libagl
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl
+LOCAL_MODULE:= libGLES_android
include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp
index ce31854..255ccac 100644
--- a/opengl/libagl/TextureObjectManager.cpp
+++ b/opengl/libagl/TextureObjectManager.cpp
@@ -1,16 +1,16 @@
/*
** Copyright 2006, The Android Open Source Project
**
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
**
- ** http://www.apache.org/licenses/LICENSE-2.0
+ ** http://www.apache.org/licenses/LICENSE-2.0
**
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -19,11 +19,13 @@
#include "context.h"
#include "TextureObjectManager.h"
+#include <private/ui/android_natives_priv.h>
+
namespace android {
// ----------------------------------------------------------------------------
EGLTextureObject::EGLTextureObject()
- : mCount(0), mSize(0)
+ : mSize(0)
{
init();
}
@@ -53,6 +55,10 @@ void EGLTextureObject::init()
memset(crop_rect, 0, sizeof(crop_rect));
generate_mipmap = GL_FALSE;
direct = GL_FALSE;
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ try_copybit = false;
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+ buffer = 0;
}
void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old)
@@ -123,6 +129,7 @@ status_t EGLTextureObject::setSurface(GGLSurface const* s)
}
surface = *s;
internalformat = 0;
+ buffer = 0;
// we should keep the crop_rect, but it's delicate because
// the new size of the surface could make it invalid.
@@ -141,12 +148,26 @@ status_t EGLTextureObject::setSurface(GGLSurface const* s)
return NO_ERROR;
}
+status_t EGLTextureObject::setImage(android_native_buffer_t* native_buffer)
+{
+ GGLSurface sur;
+ sur.version = sizeof(GGLSurface);
+ sur.width = native_buffer->width;
+ sur.height= native_buffer->height;
+ sur.stride= native_buffer->stride;
+ sur.format= native_buffer->format;
+ sur.data = 0;
+ setSurface(&sur);
+ buffer = native_buffer;
+ return NO_ERROR;
+}
+
status_t EGLTextureObject::reallocate(
GLint level, int w, int h, int s,
int format, int compressedFormat, int bpr)
{
const size_t size = h * bpr;
- if (level == 0)
+ if (level == 0)
{
if (size!=mSize || !surface.data) {
if (mSize && surface.data) {
@@ -177,9 +198,9 @@ status_t EGLTextureObject::reallocate(
return NO_MEMORY;
}
- LOGW_IF(level-1 >= mNumExtraLod,
+ LOGW_IF(level-1 >= mNumExtraLod,
"specifying mipmap level %d, but # of level is %d",
- level, mNumExtraLod+1);
+ level, mNumExtraLod+1);
GGLSurface& mipmap = editMip(level);
if (mipmap.data)
@@ -224,7 +245,7 @@ status_t EGLTextureObject::reallocate(
// ----------------------------------------------------------------------------
EGLSurfaceManager::EGLSurfaceManager()
- : TokenManager(), mCount(0)
+ : TokenManager()
{
}
diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h
index 74ed1a4..279e040 100644
--- a/opengl/libagl/TextureObjectManager.h
+++ b/opengl/libagl/TextureObjectManager.h
@@ -1,16 +1,16 @@
/*
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -30,6 +30,8 @@
#include <private/pixelflinger/ggl_context.h>
#include <GLES/gl.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
#include "Tokenizer.h"
#include "TokenManager.h"
@@ -39,22 +41,20 @@ namespace android {
// ----------------------------------------------------------------------------
-class EGLTextureObject
+class EGLTextureObject : public LightRefBase<EGLTextureObject>
{
public:
EGLTextureObject();
~EGLTextureObject();
- // protocol for sp<>
- inline void incStrong(const void* id) const;
- inline void decStrong(const void* id) const;
- inline uint32_t getStrongCount() const;
+ status_t setSurface(GGLSurface const* s);
+ status_t setImage(android_native_buffer_t* buffer);
+ void setImageBits(void* vaddr) { surface.data = (GGLubyte*)vaddr; }
- status_t setSurface(GGLSurface const* s);
status_t reallocate(GLint level,
int w, int h, int s,
int format, int compressedFormat, int bpr);
- inline size_t size() const;
+ inline size_t size() const { return mSize; }
const GGLSurface& mip(int lod) const;
GGLSurface& editMip(int lod);
bool hasMipmaps() const { return mMipmaps!=0; }
@@ -65,7 +65,6 @@ private:
status_t allocateMipmaps();
void freeMipmaps();
void init();
- mutable int32_t mCount;
size_t mSize;
GGLSurface *mMipmaps;
int mNumExtraLod;
@@ -81,36 +80,22 @@ public:
GLint crop_rect[4];
GLint generate_mipmap;
GLint direct;
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ bool try_copybit;
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+ android_native_buffer_t* buffer;
};
-void EGLTextureObject::incStrong(const void* id) const {
- android_atomic_inc(&mCount);
-}
-void EGLTextureObject::decStrong(const void* id) const {
- if (android_atomic_dec(&mCount) == 1) {
- delete this;
- }
-}
-uint32_t EGLTextureObject::getStrongCount() const {
- return mCount;
-}
-size_t EGLTextureObject::size() const {
- return mSize;
-}
-
// ----------------------------------------------------------------------------
-class EGLSurfaceManager : public TokenManager
+class EGLSurfaceManager :
+ public LightRefBase<EGLSurfaceManager>,
+ public TokenManager
{
public:
EGLSurfaceManager();
~EGLSurfaceManager();
- // protocol for sp<>
- inline void incStrong(const void* id) const;
- inline void decStrong(const void* id) const;
- typedef void weakref_type;
-
sp<EGLTextureObject> createTexture(GLuint name);
sp<EGLTextureObject> removeTexture(GLuint name);
sp<EGLTextureObject> replaceTexture(GLuint name);
@@ -118,21 +103,10 @@ public:
sp<EGLTextureObject> texture(GLuint name);
private:
- mutable int32_t mCount;
mutable Mutex mLock;
KeyedVector< GLuint, sp<EGLTextureObject> > mTextures;
};
-void EGLSurfaceManager::incStrong(const void* id) const {
- android_atomic_inc(&mCount);
-}
-void EGLSurfaceManager::decStrong(const void* id) const {
- if (android_atomic_dec(&mCount) == 1) {
- delete this;
- }
-}
-
-
// ----------------------------------------------------------------------------
}; // namespace android
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 3e9c6a5..f414ee5 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -1,16 +1,16 @@
-/*
+/*
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -26,6 +26,9 @@
#include "primitives.h"
#include "texture.h"
#include "BufferObjectManager.h"
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+#include "copybit.h"
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
// ----------------------------------------------------------------------------
@@ -250,7 +253,7 @@ static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) {
v[2] = GGL_S_TO_X(p[2]);
}
-typedef array_t::fetcher_t fn_t;
+typedef array_t::fetcher_t fn_t;
static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x}
{ 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0,
@@ -334,7 +337,7 @@ void array_t::init(
this->bounds = count;
}
-inline void array_t::resolve()
+inline void array_t::resolve()
{
physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer;
}
@@ -465,7 +468,7 @@ vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index)
// We compute directly the index of a "free" entry from the locked
// state of v[2] and v[3].
v = c->vc.vBuffer + 2;
- v += v[0].locked | (v[1].locked<<1);
+ v += v[0].locked | (v[1].locked<<1);
}
// note: compileElement clears v->flags
c->arrays.compileElement(c, v, index);
@@ -480,7 +483,7 @@ vertex_t* fetch_vertex(ogles_context_t* c, size_t index)
#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED
- vertex_t* const v = c->vc.vCache +
+ vertex_t* const v = c->vc.vCache +
(index & (vertex_cache_t::VERTEX_CACHE_SIZE-1));
if (ggl_likely(v->index == index)) {
@@ -491,7 +494,7 @@ vertex_t* fetch_vertex(ogles_context_t* c, size_t index)
#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU
- vertex_t* v = c->vc.vCache +
+ vertex_t* v = c->vc.vCache +
(index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2;
// always record LRU in v[0]
@@ -532,12 +535,12 @@ void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count)
return;
// vertex cache size must be multiple of 1
- const GLsizei vcs =
+ const GLsizei vcs =
(vertex_cache_t::VERTEX_BUFFER_SIZE +
vertex_cache_t::VERTEX_CACHE_SIZE);
do {
vertex_t* v = c->vc.vBuffer;
- GLsizei num = count > vcs ? vcs : count;
+ GLsizei num = count > vcs ? vcs : count;
c->arrays.cull = vertex_t::CLIP_ALL;
c->arrays.compileElements(c, v, first, num);
first += num;
@@ -569,13 +572,13 @@ void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count)
count -= 1;
// vertex cache size must be multiple of 1
- const GLsizei vcs =
+ const GLsizei vcs =
(vertex_cache_t::VERTEX_BUFFER_SIZE +
vertex_cache_t::VERTEX_CACHE_SIZE - 1);
do {
- v0 = c->vc.vBuffer + 0;
+ v0 = c->vc.vBuffer + 0;
v = c->vc.vBuffer + 1;
- GLsizei num = count > vcs ? vcs : count;
+ GLsizei num = count > vcs ? vcs : count;
c->arrays.compileElements(c, v, first, num);
first += num;
count -= num;
@@ -602,7 +605,7 @@ void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count)
return;
drawPrimitivesLineStrip(c, first, count);
if (ggl_likely(count >= 3)) {
- vertex_t* v0 = c->vc.vBuffer;
+ vertex_t* v0 = c->vc.vBuffer;
vertex_t* v1 = c->vc.vBuffer + 1;
c->arrays.compileElement(c, v1, first);
const uint32_t cc = v0->flags & v1->flags;
@@ -617,12 +620,12 @@ void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count)
return;
// vertex cache size must be multiple of 2
- const GLsizei vcs =
+ const GLsizei vcs =
((vertex_cache_t::VERTEX_BUFFER_SIZE +
vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2;
do {
vertex_t* v = c->vc.vBuffer;
- GLsizei num = count > vcs ? vcs : count;
+ GLsizei num = count > vcs ? vcs : count;
c->arrays.cull = vertex_t::CLIP_ALL;
c->arrays.compileElements(c, v, first, num);
first += num;
@@ -662,14 +665,14 @@ static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c,
// because it allows us to preserve the same winding when the whole
// batch is culled. We also need 2 extra vertices in the array, because
// we always keep the two first ones.
- const GLsizei vcs =
+ const GLsizei vcs =
((vertex_cache_t::VERTEX_BUFFER_SIZE +
vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2;
do {
- v0 = c->vc.vBuffer + 0;
- v1 = c->vc.vBuffer + 1;
+ v0 = c->vc.vBuffer + 0;
+ v1 = c->vc.vBuffer + 1;
v = c->vc.vBuffer + 2;
- GLsizei num = count > vcs ? vcs : count;
+ GLsizei num = count > vcs ? vcs : count;
c->arrays.compileElements(c, v, first, num);
first += num;
count -= num;
@@ -697,13 +700,19 @@ static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c,
} while (count > 0);
}
-void drawPrimitivesTriangleStrip(ogles_context_t* c,
+void drawPrimitivesTriangleStrip(ogles_context_t* c,
GLint first, GLsizei count) {
drawPrimitivesTriangleFanOrStrip(c, first, count, 1);
}
void drawPrimitivesTriangleFan(ogles_context_t* c,
GLint first, GLsizei count) {
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ if (drawTriangleFanWithCopybit(c, first, count)) {
+ return;
+ }
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
drawPrimitivesTriangleFanOrStrip(c, first, count, 2);
}
@@ -713,12 +722,12 @@ void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count)
return;
// vertex cache size must be multiple of 3
- const GLsizei vcs =
+ const GLsizei vcs =
((vertex_cache_t::VERTEX_BUFFER_SIZE +
vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3;
do {
vertex_t* v = c->vc.vBuffer;
- GLsizei num = count > vcs ? vcs : count;
+ GLsizei num = count > vcs ? vcs : count;
c->arrays.cull = vertex_t::CLIP_ALL;
c->arrays.compileElements(c, v, first, num);
first += num;
@@ -779,11 +788,11 @@ void drawIndexedPrimitivesLineStrip(ogles_context_t* c,
{
if (ggl_unlikely(count < 2))
return;
-
+
vertex_t * const v = c->vc.vBuffer;
vertex_t* v0 = v;
vertex_t* v1;
-
+
const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
c->arrays.compileElement(c, v0, read_index(type, indices));
count -= 1;
@@ -806,11 +815,11 @@ void drawIndexedPrimitivesLineLoop(ogles_context_t* c,
drawIndexedPrimitivesLines(c, count, indices);
return;
}
-
+
vertex_t * const v = c->vc.vBuffer;
vertex_t* v0 = v;
vertex_t* v1;
-
+
const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE);
c->arrays.compileElement(c, v0, read_index(type, indices));
count -= 1;
@@ -825,7 +834,7 @@ void drawIndexedPrimitivesLineLoop(ogles_context_t* c,
} while (count);
v1->locked = 0;
- v1 = c->vc.vBuffer;
+ v1 = c->vc.vBuffer;
const uint32_t cc = v0->flags & v1->flags;
if (ggl_likely(!(cc & vertex_t::CLIP_ALL)))
c->prims.renderLine(c, v0, v1);
@@ -861,7 +870,7 @@ static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c,
if (ggl_unlikely(count < 3))
return;
-
+
vertex_t * const v = c->vc.vBuffer;
vertex_t* v0 = v;
vertex_t* v1 = v+1;
@@ -985,17 +994,17 @@ void compileElements__3x_full(ogles_context_t* c,
const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first);
const size_t stride = c->arrays.vertex.stride / 4;
// const GLfixed* const& m = c->transforms.mvp.matrix.m;
-
+
GLfixed m[16];
memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m));
-
+
do {
const GLfixed rx = vp[0];
const GLfixed ry = vp[1];
const GLfixed rz = vp[2];
vp += stride;
v->index = first++;
- v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
+ v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]);
@@ -1023,7 +1032,7 @@ void compileElements__3x_full(ogles_context_t* c,
#pragma mark clippers
#endif
-static void clipVec4(vec4_t& nv,
+static void clipVec4(vec4_t& nv,
GLfixed t, const vec4_t& s, const vec4_t& p)
{
for (int i=0; i<4 ; i++)
@@ -1086,10 +1095,10 @@ void validate_arrays(ogles_context_t* c, GLenum mode)
// automatically turn it off (in fact we could when the 4th coordinate
// is not spcified in the vertex array).
// W interpolation is never needed for points.
- GLboolean perspective =
+ GLboolean perspective =
c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS);
c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective);
-
+
// set anti-aliasing
GLboolean smooth = GL_FALSE;
switch (mode) {
@@ -1120,7 +1129,7 @@ void validate_arrays(ogles_context_t* c, GLenum mode)
if (enables & GGL_ENABLE_TMUS) { // needs texture transforms
want |= transform_state_t::TEXTURE;
}
- if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) {
+ if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) {
want |= transform_state_t::MODELVIEW; // needs eye coords
}
ogles_validate_transform(c, want);
@@ -1139,18 +1148,18 @@ void validate_arrays(ogles_context_t* c, GLenum mode)
c->arrays.mv_transform =
c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2];
-
+
/*
* ***********************************************************************
* pick fetchers
* ***********************************************************************
*/
-
+
array_machine_t& am = c->arrays;
am.vertex.fetch = fetchNop;
am.normal.fetch = currentNormal;
am.color.fetch = currentColor;
-
+
if (am.vertex.enable) {
am.vertex.resolve();
if (am.vertex.bo || am.vertex.pointer) {
@@ -1366,9 +1375,18 @@ void glDrawArrays(GLenum mode, GLint first, GLsizei count)
if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK))
return; // all triangles are culled
+
validate_arrays(c, mode);
+
+ const uint32_t enables = c->rasterizer.state.enables;
+ if (enables & GGL_ENABLE_TMUS)
+ ogles_lock_textures(c);
+
drawArraysPrims[mode](c, first, count);
+ if (enables & GGL_ENABLE_TMUS)
+ ogles_unlock_textures(c);
+
#if VC_CACHE_STATISTICS
c->vc.total = count;
c->vc.dump_stats(mode);
@@ -1413,15 +1431,23 @@ void glDrawElements(
// clear the vertex-cache
c->vc.clear();
validate_arrays(c, mode);
-
+
// if indices are in a buffer object, the pointer is treated as an
// offset in that buffer.
if (c->arrays.element_array_buffer) {
indices = c->arrays.element_array_buffer->data + uintptr_t(indices);
}
+ const uint32_t enables = c->rasterizer.state.enables;
+ if (enables & GGL_ENABLE_TMUS)
+ ogles_lock_textures(c);
+
drawElementsPrims[mode](c, count, indices);
+
+ if (enables & GGL_ENABLE_TMUS)
+ ogles_unlock_textures(c);
+
#if VC_CACHE_STATISTICS
c->vc.total = count;
c->vc.dump_stats(mode);
@@ -1448,7 +1474,7 @@ void glBindBuffer(GLenum target, GLuint buffer)
return;
}
}
- ((target == GL_ARRAY_BUFFER) ?
+ ((target == GL_ARRAY_BUFFER) ?
c->arrays.array_buffer : c->arrays.element_array_buffer) = bo;
}
@@ -1467,7 +1493,7 @@ void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usa
ogles_error(c, GL_INVALID_ENUM);
return;
}
- buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
+ buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
c->arrays.array_buffer : c->arrays.element_array_buffer);
if (bo == 0) {
@@ -1497,7 +1523,7 @@ void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvo
ogles_error(c, GL_INVALID_VALUE);
return;
}
- buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
+ buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ?
c->arrays.array_buffer : c->arrays.element_array_buffer);
if (bo == 0) {
@@ -1545,7 +1571,7 @@ void glDeleteBuffers(GLsizei n, const GLuint* buffers)
}
}
}
- }
+ }
c->bufferObjectManager->deleteBuffers(n, buffers);
c->bufferObjectManager->recycleTokens(n, buffers);
}
diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp
new file mode 100644
index 0000000..867459d
--- /dev/null
+++ b/opengl/libagl/copybit.cpp
@@ -0,0 +1,460 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "context.h"
+#include "fp.h"
+#include "state.h"
+#include "matrix.h"
+#include "vertex.h"
+#include "light.h"
+#include "primitives.h"
+#include "texture.h"
+#include "BufferObjectManager.h"
+#include "TextureObjectManager.h"
+
+#include <hardware/gralloc.h>
+#include <hardware/copybit.h>
+#include <private/ui/android_natives_priv.h>
+
+
+#define DEBUG_COPYBIT true
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static void textureToCopyBitImage(
+ const GGLSurface* surface, buffer_handle_t buffer, copybit_image_t* img)
+{
+ img->w = surface->stride;
+ img->h = surface->height;
+ img->format = surface->format;
+ img->base = surface->data;
+ img->handle = (native_handle_t *)buffer;
+}
+
+struct clipRectRegion : public copybit_region_t {
+ clipRectRegion(ogles_context_t* c)
+ {
+ scissor_t const* scissor = &c->rasterizer.state.scissor;
+ r.l = scissor->left;
+ r.t = scissor->top;
+ r.r = scissor->right;
+ r.b = scissor->bottom;
+ next = iterate;
+ }
+private:
+ static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
+ *rect = static_cast<clipRectRegion const*>(self)->r;
+ const_cast<copybit_region_t *>(self)->next = iterate_done;
+ return 1;
+ }
+ static int iterate_done(copybit_region_t const *, copybit_rect_t*) {
+ return 0;
+ }
+ copybit_rect_t r;
+};
+
+static bool supportedCopybitsFormat(int format) {
+ switch (format) {
+ case COPYBIT_FORMAT_RGBA_8888:
+ case COPYBIT_FORMAT_RGBX_8888:
+ case COPYBIT_FORMAT_RGB_888:
+ case COPYBIT_FORMAT_RGB_565:
+ case COPYBIT_FORMAT_BGRA_8888:
+ case COPYBIT_FORMAT_RGBA_5551:
+ case COPYBIT_FORMAT_RGBA_4444:
+ case COPYBIT_FORMAT_YCbCr_422_SP:
+ case COPYBIT_FORMAT_YCbCr_420_SP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool hasAlpha(int format) {
+ switch (format) {
+ case COPYBIT_FORMAT_RGBA_8888:
+ case COPYBIT_FORMAT_BGRA_8888:
+ case COPYBIT_FORMAT_RGBA_5551:
+ case COPYBIT_FORMAT_RGBA_4444:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline int fixedToByte(GGLfixed val) {
+ return (val - (val >> 8)) >> 8;
+}
+
+/**
+ * Performs a quick check of the rendering state. If this function returns
+ * false we cannot use the copybit driver.
+ */
+
+static bool checkContext(ogles_context_t* c) {
+
+ // By convention copybitQuickCheckContext() has already returned true.
+ // avoid checking the same information again.
+
+ if (c->copybits.blitEngine == NULL) {
+ LOGD_IF(DEBUG_COPYBIT, "no copybit hal");
+ return false;
+ }
+
+ if (c->rasterizer.state.enables
+ & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) {
+ LOGD_IF(DEBUG_COPYBIT, "depth test and/or fog");
+ return false;
+ }
+
+ // Note: The drawSurfaceBuffer is only set for destination
+ // surfaces types that are supported by the hardware and
+ // do not have an alpha channel. So we don't have to re-check that here.
+
+ static const int tmu = 0;
+ texture_unit_t& u(c->textures.tmu[tmu]);
+ EGLTextureObject* textureObject = u.texture;
+
+ if (!supportedCopybitsFormat(textureObject->surface.format)) {
+ LOGD_IF(DEBUG_COPYBIT, "texture format not supported");
+ return false;
+ }
+ return true;
+}
+
+
+static bool copybit(GLint x, GLint y,
+ GLint w, GLint h,
+ EGLTextureObject* textureObject,
+ const GLint* crop_rect,
+ int transform,
+ ogles_context_t* c)
+{
+ // We assume checkContext has already been called and has already
+ // returned true.
+
+ const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+
+ y = cbSurface.height - (y + h);
+
+ const GLint Ucr = crop_rect[0];
+ const GLint Vcr = crop_rect[1];
+ const GLint Wcr = crop_rect[2];
+ const GLint Hcr = crop_rect[3];
+
+ GLint screen_w = w;
+ GLint screen_h = h;
+ int32_t dsdx = Wcr << 16; // dsdx = ((Wcr/screen_w)/Wt)*Wt
+ int32_t dtdy = Hcr << 16; // dtdy = -((Hcr/screen_h)/Ht)*Ht
+ if (transform & COPYBIT_TRANSFORM_ROT_90) {
+ swap(screen_w, screen_h);
+ }
+ if (dsdx!=screen_w || dtdy!=screen_h) {
+ // in most cases the divide is not needed
+ dsdx /= screen_w;
+ dtdy /= screen_h;
+ }
+ dtdy = -dtdy; // see equation of dtdy above
+ if (dsdx < c->copybits.minScale || dsdx > c->copybits.maxScale
+ || dtdy < c->copybits.minScale || dtdy > c->copybits.maxScale) {
+ // The requested scale is out of the range the hardware
+ // can support.
+ LOGD_IF(DEBUG_COPYBIT,
+ "scale out of range dsdx=%08x (Wcr=%d / w=%d), "
+ "dtdy=%08x (Hcr=%d / h=%d), Ucr=%d, Vcr=%d",
+ dsdx, Wcr, w, dtdy, Hcr, h, Ucr, Vcr);
+ return false;
+ }
+
+ // copybit doesn't say anything about filtering, so we can't
+ // discriminate. On msm7k, copybit will always filter.
+ // the code below handles min/mag filters, we keep it as a reference.
+
+#ifdef MIN_MAG_FILTER
+ int32_t texelArea = gglMulx(dtdy, dsdx);
+ if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) {
+ // Non-linear filtering on a texture enlargement.
+ LOGD_IF(DEBUG_COPYBIT, "mag filter is not GL_LINEAR");
+ return false;
+ }
+ if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) {
+ // Non-linear filtering on an texture shrink.
+ LOGD_IF(DEBUG_COPYBIT, "min filter is not GL_LINEAR");
+ return false;
+ }
+#endif
+
+ const uint32_t enables = c->rasterizer.state.enables;
+ int planeAlpha = 255;
+ static const int tmu = 0;
+ texture_t& tev(c->rasterizer.state.texture[tmu]);
+ bool srcTextureHasAlpha = hasAlpha(textureObject->surface.format);
+ if (!srcTextureHasAlpha) {
+ planeAlpha = fixedToByte(c->currentColorClamped.a);
+ }
+
+ switch (tev.env) {
+ case GGL_REPLACE:
+ break;
+ case GGL_MODULATE:
+ if (! (c->currentColorClamped.r == FIXED_ONE &&
+ c->currentColorClamped.g == FIXED_ONE &&
+ c->currentColorClamped.b == FIXED_ONE)) {
+ LOGD_IF(DEBUG_COPYBIT,
+ "MODULATE and non white color (%08x, %08x, %08x)",
+ c->currentColorClamped.r,
+ c->currentColorClamped.g,
+ c->currentColorClamped.b);
+ return false;
+ }
+ if (srcTextureHasAlpha && c->currentColorClamped.a < FIXED_ONE) {
+ LOGD_IF(DEBUG_COPYBIT,
+ "MODULATE and texture w/alpha and alpha=%08x)",
+ c->currentColorClamped.a);
+ return false;
+ }
+ break;
+
+ default:
+ // Incompatible texture environment.
+ LOGD_IF(DEBUG_COPYBIT, "incompatible texture environment");
+ return false;
+ }
+
+ bool blending = false;
+ if ((enables & GGL_ENABLE_BLENDING)
+ && !(c->rasterizer.state.blend.src == GL_ONE
+ && c->rasterizer.state.blend.dst == GL_ZERO)) {
+ // Blending is OK if it is
+ // the exact kind of blending that the copybits hardware supports.
+ // Note: The hardware only supports
+ // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA,
+ // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA.
+ // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case,
+ // because the performance is worth it, even if the results are
+ // not correct.
+ if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA
+ || c->rasterizer.state.blend.src == GL_ONE)
+ && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA
+ && c->rasterizer.state.blend.alpha_separate == 0)) {
+ // Incompatible blend mode.
+ LOGD_IF(DEBUG_COPYBIT, "incompatible blend mode");
+ return false;
+ }
+ blending = true;
+ } else {
+ // No blending is OK if we are not using alpha.
+ if (srcTextureHasAlpha || planeAlpha != 255) {
+ // Incompatible alpha
+ LOGD_IF(DEBUG_COPYBIT, "incompatible alpha");
+ return false;
+ }
+ }
+
+ if (srcTextureHasAlpha && planeAlpha != 255) {
+ // Can't do two types of alpha at once.
+ LOGD_IF(DEBUG_COPYBIT, "src alpha and plane alpha");
+ return false;
+ }
+
+ // LOGW("calling copybits");
+
+ copybit_device_t* copybit = c->copybits.blitEngine;
+
+ copybit_image_t dst;
+ buffer_handle_t target_hnd = c->copybits.drawSurfaceBuffer;
+ textureToCopyBitImage(&cbSurface, target_hnd, &dst);
+ copybit_rect_t drect = {x, y, x+w, y+h};
+
+ copybit_image_t src;
+ buffer_handle_t source_hnd = textureObject->buffer->handle;
+ textureToCopyBitImage(&textureObject->surface, source_hnd, &src);
+ copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr };
+
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha);
+ copybit->set_parameter(copybit, COPYBIT_DITHER,
+ (enables & GGL_ENABLE_DITHER) ? COPYBIT_ENABLE : COPYBIT_DISABLE);
+
+ clipRectRegion it(c);
+ status_t err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it);
+ if (err != NO_ERROR) {
+ c->textures.tmu[0].texture->try_copybit = false;
+ }
+ return err == NO_ERROR ? true : false;
+}
+
+/*
+ * Try to draw a triangle fan with copybit, return false if we fail.
+ */
+bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count)
+{
+ if (!checkContext(c)) {
+ return false;
+ }
+
+ // FIXME: we should handle culling here
+ c->arrays.compileElements(c, c->vc.vBuffer, 0, 4);
+
+ // we detect if we're dealing with a rectangle, by comparing the
+ // rectangles {v0,v2} and {v1,v3} which should be identical.
+
+ // NOTE: we should check that the rectangle is window aligned, however
+ // if we do that, the optimization won't be taken in a lot of cases.
+ // Since this code is intended to be used with SurfaceFlinger only,
+ // so it's okay...
+
+ const vec4_t& v0 = c->vc.vBuffer[0].window;
+ const vec4_t& v1 = c->vc.vBuffer[1].window;
+ const vec4_t& v2 = c->vc.vBuffer[2].window;
+ const vec4_t& v3 = c->vc.vBuffer[3].window;
+ int l = min(v0.x, v2.x);
+ int b = min(v0.y, v2.y);
+ int r = max(v0.x, v2.x);
+ int t = max(v0.y, v2.y);
+ if ((l != min(v1.x, v3.x)) || (b != min(v1.y, v3.y)) ||
+ (r != max(v1.x, v3.x)) || (t != max(v1.y, v3.y))) {
+ LOGD_IF(DEBUG_COPYBIT, "geometry not a rectangle");
+ return false;
+ }
+
+ // fetch and transform texture coordinates
+ // NOTE: maybe it would be better to have a "compileElementsAll" method
+ // that would ensure all vertex data are fetched and transformed
+ const transform_t& tr = c->transforms.texture[0].transform;
+ for (size_t i=0 ; i<4 ; i++) {
+ const GLubyte* tp = c->arrays.texture[0].element(i);
+ vertex_t* const v = &c->vc.vBuffer[i];
+ c->arrays.texture[0].fetch(c, v->texture[0].v, tp);
+ // FIXME: we should bail if q!=1
+ c->arrays.tex_transform[0](&tr, &v->texture[0], &v->texture[0]);
+ }
+
+ const vec4_t& t0 = c->vc.vBuffer[0].texture[0];
+ const vec4_t& t1 = c->vc.vBuffer[1].texture[0];
+ const vec4_t& t2 = c->vc.vBuffer[2].texture[0];
+ const vec4_t& t3 = c->vc.vBuffer[3].texture[0];
+ int txl = min(t0.x, t2.x);
+ int txb = min(t0.y, t2.y);
+ int txr = max(t0.x, t2.x);
+ int txt = max(t0.y, t2.y);
+ if ((txl != min(t1.x, t3.x)) || (txb != min(t1.y, t3.y)) ||
+ (txr != max(t1.x, t3.x)) || (txt != max(t1.y, t3.y))) {
+ LOGD_IF(DEBUG_COPYBIT, "texcoord not a rectangle");
+ return false;
+ }
+ if ((txl != 0) || (txb != 0) ||
+ (txr != FIXED_ONE) || (txt != FIXED_ONE)) {
+ // we could probably handle this case, if we wanted to
+ LOGD_IF(DEBUG_COPYBIT, "texture is cropped: %08x,%08x,%08x,%08x",
+ txl, txb, txr, txt);
+ return false;
+ }
+
+ // at this point, we know we are dealing with a rectangle, so we
+ // only need to consider 3 vertices for computing the jacobians
+
+ const int dx01 = v1.x - v0.x;
+ const int dx02 = v2.x - v0.x;
+ const int dy01 = v1.y - v0.y;
+ const int dy02 = v2.y - v0.y;
+ const int ds01 = t1.S - t0.S;
+ const int ds02 = t2.S - t0.S;
+ const int dt01 = t1.T - t0.T;
+ const int dt02 = t2.T - t0.T;
+ const int area = dx01*dy02 - dy01*dx02;
+ int dsdx, dsdy, dtdx, dtdy;
+ if (area >= 0) {
+ dsdx = ds01*dy02 - ds02*dy01;
+ dtdx = dt01*dy02 - dt02*dy01;
+ dsdy = ds02*dx01 - ds01*dx02;
+ dtdy = dt02*dx01 - dt01*dx02;
+ } else {
+ dsdx = ds02*dy01 - ds01*dy02;
+ dtdx = dt02*dy01 - dt01*dy02;
+ dsdy = ds01*dx02 - ds02*dx01;
+ dtdy = dt01*dx02 - dt02*dx01;
+ }
+
+ // here we rely on the fact that we know the transform is
+ // a rigid-body transform AND that it can only rotate in 90 degrees
+ // increments
+
+ int transform = 0;
+ if (dsdx == 0) {
+ // 90 deg rotation case
+ // [ 0 dtdx ]
+ // [ dsdx 0 ]
+ transform |= COPYBIT_TRANSFORM_ROT_90;
+ // FIXME: not sure if FLIP_H and FLIP_V shouldn't be inverted
+ if (dtdx > 0)
+ transform |= COPYBIT_TRANSFORM_FLIP_H;
+ if (dsdy < 0)
+ transform |= COPYBIT_TRANSFORM_FLIP_V;
+ } else {
+ // [ dsdx 0 ]
+ // [ 0 dtdy ]
+ if (dsdx < 0)
+ transform |= COPYBIT_TRANSFORM_FLIP_H;
+ if (dtdy < 0)
+ transform |= COPYBIT_TRANSFORM_FLIP_V;
+ }
+
+ //LOGD("l=%d, b=%d, w=%d, h=%d, tr=%d", x, y, w, h, transform);
+ //LOGD("A=%f\tB=%f\nC=%f\tD=%f",
+ // dsdx/65536.0, dtdx/65536.0, dsdy/65536.0, dtdy/65536.0);
+
+ int x = l >> 4;
+ int y = b >> 4;
+ int w = (r-l) >> 4;
+ int h = (t-b) >> 4;
+ texture_unit_t& u(c->textures.tmu[0]);
+ EGLTextureObject* textureObject = u.texture;
+ GLint tWidth = textureObject->surface.width;
+ GLint tHeight = textureObject->surface.height;
+ GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight};
+ const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
+ y = cbSurface.height - (y + h);
+ return copybit(x, y, w, h, textureObject, crop_rect, transform, c);
+}
+
+/*
+ * Try to drawTexiOESWithCopybit, return false if we fail.
+ */
+
+bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
+ GLint w, GLint h, ogles_context_t* c)
+{
+ // quickly process empty rects
+ if ((w|h) <= 0) {
+ return true;
+ }
+ if (!checkContext(c)) {
+ return false;
+ }
+ texture_unit_t& u(c->textures.tmu[0]);
+ EGLTextureObject* textureObject = u.texture;
+ return copybit(x, y, w, h, textureObject, textureObject->crop_rect, 0, c);
+}
+
+} // namespace android
+
diff --git a/opengl/libagl/copybit.h b/opengl/libagl/copybit.h
new file mode 100644
index 0000000..b8b5afd
--- /dev/null
+++ b/opengl/libagl/copybit.h
@@ -0,0 +1,75 @@
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_OPENGLES_COPYBIT_H
+#define ANDROID_OPENGLES_COPYBIT_H
+
+#include <stdlib.h>
+
+#include <GLES/gl.h>
+
+#include "TextureObjectManager.h"
+namespace android {
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+
+bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z,
+ GLint w, GLint h, ogles_context_t* c);
+
+bool drawTriangleFanWithCopybit_impl(ogles_context_t* c, GLint first,
+ GLsizei count);
+
+inline bool copybitQuickCheckContext(ogles_context_t* c) {
+ return c->copybits.drawSurfaceBuffer != 0
+ && c->rasterizer.state.enabled_tmu == 1
+ && c->textures.tmu[0].texture->try_copybit;
+}
+
+/*
+ * Tries to draw a drawTexiOES using copybit hardware.
+ * Returns true if successful.
+ */
+inline bool drawTexiOESWithCopybit(GLint x, GLint y, GLint z,
+ GLint w, GLint h, ogles_context_t* c) {
+ if (!copybitQuickCheckContext(c)) {
+ return false;
+ }
+
+ return drawTexiOESWithCopybit_impl(x, y, z, w, h, c);
+}
+
+/*
+ * Tries to draw a triangle fan using copybit hardware.
+ * Returns true if successful.
+ */
+inline bool drawTriangleFanWithCopybit(ogles_context_t* c, GLint first,
+ GLsizei count) {
+ /*
+ * We are looking for the glDrawArrays call made by SurfaceFlinger.
+ */
+
+ if ((count!=4) || first || !copybitQuickCheckContext(c))
+ return false;
+
+ return drawTriangleFanWithCopybit_impl(c, first, count);
+}
+
+
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
+} // namespace android
+
+#endif // ANDROID_OPENGLES_COPYBIT_H
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 4461567..9c0f7fd 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -15,8 +15,6 @@
** limitations under the License.
*/
-#define LOG_TAG "EGL"
-
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
@@ -41,6 +39,10 @@
#include <pixelflinger/format.h>
#include <pixelflinger/pixelflinger.h>
+#include <private/ui/android_natives_priv.h>
+
+#include <hardware/copybit.h>
+
#include "context.h"
#include "state.h"
#include "texture.h"
@@ -89,9 +91,9 @@ static GLint getError() {
struct egl_display_t
{
egl_display_t() : type(0), initialized(0) { }
-
+
static egl_display_t& get_display(EGLDisplay dpy);
-
+
static EGLBoolean is_valid(EGLDisplay dpy) {
return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE;
}
@@ -139,19 +141,23 @@ struct egl_surface_t
egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat);
virtual ~egl_surface_t();
- virtual bool isValid() const = 0;
-
+ bool isValid() const;
+ virtual bool initCheck() const = 0;
+
virtual EGLBoolean bindDrawSurface(ogles_context_t* gl) = 0;
virtual EGLBoolean bindReadSurface(ogles_context_t* gl) = 0;
+ virtual EGLBoolean connect() { return EGL_TRUE; }
+ virtual void disconnect() {}
virtual EGLint getWidth() const = 0;
virtual EGLint getHeight() const = 0;
- virtual void* getBits() const = 0;
virtual EGLint getHorizontalResolution() const;
virtual EGLint getVerticalResolution() const;
virtual EGLint getRefreshRate() const;
virtual EGLint getSwapBehavior() const;
virtual EGLBoolean swapBuffers();
+ virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
+ virtual EGLClientBuffer getRenderBuffer() const;
protected:
GGLSurface depth;
};
@@ -170,6 +176,11 @@ egl_surface_t::~egl_surface_t()
magic = 0;
free(depth.data);
}
+bool egl_surface_t::isValid() const {
+ LOGE_IF(magic != MAGIC, "invalid EGLSurface (%p)", this);
+ return magic == MAGIC;
+}
+
EGLBoolean egl_surface_t::swapBuffers() {
return EGL_FALSE;
}
@@ -185,119 +196,507 @@ EGLint egl_surface_t::getRefreshRate() const {
EGLint egl_surface_t::getSwapBehavior() const {
return EGL_BUFFER_PRESERVED;
}
+EGLBoolean egl_surface_t::setSwapRectangle(
+ EGLint l, EGLint t, EGLint w, EGLint h)
+{
+ return EGL_FALSE;
+}
+EGLClientBuffer egl_surface_t::getRenderBuffer() const {
+ return 0;
+}
// ----------------------------------------------------------------------------
-struct egl_window_surface_t : public egl_surface_t
+struct egl_window_surface_v2_t : public egl_surface_t
{
- egl_window_surface_t(
+ egl_window_surface_v2_t(
EGLDisplay dpy, EGLConfig config,
int32_t depthFormat,
- egl_native_window_t* window);
+ android_native_window_t* window);
- ~egl_window_surface_t();
+ ~egl_window_surface_v2_t();
- virtual bool isValid() const { return nativeWindow->magic == 0x600913; }
+ virtual bool initCheck() const { return true; } // TODO: report failure if ctor fails
virtual EGLBoolean swapBuffers();
virtual EGLBoolean bindDrawSurface(ogles_context_t* gl);
virtual EGLBoolean bindReadSurface(ogles_context_t* gl);
- virtual EGLint getWidth() const { return nativeWindow->width; }
- virtual EGLint getHeight() const { return nativeWindow->height; }
- virtual void* getBits() const;
+ virtual EGLBoolean connect();
+ virtual void disconnect();
+ virtual EGLint getWidth() const { return width; }
+ virtual EGLint getHeight() const { return height; }
virtual EGLint getHorizontalResolution() const;
virtual EGLint getVerticalResolution() const;
virtual EGLint getRefreshRate() const;
virtual EGLint getSwapBehavior() const;
+ virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
+ virtual EGLClientBuffer getRenderBuffer() const;
+
private:
- egl_native_window_t* nativeWindow;
+ status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
+ status_t unlock(android_native_buffer_t* buf);
+ android_native_window_t* nativeWindow;
+ android_native_buffer_t* buffer;
+ android_native_buffer_t* previousBuffer;
+ gralloc_module_t const* module;
+ copybit_device_t* blitengine;
+ int width;
+ int height;
+ void* bits;
+ GGLFormat const* pixelFormatTable;
+
+ struct Rect {
+ inline Rect() { };
+ inline Rect(int32_t w, int32_t h)
+ : left(0), top(0), right(w), bottom(h) { }
+ inline Rect(int32_t l, int32_t t, int32_t r, int32_t b)
+ : left(l), top(t), right(r), bottom(b) { }
+ Rect& andSelf(const Rect& r) {
+ left = max(left, r.left);
+ top = max(top, r.top);
+ right = min(right, r.right);
+ bottom = min(bottom, r.bottom);
+ return *this;
+ }
+ bool isEmpty() const {
+ return (left>=right || top>=bottom);
+ }
+ void dump(char const* what) {
+ LOGD("%s { %5d, %5d, w=%5d, h=%5d }",
+ what, left, top, right-left, bottom-top);
+ }
+
+ int32_t left;
+ int32_t top;
+ int32_t right;
+ int32_t bottom;
+ };
+
+ struct Region {
+ inline Region() : count(0) { }
+ typedef Rect const* const_iterator;
+ const_iterator begin() const { return storage; }
+ const_iterator end() const { return storage+count; }
+ static Region subtract(const Rect& lhs, const Rect& rhs) {
+ Region reg;
+ Rect* storage = reg.storage;
+ if (!lhs.isEmpty()) {
+ if (lhs.top < rhs.top) { // top rect
+ storage->left = lhs.left;
+ storage->top = lhs.top;
+ storage->right = lhs.right;
+ storage->bottom = rhs.top;
+ storage++;
+ }
+ const int32_t top = max(lhs.top, rhs.top);
+ const int32_t bot = min(lhs.bottom, rhs.bottom);
+ if (top < bot) {
+ if (lhs.left < rhs.left) { // left-side rect
+ storage->left = lhs.left;
+ storage->top = top;
+ storage->right = rhs.left;
+ storage->bottom = bot;
+ storage++;
+ }
+ if (lhs.right > rhs.right) { // right-side rect
+ storage->left = rhs.right;
+ storage->top = top;
+ storage->right = lhs.right;
+ storage->bottom = bot;
+ storage++;
+ }
+ }
+ if (lhs.bottom > rhs.bottom) { // bottom rect
+ storage->left = lhs.left;
+ storage->top = rhs.bottom;
+ storage->right = lhs.right;
+ storage->bottom = lhs.bottom;
+ storage++;
+ }
+ reg.count = storage - reg.storage;
+ }
+ return reg;
+ }
+ bool isEmpty() const {
+ return count<=0;
+ }
+ private:
+ Rect storage[4];
+ ssize_t count;
+ };
+
+ struct region_iterator : public copybit_region_t {
+ region_iterator(const Region& region)
+ : b(region.begin()), e(region.end()) {
+ this->next = iterate;
+ }
+ private:
+ static int iterate(copybit_region_t const * self, copybit_rect_t* rect) {
+ region_iterator const* me = static_cast<region_iterator const*>(self);
+ if (me->b != me->e) {
+ *reinterpret_cast<Rect*>(rect) = *me->b++;
+ return 1;
+ }
+ return 0;
+ }
+ mutable Region::const_iterator b;
+ Region::const_iterator const e;
+ };
+
+ void copyBlt(
+ android_native_buffer_t* dst, void* dst_vaddr,
+ android_native_buffer_t* src, void const* src_vaddr,
+ const Region& clip);
+
+ Rect dirtyRegion;
+ Rect oldDirtyRegion;
};
-egl_window_surface_t::egl_window_surface_t(EGLDisplay dpy,
+egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy,
EGLConfig config,
int32_t depthFormat,
- egl_native_window_t* window)
- : egl_surface_t(dpy, config, depthFormat), nativeWindow(window)
+ android_native_window_t* window)
+ : egl_surface_t(dpy, config, depthFormat),
+ nativeWindow(window), buffer(0), previousBuffer(0), module(0),
+ blitengine(0), bits(NULL)
{
- if (depthFormat) {
- depth.width = window->width;
- depth.height = window->height;
+ hw_module_t const* pModule;
+ hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule);
+ module = reinterpret_cast<gralloc_module_t const*>(pModule);
+
+ if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &pModule) == 0) {
+ copybit_open(pModule, &blitengine);
+ }
+
+ pixelFormatTable = gglGetPixelFormatTable();
+
+ // keep a reference on the window
+ nativeWindow->common.incRef(&nativeWindow->common);
+ nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width);
+ nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &height);
+}
+
+egl_window_surface_v2_t::~egl_window_surface_v2_t() {
+ if (buffer) {
+ buffer->common.decRef(&buffer->common);
+ }
+ if (previousBuffer) {
+ previousBuffer->common.decRef(&previousBuffer->common);
+ }
+ nativeWindow->common.decRef(&nativeWindow->common);
+ if (blitengine) {
+ copybit_close(blitengine);
+ }
+}
+
+EGLBoolean egl_window_surface_v2_t::connect()
+{
+ // we're intending to do software rendering
+ native_window_set_usage(nativeWindow,
+ GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+
+ // dequeue a buffer
+ if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) != NO_ERROR) {
+ return setError(EGL_BAD_ALLOC, EGL_FALSE);
+ }
+
+ // allocate a corresponding depth-buffer
+ width = buffer->width;
+ height = buffer->height;
+ if (depth.format) {
+ depth.width = width;
+ depth.height = height;
depth.stride = depth.width; // use the width here
depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
if (depth.data == 0) {
- setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
- return;
+ return setError(EGL_BAD_ALLOC, EGL_FALSE);
}
}
- nativeWindow->incRef(nativeWindow);
+
+ // keep a reference on the buffer
+ buffer->common.incRef(&buffer->common);
+
+ // Lock the buffer
+ nativeWindow->lockBuffer(nativeWindow, buffer);
+ // pin the buffer down
+ if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+ LOGE("connect() failed to lock buffer %p (%ux%u)",
+ buffer, buffer->width, buffer->height);
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ // FIXME: we should make sure we're not accessing the buffer anymore
+ }
+ return EGL_TRUE;
+}
+
+void egl_window_surface_v2_t::disconnect()
+{
+ if (buffer && bits) {
+ bits = NULL;
+ unlock(buffer);
+ }
+ // enqueue the last frame
+ nativeWindow->queueBuffer(nativeWindow, buffer);
+ if (buffer) {
+ buffer->common.decRef(&buffer->common);
+ buffer = 0;
+ }
+ if (previousBuffer) {
+ previousBuffer->common.decRef(&previousBuffer->common);
+ previousBuffer = 0;
+ }
+}
+
+status_t egl_window_surface_v2_t::lock(
+ android_native_buffer_t* buf, int usage, void** vaddr)
+{
+ int err = module->lock(module, buf->handle,
+ usage, 0, 0, buf->width, buf->height, vaddr);
+ return err;
}
-egl_window_surface_t::~egl_window_surface_t() {
- nativeWindow->decRef(nativeWindow);
+
+status_t egl_window_surface_v2_t::unlock(android_native_buffer_t* buf)
+{
+ if (!buf) return BAD_VALUE;
+ int err = module->unlock(module, buf->handle);
+ return err;
+}
+
+void egl_window_surface_v2_t::copyBlt(
+ android_native_buffer_t* dst, void* dst_vaddr,
+ android_native_buffer_t* src, void const* src_vaddr,
+ const Region& clip)
+{
+ // FIXME: use copybit if possible
+ // NOTE: dst and src must be the same format
+
+ status_t err = NO_ERROR;
+ copybit_device_t* const copybit = blitengine;
+ if (copybit) {
+ copybit_image_t simg;
+ simg.w = src->width;
+ simg.h = src->height;
+ simg.format = src->format;
+ simg.handle = const_cast<native_handle_t*>(src->handle);
+
+ copybit_image_t dimg;
+ dimg.w = dst->width;
+ dimg.h = dst->height;
+ dimg.format = dst->format;
+ dimg.handle = const_cast<native_handle_t*>(dst->handle);
+
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 255);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE);
+ region_iterator it(clip);
+ err = copybit->blit(copybit, &dimg, &simg, &it);
+ if (err != NO_ERROR) {
+ LOGE("copybit failed (%s)", strerror(err));
+ }
+ }
+
+ if (!copybit || err) {
+ Region::const_iterator cur = clip.begin();
+ Region::const_iterator end = clip.end();
+
+ const size_t bpp = pixelFormatTable[src->format].size;
+ const size_t dbpr = dst->stride * bpp;
+ const size_t sbpr = src->stride * bpp;
+
+ uint8_t const * const src_bits = (uint8_t const *)src_vaddr;
+ uint8_t * const dst_bits = (uint8_t *)dst_vaddr;
+
+ while (cur != end) {
+ const Rect& r(*cur++);
+ ssize_t w = r.right - r.left;
+ ssize_t h = r.bottom - r.top;
+ if (w <= 0 || h<=0) continue;
+ size_t size = w * bpp;
+ uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp;
+ uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp;
+ if (dbpr==sbpr && size==sbpr) {
+ size *= h;
+ h = 1;
+ }
+ do {
+ memcpy(d, s, size);
+ d += dbpr;
+ s += sbpr;
+ } while (--h > 0);
+ }
+ }
}
-EGLBoolean egl_window_surface_t::swapBuffers()
+EGLBoolean egl_window_surface_v2_t::swapBuffers()
{
- uint32_t flags = nativeWindow->swapBuffers(nativeWindow);
- if (flags & EGL_NATIVES_FLAG_SIZE_CHANGED) {
+ if (!buffer) {
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ }
+
+ /*
+ * Handle eglSetSwapRectangleANDROID()
+ * We copyback from the front buffer
+ */
+ if (!dirtyRegion.isEmpty()) {
+ dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
+ if (previousBuffer) {
+ const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
+ if (!copyBack.isEmpty()) {
+ void* prevBits;
+ if (lock(previousBuffer,
+ GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {
+ // copy from previousBuffer to buffer
+ copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);
+ unlock(previousBuffer);
+ }
+ }
+ }
+ oldDirtyRegion = dirtyRegion;
+ }
+
+ if (previousBuffer) {
+ previousBuffer->common.decRef(&previousBuffer->common);
+ previousBuffer = 0;
+ }
+
+ unlock(buffer);
+ previousBuffer = buffer;
+ nativeWindow->queueBuffer(nativeWindow, buffer);
+ buffer = 0;
+
+ // dequeue a new buffer
+ nativeWindow->dequeueBuffer(nativeWindow, &buffer);
+
+ // TODO: lockBuffer should rather be executed when the very first
+ // direct rendering occurs.
+ nativeWindow->lockBuffer(nativeWindow, buffer);
+
+ // reallocate the depth-buffer if needed
+ if ((width != buffer->width) || (height != buffer->height)) {
// TODO: we probably should reset the swap rect here
// if the window size has changed
+ width = buffer->width;
+ height = buffer->height;
if (depth.data) {
free(depth.data);
- depth.width = nativeWindow->width;
- depth.height = nativeWindow->height;
- depth.stride = nativeWindow->stride;
+ depth.width = width;
+ depth.height = height;
+ depth.stride = buffer->stride;
depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
if (depth.data == 0) {
- setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
+ setError(EGL_BAD_ALLOC, EGL_FALSE);
return EGL_FALSE;
}
}
}
+
+ // keep a reference on the buffer
+ buffer->common.incRef(&buffer->common);
+
+ // finally pin the buffer down
+ if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+ LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+ buffer, buffer->width, buffer->height);
+ return setError(EGL_BAD_ACCESS, EGL_FALSE);
+ // FIXME: we should make sure we're not accessing the buffer anymore
+ }
+
return EGL_TRUE;
}
-EGLBoolean egl_window_surface_t::bindDrawSurface(ogles_context_t* gl)
+EGLBoolean egl_window_surface_v2_t::setSwapRectangle(
+ EGLint l, EGLint t, EGLint w, EGLint h)
+{
+ dirtyRegion = Rect(l, t, l+w, t+h);
+ return EGL_TRUE;
+}
+
+EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const
+{
+ return buffer;
+}
+
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+
+static bool supportedCopybitsDestinationFormat(int format) {
+ // Hardware supported
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBA_4444:
+ case HAL_PIXEL_FORMAT_RGBA_5551:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ return true;
+ }
+ return false;
+}
+#endif
+
+EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
{
GGLSurface buffer;
buffer.version = sizeof(GGLSurface);
- buffer.width = nativeWindow->width;
- buffer.height = nativeWindow->height;
- buffer.stride = nativeWindow->stride;
- buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset;
- buffer.format = nativeWindow->format;
+ buffer.width = this->buffer->width;
+ buffer.height = this->buffer->height;
+ buffer.stride = this->buffer->stride;
+ buffer.data = (GGLubyte*)bits;
+ buffer.format = this->buffer->format;
gl->rasterizer.procs.colorBuffer(gl, &buffer);
if (depth.data != gl->rasterizer.state.buffers.depth.data)
gl->rasterizer.procs.depthBuffer(gl, &depth);
+
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ gl->copybits.drawSurfaceBuffer = 0;
+ if (gl->copybits.blitEngine != NULL) {
+ if (supportedCopybitsDestinationFormat(buffer.format)) {
+ buffer_handle_t handle = this->buffer->handle;
+ if (handle != NULL) {
+ gl->copybits.drawSurfaceBuffer = handle;
+ }
+ }
+ }
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
return EGL_TRUE;
}
-EGLBoolean egl_window_surface_t::bindReadSurface(ogles_context_t* gl)
+EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl)
{
GGLSurface buffer;
buffer.version = sizeof(GGLSurface);
- buffer.width = nativeWindow->width;
- buffer.height = nativeWindow->height;
- buffer.stride = nativeWindow->stride;
- buffer.data = (GGLubyte*)nativeWindow->base + nativeWindow->offset;
- buffer.format = nativeWindow->format;
+ buffer.width = this->buffer->width;
+ buffer.height = this->buffer->height;
+ buffer.stride = this->buffer->stride;
+ buffer.data = (GGLubyte*)bits; // FIXME: hopefully is is LOCKED!!!
+ buffer.format = this->buffer->format;
gl->rasterizer.procs.readBuffer(gl, &buffer);
return EGL_TRUE;
}
-void* egl_window_surface_t::getBits() const {
- return (GGLubyte*)nativeWindow->base + nativeWindow->offset;
-}
-EGLint egl_window_surface_t::getHorizontalResolution() const {
+EGLint egl_window_surface_v2_t::getHorizontalResolution() const {
return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
}
-EGLint egl_window_surface_t::getVerticalResolution() const {
+EGLint egl_window_surface_v2_t::getVerticalResolution() const {
return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f);
}
-EGLint egl_window_surface_t::getRefreshRate() const {
- return (nativeWindow->fps * EGL_DISPLAY_SCALING);
+EGLint egl_window_surface_v2_t::getRefreshRate() const {
+ return (60 * EGL_DISPLAY_SCALING); // FIXME
}
-EGLint egl_window_surface_t::getSwapBehavior() const {
- uint32_t flags = nativeWindow->flags;
- if (flags & EGL_NATIVES_FLAG_DESTROY_BACKBUFFER)
- return EGL_BUFFER_DESTROYED;
- return EGL_BUFFER_PRESERVED;
+EGLint egl_window_surface_v2_t::getSwapBehavior() const
+{
+ /*
+ * EGL_BUFFER_PRESERVED means that eglSwapBuffers() completely preserves
+ * the content of the swapped buffer.
+ *
+ * EGL_BUFFER_DESTROYED means that the content of the buffer is lost.
+ *
+ * However when ANDROID_swap_retcangle is supported, EGL_BUFFER_DESTROYED
+ * only applies to the area specified by eglSetSwapRectangleANDROID(), that
+ * is, everything outside of this area is preserved.
+ *
+ * This implementation of EGL assumes the later case.
+ *
+ */
+
+ return EGL_BUFFER_DESTROYED;
}
// ----------------------------------------------------------------------------
@@ -311,12 +710,11 @@ struct egl_pixmap_surface_t : public egl_surface_t
virtual ~egl_pixmap_surface_t() { }
- virtual bool isValid() const { return nativePixmap.version == sizeof(egl_native_pixmap_t); }
+ virtual bool initCheck() const { return !depth.format || depth.data!=0; }
virtual EGLBoolean bindDrawSurface(ogles_context_t* gl);
virtual EGLBoolean bindReadSurface(ogles_context_t* gl);
virtual EGLint getWidth() const { return nativePixmap.width; }
virtual EGLint getHeight() const { return nativePixmap.height; }
- virtual void* getBits() const { return nativePixmap.data; }
private:
egl_native_pixmap_t nativePixmap;
};
@@ -334,7 +732,6 @@ egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy,
depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2);
if (depth.data == 0) {
setError(EGL_BAD_ALLOC, EGL_NO_SURFACE);
- return;
}
}
}
@@ -347,7 +744,7 @@ EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl)
buffer.stride = nativePixmap.stride;
buffer.data = nativePixmap.data;
buffer.format = nativePixmap.format;
-
+
gl->rasterizer.procs.colorBuffer(gl, &buffer);
if (depth.data != gl->rasterizer.state.buffers.depth.data)
gl->rasterizer.procs.depthBuffer(gl, &depth);
@@ -376,12 +773,11 @@ struct egl_pbuffer_surface_t : public egl_surface_t
virtual ~egl_pbuffer_surface_t();
- virtual bool isValid() const { return pbuffer.data != 0; }
+ virtual bool initCheck() const { return pbuffer.data != 0; }
virtual EGLBoolean bindDrawSurface(ogles_context_t* gl);
virtual EGLBoolean bindReadSurface(ogles_context_t* gl);
virtual EGLint getWidth() const { return pbuffer.width; }
virtual EGLint getHeight() const { return pbuffer.height; }
- virtual void* getBits() const { return pbuffer.data; }
private:
GGLSurface pbuffer;
};
@@ -407,7 +803,7 @@ egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy,
pbuffer.stride = w;
pbuffer.data = (GGLubyte*)malloc(size);
pbuffer.format = f;
-
+
if (depthFormat) {
depth.width = pbuffer.width;
depth.height = pbuffer.height;
@@ -468,7 +864,13 @@ struct config_management_t {
static char const * const gVendorString = "Google Inc.";
static char const * const gVersionString = "1.2 Android Driver";
static char const * const gClientApiString = "OpenGL ES";
-static char const * const gExtensionsString = "";
+static char const * const gExtensionsString =
+ "EGL_KHR_image_base "
+ // "KHR_image_pixmap "
+ "EGL_ANDROID_image_native_buffer "
+ "EGL_ANDROID_swap_rectangle "
+ "EGL_ANDROID_get_render_buffer "
+ ;
// ----------------------------------------------------------------------------
@@ -496,6 +898,10 @@ static const extention_map_t gExtentionMap[] = {
(__eglMustCastToProperFunctionPointerType)&glDrawTexxvOES },
{ "glQueryMatrixxOES",
(__eglMustCastToProperFunctionPointerType)&glQueryMatrixxOES },
+ { "glEGLImageTargetTexture2DOES",
+ (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetTexture2DOES },
+ { "glEGLImageTargetRenderbufferStorageOES",
+ (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetRenderbufferStorageOES },
{ "glClipPlanef",
(__eglMustCastToProperFunctionPointerType)&glClipPlanef },
{ "glClipPlanex",
@@ -510,9 +916,17 @@ static const extention_map_t gExtentionMap[] = {
(__eglMustCastToProperFunctionPointerType)&glDeleteBuffers },
{ "glGenBuffers",
(__eglMustCastToProperFunctionPointerType)&glGenBuffers },
+ { "eglCreateImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+ { "eglSetSwapRectangleANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
+ { "eglGetRenderBufferANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
-/*
+/*
* In the lists below, attributes names MUST be sorted.
* Additionally, all configs must be sorted according to
* the EGL specification.
@@ -523,7 +937,7 @@ static config_pair_t const config_base_attribute_list[] = {
{ EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG },
{ EGL_LEVEL, 0 },
{ EGL_MAX_PBUFFER_HEIGHT, GGL_MAX_VIEWPORT_DIMS },
- { EGL_MAX_PBUFFER_PIXELS,
+ { EGL_MAX_PBUFFER_PIXELS,
GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS },
{ EGL_MAX_PBUFFER_WIDTH, GGL_MAX_VIEWPORT_DIMS },
{ EGL_NATIVE_RENDERABLE, EGL_TRUE },
@@ -660,9 +1074,9 @@ static int binarySearch(T const sortedArray[], int first, int last, EGLint key)
{
while (first <= last) {
int mid = (first + last) / 2;
- if (key > sortedArray[mid].key) {
+ if (key > sortedArray[mid].key) {
first = mid + 1;
- } else if (key < sortedArray[mid].key) {
+ } else if (key < sortedArray[mid].key) {
last = mid - 1;
} else {
return mid;
@@ -674,13 +1088,13 @@ static int binarySearch(T const sortedArray[], int first, int last, EGLint key)
static int isAttributeMatching(int i, EGLint attr, EGLint val)
{
// look for the attribute in all of our configs
- config_pair_t const* configFound = gConfigs[i].array;
+ config_pair_t const* configFound = gConfigs[i].array;
int index = binarySearch<config_pair_t>(
gConfigs[i].array,
0, gConfigs[i].size-1,
attr);
if (index < 0) {
- configFound = config_base_attribute_list;
+ configFound = config_base_attribute_list;
index = binarySearch<config_pair_t>(
config_base_attribute_list,
0, NELEM(config_base_attribute_list)-1,
@@ -787,6 +1201,11 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
if (!(surfaceType & EGL_WINDOW_BIT))
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+ if (static_cast<android_native_window_t*>(window)->common.magic !=
+ ANDROID_NATIVE_WINDOW_MAGIC) {
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
+
EGLint configID;
if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
return EGL_FALSE;
@@ -794,28 +1213,28 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
int32_t depthFormat;
int32_t pixelFormat;
switch(configID) {
- case 0:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ case 0:
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = 0;
break;
case 1:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 2:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = 0;
break;
case 3:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 4:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = 0;
break;
case 5:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
default:
@@ -828,11 +1247,11 @@ static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config,
//if (EGLint(info.format) != pixelFormat)
// return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
- egl_surface_t* surface =
- new egl_window_surface_t(dpy, config, depthFormat,
- static_cast<egl_native_window_t*>(window));
+ egl_surface_t* surface;
+ surface = new egl_window_surface_v2_t(dpy, config, depthFormat,
+ static_cast<android_native_window_t*>(window));
- if (!surface->isValid()) {
+ if (!surface->initCheck()) {
// there was a problem in the ctor, the error
// flag has been set.
delete surface;
@@ -856,6 +1275,11 @@ static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config,
if (!(surfaceType & EGL_PIXMAP_BIT))
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
+ if (static_cast<egl_native_pixmap_t*>(pixmap)->version !=
+ sizeof(egl_native_pixmap_t)) {
+ return setError(EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE);
+ }
+
EGLint configID;
if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
return EGL_FALSE;
@@ -863,28 +1287,28 @@ static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config,
int32_t depthFormat;
int32_t pixelFormat;
switch(configID) {
- case 0:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ case 0:
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = 0;
break;
case 1:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 2:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = 0;
break;
case 3:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 4:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = 0;
break;
case 5:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
default:
@@ -898,7 +1322,7 @@ static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config,
new egl_pixmap_surface_t(dpy, config, depthFormat,
static_cast<egl_native_pixmap_t*>(pixmap));
- if (!surface->isValid()) {
+ if (!surface->initCheck()) {
// there was a problem in the ctor, the error
// flag has been set.
delete surface;
@@ -916,10 +1340,10 @@ static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config,
EGLint surfaceType;
if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE)
return EGL_FALSE;
-
+
if (!(surfaceType & EGL_PBUFFER_BIT))
return setError(EGL_BAD_MATCH, EGL_NO_SURFACE);
-
+
EGLint configID;
if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE)
return EGL_FALSE;
@@ -927,28 +1351,28 @@ static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config,
int32_t depthFormat;
int32_t pixelFormat;
switch(configID) {
- case 0:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ case 0:
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = 0;
break;
case 1:
- pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
+ pixelFormat = GGL_PIXEL_FORMAT_RGB_565;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 2:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = 0;
break;
case 3:
- pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
+ pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
case 4:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = 0;
break;
case 5:
- pixelFormat = GGL_PIXEL_FORMAT_A_8;
+ pixelFormat = GGL_PIXEL_FORMAT_A_8;
depthFormat = GGL_PIXEL_FORMAT_Z_16;
break;
default:
@@ -966,7 +1390,7 @@ static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config,
egl_surface_t* surface =
new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat);
- if (!surface->isValid()) {
+ if (!surface->initCheck()) {
// there was a problem in the ctor, the error
// flag has been set.
delete surface;
@@ -1001,7 +1425,7 @@ EGLDisplay eglGetDisplay(NativeDisplayType display)
egl_display_t& d = egl_display_t::get_display(dpy);
d.type = display;
return dpy;
- }
+ }
return EGL_NO_DISPLAY;
}
@@ -1009,10 +1433,10 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
{
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
+
EGLBoolean res = EGL_TRUE;
egl_display_t& d = egl_display_t::get_display(dpy);
-
+
if (android_atomic_inc(&d.initialized) == 0) {
// initialize stuff here if needed
//pthread_mutex_lock(&gInitMutex);
@@ -1080,7 +1504,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
*num_config = 0;
return EGL_TRUE;
}
-
+
int numAttributes = 0;
int numConfigs = NELEM(gConfigs);
uint32_t possibleMatch = (1<<numConfigs)-1;
@@ -1161,7 +1585,7 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
{
return createWindowSurface(dpy, config, window, attrib_list);
}
-
+
EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
NativePixmapType pixmap,
const EGLint *attrib_list)
@@ -1174,17 +1598,22 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
{
return createPbufferSurface(dpy, config, attrib_list);
}
-
+
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface)
{
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (eglSurface != EGL_NO_SURFACE) {
egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) );
- if (surface->magic != egl_surface_t::MAGIC)
+ if (!surface->isValid())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (surface->dpy != dpy)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ if (surface->ctx) {
+ // FIXME: this surface is current check what the spec says
+ surface->disconnect();
+ surface->ctx = 0;
+ }
delete surface;
}
return EGL_TRUE;
@@ -1196,6 +1625,8 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface,
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface);
+ if (!surface->isValid())
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (surface->dpy != dpy)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
@@ -1244,7 +1675,7 @@ EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface,
*value = (wr * EGL_DISPLAY_SCALING) / hr;
} break;
case EGL_SWAP_BEHAVIOR:
- *value = surface->getSwapBehavior();
+ *value = surface->getSwapBehavior();
break;
default:
ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE);
@@ -1288,13 +1719,23 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (draw) {
egl_surface_t* s = (egl_surface_t*)draw;
+ if (!s->isValid())
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (s->dpy != dpy)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- // TODO: check that draw and read are compatible with the context
+ // TODO: check that draw is compatible with the context
+ }
+ if (read && read!=draw) {
+ egl_surface_t* s = (egl_surface_t*)read;
+ if (!s->isValid())
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ if (s->dpy != dpy)
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ // TODO: check that read is compatible with the context
}
EGLContext current_ctx = EGL_NO_CONTEXT;
-
+
if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT))
return setError(EGL_BAD_MATCH, EGL_FALSE);
@@ -1310,21 +1751,29 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
egl_surface_t* r = (egl_surface_t*)read;
if ((d && d->ctx && d->ctx != ctx) ||
(r && r->ctx && r->ctx != ctx)) {
- // once of the surface is bound to a context in another thread
+ // one of the surface is bound to a context in another thread
return setError(EGL_BAD_ACCESS, EGL_FALSE);
}
}
- // TODO: call connect / disconnect on the surface
-
ogles_context_t* gl = (ogles_context_t*)ctx;
if (makeCurrent(gl) == 0) {
if (ctx) {
egl_context_t* c = egl_context_t::context(ctx);
egl_surface_t* d = (egl_surface_t*)draw;
egl_surface_t* r = (egl_surface_t*)read;
- c->read = read;
+
+ if (c->draw) {
+ egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw);
+ s->disconnect();
+ }
+ if (c->read) {
+ // FIXME: unlock/disconnect the read surface too
+ }
+
c->draw = draw;
+ c->read = read;
+
if (c->flags & egl_context_t::NEVER_CURRENT) {
c->flags &= ~egl_context_t::NEVER_CURRENT;
GLint w = 0;
@@ -1338,10 +1787,14 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
ogles_scissor(gl, 0, 0, w, h);
}
if (d) {
+ if (d->connect() == EGL_FALSE) {
+ return EGL_FALSE;
+ }
d->ctx = ctx;
d->bindDrawSurface(gl);
}
if (r) {
+ // FIXME: lock/connect the read surface too
r->ctx = ctx;
r->bindReadSurface(gl);
}
@@ -1352,8 +1805,16 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
egl_context_t* c = egl_context_t::context(current_ctx);
egl_surface_t* d = (egl_surface_t*)c->draw;
egl_surface_t* r = (egl_surface_t*)c->read;
- if (d) d->ctx = EGL_NO_CONTEXT;
- if (r) r->ctx = EGL_NO_CONTEXT;
+ if (d) {
+ c->draw = 0;
+ d->ctx = EGL_NO_CONTEXT;
+ d->disconnect();
+ }
+ if (r) {
+ c->read = 0;
+ r->ctx = EGL_NO_CONTEXT;
+ // FIXME: unlock/disconnect the read surface too
+ }
}
}
return EGL_TRUE;
@@ -1425,8 +1886,10 @@ EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
if (egl_display_t::is_valid(dpy) == EGL_FALSE)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
-
+
egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+ if (!d->isValid())
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
if (d->dpy != dpy)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
@@ -1558,7 +2021,7 @@ EGLSurface eglCreatePbufferFromClientBuffer(
}
// ----------------------------------------------------------------------------
-// Android extensions
+// EGL_EGLEXT_VERSION 3
// ----------------------------------------------------------------------------
void (*eglGetProcAddress (const char *procname))()
@@ -1571,3 +2034,97 @@ void (*eglGetProcAddress (const char *procname))()
}
return NULL;
}
+
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
+ const EGLint *attrib_list)
+{
+ EGLBoolean result = EGL_FALSE;
+ return result;
+}
+
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
+{
+ EGLBoolean result = EGL_FALSE;
+ return result;
+}
+
+EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const EGLint *attrib_list)
+{
+ if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
+ return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
+ }
+ if (ctx != EGL_NO_CONTEXT) {
+ return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
+ }
+ if (target != EGL_NATIVE_BUFFER_ANDROID) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+ }
+
+ android_native_buffer_t* native_buffer = (android_native_buffer_t*)buffer;
+
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
+ return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+
+ if (native_buffer->common.version != sizeof(android_native_buffer_t))
+ return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+
+ native_buffer->common.incRef(&native_buffer->common);
+ return (EGLImageKHR)native_buffer;
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
+{
+ if (egl_display_t::is_valid(dpy) == EGL_FALSE) {
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ android_native_buffer_t* native_buffer = (android_native_buffer_t*)img;
+
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+
+ if (native_buffer->common.version != sizeof(android_native_buffer_t))
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+
+ native_buffer->common.decRef(&native_buffer->common);
+
+ return EGL_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
+ EGLint left, EGLint top, EGLint width, EGLint height)
+{
+ if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+ egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+ if (!d->isValid())
+ return setError(EGL_BAD_SURFACE, EGL_FALSE);
+ if (d->dpy != dpy)
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+
+ // post the surface
+ d->setSwapRectangle(left, top, width, height);
+
+ return EGL_TRUE;
+}
+
+EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
+{
+ if (egl_display_t::is_valid(dpy) == EGL_FALSE)
+ return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
+
+ egl_surface_t* d = static_cast<egl_surface_t*>(draw);
+ if (!d->isValid())
+ return setError(EGL_BAD_SURFACE, (EGLClientBuffer)0);
+ if (d->dpy != dpy)
+ return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
+
+ // post the surface
+ return d->getRenderBuffer();
+}
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 8ae32cc0f..f211bca 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -216,6 +216,8 @@ static inline void light_picker(ogles_context_t* c)
static inline void validate_light_mvi(ogles_context_t* c)
{
uint32_t en = c->lighting.enabledLights;
+ // Vector from object to viewer, in eye coordinates
+ const vec4_t eyeViewer = { 0, 0, 0x1000, 0 };
while (en) {
const int i = 31 - gglClz(en);
en &= ~(1<<i);
@@ -223,6 +225,9 @@ static inline void validate_light_mvi(ogles_context_t* c)
c->transforms.mvui.point4(&c->transforms.mvui,
&l.objPosition, &l.position);
vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
+ c->transforms.mvui.point4(&c->transforms.mvui,
+ &l.objViewer, &eyeViewer);
+ vnorm3(l.objViewer.v, l.objViewer.v);
}
}
@@ -379,9 +384,9 @@ void lightVertex(ogles_context_t* c, vertex_t* v)
// specular
if (ggl_unlikely(s && l.implicitSpecular.v[3])) {
vec4_t h;
- h.x = d.x;
- h.y = d.y;
- h.z = d.z + 0x10000;
+ h.x = d.x + l.objViewer.x;
+ h.y = d.y + l.objViewer.y;
+ h.z = d.z + l.objViewer.z;
vnorm3(h.v, h.v);
s = dot3(n.v, h.v);
s = (s<0) ? (twoSide?(-s):0) : s;
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index 0b68dc0..21ef50e 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -696,6 +696,8 @@ void ogles_viewport(ogles_context_t* c,
f[2] = 0; f[6] = 0; f[10] = A; f[14] = B;
f[3] = 0; f[7] = 0; f[11] = 0; f[15] = 1;
c->transforms.dirty |= transform_state_t::VIEWPORT;
+ if (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)
+ c->transforms.dirty |= transform_state_t::MVP;
}
// ----------------------------------------------------------------------------
diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp
index f164c02..769ec40 100644
--- a/opengl/libagl/primitives.cpp
+++ b/opengl/libagl/primitives.cpp
@@ -369,7 +369,7 @@ void compute_iterators_t::iterators0032(int32_t* it,
int32_t c0, int32_t c1, int32_t c2) const
{
int64_t it64[3];
- iterators0032(it, c0, c1, c2);
+ iterators0032(it64, c0, c1, c2);
it[0] = it64[0];
it[1] = it64[1];
it[2] = it64[2];
diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp
index 5cbabea..a59b3b0 100644
--- a/opengl/libagl/state.cpp
+++ b/opengl/libagl/state.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -28,12 +28,16 @@
#include "BufferObjectManager.h"
#include "TextureObjectManager.h"
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+#include <hardware/copybit.h>
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
namespace android {
// ----------------------------------------------------------------------------
static char const * const gVendorString = "Android";
-static char const * const gRendererString = "Android PixelFlinger 1.0";
+static char const * const gRendererString = "Android PixelFlinger 1.1";
static char const * const gVersionString = "OpenGL ES-CM 1.0";
static char const * const gExtensionsString =
"GL_OES_byte_coordinates " // OK
@@ -46,9 +50,9 @@ static char const * const gExtensionsString =
"GL_OES_query_matrix " // OK
// "GL_OES_point_size_array " // TODO
// "GL_OES_point_sprite " // TODO
+ "GL_OES_EGL_image " // OK
"GL_ARB_texture_compression " // OK
"GL_ARB_texture_non_power_of_two " // OK
- "GL_ANDROID_direct_texture " // OK
"GL_ANDROID_user_clip_plane " // OK
"GL_ANDROID_vertex_buffer_object " // OK
"GL_ANDROID_generate_mipmap " // OK
@@ -62,13 +66,13 @@ static char const * const gExtensionsString =
ogles_context_t *ogles_init(size_t extra)
{
void* const base = malloc(extra + sizeof(ogles_context_t) + 32);
- if (!base) return 0;
+ if (!base) return 0;
ogles_context_t *c =
(ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL);
memset(c, 0, sizeof(ogles_context_t));
ggl_init_context(&(c->rasterizer));
-
+
// XXX: this should be passed as an argument
sp<EGLSurfaceManager> smgr(new EGLSurfaceManager());
c->surfaceManager = smgr.get();
@@ -87,13 +91,42 @@ ogles_context_t *ogles_init(size_t extra)
c->rasterizer.base = base;
c->point.size = TRI_ONE;
c->line.width = TRI_ONE;
-
+
// in OpenGL, writing to the depth buffer is enabled by default.
c->rasterizer.procs.depthMask(c, 1);
-
+
// OpenGL enables dithering by default
c->rasterizer.procs.enable(c, GL_DITHER);
+ c->copybits.blitEngine = NULL;
+ c->copybits.minScale = 0;
+ c->copybits.maxScale = 0;
+ c->copybits.drawSurfaceBuffer = 0;
+
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ hw_module_t const* module;
+ if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
+ struct copybit_device_t* copyBits;
+ if (copybit_open(module, &copyBits) == 0) {
+ c->copybits.blitEngine = copyBits;
+ {
+ int minLim = copyBits->get(copyBits,
+ COPYBIT_MINIFICATION_LIMIT);
+ if (minLim != -EINVAL && minLim > 0) {
+ c->copybits.minScale = (1 << 16) / minLim;
+ }
+ }
+ {
+ int magLim = copyBits->get(copyBits,
+ COPYBIT_MAGNIFICATION_LIMIT);
+ if (magLim != -EINVAL && magLim > 0) {
+ c->copybits.maxScale = min(32*1024-1, magLim) << 16;
+ }
+ }
+ }
+ }
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
return c;
}
@@ -107,7 +140,12 @@ void ogles_uninit(ogles_context_t* c)
c->surfaceManager->decStrong(c);
c->bufferObjectManager->decStrong(c);
ggl_uninit_context(&(c->rasterizer));
- free(c->rasterizer.base);
+ free(c->rasterizer.base);
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ if (c->copybits.blitEngine != NULL) {
+ copybit_close((struct copybit_device_t*) c->copybits.blitEngine);
+ }
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
}
void _ogles_error(ogles_context_t* c, GLenum error)
@@ -188,7 +226,7 @@ static void enable_disable(ogles_context_t* c, GLenum cap, int enabled)
// these need to fall through into the rasterizer
c->rasterizer.procs.enableDisable(c, cap, enabled);
break;
-
+
case GL_MULTISAMPLE:
case GL_SAMPLE_ALPHA_TO_COVERAGE:
case GL_SAMPLE_ALPHA_TO_ONE:
@@ -281,7 +319,7 @@ void glHint(GLenum target, GLenum mode)
case GL_LINE_SMOOTH_HINT:
break;
case GL_POINT_SMOOTH_HINT:
- c->rasterizer.procs.enableDisable(c,
+ c->rasterizer.procs.enableDisable(c,
GGL_POINT_SMOOTH_NICE, mode==GL_NICEST);
break;
case GL_PERSPECTIVE_CORRECTION_HINT:
@@ -323,7 +361,7 @@ GLenum glGetError()
c->error = 0;
return ret;
}
-
+
if (c->rasterizer.error) {
const GLenum ret(c->rasterizer.error);
c->rasterizer.error = 0;
@@ -362,25 +400,25 @@ void glGetIntegerv(GLenum pname, GLint *params)
int index = c->rasterizer.state.buffers.color.format;
GGLFormat const * formats = gglGetPixelFormatTable();
params[0] = formats[index].ah - formats[index].al;
- break;
+ break;
}
case GL_RED_BITS: {
int index = c->rasterizer.state.buffers.color.format;
GGLFormat const * formats = gglGetPixelFormatTable();
params[0] = formats[index].rh - formats[index].rl;
- break;
+ break;
}
case GL_GREEN_BITS: {
int index = c->rasterizer.state.buffers.color.format;
GGLFormat const * formats = gglGetPixelFormatTable();
params[0] = formats[index].gh - formats[index].gl;
- break;
+ break;
}
case GL_BLUE_BITS: {
int index = c->rasterizer.state.buffers.color.format;
GGLFormat const * formats = gglGetPixelFormatTable();
params[0] = formats[index].bh - formats[index].bl;
- break;
+ break;
}
case GL_COMPRESSED_TEXTURE_FORMATS:
params[ 0] = GL_PALETTE4_RGB8_OES;
diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp
index 14a910c..90e6d29 100644
--- a/opengl/libagl/texture.cpp
+++ b/opengl/libagl/texture.cpp
@@ -2,16 +2,16 @@
**
** Copyright 2006, The Android Open Source Project
**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -23,6 +23,12 @@
#include "texture.h"
#include "TextureObjectManager.h"
+#include <private/ui/android_natives_priv.h>
+
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+#include "copybit.h"
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+
namespace android {
// ----------------------------------------------------------------------------
@@ -48,7 +54,7 @@ void ogles_init_texture(ogles_context_t* c)
// each context has a default named (0) texture (not shared)
c->textures.defaultTexture = new EGLTextureObject();
c->textures.defaultTexture->incStrong(c);
-
+
// bind the default texture to each texture unit
for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
bindTextureTmu(c, i, 0, c->textures.defaultTexture);
@@ -96,7 +102,7 @@ void validate_tmu(ogles_context_t* c, int i)
}
}
-void ogles_validate_texture_impl(ogles_context_t* c)
+void ogles_validate_texture(ogles_context_t* c)
{
for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
if (c->rasterizer.state.texture[i].enable)
@@ -110,6 +116,66 @@ void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) {
c->textures.tmu[tmu].dirty = flags;
}
+/*
+ * If the active textures are EGLImage, they need to be locked before
+ * they can be used.
+ *
+ * FIXME: code below is far from being optimal
+ *
+ */
+
+void ogles_lock_textures(ogles_context_t* c)
+{
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+ if (c->rasterizer.state.texture[i].enable) {
+ texture_unit_t& u(c->textures.tmu[i]);
+ android_native_buffer_t* native_buffer = u.texture->buffer;
+ if (native_buffer) {
+ c->rasterizer.procs.activeTexture(c, i);
+ hw_module_t const* pModule;
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
+ continue;
+
+ gralloc_module_t const* module =
+ reinterpret_cast<gralloc_module_t const*>(pModule);
+
+ void* vaddr;
+ int err = module->lock(module, native_buffer->handle,
+ GRALLOC_USAGE_SW_READ_OFTEN,
+ 0, 0, native_buffer->width, native_buffer->height,
+ &vaddr);
+
+ u.texture->setImageBits(vaddr);
+ c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
+ }
+ }
+ }
+}
+
+void ogles_unlock_textures(ogles_context_t* c)
+{
+ for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) {
+ if (c->rasterizer.state.texture[i].enable) {
+ texture_unit_t& u(c->textures.tmu[i]);
+ android_native_buffer_t* native_buffer = u.texture->buffer;
+ if (native_buffer) {
+ c->rasterizer.procs.activeTexture(c, i);
+ hw_module_t const* pModule;
+ if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &pModule))
+ continue;
+
+ gralloc_module_t const* module =
+ reinterpret_cast<gralloc_module_t const*>(pModule);
+
+ module->unlock(module, native_buffer->handle);
+ u.texture->setImageBits(NULL);
+ c->rasterizer.procs.bindTexture(c, &(u.texture->surface));
+ }
+ }
+ }
+ c->rasterizer.procs.activeTexture(c, c->textures.active);
+}
+
// ----------------------------------------------------------------------------
#if 0
#pragma mark -
@@ -255,7 +321,7 @@ sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
u.texture->decStrong(c);
if (name == 0) {
- // 0 is our local texture object, not shared with anyone.
+ // 0 is our local texture object, not shared with anyone.
// But it affects all bound TMUs immediately.
// (we need to invalidate all units bound to this texture object)
tex = c->textures.defaultTexture;
@@ -273,7 +339,7 @@ sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c)
u.texture = tex.get();
u.texture->incStrong(c);
u.name = name;
- invalidate_texture(c, active);
+ invalidate_texture(c, active);
return tex;
}
@@ -282,7 +348,7 @@ void bindTextureTmu(
{
if (tex.get() == c->textures.tmu[tmu].texture)
return;
-
+
// free the reference to the previously bound object
texture_unit_t& u(c->textures.tmu[tmu]);
if (u.texture)
@@ -310,7 +376,7 @@ int createTextureSurface(ogles_context_t* c,
if (formatIdx == 0) { // we don't know what to do with this
return GL_INVALID_OPERATION;
}
-
+
// figure out the size we need as well as the stride
const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]);
const int32_t align = c->textures.unpackAlignment-1;
@@ -343,6 +409,49 @@ int createTextureSurface(ogles_context_t* c,
return 0;
}
+static size_t dataSizePalette4(int numLevels, int width, int height, int format)
+{
+ int indexBits = 8;
+ int entrySize = 0;
+ switch (format) {
+ case GL_PALETTE4_RGB8_OES:
+ indexBits = 4;
+ /* FALLTHROUGH */
+ case GL_PALETTE8_RGB8_OES:
+ entrySize = 3;
+ break;
+
+ case GL_PALETTE4_RGBA8_OES:
+ indexBits = 4;
+ /* FALLTHROUGH */
+ case GL_PALETTE8_RGBA8_OES:
+ entrySize = 4;
+ break;
+
+ case GL_PALETTE4_R5_G6_B5_OES:
+ case GL_PALETTE4_RGBA4_OES:
+ case GL_PALETTE4_RGB5_A1_OES:
+ indexBits = 4;
+ /* FALLTHROUGH */
+ case GL_PALETTE8_R5_G6_B5_OES:
+ case GL_PALETTE8_RGBA4_OES:
+ case GL_PALETTE8_RGB5_A1_OES:
+ entrySize = 2;
+ break;
+ }
+
+ size_t size = (1 << indexBits) * entrySize; // palette size
+
+ for (int i=0 ; i< numLevels ; i++) {
+ int w = (width >> i) ? : 1;
+ int h = (height >> i) ? : 1;
+ int levelSize = h * ((w * indexBits) / 8) ? : 1;
+ size += levelSize;
+ }
+
+ return size;
+}
+
static void decodePalette4(const GLvoid *data, int level, int width, int height,
void *surface, int stride, int format)
@@ -377,6 +486,7 @@ static void decodePalette4(const GLvoid *data, int level, int width, int height,
}
const int paletteSize = (1 << indexBits) * entrySize;
+
uint8_t const* pixels = (uint8_t *)data + paletteSize;
for (int i=0 ; i<level ; i++) {
int w = (width >> i) ? : 1;
@@ -530,8 +640,8 @@ static void texParameterx(
ogles_error(c, GL_INVALID_ENUM);
return;
}
-
- EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
+
+ EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture;
switch (pname) {
case GL_TEXTURE_WRAP_S:
if ((param == GL_REPEAT) ||
@@ -581,12 +691,11 @@ invalid_enum:
}
-static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
+
+static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
ogles_context_t* c)
{
- // quickly reject empty rects
- if ((w|h) <= 0)
- return;
+ ogles_lock_textures(c);
const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
y = gglIntToFixed(cbSurface.height) - (y + h);
@@ -610,7 +719,7 @@ static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP);
u.dirty = 0xFF; // XXX: should be more subtle
- EGLTextureObject* textureObject = u.texture;
+ EGLTextureObject* textureObject = u.texture;
const GLint Ucr = textureObject->crop_rect[0] << 16;
const GLint Vcr = textureObject->crop_rect[1] << 16;
const GLint Wcr = textureObject->crop_rect[2] << 16;
@@ -641,11 +750,30 @@ static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
c->rasterizer.procs.disable(c, GGL_W_LERP);
c->rasterizer.procs.disable(c, GGL_AA);
c->rasterizer.procs.shadeModel(c, GL_FLAT);
- c->rasterizer.procs.recti(c,
+ c->rasterizer.procs.recti(c,
gglFixedToIntRound(x),
gglFixedToIntRound(y),
gglFixedToIntRound(x)+w,
gglFixedToIntRound(y)+h);
+
+ ogles_unlock_textures(c);
+}
+
+static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,
+ ogles_context_t* c)
+{
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ if (drawTexiOESWithCopybit(gglFixedToIntRound(x),
+ gglFixedToIntRound(y), gglFixedToIntRound(z),
+ gglFixedToIntRound(w), gglFixedToIntRound(h), c)) {
+ return;
+ }
+#else
+ // quickly reject empty rects
+ if ((w|h) <= 0)
+ return;
+#endif
+ drawTexxOESImp(x, y, z, w, h, c);
}
static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)
@@ -656,14 +784,21 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
// which is a lot faster.
if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) {
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ if (drawTexiOESWithCopybit(x, y, z, w, h, c)) {
+ return;
+ }
+#endif
const int tmu = 0;
texture_unit_t& u(c->textures.tmu[tmu]);
- EGLTextureObject* textureObject = u.texture;
+ EGLTextureObject* textureObject = u.texture;
const GLint Wcr = textureObject->crop_rect[2];
const GLint Hcr = textureObject->crop_rect[3];
if ((w == Wcr) && (h == -Hcr)) {
+#ifndef LIBAGL_USE_GRALLOC_COPYBITS
if ((w|h) <= 0) return; // quickly reject empty rects
+#endif
if (u.dirty) {
c->rasterizer.procs.activeTexture(c, tmu);
@@ -679,14 +814,14 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE);
u.dirty = 0xFF; // XXX: should be more subtle
c->rasterizer.procs.activeTexture(c, c->textures.active);
-
+
const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s;
y = cbSurface.height - (y + h);
const GLint Ucr = textureObject->crop_rect[0];
const GLint Vcr = textureObject->crop_rect[1];
const GLint s0 = Ucr - x;
const GLint t0 = (Vcr + Hcr) - y;
-
+
const GLuint tw = textureObject->surface.width;
const GLuint th = textureObject->surface.height;
if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) {
@@ -694,7 +829,9 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
// in this case, so we just use the slow case, which
// at least won't crash
goto slow_case;
- }
+ }
+
+ ogles_lock_textures(c);
c->rasterizer.procs.texCoord2i(c, s0, t0);
const uint32_t enables = c->rasterizer.state.enables;
@@ -706,12 +843,15 @@ static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_conte
c->rasterizer.procs.disable(c, GGL_AA);
c->rasterizer.procs.shadeModel(c, GL_FLAT);
c->rasterizer.procs.recti(c, x, y, x+w, y+h);
+
+ ogles_unlock_textures(c);
+
return;
}
}
slow_case:
- drawTexxOES(
+ drawTexxOESImp(
gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z),
gglIntToFixed(w), gglIntToFixed(h),
c);
@@ -749,7 +889,7 @@ void glBindTexture(GLenum target, GLuint texture)
}
// Bind or create a texture
- sp<EGLTextureObject> tex;
+ sp<EGLTextureObject> tex;
if (texture == 0) {
// 0 is our local texture object
tex = c->textures.defaultTexture;
@@ -837,7 +977,7 @@ void glPixelStorei(GLenum pname, GLint param)
if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) {
ogles_error(c, GL_INVALID_ENUM);
return;
- }
+ }
if ((param<=0 || param>8) || (param & (param-1))) {
ogles_error(c, GL_INVALID_VALUE);
return;
@@ -952,7 +1092,7 @@ void glCompressedTexImage2D(
}
// "uncompress" the texture since pixelflinger doesn't support
- // any compressed texture format natively.
+ // any compressed texture format natively.
GLenum format;
GLenum type;
switch (internalformat) {
@@ -995,6 +1135,12 @@ void glCompressedTexImage2D(
GGLSurface* surface;
// all mipmap levels are specified at once.
const int numLevels = level<0 ? -level : 1;
+
+ if (dataSizePalette4(numLevels, width, height, format) > imageSize) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+
for (int i=0 ; i<numLevels ; i++) {
int lod_w = (width >> i) ? : 1;
int lod_h = (height >> i) ? : 1;
@@ -1016,7 +1162,7 @@ void glTexImage2D(
GLenum format, GLenum type, const GLvoid *pixels)
{
ogles_context_t* c = ogles_context_t::get();
- if (target != GL_TEXTURE_2D && target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
+ if (target != GL_TEXTURE_2D) {
ogles_error(c, GL_INVALID_ENUM);
return;
}
@@ -1024,7 +1170,7 @@ void glTexImage2D(
ogles_error(c, GL_INVALID_VALUE);
return;
}
- if (format != internalformat) {
+ if (format != (GLenum)internalformat) {
ogles_error(c, GL_INVALID_OPERATION);
return;
}
@@ -1034,16 +1180,10 @@ void glTexImage2D(
int32_t size = 0;
GGLSurface* surface = 0;
- if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
- int error = createTextureSurface(c, &surface, &size,
- level, format, type, width, height);
- if (error) {
- ogles_error(c, error);
- return;
- }
- } else if (pixels == 0 || level != 0) {
- // pixel can't be null for direct texture
- ogles_error(c, GL_INVALID_OPERATION);
+ int error = createTextureSurface(c, &surface, &size,
+ level, format, type, width, height);
+ if (error) {
+ ogles_error(c, error);
return;
}
@@ -1064,18 +1204,12 @@ void glTexImage2D(
userSurface.compressedFormat = 0;
userSurface.data = (GLubyte*)pixels;
- if (target != GL_DIRECT_TEXTURE_2D_QUALCOMM) {
- int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
- if (err) {
- ogles_error(c, err);
- return;
- }
- generateMipmap(c, level);
- } else {
- // bind it to the texture unit
- sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
- tex->setSurface(&userSurface);
+ int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height);
+ if (err) {
+ ogles_error(c, err);
+ return;
}
+ generateMipmap(c, level);
}
}
@@ -1150,7 +1284,7 @@ void glTexSubImage2D(
int err = copyPixels(c,
surface, xoffset, yoffset,
- userSurface, 0, 0, width, height);
+ userSurface, 0, 0, width, height);
if (err) {
ogles_error(c, err);
return;
@@ -1203,7 +1337,7 @@ void glCopyTexImage2D(
case GL_LUMINANCE_ALPHA:
case GL_LUMINANCE:
type = GL_UNSIGNED_BYTE;
- break;
+ break;
}
// figure out the format to use for the new texture
@@ -1213,7 +1347,7 @@ void glCopyTexImage2D(
case GGL_PIXEL_FORMAT_RGBA_5551:
case GGL_PIXEL_FORMAT_RGBA_4444:
format = internalformat;
- break;
+ break;
case GGL_PIXEL_FORMAT_RGBX_8888:
case GGL_PIXEL_FORMAT_RGB_888:
case GGL_PIXEL_FORMAT_RGB_565:
@@ -1222,7 +1356,7 @@ void glCopyTexImage2D(
case GL_LUMINANCE:
case GL_RGB:
format = internalformat;
- break;
+ break;
}
break;
}
@@ -1242,7 +1376,7 @@ void glCopyTexImage2D(
ogles_error(c, error);
return;
}
-
+
// The bottom row is stored first in textures
GGLSurface txSurface(*surface);
txSurface.stride = -txSurface.stride;
@@ -1252,7 +1386,7 @@ void glCopyTexImage2D(
int err = copyPixels(c,
txSurface, 0, 0,
- cbSurface, x, y, cbSurface.width, cbSurface.height);
+ cbSurface, x, y, cbSurface.width, cbSurface.height);
if (err) {
ogles_error(c, err);
}
@@ -1302,7 +1436,7 @@ void glCopyTexSubImage2D(
int err = copyPixels(c,
surface, xoffset, yoffset,
- cbSurface, x, y, width, height);
+ cbSurface, x, y, width, height);
if (err) {
ogles_error(c, err);
return;
@@ -1372,7 +1506,7 @@ void glReadPixels(
return;
}
- ggl->colorBuffer(ggl, &userSurface); // destination is user buffer
+ ggl->colorBuffer(ggl, &userSurface); // destination is user buffer
ggl->bindTexture(ggl, &readSurface); // source is read-buffer
ggl->texCoord2i(ggl, x, readSurface.height - (y + height));
ggl->recti(ggl, 0, 0, width, height);
@@ -1426,3 +1560,43 @@ void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) {
ogles_context_t* c = ogles_context_t::get();
drawTexxOES(x, y, z, w, h, c);
}
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark EGL Image Extension
+#endif
+
+void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
+{
+ ogles_context_t* c = ogles_context_t::get();
+ if (target != GL_TEXTURE_2D) {
+ ogles_error(c, GL_INVALID_ENUM);
+ return;
+ }
+
+ android_native_buffer_t* native_buffer = (android_native_buffer_t*)image;
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+ if (native_buffer->common.version != sizeof(android_native_buffer_t)) {
+ ogles_error(c, GL_INVALID_VALUE);
+ return;
+ }
+
+ // bind it to the texture unit
+ sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c);
+ tex->setImage(native_buffer);
+
+#ifdef LIBAGL_USE_GRALLOC_COPYBITS
+ tex->try_copybit = false;
+ if (c->copybits.blitEngine != NULL) {
+ tex->try_copybit = true;
+ }
+#endif // LIBAGL_USE_GRALLOC_COPYBITS
+}
+
+void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
+{
+}
diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h
index 5c57948..98f7550 100644
--- a/opengl/libagl/texture.h
+++ b/opengl/libagl/texture.h
@@ -32,13 +32,9 @@ namespace android {
void ogles_init_texture(ogles_context_t* c);
void ogles_uninit_texture(ogles_context_t* c);
-void ogles_validate_texture_impl(ogles_context_t* c);
-
-inline void ogles_validate_texture(ogles_context_t* c) {
- if (c->rasterizer.state.enables & GGL_ENABLE_TMUS)
- ogles_validate_texture_impl(c);
-}
-
+void ogles_validate_texture(ogles_context_t* c);
+void ogles_lock_textures(ogles_context_t* c);
+void ogles_unlock_textures(ogles_context_t* c);
}; // namespace android
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 23304d5..9578452 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -1,17 +1,18 @@
LOCAL_PATH:= $(call my-dir)
-#
+###############################################################################
# Build META EGL library
#
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- EGL/egl.cpp \
- EGL/gpu.cpp \
+ EGL/egl.cpp \
+ EGL/hooks.cpp \
+ EGL/Loader.cpp \
#
-LOCAL_SHARED_LIBRARIES += libcutils libutils libui
+LOCAL_SHARED_LIBRARIES += libcutils libutils
LOCAL_LDLIBS := -lpthread -ldl
LOCAL_MODULE:= libEGL
@@ -19,24 +20,46 @@ LOCAL_MODULE:= libEGL
ifeq ($(TARGET_SIMULATOR),true)
else
LOCAL_SHARED_LIBRARIES += libdl
- # we need to access the Bionic private header <bionic_tls.h>
- LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private
+ # we need to access the private Bionic header <bionic_tls.h>
+ LOCAL_C_INCLUDES += bionic/libc/private
endif
+LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -fvisibility=hidden
+ifeq ($(TARGET_BOARD_PLATFORM),msm7k)
+LOCAL_CFLAGS += -DADRENO130=1
+endif
+
include $(BUILD_SHARED_LIBRARY)
+installed_libEGL := $(LOCAL_INSTALLED_MODULE)
+# OpenGL drivers config file
+ifneq ($(BOARD_EGL_CFG),)
-#
-# Build the wrapper OpenGL ES library
+include $(CLEAR_VARS)
+LOCAL_MODULE := egl.cfg
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT)/lib/egl
+LOCAL_SRC_FILES := ../../../../$(BOARD_EGL_CFG)
+include $(BUILD_PREBUILT)
+
+# make sure we depend on egl.cfg, so it gets installed
+$(installed_libEGL): | egl.cfg
+
+endif
+
+###############################################################################
+# Build the wrapper OpenGL ES 1.x library
#
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- GLES_CM/gl.cpp.arm \
+LOCAL_SRC_FILES:= \
+ GLES_CM/gl.cpp.arm \
#
LOCAL_SHARED_LIBRARIES += libcutils libEGL
@@ -47,10 +70,41 @@ LOCAL_MODULE:= libGLESv1_CM
ifeq ($(TARGET_SIMULATOR),true)
else
LOCAL_SHARED_LIBRARIES += libdl
- # we need to access the Bionic private header <bionic_tls.h>
- LOCAL_CFLAGS += -I$(LOCAL_PATH)/../../../../bionic/libc/private
+ # we need to access the private Bionic header <bionic_tls.h>
+ LOCAL_C_INCLUDES += bionic/libc/private
+endif
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv1\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -fvisibility=hidden
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+###############################################################################
+# Build the wrapper OpenGL ES 2.x library
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ GLES2/gl2.cpp.arm \
+#
+
+LOCAL_SHARED_LIBRARIES += libcutils libEGL
+LOCAL_LDLIBS := -lpthread -ldl
+LOCAL_MODULE:= libGLESv2
+
+# needed on sim build because of weird logging issues
+ifeq ($(TARGET_SIMULATOR),true)
+else
+ LOCAL_SHARED_LIBRARIES += libdl
+ # we need to access the private Bionic header <bionic_tls.h>
+ LOCAL_C_INCLUDES += bionic/libc/private
endif
+LOCAL_CFLAGS += -DLOG_TAG=\"libGLESv2\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -fvisibility=hidden
include $(BUILD_SHARED_LIBRARY)
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
new file mode 100644
index 0000000..d51b333
--- /dev/null
+++ b/opengl/libs/EGL/Loader.cpp
@@ -0,0 +1,276 @@
+/*
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <limits.h>
+
+#include <cutils/log.h>
+
+#include <EGL/egl.h>
+
+#include "hooks.h"
+#include "egl_impl.h"
+
+#include "Loader.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+
+/*
+ * EGL drivers are called
+ *
+ * /system/lib/egl/lib{[EGL|GLESv1_CM|GLESv2] | GLES}_$TAG.so
+ *
+ */
+
+ANDROID_SINGLETON_STATIC_INSTANCE( Loader )
+
+// ----------------------------------------------------------------------------
+
+Loader::driver_t::driver_t(void* gles)
+{
+ dso[0] = gles;
+ for (size_t i=1 ; i<NELEM(dso) ; i++)
+ dso[i] = 0;
+}
+
+Loader::driver_t::~driver_t()
+{
+ for (size_t i=0 ; i<NELEM(dso) ; i++) {
+ if (dso[i]) {
+ dlclose(dso[i]);
+ dso[i] = 0;
+ }
+ }
+}
+
+status_t Loader::driver_t::set(void* hnd, int32_t api)
+{
+ switch (api) {
+ case EGL:
+ dso[0] = hnd;
+ break;
+ case GLESv1_CM:
+ dso[1] = hnd;
+ break;
+ case GLESv2:
+ dso[2] = hnd;
+ break;
+ default:
+ return BAD_INDEX;
+ }
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
+Loader::entry_t::entry_t(int dpy, int impl, const char* tag)
+ : dpy(dpy), impl(impl), tag(tag) {
+}
+
+// ----------------------------------------------------------------------------
+
+Loader::Loader()
+{
+ char line[256];
+ char tag[256];
+ FILE* cfg = fopen("/system/lib/egl/egl.cfg", "r");
+ if (cfg == NULL) {
+ // default config
+ LOGD("egl.cfg not found, using default config");
+ gConfig.add( entry_t(0, 0, "android") );
+ } else {
+ while (fgets(line, 256, cfg)) {
+ int dpy;
+ int impl;
+ if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) {
+ //LOGD(">>> %u %u %s", dpy, impl, tag);
+ gConfig.add( entry_t(dpy, impl, tag) );
+ }
+ }
+ fclose(cfg);
+ }
+}
+
+Loader::~Loader()
+{
+}
+
+const char* Loader::getTag(int dpy, int impl)
+{
+ const Vector<entry_t>& cfgs(gConfig);
+ const size_t c = cfgs.size();
+ for (size_t i=0 ; i<c ; i++) {
+ if (dpy == cfgs[i].dpy)
+ if (impl == cfgs[i].impl)
+ return cfgs[i].tag.string();
+ }
+ return 0;
+}
+
+void* Loader::open(EGLNativeDisplayType display, int impl, gl_hooks_t* hooks)
+{
+ /*
+ * TODO: if we don't find display/0, then use 0/0
+ * (0/0 should always work)
+ */
+
+ void* dso;
+ char path[PATH_MAX];
+ int index = int(display);
+ driver_t* hnd = 0;
+ const char* const format = "/system/lib/egl/lib%s_%s.so";
+
+ char const* tag = getTag(index, impl);
+ if (tag) {
+ snprintf(path, PATH_MAX, format, "GLES", tag);
+ dso = load_driver(path, hooks, EGL | GLESv1_CM | GLESv2);
+ if (dso) {
+ hnd = new driver_t(dso);
+ } else {
+ // Always load EGL first
+ snprintf(path, PATH_MAX, format, "EGL", tag);
+ dso = load_driver(path, hooks, EGL);
+ if (dso) {
+ hnd = new driver_t(dso);
+
+ // TODO: make this more automated
+ snprintf(path, PATH_MAX, format, "GLESv1_CM", tag);
+ hnd->set( load_driver(path, hooks, GLESv1_CM), GLESv1_CM );
+
+ snprintf(path, PATH_MAX, format, "GLESv2", tag);
+ hnd->set( load_driver(path, hooks, GLESv2), GLESv2 );
+ }
+ }
+ }
+
+ LOG_FATAL_IF(!index && !impl && !hnd,
+ "couldn't find the default OpenGL ES implementation "
+ "for default display");
+
+ return (void*)hnd;
+}
+
+status_t Loader::close(void* driver)
+{
+ driver_t* hnd = (driver_t*)driver;
+ delete hnd;
+ return NO_ERROR;
+}
+
+void Loader::init_api(void* dso,
+ char const * const * api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress)
+{
+ char scrap[256];
+ while (*api) {
+ char const * name = *api;
+ __eglMustCastToProperFunctionPointerType f =
+ (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
+ if (f == NULL) {
+ // couldn't find the entry-point, use eglGetProcAddress()
+ f = getProcAddress(name);
+ }
+ if (f == NULL) {
+ // Try without the OES postfix
+ ssize_t index = ssize_t(strlen(name)) - 3;
+ if ((index>0 && (index<255)) && (!strcmp(name+index, "OES"))) {
+ strncpy(scrap, name, index);
+ scrap[index] = 0;
+ f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
+ //LOGD_IF(f, "found <%s> instead", scrap);
+ }
+ }
+ if (f == NULL) {
+ // Try with the OES postfix
+ ssize_t index = ssize_t(strlen(name)) - 3;
+ if ((index>0 && (index<252)) && (strcmp(name+index, "OES"))) {
+ strncpy(scrap, name, index);
+ scrap[index] = 0;
+ strcat(scrap, "OES");
+ f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
+ //LOGD_IF(f, "found <%s> instead", scrap);
+ }
+ }
+ if (f == NULL) {
+ //LOGD("%s", name);
+ f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
+ }
+ *curr++ = f;
+ api++;
+ }
+}
+
+void *Loader::load_driver(const char* driver, gl_hooks_t* hooks, uint32_t mask)
+{
+ void* dso = dlopen(driver, RTLD_NOW | RTLD_LOCAL);
+ if (dso == 0)
+ return 0;
+
+ LOGD("loaded %s", driver);
+
+ if (mask & EGL) {
+ getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");
+
+ LOGE_IF(!getProcAddress,
+ "can't find eglGetProcAddress() in %s", driver);
+
+ gl_hooks_t::egl_t* egl = &hooks->egl;
+ __eglMustCastToProperFunctionPointerType* curr =
+ (__eglMustCastToProperFunctionPointerType*)egl;
+ char const * const * api = egl_names;
+ while (*api) {
+ char const * name = *api;
+ __eglMustCastToProperFunctionPointerType f =
+ (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
+ if (f == NULL) {
+ // couldn't find the entry-point, use eglGetProcAddress()
+ f = getProcAddress(name);
+ if (f == NULL) {
+ f = (__eglMustCastToProperFunctionPointerType)0;
+ }
+ }
+ *curr++ = f;
+ api++;
+ }
+ }
+
+ if (mask & GLESv1_CM) {
+ init_api(dso, gl_names,
+ (__eglMustCastToProperFunctionPointerType*)&hooks->gl,
+ getProcAddress);
+ }
+
+ if (mask & GLESv2) {
+ init_api(dso, gl2_names,
+ (__eglMustCastToProperFunctionPointerType*)&hooks->gl2,
+ getProcAddress);
+ }
+
+ return dso;
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/Loader.h b/opengl/libs/EGL/Loader.h
new file mode 100644
index 0000000..69f6dd5
--- /dev/null
+++ b/opengl/libs/EGL/Loader.h
@@ -0,0 +1,90 @@
+/*
+ ** Copyright 2009, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef ANDROID_EGL_LOADER_H
+#define ANDROID_EGL_LOADER_H
+
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include <utils/Errors.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <EGL/egl.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+struct gl_hooks_t;
+
+class Loader : public Singleton<Loader>
+{
+ friend class Singleton<Loader>;
+
+ typedef __eglMustCastToProperFunctionPointerType (*getProcAddressType)(
+ const char*);
+
+ enum {
+ EGL = 0x01,
+ GLESv1_CM = 0x02,
+ GLESv2 = 0x04
+ };
+ struct driver_t {
+ driver_t(void* gles);
+ ~driver_t();
+ status_t set(void* hnd, int32_t api);
+ void* dso[3];
+ };
+
+ struct entry_t {
+ entry_t() { }
+ entry_t(int dpy, int impl, const char* tag);
+ int dpy;
+ int impl;
+ String8 tag;
+ };
+
+ Vector<entry_t> gConfig;
+ getProcAddressType getProcAddress;
+
+ const char* getTag(int dpy, int impl);
+
+public:
+ ~Loader();
+
+ void* open(EGLNativeDisplayType display, int impl, gl_hooks_t* hooks);
+ status_t close(void* driver);
+
+private:
+ Loader();
+ void *load_driver(const char* driver, gl_hooks_t* hooks, uint32_t mask);
+
+ static __attribute__((noinline))
+ void init_api(void* dso,
+ char const * const * api,
+ __eglMustCastToProperFunctionPointerType* curr,
+ getProcAddressType getProcAddress);
+};
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+#endif /* ANDROID_EGL_LOADER_H */
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index c6e0f50..d1ddcd3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -14,9 +14,8 @@
** limitations under the License.
*/
-#define LOG_TAG "libEGL"
-
#include <ctype.h>
+#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dlfcn.h>
@@ -37,11 +36,11 @@
#include <cutils/properties.h>
#include <cutils/memory.h>
-#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
#include "hooks.h"
#include "egl_impl.h"
-
+#include "Loader.h"
#define MAKE_CONFIG(_impl, _index) ((EGLConfig)(((_impl)<<24) | (_index)))
#define setError(_e, _r) setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
@@ -53,61 +52,148 @@ namespace android {
#define VERSION_MAJOR 1
#define VERSION_MINOR 4
static char const * const gVendorString = "Android";
-static char const * const gVersionString = "1.31 Android META-EGL";
+static char const * const gVersionString = "1.4 Android META-EGL";
static char const * const gClientApiString = "OpenGL ES";
-static char const * const gExtensionString = "";
+static char const * const gExtensionString =
+ "EGL_KHR_image "
+ "EGL_KHR_image_base "
+ "EGL_KHR_image_pixmap "
+ "EGL_ANDROID_image_native_buffer "
+ "EGL_ANDROID_swap_rectangle "
+ "EGL_ANDROID_get_render_buffer "
+ ;
+
+// ----------------------------------------------------------------------------
+
+class egl_object_t {
+ static SortedVector<egl_object_t*> sObjects;
+ static Mutex sLock;
+
+ volatile int32_t terminated;
+ mutable volatile int32_t count;
+
+public:
+ egl_object_t() : terminated(0), count(1) {
+ Mutex::Autolock _l(sLock);
+ sObjects.add(this);
+ }
+
+ inline bool isAlive() const { return !terminated; }
-template <int MAGIC>
-struct egl_object_t
-{
- egl_object_t() : magic(MAGIC) { }
- ~egl_object_t() { magic = 0; }
- bool isValid() const { return magic == MAGIC; }
private:
- uint32_t magic;
+ bool get() {
+ Mutex::Autolock _l(sLock);
+ if (egl_object_t::sObjects.indexOf(this) >= 0) {
+ android_atomic_inc(&count);
+ return true;
+ }
+ return false;
+ }
+
+ bool put() {
+ Mutex::Autolock _l(sLock);
+ if (android_atomic_dec(&count) == 1) {
+ sObjects.remove(this);
+ return true;
+ }
+ return false;
+ }
+
+public:
+ template <typename N, typename T>
+ struct LocalRef {
+ N* ref;
+ LocalRef(T o) : ref(0) {
+ N* native = reinterpret_cast<N*>(o);
+ if (o && native->get()) {
+ ref = native;
+ }
+ }
+ ~LocalRef() {
+ if (ref && ref->put()) {
+ delete ref;
+ }
+ }
+ inline N* get() {
+ return ref;
+ }
+ void acquire() const {
+ if (ref) {
+ android_atomic_inc(&ref->count);
+ }
+ }
+ void release() const {
+ if (ref) {
+ int32_t c = android_atomic_dec(&ref->count);
+ // ref->count cannot be 1 prior atomic_dec because we have
+ // a reference, and if we have one, it means there was
+ // already one before us.
+ LOGE_IF(c==1, "refcount is now 0 in release()");
+ }
+ }
+ void terminate() {
+ if (ref) {
+ ref->terminated = 1;
+ release();
+ }
+ }
+ };
};
-struct egl_display_t : public egl_object_t<'_dpy'>
-{
- EGLDisplay dpys[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
- EGLConfig* configs[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
- EGLint numConfigs[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
- EGLint numTotalConfigs;
- char const* extensionsString;
- volatile int32_t refs;
+SortedVector<egl_object_t*> egl_object_t::sObjects;
+Mutex egl_object_t::sLock;
+
+struct egl_display_t {
+ enum { NOT_INITIALIZED, INITIALIZED, TERMINATED };
+
struct strings_t {
char const * vendor;
char const * version;
char const * clientApi;
char const * extensions;
};
- strings_t queryString[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
+
+ struct DisplayImpl {
+ DisplayImpl() : dpy(EGL_NO_DISPLAY), config(0),
+ state(NOT_INITIALIZED), numConfigs(0) { }
+ EGLDisplay dpy;
+ EGLConfig* config;
+ EGLint state;
+ EGLint numConfigs;
+ strings_t queryString;
+ };
+
+ uint32_t magic;
+ DisplayImpl disp[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
+ EGLint numTotalConfigs;
+ volatile int32_t refs;
+
+ egl_display_t() : magic('_dpy'), numTotalConfigs(0) { }
+ ~egl_display_t() { magic = 0; }
+ inline bool isValid() const { return magic == '_dpy'; }
+ inline bool isAlive() const { return isValid(); }
};
-struct egl_surface_t : public egl_object_t<'_srf'>
+struct egl_surface_t : public egl_object_t
{
+ typedef egl_object_t::LocalRef<egl_surface_t, EGLSurface> Ref;
+
egl_surface_t(EGLDisplay dpy, EGLSurface surface,
- NativeWindowType window, int impl, egl_connection_t const* cnx)
- : dpy(dpy), surface(surface), window(window), impl(impl), cnx(cnx)
- {
- // NOTE: window must be incRef'ed and connected already
+ int impl, egl_connection_t const* cnx)
+ : dpy(dpy), surface(surface), impl(impl), cnx(cnx) {
}
~egl_surface_t() {
- if (window) {
- if (window->disconnect)
- window->disconnect(window);
- window->decRef(window);
- }
}
EGLDisplay dpy;
EGLSurface surface;
- NativeWindowType window;
int impl;
egl_connection_t const* cnx;
};
-struct egl_context_t : public egl_object_t<'_ctx'>
+struct egl_context_t : public egl_object_t
{
+ typedef egl_object_t::LocalRef<egl_context_t, EGLContext> Ref;
+
egl_context_t(EGLDisplay dpy, EGLContext context,
int impl, egl_connection_t const* cnx)
: dpy(dpy), context(context), read(0), draw(0), impl(impl), cnx(cnx)
@@ -121,39 +207,32 @@ struct egl_context_t : public egl_object_t<'_ctx'>
egl_connection_t const* cnx;
};
-struct tls_t
+struct egl_image_t : public egl_object_t
{
- tls_t() : error(EGL_SUCCESS), ctx(0) { }
- EGLint error;
- EGLContext ctx;
-};
-
-static void gl_unimplemented() {
- LOGE("called unimplemented OpenGL ES API");
-}
+ typedef egl_object_t::LocalRef<egl_image_t, EGLImageKHR> Ref;
-// ----------------------------------------------------------------------------
-// GL / EGL hooks
-// ----------------------------------------------------------------------------
-
-#undef GL_ENTRY
-#undef EGL_ENTRY
-#define GL_ENTRY(_r, _api, ...) #_api,
-#define EGL_ENTRY(_r, _api, ...) #_api,
-
-static char const * const gl_names[] = {
- #include "gl_entries.in"
- #include "glext_entries.in"
- NULL
+ egl_image_t(EGLDisplay dpy, EGLContext context)
+ : dpy(dpy), context(context)
+ {
+ memset(images, 0, sizeof(images));
+ }
+ EGLDisplay dpy;
+ EGLConfig context;
+ EGLImageKHR images[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
};
-static char const * const egl_names[] = {
- #include "egl_entries.in"
- NULL
+typedef egl_surface_t::Ref SurfaceRef;
+typedef egl_context_t::Ref ContextRef;
+typedef egl_image_t::Ref ImageRef;
+
+struct tls_t
+{
+ tls_t() : error(EGL_SUCCESS), ctx(0), logCallWithNoContext(EGL_TRUE) { }
+ EGLint error;
+ EGLContext ctx;
+ EGLBoolean logCallWithNoContext;
};
-#undef GL_ENTRY
-#undef EGL_ENTRY
// ----------------------------------------------------------------------------
@@ -262,110 +341,8 @@ EGLContext getContext() {
return tls->ctx;
}
-
/*****************************************************************************/
-class ISurfaceComposer;
-const sp<ISurfaceComposer>& getSurfaceFlinger();
-request_gpu_t* gpu_acquire(void* user);
-int gpu_release(void*, request_gpu_t* gpu);
-
-static __attribute__((noinline))
-void *load_driver(const char* driver, gl_hooks_t* hooks)
-{
- //LOGD("%s", driver);
- char scrap[256];
- void* dso = dlopen(driver, RTLD_NOW | RTLD_LOCAL);
- LOGE_IF(!dso,
- "couldn't load <%s> library (%s)",
- driver, dlerror());
-
- if (dso) {
- // first find the symbol for eglGetProcAddress
-
- typedef __eglMustCastToProperFunctionPointerType (*getProcAddressType)(
- const char*);
-
- getProcAddressType getProcAddress =
- (getProcAddressType)dlsym(dso, "eglGetProcAddress");
-
- LOGE_IF(!getProcAddress,
- "can't find eglGetProcAddress() in %s", driver);
-
- __eglMustCastToProperFunctionPointerType* curr;
- char const * const * api;
-
- gl_hooks_t::egl_t* egl = &hooks->egl;
- curr = (__eglMustCastToProperFunctionPointerType*)egl;
- api = egl_names;
- while (*api) {
- char const * name = *api;
- __eglMustCastToProperFunctionPointerType f =
- (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
- if (f == NULL) {
- // couldn't find the entry-point, use eglGetProcAddress()
- f = getProcAddress(name);
- if (f == NULL) {
- f = (__eglMustCastToProperFunctionPointerType)0;
- }
- }
- *curr++ = f;
- api++;
- }
-
- gl_hooks_t::gl_t* gl = &hooks->gl;
- curr = (__eglMustCastToProperFunctionPointerType*)gl;
- api = gl_names;
- while (*api) {
- char const * name = *api;
- __eglMustCastToProperFunctionPointerType f =
- (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
- if (f == NULL) {
- // couldn't find the entry-point, use eglGetProcAddress()
- f = getProcAddress(name);
- }
- if (f == NULL) {
- // Try without the OES postfix
- ssize_t index = ssize_t(strlen(name)) - 3;
- if ((index>0 && (index<255)) && (!strcmp(name+index, "OES"))) {
- strncpy(scrap, name, index);
- scrap[index] = 0;
- f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
- //LOGD_IF(f, "found <%s> instead", scrap);
- }
- }
- if (f == NULL) {
- // Try with the OES postfix
- ssize_t index = ssize_t(strlen(name)) - 3;
- if ((index>0 && (index<252)) && (strcmp(name+index, "OES"))) {
- strncpy(scrap, name, index);
- scrap[index] = 0;
- strcat(scrap, "OES");
- f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, scrap);
- //LOGD_IF(f, "found <%s> instead", scrap);
- }
- }
- if (f == NULL) {
- //LOGD("%s", name);
- f = (__eglMustCastToProperFunctionPointerType)gl_unimplemented;
- }
- *curr++ = f;
- api++;
- }
-
- // hook this driver up with surfaceflinger if needed
- register_gpu_t register_gpu =
- (register_gpu_t)dlsym(dso, "oem_register_gpu");
-
- if (register_gpu != NULL) {
- if (getSurfaceFlinger() != 0) {
- register_gpu(dso, gpu_acquire, gpu_release);
- }
- }
- }
- return dso;
-}
-
template<typename T>
static __attribute__((noinline))
int binarySearch(
@@ -387,14 +364,14 @@ int binarySearch(
static EGLint configToUniqueId(egl_display_t const* dp, int i, int index)
{
// NOTE: this mapping works only if we have no more than two EGLimpl
- return (i>0 ? dp->numConfigs[0] : 0) + index;
+ return (i>0 ? dp->disp[0].numConfigs : 0) + index;
}
static void uniqueIdToConfig(egl_display_t const* dp, EGLint configId,
int& i, int& index)
{
// NOTE: this mapping works only if we have no more than two EGLimpl
- size_t numConfigs = dp->numConfigs[0];
+ size_t numConfigs = dp->disp[0].numConfigs;
i = configId / numConfigs;
index = configId % numConfigs;
}
@@ -412,6 +389,18 @@ struct extention_map_t {
};
static const extention_map_t gExtentionMap[] = {
+ { "eglLockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
+ { "eglUnlockSurfaceKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
+ { "eglCreateImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
+ { "eglDestroyImageKHR",
+ (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
+ { "eglSetSwapRectangleANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
+ { "eglGetRenderBufferANDROID",
+ (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
static extention_map_t gGLExtentionMap[MAX_NUMBER_OF_GL_EXTENSIONS];
@@ -429,29 +418,15 @@ static void(*findProcAddress(const char* name,
// ----------------------------------------------------------------------------
-static int gl_context_lost() {
- setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]);
- return 0;
-}
-static int egl_context_lost() {
- setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]);
- return EGL_FALSE;
-}
-static EGLBoolean egl_context_lost_swap_buffers(void*, void*) {
- usleep(100000); // don't use all the CPU
- setGlThreadSpecific(&gHooks[IMPL_CONTEXT_LOST]);
- return EGL_FALSE;
-}
-static GLint egl_context_lost_get_error() {
- return EGL_CONTEXT_LOST;
-}
-static int ext_context_lost() {
- return 0;
-}
-
static void gl_no_context() {
- LOGE("call to OpenGL ES API with no current context");
+ tls_t* tls = getTLS();
+ if (tls->logCallWithNoContext == EGL_TRUE) {
+ tls->logCallWithNoContext = EGL_FALSE;
+ LOGE("call to OpenGL ES API with no current context "
+ "(logged once per thread)");
+ }
}
+
static void early_egl_init(void)
{
#if !USE_FAST_TLS_KEY
@@ -491,6 +466,11 @@ egl_context_t* get_context(EGLContext context) {
return egl_to_native_cast<egl_context_t>(context);
}
+static inline
+egl_image_t* get_image(EGLImageKHR image) {
+ return egl_to_native_cast<egl_image_t>(image);
+}
+
static egl_connection_t* validate_display_config(
EGLDisplay dpy, EGLConfig config,
egl_display_t const*& dp, int& impl, int& index)
@@ -503,7 +483,7 @@ static egl_connection_t* validate_display_config(
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
index = uintptr_t(config) & 0xFFFFFF;
- if (index >= dp->numConfigs[impl]) {
+ if (index >= dp->disp[impl].numConfigs) {
return setError(EGL_BAD_CONFIG, (egl_connection_t*)NULL);
}
egl_connection_t* const cnx = &gEGLImpl[impl];
@@ -517,11 +497,9 @@ static EGLBoolean validate_display_context(EGLDisplay dpy, EGLContext ctx)
{
if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (!get_display(dpy)->isValid())
+ if (!get_display(dpy)->isAlive())
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (!ctx) // TODO: make sure context is a valid object
- return setError(EGL_BAD_CONTEXT, EGL_FALSE);
- if (!get_context(ctx)->isValid())
+ if (!get_context(ctx)->isAlive())
return setError(EGL_BAD_CONTEXT, EGL_FALSE);
return EGL_TRUE;
}
@@ -530,92 +508,106 @@ static EGLBoolean validate_display_surface(EGLDisplay dpy, EGLSurface surface)
{
if ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS)
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (!get_display(dpy)->isValid())
+ if (!get_display(dpy)->isAlive())
return setError(EGL_BAD_DISPLAY, EGL_FALSE);
- if (!surface) // TODO: make sure surface is a valid object
- return setError(EGL_BAD_SURFACE, EGL_FALSE);
- if (!get_surface(surface)->isValid())
+ if (!get_surface(surface)->isAlive())
return setError(EGL_BAD_SURFACE, EGL_FALSE);
return EGL_TRUE;
}
+EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image)
+{
+ ImageRef _i(image);
+ if (!_i.get()) return EGL_NO_IMAGE_KHR;
+
+ EGLContext context = getContext();
+ if (context == EGL_NO_CONTEXT || image == EGL_NO_IMAGE_KHR)
+ return EGL_NO_IMAGE_KHR;
+
+ egl_context_t const * const c = get_context(context);
+ if (!c->isAlive())
+ return EGL_NO_IMAGE_KHR;
+
+ egl_image_t const * const i = get_image(image);
+ return i->images[c->impl];
+}
-EGLDisplay egl_init_displays(NativeDisplayType display)
+// ----------------------------------------------------------------------------
+
+// this mutex protects:
+// d->disp[]
+// egl_init_drivers_locked()
+//
+static pthread_mutex_t gInitDriverMutex = PTHREAD_MUTEX_INITIALIZER;
+
+EGLBoolean egl_init_drivers_locked()
{
if (sEarlyInitState) {
- return EGL_NO_DISPLAY;
+ // initialized by static ctor. should be set here.
+ return EGL_FALSE;
}
- uint32_t index = uint32_t(display);
- if (index >= NUM_DISPLAYS) {
- return EGL_NO_DISPLAY;
- }
+ // get our driver loader
+ Loader& loader(Loader::getInstance());
- EGLDisplay dpy = EGLDisplay(uintptr_t(display) + 1LU);
- egl_display_t* d = &gDisplay[index];
-
- // dynamically load all our EGL implementations for that display
- // and call into the real eglGetGisplay()
- egl_connection_t* cnx = &gEGLImpl[IMPL_SOFTWARE];
+ // dynamically load all our EGL implementations for all displays
+ // and retrieve the corresponding EGLDisplay
+ // if that fails, don't use this driver.
+ // TODO: currently we only deal with EGL_DEFAULT_DISPLAY
+ egl_connection_t* cnx;
+ egl_display_t* d = &gDisplay[0];
+
+ cnx = &gEGLImpl[IMPL_SOFTWARE];
if (cnx->dso == 0) {
cnx->hooks = &gHooks[IMPL_SOFTWARE];
- cnx->dso = load_driver("libagl.so", cnx->hooks);
- }
- if (cnx->dso && d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY) {
- d->dpys[IMPL_SOFTWARE] = cnx->hooks->egl.eglGetDisplay(display);
- LOGE_IF(d->dpys[IMPL_SOFTWARE]==EGL_NO_DISPLAY,
- "No EGLDisplay for software EGL!");
+ cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 0, cnx->hooks);
+ if (cnx->dso) {
+ EGLDisplay dpy = cnx->hooks->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for software EGL!");
+ d->disp[IMPL_SOFTWARE].dpy = dpy;
+ if (dpy == EGL_NO_DISPLAY) {
+ loader.close(cnx->dso);
+ cnx->dso = NULL;
+ }
+ }
}
cnx = &gEGLImpl[IMPL_HARDWARE];
- if (cnx->dso == 0 && cnx->unavailable == 0) {
+ if (cnx->dso == 0) {
char value[PROPERTY_VALUE_MAX];
property_get("debug.egl.hw", value, "1");
if (atoi(value) != 0) {
cnx->hooks = &gHooks[IMPL_HARDWARE];
- cnx->dso = load_driver("libhgl.so", cnx->hooks);
+ cnx->dso = loader.open(EGL_DEFAULT_DISPLAY, 1, cnx->hooks);
+ if (cnx->dso) {
+ EGLDisplay dpy = cnx->hooks->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for hardware EGL!");
+ d->disp[IMPL_HARDWARE].dpy = dpy;
+ if (dpy == EGL_NO_DISPLAY) {
+ loader.close(cnx->dso);
+ cnx->dso = NULL;
+ }
+ }
} else {
LOGD("3D hardware acceleration is disabled");
}
}
- if (cnx->dso && d->dpys[IMPL_HARDWARE]==EGL_NO_DISPLAY) {
- android_memset32(
- (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].gl,
- (uint32_t)((void*)gl_context_lost),
- sizeof(gHooks[IMPL_CONTEXT_LOST].gl));
- android_memset32(
- (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].egl,
- (uint32_t)((void*)egl_context_lost),
- sizeof(gHooks[IMPL_CONTEXT_LOST].egl));
- android_memset32(
- (uint32_t*)(void*)&gHooks[IMPL_CONTEXT_LOST].ext,
- (uint32_t)((void*)ext_context_lost),
- sizeof(gHooks[IMPL_CONTEXT_LOST].ext));
-
- gHooks[IMPL_CONTEXT_LOST].egl.eglSwapBuffers =
- egl_context_lost_swap_buffers;
-
- gHooks[IMPL_CONTEXT_LOST].egl.eglGetError =
- egl_context_lost_get_error;
- gHooks[IMPL_CONTEXT_LOST].egl.eglTerminate =
- gHooks[IMPL_HARDWARE].egl.eglTerminate;
-
- d->dpys[IMPL_HARDWARE] = cnx->hooks->egl.eglGetDisplay(display);
- if (d->dpys[IMPL_HARDWARE] == EGL_NO_DISPLAY) {
- LOGE("h/w accelerated eglGetDisplay() failed (%s)",
- egl_strerror(cnx->hooks->egl.eglGetError()));
- dlclose((void*)cnx->dso);
- cnx->dso = 0;
- // in case of failure, we want to make sure we don't try again
- // as it's expensive.
- cnx->unavailable = 1;
- }
+ if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) {
+ return EGL_FALSE;
}
- return dpy;
+ return EGL_TRUE;
}
+EGLBoolean egl_init_drivers()
+{
+ EGLBoolean res;
+ pthread_mutex_lock(&gInitDriverMutex);
+ res = egl_init_drivers_locked();
+ pthread_mutex_unlock(&gInitDriverMutex);
+ return res;
+}
// ----------------------------------------------------------------------------
}; // namespace android
@@ -625,7 +617,17 @@ using namespace android;
EGLDisplay eglGetDisplay(NativeDisplayType display)
{
- return egl_init_displays(display);
+ uint32_t index = uint32_t(display);
+ if (index >= NUM_DISPLAYS) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
+ }
+
+ EGLDisplay dpy = EGLDisplay(uintptr_t(display) + 1LU);
+ return dpy;
}
// ----------------------------------------------------------------------------
@@ -648,7 +650,6 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
// initialize each EGL and
// build our own extension string first, based on the extension we know
// and the extension supported by our client implementation
- dp->extensionsString = strdup(gExtensionString);
for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
cnx->major = -1;
@@ -656,25 +657,43 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
if (!cnx->dso)
continue;
- if (cnx->hooks->egl.eglInitialize(
- dp->dpys[i], &cnx->major, &cnx->minor)) {
+#if defined(ADRENO130)
+#warning "Adreno-130 eglInitialize() workaround"
+ /*
+ * The ADRENO 130 driver returns a different EGLDisplay each time
+ * eglGetDisplay() is called, but also makes the EGLDisplay invalid
+ * after eglTerminate() has been called, so that eglInitialize()
+ * cannot be called again. Therefore, we need to make sure to call
+ * eglGetDisplay() before calling eglInitialize();
+ */
+ if (i == IMPL_HARDWARE) {
+ dp->disp[i].dpy =
+ cnx->hooks->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ }
+#endif
+
+ EGLDisplay idpy = dp->disp[i].dpy;
+ if (cnx->hooks->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
//LOGD("initialized %d dpy=%p, ver=%d.%d, cnx=%p",
- // i, dp->dpys[i], cnx->major, cnx->minor, cnx);
+ // i, idpy, cnx->major, cnx->minor, cnx);
+
+ // display is now initialized
+ dp->disp[i].state = egl_display_t::INITIALIZED;
// get the query-strings for this display for each implementation
- dp->queryString[i].vendor =
- cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VENDOR);
- dp->queryString[i].version =
- cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_VERSION);
- dp->queryString[i].extensions = strdup(
- cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_EXTENSIONS));
- dp->queryString[i].clientApi =
- cnx->hooks->egl.eglQueryString(dp->dpys[i], EGL_CLIENT_APIS);
+ dp->disp[i].queryString.vendor =
+ cnx->hooks->egl.eglQueryString(idpy, EGL_VENDOR);
+ dp->disp[i].queryString.version =
+ cnx->hooks->egl.eglQueryString(idpy, EGL_VERSION);
+ dp->disp[i].queryString.extensions =
+ cnx->hooks->egl.eglQueryString(idpy, EGL_EXTENSIONS);
+ dp->disp[i].queryString.clientApi =
+ cnx->hooks->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
} else {
- LOGD("%d: eglInitialize() failed (%s)",
- i, egl_strerror(cnx->hooks->egl.eglGetError()));
+ LOGW("%d: eglInitialize(%p) failed (%s)", i, idpy,
+ egl_strerror(cnx->hooks->egl.eglGetError()));
}
}
@@ -683,15 +702,16 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso && cnx->major>=0 && cnx->minor>=0) {
EGLint n;
- if (cnx->hooks->egl.eglGetConfigs(dp->dpys[i], 0, 0, &n)) {
- dp->configs[i] = (EGLConfig*)malloc(sizeof(EGLConfig)*n);
- if (dp->configs[i]) {
+ if (cnx->hooks->egl.eglGetConfigs(dp->disp[i].dpy, 0, 0, &n)) {
+ dp->disp[i].config = (EGLConfig*)malloc(sizeof(EGLConfig)*n);
+ if (dp->disp[i].config) {
if (cnx->hooks->egl.eglGetConfigs(
- dp->dpys[i], dp->configs[i], n, &dp->numConfigs[i]))
+ dp->disp[i].dpy, dp->disp[i].config, n,
+ &dp->disp[i].numConfigs))
{
// sort the configurations so we can do binary searches
- qsort( dp->configs[i],
- dp->numConfigs[i],
+ qsort( dp->disp[i].config,
+ dp->disp[i].numConfigs,
sizeof(EGLConfig), cmp_configs);
dp->numTotalConfigs += n;
@@ -712,33 +732,36 @@ EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
EGLBoolean eglTerminate(EGLDisplay dpy)
{
+ // NOTE: don't unload the drivers b/c some APIs can be called
+ // after eglTerminate() has been called. eglTerminate() only
+ // terminates an EGLDisplay, not a EGL itself.
+
egl_display_t* const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
if (android_atomic_dec(&dp->refs) != 1)
return EGL_TRUE;
-
+
EGLBoolean res = EGL_FALSE;
for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
- if (cnx->dso) {
- cnx->hooks->egl.eglTerminate(dp->dpys[i]);
-
- /* REVISIT: it's unclear what to do if eglTerminate() fails,
- * on one end we shouldn't care, on the other end if it fails
- * it might not be safe to call dlclose() (there could be some
- * threads around). */
-
- free(dp->configs[i]);
- free((void*)dp->queryString[i].extensions);
- dp->numConfigs[i] = 0;
- dp->dpys[i] = EGL_NO_DISPLAY;
- dlclose((void*)cnx->dso);
- cnx->dso = 0;
+ if (cnx->dso && dp->disp[i].state == egl_display_t::INITIALIZED) {
+ if (cnx->hooks->egl.eglTerminate(dp->disp[i].dpy) == EGL_FALSE) {
+ LOGW("%d: eglTerminate(%p) failed (%s)", i, dp->disp[i].dpy,
+ egl_strerror(cnx->hooks->egl.eglGetError()));
+ }
+ // REVISIT: it's unclear what to do if eglTerminate() fails
+ free(dp->disp[i].config);
+
+ dp->disp[i].numConfigs = 0;
+ dp->disp[i].config = 0;
+ dp->disp[i].state = egl_display_t::TERMINATED;
+
res = EGL_TRUE;
}
}
- free((void*)dp->extensionsString);
- dp->extensionsString = 0;
+
+ // TODO: all egl_object_t should be marked for termination
+
dp->numTotalConfigs = 0;
clearTLS();
return res;
@@ -762,7 +785,7 @@ EGLBoolean eglGetConfigs( EGLDisplay dpy,
}
GLint n = 0;
for (int j=0 ; j<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; j++) {
- for (int i=0 ; i<dp->numConfigs[j] && config_size ; i++) {
+ for (int i=0 ; i<dp->disp[j].numConfigs && config_size ; i++) {
*configs++ = MAKE_CONFIG(j, i);
config_size--;
n++;
@@ -819,7 +842,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
cnx->hooks->egl.eglGetConfigAttrib(
- dp->dpys[i], dp->configs[i][index],
+ dp->disp[i].dpy, dp->disp[i].config[index],
EGL_CONFIG_ID, &configId);
// and switch to the new list
@@ -834,7 +857,7 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
// which one.
res = cnx->hooks->egl.eglChooseConfig(
- dp->dpys[i], attrib_list, configs, config_size, &n);
+ dp->disp[i].dpy, attrib_list, configs, config_size, &n);
if (res && n>0) {
// n has to be 0 or 1, by construction, and we already know
// which config it will return (since there can be only one).
@@ -853,13 +876,14 @@ EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->hooks->egl.eglChooseConfig(
- dp->dpys[i], attrib_list, configs, config_size, &n)) {
+ dp->disp[i].dpy, attrib_list, configs, config_size, &n)) {
if (configs) {
// now we need to convert these client EGLConfig to our
// internal EGLConfig format. This is done in O(n log n).
for (int j=0 ; j<n ; j++) {
int index = binarySearch<EGLConfig>(
- dp->configs[i], 0, dp->numConfigs[i]-1, configs[j]);
+ dp->disp[i].config, 0,
+ dp->disp[i].numConfigs-1, configs[j]);
if (index >= 0) {
if (configs) {
configs[j] = MAKE_CONFIG(i, index);
@@ -894,7 +918,7 @@ EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
return EGL_TRUE;
}
return cnx->hooks->egl.eglGetConfigAttrib(
- dp->dpys[i], dp->configs[i][index], attribute, value);
+ dp->disp[i].dpy, dp->disp[i].config[index], attribute, value);
}
// ----------------------------------------------------------------------------
@@ -909,26 +933,12 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config,
int i=0, index=0;
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
- // window must be connected upon calling underlying
- // eglCreateWindowSurface
- if (window) {
- window->incRef(window);
- if (window->connect)
- window->connect(window);
- }
-
EGLSurface surface = cnx->hooks->egl.eglCreateWindowSurface(
- dp->dpys[i], dp->configs[i][index], window, attrib_list);
+ dp->disp[i].dpy, dp->disp[i].config[index], window, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, window, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
-
- // something went wrong, disconnect and free window
- // (will disconnect() automatically)
- if (window) {
- window->decRef(window);
- }
}
return EGL_NO_SURFACE;
}
@@ -942,9 +952,9 @@ EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config,
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLSurface surface = cnx->hooks->egl.eglCreatePixmapSurface(
- dp->dpys[i], dp->configs[i][index], pixmap, attrib_list);
+ dp->disp[i].dpy, dp->disp[i].config[index], pixmap, attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
}
@@ -959,9 +969,9 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLSurface surface = cnx->hooks->egl.eglCreatePbufferSurface(
- dp->dpys[i], dp->configs[i][index], attrib_list);
+ dp->disp[i].dpy, dp->disp[i].config[index], attrib_list);
if (surface != EGL_NO_SURFACE) {
- egl_surface_t* s = new egl_surface_t(dpy, surface, NULL, i, cnx);
+ egl_surface_t* s = new egl_surface_t(dpy, surface, i, cnx);
return s;
}
}
@@ -970,28 +980,35 @@ EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
- egl_surface_t const * const s = get_surface(surface);
+ egl_surface_t * const s = get_surface(surface);
EGLBoolean result = s->cnx->hooks->egl.eglDestroySurface(
- dp->dpys[s->impl], s->surface);
-
- delete s;
+ dp->disp[s->impl].dpy, s->surface);
+ if (result == EGL_TRUE) {
+ _s.terminate();
+ }
return result;
}
EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
EGLint attribute, EGLint *value)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
return s->cnx->hooks->egl.eglQuerySurface(
- dp->dpys[s->impl], s->surface, attribute, value);
+ dp->disp[s->impl].dpy, s->surface, attribute, value);
}
// ----------------------------------------------------------------------------
@@ -1006,7 +1023,8 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
egl_connection_t* cnx = validate_display_config(dpy, config, dp, i, index);
if (cnx) {
EGLContext context = cnx->hooks->egl.eglCreateContext(
- dp->dpys[i], dp->configs[i][index], share_list, attrib_list);
+ dp->disp[i].dpy, dp->disp[i].config[index],
+ share_list, attrib_list);
if (context != EGL_NO_CONTEXT) {
egl_context_t* c = new egl_context_t(dpy, context, i, cnx);
return c;
@@ -1017,66 +1035,125 @@ EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
{
+ ContextRef _c(ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_context_t * const c = get_context(ctx);
EGLBoolean result = c->cnx->hooks->egl.eglDestroyContext(
- dp->dpys[c->impl], c->context);
- delete c;
+ dp->disp[c->impl].dpy, c->context);
+ if (result == EGL_TRUE) {
+ _c.terminate();
+ }
return result;
}
EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
EGLSurface read, EGLContext ctx)
{
+ // get a reference to the object passed in
+ ContextRef _c(ctx);
+ SurfaceRef _d(draw);
+ SurfaceRef _r(read);
+
+ // validate the display and the context (if not EGL_NO_CONTEXT)
egl_display_t const * const dp = get_display(dpy);
if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ if ((ctx != EGL_NO_CONTEXT) && (!validate_display_context(dpy, ctx))) {
+ // EGL_NO_CONTEXT is valid
+ return EGL_FALSE;
+ }
- if (read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE &&
- ctx == EGL_NO_CONTEXT)
- {
- EGLBoolean result = EGL_TRUE;
- ctx = getContext();
- if (ctx) {
- egl_context_t * const c = get_context(ctx);
- result = c->cnx->hooks->egl.eglMakeCurrent(dp->dpys[c->impl], 0, 0, 0);
- if (result == EGL_TRUE) {
- setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]);
- setContext(EGL_NO_CONTEXT);
+ // these are the underlying implementation's object
+ EGLContext impl_ctx = EGL_NO_CONTEXT;
+ EGLSurface impl_draw = EGL_NO_SURFACE;
+ EGLSurface impl_read = EGL_NO_SURFACE;
+
+ // these are our objects structs passed in
+ egl_context_t * c = NULL;
+ egl_surface_t const * d = NULL;
+ egl_surface_t const * r = NULL;
+
+ // these are the current objects structs
+ egl_context_t * cur_c = get_context(getContext());
+ egl_surface_t * cur_r = NULL;
+ egl_surface_t * cur_d = NULL;
+
+ if (ctx != EGL_NO_CONTEXT) {
+ c = get_context(ctx);
+ cur_r = get_surface(c->read);
+ cur_d = get_surface(c->draw);
+ impl_ctx = c->context;
+ } else {
+ // no context given, use the implementation of the current context
+ if (cur_c == NULL) {
+ // no current context
+ if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
+ // calling eglMakeCurrent( ..., EGL_NO_CONTEXT, !=0, !=0);
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
}
+ // not an error, there is just not current context.
+ return EGL_TRUE;
}
- return result;
}
- if (!validate_display_context(dpy, ctx))
- return EGL_FALSE;
-
- EGLSurface impl_draw = EGL_NO_SURFACE;
- EGLSurface impl_read = EGL_NO_SURFACE;
- egl_context_t * const c = get_context(ctx);
+ // retrieve the underlying implementation's draw EGLSurface
if (draw != EGL_NO_SURFACE) {
- egl_surface_t const * d = get_surface(draw);
- if (!d) return setError(EGL_BAD_SURFACE, EGL_FALSE);
- if (d->impl != c->impl)
+ d = get_surface(draw);
+ // make sure the EGLContext and EGLSurface passed in are for
+ // the same driver
+ if (c && d->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
impl_draw = d->surface;
}
+
+ // retrieve the underlying implementation's read EGLSurface
if (read != EGL_NO_SURFACE) {
- egl_surface_t const * r = get_surface(read);
- if (!r) return setError(EGL_BAD_SURFACE, EGL_FALSE);
- if (r->impl != c->impl)
+ r = get_surface(read);
+ // make sure the EGLContext and EGLSurface passed in are for
+ // the same driver
+ if (c && r->impl != c->impl)
return setError(EGL_BAD_MATCH, EGL_FALSE);
impl_read = r->surface;
}
- EGLBoolean result = c->cnx->hooks->egl.eglMakeCurrent(
- dp->dpys[c->impl], impl_draw, impl_read, c->context);
+
+ EGLBoolean result;
+
+ if (c) {
+ result = c->cnx->hooks->egl.eglMakeCurrent(
+ dp->disp[c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ } else {
+ result = cur_c->cnx->hooks->egl.eglMakeCurrent(
+ dp->disp[cur_c->impl].dpy, impl_draw, impl_read, impl_ctx);
+ }
if (result == EGL_TRUE) {
- setGlThreadSpecific(c->cnx->hooks);
- setContext(ctx);
- c->read = read;
- c->draw = draw;
+ // by construction, these are either 0 or valid (possibly terminated)
+ // it should be impossible for these to be invalid
+ ContextRef _cur_c(cur_c);
+ SurfaceRef _cur_r(cur_r);
+ SurfaceRef _cur_d(cur_d);
+
+ // cur_c has to be valid here (but could be terminated)
+ if (ctx != EGL_NO_CONTEXT) {
+ setGlThreadSpecific(c->cnx->hooks);
+ setContext(ctx);
+ _c.acquire();
+ } else {
+ setGlThreadSpecific(&gHooks[IMPL_NO_CONTEXT]);
+ setContext(EGL_NO_CONTEXT);
+ }
+ _cur_c.release();
+
+ _r.acquire();
+ _cur_r.release();
+ if (c) c->read = read;
+
+ _d.acquire();
+ _cur_d.release();
+ if (c) c->draw = draw;
}
return result;
}
@@ -1085,6 +1162,9 @@ EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw,
EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
EGLint attribute, EGLint *value)
{
+ ContextRef _c(ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
+
if (!validate_display_context(dpy, ctx))
return EGL_FALSE;
@@ -1092,17 +1172,23 @@ EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
egl_context_t * const c = get_context(ctx);
return c->cnx->hooks->egl.eglQueryContext(
- dp->dpys[c->impl], c->context, attribute, value);
+ dp->disp[c->impl].dpy, c->context, attribute, value);
}
EGLContext eglGetCurrentContext(void)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_CONTEXT.
+
EGLContext ctx = getContext();
return ctx;
}
EGLSurface eglGetCurrentSurface(EGLint readdraw)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_SURFACE.
+
EGLContext ctx = getContext();
if (ctx) {
egl_context_t const * const c = get_context(ctx);
@@ -1118,6 +1204,9 @@ EGLSurface eglGetCurrentSurface(EGLint readdraw)
EGLDisplay eglGetCurrentDisplay(void)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would correctly return EGL_NO_DISPLAY.
+
EGLContext ctx = getContext();
if (ctx) {
egl_context_t const * const c = get_context(ctx);
@@ -1129,6 +1218,9 @@ EGLDisplay eglGetCurrentDisplay(void)
EGLBoolean eglWaitGL(void)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would return GL_TRUE, which isn't wrong.
+
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
@@ -1146,6 +1238,9 @@ EGLBoolean eglWaitGL(void)
EGLBoolean eglWaitNative(EGLint engine)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would return GL_TRUE, which isn't wrong.
+
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
@@ -1182,9 +1277,11 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
// eglGetProcAddress() could be the very first function called
// in which case we must make sure we've initialized ourselves, this
// happens the first time egl_get_display() is called.
-
- if (egl_init_displays(EGL_DEFAULT_DISPLAY) == EGL_NO_DISPLAY)
- return NULL;
+
+ if (egl_init_drivers() == EGL_FALSE) {
+ setError(EGL_BAD_PARAMETER, NULL);
+ return NULL;
+ }
__eglMustCastToProperFunctionPointerType addr;
addr = findProcAddress(procname, gExtentionMap, NELEM(gExtentionMap));
@@ -1243,22 +1340,28 @@ __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
+ SurfaceRef _s(draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, draw))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(draw);
- return s->cnx->hooks->egl.eglSwapBuffers(dp->dpys[s->impl], s->surface);
+ return s->cnx->hooks->egl.eglSwapBuffers(dp->disp[s->impl].dpy, s->surface);
}
EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface,
NativePixmapType target)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
return s->cnx->hooks->egl.eglCopyBuffers(
- dp->dpys[s->impl], s->surface, target);
+ dp->disp[s->impl].dpy, s->surface, target);
}
const char* eglQueryString(EGLDisplay dpy, EGLint name)
@@ -1285,13 +1388,16 @@ const char* eglQueryString(EGLDisplay dpy, EGLint name)
EGLBoolean eglSurfaceAttrib(
EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->hooks->egl.eglSurfaceAttrib) {
return s->cnx->hooks->egl.eglSurfaceAttrib(
- dp->dpys[s->impl], s->surface, attribute, value);
+ dp->disp[s->impl].dpy, s->surface, attribute, value);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -1299,13 +1405,16 @@ EGLBoolean eglSurfaceAttrib(
EGLBoolean eglBindTexImage(
EGLDisplay dpy, EGLSurface surface, EGLint buffer)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->hooks->egl.eglBindTexImage) {
return s->cnx->hooks->egl.eglBindTexImage(
- dp->dpys[s->impl], s->surface, buffer);
+ dp->disp[s->impl].dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -1313,13 +1422,16 @@ EGLBoolean eglBindTexImage(
EGLBoolean eglReleaseTexImage(
EGLDisplay dpy, EGLSurface surface, EGLint buffer)
{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
if (!validate_display_surface(dpy, surface))
return EGL_FALSE;
egl_display_t const * const dp = get_display(dpy);
egl_surface_t const * const s = get_surface(surface);
if (s->cnx->hooks->egl.eglReleaseTexImage) {
return s->cnx->hooks->egl.eglReleaseTexImage(
- dp->dpys[s->impl], s->surface, buffer);
+ dp->disp[s->impl].dpy, s->surface, buffer);
}
return setError(EGL_BAD_SURFACE, EGL_FALSE);
}
@@ -1334,7 +1446,8 @@ EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
if (cnx->hooks->egl.eglSwapInterval) {
- if (cnx->hooks->egl.eglSwapInterval(dp->dpys[i], interval) == EGL_FALSE) {
+ if (cnx->hooks->egl.eglSwapInterval(
+ dp->disp[i].dpy, interval) == EGL_FALSE) {
res = EGL_FALSE;
}
}
@@ -1350,6 +1463,8 @@ EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
EGLBoolean eglWaitClient(void)
{
+ // could be called before eglInitialize(), but we wouldn't have a context
+ // then, and this function would return GL_TRUE, which isn't wrong.
EGLBoolean res = EGL_TRUE;
EGLContext ctx = getContext();
if (ctx) {
@@ -1371,6 +1486,10 @@ EGLBoolean eglWaitClient(void)
EGLBoolean eglBindAPI(EGLenum api)
{
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+ }
+
// bind this API on all EGLs
EGLBoolean res = EGL_TRUE;
for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
@@ -1388,6 +1507,10 @@ EGLBoolean eglBindAPI(EGLenum api)
EGLenum eglQueryAPI(void)
{
+ if (egl_init_drivers() == EGL_FALSE) {
+ return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+ }
+
for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
egl_connection_t* const cnx = &gEGLImpl[i];
if (cnx->dso) {
@@ -1426,7 +1549,172 @@ EGLSurface eglCreatePbufferFromClientBuffer(
if (!cnx) return EGL_FALSE;
if (cnx->hooks->egl.eglCreatePbufferFromClientBuffer) {
return cnx->hooks->egl.eglCreatePbufferFromClientBuffer(
- dp->dpys[i], buftype, buffer, dp->configs[i][index], attrib_list);
+ dp->disp[i].dpy, buftype, buffer,
+ dp->disp[i].config[index], attrib_list);
}
return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
}
+
+// ----------------------------------------------------------------------------
+// EGL_EGLEXT_VERSION 3
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
+ const EGLint *attrib_list)
+{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
+ if (!validate_display_surface(dpy, surface))
+ return EGL_FALSE;
+
+ egl_display_t const * const dp = get_display(dpy);
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (s->cnx->hooks->egl.eglLockSurfaceKHR) {
+ return s->cnx->hooks->egl.eglLockSurfaceKHR(
+ dp->disp[s->impl].dpy, s->surface, attrib_list);
+ }
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+}
+
+EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
+{
+ SurfaceRef _s(surface);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
+ if (!validate_display_surface(dpy, surface))
+ return EGL_FALSE;
+
+ egl_display_t const * const dp = get_display(dpy);
+ egl_surface_t const * const s = get_surface(surface);
+
+ if (s->cnx->hooks->egl.eglUnlockSurfaceKHR) {
+ return s->cnx->hooks->egl.eglUnlockSurfaceKHR(
+ dp->disp[s->impl].dpy, s->surface);
+ }
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+}
+
+EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
+ EGLClientBuffer buffer, const EGLint *attrib_list)
+{
+ if (ctx != EGL_NO_CONTEXT) {
+ ContextRef _c(ctx);
+ if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
+ if (!validate_display_context(dpy, ctx))
+ return EGL_NO_IMAGE_KHR;
+ egl_display_t const * const dp = get_display(dpy);
+ egl_context_t * const c = get_context(ctx);
+ // since we have an EGLContext, we know which implementation to use
+ EGLImageKHR image = c->cnx->hooks->egl.eglCreateImageKHR(
+ dp->disp[c->impl].dpy, c->context, target, buffer, attrib_list);
+ if (image == EGL_NO_IMAGE_KHR)
+ return image;
+
+ egl_image_t* result = new egl_image_t(dpy, ctx);
+ result->images[c->impl] = image;
+ return (EGLImageKHR)result;
+ } else {
+ // EGL_NO_CONTEXT is a valid parameter
+ egl_display_t const * const dp = get_display(dpy);
+ if (dp == 0) {
+ return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR);
+ }
+ // since we don't have a way to know which implementation to call,
+ // we're calling all of them
+
+ EGLImageKHR implImages[IMPL_NUM_DRIVERS_IMPLEMENTATIONS];
+ bool success = false;
+ for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ implImages[i] = EGL_NO_IMAGE_KHR;
+ if (cnx->dso) {
+ if (cnx->hooks->egl.eglCreateImageKHR) {
+ implImages[i] = cnx->hooks->egl.eglCreateImageKHR(
+ dp->disp[i].dpy, ctx, target, buffer, attrib_list);
+ if (implImages[i] != EGL_NO_IMAGE_KHR) {
+ success = true;
+ }
+ }
+ }
+ }
+ if (!success)
+ return EGL_NO_IMAGE_KHR;
+
+ egl_image_t* result = new egl_image_t(dpy, ctx);
+ memcpy(result->images, implImages, sizeof(implImages));
+ return (EGLImageKHR)result;
+ }
+}
+
+EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
+{
+ egl_display_t const * const dp = get_display(dpy);
+ if (dp == 0) {
+ return setError(EGL_BAD_DISPLAY, EGL_FALSE);
+ }
+
+ ImageRef _i(img);
+ if (!_i.get()) return setError(EGL_BAD_PARAMETER, EGL_FALSE);
+
+ egl_image_t* image = get_image(img);
+ bool success = false;
+ for (int i=0 ; i<IMPL_NUM_DRIVERS_IMPLEMENTATIONS ; i++) {
+ egl_connection_t* const cnx = &gEGLImpl[i];
+ if (image->images[i] != EGL_NO_IMAGE_KHR) {
+ if (cnx->dso) {
+ if (cnx->hooks->egl.eglCreateImageKHR) {
+ if (cnx->hooks->egl.eglDestroyImageKHR(
+ dp->disp[i].dpy, image->images[i])) {
+ success = true;
+ }
+ }
+ }
+ }
+ }
+ if (!success)
+ return EGL_FALSE;
+
+ _i.terminate();
+
+ return EGL_TRUE;
+}
+
+
+// ----------------------------------------------------------------------------
+// ANDROID extensions
+// ----------------------------------------------------------------------------
+
+EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw,
+ EGLint left, EGLint top, EGLint width, EGLint height)
+{
+ SurfaceRef _s(draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, EGL_FALSE);
+
+ if (!validate_display_surface(dpy, draw))
+ return EGL_FALSE;
+ egl_display_t const * const dp = get_display(dpy);
+ egl_surface_t const * const s = get_surface(draw);
+ if (s->cnx->hooks->egl.eglSetSwapRectangleANDROID) {
+ return s->cnx->hooks->egl.eglSetSwapRectangleANDROID(
+ dp->disp[s->impl].dpy, s->surface, left, top, width, height);
+ }
+ return setError(EGL_BAD_DISPLAY, NULL);
+}
+
+EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
+{
+ SurfaceRef _s(draw);
+ if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLClientBuffer*)0);
+
+ if (!validate_display_surface(dpy, draw))
+ return 0;
+ egl_display_t const * const dp = get_display(dpy);
+ egl_surface_t const * const s = get_surface(draw);
+ if (s->cnx->hooks->egl.eglGetRenderBufferANDROID) {
+ return s->cnx->hooks->egl.eglGetRenderBufferANDROID(
+ dp->disp[s->impl].dpy, s->surface);
+ }
+ return setError(EGL_BAD_DISPLAY, (EGLClientBuffer*)0);
+}
diff --git a/opengl/libs/EGL/egl_entries.in b/opengl/libs/EGL/egl_entries.in
new file mode 100644
index 0000000..5d89287
--- /dev/null
+++ b/opengl/libs/EGL/egl_entries.in
@@ -0,0 +1,57 @@
+EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
+EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
+EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
+EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
+EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)
+
+EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint *)
+EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint *)
+EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint *)
+EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
+EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
+EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
+EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
+EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
+EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint *)
+EGL_ENTRY(EGLBoolean, eglWaitGL, void)
+EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
+EGL_ENTRY(EGLint, eglGetError, void)
+EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
+EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char *)
+
+/* EGL 1.1 */
+
+EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
+EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
+EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
+
+/* EGL 1.2 */
+
+EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
+EGL_ENTRY(EGLenum, eglQueryAPI, void)
+EGL_ENTRY(EGLBoolean, eglWaitClient, void)
+EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
+EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *)
+
+/* EGL 1.3 */
+
+/* EGL 1.4 */
+
+/* EGL_EGLEXT_VERSION 3 */
+
+EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
+EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *)
+EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
+
+/* ANDROID extensions */
+
+EGL_ENTRY(EGLBoolean, eglSetSwapRectangleANDROID, EGLDisplay, EGLSurface, EGLint, EGLint, EGLint, EGLint)
+EGL_ENTRY(EGLClientBuffer, eglGetRenderBufferANDROID, EGLDisplay, EGLSurface)
diff --git a/opengl/libs/EGL/gpu.cpp b/opengl/libs/EGL/gpu.cpp
deleted file mode 100644
index 4c902c8..0000000
--- a/opengl/libs/EGL/gpu.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- ** Copyright 2007, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License");
- ** you may not use this file except in compliance with the License.
- ** You may obtain a copy of the License at
- **
- ** http://www.apache.org/licenses/LICENSE-2.0
- **
- ** Unless required by applicable law or agreed to in writing, software
- ** distributed under the License is distributed on an "AS IS" BASIS,
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ** See the License for the specific language governing permissions and
- ** limitations under the License.
- */
-
-#define LOG_TAG "EGL"
-
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-
-#include <sys/ioctl.h>
-
-#if HAVE_ANDROID_OS
-#include <linux/android_pmem.h>
-#endif
-
-#include <cutils/log.h>
-#include <cutils/properties.h>
-
-#include <utils/IMemory.h>
-#include <utils/threads.h>
-#include <utils/IServiceManager.h>
-#include <utils/IPCThreadState.h>
-#include <utils/Parcel.h>
-
-#include <ui/EGLDisplaySurface.h>
-#include <ui/ISurfaceComposer.h>
-
-#include "hooks.h"
-#include "egl_impl.h"
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-/*
- * we provide our own allocators for the GPU regions, these
- * allocators go through surfaceflinger
- */
-
-static Mutex gRegionsLock;
-static request_gpu_t gRegions;
-static sp<ISurfaceComposer> gSurfaceManager;
-GL_API ISurfaceComposer* GLES_localSurfaceManager = 0;
-
-extern egl_connection_t gEGLImpl[2];
-
-const sp<ISurfaceComposer>& getSurfaceFlinger()
-{
- Mutex::Autolock _l(gRegionsLock);
-
- /*
- * There is a little bit of voodoo magic here. We want to access
- * surfaceflinger for allocating GPU regions, however, when we are
- * running as part of surfaceflinger, we want to bypass the
- * service manager because surfaceflinger might not be registered yet.
- * SurfaceFlinger will populate "GLES_localSurfaceManager" with its
- * own address, so we can just use that.
- */
- if (gSurfaceManager == 0) {
- if (GLES_localSurfaceManager) {
- // we're running in SurfaceFlinger's context
- gSurfaceManager = GLES_localSurfaceManager;
- } else {
- // we're a remote process or not part of surfaceflinger,
- // go through the service manager
- sp<IServiceManager> sm = defaultServiceManager();
- if (sm != NULL) {
- sp<IBinder> binder = sm->getService(String16("SurfaceFlinger"));
- gSurfaceManager = interface_cast<ISurfaceComposer>(binder);
- }
- }
- }
- return gSurfaceManager;
-}
-
-class GPURevokeRequester : public BnGPUCallback
-{
-public:
- virtual void gpuLost() {
- LOGD("CONTEXT_LOST: Releasing GPU upon request from SurfaceFlinger.");
- gEGLImpl[IMPL_HARDWARE].hooks = &gHooks[IMPL_CONTEXT_LOST];
- }
-};
-
-static sp<GPURevokeRequester> gRevokerCallback;
-
-
-request_gpu_t* gpu_acquire(void* user)
-{
- sp<ISurfaceComposer> server( getSurfaceFlinger() );
-
- Mutex::Autolock _l(gRegionsLock);
- if (server == NULL) {
- return 0;
- }
-
- ISurfaceComposer::gpu_info_t info;
-
- if (gRevokerCallback == 0)
- gRevokerCallback = new GPURevokeRequester();
-
- status_t err = server->requestGPU(gRevokerCallback, &info);
- if (err != NO_ERROR) {
- LOGD("requestGPU returned %d", err);
- return 0;
- }
-
- if (info.regs == 0) {
- LOGD("requestGPU() failed");
- return 0;
- }
-
- bool failed = false;
- request_gpu_t* gpu = &gRegions;
- memset(gpu, 0, sizeof(*gpu));
-
- if (info.regs != 0) {
- sp<IMemoryHeap> heap(info.regs->getMemory());
- if (heap != 0) {
- int fd = heap->heapID();
- gpu->regs.fd = fd;
- gpu->regs.base = info.regs->pointer();
- gpu->regs.size = info.regs->size();
- gpu->regs.user = info.regs.get();
-#if HAVE_ANDROID_OS
- struct pmem_region region;
- if (ioctl(fd, PMEM_GET_PHYS, &region) >= 0)
- gpu->regs.phys = (void*)region.offset;
-#endif
- info.regs->incStrong(gpu);
- } else {
- LOGE("GPU register handle %p is invalid!", info.regs.get());
- failed = true;
- }
- }
-
- for (size_t i=0 ; i<info.count && !failed ; i++) {
- sp<IMemory>& region(info.regions[i].region);
- if (region != 0) {
- sp<IMemoryHeap> heap(region->getMemory());
- if (heap != 0) {
- const int fd = heap->heapID();
- gpu->gpu[i].fd = fd;
- gpu->gpu[i].base = region->pointer();
- gpu->gpu[i].size = region->size();
- gpu->gpu[i].user = region.get();
- gpu->gpu[i].offset = info.regions[i].reserved;
-#if HAVE_ANDROID_OS
- struct pmem_region reg;
- if (ioctl(fd, PMEM_GET_PHYS, &reg) >= 0)
- gpu->gpu[i].phys = (void*)reg.offset;
-#endif
- region->incStrong(gpu);
- } else {
- LOGE("GPU region handle [%d, %p] is invalid!", i, region.get());
- failed = true;
- }
- }
- }
-
- if (failed) {
- // something went wrong, clean up everything!
- if (gpu->regs.user) {
- static_cast<IMemory*>(gpu->regs.user)->decStrong(gpu);
- for (size_t i=0 ; i<info.count ; i++) {
- if (gpu->gpu[i].user) {
- static_cast<IMemory*>(gpu->gpu[i].user)->decStrong(gpu);
- }
- }
- }
- }
-
- gpu->count = info.count;
- return gpu;
-}
-
-int gpu_release(void*, request_gpu_t* gpu)
-{
- sp<IMemory> regs;
-
- { // scope for lock
- Mutex::Autolock _l(gRegionsLock);
- regs = static_cast<IMemory*>(gpu->regs.user);
- gpu->regs.user = 0;
- if (regs != 0) regs->decStrong(gpu);
-
- for (int i=0 ; i<gpu->count ; i++) {
- sp<IMemory> r(static_cast<IMemory*>(gpu->gpu[i].user));
- gpu->gpu[i].user = 0;
- if (r != 0) r->decStrong(gpu);
- }
- }
-
- // there is a special transaction to relinquish the GPU
- // (it will happen automatically anyway if we don't do this)
- Parcel data, reply;
- // NOTE: this transaction does not require an interface token
- regs->asBinder()->transact(1000, data, &reply);
- return 1;
-}
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/hooks.cpp b/opengl/libs/EGL/hooks.cpp
new file mode 100644
index 0000000..2246366
--- /dev/null
+++ b/opengl/libs/EGL/hooks.cpp
@@ -0,0 +1,67 @@
+/*
+ ** Copyright 2009, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <cutils/log.h>
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+void gl_unimplemented() {
+ LOGE("called unimplemented OpenGL ES API");
+}
+
+
+// ----------------------------------------------------------------------------
+// GL / EGL hooks
+// ----------------------------------------------------------------------------
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+#define GL_ENTRY(_r, _api, ...) #_api,
+#define EGL_ENTRY(_r, _api, ...) #_api,
+
+char const * const gl_names[] = {
+ #include "GLES_CM/gl_entries.in"
+ #include "GLES_CM/glext_entries.in"
+ NULL
+};
+
+char const * const gl2_names[] = {
+ #include "GLES2/gl2_entries.in"
+ #include "GLES2/gl2ext_entries.in"
+ NULL
+};
+
+char const * const egl_names[] = {
+ #include "egl_entries.in"
+ NULL
+};
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
new file mode 100644
index 0000000..e5358c3
--- /dev/null
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -0,0 +1,111 @@
+/*
+ ** Copyright 2007, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "hooks.h"
+#include "egl_impl.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+// Actual GL entry-points
+// ----------------------------------------------------------------------------
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+#if USE_FAST_TLS_KEY
+
+ #define API_ENTRY(_api) __attribute__((naked)) _api
+
+ #define CALL_GL_API(_api, ...) \
+ asm volatile( \
+ "mov r12, #0xFFFF0FFF \n" \
+ "ldr r12, [r12, #-15] \n" \
+ "ldr r12, [r12, %[tls]] \n" \
+ "cmp r12, #0 \n" \
+ "ldrne pc, [r12, %[api]] \n" \
+ "bx lr \n" \
+ : \
+ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \
+ [api] "J"(__builtin_offsetof(gl_hooks_t, gl2._api)) \
+ : \
+ );
+
+ #define CALL_GL_API_RETURN(_api, ...) \
+ CALL_GL_API(_api, __VA_ARGS__) \
+ return 0; // placate gcc's warnings. never reached.
+
+#else
+
+ #define API_ENTRY(_api) _api
+
+ #define CALL_GL_API(_api, ...) \
+ gl_hooks_t::gl2_t const * const _c = &getGlThreadSpecific()->gl2; \
+ _c->_api(__VA_ARGS__)
+
+ #define CALL_GL_API_RETURN(_api, ...) \
+ gl_hooks_t::gl2_t const * const _c = &getGlThreadSpecific()->gl2; \
+ return _c->_api(__VA_ARGS__)
+
+#endif
+
+
+extern "C" {
+#include "gl2_api.in"
+#include "gl2ext_api.in"
+}
+
+#undef API_ENTRY
+#undef CALL_GL_API
+#undef CALL_GL_API_RETURN
+
+
+/*
+ * These GL calls are special because they need to EGL to retrieve some
+ * informations before they can execute.
+ */
+
+extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
+extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
+
+
+void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
+{
+ GLeglImageOES implImage =
+ (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
+ __glEGLImageTargetTexture2DOES(target, implImage);
+}
+
+void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
+{
+ GLeglImageOES implImage =
+ (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
+ __glEGLImageTargetRenderbufferStorageOES(target, image);
+}
+
diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in
new file mode 100644
index 0000000..9c2e69a
--- /dev/null
+++ b/opengl/libs/GLES2/gl2_api.in
@@ -0,0 +1,426 @@
+void API_ENTRY(glActiveTexture)(GLenum texture) {
+ CALL_GL_API(glActiveTexture, texture);
+}
+void API_ENTRY(glAttachShader)(GLuint program, GLuint shader) {
+ CALL_GL_API(glAttachShader, program, shader);
+}
+void API_ENTRY(glBindAttribLocation)(GLuint program, GLuint index, const char* name) {
+ CALL_GL_API(glBindAttribLocation, program, index, name);
+}
+void API_ENTRY(glBindBuffer)(GLenum target, GLuint buffer) {
+ CALL_GL_API(glBindBuffer, target, buffer);
+}
+void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) {
+ CALL_GL_API(glBindFramebuffer, target, framebuffer);
+}
+void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) {
+ CALL_GL_API(glBindRenderbuffer, target, renderbuffer);
+}
+void API_ENTRY(glBindTexture)(GLenum target, GLuint texture) {
+ CALL_GL_API(glBindTexture, target, texture);
+}
+void API_ENTRY(glBlendColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
+ CALL_GL_API(glBlendColor, red, green, blue, alpha);
+}
+void API_ENTRY(glBlendEquation)( GLenum mode ) {
+ CALL_GL_API(glBlendEquation, mode);
+}
+void API_ENTRY(glBlendEquationSeparate)(GLenum modeRGB, GLenum modeAlpha) {
+ CALL_GL_API(glBlendEquationSeparate, modeRGB, modeAlpha);
+}
+void API_ENTRY(glBlendFunc)(GLenum sfactor, GLenum dfactor) {
+ CALL_GL_API(glBlendFunc, sfactor, dfactor);
+}
+void API_ENTRY(glBlendFuncSeparate)(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) {
+ CALL_GL_API(glBlendFuncSeparate, srcRGB, dstRGB, srcAlpha, dstAlpha);
+}
+void API_ENTRY(glBufferData)(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
+ CALL_GL_API(glBufferData, target, size, data, usage);
+}
+void API_ENTRY(glBufferSubData)(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
+ CALL_GL_API(glBufferSubData, target, offset, size, data);
+}
+GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) {
+ CALL_GL_API_RETURN(glCheckFramebufferStatus, target);
+}
+void API_ENTRY(glClear)(GLbitfield mask) {
+ CALL_GL_API(glClear, mask);
+}
+void API_ENTRY(glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
+ CALL_GL_API(glClearColor, red, green, blue, alpha);
+}
+void API_ENTRY(glClearDepthf)(GLclampf depth) {
+ CALL_GL_API(glClearDepthf, depth);
+}
+void API_ENTRY(glClearStencil)(GLint s) {
+ CALL_GL_API(glClearStencil, s);
+}
+void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) {
+ CALL_GL_API(glColorMask, red, green, blue, alpha);
+}
+void API_ENTRY(glCompileShader)(GLuint shader) {
+ CALL_GL_API(glCompileShader, shader);
+}
+void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data) {
+ CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data);
+}
+void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
+ CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data);
+}
+void API_ENTRY(glCopyTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+ CALL_GL_API(glCopyTexImage2D, target, level, internalformat, x, y, width, height, border);
+}
+void API_ENTRY(glCopyTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glCopyTexSubImage2D, target, level, xoffset, yoffset, x, y, width, height);
+}
+GLuint API_ENTRY(glCreateProgram)(void) {
+ CALL_GL_API_RETURN(glCreateProgram);
+}
+GLuint API_ENTRY(glCreateShader)(GLenum type) {
+ CALL_GL_API_RETURN(glCreateShader, type);
+}
+void API_ENTRY(glCullFace)(GLenum mode) {
+ CALL_GL_API(glCullFace, mode);
+}
+void API_ENTRY(glDeleteBuffers)(GLsizei n, const GLuint* buffers) {
+ CALL_GL_API(glDeleteBuffers, n, buffers);
+}
+void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers) {
+ CALL_GL_API(glDeleteFramebuffers, n, framebuffers);
+}
+void API_ENTRY(glDeleteProgram)(GLuint program) {
+ CALL_GL_API(glDeleteProgram, program);
+}
+void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers) {
+ CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers);
+}
+void API_ENTRY(glDeleteShader)(GLuint shader) {
+ CALL_GL_API(glDeleteShader, shader);
+}
+void API_ENTRY(glDeleteTextures)(GLsizei n, const GLuint* textures) {
+ CALL_GL_API(glDeleteTextures, n, textures);
+}
+void API_ENTRY(glDepthFunc)(GLenum func) {
+ CALL_GL_API(glDepthFunc, func);
+}
+void API_ENTRY(glDepthMask)(GLboolean flag) {
+ CALL_GL_API(glDepthMask, flag);
+}
+void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) {
+ CALL_GL_API(glDepthRangef, zNear, zFar);
+}
+void API_ENTRY(glDetachShader)(GLuint program, GLuint shader) {
+ CALL_GL_API(glDetachShader, program, shader);
+}
+void API_ENTRY(glDisable)(GLenum cap) {
+ CALL_GL_API(glDisable, cap);
+}
+void API_ENTRY(glDisableVertexAttribArray)(GLuint index) {
+ CALL_GL_API(glDisableVertexAttribArray, index);
+}
+void API_ENTRY(glDrawArrays)(GLenum mode, GLint first, GLsizei count) {
+ CALL_GL_API(glDrawArrays, mode, first, count);
+}
+void API_ENTRY(glDrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices) {
+ CALL_GL_API(glDrawElements, mode, count, type, indices);
+}
+void API_ENTRY(glEnable)(GLenum cap) {
+ CALL_GL_API(glEnable, cap);
+}
+void API_ENTRY(glEnableVertexAttribArray)(GLuint index) {
+ CALL_GL_API(glEnableVertexAttribArray, index);
+}
+void API_ENTRY(glFinish)(void) {
+ CALL_GL_API(glFinish);
+}
+void API_ENTRY(glFlush)(void) {
+ CALL_GL_API(glFlush);
+}
+void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) {
+ CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer);
+}
+void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) {
+ CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level);
+}
+void API_ENTRY(glFrontFace)(GLenum mode) {
+ CALL_GL_API(glFrontFace, mode);
+}
+void API_ENTRY(glGenBuffers)(GLsizei n, GLuint* buffers) {
+ CALL_GL_API(glGenBuffers, n, buffers);
+}
+void API_ENTRY(glGenerateMipmap)(GLenum target) {
+ CALL_GL_API(glGenerateMipmap, target);
+}
+void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint* framebuffers) {
+ CALL_GL_API(glGenFramebuffers, n, framebuffers);
+}
+void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers) {
+ CALL_GL_API(glGenRenderbuffers, n, renderbuffers);
+}
+void API_ENTRY(glGenTextures)(GLsizei n, GLuint* textures) {
+ CALL_GL_API(glGenTextures, n, textures);
+}
+void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) {
+ CALL_GL_API(glGetActiveAttrib, program, index, bufsize, length, size, type, name);
+}
+void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name) {
+ CALL_GL_API(glGetActiveUniform, program, index, bufsize, length, size, type, name);
+}
+void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) {
+ CALL_GL_API(glGetAttachedShaders, program, maxcount, count, shaders);
+}
+int API_ENTRY(glGetAttribLocation)(GLuint program, const char* name) {
+ CALL_GL_API_RETURN(glGetAttribLocation, program, name);
+}
+void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean* params) {
+ CALL_GL_API(glGetBooleanv, pname, params);
+}
+void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetBufferParameteriv, target, pname, params);
+}
+GLenum API_ENTRY(glGetError)(void) {
+ CALL_GL_API_RETURN(glGetError);
+}
+void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat* params) {
+ CALL_GL_API(glGetFloatv, pname, params);
+}
+void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params);
+}
+void API_ENTRY(glGetIntegerv)(GLenum pname, GLint* params) {
+ CALL_GL_API(glGetIntegerv, pname, params);
+}
+void API_ENTRY(glGetProgramiv)(GLuint program, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetProgramiv, program, pname, params);
+}
+void API_ENTRY(glGetProgramInfoLog)(GLuint program, GLsizei bufsize, GLsizei* length, char* infolog) {
+ CALL_GL_API(glGetProgramInfoLog, program, bufsize, length, infolog);
+}
+void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetShaderiv)(GLuint shader, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetShaderiv, shader, pname, params);
+}
+void API_ENTRY(glGetShaderInfoLog)(GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog) {
+ CALL_GL_API(glGetShaderInfoLog, shader, bufsize, length, infolog);
+}
+void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
+ CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision);
+}
+void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, char* source) {
+ CALL_GL_API(glGetShaderSource, shader, bufsize, length, source);
+}
+const GLubyte* API_ENTRY(glGetString)(GLenum name) {
+ CALL_GL_API_RETURN(glGetString, name);
+}
+void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat* params) {
+ CALL_GL_API(glGetTexParameterfv, target, pname, params);
+}
+void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetTexParameteriv, target, pname, params);
+}
+void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat* params) {
+ CALL_GL_API(glGetUniformfv, program, location, params);
+}
+void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint* params) {
+ CALL_GL_API(glGetUniformiv, program, location, params);
+}
+int API_ENTRY(glGetUniformLocation)(GLuint program, const char* name) {
+ CALL_GL_API_RETURN(glGetUniformLocation, program, name);
+}
+void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params) {
+ CALL_GL_API(glGetVertexAttribfv, index, pname, params);
+}
+void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params) {
+ CALL_GL_API(glGetVertexAttribiv, index, pname, params);
+}
+void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, void** pointer) {
+ CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer);
+}
+void API_ENTRY(glHint)(GLenum target, GLenum mode) {
+ CALL_GL_API(glHint, target, mode);
+}
+GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) {
+ CALL_GL_API_RETURN(glIsBuffer, buffer);
+}
+GLboolean API_ENTRY(glIsEnabled)(GLenum cap) {
+ CALL_GL_API_RETURN(glIsEnabled, cap);
+}
+GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) {
+ CALL_GL_API_RETURN(glIsFramebuffer, framebuffer);
+}
+GLboolean API_ENTRY(glIsProgram)(GLuint program) {
+ CALL_GL_API_RETURN(glIsProgram, program);
+}
+GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) {
+ CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer);
+}
+GLboolean API_ENTRY(glIsShader)(GLuint shader) {
+ CALL_GL_API_RETURN(glIsShader, shader);
+}
+GLboolean API_ENTRY(glIsTexture)(GLuint texture) {
+ CALL_GL_API_RETURN(glIsTexture, texture);
+}
+void API_ENTRY(glLineWidth)(GLfloat width) {
+ CALL_GL_API(glLineWidth, width);
+}
+void API_ENTRY(glLinkProgram)(GLuint program) {
+ CALL_GL_API(glLinkProgram, program);
+}
+void API_ENTRY(glPixelStorei)(GLenum pname, GLint param) {
+ CALL_GL_API(glPixelStorei, pname, param);
+}
+void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) {
+ CALL_GL_API(glPolygonOffset, factor, units);
+}
+void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {
+ CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels);
+}
+void API_ENTRY(glReleaseShaderCompiler)(void) {
+ CALL_GL_API(glReleaseShaderCompiler);
+}
+void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+ CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height);
+}
+void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) {
+ CALL_GL_API(glSampleCoverage, value, invert);
+}
+void API_ENTRY(glScissor)(GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glScissor, x, y, width, height);
+}
+void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length) {
+ CALL_GL_API(glShaderBinary, n, shaders, binaryformat, binary, length);
+}
+void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const char** string, const GLint* length) {
+ CALL_GL_API(glShaderSource, shader, count, string, length);
+}
+void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
+ CALL_GL_API(glStencilFunc, func, ref, mask);
+}
+void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) {
+ CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask);
+}
+void API_ENTRY(glStencilMask)(GLuint mask) {
+ CALL_GL_API(glStencilMask, mask);
+}
+void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) {
+ CALL_GL_API(glStencilMaskSeparate, face, mask);
+}
+void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) {
+ CALL_GL_API(glStencilOp, fail, zfail, zpass);
+}
+void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
+ CALL_GL_API(glStencilOpSeparate, face, fail, zfail, zpass);
+}
+void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) {
+ CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels);
+}
+void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
+ CALL_GL_API(glTexParameterf, target, pname, param);
+}
+void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat* params) {
+ CALL_GL_API(glTexParameterfv, target, pname, params);
+}
+void API_ENTRY(glTexParameteri)(GLenum target, GLenum pname, GLint param) {
+ CALL_GL_API(glTexParameteri, target, pname, param);
+}
+void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint* params) {
+ CALL_GL_API(glTexParameteriv, target, pname, params);
+}
+void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels) {
+ CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
+}
+void API_ENTRY(glUniform1f)(GLint location, GLfloat x) {
+ CALL_GL_API(glUniform1f, location, x);
+}
+void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat* v) {
+ CALL_GL_API(glUniform1fv, location, count, v);
+}
+void API_ENTRY(glUniform1i)(GLint location, GLint x) {
+ CALL_GL_API(glUniform1i, location, x);
+}
+void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint* v) {
+ CALL_GL_API(glUniform1iv, location, count, v);
+}
+void API_ENTRY(glUniform2f)(GLint location, GLfloat x, GLfloat y) {
+ CALL_GL_API(glUniform2f, location, x, y);
+}
+void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat* v) {
+ CALL_GL_API(glUniform2fv, location, count, v);
+}
+void API_ENTRY(glUniform2i)(GLint location, GLint x, GLint y) {
+ CALL_GL_API(glUniform2i, location, x, y);
+}
+void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint* v) {
+ CALL_GL_API(glUniform2iv, location, count, v);
+}
+void API_ENTRY(glUniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z) {
+ CALL_GL_API(glUniform3f, location, x, y, z);
+}
+void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat* v) {
+ CALL_GL_API(glUniform3fv, location, count, v);
+}
+void API_ENTRY(glUniform3i)(GLint location, GLint x, GLint y, GLint z) {
+ CALL_GL_API(glUniform3i, location, x, y, z);
+}
+void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint* v) {
+ CALL_GL_API(glUniform3iv, location, count, v);
+}
+void API_ENTRY(glUniform4f)(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
+ CALL_GL_API(glUniform4f, location, x, y, z, w);
+}
+void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat* v) {
+ CALL_GL_API(glUniform4fv, location, count, v);
+}
+void API_ENTRY(glUniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w) {
+ CALL_GL_API(glUniform4i, location, x, y, z, w);
+}
+void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint* v) {
+ CALL_GL_API(glUniform4iv, location, count, v);
+}
+void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
+ CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
+ CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value);
+}
+void API_ENTRY(glUniformMatrix4fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) {
+ CALL_GL_API(glUniformMatrix4fv, location, count, transpose, value);
+}
+void API_ENTRY(glUseProgram)(GLuint program) {
+ CALL_GL_API(glUseProgram, program);
+}
+void API_ENTRY(glValidateProgram)(GLuint program) {
+ CALL_GL_API(glValidateProgram, program);
+}
+void API_ENTRY(glVertexAttrib1f)(GLuint indx, GLfloat x) {
+ CALL_GL_API(glVertexAttrib1f, indx, x);
+}
+void API_ENTRY(glVertexAttrib1fv)(GLuint indx, const GLfloat* values) {
+ CALL_GL_API(glVertexAttrib1fv, indx, values);
+}
+void API_ENTRY(glVertexAttrib2f)(GLuint indx, GLfloat x, GLfloat y) {
+ CALL_GL_API(glVertexAttrib2f, indx, x, y);
+}
+void API_ENTRY(glVertexAttrib2fv)(GLuint indx, const GLfloat* values) {
+ CALL_GL_API(glVertexAttrib2fv, indx, values);
+}
+void API_ENTRY(glVertexAttrib3f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z) {
+ CALL_GL_API(glVertexAttrib3f, indx, x, y, z);
+}
+void API_ENTRY(glVertexAttrib3fv)(GLuint indx, const GLfloat* values) {
+ CALL_GL_API(glVertexAttrib3fv, indx, values);
+}
+void API_ENTRY(glVertexAttrib4f)(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
+ CALL_GL_API(glVertexAttrib4f, indx, x, y, z, w);
+}
+void API_ENTRY(glVertexAttrib4fv)(GLuint indx, const GLfloat* values) {
+ CALL_GL_API(glVertexAttrib4fv, indx, values);
+}
+void API_ENTRY(glVertexAttribPointer)(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr) {
+ CALL_GL_API(glVertexAttribPointer, indx, size, type, normalized, stride, ptr);
+}
+void API_ENTRY(glViewport)(GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glViewport, x, y, width, height);
+}
diff --git a/opengl/libs/GLES2/gl2_entries.in b/opengl/libs/GLES2/gl2_entries.in
new file mode 100644
index 0000000..6a41b94
--- /dev/null
+++ b/opengl/libs/GLES2/gl2_entries.in
@@ -0,0 +1,142 @@
+GL_ENTRY(void, glActiveTexture, GLenum texture)
+GL_ENTRY(void, glAttachShader, GLuint program, GLuint shader)
+GL_ENTRY(void, glBindAttribLocation, GLuint program, GLuint index, const char* name)
+GL_ENTRY(void, glBindBuffer, GLenum target, GLuint buffer)
+GL_ENTRY(void, glBindFramebuffer, GLenum target, GLuint framebuffer)
+GL_ENTRY(void, glBindRenderbuffer, GLenum target, GLuint renderbuffer)
+GL_ENTRY(void, glBindTexture, GLenum target, GLuint texture)
+GL_ENTRY(void, glBlendColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+GL_ENTRY(void, glBlendEquation, GLenum mode )
+GL_ENTRY(void, glBlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha)
+GL_ENTRY(void, glBlendFunc, GLenum sfactor, GLenum dfactor)
+GL_ENTRY(void, glBlendFuncSeparate, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)
+GL_ENTRY(void, glBufferData, GLenum target, GLsizeiptr size, const void* data, GLenum usage)
+GL_ENTRY(void, glBufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void* data)
+GL_ENTRY(GLenum, glCheckFramebufferStatus, GLenum target)
+GL_ENTRY(void, glClear, GLbitfield mask)
+GL_ENTRY(void, glClearColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
+GL_ENTRY(void, glClearDepthf, GLclampf depth)
+GL_ENTRY(void, glClearStencil, GLint s)
+GL_ENTRY(void, glColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)
+GL_ENTRY(void, glCompileShader, GLuint shader)
+GL_ENTRY(void, glCompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glCompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glCopyTexImage2D, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)
+GL_ENTRY(void, glCopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(GLuint, glCreateProgram, void)
+GL_ENTRY(GLuint, glCreateShader, GLenum type)
+GL_ENTRY(void, glCullFace, GLenum mode)
+GL_ENTRY(void, glDeleteBuffers, GLsizei n, const GLuint* buffers)
+GL_ENTRY(void, glDeleteFramebuffers, GLsizei n, const GLuint* framebuffers)
+GL_ENTRY(void, glDeleteProgram, GLuint program)
+GL_ENTRY(void, glDeleteRenderbuffers, GLsizei n, const GLuint* renderbuffers)
+GL_ENTRY(void, glDeleteShader, GLuint shader)
+GL_ENTRY(void, glDeleteTextures, GLsizei n, const GLuint* textures)
+GL_ENTRY(void, glDepthFunc, GLenum func)
+GL_ENTRY(void, glDepthMask, GLboolean flag)
+GL_ENTRY(void, glDepthRangef, GLclampf zNear, GLclampf zFar)
+GL_ENTRY(void, glDetachShader, GLuint program, GLuint shader)
+GL_ENTRY(void, glDisable, GLenum cap)
+GL_ENTRY(void, glDisableVertexAttribArray, GLuint index)
+GL_ENTRY(void, glDrawArrays, GLenum mode, GLint first, GLsizei count)
+GL_ENTRY(void, glDrawElements, GLenum mode, GLsizei count, GLenum type, const void* indices)
+GL_ENTRY(void, glEnable, GLenum cap)
+GL_ENTRY(void, glEnableVertexAttribArray, GLuint index)
+GL_ENTRY(void, glFinish, void)
+GL_ENTRY(void, glFlush, void)
+GL_ENTRY(void, glFramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
+GL_ENTRY(void, glFramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
+GL_ENTRY(void, glFrontFace, GLenum mode)
+GL_ENTRY(void, glGenBuffers, GLsizei n, GLuint* buffers)
+GL_ENTRY(void, glGenerateMipmap, GLenum target)
+GL_ENTRY(void, glGenFramebuffers, GLsizei n, GLuint* framebuffers)
+GL_ENTRY(void, glGenRenderbuffers, GLsizei n, GLuint* renderbuffers)
+GL_ENTRY(void, glGenTextures, GLsizei n, GLuint* textures)
+GL_ENTRY(void, glGetActiveAttrib, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+GL_ENTRY(void, glGetActiveUniform, GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, char* name)
+GL_ENTRY(void, glGetAttachedShaders, GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders)
+GL_ENTRY(int, glGetAttribLocation, GLuint program, const char* name)
+GL_ENTRY(void, glGetBooleanv, GLenum pname, GLboolean* params)
+GL_ENTRY(void, glGetBufferParameteriv, GLenum target, GLenum pname, GLint* params)
+GL_ENTRY(GLenum, glGetError, void)
+GL_ENTRY(void, glGetFloatv, GLenum pname, GLfloat* params)
+GL_ENTRY(void, glGetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetIntegerv, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetProgramiv, GLuint program, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetProgramInfoLog, GLuint program, GLsizei bufsize, GLsizei* length, char* infolog)
+GL_ENTRY(void, glGetRenderbufferParameteriv, GLenum target, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetShaderiv, GLuint shader, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetShaderInfoLog, GLuint shader, GLsizei bufsize, GLsizei* length, char* infolog)
+GL_ENTRY(void, glGetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision)
+GL_ENTRY(void, glGetShaderSource, GLuint shader, GLsizei bufsize, GLsizei* length, char* source)
+GL_ENTRY(const GLubyte*, glGetString, GLenum name)
+GL_ENTRY(void, glGetTexParameterfv, GLenum target, GLenum pname, GLfloat* params)
+GL_ENTRY(void, glGetTexParameteriv, GLenum target, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetUniformfv, GLuint program, GLint location, GLfloat* params)
+GL_ENTRY(void, glGetUniformiv, GLuint program, GLint location, GLint* params)
+GL_ENTRY(int, glGetUniformLocation, GLuint program, const char* name)
+GL_ENTRY(void, glGetVertexAttribfv, GLuint index, GLenum pname, GLfloat* params)
+GL_ENTRY(void, glGetVertexAttribiv, GLuint index, GLenum pname, GLint* params)
+GL_ENTRY(void, glGetVertexAttribPointerv, GLuint index, GLenum pname, void** pointer)
+GL_ENTRY(void, glHint, GLenum target, GLenum mode)
+GL_ENTRY(GLboolean, glIsBuffer, GLuint buffer)
+GL_ENTRY(GLboolean, glIsEnabled, GLenum cap)
+GL_ENTRY(GLboolean, glIsFramebuffer, GLuint framebuffer)
+GL_ENTRY(GLboolean, glIsProgram, GLuint program)
+GL_ENTRY(GLboolean, glIsRenderbuffer, GLuint renderbuffer)
+GL_ENTRY(GLboolean, glIsShader, GLuint shader)
+GL_ENTRY(GLboolean, glIsTexture, GLuint texture)
+GL_ENTRY(void, glLineWidth, GLfloat width)
+GL_ENTRY(void, glLinkProgram, GLuint program)
+GL_ENTRY(void, glPixelStorei, GLenum pname, GLint param)
+GL_ENTRY(void, glPolygonOffset, GLfloat factor, GLfloat units)
+GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels)
+GL_ENTRY(void, glReleaseShaderCompiler, void)
+GL_ENTRY(void, glRenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
+GL_ENTRY(void, glSampleCoverage, GLclampf value, GLboolean invert)
+GL_ENTRY(void, glScissor, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glShaderBinary, GLsizei n, const GLuint* shaders, GLenum binaryformat, const void* binary, GLsizei length)
+GL_ENTRY(void, glShaderSource, GLuint shader, GLsizei count, const char** string, const GLint* length)
+GL_ENTRY(void, glStencilFunc, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilFuncSeparate, GLenum face, GLenum func, GLint ref, GLuint mask)
+GL_ENTRY(void, glStencilMask, GLuint mask)
+GL_ENTRY(void, glStencilMaskSeparate, GLenum face, GLuint mask)
+GL_ENTRY(void, glStencilOp, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(void, glStencilOpSeparate, GLenum face, GLenum fail, GLenum zfail, GLenum zpass)
+GL_ENTRY(void, glTexImage2D, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
+GL_ENTRY(void, glTexParameterf, GLenum target, GLenum pname, GLfloat param)
+GL_ENTRY(void, glTexParameterfv, GLenum target, GLenum pname, const GLfloat* params)
+GL_ENTRY(void, glTexParameteri, GLenum target, GLenum pname, GLint param)
+GL_ENTRY(void, glTexParameteriv, GLenum target, GLenum pname, const GLint* params)
+GL_ENTRY(void, glTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels)
+GL_ENTRY(void, glUniform1f, GLint location, GLfloat x)
+GL_ENTRY(void, glUniform1fv, GLint location, GLsizei count, const GLfloat* v)
+GL_ENTRY(void, glUniform1i, GLint location, GLint x)
+GL_ENTRY(void, glUniform1iv, GLint location, GLsizei count, const GLint* v)
+GL_ENTRY(void, glUniform2f, GLint location, GLfloat x, GLfloat y)
+GL_ENTRY(void, glUniform2fv, GLint location, GLsizei count, const GLfloat* v)
+GL_ENTRY(void, glUniform2i, GLint location, GLint x, GLint y)
+GL_ENTRY(void, glUniform2iv, GLint location, GLsizei count, const GLint* v)
+GL_ENTRY(void, glUniform3f, GLint location, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glUniform3fv, GLint location, GLsizei count, const GLfloat* v)
+GL_ENTRY(void, glUniform3i, GLint location, GLint x, GLint y, GLint z)
+GL_ENTRY(void, glUniform3iv, GLint location, GLsizei count, const GLint* v)
+GL_ENTRY(void, glUniform4f, GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+GL_ENTRY(void, glUniform4fv, GLint location, GLsizei count, const GLfloat* v)
+GL_ENTRY(void, glUniform4i, GLint location, GLint x, GLint y, GLint z, GLint w)
+GL_ENTRY(void, glUniform4iv, GLint location, GLsizei count, const GLint* v)
+GL_ENTRY(void, glUniformMatrix2fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+GL_ENTRY(void, glUniformMatrix3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+GL_ENTRY(void, glUniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
+GL_ENTRY(void, glUseProgram, GLuint program)
+GL_ENTRY(void, glValidateProgram, GLuint program)
+GL_ENTRY(void, glVertexAttrib1f, GLuint indx, GLfloat x)
+GL_ENTRY(void, glVertexAttrib1fv, GLuint indx, const GLfloat* values)
+GL_ENTRY(void, glVertexAttrib2f, GLuint indx, GLfloat x, GLfloat y)
+GL_ENTRY(void, glVertexAttrib2fv, GLuint indx, const GLfloat* values)
+GL_ENTRY(void, glVertexAttrib3f, GLuint indx, GLfloat x, GLfloat y, GLfloat z)
+GL_ENTRY(void, glVertexAttrib3fv, GLuint indx, const GLfloat* values)
+GL_ENTRY(void, glVertexAttrib4f, GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+GL_ENTRY(void, glVertexAttrib4fv, GLuint indx, const GLfloat* values)
+GL_ENTRY(void, glVertexAttribPointer, GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* ptr)
+GL_ENTRY(void, glViewport, GLint x, GLint y, GLsizei width, GLsizei height)
diff --git a/opengl/libs/GLES2/gl2ext_api.in b/opengl/libs/GLES2/gl2ext_api.in
new file mode 100644
index 0000000..6eeecb3
--- /dev/null
+++ b/opengl/libs/GLES2/gl2ext_api.in
@@ -0,0 +1,105 @@
+void API_ENTRY(__glEGLImageTargetTexture2DOES)(GLenum target, GLeglImageOES image) {
+ CALL_GL_API(glEGLImageTargetTexture2DOES, target, image);
+}
+void API_ENTRY(__glEGLImageTargetRenderbufferStorageOES)(GLenum target, GLeglImageOES image) {
+ CALL_GL_API(glEGLImageTargetRenderbufferStorageOES, target, image);
+}
+void API_ENTRY(glGetProgramBinaryOES)(GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary) {
+ CALL_GL_API(glGetProgramBinaryOES, program, bufSize, length, binaryFormat, binary);
+}
+void API_ENTRY(glProgramBinaryOES)(GLuint program, GLenum binaryFormat, const void *binary, GLint length) {
+ CALL_GL_API(glProgramBinaryOES, program, binaryFormat, binary, length);
+}
+void* API_ENTRY(glMapBufferOES)(GLenum target, GLenum access) {
+ CALL_GL_API_RETURN(glMapBufferOES, target, access);
+}
+GLboolean API_ENTRY(glUnmapBufferOES)(GLenum target) {
+ CALL_GL_API_RETURN(glUnmapBufferOES, target);
+}
+void API_ENTRY(glGetBufferPointervOES)(GLenum target, GLenum pname, void** params) {
+ CALL_GL_API(glGetBufferPointervOES, target, pname, params);
+}
+void API_ENTRY(glTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) {
+ CALL_GL_API(glTexImage3DOES, target, level, internalformat, width, height, depth, border, format, type, pixels);
+}
+void API_ENTRY(glTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
+ CALL_GL_API(glTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+}
+void API_ENTRY(glCopyTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
+ CALL_GL_API(glCopyTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, x, y, width, height);
+}
+void API_ENTRY(glCompressedTexImage3DOES)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
+ CALL_GL_API(glCompressedTexImage3DOES, target, level, internalformat, width, height, depth, border, imageSize, data);
+}
+void API_ENTRY(glCompressedTexSubImage3DOES)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) {
+ CALL_GL_API(glCompressedTexSubImage3DOES, target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data);
+}
+void API_ENTRY(glFramebufferTexture3DOES)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset) {
+ CALL_GL_API(glFramebufferTexture3DOES, target, attachment, textarget, texture, level, zoffset);
+}
+void API_ENTRY(glGetPerfMonitorGroupsAMD)(GLint *numGroups, GLsizei groupsSize, GLuint *groups) {
+ CALL_GL_API(glGetPerfMonitorGroupsAMD, numGroups, groupsSize, groups);
+}
+void API_ENTRY(glGetPerfMonitorCountersAMD)(GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters) {
+ CALL_GL_API(glGetPerfMonitorCountersAMD, group, numCounters, maxActiveCounters, counterSize, counters);
+}
+void API_ENTRY(glGetPerfMonitorGroupStringAMD)(GLuint group, GLsizei bufSize, GLsizei *length, char *groupString) {
+ CALL_GL_API(glGetPerfMonitorGroupStringAMD, group, bufSize, length, groupString);
+}
+void API_ENTRY(glGetPerfMonitorCounterStringAMD)(GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString) {
+ CALL_GL_API(glGetPerfMonitorCounterStringAMD, group, counter, bufSize, length, counterString);
+}
+void API_ENTRY(glGetPerfMonitorCounterInfoAMD)(GLuint group, GLuint counter, GLenum pname, void *data) {
+ CALL_GL_API(glGetPerfMonitorCounterInfoAMD, group, counter, pname, data);
+}
+void API_ENTRY(glGenPerfMonitorsAMD)(GLsizei n, GLuint *monitors) {
+ CALL_GL_API(glGenPerfMonitorsAMD, n, monitors);
+}
+void API_ENTRY(glDeletePerfMonitorsAMD)(GLsizei n, GLuint *monitors) {
+ CALL_GL_API(glDeletePerfMonitorsAMD, n, monitors);
+}
+void API_ENTRY(glSelectPerfMonitorCountersAMD)(GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList) {
+ CALL_GL_API(glSelectPerfMonitorCountersAMD, monitor, enable, group, numCounters, countersList);
+}
+void API_ENTRY(glBeginPerfMonitorAMD)(GLuint monitor) {
+ CALL_GL_API(glBeginPerfMonitorAMD, monitor);
+}
+void API_ENTRY(glEndPerfMonitorAMD)(GLuint monitor) {
+ CALL_GL_API(glEndPerfMonitorAMD, monitor);
+}
+void API_ENTRY(glGetPerfMonitorCounterDataAMD)(GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten) {
+ CALL_GL_API(glGetPerfMonitorCounterDataAMD, monitor, pname, dataSize, data, bytesWritten);
+}
+void API_ENTRY(glDeleteFencesNV)(GLsizei n, const GLuint *fences) {
+ CALL_GL_API(glDeleteFencesNV, n, fences);
+}
+void API_ENTRY(glGenFencesNV)(GLsizei n, GLuint *fences) {
+ CALL_GL_API(glGenFencesNV, n, fences);
+}
+GLboolean API_ENTRY(glIsFenceNV)(GLuint fence) {
+ CALL_GL_API_RETURN(glIsFenceNV, fence);
+}
+GLboolean API_ENTRY(glTestFenceNV)(GLuint fence) {
+ CALL_GL_API_RETURN(glTestFenceNV, fence);
+}
+void API_ENTRY(glGetFenceivNV)(GLuint fence, GLenum pname, GLint *params) {
+ CALL_GL_API(glGetFenceivNV, fence, pname, params);
+}
+void API_ENTRY(glFinishFenceNV)(GLuint fence) {
+ CALL_GL_API(glFinishFenceNV, fence);
+}
+void API_ENTRY(glSetFenceNV)(GLuint fence, GLenum condition) {
+ CALL_GL_API(glSetFenceNV, fence, condition);
+}
+void API_ENTRY(glGetDriverControlsQCOM)(GLint *num, GLsizei size, GLuint *driverControls) {
+ CALL_GL_API(glGetDriverControlsQCOM, num, size, driverControls);
+}
+void API_ENTRY(glGetDriverControlStringQCOM)(GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString) {
+ CALL_GL_API(glGetDriverControlStringQCOM, driverControl, bufSize, length, driverControlString);
+}
+void API_ENTRY(glEnableDriverControlQCOM)(GLuint driverControl) {
+ CALL_GL_API(glEnableDriverControlQCOM, driverControl);
+}
+void API_ENTRY(glDisableDriverControlQCOM)(GLuint driverControl) {
+ CALL_GL_API(glDisableDriverControlQCOM, driverControl);
+}
diff --git a/opengl/libs/GLES2/gl2ext_entries.in b/opengl/libs/GLES2/gl2ext_entries.in
new file mode 100644
index 0000000..e608f5d
--- /dev/null
+++ b/opengl/libs/GLES2/gl2ext_entries.in
@@ -0,0 +1,35 @@
+GL_ENTRY(void, glEGLImageTargetTexture2DOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glEGLImageTargetRenderbufferStorageOES, GLenum target, GLeglImageOES image)
+GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
+GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length)
+GL_ENTRY(void*, glMapBufferOES, GLenum target, GLenum access)
+GL_ENTRY(GLboolean, glUnmapBufferOES, GLenum target)
+GL_ENTRY(void, glGetBufferPointervOES, GLenum target, GLenum pname, void** params)
+GL_ENTRY(void, glTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels)
+GL_ENTRY(void, glTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels)
+GL_ENTRY(void, glCopyTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)
+GL_ENTRY(void, glCompressedTexImage3DOES, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glCompressedTexSubImage3DOES, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data)
+GL_ENTRY(void, glFramebufferTexture3DOES, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)
+GL_ENTRY(void, glGetPerfMonitorGroupsAMD, GLint *numGroups, GLsizei groupsSize, GLuint *groups)
+GL_ENTRY(void, glGetPerfMonitorCountersAMD, GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters)
+GL_ENTRY(void, glGetPerfMonitorGroupStringAMD, GLuint group, GLsizei bufSize, GLsizei *length, char *groupString)
+GL_ENTRY(void, glGetPerfMonitorCounterStringAMD, GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, char *counterString)
+GL_ENTRY(void, glGetPerfMonitorCounterInfoAMD, GLuint group, GLuint counter, GLenum pname, void *data)
+GL_ENTRY(void, glGenPerfMonitorsAMD, GLsizei n, GLuint *monitors)
+GL_ENTRY(void, glDeletePerfMonitorsAMD, GLsizei n, GLuint *monitors)
+GL_ENTRY(void, glSelectPerfMonitorCountersAMD, GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *countersList)
+GL_ENTRY(void, glBeginPerfMonitorAMD, GLuint monitor)
+GL_ENTRY(void, glEndPerfMonitorAMD, GLuint monitor)
+GL_ENTRY(void, glGetPerfMonitorCounterDataAMD, GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten)
+GL_ENTRY(void, glDeleteFencesNV, GLsizei n, const GLuint *fences)
+GL_ENTRY(void, glGenFencesNV, GLsizei n, GLuint *fences)
+GL_ENTRY(GLboolean, glIsFenceNV, GLuint fence)
+GL_ENTRY(GLboolean, glTestFenceNV, GLuint fence)
+GL_ENTRY(void, glGetFenceivNV, GLuint fence, GLenum pname, GLint *params)
+GL_ENTRY(void, glFinishFenceNV, GLuint fence)
+GL_ENTRY(void, glSetFenceNV, GLuint fence, GLenum condition)
+GL_ENTRY(void, glGetDriverControlsQCOM, GLint *num, GLsizei size, GLuint *driverControls)
+GL_ENTRY(void, glGetDriverControlStringQCOM, GLuint driverControl, GLsizei bufSize, GLsizei *length, char *driverControlString)
+GL_ENTRY(void, glEnableDriverControlQCOM, GLuint driverControl)
+GL_ENTRY(void, glDisableDriverControlQCOM, GLuint driverControl)
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index 384b59a..3204d9a 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -14,8 +14,6 @@
** limitations under the License.
*/
-#define LOG_TAG "GLES_CM"
-
#include <ctype.h>
#include <string.h>
#include <errno.h>
@@ -121,16 +119,25 @@ extern "C" {
/*
- * These GL calls are special because they need to call into EGL to retrieve
- * some informations before they can execute.
+ * These GL calls are special because they need to EGL to retrieve some
+ * informations before they can execute.
*/
+extern "C" void __glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image);
+extern "C" void __glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image);
+
void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image)
{
+ GLeglImageOES implImage =
+ (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
+ __glEGLImageTargetTexture2DOES(target, implImage);
}
void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image)
{
+ GLeglImageOES implImage =
+ (GLeglImageOES)egl_get_image_for_current_context((EGLImageKHR)image);
+ __glEGLImageTargetRenderbufferStorageOES(target, image);
}
diff --git a/opengl/libs/gl_entries.in b/opengl/libs/GLES_CM/gl_entries.in
index d7cc5da..d7cc5da 100644
--- a/opengl/libs/gl_entries.in
+++ b/opengl/libs/GLES_CM/gl_entries.in
diff --git a/opengl/libs/glext_entries.in b/opengl/libs/GLES_CM/glext_entries.in
index dd09c71..dd09c71 100644
--- a/opengl/libs/glext_entries.in
+++ b/opengl/libs/GLES_CM/glext_entries.in
diff --git a/opengl/libs/egl_entries.in b/opengl/libs/egl_entries.in
deleted file mode 100644
index 3b4551b..0000000
--- a/opengl/libs/egl_entries.in
+++ /dev/null
@@ -1,52 +0,0 @@
-EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
-EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
-EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
-EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
-EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)
-
-EGL_ENTRY(EGLBoolean, eglGetConfigAttrib, EGLDisplay, EGLConfig, EGLint, EGLint *)
-EGL_ENTRY(EGLSurface, eglCreateWindowSurface, EGLDisplay, EGLConfig, NativeWindowType, const EGLint *)
-EGL_ENTRY(EGLSurface, eglCreatePixmapSurface, EGLDisplay, EGLConfig, NativePixmapType, const EGLint *)
-EGL_ENTRY(EGLSurface, eglCreatePbufferSurface, EGLDisplay, EGLConfig, const EGLint *)
-EGL_ENTRY(EGLBoolean, eglDestroySurface, EGLDisplay, EGLSurface)
-EGL_ENTRY(EGLBoolean, eglQuerySurface, EGLDisplay, EGLSurface, EGLint, EGLint *)
-EGL_ENTRY(EGLContext, eglCreateContext, EGLDisplay, EGLConfig, EGLContext, const EGLint *)
-EGL_ENTRY(EGLBoolean, eglDestroyContext, EGLDisplay, EGLContext)
-EGL_ENTRY(EGLBoolean, eglMakeCurrent, EGLDisplay, EGLSurface, EGLSurface, EGLContext)
-EGL_ENTRY(EGLContext, eglGetCurrentContext, void)
-EGL_ENTRY(EGLSurface, eglGetCurrentSurface, EGLint)
-EGL_ENTRY(EGLDisplay, eglGetCurrentDisplay, void)
-EGL_ENTRY(EGLBoolean, eglQueryContext, EGLDisplay, EGLContext, EGLint, EGLint *)
-EGL_ENTRY(EGLBoolean, eglWaitGL, void)
-EGL_ENTRY(EGLBoolean, eglWaitNative, EGLint)
-EGL_ENTRY(EGLBoolean, eglSwapBuffers, EGLDisplay, EGLSurface)
-EGL_ENTRY(EGLBoolean, eglCopyBuffers, EGLDisplay, EGLSurface, NativePixmapType)
-EGL_ENTRY(EGLint, eglGetError, void)
-EGL_ENTRY(const char*, eglQueryString, EGLDisplay, EGLint)
-EGL_ENTRY(__eglMustCastToProperFunctionPointerType, eglGetProcAddress, const char *)
-
-/* EGL 1.1 */
-
-EGL_ENTRY(EGLBoolean, eglSurfaceAttrib, EGLDisplay, EGLSurface, EGLint, EGLint)
-EGL_ENTRY(EGLBoolean, eglBindTexImage, EGLDisplay, EGLSurface, EGLint)
-EGL_ENTRY(EGLBoolean, eglReleaseTexImage, EGLDisplay, EGLSurface, EGLint)
-EGL_ENTRY(EGLBoolean, eglSwapInterval, EGLDisplay, EGLint)
-
-/* EGL 1.2 */
-
-EGL_ENTRY(EGLBoolean, eglBindAPI, EGLenum)
-EGL_ENTRY(EGLenum, eglQueryAPI, void)
-EGL_ENTRY(EGLBoolean, eglWaitClient, void)
-EGL_ENTRY(EGLBoolean, eglReleaseThread, void)
-EGL_ENTRY(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, EGLenum, EGLClientBuffer, EGLConfig, const EGLint *)
-
-/* EGL 1.3 */
-
-/* EGL 1.4 */
-
-/* EGL_EGLEXT_VERSION 3 */
-
-EGL_ENTRY(EGLBoolean, eglLockSurfaceKHR, EGLDisplay, EGLSurface, const EGLint *)
-EGL_ENTRY(EGLBoolean, eglUnlockSurfaceKHR, EGLDisplay, EGLSurface)
-EGL_ENTRY(EGLImageKHR, eglCreateImageKHR, EGLDisplay, EGLContext, EGLenum, EGLClientBuffer, const EGLint *)
-EGL_ENTRY(EGLBoolean, eglDestroyImageKHR, EGLDisplay, EGLImageKHR)
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index 312b176..ac286cb 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -31,13 +31,14 @@ struct gl_hooks_t;
struct egl_connection_t
{
- void volatile * dso;
+ void * dso;
gl_hooks_t * hooks;
EGLint major;
EGLint minor;
- int unavailable;
};
+EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
+
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/gl_enums.in b/opengl/libs/gl_enums.in
deleted file mode 100644
index ffc2fad..0000000
--- a/opengl/libs/gl_enums.in
+++ /dev/null
@@ -1,261 +0,0 @@
-GLENUM(GL_POINTS, 0x0000)
-GLENUM(GL_LINES, 0x0001)
-GLENUM(GL_LINE_LOOP, 0x0002)
-GLENUM(GL_LINE_STRIP, 0x0003)
-GLENUM(GL_TRIANGLES, 0x0004)
-GLENUM(GL_TRIANGLE_STRIP, 0x0005)
-GLENUM(GL_TRIANGLE_FAN, 0x0006)
-GLENUM(GL_ADD, 0x0104)
-GLENUM(GL_NEVER, 0x0200)
-GLENUM(GL_LESS, 0x0201)
-GLENUM(GL_EQUAL, 0x0202)
-GLENUM(GL_LEQUAL, 0x0203)
-GLENUM(GL_GREATER, 0x0204)
-GLENUM(GL_NOTEQUAL, 0x0205)
-GLENUM(GL_GEQUAL, 0x0206)
-GLENUM(GL_ALWAYS, 0x0207)
-GLENUM(GL_SRC_COLOR, 0x0300)
-GLENUM(GL_ONE_MINUS_SRC_COLOR, 0x0301)
-GLENUM(GL_SRC_ALPHA, 0x0302)
-GLENUM(GL_ONE_MINUS_SRC_ALPHA, 0x0303)
-GLENUM(GL_DST_ALPHA, 0x0304)
-GLENUM(GL_ONE_MINUS_DST_ALPHA, 0x0305)
-GLENUM(GL_DST_COLOR, 0x0306)
-GLENUM(GL_ONE_MINUS_DST_COLOR, 0x0307)
-GLENUM(GL_SRC_ALPHA_SATURATE, 0x0308)
-GLENUM(GL_FRONT, 0x0404)
-GLENUM(GL_BACK, 0x0405)
-GLENUM(GL_FRONT_AND_BACK, 0x0408)
-GLENUM(GL_INVALID_ENUM, 0x0500)
-GLENUM(GL_INVALID_VALUE, 0x0501)
-GLENUM(GL_INVALID_OPERATION, 0x0502)
-GLENUM(GL_STACK_OVERFLOW, 0x0503)
-GLENUM(GL_STACK_UNDERFLOW, 0x0504)
-GLENUM(GL_OUT_OF_MEMORY, 0x0505)
-GLENUM(GL_EXP, 0x0800)
-GLENUM(GL_EXP2, 0x0801)
-GLENUM(GL_CW, 0x0900)
-GLENUM(GL_CCW, 0x0901)
-GLENUM(GL_POINT_SMOOTH, 0x0B10)
-GLENUM(GL_SMOOTH_POINT_SIZE_RANGE, 0x0B12)
-GLENUM(GL_LINE_SMOOTH, 0x0B20)
-GLENUM(GL_SMOOTH_LINE_WIDTH_RANGE, 0x0B22)
-GLENUM(GL_CULL_FACE, 0x0B44)
-GLENUM(GL_LIGHTING, 0x0B50)
-GLENUM(GL_LIGHT_MODEL_TWO_SIDE, 0x0B52)
-GLENUM(GL_LIGHT_MODEL_AMBIENT, 0x0B53)
-GLENUM(GL_COLOR_MATERIAL, 0x0B57)
-GLENUM(GL_FOG, 0x0B60)
-GLENUM(GL_FOG_DENSITY, 0x0B62)
-GLENUM(GL_FOG_START, 0x0B63)
-GLENUM(GL_FOG_END, 0x0B64)
-GLENUM(GL_FOG_MODE, 0x0B65)
-GLENUM(GL_FOG_COLOR, 0x0B66)
-GLENUM(GL_DEPTH_TEST, 0x0B71)
-GLENUM(GL_STENCIL_TEST, 0x0B90)
-GLENUM(GL_NORMALIZE, 0x0BA1)
-GLENUM(GL_ALPHA_TEST, 0x0BC0)
-GLENUM(GL_DITHER, 0x0BD0)
-GLENUM(GL_BLEND, 0x0BE2)
-GLENUM(GL_COLOR_LOGIC_OP, 0x0BF2)
-GLENUM(GL_SCISSOR_TEST, 0x0C11)
-GLENUM(GL_PERSPECTIVE_CORRECTION_HINT, 0x0C50)
-GLENUM(GL_POINT_SMOOTH_HINT, 0x0C51)
-GLENUM(GL_LINE_SMOOTH_HINT, 0x0C52)
-GLENUM(GL_POLYGON_SMOOTH_HINT, 0x0C53)
-GLENUM(GL_FOG_HINT, 0x0C54)
-GLENUM(GL_UNPACK_ALIGNMENT, 0x0CF5)
-GLENUM(GL_PACK_ALIGNMENT, 0x0D05)
-GLENUM(GL_MAX_LIGHTS, 0x0D31)
-GLENUM(GL_MAX_CLIP_PLANES, 0x0D32)
-GLENUM(GL_MAX_TEXTURE_SIZE, 0x0D33)
-GLENUM(GL_MAX_MODELVIEW_STACK_DEPTH, 0x0D36)
-GLENUM(GL_MAX_PROJECTION_STACK_DEPTH, 0x0D38)
-GLENUM(GL_MAX_TEXTURE_STACK_DEPTH, 0x0D39)
-GLENUM(GL_MAX_VIEWPORT_DIMS, 0x0D3A)
-GLENUM(GL_RED_BITS, 0x0D52)
-GLENUM(GL_GREEN_BITS, 0x0D53)
-GLENUM(GL_BLUE_BITS, 0x0D54)
-GLENUM(GL_ALPHA_BITS, 0x0D55)
-GLENUM(GL_DEPTH_BITS, 0x0D56)
-GLENUM(GL_STENCIL_BITS, 0x0D57)
-GLENUM(GL_TEXTURE_2D, 0x0DE1)
-GLENUM(GL_DONT_CARE, 0x1100)
-GLENUM(GL_FASTEST, 0x1101)
-GLENUM(GL_NICEST, 0x1102)
-GLENUM(GL_AMBIENT, 0x1200)
-GLENUM(GL_DIFFUSE, 0x1201)
-GLENUM(GL_SPECULAR, 0x1202)
-GLENUM(GL_POSITION, 0x1203)
-GLENUM(GL_SPOT_DIRECTION, 0x1204)
-GLENUM(GL_SPOT_EXPONENT, 0x1205)
-GLENUM(GL_SPOT_CUTOFF, 0x1206)
-GLENUM(GL_CONSTANT_ATTENUATION, 0x1207)
-GLENUM(GL_LINEAR_ATTENUATION, 0x1208)
-GLENUM(GL_QUADRATIC_ATTENUATION, 0x1209)
-GLENUM(GL_BYTE, 0x1400)
-GLENUM(GL_UNSIGNED_BYTE, 0x1401)
-GLENUM(GL_SHORT, 0x1402)
-GLENUM(GL_UNSIGNED_SHORT, 0x1403)
-GLENUM(GL_FLOAT, 0x1406)
-GLENUM(GL_FIXED, 0x140C)
-GLENUM(GL_CLEAR, 0x1500)
-GLENUM(GL_AND, 0x1501)
-GLENUM(GL_AND_REVERSE, 0x1502)
-GLENUM(GL_COPY, 0x1503)
-GLENUM(GL_AND_INVERTED, 0x1504)
-GLENUM(GL_NOOP, 0x1505)
-GLENUM(GL_XOR, 0x1506)
-GLENUM(GL_OR, 0x1507)
-GLENUM(GL_NOR, 0x1508)
-GLENUM(GL_EQUIV, 0x1509)
-GLENUM(GL_INVERT, 0x150A)
-GLENUM(GL_OR_REVERSE, 0x150B)
-GLENUM(GL_COPY_INVERTED, 0x150C)
-GLENUM(GL_OR_INVERTED, 0x150D)
-GLENUM(GL_NAND, 0x150E)
-GLENUM(GL_SET, 0x150F)
-GLENUM(GL_EMISSION, 0x1600)
-GLENUM(GL_SHININESS, 0x1601)
-GLENUM(GL_AMBIENT_AND_DIFFUSE, 0x1602)
-GLENUM(GL_MODELVIEW, 0x1700)
-GLENUM(GL_PROJECTION, 0x1701)
-GLENUM(GL_TEXTURE, 0x1702)
-GLENUM(GL_ALPHA, 0x1906)
-GLENUM(GL_RGB, 0x1907)
-GLENUM(GL_RGBA, 0x1908)
-GLENUM(GL_LUMINANCE, 0x1909)
-GLENUM(GL_LUMINANCE_ALPHA, 0x190A)
-GLENUM(GL_FLAT, 0x1D00)
-GLENUM(GL_SMOOTH, 0x1D01)
-GLENUM(GL_KEEP, 0x1E00)
-GLENUM(GL_REPLACE, 0x1E01)
-GLENUM(GL_REPLACE, 0x1E01)
-GLENUM(GL_INCR, 0x1E02)
-GLENUM(GL_DECR, 0x1E03)
-GLENUM(GL_VENDOR, 0x1F00)
-GLENUM(GL_RENDERER, 0x1F01)
-GLENUM(GL_VERSION, 0x1F02)
-GLENUM(GL_EXTENSIONS, 0x1F03)
-GLENUM(GL_MODULATE, 0x2100)
-GLENUM(GL_DECAL, 0x2101)
-GLENUM(GL_TEXTURE_ENV_MODE, 0x2200)
-GLENUM(GL_TEXTURE_ENV_COLOR, 0x2201)
-GLENUM(GL_TEXTURE_ENV, 0x2300)
-GLENUM(GL_NEAREST, 0x2600)
-GLENUM(GL_LINEAR, 0x2601)
-GLENUM(GL_NEAREST_MIPMAP_NEAREST, 0x2700)
-GLENUM(GL_LINEAR_MIPMAP_NEAREST, 0x2701)
-GLENUM(GL_NEAREST_MIPMAP_LINEAR, 0x2702)
-GLENUM(GL_LINEAR_MIPMAP_LINEAR, 0x2703)
-GLENUM(GL_TEXTURE_MAG_FILTER, 0x2800)
-GLENUM(GL_TEXTURE_MIN_FILTER, 0x2801)
-GLENUM(GL_TEXTURE_WRAP_S, 0x2802)
-GLENUM(GL_TEXTURE_WRAP_T, 0x2803)
-GLENUM(GL_CLAMP, 0x2900)
-GLENUM(GL_REPEAT, 0x2901)
-GLENUM(GL_CLIP_PLANE0, 0x3000)
-GLENUM(GL_CLIP_PLANE1, 0x3001)
-GLENUM(GL_CLIP_PLANE2, 0x3002)
-GLENUM(GL_CLIP_PLANE3, 0x3003)
-GLENUM(GL_CLIP_PLANE4, 0x3004)
-GLENUM(GL_CLIP_PLANE5, 0x3005)
-GLENUM(GL_LIGHT0, 0x4000)
-GLENUM(GL_LIGHT1, 0x4001)
-GLENUM(GL_LIGHT2, 0x4002)
-GLENUM(GL_LIGHT3, 0x4003)
-GLENUM(GL_LIGHT4, 0x4004)
-GLENUM(GL_LIGHT5, 0x4005)
-GLENUM(GL_LIGHT6, 0x4006)
-GLENUM(GL_LIGHT7, 0x4007)
-GLENUM(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0x7E80)
-GLENUM(GL_UNSIGNED_SHORT_4_4_4_4, 0x8033)
-GLENUM(GL_UNSIGNED_SHORT_5_5_5_1, 0x8034)
-GLENUM(GL_POLYGON_OFFSET_FILL, 0x8037)
-GLENUM(GL_RESCALE_NORMAL, 0x803A)
-GLENUM(GL_VERTEX_ARRAY, 0x8074)
-GLENUM(GL_NORMAL_ARRAY, 0x8075)
-GLENUM(GL_COLOR_ARRAY, 0x8076)
-GLENUM(GL_TEXTURE_COORD_ARRAY, 0x8078)
-GLENUM(GL_MULTISAMPLE, 0x809D)
-GLENUM(GL_SAMPLE_ALPHA_TO_COVERAGE, 0x809E)
-GLENUM(GL_SAMPLE_ALPHA_TO_ONE, 0x809F)
-GLENUM(GL_SAMPLE_COVERAGE, 0x80A0)
-GLENUM(GL_MAX_ELEMENTS_VERTICES, 0x80E8)
-GLENUM(GL_MAX_ELEMENTS_INDICES, 0x80E9)
-GLENUM(GL_CLAMP_TO_EDGE, 0x812F)
-GLENUM(GL_GENERATE_MIPMAP, 0x8191)
-GLENUM(GL_GENERATE_MIPMAP_HINT, 0x8192)
-GLENUM(GL_UNSIGNED_SHORT_5_6_5, 0x8363)
-GLENUM(GL_ALIASED_POINT_SIZE_RANGE, 0x846D)
-GLENUM(GL_ALIASED_LINE_WIDTH_RANGE, 0x846E)
-GLENUM(GL_TEXTURE0, 0x84C0)
-GLENUM(GL_TEXTURE1, 0x84C1)
-GLENUM(GL_TEXTURE2, 0x84C2)
-GLENUM(GL_TEXTURE3, 0x84C3)
-GLENUM(GL_TEXTURE4, 0x84C4)
-GLENUM(GL_TEXTURE5, 0x84C5)
-GLENUM(GL_TEXTURE6, 0x84C6)
-GLENUM(GL_TEXTURE7, 0x84C7)
-GLENUM(GL_TEXTURE8, 0x84C8)
-GLENUM(GL_TEXTURE9, 0x84C9)
-GLENUM(GL_TEXTURE10, 0x84CA)
-GLENUM(GL_TEXTURE11, 0x84CB)
-GLENUM(GL_TEXTURE12, 0x84CC)
-GLENUM(GL_TEXTURE13, 0x84CD)
-GLENUM(GL_TEXTURE14, 0x84CE)
-GLENUM(GL_TEXTURE15, 0x84CF)
-GLENUM(GL_TEXTURE16, 0x84D0)
-GLENUM(GL_TEXTURE17, 0x84D1)
-GLENUM(GL_TEXTURE18, 0x84D2)
-GLENUM(GL_TEXTURE19, 0x84D3)
-GLENUM(GL_TEXTURE20, 0x84D4)
-GLENUM(GL_TEXTURE21, 0x84D5)
-GLENUM(GL_TEXTURE22, 0x84D6)
-GLENUM(GL_TEXTURE23, 0x84D7)
-GLENUM(GL_TEXTURE24, 0x84D8)
-GLENUM(GL_TEXTURE25, 0x84D9)
-GLENUM(GL_TEXTURE26, 0x84DA)
-GLENUM(GL_TEXTURE27, 0x84DB)
-GLENUM(GL_TEXTURE28, 0x84DC)
-GLENUM(GL_TEXTURE29, 0x84DD)
-GLENUM(GL_TEXTURE30, 0x84DE)
-GLENUM(GL_TEXTURE31, 0x84DF)
-GLENUM(GL_MAX_TEXTURE_UNITS, 0x84E2)
-GLENUM(GL_NUM_COMPRESSED_TEXTURE_FORMATS, 0x86A2)
-GLENUM(GL_COMPRESSED_TEXTURE_FORMATS, 0x86A3)
-GLENUM(GL_BUFFER_SIZE, 0x8764)
-GLENUM(GL_BUFFER_USAGE, 0x8765)
-GLENUM(GL_POINT_SPRITE_OES, 0x8861)
-GLENUM(GL_COORD_REPLACE_OES, 0x8862)
-GLENUM(GL_ARRAY_BUFFER, 0x8892)
-GLENUM(GL_ELEMENT_ARRAY_BUFFER, 0x8893)
-GLENUM(GL_ARRAY_BUFFER_BINDING, 0x8894)
-GLENUM(GL_ELEMENT_ARRAY_BUFFER_BINDING, 0x8895)
-GLENUM(GL_VERTEX_ARRAY_BUFFER_BINDING, 0x8896)
-GLENUM(GL_NORMAL_ARRAY_BUFFER_BINDING, 0x8897)
-GLENUM(GL_COLOR_ARRAY_BUFFER_BINDING, 0x8898)
-GLENUM(GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING, 0x889A)
-GLENUM(GL_STATIC_DRAW, 0x88E4)
-GLENUM(GL_DYNAMIC_DRAW, 0x88E8)
-GLENUM(GL_POINT_SIZE_ARRAY_TYPE_OES, 0x898A)
-GLENUM(GL_POINT_SIZE_ARRAY_STRIDE_OES, 0x898B)
-GLENUM(GL_POINT_SIZE_ARRAY_POINTER_OES, 0x898C)
-GLENUM(GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898D)
-GLENUM(GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898E)
-GLENUM(GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES, 0x898F)
-GLENUM(GL_PALETTE4_RGB8_OES, 0x8B90)
-GLENUM(GL_PALETTE4_RGBA8_OES, 0x8B91)
-GLENUM(GL_PALETTE4_R5_G6_B5_OES, 0x8B92)
-GLENUM(GL_PALETTE4_RGBA4_OES, 0x8B93)
-GLENUM(GL_PALETTE4_RGB5_A1_OES, 0x8B94)
-GLENUM(GL_PALETTE8_RGB8_OES, 0x8B95)
-GLENUM(GL_PALETTE8_RGBA8_OES, 0x8B96)
-GLENUM(GL_PALETTE8_R5_G6_B5_OES, 0x8B97)
-GLENUM(GL_PALETTE8_RGBA4_OES, 0x8B98)
-GLENUM(GL_PALETTE8_RGB5_A1_OES, 0x8B99)
-GLENUM(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, 0x8B9A)
-GLENUM(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, 0x8B9B)
-GLENUM(GL_POINT_SIZE_ARRAY_OES, 0x8B9C)
-GLENUM(GL_TEXTURE_CROP_RECT_OES, 0x8B9D)
-GLENUM(GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES, 0x8B9F)
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index fd97254..8c0357e 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -21,10 +21,14 @@
#include <string.h>
#include <errno.h>
+#include <pthread.h>
+
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
#if !defined(__arm__)
#define USE_SLOW_BINDING 1
@@ -56,12 +60,8 @@ const unsigned int NUM_DISPLAYS = 1;
enum {
IMPL_HARDWARE = 0,
IMPL_SOFTWARE,
-
IMPL_NUM_DRIVERS_IMPLEMENTATIONS,
-
- IMPL_CONTEXT_LOST = IMPL_NUM_DRIVERS_IMPLEMENTATIONS,
- IMPL_NO_CONTEXT,
-
+ IMPL_NO_CONTEXT = IMPL_NUM_DRIVERS_IMPLEMENTATIONS,
IMPL_NUM_IMPLEMENTATIONS
};
@@ -76,11 +76,15 @@ enum {
struct gl_hooks_t {
struct gl_t {
- #include "gl_entries.in"
- #include "glext_entries.in"
+ #include "GLES_CM/gl_entries.in"
+ #include "GLES_CM/glext_entries.in"
} gl;
+ struct gl2_t {
+ #include "GLES2/gl2_entries.in"
+ #include "GLES2/gl2ext_entries.in"
+ } gl2;
struct egl_t {
- #include "egl_entries.in"
+ #include "EGL/egl_entries.in"
} egl;
struct gl_ext_t {
void (*extensions[MAX_NUMBER_OF_GL_EXTENSIONS])(void);
@@ -94,6 +98,13 @@ struct gl_hooks_t {
extern gl_hooks_t gHooks[IMPL_NUM_IMPLEMENTATIONS];
extern pthread_key_t gGLWrapperKey;
+extern "C" void gl_unimplemented();
+
+extern char const * const gl_names[];
+extern char const * const gl2_names[];
+extern char const * const egl_names[];
+
+// ----------------------------------------------------------------------------
#if USE_FAST_TLS_KEY
diff --git a/opengl/libs/tools/enumextract.sh b/opengl/libs/tools/enumextract.sh
deleted file mode 100644
index 5707302..0000000
--- a/opengl/libs/tools/enumextract.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/sh
-
-awk '
-/^#define GL_/ {
- names[count] = $2;
- values[count] = $3;
- sort[count] = $3 + 0;
- count++;
-}
-END {
- for (i = 1; i < count; i++) {
- for (j = 0; j < i; j++) {
- if (sort[i] < sort[j]) {
- tn = names[i];
- tv = values[i];
- ts = sort[i];
- names[i] = names[j];
- values[i] = values[j];
- sort[i] = sort[j];
- names[j] = tn;
- values[j] = tv;
- sort[j] = ts;
- }
- }
- }
-
- for (i = 0; i < count; i++) {
- printf("GLENUM(%s, %s)\n", names[i], values[i]);
- }
-}
-' < $1
-
diff --git a/opengl/libs/tools/genfiles b/opengl/libs/tools/genfiles
index 107768b..4f8eda4 100755
--- a/opengl/libs/tools/genfiles
+++ b/opengl/libs/tools/genfiles
@@ -15,6 +15,13 @@
# limitations under the License.
./glapigen ../../include/GLES/gl.h > ../GLES_CM/gl_api.in
-./glentrygen ../../include/GLES/gl.h > ../gl_entries.in
+./glentrygen ../../include/GLES/gl.h > ../GLES_CM/gl_entries.in
+
./glapigen ../../include/GLES/glext.h > ../GLES_CM/glext_api.in
-./glentrygen ../../include/GLES/glext.h > ../glext_entries.in
+./glentrygen ../../include/GLES/glext.h > ../GLES_CM/glext_entries.in
+
+./glapigen ../../include/GLES2/gl2.h > ../GLES2/gl2_api.in
+./glentrygen ../../include/GLES2/gl2.h > ../GLES2/gl2_entries.in
+
+./glapigen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_api.in
+./glentrygen ../../include/GLES2/gl2ext.h > ../GLES2/gl2ext_entries.in
diff --git a/opengl/libs/tools/glapigen b/opengl/libs/tools/glapigen
index a2c3a7b..bd8dda3 100755
--- a/opengl/libs/tools/glapigen
+++ b/opengl/libs/tools/glapigen
@@ -16,16 +16,23 @@
use strict;
+sub rtrim($)
+{
+ my $string = shift;
+ $string =~ s/\s+$//;
+ return $string;
+}
+
while (my $line = <>) {
next if $line =~ /^\//;
next if $line =~ /^#/;
next if $line =~ /^\s*$/;
- if ($line !~ /^GL_API\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
+ if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
next;
}
- my $type = $1;
- my $name = $2;
- my $args = $3;
+ my $type = rtrim($2);
+ my $name = $3;
+ my $args = $4;
#printf("%s", $line);
diff --git a/opengl/libs/tools/glentrygen b/opengl/libs/tools/glentrygen
index 5e0f7b6..170f041 100755
--- a/opengl/libs/tools/glentrygen
+++ b/opengl/libs/tools/glentrygen
@@ -16,16 +16,23 @@
use strict;
+sub rtrim($)
+{
+ my $string = shift;
+ $string =~ s/\s+$//;
+ return $string;
+}
+
while (my $line = <>) {
next if $line =~ /^\//;
next if $line =~ /^#/;
next if $line =~ /^\s*$/;
- if ($line !~ /^GL_API\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
+ if ($line !~ /^GL_API(CALL)?\s+(.+)\s+GL_APIENTRY\s+([\w]+)\s*\(([^\)]+)\);/) {
next;
}
- my $type = $1;
- my $name = $2;
- my $args = $3;
+ my $type = rtrim($2);
+ my $name = $3;
+ my $args = $4;
printf("GL_ENTRY(%s, %s, %s)\n", $type, $name, $args);
}
diff --git a/opengl/tests/angeles/Android.mk b/opengl/tests/angeles/Android.mk
index e193483..d0c3221 100644
--- a/opengl/tests/angeles/Android.mk
+++ b/opengl/tests/angeles/Android.mk
@@ -2,7 +2,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= app-linux.c demo.c.arm
+LOCAL_SRC_FILES:= app-linux.cpp demo.c.arm
LOCAL_SHARED_LIBRARIES := libEGL libGLESv1_CM libui
LOCAL_MODULE:= angeles
LOCAL_MODULE_TAGS := optional
diff --git a/opengl/tests/angeles/app-linux.c b/opengl/tests/angeles/app-linux.c
deleted file mode 100644
index 7d0d320..0000000
--- a/opengl/tests/angeles/app-linux.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/* San Angeles Observation OpenGL ES version example
- * Copyright 2004-2005 Jetro Lauha
- * All rights reserved.
- * Web: http://iki.fi/jetro/
- *
- * This source is free software; you can redistribute it and/or
- * modify it under the terms of EITHER:
- * (1) The GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at
- * your option) any later version. The text of the GNU Lesser
- * General Public License is included with this source in the
- * file LICENSE-LGPL.txt.
- * (2) The BSD-style license that is included with this source in
- * the file LICENSE-BSD.txt.
- *
- * This source is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
- * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
- *
- * $Id: app-linux.c,v 1.4 2005/02/08 18:42:48 tonic Exp $
- * $Revision: 1.4 $
- *
- * Parts of this source file is based on test/example code from
- * GLESonGL implementation by David Blythe. Here is copy of the
- * license notice from that source:
- *
- * Copyright (C) 2003 David Blythe All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * DAVID BLYTHE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/time.h>
-
-#include <EGL/egl.h>
-#include <GLES/gl.h>
-
-#include "app.h"
-
-
-int gAppAlive = 1;
-
-static const char sAppName[] =
- "San Angeles Observation OpenGL ES version example (Linux)";
-
-static int sWindowWidth = WINDOW_DEFAULT_WIDTH;
-static int sWindowHeight = WINDOW_DEFAULT_HEIGHT;
-static EGLDisplay sEglDisplay = EGL_NO_DISPLAY;
-static EGLContext sEglContext = EGL_NO_CONTEXT;
-static EGLSurface sEglSurface = EGL_NO_SURFACE;
-
-const char *egl_strerror(unsigned err)
-{
- switch(err){
- case EGL_SUCCESS: return "SUCCESS";
- case EGL_NOT_INITIALIZED: return "NOT INITIALIZED";
- case EGL_BAD_ACCESS: return "BAD ACCESS";
- case EGL_BAD_ALLOC: return "BAD ALLOC";
- case EGL_BAD_ATTRIBUTE: return "BAD_ATTRIBUTE";
- case EGL_BAD_CONFIG: return "BAD CONFIG";
- case EGL_BAD_CONTEXT: return "BAD CONTEXT";
- case EGL_BAD_CURRENT_SURFACE: return "BAD CURRENT SURFACE";
- case EGL_BAD_DISPLAY: return "BAD DISPLAY";
- case EGL_BAD_MATCH: return "BAD MATCH";
- case EGL_BAD_NATIVE_PIXMAP: return "BAD NATIVE PIXMAP";
- case EGL_BAD_NATIVE_WINDOW: return "BAD NATIVE WINDOW";
- case EGL_BAD_PARAMETER: return "BAD PARAMETER";
- case EGL_BAD_SURFACE: return "BAD_SURFACE";
-// case EGL_CONTEXT_LOST: return "CONTEXT LOST";
- default: return "UNKNOWN";
- }
-}
-
-void egl_error(const char *name)
-{
- unsigned err = eglGetError();
- if(err != EGL_SUCCESS) {
- fprintf(stderr,"%s(): egl error 0x%x (%s)\n",
- name, err, egl_strerror(err));
- }
-}
-
-static void checkGLErrors()
-{
- GLenum error = glGetError();
- if (error != GL_NO_ERROR)
- fprintf(stderr, "GL Error: 0x%04x\n", (int)error);
-}
-
-
-static void checkEGLErrors()
-{
- EGLint error = eglGetError();
- // GLESonGL seems to be returning 0 when there is no errors?
- if (error && error != EGL_SUCCESS)
- fprintf(stderr, "EGL Error: 0x%04x\n", (int)error);
-}
-
-static int initGraphics()
-{
- EGLint s_configAttribs[] = {
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- #if 1
- EGL_DEPTH_SIZE, 16,
- EGL_STENCIL_SIZE, 0,
- #else
- EGL_ALPHA_SIZE, EGL_DONT_CARE,
- EGL_DEPTH_SIZE, EGL_DONT_CARE,
- EGL_STENCIL_SIZE, EGL_DONT_CARE,
- EGL_SURFACE_TYPE, EGL_DONT_CARE,
- #endif
- EGL_NONE
- };
-
- EGLint numConfigs = -1;
- EGLint majorVersion;
- EGLint minorVersion;
- EGLConfig config;
- EGLContext context;
- EGLSurface surface;
-
- EGLDisplay dpy;
-
- dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- egl_error("eglGetDisplay");
- fprintf(stderr,"dpy = 0x%08x\n", (unsigned) dpy);
-
- eglInitialize(dpy, &majorVersion, &minorVersion);
- egl_error("eglInitialize");
-
- eglGetConfigs(dpy, NULL, 0, &numConfigs);
- egl_error("eglGetConfigs");
- fprintf(stderr,"num configs %d\n", numConfigs);
-
- eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
- egl_error("eglChooseConfig");
-
- surface = eglCreateWindowSurface(dpy, config,
- android_createDisplaySurface(), NULL);
- egl_error("eglMapWindowSurface");
-
- fprintf(stderr,"surface = %p\n", surface);
-
- context = eglCreateContext(dpy, config, NULL, NULL);
- egl_error("eglCreateContext");
- fprintf(stderr,"context = %p\n", context);
-
- eglMakeCurrent(dpy, surface, surface, context);
- egl_error("eglMakeCurrent");
-
- eglQuerySurface(dpy, surface, EGL_WIDTH, &sWindowWidth);
- eglQuerySurface(dpy, surface, EGL_HEIGHT, &sWindowHeight);
-
- sEglDisplay = dpy;
- sEglSurface = surface;
- sEglContext = context;
-
- return EGL_TRUE;
-}
-
-
-static void deinitGraphics()
-{
- eglMakeCurrent(sEglDisplay, NULL, NULL, NULL);
- eglDestroyContext(sEglDisplay, sEglContext);
- eglDestroySurface(sEglDisplay, sEglSurface);
- eglTerminate(sEglDisplay);
-}
-
-
-int main(int argc, char *argv[])
-{
- // not referenced:
- argc = argc;
- argv = argv;
-
- if (!initGraphics())
- {
- fprintf(stderr, "Graphics initialization failed.\n");
- return EXIT_FAILURE;
- }
-
- appInit();
-
- while (gAppAlive)
- {
- struct timeval timeNow;
-
- if (gAppAlive)
- {
- gettimeofday(&timeNow, NULL);
- appRender(timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000,
- sWindowWidth, sWindowHeight);
- checkGLErrors();
- eglSwapBuffers(sEglDisplay, sEglSurface);
- checkEGLErrors();
- }
- }
-
- appDeinit();
- deinitGraphics();
-
- return EXIT_SUCCESS;
-}
diff --git a/opengl/tests/angeles/app-linux.cpp b/opengl/tests/angeles/app-linux.cpp
new file mode 100644
index 0000000..06fa0c2
--- /dev/null
+++ b/opengl/tests/angeles/app-linux.cpp
@@ -0,0 +1,213 @@
+/* San Angeles Observation OpenGL ES version example
+ * Copyright 2004-2005 Jetro Lauha
+ * All rights reserved.
+ * Web: http://iki.fi/jetro/
+ *
+ * This source is free software; you can redistribute it and/or
+ * modify it under the terms of EITHER:
+ * (1) The GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version. The text of the GNU Lesser
+ * General Public License is included with this source in the
+ * file LICENSE-LGPL.txt.
+ * (2) The BSD-style license that is included with this source in
+ * the file LICENSE-BSD.txt.
+ *
+ * This source is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
+ * LICENSE-LGPL.txt and LICENSE-BSD.txt for more details.
+ *
+ * $Id: app-linux.c,v 1.4 2005/02/08 18:42:48 tonic Exp $
+ * $Revision: 1.4 $
+ *
+ * Parts of this source file is based on test/example code from
+ * GLESonGL implementation by David Blythe. Here is copy of the
+ * license notice from that source:
+ *
+ * Copyright (C) 2003 David Blythe All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * DAVID BLYTHE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+#include "app.h"
+
+
+int gAppAlive = 1;
+
+static const char sAppName[] =
+ "San Angeles Observation OpenGL ES version example (Linux)";
+
+static int sWindowWidth = WINDOW_DEFAULT_WIDTH;
+static int sWindowHeight = WINDOW_DEFAULT_HEIGHT;
+static EGLDisplay sEglDisplay = EGL_NO_DISPLAY;
+static EGLContext sEglContext = EGL_NO_CONTEXT;
+static EGLSurface sEglSurface = EGL_NO_SURFACE;
+
+const char *egl_strerror(unsigned err)
+{
+ switch(err){
+ case EGL_SUCCESS: return "SUCCESS";
+ case EGL_NOT_INITIALIZED: return "NOT INITIALIZED";
+ case EGL_BAD_ACCESS: return "BAD ACCESS";
+ case EGL_BAD_ALLOC: return "BAD ALLOC";
+ case EGL_BAD_ATTRIBUTE: return "BAD_ATTRIBUTE";
+ case EGL_BAD_CONFIG: return "BAD CONFIG";
+ case EGL_BAD_CONTEXT: return "BAD CONTEXT";
+ case EGL_BAD_CURRENT_SURFACE: return "BAD CURRENT SURFACE";
+ case EGL_BAD_DISPLAY: return "BAD DISPLAY";
+ case EGL_BAD_MATCH: return "BAD MATCH";
+ case EGL_BAD_NATIVE_PIXMAP: return "BAD NATIVE PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW: return "BAD NATIVE WINDOW";
+ case EGL_BAD_PARAMETER: return "BAD PARAMETER";
+ case EGL_BAD_SURFACE: return "BAD_SURFACE";
+// case EGL_CONTEXT_LOST: return "CONTEXT LOST";
+ default: return "UNKNOWN";
+ }
+}
+
+void egl_error(const char *name)
+{
+ unsigned err = eglGetError();
+ if(err != EGL_SUCCESS) {
+ fprintf(stderr,"%s(): egl error 0x%x (%s)\n",
+ name, err, egl_strerror(err));
+ }
+}
+
+static void checkGLErrors()
+{
+ GLenum error = glGetError();
+ if (error != GL_NO_ERROR)
+ fprintf(stderr, "GL Error: 0x%04x\n", (int)error);
+}
+
+
+static void checkEGLErrors()
+{
+ EGLint error = eglGetError();
+ // GLESonGL seems to be returning 0 when there is no errors?
+ if (error && error != EGL_SUCCESS)
+ fprintf(stderr, "EGL Error: 0x%04x\n", (int)error);
+}
+
+static int initGraphics()
+{
+ EGLint configAttribs[] = {
+ EGL_DEPTH_SIZE, 16,
+ EGL_NONE
+ };
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ dpy, configAttribs, window, &config);
+ if (err) {
+ fprintf(stderr, "couldn't find an EGLConfig matching the screen format\n");
+ return 0;
+ }
+
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ egl_error("eglCreateWindowSurface");
+
+ fprintf(stderr,"surface = %p\n", surface);
+
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ egl_error("eglCreateContext");
+ fprintf(stderr,"context = %p\n", context);
+
+ eglMakeCurrent(dpy, surface, surface, context);
+ egl_error("eglMakeCurrent");
+
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &sWindowWidth);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &sWindowHeight);
+
+ sEglDisplay = dpy;
+ sEglSurface = surface;
+ sEglContext = context;
+
+ return EGL_TRUE;
+}
+
+
+static void deinitGraphics()
+{
+ eglMakeCurrent(sEglDisplay, NULL, NULL, NULL);
+ eglDestroyContext(sEglDisplay, sEglContext);
+ eglDestroySurface(sEglDisplay, sEglSurface);
+ eglTerminate(sEglDisplay);
+}
+
+
+int main(int argc, char *argv[])
+{
+ // not referenced:
+ argc = argc;
+ argv = argv;
+
+ if (!initGraphics())
+ {
+ fprintf(stderr, "Graphics initialization failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ appInit();
+
+ while (gAppAlive)
+ {
+ struct timeval timeNow;
+
+ if (gAppAlive)
+ {
+ gettimeofday(&timeNow, NULL);
+ appRender(timeNow.tv_sec * 1000 + timeNow.tv_usec / 1000,
+ sWindowWidth, sWindowHeight);
+ checkGLErrors();
+ eglSwapBuffers(sEglDisplay, sEglSurface);
+ checkEGLErrors();
+ }
+ }
+
+ appDeinit();
+ deinitGraphics();
+
+ return EXIT_SUCCESS;
+}
diff --git a/opengl/tests/copybits/Android.mk b/opengl/tests/copybits/Android.mk
new file mode 100644
index 0000000..d5ded42
--- /dev/null
+++ b/opengl/tests/copybits/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ copybits.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv1_CM \
+ libui
+
+LOCAL_MODULE:= test-opengl-copybits
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/opengl/tests/copybits/copybits.cpp b/opengl/tests/copybits/copybits.cpp
new file mode 100644
index 0000000..11dfb6e
--- /dev/null
+++ b/opengl/tests/copybits/copybits.cpp
@@ -0,0 +1,726 @@
+// Test software OpenGL hardware accelleration using copybits.
+
+#define LOG_TAG "copybits_test"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <ui/PixelFormat.h>
+
+#include <cutils/log.h>
+#include <cutils/native_handle.h>
+
+#include <utils/Atomic.h>
+
+#include <private/ui/SurfaceBuffer.h>
+#include <pixelflinger/pixelflinger.h>
+
+#include <hardware/gralloc.h>
+#include <hardware/hardware.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+#define EGL_EGLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+using namespace android;
+
+EGLDisplay eglDisplay;
+EGLSurface eglSurface;
+EGLContext eglContext;
+GLuint texture;
+
+hw_module_t const* gralloc_module;
+alloc_device_t *sAllocDev;
+
+#define FIXED_ONE 0x10000 /* 1.0 in 16.16 fixed point. */
+
+int init_gl_surface();
+void free_gl_surface();
+void init_scene();
+
+int create_physical_texture();
+int readTimer();
+
+// ===========================================================================
+// Buffer an implementation of android_native_buffer_t
+// ===========================================================================
+
+class NativeBuffer;
+
+class Buffer : public android::SurfaceBuffer
+{
+public:
+ // creates w * h buffer
+ Buffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage);
+
+ // return status
+ status_t initCheck() const;
+
+ uint32_t getWidth() const { return width; }
+ uint32_t getHeight() const { return height; }
+ uint32_t getStride() const { return stride; }
+ uint32_t getUsage() const { return usage; }
+ PixelFormat getPixelFormat() const { return format; }
+
+
+ android_native_buffer_t* getNativeBuffer() const;
+
+ void setPixel(int x, int y, int r, int g, int b, int a);
+
+ status_t lock(GGLSurface* surface, uint32_t usage);
+ void lock() {
+ GGLSurface s;
+ lock(&s, GRALLOC_USAGE_SW_WRITE_OFTEN);
+ mData = (void*)s.data;
+ }
+
+private:
+ friend class LightRefBase<Buffer>;
+ Buffer(const Buffer& rhs);
+ virtual ~Buffer();
+ Buffer& operator = (const Buffer& rhs);
+ const Buffer& operator = (const Buffer& rhs) const;
+
+ status_t initSize(uint32_t w, uint32_t h);
+
+ ssize_t mInitCheck;
+ void* mData;
+};
+
+Buffer::Buffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t usage)
+ : SurfaceBuffer(), mInitCheck(NO_INIT)
+{
+ this->usage = usage;
+ this->format = format;
+ if (w>0 && h>0) {
+ mInitCheck = initSize(w, h);
+ }
+}
+
+Buffer::~Buffer()
+{
+ if (handle) {
+ sAllocDev->free(sAllocDev, handle);
+ }
+}
+
+status_t Buffer::initCheck() const {
+ return mInitCheck;
+}
+
+android_native_buffer_t* Buffer::getNativeBuffer() const
+{
+ return static_cast<android_native_buffer_t*>(const_cast<Buffer*>(this));
+}
+
+status_t Buffer::initSize(uint32_t w, uint32_t h)
+{
+ status_t err = NO_ERROR;
+
+ err = sAllocDev->alloc(sAllocDev, w, h, format, usage, &handle, &stride);
+
+ if (err == NO_ERROR) {
+ if (err == NO_ERROR) {
+ width = w;
+ height = h;
+ }
+ }
+
+ return err;
+}
+
+status_t Buffer::lock(GGLSurface* sur, uint32_t usage)
+{
+ void* vaddr;
+ status_t res = SurfaceBuffer::lock(usage, &vaddr);
+ if (res == NO_ERROR && sur) {
+ sur->version = sizeof(GGLSurface);
+ sur->width = width;
+ sur->height = height;
+ sur->stride = stride;
+ sur->format = format;
+ sur->data = static_cast<GGLubyte*>(vaddr);
+ }
+ return res;
+}
+
+
+void Buffer::setPixel(int x, int y, int r, int g, int b, int a) {
+ if (x < 0 || (unsigned int) x >= width
+ || y < 0 || (unsigned int) y >= height) {
+ // clipped
+ return;
+ }
+ int index = stride * y + x;
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGB_565: {
+ unsigned short val = (unsigned short) (
+ ((0x1f & (r >> 3)) << 11)
+ | ((0x3f & (g >> 2)) << 5)
+ | (0x1f & (b >> 3)));
+ ((unsigned short*) mData)[index]= val;
+ }
+ break;
+ case HAL_PIXEL_FORMAT_RGBA_8888: { // ABGR
+ unsigned int val = (unsigned int)
+ (((a & 0xff) << 24)
+ | ((b & 0xff) << 16)
+ | ((g & 0xff) << 8)
+ | (r & 0xff));
+ ((unsigned int*) mData)[index] = val;
+ }
+ break;
+ default:
+ // Unsupported pixel format
+ break;
+ }
+}
+
+
+static void gluLookAt(float eyeX, float eyeY, float eyeZ,
+ float centerX, float centerY, float centerZ, float upX, float upY,
+ float upZ)
+{
+ // See the OpenGL GLUT documentation for gluLookAt for a description
+ // of the algorithm. We implement it in a straightforward way:
+
+ float fx = centerX - eyeX;
+ float fy = centerY - eyeY;
+ float fz = centerZ - eyeZ;
+
+ // Normalize f
+ float rlf = 1.0f / sqrtf(fx*fx + fy*fy + fz*fz);
+ fx *= rlf;
+ fy *= rlf;
+ fz *= rlf;
+
+ // Normalize up
+ float rlup = 1.0f / sqrtf(upX*upX + upY*upY + upZ*upZ);
+ upX *= rlup;
+ upY *= rlup;
+ upZ *= rlup;
+
+ // compute s = f x up (x means "cross product")
+
+ float sx = fy * upZ - fz * upY;
+ float sy = fz * upX - fx * upZ;
+ float sz = fx * upY - fy * upX;
+
+ // compute u = s x f
+ float ux = sy * fz - sz * fy;
+ float uy = sz * fx - sx * fz;
+ float uz = sx * fy - sy * fx;
+
+ float m[16] ;
+ m[0] = sx;
+ m[1] = ux;
+ m[2] = -fx;
+ m[3] = 0.0f;
+
+ m[4] = sy;
+ m[5] = uy;
+ m[6] = -fy;
+ m[7] = 0.0f;
+
+ m[8] = sz;
+ m[9] = uz;
+ m[10] = -fz;
+ m[11] = 0.0f;
+
+ m[12] = 0.0f;
+ m[13] = 0.0f;
+ m[14] = 0.0f;
+ m[15] = 1.0f;
+
+ glMultMatrixf(m);
+ glTranslatef(-eyeX, -eyeY, -eyeZ);
+}
+
+int init_gralloc() {
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &gralloc_module);
+ LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID);
+
+ if (err == 0) {
+ gralloc_open(gralloc_module, &sAllocDev);
+ }
+ return err;
+}
+
+int init_gl_surface(void)
+{
+ EGLint numConfigs = 1;
+ EGLConfig myConfig = {0};
+ EGLint attrib[] =
+ {
+ EGL_DEPTH_SIZE, 16,
+ EGL_NONE
+ };
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ printf("init_gl_surface\n");
+ if ( (eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY )
+ {
+ printf("eglGetDisplay failed\n");
+ return 0;
+ }
+
+ if ( eglInitialize(eglDisplay, NULL, NULL) != EGL_TRUE )
+ {
+ printf("eglInitialize failed\n");
+ return 0;
+ }
+
+ if ( EGLUtils::selectConfigForNativeWindow(eglDisplay, attrib, window, &myConfig) != 0)
+ {
+ printf("EGLUtils::selectConfigForNativeWindow failed\n");
+ return 0;
+ }
+
+
+ if ( (eglSurface = eglCreateWindowSurface(eglDisplay, myConfig,
+ window, 0)) == EGL_NO_SURFACE )
+ {
+ printf("eglCreateWindowSurface failed\n");
+ return 0;
+ }
+
+ if ( (eglContext = eglCreateContext(eglDisplay, myConfig, 0, 0)) == EGL_NO_CONTEXT )
+ {
+ printf("eglCreateContext failed\n");
+ return 0;
+ }
+
+ if ( eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) != EGL_TRUE )
+ {
+ printf("eglMakeCurrent failed\n");
+ return 0;
+ }
+
+#if EGL_ANDROID_swap_rectangle
+ eglSetSwapRectangleANDROID(eglDisplay, eglSurface, 0, 0, 320, 480);
+#endif
+
+ return 1;
+}
+
+void free_gl_surface(void)
+{
+ if (eglDisplay != EGL_NO_DISPLAY)
+ {
+ eglMakeCurrent( eglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT );
+ eglDestroyContext( eglDisplay, eglContext );
+ eglDestroySurface( eglDisplay, eglSurface );
+ eglTerminate( eglDisplay );
+ eglDisplay = EGL_NO_DISPLAY;
+ }
+}
+
+void init_scene(void)
+{
+ glDisable(GL_DITHER);
+ glEnable(GL_CULL_FACE);
+ float ratio = 320.0f / 480.0f;
+ glViewport(0, 0, 320, 480);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+ gluLookAt(
+ 0, 0, 3, // eye
+ 0, 0, 0, // center
+ 0, 1, 0); // up
+
+ glEnable(GL_TEXTURE_2D);
+}
+
+// #define USE_ALPHA_COLOR
+
+#define USE_GL_REPLACE
+//#define USE_GL_MODULATE
+
+// #define USE_BLEND
+
+#define USE_565
+// #define USE_8888
+
+// #define USE_NEAREST
+#define USE_LINEAR
+
+#define USE_SCALE
+
+void setSmoothGradient(Buffer* bufferObject) {
+ int pixels = bufferObject->getHeight() * bufferObject->getWidth();
+ int step = 0;
+ for (unsigned int y = 0; y < bufferObject->getHeight(); y++) {
+ for(unsigned int x = 0; x < bufferObject->getWidth() ; x++) {
+ int grey = step * 255 / pixels;
+ bufferObject->setPixel(x, y, grey, grey, grey, 255);
+ ++step;
+ }
+ }
+}
+
+void setSmoothAlphaGradient(Buffer* bufferObject) {
+ int pixels = bufferObject->getHeight() * bufferObject->getWidth();
+ int step = 0;
+ for (unsigned int y = 0; y < bufferObject->getHeight(); y++) {
+ for(unsigned int x = 0; x < bufferObject->getWidth() ; x++) {
+ int grey = step * 255 / pixels;
+ bufferObject->setPixel(x, y, 255, 255, 255, grey);
+ ++step;
+ }
+ }
+}
+
+void setOrientedCheckerboard(Buffer* bufferObject) {
+ bufferObject->setPixel(0, 0, 0, 0, 0, 255);
+ for(unsigned int x = 1; x < bufferObject->getWidth() ; x++) {
+ bufferObject->setPixel(x, 0, 0, 255, 0, 255);
+ }
+ for (unsigned int y = 1; y < bufferObject->getHeight(); y++) {
+ for(unsigned int x = 0; x < bufferObject->getWidth() ; x++) {
+ if ((x ^ y ) & 1) {
+ bufferObject->setPixel(x, y, 255, 255, 255, 255);
+ } else {
+ bufferObject->setPixel(x, y, 255, 0, 0, 255);
+ }
+ }
+ }
+}
+
+int create_physical_texture(unsigned int w, unsigned int h)
+{
+
+#ifdef USE_565
+ PixelFormat format = HAL_PIXEL_FORMAT_RGB_565;
+#else
+ PixelFormat format = HAL_PIXEL_FORMAT_RGBA_8888;
+#endif
+ int usage = GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_TEXTURE |
+ GRALLOC_USAGE_HW_2D; /* This is the key to allocating the texture in pmem. */
+ int32_t stride;
+ buffer_handle_t handle;
+
+ // Allocate the hardware buffer
+ Buffer* bufferObject = new Buffer(w, h, format, usage);
+
+ android_native_buffer_t* buffer = bufferObject->getNativeBuffer();
+
+ buffer->common.incRef(&buffer->common);
+
+ // create the new EGLImageKHR
+ EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_NONE };
+ EGLDisplay dpy = eglGetCurrentDisplay();
+ EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ (EGLClientBuffer)buffer, attrs);
+ if (image == EGL_NO_IMAGE_KHR) {
+ printf("Could not create an image %d\n", eglGetError());
+ return -1;
+ }
+
+ bufferObject->lock();
+ setOrientedCheckerboard(bufferObject);
+ // setSmoothGradient(bufferObject);
+ // setSmoothAlphaGradient(bufferObject);
+ bufferObject->unlock();
+
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+#ifdef USE_LINEAR
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+#elif defined(USE_NEAREST)
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+#endif
+
+#ifdef USE_GL_REPLACE
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+#elif defined(USE_GL_MODULATE)
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+#endif
+
+#ifdef USE_ALPHA_COLOR
+ glColor4f(1.0f, 1.0f, 1.0f, 0.4f);
+#else
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+#endif
+
+#ifdef USE_BLEND
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+#endif
+ return 0;
+}
+
+static const int SCALE_COUNT = 12;
+
+int scale(int base, int factor) {
+ static const float kTable[SCALE_COUNT] = {
+ 0.24f, 0.25f, 0.5f, 0.75f, 1.0f,
+ 1.5f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 5.0f
+ };
+ return base * kTable[factor];
+}
+
+class Timer {
+ struct timeval first;
+ double elapsedSeconds;
+
+public:
+ Timer() {}
+ void start() {
+ gettimeofday(&first, NULL);
+ }
+
+ void stop() {
+ struct timeval second,
+ elapsed;
+ gettimeofday(&second, NULL);
+
+ if (first.tv_usec > second.tv_usec) {
+ second.tv_usec += 1000000;
+ second.tv_sec--;
+ }
+
+ elapsedSeconds = (second.tv_sec - first.tv_sec) +
+ (second.tv_usec - first.tv_usec) / 1000000.0;
+ }
+
+ double getElapsedSeconds() {
+ return elapsedSeconds;
+ }
+
+ double getElapsedMs() {
+ return elapsedSeconds* 1000.0f;
+ }
+};
+
+int testTime()
+{
+ static const int WIDTH = 320;
+ static const int HEIGHT = 480;
+ static const int SCALE = 8;
+
+ if (create_physical_texture(WIDTH, HEIGHT) != 0) {
+ return -1;
+ }
+ // Need to do a dummy eglSwapBuffers first. Don't know why.
+ glClearColor(0.4, 1.0, 0.4, 0.4);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(eglDisplay, eglSurface);
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+#if defined(USE_SCALE)
+ static const int scaleOffset = 0;
+#else
+ static const int scaleOffset = 1;
+#endif
+ printf("ms\n");
+ for(int j = 0; j < SCALE; j++) {
+ int w = WIDTH >> (j + scaleOffset);
+ int h = HEIGHT >> j;
+ int cropRect[4] = {0,h,w,-h}; // Left bottom width height. Width and Height can be neg to flip.
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
+ Timer timer;
+ timer.start();
+
+ int copyCount = 1000;
+ for (int i = 0; i < copyCount; i++) {
+ glDrawTexiOES(0, 0, 0, w, h);
+ }
+
+ timer.stop();
+ printf("%g\n", timer.getElapsedMs() / copyCount);
+ }
+
+ eglSwapBuffers(eglDisplay, eglSurface);
+ return 0;
+}
+
+int testStretch()
+{
+ static const int WIDTH = 8;
+ static const int HEIGHT = 8;
+
+ if (create_physical_texture(WIDTH, HEIGHT) != 0) {
+ return -1;
+ }
+ // Need to do a dummy eglSwapBuffers first. Don't know why.
+ glClearColor(0.4, 1.0, 0.4, 1.0);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(eglDisplay, eglSurface);
+
+ int cropRect[4] = {0,HEIGHT,WIDTH,-HEIGHT}; // Left bottom width height. Width and Height can be neg to flip.
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
+
+ for(int frame = 0; frame < 2; frame++) {
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ int baseX = 10;
+ for (int x = 0; x < SCALE_COUNT; x++) {
+ int baseY = 10;
+ int width = scale(WIDTH, x);
+ for (int y = 0; y < SCALE_COUNT; y++) {
+ int height = scale(HEIGHT, y);
+ glDrawTexxOES(baseX << 16, baseY << 16, 0, width << 16, height << 16);
+ baseY += height + 10;
+ }
+ baseX += width + 10;
+ }
+
+ eglSwapBuffers(eglDisplay, eglSurface);
+ LOGD("wait 1s");
+ usleep(1000000);
+ }
+ return 0;
+}
+
+int testRot90()
+{
+ static const int WIDTH = 8;
+ static const int HEIGHT = 8;
+
+ if (create_physical_texture(WIDTH, HEIGHT) != 0) {
+ return -1;
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, 320, 480, 0, 0, 1);
+
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+
+ // Need to do a dummy eglSwapBuffers first. Don't know why.
+ glClearColor(0.4, 0.4, 0.4, 0.4);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(eglDisplay, eglSurface);
+
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
+ glDisable(GL_BLEND);
+ glShadeModel(GL_FLAT);
+ glDisable(GL_DITHER);
+ glDisable(GL_CULL_FACE);
+
+ for(int frame = 0; frame < 2; frame++) {
+ LOGD("frame = %d", frame);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ int baseX = 10;
+ for (int x = 0; x < SCALE_COUNT; x++) {
+ int baseY = 10;
+ int width = scale(WIDTH, x);
+ for (int y = 0; y < SCALE_COUNT; y++) {
+ int height = scale(HEIGHT, y);
+
+ // Code copied from SurfaceFlinger LayerBase.cpp
+
+ const GLfixed texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 0x10000 },
+ { 0x10000, 0x10000 },
+ { 0x10000, 0 }
+ };
+
+ GLfixed fx = baseX << 16;
+ GLfixed fy = baseY << 16;
+ GLfixed fw = width << 16;
+ GLfixed fh = height << 16;
+
+ /*
+ * Vertex pattern:
+ * (2)--(3)
+ * |\ |
+ * | \ |
+ * | \ |
+ * | \|
+ * (1)--(0)
+ *
+ */
+
+ const GLfixed vertices[4][2] = {
+ {fx + fw, fy},
+ {fx, fy},
+ {fx, fy + fh},
+ {fx + fw, fy + fh}
+ };
+
+ static const bool rotate90 = true;
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FIXED, 0, vertices);
+ glTexCoordPointer(2, GL_FIXED, 0, texCoords);
+
+ LOGD("testRot90 %d, %d %d, %d", baseX, baseY, width, height);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ baseY += height + 10;
+ }
+ baseX += width + 10;
+ }
+
+ eglSwapBuffers(eglDisplay, eglSurface);
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+
+ int q;
+ int start, end;
+
+ if (init_gralloc()) {
+ printf("gralloc initialization failed - exiting\n");
+ return 0;
+ }
+
+ printf("Initializing EGL...\n");
+
+ if(!init_gl_surface())
+ {
+ printf("GL initialisation failed - exiting\n");
+ return 0;
+ }
+
+ init_scene();
+
+ printf("Start test...\n");
+ // testTime();
+ testStretch();
+ //testRot90();
+ free_gl_surface();
+
+ return 0;
+}
diff --git a/opengl/tests/fillrate/Android.mk b/opengl/tests/fillrate/Android.mk
new file mode 100644
index 0000000..a7d30c2
--- /dev/null
+++ b/opengl/tests/fillrate/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ fillrate.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv1_CM \
+ libui
+
+LOCAL_MODULE:= test-opengl-fillrate
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/fillrate/fillrate.cpp b/opengl/tests/fillrate/fillrate.cpp
new file mode 100644
index 0000000..911d354
--- /dev/null
+++ b/opengl/tests/fillrate/fillrate.cpp
@@ -0,0 +1,161 @@
+/*
+**
+** Copyright 2006, 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.
+*/
+
+#define LOG_TAG "fillrate"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <utils/StopWatch.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ EGLint configAttribs[] = {
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ dpy, configAttribs, window, &config);
+ if (err) {
+ fprintf(stderr, "couldn't find an EGLConfig matching the screen format\n");
+ return 0;
+ }
+
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+
+ printf("w=%d, h=%d\n", w, h);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1,1,1,1);
+
+ uint32_t* t32 = (uint32_t*)malloc(512*512*4);
+ for (int y=0 ; y<512 ; y++) {
+ for (int x=0 ; x<512 ; x++) {
+ int u = x-256;
+ int v = y-256;
+ if (u*u+v*v < 256*256) {
+ t32[x+y*512] = 0x10FFFFFF;
+ } else {
+ t32[x+y*512] = 0x20FF0000;
+ }
+ }
+ }
+
+ const GLfloat vertices[4][2] = {
+ { 0, 0 },
+ { 0, h },
+ { w, h },
+ { w, 0 }
+ };
+
+ const GLfloat texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { 1, 0 }
+ };
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, t32);
+
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, w, 0, h, 0, 1);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+
+ eglSwapInterval(dpy, 1);
+
+ glClearColor(1,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ eglSwapBuffers(dpy, surface);
+
+
+ nsecs_t times[32];
+
+ for (int c=1 ; c<32 ; c++) {
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (int i=0 ; i<c ; i++) {
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ eglSwapBuffers(dpy, surface);
+ }
+
+
+ // for (int c=31 ; c>=1 ; c--) {
+ int j=0;
+ for (int c=1 ; c<32 ; c++) {
+ glClear(GL_COLOR_BUFFER_BIT);
+ nsecs_t now = systemTime();
+ for (int i=0 ; i<c ; i++) {
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ }
+ eglSwapBuffers(dpy, surface);
+ nsecs_t t = systemTime() - now;
+ times[j++] = t;
+ }
+
+ for (int c=1, j=0 ; c<32 ; c++, j++) {
+ nsecs_t t = times[j];
+ printf("%lld\t%d\t%f\n", t, c, (double(t)/c)/1000000.0);
+ }
+
+
+
+ eglTerminate(dpy);
+
+ return 0;
+}
diff --git a/opengl/tests/filter/Android.mk b/opengl/tests/filter/Android.mk
index 31b7d9a..a254127 100644
--- a/opengl/tests/filter/Android.mk
+++ b/opengl/tests/filter/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- filter.c
+ filter.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -14,4 +14,6 @@ LOCAL_MODULE:= test-opengl-filter
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/filter/filter.c b/opengl/tests/filter/filter.c
deleted file mode 100644
index de97119..0000000
--- a/opengl/tests/filter/filter.c
+++ /dev/null
@@ -1,130 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <EGL/egl.h>
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-int main(int argc, char** argv)
-{
- if (argc!=2 && argc!=3) {
- printf("usage: %s <0-6> [pbuffer]\n", argv[0]);
- return 0;
- }
-
- const int test = atoi(argv[1]);
- int usePbuffer = argc==3 && !strcmp(argv[2], "pbuffer");
- EGLint s_configAttribs[] = {
- EGL_SURFACE_TYPE, EGL_PBUFFER_BIT|EGL_WINDOW_BIT,
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_NONE
- };
-
- EGLint numConfigs = -1;
- EGLint majorVersion;
- EGLint minorVersion;
- EGLConfig config;
- EGLContext context;
- EGLSurface surface;
- EGLint w, h;
-
- EGLDisplay dpy;
-
- dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(dpy, &majorVersion, &minorVersion);
- eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
- if (!usePbuffer) {
- surface = eglCreateWindowSurface(dpy, config,
- android_createDisplaySurface(), NULL);
- } else {
- printf("using pbuffer\n");
- EGLint attribs[] = { EGL_WIDTH, 320, EGL_HEIGHT, 480, EGL_NONE };
- surface = eglCreatePbufferSurface(dpy, config, attribs);
- if (surface == EGL_NO_SURFACE) {
- printf("eglCreatePbufferSurface error %x\n", eglGetError());
- }
- }
- context = eglCreateContext(dpy, config, NULL, NULL);
- eglMakeCurrent(dpy, surface, surface, context);
- eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
- eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
- GLint dim = w<h ? w : h;
-
- glClear(GL_COLOR_BUFFER_BIT);
-
- GLint crop[4] = { 0, 4, 4, -4 };
- glBindTexture(GL_TEXTURE_2D, 0);
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glEnable(GL_TEXTURE_2D);
- glColor4f(1,1,1,1);
-
- // packing is always 4
- uint8_t t8[] = {
- 0x00, 0x55, 0x00, 0x55,
- 0xAA, 0xFF, 0xAA, 0xFF,
- 0x00, 0x55, 0x00, 0x55,
- 0xAA, 0xFF, 0xAA, 0xFF };
-
- uint16_t t16[] = {
- 0x0000, 0x5555, 0x0000, 0x5555,
- 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF,
- 0x0000, 0x5555, 0x0000, 0x5555,
- 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF };
-
- uint16_t t5551[] = {
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000,
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000 };
-
- uint32_t t32[] = {
- 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000,
- 0xFF00FF00, 0xFFFF0000, 0xFF000000, 0xFF0000FF,
- 0xFF00FFFF, 0xFF00FF00, 0x00FF00FF, 0xFFFFFF00,
- 0xFF000000, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF
- };
-
- switch(test)
- {
- case 1:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
- 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, t8);
- break;
- case 2:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
- 4, 4, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t16);
- break;
- case 3:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t16);
- break;
- case 4:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
- 4, 4, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, t16);
- break;
- case 5:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, t5551);
- break;
- case 6:
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
- 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, t32);
- break;
- }
-
- glDrawTexiOES(0, 0, 0, dim, dim);
-
- if (!usePbuffer) {
- eglSwapBuffers(dpy, surface);
- } else {
- glFinish();
- }
-
- eglTerminate(dpy);
- return 0;
-}
diff --git a/opengl/tests/filter/filter.cpp b/opengl/tests/filter/filter.cpp
new file mode 100644
index 0000000..2351909
--- /dev/null
+++ b/opengl/tests/filter/filter.cpp
@@ -0,0 +1,190 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+#define USE_DRAW_TEXTURE 1
+
+int main(int argc, char** argv)
+{
+ if (argc!=2 && argc!=3) {
+ printf("usage: %s <0-6> [pbuffer]\n", argv[0]);
+ return 0;
+ }
+
+ const int test = atoi(argv[1]);
+ int usePbuffer = argc==3 && !strcmp(argv[2], "pbuffer");
+ EGLint s_configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT|EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 6,
+ EGL_BLUE_SIZE, 5,
+ EGL_NONE
+ };
+
+ EGLint numConfigs = -1;
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLConfig config;
+ EGLContext context;
+ EGLSurface surface;
+ EGLint w, h;
+
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = 0;
+ if (!usePbuffer) {
+ window = android_createDisplaySurface();
+ }
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+ if (!usePbuffer) {
+ EGLUtils::selectConfigForNativeWindow(
+ dpy, s_configAttribs, window, &config);
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ } else {
+ printf("using pbuffer\n");
+ eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
+ EGLint attribs[] = { EGL_WIDTH, 320, EGL_HEIGHT, 480, EGL_NONE };
+ surface = eglCreatePbufferSurface(dpy, config, attribs);
+ if (surface == EGL_NO_SURFACE) {
+ printf("eglCreatePbufferSurface error %x\n", eglGetError());
+ }
+ }
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ GLint dim = w<h ? w : h;
+
+ glViewport(0, 0, w, h);
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrthof(0, w, 0, h, 0, 1);
+
+ glClearColor(0,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ GLint crop[4] = { 0, 4, 4, -4 };
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1,1,1,1);
+
+ // packing is always 4
+ uint8_t t8[] = {
+ 0x00, 0x55, 0x00, 0x55,
+ 0xAA, 0xFF, 0xAA, 0xFF,
+ 0x00, 0x55, 0x00, 0x55,
+ 0xAA, 0xFF, 0xAA, 0xFF };
+
+ uint16_t t16[] = {
+ 0x0000, 0x5555, 0x0000, 0x5555,
+ 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF,
+ 0x0000, 0x5555, 0x0000, 0x5555,
+ 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF };
+
+ uint16_t t5551[] = {
+ 0x0000, 0xFFFF, 0x0000, 0xFFFF,
+ 0xFFFF, 0x0000, 0xFFFF, 0x0000,
+ 0x0000, 0xFFFF, 0x0000, 0xFFFF,
+ 0xFFFF, 0x0000, 0xFFFF, 0x0000 };
+
+ uint32_t t32[] = {
+ 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000,
+ 0xFF00FF00, 0xFFFF0000, 0xFF000000, 0xFF0000FF,
+ 0xFF00FFFF, 0xFF00FF00, 0x00FF00FF, 0xFFFFFF00,
+ 0xFF000000, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF
+ };
+
+ switch(test)
+ {
+ case 1:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,
+ 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, t8);
+ break;
+ case 2:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+ 4, 4, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t16);
+ break;
+ case 3:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t16);
+ break;
+ case 4:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA,
+ 4, 4, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, t16);
+ break;
+ case 5:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, t5551);
+ break;
+ case 6:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, t32);
+ break;
+ }
+
+ //glDrawTexiOES(0, 0, 0, dim, dim);
+
+ const GLfloat vertices[4][2] = {
+ { 0, 0 },
+ { 0, dim },
+ { dim, dim },
+ { dim, 0 }
+ };
+
+ const GLfloat texCoords[4][2] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { 1, 0 }
+ };
+
+ if (!usePbuffer) {
+ eglSwapBuffers(dpy, surface);
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glScissor(0,dim,dim,h-dim);
+ glDisable(GL_SCISSOR_TEST);
+
+ for (int y=0 ; y<dim ; y++) {
+ //glDisable(GL_SCISSOR_TEST);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ //glEnable(GL_SCISSOR_TEST);
+
+#if USE_DRAW_TEXTURE && GL_OES_draw_texture
+ glDrawTexiOES(0, y, 1, dim, dim);
+#else
+ glLoadIdentity();
+ glTranslatef(0, y, 0);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertices);
+ glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+#endif
+
+ if (!usePbuffer) {
+ eglSwapBuffers(dpy, surface);
+ } else {
+ glFinish();
+ }
+ }
+
+ eglTerminate(dpy);
+ return 0;
+}
diff --git a/opengl/tests/finish/Android.mk b/opengl/tests/finish/Android.mk
index 8b46cd7..5620814 100644
--- a/opengl/tests/finish/Android.mk
+++ b/opengl/tests/finish/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- finish.c
+ finish.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -14,4 +14,6 @@ LOCAL_MODULE:= test-opengl-finish
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/finish/finish.c b/opengl/tests/finish/finish.c
deleted file mode 100644
index 45fc758..0000000
--- a/opengl/tests/finish/finish.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <sched.h>
-#include <sys/resource.h>
-
-#include <EGL/egl.h>
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-
-long long systemTime()
-{
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_MONOTONIC, &t);
- return (long long)(t.tv_sec)*1000000000LL + t.tv_nsec;
-}
-
-int main(int argc, char** argv)
-{
- EGLint s_configAttribs[] = {
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_NONE
- };
-
- EGLint numConfigs = -1;
- EGLint majorVersion;
- EGLint minorVersion;
- EGLConfig config;
- EGLContext context;
- EGLSurface surface;
- EGLint w, h;
-
- EGLDisplay dpy;
-
- dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(dpy, &majorVersion, &minorVersion);
- eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
- surface = eglCreateWindowSurface(dpy, config,
- android_createDisplaySurface(), NULL);
- context = eglCreateContext(dpy, config, NULL, NULL);
- eglMakeCurrent(dpy, surface, surface, context);
- eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
- eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
- GLint dim = w<h ? w : h;
-
- glBindTexture(GL_TEXTURE_2D, 0);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glEnable(GL_TEXTURE_2D);
- glColor4f(1,1,1,1);
- glDisable(GL_DITHER);
- glShadeModel(GL_FLAT);
-
- long long now, t;
- int i;
-
- char* texels = malloc(512*512*2);
- memset(texels,0xFF,512*512*2);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
- 512, 512, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texels);
-
- char* dst = malloc(320*480*2);
- memset(dst, 0, 320*480*2);
- printf("307200 bytes memcpy\n");
- for (i=0 ; i<4 ; i++) {
- now = systemTime();
- memcpy(dst, texels, 320*480*2);
- t = systemTime();
- printf("memcpy() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- }
- free(dst);
-
- free(texels);
-
- setpriority(PRIO_PROCESS, 0, -20);
-
- printf("512x512 unmodified texture, 512x512 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 512, 512, -512 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 512, 512);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
- printf("512x512 unmodified texture, 1x1 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 1, 1, -1 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 1, 1);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
- printf("512x512 unmodified texture, 512x512 blit (x2):\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 512, 512, -512 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 512, 512);
- glDrawTexiOES(0, 0, 0, 512, 512);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
- printf("512x512 unmodified texture, 1x1 blit (x2):\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 1, 1, -1 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 1, 1);
- glDrawTexiOES(0, 0, 0, 1, 1);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
-
- printf("512x512 (1x1 texel MODIFIED texture), 512x512 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- uint16_t green = 0x7E0;
- GLint crop[4] = { 0, 512, 512, -512 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &green);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 512, 512);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
-
- int16_t texel = 0xF800;
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
- 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &texel);
-
- printf("1x1 unmodified texture, 1x1 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 1, 1, -1 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 1, 1);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- eglSwapBuffers(dpy, surface);
- }
-
- printf("1x1 unmodified texture, 512x512 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- GLint crop[4] = { 0, 1, 1, -1 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 512, 512);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
- printf("1x1 (1x1 texel MODIFIED texture), 512x512 blit:\n");
- glClear(GL_COLOR_BUFFER_BIT);
- for (i=0 ; i<4 ; i++) {
- uint16_t green = 0x7E0;
- GLint crop[4] = { 0, 1, 1, -1 };
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &green);
- now = systemTime();
- glDrawTexiOES(0, 0, 0, 1, 1);
- glFinish();
- t = systemTime();
- printf("glFinish() time = %llu us\n", (t-now)/1000);
- fflush(stdout);
- eglSwapBuffers(dpy, surface);
- }
-
- return 0;
-}
diff --git a/opengl/tests/finish/finish.cpp b/opengl/tests/finish/finish.cpp
new file mode 100644
index 0000000..91f5c45
--- /dev/null
+++ b/opengl/tests/finish/finish.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ EGLint configAttribs[] = {
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ dpy, configAttribs, window, &config);
+ if (err) {
+ fprintf(stderr, "couldn't find an EGLConfig matching the screen format\n");
+ return 0;
+ }
+
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ GLint dim = w<h ? w : h;
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1,1,1,1);
+ glDisable(GL_DITHER);
+ glShadeModel(GL_FLAT);
+
+ long long now, t;
+ int i;
+
+ char* texels = (char*)malloc(512*512*2);
+ memset(texels,0xFF,512*512*2);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+ 512, 512, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texels);
+
+ char* dst = (char*)malloc(320*480*2);
+ memset(dst, 0, 320*480*2);
+ printf("307200 bytes memcpy\n");
+ for (i=0 ; i<4 ; i++) {
+ now = systemTime();
+ memcpy(dst, texels, 320*480*2);
+ t = systemTime();
+ printf("memcpy() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ }
+ free(dst);
+
+ free(texels);
+
+ setpriority(PRIO_PROCESS, 0, -20);
+
+ printf("512x512 unmodified texture, 512x512 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 512, 512, -512 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 512, 512);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ printf("512x512 unmodified texture, 1x1 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 1, 1, -1 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 1, 1);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ printf("512x512 unmodified texture, 512x512 blit (x2):\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 512, 512, -512 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 512, 512);
+ glDrawTexiOES(0, 0, 0, 512, 512);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ printf("512x512 unmodified texture, 1x1 blit (x2):\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 1, 1, -1 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 1, 1);
+ glDrawTexiOES(0, 0, 0, 1, 1);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+
+ printf("512x512 (1x1 texel MODIFIED texture), 512x512 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ uint16_t green = 0x7E0;
+ GLint crop[4] = { 0, 512, 512, -512 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &green);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 512, 512);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+
+ int16_t texel = 0xF800;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
+ 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &texel);
+
+ printf("1x1 unmodified texture, 1x1 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 1, 1, -1 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 1, 1);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ printf("1x1 unmodified texture, 512x512 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ GLint crop[4] = { 0, 1, 1, -1 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 512, 512);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ printf("1x1 (1x1 texel MODIFIED texture), 512x512 blit:\n");
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (i=0 ; i<4 ; i++) {
+ uint16_t green = 0x7E0;
+ GLint crop[4] = { 0, 1, 1, -1 };
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, &green);
+ now = systemTime();
+ glDrawTexiOES(0, 0, 0, 1, 1);
+ glFinish();
+ t = systemTime();
+ printf("glFinish() time = %llu us\n", (t-now)/1000);
+ fflush(stdout);
+ eglSwapBuffers(dpy, surface);
+ }
+
+ return 0;
+}
diff --git a/opengl/tests/gl2_basic/Android.mk b/opengl/tests/gl2_basic/Android.mk
new file mode 100644
index 0000000..a642eaf
--- /dev/null
+++ b/opengl/tests/gl2_basic/Android.mk
@@ -0,0 +1,19 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ gl2_basic.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv2 \
+ libui
+
+LOCAL_MODULE:= test-opengl-gl2_basic
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/gl2_basic/gl2_basic.cpp b/opengl/tests/gl2_basic/gl2_basic.cpp
new file mode 100644
index 0000000..705794a
--- /dev/null
+++ b/opengl/tests/gl2_basic/gl2_basic.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sched.h>
+#include <sys/resource.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <utils/Timers.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+static void printGLString(const char *name, GLenum s)
+{
+ const char *v = (const char *)glGetString(s);
+ if (v)
+ printf("GL %s = %s\n", name, v);
+ else
+ printf("GL %s = (null)\n", name);
+}
+
+int main(int argc, char** argv)
+{
+ EGLint s_configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RED_SIZE, 5,
+ EGL_GREEN_SIZE, 6,
+ EGL_BLUE_SIZE, 5,
+ EGL_NONE
+ };
+
+ EGLint numConfigs = -1;
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLConfig config;
+ EGLContext context;
+ EGLSurface surface;
+ EGLint w, h;
+
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = 0;
+ window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+ EGLUtils::selectConfigForNativeWindow(dpy, s_configAttribs, window, &config);
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+
+ EGLint gl2_0Attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
+
+ context = eglCreateContext(dpy, config, NULL, gl2_0Attribs);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ GLint dim = w<h ? w : h;
+
+ printGLString("Version", GL_VERSION);
+ printGLString("Vendor", GL_VENDOR);
+ printGLString("Renderer", GL_RENDERER);
+ printGLString("Extensions", GL_EXTENSIONS);
+
+ return 0;
+}
diff --git a/opengl/tests/swapinterval/Android.mk b/opengl/tests/swapinterval/Android.mk
new file mode 100644
index 0000000..619447c
--- /dev/null
+++ b/opengl/tests/swapinterval/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ swapinterval.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils \
+ libEGL \
+ libGLESv1_CM \
+ libui
+
+LOCAL_MODULE:= test-opengl-swapinterval
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/swapinterval/swapinterval.cpp b/opengl/tests/swapinterval/swapinterval.cpp
new file mode 100644
index 0000000..df53b62
--- /dev/null
+++ b/opengl/tests/swapinterval/swapinterval.cpp
@@ -0,0 +1,121 @@
+/*
+ **
+ ** Copyright 2006, 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <utils/StopWatch.h>
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ EGLint configAttribs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_NONE
+ };
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLConfig config;
+ EGLint numConfigs=0;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay dpy;
+
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, 0 ,0) ;//&majorVersion, &minorVersion);
+ eglGetConfigs(dpy, NULL, 0, &numConfigs);
+ printf("# configs = %d\n", numConfigs);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ dpy, configAttribs, window, &config);
+ if (err) {
+ fprintf(stderr, "couldn't find an EGLConfig matching the screen format\n");
+ return 0;
+ }
+
+ EGLint r,g,b,a;
+ eglGetConfigAttrib(dpy, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(dpy, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(dpy, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(dpy, config, EGL_ALPHA_SIZE, &a);
+
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ if (surface == EGL_NO_SURFACE) {
+ EGLint err = eglGetError();
+ fprintf(stderr, "%s, config=%p, format = %d-%d-%d-%d\n",
+ EGLUtils::strerror(err), config, r,g,b,a);
+ return 0;
+ } else {
+ printf("config=%p, format = %d-%d-%d-%d\n", config, r,g,b,a);
+ }
+
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+
+ printf("w=%d, h=%d\n", w, h);
+
+ glDisable(GL_DITHER);
+ glEnable(GL_BLEND);
+
+ glViewport(0, 0, w, h);
+ glOrthof(0, w, 0, h, 0, 1);
+
+ eglSwapInterval(dpy, 1);
+
+ glClearColor(1,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(dpy, surface);
+
+
+ int time = 10;
+ printf("screen should flash red/green quickly for %d s...\n", time);
+
+ int c = 0;
+ nsecs_t start = systemTime();
+ nsecs_t t;
+ do {
+ glClearColor(1,0,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(dpy, surface);
+ glClearColor(0,1,0,0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(dpy, surface);
+ t = systemTime() - start;
+ c += 2;
+ } while (int(ns2s(t))<=time);
+
+ double p = (double(t) / c) / 1000000000.0;
+ printf("refresh-rate is %f fps (%f ms)\n", 1.0f/p, p*1000.0);
+
+ eglTerminate(dpy);
+
+ return 0;
+}
diff --git a/opengl/tests/textures/Android.mk b/opengl/tests/textures/Android.mk
index 8d5f56d..b2fa185 100644
--- a/opengl/tests/textures/Android.mk
+++ b/opengl/tests/textures/Android.mk
@@ -2,7 +2,7 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- textures.c
+ textures.cpp
LOCAL_SHARED_LIBRARIES := \
libcutils \
@@ -14,4 +14,6 @@ LOCAL_MODULE:= test-opengl-textures
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -DGL_GLEXT_PROTOTYPES
+
include $(BUILD_EXECUTABLE)
diff --git a/opengl/tests/textures/textures.c b/opengl/tests/textures/textures.c
deleted file mode 100644
index 214291b..0000000
--- a/opengl/tests/textures/textures.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-**
-** Copyright 2006, 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.
-*/
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <EGL/egl.h>
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-int main(int argc, char** argv)
-{
- EGLint s_configAttribs[] = {
- EGL_RED_SIZE, 5,
- EGL_GREEN_SIZE, 6,
- EGL_BLUE_SIZE, 5,
- EGL_NONE
- };
-
- EGLint numConfigs = -1;
- EGLint majorVersion;
- EGLint minorVersion;
- EGLConfig config;
- EGLContext context;
- EGLSurface surface;
- EGLint w, h;
-
- EGLDisplay dpy;
-
- dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- eglInitialize(dpy, &majorVersion, &minorVersion);
- eglChooseConfig(dpy, s_configAttribs, &config, 1, &numConfigs);
- surface = eglCreateWindowSurface(dpy, config,
- android_createDisplaySurface(), NULL);
- context = eglCreateContext(dpy, config, NULL, NULL);
- eglMakeCurrent(dpy, surface, surface, context);
- eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
- eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
- GLint dim = w<h ? w : h;
-
-
- GLint crop[4] = { 0, 4, 4, -4 };
- glBindTexture(GL_TEXTURE_2D, 0);
- glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glEnable(GL_TEXTURE_2D);
- glColor4f(1,1,1,1);
-
- // packing is always 4
- uint8_t t8[] = {
- 0x00, 0x55, 0x00, 0x55,
- 0xAA, 0xFF, 0xAA, 0xFF,
- 0x00, 0x55, 0x00, 0x55,
- 0xAA, 0xFF, 0xAA, 0xFF };
-
- uint16_t t16[] = {
- 0x0000, 0x5555, 0x0000, 0x5555,
- 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF,
- 0x0000, 0x5555, 0x0000, 0x5555,
- 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF };
-
- uint16_t t5551[] = {
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000,
- 0x0000, 0xFFFF, 0x0000, 0xFFFF,
- 0xFFFF, 0x0000, 0xFFFF, 0x0000 };
-
- uint32_t t32[] = {
- 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000,
- 0xFF00FF00, 0xFFFF0000, 0xFF000000, 0xFF0000FF,
- 0xFF00FFFF, 0xFF00FF00, 0x00FF00FF, 0xFFFFFF00,
- 0xFF000000, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF
- };
-
-
- glClear(GL_COLOR_BUFFER_BIT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, t8);
- glDrawTexiOES(0, 0, 0, dim/2, dim/2);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t16);
- glDrawTexiOES(dim/2, 0, 0, dim/2, dim/2);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t16);
- glDrawTexiOES(0, dim/2, 0, dim/2, dim/2);
-
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, t32);
- glDrawTexiOES(dim/2, dim/2, 0, dim/2, dim/2);
-
- eglSwapBuffers(dpy, surface);
- return 0;
-}
diff --git a/opengl/tests/textures/textures.cpp b/opengl/tests/textures/textures.cpp
new file mode 100644
index 0000000..cbe8ffd
--- /dev/null
+++ b/opengl/tests/textures/textures.cpp
@@ -0,0 +1,118 @@
+/*
+**
+** Copyright 2006, 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.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <EGL/egl.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include <ui/FramebufferNativeWindow.h>
+#include <ui/EGLUtils.h>
+
+using namespace android;
+
+int main(int argc, char** argv)
+{
+ EGLint configAttribs[] = {
+ EGL_DEPTH_SIZE, 0,
+ EGL_NONE
+ };
+
+ EGLint majorVersion;
+ EGLint minorVersion;
+ EGLContext context;
+ EGLConfig config;
+ EGLSurface surface;
+ EGLint w, h;
+ EGLDisplay dpy;
+
+ EGLNativeWindowType window = android_createDisplaySurface();
+
+ dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ eglInitialize(dpy, &majorVersion, &minorVersion);
+
+ status_t err = EGLUtils::selectConfigForNativeWindow(
+ dpy, configAttribs, window, &config);
+ if (err) {
+ fprintf(stderr, "couldn't find an EGLConfig matching the screen format\n");
+ return 0;
+ }
+
+ surface = eglCreateWindowSurface(dpy, config, window, NULL);
+ context = eglCreateContext(dpy, config, NULL, NULL);
+ eglMakeCurrent(dpy, surface, surface, context);
+ eglQuerySurface(dpy, surface, EGL_WIDTH, &w);
+ eglQuerySurface(dpy, surface, EGL_HEIGHT, &h);
+ GLint dim = w<h ? w : h;
+
+
+ GLint crop[4] = { 0, 4, 4, -4 };
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glEnable(GL_TEXTURE_2D);
+ glColor4f(1,1,1,1);
+
+ // packing is always 4
+ uint8_t t8[] = {
+ 0x00, 0x55, 0x00, 0x55,
+ 0xAA, 0xFF, 0xAA, 0xFF,
+ 0x00, 0x55, 0x00, 0x55,
+ 0xAA, 0xFF, 0xAA, 0xFF };
+
+ uint16_t t16[] = {
+ 0x0000, 0x5555, 0x0000, 0x5555,
+ 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF,
+ 0x0000, 0x5555, 0x0000, 0x5555,
+ 0xAAAA, 0xFFFF, 0xAAAA, 0xFFFF };
+
+ uint16_t t5551[] = {
+ 0x0000, 0xFFFF, 0x0000, 0xFFFF,
+ 0xFFFF, 0x0000, 0xFFFF, 0x0000,
+ 0x0000, 0xFFFF, 0x0000, 0xFFFF,
+ 0xFFFF, 0x0000, 0xFFFF, 0x0000 };
+
+ uint32_t t32[] = {
+ 0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFFFF0000,
+ 0xFF00FF00, 0xFFFF0000, 0xFF000000, 0xFF0000FF,
+ 0xFF00FFFF, 0xFF00FF00, 0x00FF00FF, 0xFFFFFF00,
+ 0xFF000000, 0xFFFF00FF, 0xFF00FFFF, 0xFFFFFFFF
+ };
+
+
+ glClear(GL_COLOR_BUFFER_BIT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 4, 4, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, t8);
+ glDrawTexiOES(0, 0, 0, dim/2, dim/2);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 4, 4, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t16);
+ glDrawTexiOES(dim/2, 0, 0, dim/2, dim/2);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t16);
+ glDrawTexiOES(0, dim/2, 0, dim/2, dim/2);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, t32);
+ glDrawTexiOES(dim/2, dim/2, 0, dim/2, dim/2);
+
+ eglSwapBuffers(dpy, surface);
+ return 0;
+}
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index d84572b..1e1d729 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -2,10 +2,13 @@
package="com.android.providers.settings"
android:sharedUserId="android.uid.system">
+ <uses-permission android:name="android.permission.BACKUP_DATA" />
+
<application android:allowClearUserData="false"
android:label="@string/app_label"
android:process="system"
android:backupAgent="SettingsBackupAgent"
+ android:killAfterRestore="false"
android:icon="@drawable/ic_launcher_settings">
<provider android:name="SettingsProvider" android:authorities="settings"
diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/etc/bookmarks.xml
index 5af416a..734e0cd 100644
--- a/packages/SettingsProvider/etc/bookmarks.xml
+++ b/packages/SettingsProvider/etc/bookmarks.xml
@@ -39,10 +39,12 @@
package="com.android.calendar"
class="com.android.calendar.LaunchActivity"
shortcut="l" />
+<!--
<bookmark
package="com.google.android.apps.maps"
class="com.google.android.maps.MapsActivity"
shortcut="m" />
+-->
<bookmark
package="com.android.music"
class="com.android.music.MusicBrowserActivity"
diff --git a/packages/SettingsProvider/res/values-da/strings.xml b/packages/SettingsProvider/res/values-da/strings.xml
new file mode 100644
index 0000000..f0afc0b
--- /dev/null
+++ b/packages/SettingsProvider/res/values-da/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Indstillingslagring"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-el/strings.xml b/packages/SettingsProvider/res/values-el/strings.xml
new file mode 100644
index 0000000..1cac86d
--- /dev/null
+++ b/packages/SettingsProvider/res/values-el/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Αποθηκευτικός χώÏος Ïυθμίσεων"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-es-rUS/strings.xml b/packages/SettingsProvider/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..de3958b
--- /dev/null
+++ b/packages/SettingsProvider/res/values-es-rUS/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Almacenamiento de configuración"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ko/strings.xml b/packages/SettingsProvider/res/values-ko/strings.xml
new file mode 100644
index 0000000..aab51d6
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ko/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"설정 저장소"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-nb/strings.xml b/packages/SettingsProvider/res/values-nb/strings.xml
new file mode 100644
index 0000000..c96b1eb
--- /dev/null
+++ b/packages/SettingsProvider/res/values-nb/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Lagring av innstillinger"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pt-rPT/strings.xml b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..1e1dccb
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pt-rPT/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Armazenamento de Definições"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-pt/strings.xml b/packages/SettingsProvider/res/values-pt/strings.xml
new file mode 100644
index 0000000..c4af964
--- /dev/null
+++ b/packages/SettingsProvider/res/values-pt/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Armazenamento de configurações"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-ru/strings.xml b/packages/SettingsProvider/res/values-ru/strings.xml
new file mode 100644
index 0000000..bcf92fa
--- /dev/null
+++ b/packages/SettingsProvider/res/values-ru/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Хранилище наÑтроек"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-sv/strings.xml b/packages/SettingsProvider/res/values-sv/strings.xml
new file mode 100644
index 0000000..fa3f5cb
--- /dev/null
+++ b/packages/SettingsProvider/res/values-sv/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Lagring av inställningar"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-tr/strings.xml b/packages/SettingsProvider/res/values-tr/strings.xml
new file mode 100644
index 0000000..dc36cda
--- /dev/null
+++ b/packages/SettingsProvider/res/values-tr/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"Ayarlar Deposu"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values-zh-rCN/strings.xml b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..daf1254
--- /dev/null
+++ b/packages/SettingsProvider/res/values-zh-rCN/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4567566098528588863">"设置存储"</string>
+</resources>
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index f60ea57..6b20445 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -22,6 +22,7 @@
<bool name="def_airplane_mode_on">false</bool>
<!-- Comma-separated list of bluetooth, wifi, and cell. -->
<string name="def_airplane_mode_radios" translatable="false">cell,bluetooth,wifi</string>
+ <string name="airplane_mode_toggleable_radios" translatable="false">wifi</string>
<bool name="def_auto_time">true</bool>
<bool name="def_accelerometer_rotation">true</bool>
<!-- Default screen brightness, from 0 to 255. 102 is 40%. -->
@@ -36,6 +37,7 @@
user opt-in via Setup Wizard or Settings.
-->
<string name="def_location_providers_allowed" translatable="false">gps</string>
+ <bool name="assisted_gps_enabled">true</bool>
<!-- 0 == mobile, 1 == wifi. -->
<integer name="def_network_preference">1</integer>
<bool name="def_usb_mass_storage_enabled">true</bool>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 6dd1175..2524a30 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -25,6 +25,8 @@ import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDoneException;
+import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteStatement;
import android.media.AudioManager;
@@ -56,7 +58,7 @@ import java.util.List;
* Database helper class for {@link SettingsProvider}.
* Mostly just has a bit {@link #onCreate} to initialize the database.
*/
-class DatabaseHelper extends SQLiteOpenHelper {
+public class DatabaseHelper extends SQLiteOpenHelper {
/**
* Path to file containing default bookmarks, relative to ANDROID_ROOT.
*/
@@ -64,7 +66,12 @@ class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "SettingsProvider";
private static final String DATABASE_NAME = "settings.db";
- private static final int DATABASE_VERSION = 35;
+
+ // Please, please please. If you update the database version, check to make sure the
+ // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
+ // is properly propagated through your change. Not doing so will result in a loss of user
+ // settings.
+ private static final int DATABASE_VERSION = 39;
private Context mContext;
@@ -399,7 +406,65 @@ class DatabaseHelper extends SQLiteOpenHelper {
}
upgradeVersion = 35;
}
-
+ // due to a botched merge from donut to eclair, the initialization of ASSISTED_GPS_ENABLED
+ // was accidentally done out of order here.
+ // to fix this, ASSISTED_GPS_ENABLED is now initialized while upgrading from 38 to 39,
+ // and we intentionally do nothing from 35 to 36 now.
+ if (upgradeVersion == 35) {
+ upgradeVersion = 36;
+ }
+
+ if (upgradeVersion == 36) {
+ // This upgrade adds the STREAM_SYSTEM_ENFORCED type to the list of
+ // types affected by ringer modes (silent, vibrate, etc.)
+ db.beginTransaction();
+ try {
+ db.execSQL("DELETE FROM system WHERE name='"
+ + Settings.System.MODE_RINGER_STREAMS_AFFECTED + "'");
+ int newValue = (1 << AudioManager.STREAM_RING)
+ | (1 << AudioManager.STREAM_NOTIFICATION)
+ | (1 << AudioManager.STREAM_SYSTEM)
+ | (1 << AudioManager.STREAM_SYSTEM_ENFORCED);
+ db.execSQL("INSERT INTO system ('name', 'value') values ('"
+ + Settings.System.MODE_RINGER_STREAMS_AFFECTED + "', '"
+ + String.valueOf(newValue) + "')");
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ upgradeVersion = 37;
+ }
+
+ if (upgradeVersion == 37) {
+ db.beginTransaction();
+ try {
+ SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)"
+ + " VALUES(?,?);");
+ loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+ R.string.airplane_mode_toggleable_radios);
+ stmt.close();
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+ upgradeVersion = 38;
+ }
+
+ if (upgradeVersion == 38) {
+ db.beginTransaction();
+ try {
+ String value =
+ mContext.getResources().getBoolean(R.bool.assisted_gps_enabled) ? "1" : "0";
+ db.execSQL("INSERT OR IGNORE INTO secure(name,value) values('" +
+ Settings.Secure.ASSISTED_GPS_ENABLED + "','" + value + "');");
+ db.setTransactionSuccessful();
+ } finally {
+ db.endTransaction();
+ }
+
+ upgradeVersion = 39;
+ }
+
if (upgradeVersion != currentVersion) {
Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
+ ", must wipe the settings provider");
@@ -560,7 +625,7 @@ class DatabaseHelper extends SQLiteOpenHelper {
// By default, only the ring/notification and system streams are affected
loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
(1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |
- (1 << AudioManager.STREAM_SYSTEM));
+ (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED));
loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
((1 << AudioManager.STREAM_MUSIC) |
@@ -627,6 +692,9 @@ class DatabaseHelper extends SQLiteOpenHelper {
loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_RADIOS,
R.string.def_airplane_mode_radios);
+ loadStringSetting(stmt, Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS,
+ R.string.airplane_mode_toggleable_radios);
+
loadBooleanSetting(stmt, Settings.System.AUTO_TIME,
R.bool.def_auto_time); // Sync time to NITZ
@@ -667,6 +735,9 @@ class DatabaseHelper extends SQLiteOpenHelper {
loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
R.string.def_location_providers_allowed);
+ loadBooleanSetting(stmt, Settings.Secure.ASSISTED_GPS_ENABLED,
+ R.bool.assisted_gps_enabled);
+
loadIntegerSetting(stmt, Settings.Secure.NETWORK_PREFERENCE,
R.integer.def_network_preference);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2b36904..56a279a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -30,7 +30,7 @@ import java.util.zip.CRC32;
import android.backup.BackupDataInput;
import android.backup.BackupDataOutput;
import android.backup.BackupHelperAgent;
-import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothAdapter;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -83,6 +83,11 @@ public class SettingsBackupAgent extends BackupHelperAgent {
};
private static final String FILE_WIFI_SUPPLICANT = "/data/misc/wifi/wpa_supplicant.conf";
+
+ // the key to store the WIFI data under, should be sorted as last, so restore happens last.
+ // use very late unicode character to quasi-guarantee last sort position.
+ private static final String KEY_WIFI_SUPPLICANT = "\uffeeWIFI";
+
private static final String FILE_BT_ROOT = "/data/misc/hcid/";
private SettingsHelper mSettingsHelper;
@@ -113,7 +118,7 @@ public class SettingsBackupAgent extends BackupHelperAgent {
stateChecksums[STATE_LOCALE] =
writeIfChanged(stateChecksums[STATE_LOCALE], KEY_LOCALE, locale, data);
stateChecksums[STATE_WIFI] =
- writeIfChanged(stateChecksums[STATE_WIFI], FILE_WIFI_SUPPLICANT, wifiData, data);
+ writeIfChanged(stateChecksums[STATE_WIFI], KEY_WIFI_SUPPLICANT, wifiData, data);
writeNewChecksums(stateChecksums, newState);
}
@@ -122,7 +127,7 @@ public class SettingsBackupAgent extends BackupHelperAgent {
public void onRestore(BackupDataInput data, int appVersionCode,
ParcelFileDescriptor newState) throws IOException {
- enableWifi(false);
+
enableBluetooth(false);
while (data.readNextHeader()) {
@@ -133,12 +138,16 @@ public class SettingsBackupAgent extends BackupHelperAgent {
mSettingsHelper.applyAudioSettings();
} else if (KEY_SECURE.equals(key)) {
restoreSettings(data, Settings.Secure.CONTENT_URI);
- } else if (FILE_WIFI_SUPPLICANT.equals(key)) {
+ } else if (KEY_WIFI_SUPPLICANT.equals(key)) {
+ int retainedWifiState = enableWifi(false);
restoreFile(FILE_WIFI_SUPPLICANT, data);
FileUtils.setPermissions(FILE_WIFI_SUPPLICANT,
FileUtils.S_IRUSR | FileUtils.S_IWUSR |
FileUtils.S_IRGRP | FileUtils.S_IWGRP,
Process.myUid(), Process.WIFI_UID);
+ // retain the previous WIFI state.
+ enableWifi(retainedWifiState == WifiManager.WIFI_STATE_ENABLED ||
+ retainedWifiState == WifiManager.WIFI_STATE_ENABLING);
} else if (KEY_SYNC.equals(key)) {
mSettingsHelper.setSyncProviders(data);
} else if (KEY_LOCALE.equals(key)) {
@@ -257,7 +266,7 @@ public class SettingsBackupAgent extends BackupHelperAgent {
}
/**
- * Given a cursor sorted by key name and a set of keys sorted by name,
+ * Given a cursor sorted by key name and a set of keys sorted by name,
* extract the required keys and values and write them to a byte array.
* @param sortedCursor
* @param sortedKeys
@@ -373,15 +382,18 @@ public class SettingsBackupAgent extends BackupHelperAgent {
return result;
}
- private void enableWifi(boolean enable) {
+ private int enableWifi(boolean enable) {
WifiManager wfm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
if (wfm != null) {
+ int state = wfm.getWifiState();
wfm.setWifiEnabled(enable);
+ return state;
}
+ return WifiManager.WIFI_STATE_UNKNOWN;
}
private void enableBluetooth(boolean enable) {
- BluetoothDevice bt = (BluetoothDevice) getSystemService(Context.BLUETOOTH_SERVICE);
+ BluetoothAdapter bt = (BluetoothAdapter) getSystemService(Context.BLUETOOTH_SERVICE);
if (bt != null) {
if (!enable) {
bt.disable();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index ca739e6..b13883e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -118,16 +118,19 @@ public class SettingsHelper {
byte[] getSyncProviders() {
byte[] sync = new byte[1 + PROVIDERS.length];
+ // TODO: Sync backup needs to be moved to SystemBackupAgent
+ /*
try {
sync[0] = (byte) (mContentService.getListenForNetworkTickles() ? 1 : 0);
for (int i = 0; i < PROVIDERS.length; i++) {
sync[i + 1] = (byte)
- (mContentService.getSyncProviderAutomatically(PROVIDERS[i]) ? 1 : 0);
+ (mContentService.getSyncAutomatically(PROVIDERS[i]) ? 1 : 0);
}
} catch (RemoteException re) {
Log.w(TAG, "Unable to backup sync providers");
return sync;
}
+ */
return sync;
}
@@ -136,12 +139,15 @@ public class SettingsHelper {
try {
backup.readEntityData(sync, 0, sync.length);
+ // TODO: Sync backup needs to be moved to SystemBackupAgent
+ /*
mContentService.setListenForNetworkTickles(sync[0] == 1);
for (int i = 0; i < PROVIDERS.length; i++) {
mContentService.setSyncProviderAutomatically(PROVIDERS[i], sync[i + 1] > 0);
}
} catch (RemoteException re) {
Log.w(TAG, "Unable to restore sync providers");
+ */
} catch (java.io.IOException ioe) {
Log.w(TAG, "Unable to read sync settings");
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c0de9a5..9877342 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -24,9 +24,11 @@ import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
+import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
@@ -45,8 +47,7 @@ public class SettingsProvider extends ContentProvider {
private static final String TABLE_FAVORITES = "favorites";
private static final String TABLE_OLD_FAVORITES = "old_favorites";
- private DatabaseHelper mOpenHelper;
-
+ protected DatabaseHelper mOpenHelper;
private BackupManager mBackupManager;
/**
@@ -398,12 +399,8 @@ public class SettingsProvider extends ContentProvider {
// Get the current value for the default sound
Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
- if (soundUri == null) {
- // Fallback on any valid ringtone Uri
- soundUri = RingtoneManager.getValidRingtoneUri(context);
- }
- if (soundUri != null) {
+ if (soundUri != null) {
// Only proxy the openFile call to drm or media providers
String authority = soundUri.getAuthority();
boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
@@ -427,4 +424,64 @@ public class SettingsProvider extends ContentProvider {
return super.openFile(uri, mode);
}
+
+ @Override
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+
+ /*
+ * When a client attempts to openFile the default ringtone or
+ * notification setting Uri, we will proxy the call to the current
+ * default ringtone's Uri (if it is in the DRM or media provider).
+ */
+ int ringtoneType = RingtoneManager.getDefaultType(uri);
+ // Above call returns -1 if the Uri doesn't match a default type
+ if (ringtoneType != -1) {
+ Context context = getContext();
+
+ // Get the current value for the default sound
+ Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
+
+ if (soundUri != null) {
+ // Only proxy the openFile call to drm or media providers
+ String authority = soundUri.getAuthority();
+ boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
+ if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
+
+ if (isDrmAuthority) {
+ try {
+ // Check DRM access permission here, since once we
+ // do the below call the DRM will be checking our
+ // permission, not our caller's permission
+ DrmStore.enforceAccessDrmPermission(context);
+ } catch (SecurityException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
+ ParcelFileDescriptor pfd = null;
+ try {
+ pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
+ return new AssetFileDescriptor(pfd, 0, -1);
+ } catch (FileNotFoundException ex) {
+ // fall through and open the fallback ringtone below
+ }
+ }
+
+ try {
+ return super.openAssetFile(soundUri, mode);
+ } catch (FileNotFoundException ex) {
+ // Since a non-null Uri was specified, but couldn't be opened,
+ // fall back to the built-in ringtone.
+ return context.getResources().openRawResourceFd(
+ com.android.internal.R.raw.fallbackring);
+ }
+ }
+ // no need to fall through and have openFile() try again, since we
+ // already know that will fail.
+ throw new FileNotFoundException(); // or return null ?
+ }
+
+ // Note that this will end up calling openFile() above.
+ return super.openAssetFile(uri, mode);
+ }
}
diff --git a/packages/SubscribedFeedsProvider/AndroidManifest.xml b/packages/SubscribedFeedsProvider/AndroidManifest.xml
index ca00a9b..a3938bd 100644
--- a/packages/SubscribedFeedsProvider/AndroidManifest.xml
+++ b/packages/SubscribedFeedsProvider/AndroidManifest.xml
@@ -13,13 +13,14 @@
android:label="@string/app_label">
<uses-library android:name="com.google.android.gtalkservice" />
<provider android:name="SubscribedFeedsProvider"
- android:authorities="subscribedfeeds" android:syncable="false"
+ android:authorities="subscribedfeeds"
+ android:label="@string/provider_label"
android:multiprocess="false"
android:readPermission="android.permission.SUBSCRIBED_FEEDS_READ"
android:writePermission="android.permission.SUBSCRIBED_FEEDS_WRITE" />
<receiver android:name="SubscribedFeedsBroadcastReceiver">
<intent-filter>
- <action android:name="android.intent.action.GTALK_DATA_MESSAGE_RECEIVED" />
+ <action android:name="android.intent.action.REMOTE_INTENT" />
<category android:name="GSYNC_TICKLE"/>
</intent-filter>
<intent-filter>
diff --git a/packages/SubscribedFeedsProvider/res/values-cs/strings.xml b/packages/SubscribedFeedsProvider/res/values-cs/strings.xml
index 46de1b1..b570a3f 100644
--- a/packages/SubscribedFeedsProvider/res/values-cs/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-cs/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Synchronizace zdrojů"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-de/strings.xml b/packages/SubscribedFeedsProvider/res/values-de/strings.xml
index 7e39aa8..f1430e1 100644
--- a/packages/SubscribedFeedsProvider/res/values-de/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-de/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Feedsynchronisierung"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-es/strings.xml b/packages/SubscribedFeedsProvider/res/values-es/strings.xml
index 9165f65..d04206d 100644
--- a/packages/SubscribedFeedsProvider/res/values-es/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-es/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Sincronización de feeds"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-fr/strings.xml b/packages/SubscribedFeedsProvider/res/values-fr/strings.xml
index f022e70..1db303d 100644
--- a/packages/SubscribedFeedsProvider/res/values-fr/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-fr/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Synchronisation des flux"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-it/strings.xml b/packages/SubscribedFeedsProvider/res/values-it/strings.xml
index 1053314..fc893ae 100644
--- a/packages/SubscribedFeedsProvider/res/values-it/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-it/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Sincronizzazione feed"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-ja/strings.xml b/packages/SubscribedFeedsProvider/res/values-ja/strings.xml
index b8f2d09..4026b26 100644
--- a/packages/SubscribedFeedsProvider/res/values-ja/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-ja/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"フィードã®åŒæœŸ"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-nl/strings.xml b/packages/SubscribedFeedsProvider/res/values-nl/strings.xml
index 7dc5495..2537b64 100644
--- a/packages/SubscribedFeedsProvider/res/values-nl/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-nl/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Feeds synchroniseren"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-pl/strings.xml b/packages/SubscribedFeedsProvider/res/values-pl/strings.xml
index 32c4a5e..86d1c3e 100644
--- a/packages/SubscribedFeedsProvider/res/values-pl/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-pl/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"Synchronizowanie kanałów"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml b/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml
index 15307aa..87d93e4 100644
--- a/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values-zh-rTW/strings.xml
@@ -16,4 +16,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="5400580392303600842">"åŒæ­¥è³‡è¨Šæä¾›"</string>
+ <!-- no translation found for provider_label (3669714991966737047) -->
+ <skip />
</resources>
diff --git a/packages/SubscribedFeedsProvider/res/values/strings.xml b/packages/SubscribedFeedsProvider/res/values/strings.xml
index 072571d..c4c2484 100644
--- a/packages/SubscribedFeedsProvider/res/values/strings.xml
+++ b/packages/SubscribedFeedsProvider/res/values/strings.xml
@@ -17,5 +17,9 @@
<resources>
<!-- Title of the feed synchronization activity. -->
<string name="app_label">Sync Feeds</string>
+
+ <!-- What to show in messaging that refers to this provider, e.g. AccountSyncSettings -->
+ <string name="provider_label">Push Subscriptions</string>
+
</resources>
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsBroadcastReceiver.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsBroadcastReceiver.java
index 3513215..ea14307 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsBroadcastReceiver.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsBroadcastReceiver.java
@@ -16,6 +16,7 @@
package com.android.providers.subscribedfeeds;
+import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -35,7 +36,10 @@ public class SubscribedFeedsBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (Log.isLoggable(TAG, Log.VERBOSE)) Log.v(TAG, "Received intent " + intent);
- intent.setClass(context, SubscribedFeedsIntentService.class);
+ if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
+ setResultCode(Activity.RESULT_OK);
+ }
+ intent.setClass(context, SubscribedFeedsIntentService.class);
context.startService(intent);
}
}
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
index 8b3bedf..b854f86 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsIntentService.java
@@ -16,13 +16,14 @@ import android.database.sqlite.SQLiteFullException;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.os.Bundle;
-import android.os.RemoteException;
import android.text.TextUtils;
-import android.net.Uri;
+import android.accounts.Account;
import java.util.ArrayList;
import java.util.Calendar;
+import com.google.android.collect.Lists;
+
/**
* A service to handle various intents asynchronously.
*/
@@ -30,7 +31,8 @@ public class SubscribedFeedsIntentService extends IntentService {
private static final String TAG = "Sync";
private static final String[] sAccountProjection =
- new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT};
+ new String[] {SubscribedFeeds.Accounts._SYNC_ACCOUNT,
+ SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE};
/** How often to refresh the subscriptions, in milliseconds */
private static final long SUBSCRIPTION_REFRESH_INTERVAL = 1000L * 60 * 60 * 24; // one day
@@ -39,8 +41,7 @@ public class SubscribedFeedsIntentService extends IntentService {
private static final String sSubscribedFeedsPrefs = "subscribedFeeds";
- private static final String GTALK_DATA_MESSAGE_RECEIVED =
- "android.intent.action.GTALK_DATA_MESSAGE_RECEIVED";
+ private static final String REMOTE_INTENT_ACTION = Intent.ACTION_REMOTE_INTENT;
private static final String SUBSCRIBED_FEEDS_REFRESH_ACTION =
"com.android.subscribedfeeds.action.REFRESH";
@@ -52,13 +53,14 @@ public class SubscribedFeedsIntentService extends IntentService {
}
protected void onHandleIntent(Intent intent) {
- if (GTALK_DATA_MESSAGE_RECEIVED.equals(intent.getAction())) {
- boolean fromTrustedServer = intent.getBooleanExtra("from_trusted_server", false);
+ if (REMOTE_INTENT_ACTION.equals(intent.getAction())) {
+ boolean fromTrustedServer = intent.getBooleanExtra(
+ "android.intent.extra.from_trusted_server", false);
if (fromTrustedServer) {
- String account = intent.getStringExtra("account");
- String token = intent.getStringExtra("message_token");
+ String accountName = intent.getStringExtra("account");
+ String token = intent.getStringExtra(Intent.EXTRA_REMOTE_INTENT_TOKEN);
- if (TextUtils.isEmpty(account) || TextUtils.isEmpty(token)) {
+ if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(token)) {
if (Config.LOGD) {
Log.d(TAG, "Ignoring malformed tickle -- missing account or token.");
}
@@ -67,10 +69,10 @@ public class SubscribedFeedsIntentService extends IntentService {
if (Config.LOGD) {
Log.d(TAG, "Received network tickle for "
- + account + " - " + token);
+ + accountName + " - " + token);
}
- handleTickle(this, account, token);
+ handleTickle(this, accountName, token);
} else {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Ignoring tickle -- not from trusted server.");
@@ -102,16 +104,19 @@ public class SubscribedFeedsIntentService extends IntentService {
alarmManager.set(AlarmManager.RTC, when, pendingIntent);
}
- private void handleTickle(Context context, String account, String feed) {
+ private void handleTickle(Context context, String accountName, String feed) {
Cursor c = null;
final String where = SubscribedFeeds.Feeds._SYNC_ACCOUNT + "= ? "
+ + "and " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "= ? "
+ "and " + SubscribedFeeds.Feeds.FEED + "= ?";
try {
+ // TODO(fredq) fix the hardcoded type
+ final Account account = new Account(accountName, "com.google.GAIA");
c = context.getContentResolver().query(SubscribedFeeds.Feeds.CONTENT_URI,
- null, where, new String[]{account, feed}, null);
+ null, where, new String[]{account.name, account.type, feed}, null);
if (c.getCount() == 0) {
Log.w(TAG, "received tickle for non-existent feed: "
- + "account " + account + ", feed " + feed);
+ + "account " + accountName + ", feed " + feed);
EventLog.writeEvent(LOG_TICKLE, "unknown");
}
while (c.moveToNext()) {
@@ -119,21 +124,14 @@ public class SubscribedFeedsIntentService extends IntentService {
String authority = c.getString(c.getColumnIndexOrThrow(
SubscribedFeeds.Feeds.AUTHORITY));
EventLog.writeEvent(LOG_TICKLE, authority);
- try {
- if (!ContentResolver.getContentService()
- .getSyncProviderAutomatically(authority)) {
- Log.d(TAG, "supressing tickle since provider " + authority
- + " is configured to not sync automatically");
- continue;
- }
- } catch (RemoteException e) {
+ if (!ContentResolver.getSyncAutomatically(account, authority)) {
+ Log.d(TAG, "supressing tickle since provider " + authority
+ + " is configured to not sync automatically");
continue;
}
- Uri uri = Uri.parse("content://" + authority);
Bundle extras = new Bundle();
- extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
extras.putString("feed", feed);
- context.getContentResolver().startSync(uri, extras);
+ ContentResolver.requestSync(account, authority, extras);
}
} finally {
if (c != null) c.deactivate();
@@ -150,31 +148,35 @@ public class SubscribedFeedsIntentService extends IntentService {
*/
private void handleRefreshAlarm(Context context) {
// retrieve the list of accounts from the subscribed feeds
- ArrayList<String> accounts = new ArrayList<String>();
+ ArrayList<Account> accounts = Lists.newArrayList();
ContentResolver contentResolver = context.getContentResolver();
Cursor c = contentResolver.query(SubscribedFeeds.Accounts.CONTENT_URI,
sAccountProjection, null, null, null);
- while (c.moveToNext()) {
- String account = c.getString(0);
- if (TextUtils.isEmpty(account)) {
- continue;
+ try {
+ while (c.moveToNext()) {
+ String accountName = c.getString(0);
+ String accountType = c.getString(1);
+ accounts.add(new Account(accountName, accountType));
}
- accounts.add(account);
+ } finally {
+ c.close();
}
- c.deactivate();
// Clear the auth tokens for all these accounts so that we are sure
// they will still be valid until the next time we refresh them.
- // TODO: add this when the google login service is done
+ // TODO(fredq): add this when the google login service is done
// mark the feeds dirty, by setting the accounts to the same value,
// which will trigger a sync.
try {
ContentValues values = new ContentValues();
- for (String account : accounts) {
- values.put(SyncConstValue._SYNC_ACCOUNT, account);
+ for (Account account : accounts) {
+ values.put(SyncConstValue._SYNC_ACCOUNT, account.name);
+ values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
contentResolver.update(SubscribedFeeds.Feeds.CONTENT_URI, values,
- SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?", new String[] {account});
+ SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=? AND "
+ + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?",
+ new String[] {account.name, account.type});
}
} catch (SQLiteFullException e) {
Log.w(TAG, "disk full while trying to mark the feeds as dirty, skipping");
diff --git a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
index 9ecc3d6..2647752 100644
--- a/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
+++ b/packages/SubscribedFeedsProvider/src/com/android/providers/subscribedfeeds/SubscribedFeedsProvider.java
@@ -16,6 +16,7 @@
package com.android.providers.subscribedfeeds;
+import android.accounts.Account;
import android.content.UriMatcher;
import android.content.*;
import android.database.Cursor;
@@ -39,7 +40,7 @@ import java.util.HashMap;
public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
private static final String TAG = "SubscribedFeedsProvider";
private static final String DATABASE_NAME = "subscribedfeeds.db";
- private static final int DATABASE_VERSION = 10;
+ private static final int DATABASE_VERSION = 11;
private static final int FEEDS = 1;
private static final int FEED_ID = 2;
@@ -88,6 +89,7 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
db.execSQL("CREATE TABLE feeds (" +
"_id INTEGER PRIMARY KEY," +
"_sync_account TEXT," + // From the sync source
+ "_sync_account_type TEXT," + // From the sync source
"_sync_id TEXT," + // From the sync source
"_sync_time TEXT," + // From the sync source
"_sync_version TEXT," + // From the sync source
@@ -106,8 +108,8 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
"WHEN old._sync_id is not null " +
"BEGIN " +
"INSERT INTO _deleted_feeds " +
- "(_sync_id, _sync_account, _sync_version) " +
- "VALUES (old._sync_id, old._sync_account, " +
+ "(_sync_id, _sync_account, _sync_account_type, _sync_version) " +
+ "VALUES (old._sync_id, old._sync_account, old._sync_account_type, " +
"old._sync_version);" +
"END");
@@ -116,11 +118,22 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
"_sync_id TEXT," +
(isTemporary() ? "_sync_local_id INTEGER," : "") + // Used while syncing,
"_sync_account TEXT," +
+ "_sync_account_type TEXT," +
"_sync_mark INTEGER, " + // Used to filter out new rows
"UNIQUE(_sync_id))");
}
@Override
+ protected void onAccountsChanged(Account[] accountsArray) {
+ super.onAccountsChanged(accountsArray);
+ for (Account account : accountsArray) {
+ if (account.type.equals("com.google.GAIA")) {
+ ContentResolver.setSyncAutomatically(account, "subscribedfeeds", true);
+ }
+ }
+ }
+
+ @Override
protected void onDatabaseOpened(SQLiteDatabase db) {
db.markTableSyncable("feeds", "_deleted_feeds");
}
@@ -170,7 +183,8 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
qb.setDistinct(true);
qb.setProjectionMap(ACCOUNTS_PROJECTION_MAP);
return qb.query(getDatabase(), projection, selection, selectionArgs,
- SubscribedFeeds.Feeds._SYNC_ACCOUNT, null, sortOrder);
+ SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + ","
+ + SubscribedFeeds.Feeds._SYNC_ACCOUNT, null, sortOrder);
case FEED_ID:
qb.setTables(sFeedsTable);
qb.appendWhere(sFeedsTable + "._id=");
@@ -310,6 +324,8 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
DatabaseUtils.cursorStringToContentValues(diffsCursor,
SubscribedFeeds.Feeds._SYNC_ACCOUNT, mValues);
DatabaseUtils.cursorStringToContentValues(diffsCursor,
+ SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE, mValues);
+ DatabaseUtils.cursorStringToContentValues(diffsCursor,
SubscribedFeeds.Feeds._SYNC_VERSION, mValues);
db.replace(mDeletedTable, SubscribedFeeds.Feeds._SYNC_ID, mValues);
}
@@ -369,5 +385,7 @@ public class SubscribedFeedsProvider extends AbstractSyncableContentProvider {
ACCOUNTS_PROJECTION_MAP = map;
map.put(SubscribedFeeds.Accounts._COUNT, "COUNT(*) AS _count");
map.put(SubscribedFeeds.Accounts._SYNC_ACCOUNT, SubscribedFeeds.Accounts._SYNC_ACCOUNT);
+ map.put(SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE,
+ SubscribedFeeds.Accounts._SYNC_ACCOUNT_TYPE);
}
}
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
index 1bab717..0ec8dab 100644
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp
@@ -41,7 +41,7 @@
#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
#define FILTER_TRANSITION_FREQ 1100.0f // in Hz
#define FILTER_SHELF_SLOPE 1.0f // Q
-#define FILTER_GAIN 6.0f // linear gain
+#define FILTER_GAIN 5.5f // linear gain
// such a huge gain is justified by how much energy in the low frequencies is "wasted" at the output
// of the synthesis. The low shelving filter removes it, leaving room for amplification.
@@ -153,7 +153,7 @@ class SynthProxyJniStorage {
AudioTrack* mAudioOut;
AudioSystem::stream_type mStreamType;
uint32_t mSampleRate;
- AudioSystem::audio_format mAudFormat;
+ uint32_t mAudFormat;
int mNbChannels;
int8_t * mBuffer;
size_t mBufferSize;
@@ -200,7 +200,6 @@ class SynthProxyJniStorage {
mSampleRate = rate;
mAudFormat = format;
mNbChannels = channel;
-
mStreamType = streamType;
// retrieve system properties to ensure successful creation of the
@@ -221,7 +220,8 @@ class SynthProxyJniStorage {
if (minBufCount < 2) minBufCount = 2;
int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
- mAudioOut = new AudioTrack(mStreamType, rate, format, channel,
+ mAudioOut = new AudioTrack(mStreamType, rate, format,
+ (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
minFrameCount > 4096 ? minFrameCount : 4096,
0, 0, 0, 0); // not using an AudioTrack callback
@@ -264,7 +264,7 @@ void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type str
* Directly speaks using AudioTrack or write to file
*/
static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
- AudioSystem::audio_format format, int channel,
+ uint32_t format, int channel,
int8_t *&wav, size_t &bufferSize, tts_synth_status status) {
//LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
@@ -284,7 +284,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
}
if (bufferSize > 0) {
- prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel);
+ prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel);
if (pJniData->mAudioOut) {
applyFilter((int16_t*)wav, bufferSize/2);
pJniData->mAudioOut->write(wav, bufferSize);
@@ -721,6 +721,27 @@ android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
}
+static int
+android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
+{
+ int result = TTS_FAILURE;
+
+ if (jniData == 0) {
+ LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
+ return result;
+ }
+
+ // perform a regular stop
+ result = android_tts_SynthProxy_stop(env, thiz, jniData);
+ // but wait on the engine having released the engine mutex which protects
+ // the synthesizer resources.
+ engineMutex.lock();
+ engineMutex.unlock();
+
+ return result;
+}
+
+
static jobjectArray
android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
{
@@ -778,6 +799,10 @@ static JNINativeMethod gMethods[] = {
"(I)I",
(void*)android_tts_SynthProxy_stop
},
+ { "native_stopSync",
+ "(I)I",
+ (void*)android_tts_SynthProxy_stopSync
+ },
{ "native_speak",
"(ILjava/lang/String;I)I",
(void*)android_tts_SynthProxy_speak
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
index a0814aa..1d37ba0 100755
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ b/packages/TtsService/src/android/tts/SynthProxy.java
@@ -40,7 +40,7 @@ public class SynthProxy {
* Constructor; pass the location of the native TTS .so to use.
*/
public SynthProxy(String nativeSoLib) {
- Log.e("TTS is loading", nativeSoLib);
+ Log.v(TtsService.SERVICE_TAG, "TTS is loading " + nativeSoLib);
native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
}
@@ -52,6 +52,18 @@ public class SynthProxy {
}
/**
+ * Synchronous stop of the synthesizer. This method returns when the synth
+ * has completed the stop procedure and doesn't use any of the resources it
+ * was using while synthesizing.
+ *
+ * @return {@link android.speech.tts.TextToSpeech.SUCCESS} or
+ * {@link android.speech.tts.TextToSpeech.ERROR}
+ */
+ public int stopSync() {
+ return native_stopSync(mJniData);
+ }
+
+ /**
* Synthesize speech and speak it directly using AudioTrack.
*/
public int speak(String text, int streamType) {
@@ -156,6 +168,8 @@ public class SynthProxy {
private native final int native_stop(int jniData);
+ private native final int native_stopSync(int jniData);
+
private native final int native_speak(int jniData, String text, int streamType);
private native final int native_synthesizeToFile(int jniData, String text, String filename);
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index cd24727..217d3bb 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -34,6 +34,8 @@ import android.speech.tts.ITts.Stub;
import android.speech.tts.ITtsCallback;
import android.speech.tts.TextToSpeech;
import android.util.Log;
+
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -120,6 +122,7 @@ public class TtsService extends Service implements OnCompletionListener {
private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
private static final String CATEGORY = "android.intent.category.TTS";
private static final String PKGNAME = "android.tts";
+ protected static final String SERVICE_TAG = "TtsService";
private final RemoteCallbackList<ITtsCallback> mCallbacks
= new RemoteCallbackList<ITtsCallback>();
@@ -138,6 +141,7 @@ public class TtsService extends Service implements OnCompletionListener {
private ContentResolver mResolver;
+ // lock for the speech queue (mSpeechQueue) and the current speech item (mCurrentSpeechItem)
private final ReentrantLock speechQueueLock = new ReentrantLock();
private final ReentrantLock synthesizerLock = new ReentrantLock();
@@ -145,7 +149,7 @@ public class TtsService extends Service implements OnCompletionListener {
@Override
public void onCreate() {
super.onCreate();
- Log.i("TtsService", "TtsService.onCreate()");
+ Log.v("TtsService", "TtsService.onCreate()");
mResolver = getContentResolver();
@@ -173,10 +177,7 @@ public class TtsService extends Service implements OnCompletionListener {
public void onDestroy() {
super.onDestroy();
- // TODO replace the call to stopAll() with a method to clear absolutely all upcoming
- // uses of the native synth, including synthesis to a file, and delete files for which
- // synthesis was not complete.
- stopAll("");
+ killAllUtterances();
// Don't hog the media player
cleanUpPlayer();
@@ -188,6 +189,8 @@ public class TtsService extends Service implements OnCompletionListener {
// Unregister all callbacks.
mCallbacks.kill();
+
+ Log.v(SERVICE_TAG, "onDestroy() completed");
}
@@ -279,7 +282,6 @@ public class TtsService extends Service implements OnCompletionListener {
private int isLanguageAvailable(String lang, String country, String variant) {
- //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
int res = TextToSpeech.LANG_NOT_SUPPORTED;
try {
res = sNativeSynth.isLanguageAvailable(lang, country, variant);
@@ -301,7 +303,7 @@ public class TtsService extends Service implements OnCompletionListener {
private int setLanguage(String callingApp, String lang, String country, String variant) {
- Log.v("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
+ Log.v(SERVICE_TAG, "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
int res = TextToSpeech.ERROR;
try {
if (isDefaultEnforced()) {
@@ -385,7 +387,7 @@ public class TtsService extends Service implements OnCompletionListener {
* engines.
*/
private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
- Log.v("TtsService", "TTS service received " + text);
+ Log.v(SERVICE_TAG, "TTS service received " + text);
if (queueMode == TextToSpeech.QUEUE_FLUSH) {
stop(callingApp);
} else if (queueMode == 2) {
@@ -434,7 +436,7 @@ public class TtsService extends Service implements OnCompletionListener {
speechQueueAvailable =
speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
if (speechQueueAvailable) {
- Log.i("TtsService", "Stopping");
+ Log.i(SERVICE_TAG, "Stopping");
for (int i = mSpeechQueue.size() - 1; i > -1; i--){
if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
mSpeechQueue.remove(i);
@@ -461,10 +463,13 @@ public class TtsService extends Service implements OnCompletionListener {
} else {
result = TextToSpeech.SUCCESS;
}
- Log.i("TtsService", "Stopped");
+ Log.i(SERVICE_TAG, "Stopped");
+ } else {
+ Log.e(SERVICE_TAG, "TTS stop(): queue locked longer than expected");
+ result = TextToSpeech.ERROR;
}
} catch (InterruptedException e) {
- Log.e("TtsService", "TTS stop: tryLock interrupted");
+ Log.e(SERVICE_TAG, "TTS stop: tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run; even if the
@@ -477,6 +482,63 @@ public class TtsService extends Service implements OnCompletionListener {
}
+ /**
+ * Stops all speech output, both rendered to a file and directly spoken, and removes any
+ * utterances still in the queue globally. Files that were being written are deleted.
+ */
+ @SuppressWarnings("finally")
+ private int killAllUtterances() {
+ int result = TextToSpeech.ERROR;
+ boolean speechQueueAvailable = false;
+
+ try {
+ speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT,
+ TimeUnit.MILLISECONDS);
+ if (speechQueueAvailable) {
+ // remove every single entry in the speech queue
+ mSpeechQueue.clear();
+
+ // clear the current speech item
+ if (mCurrentSpeechItem != null) {
+ result = sNativeSynth.stopSync();
+ mKillList.put(mCurrentSpeechItem, true);
+ mIsSpeaking = false;
+
+ // was the engine writing to a file?
+ if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
+ // delete the file that was being written
+ if (mCurrentSpeechItem.mFilename != null) {
+ File tempFile = new File(mCurrentSpeechItem.mFilename);
+ Log.v(SERVICE_TAG, "Leaving behind " + mCurrentSpeechItem.mFilename);
+ if (tempFile.exists()) {
+ Log.v(SERVICE_TAG, "About to delete "
+ + mCurrentSpeechItem.mFilename);
+ if (tempFile.delete()) {
+ Log.v(SERVICE_TAG, "file successfully deleted");
+ }
+ }
+ }
+ }
+
+ mCurrentSpeechItem = null;
+ }
+ } else {
+ Log.e(SERVICE_TAG, "TTS killAllUtterances(): queue locked longer than expected");
+ result = TextToSpeech.ERROR;
+ }
+ } catch (InterruptedException e) {
+ Log.e(SERVICE_TAG, "TTS killAllUtterances(): tryLock interrupted");
+ result = TextToSpeech.ERROR;
+ } finally {
+ // This check is needed because finally will always run, even if the
+ // method returns somewhere in the try block.
+ if (speechQueueAvailable) {
+ speechQueueLock.unlock();
+ }
+ return result;
+ }
+ }
+
/**
* Stops all speech output and removes any utterances still in the queue globally, except
@@ -516,10 +578,13 @@ public class TtsService extends Service implements OnCompletionListener {
} else {
result = TextToSpeech.SUCCESS;
}
- Log.i("TtsService", "Stopped all");
+ Log.i(SERVICE_TAG, "Stopped all");
+ } else {
+ Log.e(SERVICE_TAG, "TTS stopAll(): queue locked longer than expected");
+ result = TextToSpeech.ERROR;
}
} catch (InterruptedException e) {
- Log.e("TtsService", "TTS stopAll: tryLock interrupted");
+ Log.e(SERVICE_TAG, "TTS stopAll: tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run; even if the
@@ -646,11 +711,11 @@ public class TtsService extends Service implements OnCompletionListener {
sNativeSynth.speak(speechItem.mText, streamType);
} catch (NullPointerException e) {
// synth will become null during onDestroy()
- Log.v("TtsService", " null synth, can't speak");
+ Log.v(SERVICE_TAG, " null synth, can't speak");
}
}
} catch (InterruptedException e) {
- Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
+ Log.e(SERVICE_TAG, "TTS speakInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
@@ -667,7 +732,7 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
Thread synth = (new Thread(new SynthThread()));
- //synth.setPriority(Thread.MIN_PRIORITY);
+ synth.setPriority(Thread.MAX_PRIORITY);
synth.start();
}
@@ -676,7 +741,7 @@ public class TtsService extends Service implements OnCompletionListener {
public void run() {
boolean synthAvailable = false;
String utteranceId = "";
- Log.i("TtsService", "Synthesizing to " + speechItem.mFilename);
+ Log.i(SERVICE_TAG, "Synthesizing to " + speechItem.mFilename);
try {
synthAvailable = synthesizerLock.tryLock();
if (!synthAvailable) {
@@ -720,11 +785,11 @@ public class TtsService extends Service implements OnCompletionListener {
sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
} catch (NullPointerException e) {
// synth will become null during onDestroy()
- Log.v("TtsService", " null synth, can't synthesize to file");
+ Log.v(SERVICE_TAG, " null synth, can't synthesize to file");
}
}
} catch (InterruptedException e) {
- Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");
+ Log.e(SERVICE_TAG, "TTS synthToFileInternalOnly(): tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run;
@@ -741,7 +806,7 @@ public class TtsService extends Service implements OnCompletionListener {
}
}
Thread synth = (new Thread(new SynthThread()));
- //synth.setPriority(Thread.MIN_PRIORITY);
+ synth.setPriority(Thread.MAX_PRIORITY);
synth.start();
}
@@ -769,7 +834,7 @@ public class TtsService extends Service implements OnCompletionListener {
if (cb == null){
return;
}
- Log.i("TtsService", "TTS callback: dispatch started");
+ Log.v(SERVICE_TAG, "TTS callback: dispatch started");
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
try {
@@ -779,7 +844,7 @@ public class TtsService extends Service implements OnCompletionListener {
// the dead object for us.
}
mCallbacks.finishBroadcast();
- Log.i("TtsService", "TTS callback: dispatch completed to " + N);
+ Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
}
private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
@@ -816,11 +881,12 @@ public class TtsService extends Service implements OnCompletionListener {
speechQueueAvailable =
speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
if (!speechQueueAvailable) {
- Log.e("TtsService", "processSpeechQueue - Speech queue is unavailable.");
+ Log.e(SERVICE_TAG, "processSpeechQueue - Speech queue is unavailable.");
return;
}
if (mSpeechQueue.size() < 1) {
mIsSpeaking = false;
+ mKillList.clear();
broadcastTtsQueueProcessingCompleted();
return;
}
@@ -830,7 +896,7 @@ public class TtsService extends Service implements OnCompletionListener {
SoundResource sr = getSoundResource(mCurrentSpeechItem);
// Synth speech as needed - synthesizer should call
// processSpeechQueue to continue running the queue
- Log.i("TtsService", "TTS processing: " + mCurrentSpeechItem.mText);
+ Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
if (sr == null) {
if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
@@ -886,7 +952,7 @@ public class TtsService extends Service implements OnCompletionListener {
mSpeechQueue.remove(0);
}
} catch (InterruptedException e) {
- Log.e("TtsService", "TTS processSpeechQueue: tryLock interrupted");
+ Log.e(SERVICE_TAG, "TTS processSpeechQueue: tryLock interrupted");
e.printStackTrace();
} finally {
// This check is needed because finally will always run; even if the
diff --git a/packages/VpnServices/res/values-cs/strings.xml b/packages/VpnServices/res/values-cs/strings.xml
index 58e211d..f5469a1 100644
--- a/packages/VpnServices/res/values-cs/strings.xml
+++ b/packages/VpnServices/res/values-cs/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Služby VPN"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"Síť VPN %s připojena"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"Síť VPN %s odpojena"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Dotykem se znovu připojíte k síti VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-da/strings.xml b/packages/VpnServices/res/values-da/strings.xml
new file mode 100644
index 0000000..8f65e25
--- /dev/null
+++ b/packages/VpnServices/res/values-da/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN-tjenester"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Tryk for at oprette forbindelse til et VPN igen."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-de/strings.xml b/packages/VpnServices/res/values-de/strings.xml
index 23558bb..b21ad72 100644
--- a/packages/VpnServices/res/values-de/strings.xml
+++ b/packages/VpnServices/res/values-de/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-Dienste"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"VPN \"%s\" verbunden"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"VPN \"%s \" getrennt"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Zur Wiederherstellung der Verbindung mit einem VPN berühren"</string>
</resources>
diff --git a/packages/VpnServices/res/values-el/strings.xml b/packages/VpnServices/res/values-el/strings.xml
new file mode 100644
index 0000000..ea03865
--- /dev/null
+++ b/packages/VpnServices/res/values-el/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"ΥπηÏεσίες VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Πατήστε για να επανασυνδεθείτε σε ένα VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-es-rUS/strings.xml b/packages/VpnServices/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..9a904cb
--- /dev/null
+++ b/packages/VpnServices/res/values-es-rUS/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"Servicios VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Tocar para volver a conectarse a una VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-es/strings.xml b/packages/VpnServices/res/values-es/strings.xml
index bc7e536..9d8e23b 100644
--- a/packages/VpnServices/res/values-es/strings.xml
+++ b/packages/VpnServices/res/values-es/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Servicios VPN"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"VPN %s conectada"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"VPN %s desconectada"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Toca para volver a conectarte a una red VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-fr/strings.xml b/packages/VpnServices/res/values-fr/strings.xml
index 5db5050..eda660e 100644
--- a/packages/VpnServices/res/values-fr/strings.xml
+++ b/packages/VpnServices/res/values-fr/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Services VPN"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"VPN %s connecté"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"VPN %s déconnecté"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Touchez l\'écran pour vous reconnecter à un VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-it/strings.xml b/packages/VpnServices/res/values-it/strings.xml
index cc2a4eb..95eaddb 100644
--- a/packages/VpnServices/res/values-it/strings.xml
+++ b/packages/VpnServices/res/values-it/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Servizi VPN"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"VPN %s collegata"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"VPN %s scollegata"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Tocca per riconnetterti a una rete VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-ja/strings.xml b/packages/VpnServices/res/values-ja/strings.xml
index 1158347..8174d44 100644
--- a/packages/VpnServices/res/values-ja/strings.xml
+++ b/packages/VpnServices/res/values-ja/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPNサービス"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"%s VPNãŒæŽ¥ç¶šã•ã‚Œã¾ã—ãŸ"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"%s VPNãŒåˆ‡æ–­ã•ã‚Œã¾ã—ãŸ"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"タップã—ã¦VPNã«å†æŽ¥ç¶šã—ã¦ãã ã•ã„。"</string>
</resources>
diff --git a/packages/VpnServices/res/values-ko/strings.xml b/packages/VpnServices/res/values-ko/strings.xml
new file mode 100644
index 0000000..f89516d
--- /dev/null
+++ b/packages/VpnServices/res/values-ko/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN 서비스"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"VPNì— ë‹¤ì‹œ 연결하려면 터치하세요."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-nb/strings.xml b/packages/VpnServices/res/values-nb/strings.xml
new file mode 100644
index 0000000..2b43b87
--- /dev/null
+++ b/packages/VpnServices/res/values-nb/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN-tjenester"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Trykk for å koble til et VPN på nytt"</string>
+</resources>
diff --git a/packages/VpnServices/res/values-nl/strings.xml b/packages/VpnServices/res/values-nl/strings.xml
index 359cfe9..3e4cb29 100644
--- a/packages/VpnServices/res/values-nl/strings.xml
+++ b/packages/VpnServices/res/values-nl/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN-services"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"%s VPN verbonden"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"%s VPN-verbinding verbroken"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Raak aan om opnieuw verbinding te maken met een VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-pl/strings.xml b/packages/VpnServices/res/values-pl/strings.xml
index 17d1dab..8568bbd 100644
--- a/packages/VpnServices/res/values-pl/strings.xml
+++ b/packages/VpnServices/res/values-pl/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"Usługi VPN"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"Połączono z siecią VPN %s"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"Rozłączono z siecią VPN %s"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Dotknij, aby ponownie połączyć się z siecią VPN."</string>
</resources>
diff --git a/packages/VpnServices/res/values-pt-rPT/strings.xml b/packages/VpnServices/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..f2bdd4e
--- /dev/null
+++ b/packages/VpnServices/res/values-pt-rPT/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"Serviços VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Toque para voltar a ligar a uma VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-pt/strings.xml b/packages/VpnServices/res/values-pt/strings.xml
new file mode 100644
index 0000000..5b24830
--- /dev/null
+++ b/packages/VpnServices/res/values-pt/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"Serviços de VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Toque para reconectar-se a uma VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-ru/strings.xml b/packages/VpnServices/res/values-ru/strings.xml
new file mode 100644
index 0000000..94a807f
--- /dev/null
+++ b/packages/VpnServices/res/values-ru/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"Службы VPN"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Ðажмите, чтобы повторно подключитьÑÑ Ðº VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-sv/strings.xml b/packages/VpnServices/res/values-sv/strings.xml
new file mode 100644
index 0000000..c086d24
--- /dev/null
+++ b/packages/VpnServices/res/values-sv/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN-tjänster"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Tryck här om du vill återansluta till ett VPN."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-tr/strings.xml b/packages/VpnServices/res/values-tr/strings.xml
new file mode 100644
index 0000000..b4369ba
--- /dev/null
+++ b/packages/VpnServices/res/values-tr/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN Hizmetleri"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"Bir VPN\'ye tekrar bağlanmak için dokunun."</string>
+</resources>
diff --git a/packages/VpnServices/res/values-zh-rCN/strings.xml b/packages/VpnServices/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..450ecb5
--- /dev/null
+++ b/packages/VpnServices/res/values-zh-rCN/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_label" msgid="4589592829302498102">"VPN æœåŠ¡"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
+ <string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"触摸å¯é‡æ–°è¿žæŽ¥ VPN。"</string>
+</resources>
diff --git a/packages/VpnServices/res/values-zh-rTW/strings.xml b/packages/VpnServices/res/values-zh-rTW/strings.xml
index ad8eddf..14b72de 100644
--- a/packages/VpnServices/res/values-zh-rTW/strings.xml
+++ b/packages/VpnServices/res/values-zh-rTW/strings.xml
@@ -16,7 +16,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4589592829302498102">"VPN æœå‹™"</string>
- <string name="vpn_notification_title_connected" msgid="2567196609405266775">"%s VPN 已連線"</string>
- <string name="vpn_notification_title_disconnected" msgid="3564361788336568244">"%s VPN 已中斷連線"</string>
+ <!-- no translation found for vpn_notification_title_connected (8598654486956133580) -->
+ <skip />
+ <!-- no translation found for vpn_notification_title_disconnected (6216572264382192027) -->
+ <skip />
<string name="vpn_notification_hint_disconnected" msgid="1952209867082269429">"輕觸å³å¯é‡æ–°é€£ç·šè‡³ VPN。"</string>
</resources>
diff --git a/packages/VpnServices/res/values/strings.xml b/packages/VpnServices/res/values/strings.xml
index 074655e..d82f52a 100755
--- a/packages/VpnServices/res/values/strings.xml
+++ b/packages/VpnServices/res/values/strings.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Title for the VPN Services activity. -->
<string name="app_label">VPN Services</string>
- <string name="vpn_notification_title_connected">%s VPN connected</string>
- <string name="vpn_notification_title_disconnected">%s VPN disconnected</string>
+ <string name="vpn_notification_title_connected"><xliff:g id="profilename">%s</xliff:g> VPN connected</string>
+ <string name="vpn_notification_title_disconnected"><xliff:g id="profilename">%s</xliff:g> VPN disconnected</string>
<string name="vpn_notification_hint_disconnected">Touch to reconnect to a VPN.</string>
</resources>
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
index e3ac996..53167f6 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java
@@ -56,10 +56,6 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
private static final String REMOTE_IP = "net.ipremote";
private static final String DNS_DOMAIN_SUFFICES = "net.dns.search";
- private static final int CHALLENGE_ERROR_CODE = 5;
- private static final int REMOTE_HUNG_UP_ERROR_CODE = 7;
- private static final int AUTH_ERROR_CODE = 51;
-
private final String TAG = VpnService.class.getSimpleName();
// FIXME: profile is only needed in connecting phase, so we can just save
@@ -202,7 +198,7 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
private void waitUntilConnectedOrTimedout() throws IOException {
sleep(2000); // 2 seconds
- for (int i = 0; i < 60; i++) {
+ for (int i = 0; i < 80; i++) {
if (mState != VpnState.CONNECTING) {
break;
} else if (VPN_IS_UP.equals(
@@ -464,22 +460,8 @@ abstract class VpnService<E extends VpnProfile> implements Serializable {
synchronized int getSocketError() {
for (DaemonProxy s : mDaemonList) {
- switch (getResultFromSocket(s)) {
- case 0:
- continue;
-
- case AUTH_ERROR_CODE:
- return VpnManager.VPN_ERROR_AUTH;
-
- case CHALLENGE_ERROR_CODE:
- return VpnManager.VPN_ERROR_CHALLENGE;
-
- case REMOTE_HUNG_UP_ERROR_CODE:
- return VpnManager.VPN_ERROR_REMOTE_HUNG_UP;
-
- default:
- return VpnManager.VPN_ERROR_CONNECTION_FAILED;
- }
+ int errCode = getResultFromSocket(s);
+ if (errCode != 0) return errCode;
}
return 0;
}
diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
index 4892a7b..e5be847 100644
--- a/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
+++ b/packages/VpnServices/src/com/android/server/vpn/VpnServiceBinder.java
@@ -91,7 +91,8 @@ public class VpnServiceBinder extends Service {
void removeStates() {
try {
- new File(STATES_FILE_PATH).delete();
+ File f = new File(STATES_FILE_PATH);
+ if (f.exists()) f.delete();
} catch (Throwable e) {
if (DBG) Log.d("VpnServiceBinder", " remove states: " + e);
}
diff --git a/preloaded-classes b/preloaded-classes
index 69c596c..3da4797 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -1,248 +1,192 @@
# Classes which are preloaded by com.android.internal.os.ZygoteInit.
-# Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java.
-# MIN_LOAD_TIME_MICROS=1250
-SQLite.Blob
-SQLite.Database
-SQLite.FunctionContext
-SQLite.Stmt
-SQLite.Vm
android.R$styleable
-android.accounts.IAccountsService$Stub
android.app.Activity
android.app.ActivityGroup
-android.app.ActivityManager
-android.app.ActivityManager$MemoryInfo
+android.app.ActivityManager$MemoryInfo$1
android.app.ActivityManagerNative
android.app.ActivityManagerProxy
android.app.ActivityThread
+android.app.ActivityThread$ActivityRecord
+android.app.ActivityThread$AppBindData
android.app.ActivityThread$ApplicationThread
+android.app.ActivityThread$ContextCleanupInfo
+android.app.ActivityThread$GcIdler
android.app.ActivityThread$H
+android.app.ActivityThread$Idler
+android.app.ActivityThread$PackageInfo
+android.app.ActivityThread$PackageInfo$ReceiverDispatcher
+android.app.ActivityThread$PackageInfo$ReceiverDispatcher$InnerReceiver
+android.app.ActivityThread$PackageInfo$ServiceDispatcher
+android.app.ActivityThread$PackageInfo$ServiceDispatcher$InnerConnection
+android.app.ActivityThread$ProviderRecord
+android.app.ActivityThread$ProviderRefCount
android.app.AlertDialog
android.app.Application
android.app.ApplicationContext
android.app.ApplicationContext$ApplicationContentResolver
android.app.ApplicationContext$ApplicationPackageManager
-android.app.ApplicationContext$WallpaperCallback
+android.app.ApplicationContext$ApplicationPackageManager$PackageRemovedReceiver
+android.app.ApplicationContext$ApplicationPackageManager$ResourceName
+android.app.ApplicationContext$SharedPreferencesImpl
+android.app.ApplicationLoaders
android.app.ApplicationThreadNative
-android.app.DatePickerDialog
android.app.Dialog
android.app.ExpandableListActivity
android.app.IActivityManager
-android.app.IActivityManager$ContentProviderHolder
+android.app.IActivityManager$ContentProviderHolder$1
android.app.IAlarmManager$Stub
+android.app.IAlarmManager$Stub$Proxy
+android.app.IApplicationThread
android.app.INotificationManager$Stub
+android.app.INotificationManager$Stub$Proxy
+android.app.ISearchManager
android.app.ISearchManager$Stub
-android.app.ISearchManagerCallback$Stub
-android.app.IStatusBar$Stub
-android.app.ITransientNotification$Stub
-android.app.IWallpaperService$Stub
-android.app.IWallpaperServiceCallback$Stub
+android.app.ISearchManager$Stub$Proxy
android.app.Instrumentation
-android.app.IntentService
+android.app.IntentReceiverLeaked
android.app.ListActivity
+android.app.ListActivity$1
+android.app.ListActivity$2
android.app.LocalActivityManager
android.app.Notification
+android.app.NotificationManager
android.app.PendingIntent
+android.app.PendingIntent$1
android.app.ProgressDialog
+android.app.ReceiverRestrictedContext
android.app.ResultInfo
-android.app.SearchManager$SearchManagerCallback
+android.app.ResultInfo$1
+android.app.SearchDialog
+android.app.SearchDialog$SearchAutoComplete
android.app.Service
-android.app.StatusBarManager
+android.app.ServiceConnectionLeaked
android.app.TabActivity
-android.app.TimePickerDialog
-android.appwidget.AppWidgetHost
-android.appwidget.AppWidgetHostView
-android.appwidget.AppWidgetManager
-android.appwidget.AppWidgetProvider
-android.appwidget.AppWidgetProviderInfo
-android.backup.BackupDataInput
-android.backup.BackupDataInput$EntityHeader
-android.backup.BackupDataOutput
-android.backup.BackupHelperDispatcher
-android.backup.BackupHelperDispatcher$Header
-android.backup.FileBackupHelperBase
-android.bluetooth.BluetoothAudioGateway
-android.bluetooth.BluetoothDevice
-android.bluetooth.Database
-android.bluetooth.HeadsetBase
-android.bluetooth.IBluetoothA2dp
-android.bluetooth.IBluetoothA2dp$Stub
-android.bluetooth.IBluetoothDevice
-android.bluetooth.IBluetoothDevice$Stub
-android.bluetooth.IBluetoothDevice$Stub$Proxy
-android.bluetooth.RfcommSocket
-android.bluetooth.ScoSocket
android.content.AbstractSyncableContentProvider
android.content.AbstractTableMerger
+android.content.AsyncQueryHandler$WorkerHandler
+android.content.BroadcastReceiver
+android.content.ComponentCallbacks
android.content.ComponentName
+android.content.ComponentName$1
android.content.ContentProvider$Transport
+android.content.ContentProviderProxy
+android.content.ContentQueryMap
+android.content.ContentQueryMap$1
android.content.ContentResolver
android.content.ContentResolver$CursorWrapperInner
android.content.ContentValues
android.content.Context
android.content.ContextWrapper
-android.content.DialogInterface$OnMultiChoiceClickListener
+android.content.DialogInterface
+android.content.DialogInterface$OnCancelListener
+android.content.DialogInterface$OnDismissListener
+android.content.IContentProvider
+android.content.IContentService
android.content.IContentService$Stub
-android.content.ISyncAdapter$Stub
android.content.Intent
-android.content.Intent$ShortcutIconResource
+android.content.Intent$1
android.content.IntentFilter
-android.content.SyncAdapter$Transport
+android.content.SearchRecentSuggestionsProvider
+android.content.ServiceConnection
+android.content.SharedPreferences
android.content.SyncResult
-android.content.SyncStateContentProviderHelper
+android.content.SyncResult$1
android.content.SyncStats
+android.content.SyncStats$1
android.content.SyncableContentProvider
-android.content.TempProviderSyncAdapter
android.content.UriMatcher
android.content.pm.ActivityInfo
+android.content.pm.ActivityInfo$1
android.content.pm.ApplicationInfo
-android.content.pm.ConfigurationInfo
-android.content.pm.IPackageDataObserver$Stub
-android.content.pm.IPackageDeleteObserver$Stub
+android.content.pm.ApplicationInfo$1
+android.content.pm.ComponentInfo
+android.content.pm.IPackageManager
android.content.pm.IPackageManager$Stub
android.content.pm.IPackageManager$Stub$Proxy
-android.content.pm.IPackageStatsObserver$Stub
android.content.pm.InstrumentationInfo
-android.content.pm.PackageInfo
+android.content.pm.InstrumentationInfo$1
+android.content.pm.PackageItemInfo
android.content.pm.PackageManager
-android.content.pm.PackageStats
-android.content.pm.PathPermission
+android.content.pm.PackageManager$NameNotFoundException
android.content.pm.PermissionInfo
-android.content.pm.ResolveInfo
-android.content.pm.Signature
-android.content.res.AssetFileDescriptor
-android.content.res.AssetFileDescriptor$1
+android.content.pm.ProviderInfo
+android.content.pm.ProviderInfo$1
+android.content.pm.ResolveInfo$1
+android.content.pm.ServiceInfo$1
android.content.res.AssetManager
android.content.res.AssetManager$AssetInputStream
android.content.res.ColorStateList
android.content.res.ColorStateList$1
-android.content.res.CompatibilityInfo
-android.content.res.CompatibilityInfo$1
android.content.res.Configuration
-android.content.res.Configuration$1
android.content.res.Resources
-android.content.res.Resources$1
+android.content.res.Resources$Theme
android.content.res.StringBlock
android.content.res.TypedArray
android.content.res.XmlBlock
android.content.res.XmlBlock$Parser
-android.content.res.XmlResourceParser
android.database.AbstractCursor
+android.database.AbstractCursor$SelfContentObserver
android.database.AbstractWindowedCursor
+android.database.BulkCursorNative
+android.database.BulkCursorProxy
android.database.BulkCursorToCursorAdaptor
-android.database.CharArrayBuffer
-android.database.CursorJoiner$Result
+android.database.ContentObservable
+android.database.ContentObserver$Transport
+android.database.Cursor
android.database.CursorToBulkCursorAdaptor
+android.database.CursorToBulkCursorAdaptor$ContentObserverProxy
android.database.CursorWindow
-android.database.CursorWindow$1
android.database.CursorWrapper
-android.database.DatabaseUtils
-android.database.MatrixCursor
+android.database.DataSetObservable
+android.database.IContentObserver$Stub$Proxy
android.database.MergeCursor
-android.database.sqlite.SQLiteClosable
android.database.sqlite.SQLiteCursor
android.database.sqlite.SQLiteDatabase
-android.database.sqlite.SQLiteDatabase$ConflictAlgorithm
-android.database.sqlite.SQLiteDebug
-android.database.sqlite.SQLiteDebug$PagerStats
+android.database.sqlite.SQLiteDatabase$CursorFactory
android.database.sqlite.SQLiteDirectCursorDriver
-android.database.sqlite.SQLiteProgram
android.database.sqlite.SQLiteQuery
-android.database.sqlite.SQLiteQueryBuilder
android.database.sqlite.SQLiteStatement
+android.ddm.DdmHandleAppName
+android.ddm.DdmHandleExit
android.ddm.DdmHandleHeap
android.ddm.DdmHandleHello
android.ddm.DdmHandleNativeHeap
-android.ddm.DdmHandleProfiling
android.ddm.DdmHandleThread
android.ddm.DdmRegister
-android.debug.JNITest
-android.emoji.EmojiFactory
-android.graphics.AvoidXfermode
android.graphics.Bitmap
-android.graphics.Bitmap$1
-android.graphics.Bitmap$CompressFormat
-android.graphics.Bitmap$Config
-android.graphics.BitmapFactory
-android.graphics.BitmapFactory$Options
android.graphics.BitmapShader
-android.graphics.BlurMaskFilter
-android.graphics.Camera
android.graphics.Canvas
+android.graphics.Canvas$EdgeType
android.graphics.Color
-android.graphics.ColorFilter
-android.graphics.ColorMatrixColorFilter
-android.graphics.ComposePathEffect
-android.graphics.ComposeShader
-android.graphics.CornerPathEffect
-android.graphics.DashPathEffect
-android.graphics.DiscretePathEffect
-android.graphics.DrawFilter
-android.graphics.EmbossMaskFilter
android.graphics.Interpolator
-android.graphics.LayerRasterizer
-android.graphics.LightingColorFilter
android.graphics.LinearGradient
-android.graphics.MaskFilter
android.graphics.Matrix
-android.graphics.Movie
+android.graphics.Matrix$ScaleToFit
android.graphics.NinePatch
android.graphics.Paint
-android.graphics.Paint$Align
-android.graphics.Paint$Cap
-android.graphics.Paint$FontMetrics
-android.graphics.Paint$FontMetricsInt
-android.graphics.Paint$Join
-android.graphics.Paint$Style
android.graphics.PaintFlagsDrawFilter
android.graphics.Path
-android.graphics.Path$FillType
-android.graphics.PathDashPathEffect
-android.graphics.PathEffect
-android.graphics.PathMeasure
+android.graphics.Path$Direction
android.graphics.Picture
-android.graphics.PixelFormat
-android.graphics.PixelXorXfermode
-android.graphics.Point
-android.graphics.PointF
+android.graphics.PorterDuff
android.graphics.PorterDuff$Mode
-android.graphics.PorterDuffColorFilter
android.graphics.PorterDuffXfermode
-android.graphics.RadialGradient
-android.graphics.Rasterizer
android.graphics.Rect
-android.graphics.Rect$1
android.graphics.RectF
-android.graphics.RectF$1
android.graphics.Region
-android.graphics.Region$1
android.graphics.Region$Op
-android.graphics.RegionIterator
android.graphics.Shader
android.graphics.Shader$TileMode
-android.graphics.SumPathEffect
-android.graphics.SweepGradient
android.graphics.Typeface
android.graphics.Xfermode
-android.graphics.drawable.Animatable
-android.graphics.drawable.AnimatedRotateDrawable
-android.graphics.drawable.AnimatedRotateDrawable$AnimatedRotateState
android.graphics.drawable.AnimationDrawable
-android.graphics.drawable.AnimationDrawable$AnimationState
android.graphics.drawable.BitmapDrawable
android.graphics.drawable.BitmapDrawable$BitmapState
-android.graphics.drawable.ClipDrawable
-android.graphics.drawable.ClipDrawable$ClipState
android.graphics.drawable.ColorDrawable
android.graphics.drawable.ColorDrawable$ColorState
android.graphics.drawable.Drawable
-android.graphics.drawable.Drawable$Callback
-android.graphics.drawable.Drawable$ConstantState
android.graphics.drawable.DrawableContainer
-android.graphics.drawable.DrawableContainer$DrawableContainerState
android.graphics.drawable.GradientDrawable
-android.graphics.drawable.GradientDrawable$GradientState
-android.graphics.drawable.GradientDrawable$Orientation
android.graphics.drawable.LayerDrawable
android.graphics.drawable.LayerDrawable$ChildDrawable
android.graphics.drawable.LayerDrawable$LayerState
@@ -250,432 +194,377 @@ android.graphics.drawable.NinePatchDrawable
android.graphics.drawable.NinePatchDrawable$NinePatchState
android.graphics.drawable.PaintDrawable
android.graphics.drawable.RotateDrawable
+android.graphics.drawable.RotateDrawable$RotateState
+android.graphics.drawable.ScaleDrawable
+android.graphics.drawable.ScaleDrawable$ScaleState
android.graphics.drawable.ShapeDrawable
+android.graphics.drawable.ShapeDrawable$ShapeState
android.graphics.drawable.StateListDrawable
android.graphics.drawable.StateListDrawable$StateListState
android.graphics.drawable.TransitionDrawable
android.graphics.drawable.TransitionDrawable$TransitionState
android.graphics.drawable.shapes.RoundRectShape
-android.hardware.Camera
-android.hardware.ISensorService$Stub
android.hardware.SensorManager
-android.inputmethodservice.AbstractInputMethodService
-android.inputmethodservice.AbstractInputMethodService$AbstractInputMethodSessionImpl
-android.inputmethodservice.ExtractButton
-android.inputmethodservice.ExtractEditText
-android.inputmethodservice.IInputMethodSessionWrapper
-android.inputmethodservice.IInputMethodSessionWrapper$InputMethodEventCallbackWrapper
-android.inputmethodservice.IInputMethodWrapper
-android.inputmethodservice.InputMethodService
-android.inputmethodservice.Keyboard
-android.inputmethodservice.Keyboard$Key
android.inputmethodservice.KeyboardView
-android.inputmethodservice.KeyboardView$2
-android.location.Address
-android.location.Criteria
android.location.ILocationManager$Stub
-android.location.ILocationManager$Stub$Proxy
-android.location.ILocationProvider
-android.location.ILocationProvider$Stub
android.location.Location
-android.location.LocationManager
-android.location.LocationManager$ListenerTransport
-android.location.LocationManager$LpPowerComparator
-android.media.AudioFormat
android.media.AudioManager
-android.media.AudioRecord
-android.media.AudioSystem
-android.media.AudioTrack
-android.media.ExifInterface
-android.media.FaceDetector
-android.media.FaceDetector$Face
android.media.IAudioService$Stub
android.media.IAudioService$Stub$Proxy
-android.media.JetPlayer
-android.media.MediaMetadataRetriever
-android.media.MediaPlayer
-android.media.MediaRecorder
-android.media.MediaScanner
-android.media.MediaScanner$MyMediaScannerClient
-android.media.Ringtone
-android.media.RingtoneManager
-android.media.ToneGenerator
-android.net.ConnectivityManager
-android.net.Credentials
-android.net.DhcpInfo
-android.net.DhcpInfo$1
-android.net.IConnectivityManager$Stub
-android.net.LocalServerSocket
android.net.LocalSocket
+android.net.LocalSocketAddress
android.net.LocalSocketAddress$Namespace
android.net.LocalSocketImpl
android.net.LocalSocketImpl$SocketInputStream
android.net.LocalSocketImpl$SocketOutputStream
-android.net.NetworkConnectivityListener
+android.net.NetworkConnectivityListener$State
android.net.NetworkInfo
android.net.NetworkInfo$DetailedState
-android.net.NetworkUtils
+android.net.SSLCertificateSocketFactory
android.net.Uri
+android.net.Uri$1
+android.net.Uri$AbstractHierarchicalUri
+android.net.Uri$AbstractPart
android.net.Uri$HierarchicalUri
android.net.Uri$OpaqueUri
android.net.Uri$Part
+android.net.Uri$Part$EmptyPart
+android.net.Uri$PathPart
+android.net.Uri$PathSegments
android.net.Uri$StringUri
android.net.WebAddress
android.net.http.AndroidHttpClient
+android.net.http.AndroidHttpClient$1
android.net.http.AndroidHttpClient$2
-android.net.http.CertificateChainValidator
-android.net.http.Connection
+android.net.http.AndroidHttpClient$CurlLogger
android.net.http.DomainNameChecker
+android.net.http.CertificateChainValidator
android.net.http.EventHandler
-android.net.http.Headers
android.net.http.HttpsConnection
-android.net.http.Request
android.net.http.RequestQueue
-android.net.http.SslCertificate
-android.net.vpn.IVpnService$Stub
-android.net.vpn.PptpProfile
-android.net.vpn.VpnManager
-android.net.vpn.VpnProfile
-android.net.vpn.VpnState
-android.net.vpn.VpnType
+android.net.http.SslError
android.net.wifi.IWifiManager$Stub
-android.net.wifi.IWifiManager$Stub$Proxy
-android.net.wifi.WifiManager
-android.net.wifi.WifiNative
-android.opengl.GLES10
-android.opengl.GLES10Ext
-android.opengl.GLES11
-android.opengl.GLES11Ext
-android.opengl.GLU
-android.opengl.GLUtils
-android.opengl.Matrix
-android.opengl.Visibility
-android.os.Base64Utils
+android.net.wifi.SupplicantState
+android.net.wifi.WifiConfiguration
+android.net.wifi.WifiInfo
+android.opengl.Material
android.os.Binder
android.os.BinderProxy
android.os.Build
-android.os.Build$VERSION
android.os.Bundle
-android.os.DeadObjectException
-android.os.Debug
-android.os.Debug$MemoryInfo
+android.os.Bundle$1
android.os.Environment
-android.os.Exec
-android.os.FileObserver$ObserverThread
android.os.FileUtils
-android.os.FileUtils$FileStatus
android.os.Handler
-android.os.Hardware
+android.os.HandlerThread
android.os.IBinder
-android.os.ICheckinService$Stub
android.os.IHardwareService$Stub
-android.os.IInterface
-android.os.IMountService$Stub
-android.os.IParentalControlCallback$Stub
+android.os.IHardwareService$Stub$Proxy
android.os.IPowerManager$Stub
+android.os.IPowerManager$Stub$Proxy
+android.os.IServiceManager
android.os.Looper
-android.os.MemoryFile
android.os.Message
-android.os.NetStat
+android.os.Message$1
+android.os.MessageQueue
+android.os.MessageQueue$IdleHandler
android.os.Parcel
-android.os.Parcel$1
-android.os.ParcelFileDescriptor
-android.os.ParcelFileDescriptor$1
-android.os.Parcelable
-android.os.Parcelable$Creator
android.os.PatternMatcher
-android.os.Power
+android.os.PatternMatcher$1
+android.os.PowerManager
+android.os.PowerManager$WakeLock
+android.os.PowerManager$WakeLock$1
android.os.Process
-android.os.ResultReceiver
-android.os.StatFs
-android.os.SystemClock
-android.os.SystemProperties
-android.os.UEventObserver
-android.pim.EventRecurrence
+android.os.ServiceManager
+android.os.ServiceManagerNative
+android.os.ServiceManagerProxy
+android.os.Vibrator
android.preference.CheckBoxPreference
-android.preference.CheckBoxPreference$SavedState
android.preference.DialogPreference
android.preference.EditTextPreference
android.preference.ListPreference
-android.preference.ListPreference$SavedState
android.preference.Preference
android.preference.PreferenceActivity
android.preference.PreferenceGroup
android.preference.PreferenceGroupAdapter
-android.preference.PreferenceInflater
android.preference.PreferenceManager
android.preference.PreferenceScreen
android.preference.RingtonePreference
-android.preference.VolumePreference
-android.preference.VolumePreference$SeekBarVolumizer
-android.provider.Browser
-android.provider.Calendar$Attendees
-android.provider.Calendar$BusyBits
-android.provider.Calendar$CalendarAlerts
-android.provider.Calendar$Calendars
-android.provider.Calendar$Events
-android.provider.Calendar$Instances
-android.provider.CallLog$Calls
-android.provider.Checkin$Events$Tag
-android.provider.Checkin$Properties
-android.provider.Checkin$Properties$Tag
-android.provider.Checkin$Stats$Tag
-android.provider.Contacts
-android.provider.Contacts$ContactMethods
-android.provider.Contacts$People
-android.provider.Contacts$Phones
-android.provider.Contacts$Presence
-android.provider.Contacts$Settings
-android.provider.Downloads
-android.provider.Gmail
-android.provider.Gmail$AttachmentOrigin
-android.provider.Gmail$AttachmentRendition
-android.provider.Gmail$ConversationCursor
-android.provider.Gmail$CursorStatus
-android.provider.Gmail$LabelMap
-android.provider.Gmail$MessageCursor
-android.provider.Gmail$PersonalLevel
-android.provider.Gmail$Settings
-android.provider.Im$Account
-android.provider.Im$Avatars
-android.provider.Im$Chats
-android.provider.Im$Contacts
-android.provider.Im$LastRmqId
-android.provider.Im$Messages
-android.provider.Im$OutgoingRmq
-android.provider.Im$Presence
-android.provider.Im$Provider
-android.provider.Im$ProviderSettings
-android.provider.MediaStore
-android.provider.MediaStore$Audio$Albums
-android.provider.MediaStore$Audio$Artists
-android.provider.MediaStore$Audio$Artists$Albums
-android.provider.MediaStore$Audio$Media
-android.provider.MediaStore$Audio$Playlists
-android.provider.MediaStore$Images$Media
-android.provider.MediaStore$Images$Thumbnails
-android.provider.SearchRecentSuggestions
-android.provider.Settings$Gservices
-android.provider.Settings$Secure
-android.provider.Settings$System
-android.provider.SubscribedFeeds$Feeds
-android.provider.Telephony$Carriers
-android.provider.Telephony$Mms
-android.provider.Telephony$MmsSms
-android.provider.Telephony$MmsSms$PendingMessages
-android.provider.Telephony$Sms
-android.provider.Telephony$Sms$Conversations
-android.provider.Telephony$Threads
-android.provider.UserDictionary
-android.provider.UserDictionary$Words
android.sax.RootElement
-android.sax.RootElement$Handler
-android.security.Keystore
-android.security.Keystore$FileKeystore
-android.security.Md5MessageDigest
-android.security.MessageDigest
-android.security.ServiceCommand
-android.security.Sha1MessageDigest
-android.server.BluetoothA2dpService
-android.server.BluetoothDeviceService
-android.server.BluetoothEventLoop
-android.server.data.BuildData
-android.server.data.CrashData
-android.server.data.ThrowableData
android.server.search.SearchableInfo
-android.server.search.Searchables
-android.speech.IRecognitionListener$Stub
-android.speech.IRecognitionService$Stub
-android.speech.RecognitionResult
-android.speech.RecognitionServiceUtil
-android.speech.srec.MicrophoneInputStream
-android.speech.srec.Recognizer
-android.speech.tts.ITts$Stub
-android.speech.tts.ITts$Stub$Proxy
-android.speech.tts.TextToSpeech
+android.server.search.SearchableInfo$1
android.telephony.PhoneNumberUtils
-android.telephony.PhoneStateListener$1
+android.telephony.PhoneStateListener
android.telephony.ServiceState
-android.telephony.SignalStrength
-android.telephony.SmsMessage
android.telephony.TelephonyManager
-android.text.AndroidCharacter
-android.text.Annotation
+android.telephony.SmsManager
+android.telephony.SmsMessage
android.text.AutoText
android.text.BoringLayout
+android.text.BoringLayout$Metrics
android.text.DynamicLayout
-android.text.Html
+android.text.DynamicLayout$ChangeWatcher
+android.text.Editable
+android.text.Editable$Factory
+android.text.GetChars
+android.text.GraphicsOperations
android.text.Html$HtmlParser
-android.text.HtmlToSpannedConverter
-android.text.IClipboard$Stub
+android.text.InputFilter
android.text.Layout
+android.text.Layout$Alignment
+android.text.Layout$Directions
+android.text.Layout$Ellipsizer
+android.text.NoCopySpan
+android.text.NoCopySpan$Concrete
+android.text.PackedIntVector
+android.text.PackedObjectVector
+android.text.ParcelableSpan
android.text.Selection
+android.text.Selection$END
+android.text.Selection$START
+android.text.SpanWatcher
+android.text.Spannable
+android.text.Spannable$Factory
+android.text.SpannableString
android.text.SpannableStringBuilder
+android.text.SpannableStringInternal
+android.text.Spanned
android.text.SpannedString
android.text.StaticLayout
+android.text.Styled
+android.text.TextPaint
android.text.TextUtils
+android.text.TextUtils$1
+android.text.TextUtils$EllipsizeCallback
+android.text.TextUtils$SimpleStringSplitter
+android.text.TextUtils$TruncateAt
+android.text.TextWatcher
android.text.format.DateUtils
-android.text.format.Formatter
android.text.format.Time
android.text.method.ArrowKeyMovementMethod
android.text.method.BaseKeyListener
-android.text.method.DialerKeyListener
-android.text.method.DigitsKeyListener
-android.text.method.LinkMovementMethod
+android.text.method.KeyListener
android.text.method.MetaKeyKeyListener
+android.text.method.MovementMethod
android.text.method.QwertyKeyListener
+android.text.method.ReplacementTransformationMethod
android.text.method.ReplacementTransformationMethod$SpannedReplacementCharSequence
android.text.method.SingleLineTransformationMethod
android.text.method.TextKeyListener
-android.text.style.BulletSpan
-android.text.style.ImageSpan
+android.text.method.TextKeyListener$Capitalize
+android.text.method.TextKeyListener$SettingsObserver
+android.text.method.TransformationMethod
+android.text.style.AlignmentSpan
+android.text.style.CharacterStyle
+android.text.style.ForegroundColorSpan
+android.text.style.LeadingMarginSpan
+android.text.style.LineBackgroundSpan
+android.text.style.LineHeightSpan
android.text.style.MetricAffectingSpan
+android.text.style.ParagraphStyle
+android.text.style.ReplacementSpan
android.text.style.StyleSpan
-android.text.style.TextAppearanceSpan
android.text.style.URLSpan
+android.text.style.UpdateAppearance
+android.text.style.UpdateLayout
+android.text.style.WrapTogetherSpan
android.text.util.Linkify
android.text.util.Regex
-android.text.util.Rfc822Validator
+android.util.AndroidRuntimeException
android.util.AttributeSet
android.util.DisplayMetrics
-android.util.EventLog
-android.util.EventLog$Event
-android.util.EventLog$List
android.util.FloatMath
-android.util.Log
-android.util.LongSparseArray
android.util.SparseArray
-android.util.StateSet
android.util.TypedValue
-android.util.Xml
-android.util.Xml$Encoding
+android.util.Xml$XmlSerializerFactory
android.view.AbsSavedState
+android.view.ContextMenu
+android.view.ContextMenu$ContextMenuInfo
android.view.ContextThemeWrapper
android.view.Display
android.view.FocusFinder
-android.view.GestureDetector
+android.view.FocusFinder$1
android.view.GestureDetector$SimpleOnGestureListener
-android.view.IRotationWatcher$Stub
+android.view.Gravity
+android.view.IWindow
+android.view.IWindow$Stub
+android.view.IWindowManager
android.view.IWindowManager$Stub
android.view.IWindowManager$Stub$Proxy
+android.view.IWindowSession
android.view.IWindowSession$Stub
+android.view.IWindowSession$Stub$Proxy
android.view.KeyCharacterMap
-android.view.KeyCharacterMap$KeyData
android.view.KeyEvent
+android.view.KeyEvent$1
+android.view.KeyEvent$Callback
android.view.LayoutInflater
-android.view.MenuInflater$MenuState
+android.view.LayoutInflater$Factory
+android.view.Menu
+android.view.MenuInflater
+android.view.MenuItem
android.view.MotionEvent
+android.view.MotionEvent$1
android.view.Surface
-android.view.Surface$1
-android.view.SurfaceSession
+android.view.SurfaceHolder
android.view.SurfaceView
+android.view.TouchDelegate
android.view.VelocityTracker
android.view.View
+android.view.View$AttachInfo
android.view.View$AttachInfo$Callbacks
-android.view.View$AttachInfo$InvalidateInfo
android.view.View$BaseSavedState
+android.view.View$BaseSavedState$1
+android.view.View$MeasureSpec
+android.view.View$OnCreateContextMenuListener
+android.view.View$ScrollabilityCache
+android.view.ViewConfiguration
android.view.ViewGroup
-android.view.ViewParent
+android.view.ViewGroup$LayoutParams
+android.view.ViewGroup$MarginLayoutParams
+android.view.ViewManager
android.view.ViewRoot
+android.view.ViewRoot$1
+android.view.ViewRoot$InputMethodCallback
+android.view.ViewRoot$RunQueue
+android.view.ViewRoot$TrackballAxis
+android.view.ViewRoot$W
android.view.ViewStub
+android.view.ViewTreeObserver
+android.view.ViewTreeObserver$InternalInsetsInfo
+android.view.ViewTreeObserver$OnPreDrawListener
android.view.Window
+android.view.Window$Callback
+android.view.Window$LocalWindowManager
+android.view.WindowLeaked
+android.view.WindowManager
android.view.WindowManager$LayoutParams
+android.view.WindowManager$LayoutParams$1
android.view.WindowManagerImpl
-android.view.accessibility.AccessibilityEvent
-android.view.accessibility.AccessibilityEvent$1
+android.view.animation.AccelerateDecelerateInterpolator
+android.view.animation.AlphaAnimation
android.view.animation.Animation
android.view.animation.AnimationSet
+android.view.animation.LinearInterpolator
+android.view.animation.Transformation
android.view.inputmethod.BaseInputConnection
android.view.inputmethod.CompletionInfo
+android.view.inputmethod.CompletionInfo$1
+
android.view.inputmethod.EditorInfo
+android.view.inputmethod.EditorInfo$1
+
android.view.inputmethod.ExtractedText
+android.view.inputmethod.ExtractedText$1
+
+android.view.inputmethod.ExtractedTextRequest
+android.view.inputmethod.ExtractedTextRequest$1
+
+android.view.inputmethod.InputBinding
+android.view.inputmethod.InputBinding$1
+android.view.inputmethod.InputConnection
+android.view.inputmethod.InputMethod
+android.view.inputmethod.InputMethod$SessionCallback
+
android.view.inputmethod.InputMethodInfo
+android.view.inputmethod.InputMethodInfo$1
android.view.inputmethod.InputMethodManager
android.view.inputmethod.InputMethodManager$1
+android.view.inputmethod.InputMethodManager$2
+android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper
+android.view.inputmethod.InputMethodManager$H
+
+android.view.inputmethod.InputMethodSession
+android.view.inputmethod.InputMethodSession$EventCallback
android.webkit.BrowserFrame
-android.webkit.CacheLoader
android.webkit.CacheManager
android.webkit.CallbackProxy
-android.webkit.CallbackProxy$ResultTransport
android.webkit.CookieManager
android.webkit.CookieSyncManager
-android.webkit.DataLoader
-android.webkit.GearsPermissionsManager
android.webkit.HttpDateTime
android.webkit.JWebCoreJavaBridge
android.webkit.LoadListener
android.webkit.MimeTypeMap
-android.webkit.TextDialog
android.webkit.URLUtil
-android.webkit.WebIconDatabase$IconListener
+android.webkit.WebBackForwardList
+android.webkit.WebHistoryItem
+android.webkit.WebIconDatabase
+android.webkit.WebIconDatabase$EventHandler
+android.webkit.WebIconDatabase$EventHandler$1
+android.webkit.WebIconDatabase$EventHandler$IconResult
android.webkit.WebSettings
+android.webkit.WebSettings$EventHandler
+android.webkit.WebSettings$EventHandler$1
+android.webkit.WebSettings$LayoutAlgorithm
+android.webkit.WebSettings$RenderPriority
android.webkit.WebSettings$TextSize
+android.webkit.WebSyncManager
+android.webkit.WebSyncManager$SyncHandler
+android.webkit.WebTextView
android.webkit.WebView
-android.webkit.WebView$HitTestResult
+android.webkit.WebView$ExtendedZoomControls
+android.webkit.WebView$PrivateHandler
android.webkit.WebViewCore
+android.webkit.WebViewCore$CursorData
+android.webkit.WebViewCore$EventHub
+android.webkit.WebViewCore$EventHub$1
+android.webkit.WebViewCore$WebCoreThread
+android.webkit.WebViewCore$WebCoreThread$1
android.webkit.WebViewDatabase
-android.webkit.gears.ApacheHttpRequestAndroid
-android.webkit.gears.ApacheHttpRequestAndroid$Buffer
-android.webkit.gears.NativeDialog
android.widget.AbsListView
-android.widget.AbsListView$3
+android.widget.AbsListView$CheckForLongPress
+android.widget.AbsListView$CheckForTap
+android.widget.AbsListView$LayoutParams
android.widget.AbsListView$PerformClick
+android.widget.AbsListView$RecycleBin
android.widget.AbsListView$SavedState
+android.widget.AbsListView$SavedState$1
android.widget.AbsSeekBar
android.widget.AbsSpinner
-android.widget.AbsSpinner$SavedState
android.widget.AbsoluteLayout
+android.widget.AbsoluteLayout$LayoutParams
android.widget.AdapterView
-android.widget.AnalogClock
-android.widget.AppSecurityPermissions
-android.widget.AppSecurityPermissions$State
+android.widget.AdapterView$AdapterDataSetObserver
android.widget.ArrayAdapter
android.widget.AutoCompleteTextView
+android.widget.AutoCompleteTextView$DropDownItemClickListener
android.widget.AutoCompleteTextView$DropDownListView
android.widget.BaseAdapter
-android.widget.BaseExpandableListAdapter
+android.widget.Button
android.widget.CheckBox
+android.widget.Checkable
android.widget.CheckedTextView
-android.widget.Chronometer
android.widget.CompoundButton
-android.widget.CompoundButton$SavedState
android.widget.CursorAdapter
+android.widget.CursorAdapter$ChangeObserver
+android.widget.CursorAdapter$MyDataSetObserver
android.widget.CursorTreeAdapter
-android.widget.DatePicker
android.widget.EditText
-android.widget.ExpandableListConnector
-android.widget.ExpandableListConnector$GroupMetadata
android.widget.ExpandableListView
-android.widget.FastScroller
android.widget.FrameLayout
+android.widget.FrameLayout$LayoutParams
android.widget.Gallery
-android.widget.GridView
android.widget.HeaderViewListAdapter
android.widget.ImageView
android.widget.ImageView$ScaleType
android.widget.LinearLayout
+android.widget.LinearLayout$LayoutParams
android.widget.ListView
+android.widget.ListView$ArrowScrollFocusResult
android.widget.ListView$SavedState
-android.widget.MediaController
-android.widget.MultiAutoCompleteTextView
+android.widget.ListView$SavedState$1
android.widget.PopupWindow
-android.widget.PopupWindow$PopupViewContainer
android.widget.ProgressBar
-android.widget.ProgressBar$SavedState
-android.widget.RadioButton
android.widget.RadioGroup
android.widget.RatingBar
android.widget.RelativeLayout
-android.widget.RelativeLayout$DependencyGraph$Node
+android.widget.RelativeLayout$LayoutParams
android.widget.RemoteViews
-android.widget.ResourceCursorAdapter
-android.widget.ResourceCursorTreeAdapter
android.widget.ScrollBarDrawable
android.widget.ScrollView
+android.widget.Scroller
android.widget.SeekBar
-android.widget.SimpleAdapter
android.widget.SimpleCursorAdapter
-android.widget.SimpleCursorTreeAdapter
android.widget.SlidingDrawer
android.widget.Spinner
android.widget.Spinner$DropDownAdapter
@@ -684,335 +573,248 @@ android.widget.TabWidget
android.widget.TableLayout
android.widget.TableRow
android.widget.TextView
+android.widget.TextView$1
+android.widget.TextView$Blink
android.widget.TextView$BufferType
-android.widget.TextView$CommitSelectionReceiver
+android.widget.TextView$ChangeWatcher
+android.widget.TextView$CharWrapper
+android.widget.TextView$Drawables
+android.widget.TextView$InputContentType
+android.widget.TextView$InputMethodState
+android.widget.TextView$Marquee
+android.widget.TextView$MenuHandler
android.widget.TextView$SavedState
-android.widget.TimePicker
-android.widget.TimePicker$SavedState
-android.widget.Toast
-android.widget.Toast$TN
+android.widget.TextView$SavedState$1
+android.widget.ToggleButton
android.widget.TwoLineListItem
-android.widget.VideoView
android.widget.ViewAnimator
android.widget.ViewSwitcher
android.widget.ZoomButton
-android.widget.ZoomButtonsController
android.widget.ZoomControls
-com.android.internal.R$drawable
-com.android.internal.R$styleable
-com.android.internal.app.AlertActivity
-com.android.internal.app.AlertController
-com.android.internal.app.AlertController$AlertParams
-com.android.internal.app.AlertController$AlertParams$1
-com.android.internal.app.AlertController$RecycleListView
-com.android.internal.app.ChooserActivity
-com.android.internal.app.ResolverActivity
-com.android.internal.app.ResolverActivity$ResolveListAdapter
-com.android.internal.app.RingtonePickerActivity
-com.android.internal.appwidget.IAppWidgetHost$Stub
-com.android.internal.appwidget.IAppWidgetService$Stub
com.android.internal.database.ArrayListCursor
com.android.internal.database.SortCursor
-com.android.internal.database.SortCursor$1
-com.android.internal.graphics.NativeUtils
-com.android.internal.location.DummyLocationProvider
-com.android.internal.location.GpsLocationProvider
+com.android.internal.appwidget.IAppWidgetService$Stub
+com.android.internal.http.multipart.FilePart
+com.android.internal.http.multipart.MultipartEntity
+com.android.internal.http.multipart.Part
+com.android.internal.http.multipart.PartSource
+com.android.internal.http.multipart.StringPart
+com.android.internal.logging.AndroidConfig
com.android.internal.logging.AndroidHandler
com.android.internal.os.AndroidPrintStream
-com.android.internal.os.BinderInternal
com.android.internal.os.BinderInternal$GcWatcher
com.android.internal.os.LoggingPrintStream
+com.android.internal.os.LoggingPrintStream$1
com.android.internal.os.RuntimeInit
com.android.internal.os.RuntimeInit$1
-com.android.internal.os.ZygoteConnection
-com.android.internal.os.ZygoteConnection$Arguments
-com.android.internal.os.ZygoteInit
+com.android.internal.os.RuntimeInit$UncaughtHandler
+com.android.internal.os.ZygoteInit$MethodAndArgsCaller
+com.android.internal.policy.IPolicy
com.android.internal.policy.PolicyManager
com.android.internal.policy.impl.PhoneLayoutInflater
com.android.internal.policy.impl.PhoneWindow
+com.android.internal.policy.impl.PhoneWindow$1
+com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback
com.android.internal.policy.impl.PhoneWindow$DecorView
+com.android.internal.policy.impl.PhoneWindow$PanelFeatureState
com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState
-com.android.internal.policy.impl.PhoneWindowManager
+com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState$1
com.android.internal.policy.impl.Policy
-com.android.internal.telephony.GsmAlphabet
+com.android.internal.telephony.Connection$DisconnectCause
+com.android.internal.telephony.Connection$PostDialState
com.android.internal.telephony.IPhoneStateListener$Stub
-com.android.internal.telephony.IPhoneSubInfo$Stub
com.android.internal.telephony.ITelephony$Stub
-com.android.internal.telephony.ITelephony$Stub$Proxy
-com.android.internal.telephony.ITelephonyRegistry$Stub
-com.android.internal.telephony.IccCard$State
+com.android.internal.telephony.Phone
+com.android.internal.telephony.Phone$DataActivityState
+com.android.internal.telephony.Phone$DataState
com.android.internal.telephony.Phone$State
+com.android.internal.telephony.Phone$SuppService
+com.android.internal.telephony.PhoneBase
com.android.internal.telephony.PhoneStateIntentReceiver
-com.android.internal.telephony.SmsMessageBase
-com.android.internal.telephony.gsm.GsmSmsAddress
-com.android.internal.telephony.gsm.SmsMessage
-com.android.internal.telephony.gsm.SmsMessage$SubmitPdu
-com.android.internal.util.ArrayUtils
+com.android.internal.telephony.IccCard$State
+com.android.internal.telephony.BaseCommands
+com.android.internal.telephony.CallForwardInfo
+com.android.internal.telephony.CommandsInterface
+com.android.internal.telephony.DriverCall
+com.android.internal.telephony.DriverCall$State
+com.android.internal.telephony.gsm.GsmConnection
+com.android.internal.telephony.gsm.GSMPhone
+com.android.internal.telephony.GsmAlphabet
+com.android.internal.telephony.gsm.GsmMmiCode
+com.android.internal.telephony.gsm.SimCard
+com.android.internal.telephony.ISms$Stub
+com.android.internal.telephony.RIL
+com.android.internal.telephony.ServiceStateTracker
+
+com.android.internal.telephony.gsm.stk.ComprehensionTlvTag
+com.android.internal.telephony.gsm.stk.ResultCode
com.android.internal.util.FastXmlSerializer
com.android.internal.view.IInputConnectionWrapper
+com.android.internal.view.IInputConnectionWrapper$MyHandler
+com.android.internal.view.IInputConnectionWrapper$SomeArgs
+
+com.android.internal.view.IInputContext
com.android.internal.view.IInputContext$Stub
+com.android.internal.view.IInputContext$Stub$Proxy
+
+com.android.internal.view.IInputContextCallback
+com.android.internal.view.IInputContextCallback$Stub
+com.android.internal.view.IInputContextCallback$Stub$Proxy
+
+com.android.internal.view.IInputMethod
com.android.internal.view.IInputMethod$Stub
+com.android.internal.view.IInputMethod$Stub$Proxy
+
+com.android.internal.view.IInputMethodCallback
+com.android.internal.view.IInputMethodCallback$Stub
+com.android.internal.view.IInputMethodCallback$Stub$Proxy
+
+com.android.internal.view.IInputMethodClient
+com.android.internal.view.IInputMethodClient$Stub
+com.android.internal.view.IInputMethodClient$Stub$Proxy
+
+com.android.internal.view.IInputMethodManager
com.android.internal.view.IInputMethodManager$Stub
+com.android.internal.view.IInputMethodManager$Stub$Proxy
+
+com.android.internal.view.IInputMethodSession
+com.android.internal.view.IInputMethodSession$Stub
+com.android.internal.view.IInputMethodSession$Stub$Proxy
+
+com.android.internal.view.InputBindResult
+com.android.internal.view.InputBindResult$1
+
+com.android.internal.view.InputConnectionWrapper
com.android.internal.view.InputConnectionWrapper$InputContextCallback
com.android.internal.view.menu.ExpandedMenuView
com.android.internal.view.menu.IconMenuItemView
com.android.internal.view.menu.IconMenuView
-com.android.internal.view.menu.IconMenuView$SavedState
com.android.internal.view.menu.ListMenuItemView
com.android.internal.view.menu.MenuBuilder
+com.android.internal.view.menu.MenuBuilder$Callback
+com.android.internal.view.menu.MenuDialogHelper
com.android.internal.view.menu.MenuItemImpl
com.android.internal.view.menu.SubMenuBuilder
-com.android.internal.widget.LockPatternUtils
+com.android.internal.widget.LinearLayoutWithDefaultTouchRecepient
+com.android.internal.widget.LockPatternView
com.android.internal.widget.NumberPicker
com.android.internal.widget.NumberPickerButton
-com.android.internal.widget.Smileys
com.google.android.gdata.client.AndroidGDataClient
-com.google.android.gdata.client.AndroidXmlParserFactory
+com.google.android.gdata.client.AndroidGDataClient$PostRequestCreator
com.google.android.gles_jni.EGLImpl
com.google.android.gles_jni.GLImpl
+com.google.android.mms.ContentType
+com.google.android.mms.pdu.CharacterSets
+com.google.android.mms.pdu.PduPart
com.google.android.mms.pdu.PduPersister
-com.google.android.mms.util.PduCache
com.google.android.net.GoogleHttpClient
-com.google.android.net.NetworkStatsEntity
com.google.android.net.UrlRules
com.google.android.net.UrlRules$Rule
+com.google.android.util.SimplePullParser
com.google.common.Config
-com.google.common.Log
-com.google.common.android.AndroidConfig
-com.google.common.async.AsyncHttpRequestFactory$AsyncHttpRequestImpl
-com.google.common.graphics.android.AndroidGraphics
-com.google.common.io.BaseHttpConnectionFactory
-com.google.common.io.IoUtil
-com.google.common.io.android.AndroidHttpClient
-com.google.common.io.android.AndroidHttpConnectionFactory
-com.google.common.io.android.AndroidPersistentStore
com.google.common.io.protocol.ProtoBuf
com.google.common.io.protocol.ProtoBufType
com.google.common.util.text.TextUtil
-com.google.masf.BlockingByteQueue
-com.google.masf.MobileServiceMux
-com.google.masf.protocol.PlainRequest
-com.google.masf.services.EventLogService
-com.google.masf.services.LogserviceMessageTypes
-com.google.masf.services.resume.WindowResumeService
-com.google.wireless.gdata.calendar.client.CalendarClient
-com.google.wireless.gdata.client.GDataServiceClient
-com.google.wireless.gdata.contacts.client.ContactsClient
-com.google.wireless.gdata.contacts.parser.xml.XmlContactsGDataParserFactory
com.ibm.icu4jni.charset.CharsetDecoderICU
com.ibm.icu4jni.charset.CharsetEncoderICU
com.ibm.icu4jni.charset.CharsetICU
-com.ibm.icu4jni.charset.CharsetProviderICU
-com.ibm.icu4jni.charset.NativeConverter
-com.ibm.icu4jni.common.ErrorCode
-com.ibm.icu4jni.lang.UCharacter
-com.ibm.icu4jni.regex.NativeRegEx
+com.ibm.icu4jni.text.CollationAttribute
com.ibm.icu4jni.text.DecimalFormat
-com.ibm.icu4jni.text.NativeBreakIterator
-com.ibm.icu4jni.text.NativeCollation
-com.ibm.icu4jni.text.NativeDecimalFormat
+com.ibm.icu4jni.text.DecimalFormatSymbols
com.ibm.icu4jni.text.NativeDecimalFormat$UNumberFormatAttribute
com.ibm.icu4jni.text.NativeDecimalFormat$UNumberFormatSymbol
-com.ibm.icu4jni.text.RuleBasedNumberFormat
-com.ibm.icu4jni.util.Resources
-dalvik.system.NativeStart
+com.ibm.icu4jni.text.RuleBasedCollator
+dalvik.system.DexFile
dalvik.system.PathClassLoader
-dalvik.system.TouchDex
-dalvik.system.VMDebug
-dalvik.system.VMRuntime
-dalvik.system.VMStack
-dalvik.system.Zygote
java.beans.PropertyChangeEvent
+java.beans.PropertyChangeListener
java.beans.PropertyChangeSupport
java.io.BufferedInputStream
-java.io.BufferedReader
-java.io.Closeable
-java.io.DataInput
-java.io.DataOutput
-java.io.DataOutputStream
-java.io.EmulatedFieldsForDumping
-java.io.EmulatedFieldsForLoading
+java.io.ByteArrayInputStream
+java.io.ByteArrayOutputStream
java.io.File
java.io.FileDescriptor
java.io.FileInputStream
java.io.FileInputStream$RepositioningLock
-java.io.FileOutputStream
-java.io.FilterOutputStream
-java.io.Flushable
-java.io.InputStream
-java.io.InputStreamReader
-java.io.ObjectInput
-java.io.ObjectInputStream
-java.io.ObjectOutput
-java.io.ObjectOutputStream
+java.io.FileNotFoundException
+java.io.FilterInputStream
+java.io.IOException
java.io.ObjectStreamClass
-java.io.ObjectStreamConstants
-java.io.ObjectStreamField
-java.io.OutputStream
-java.io.PrintStream
java.io.PrintWriter
-java.io.PushbackReader
java.io.RandomAccessFile
-java.io.Reader
-java.io.Serializable
+java.io.RandomAccessFile$RepositionLock
java.io.StringWriter
-java.lang.AbstractStringBuilder
-java.lang.Appendable
-java.lang.ArrayIndexOutOfBoundsException
-java.lang.Boolean
-java.lang.BootClassLoader
-java.lang.Byte
-java.lang.CharSequence
-java.lang.Character
+java.io.Writer
java.lang.Character$valueOfCache
java.lang.Class
java.lang.ClassCache
-java.lang.ClassCache$EnumComparator
-java.lang.ClassLoader
-java.lang.ClassLoader$SystemClassLoader
-java.lang.Cloneable
-java.lang.Comparable
-java.lang.Double
-java.lang.Enum
-java.lang.Error
-java.lang.Exception
-java.lang.Float
+java.lang.ClassNotFoundException
java.lang.IllegalArgumentException
+java.lang.IllegalStateException
java.lang.Integer
java.lang.Integer$valueOfCache
-java.lang.InternalError
-java.lang.InterruptedException
-java.lang.Iterable
-java.lang.LangAccessImpl
java.lang.LinkageError
java.lang.Long
java.lang.Long$valueOfCache
-java.lang.Math
java.lang.NoClassDefFoundError
-java.lang.Number
java.lang.NumberFormatException
java.lang.Object
-java.lang.OutOfMemoryError
-java.lang.Readable
-java.lang.Runnable
java.lang.Runtime
java.lang.RuntimeException
-java.lang.RuntimePermission
-java.lang.SecurityException
-java.lang.Short
java.lang.Short$valueOfCache
-java.lang.StackOverflowError
-java.lang.StackTraceElement
-java.lang.StrictMath
java.lang.String
-java.lang.String$CaseInsensitiveComparator
java.lang.StringBuffer
java.lang.StringBuilder
-java.lang.System
-java.lang.SystemProperties
java.lang.Thread
-java.lang.Thread$State
-java.lang.Thread$UncaughtExceptionHandler
-java.lang.ThreadGroup
-java.lang.ThreadGroup$ChildrenGroupsLock
-java.lang.ThreadGroup$ChildrenThreadsLock
+java.lang.ThreadLocal
+java.lang.ThreadLocal$Values
java.lang.Throwable
-java.lang.UnsatisfiedLinkError
-java.lang.UnsupportedOperationException
-java.lang.VMClassLoader
java.lang.VMThread
-java.lang.VirtualMachineError
-java.lang.Void
-java.lang.annotation.Annotation
-java.lang.ref.Reference
java.lang.ref.ReferenceQueue
java.lang.ref.SoftReference
java.lang.ref.WeakReference
-java.lang.reflect.AccessibleObject
-java.lang.reflect.AnnotatedElement
-java.lang.reflect.Array
java.lang.reflect.Constructor
-java.lang.reflect.Field
-java.lang.reflect.GenericDeclaration
-java.lang.reflect.InvocationHandler
-java.lang.reflect.Member
java.lang.reflect.Method
java.lang.reflect.Modifier
-java.lang.reflect.Proxy
-java.lang.reflect.ReflectionAccessImpl
-java.lang.reflect.Type
java.math.BigDecimal
java.math.BigInt
java.math.BigInteger
java.math.Multiplication
-java.net.DatagramPacket
-java.net.HttpURLConnection
-java.net.Inet4Address
+java.net.ContentHandler
java.net.InetAddress
+java.net.InetAddress$CacheElement
java.net.InetAddress$WaitReachable
-java.net.InetSocketAddress
java.net.JarURLConnection
-java.net.NetworkInterface
-java.net.Proxy
-java.net.ProxySelector
+java.net.NegativeCache
+java.net.NetPermission
java.net.ProxySelectorImpl
-java.net.ServerSocket
-java.net.Socket
-java.net.SocketImpl
-java.net.SocketOptions
+java.net.Socket$ConnectLock
java.net.URI
java.net.URL
java.net.URLConnection
-java.nio.BaseByteBuffer
-java.nio.Buffer
-java.nio.BufferFactory
-java.nio.ByteBuffer
+java.net.URLConnection$DefaultContentHandler
+java.net.URLStreamHandler
java.nio.ByteOrder
-java.nio.CharArrayBuffer
-java.nio.CharBuffer
java.nio.CharSequenceAdapter
-java.nio.CharToByteBufferAdapter
java.nio.DirectByteBuffer
-java.nio.FloatBuffer
-java.nio.FloatToByteBufferAdapter
-java.nio.HeapByteBuffer
-java.nio.IntToByteBufferAdapter
-java.nio.NIOAccess
-java.nio.ReadWriteCharArrayBuffer
java.nio.ReadWriteDirectByteBuffer
-java.nio.ReadWriteHeapByteBuffer
java.nio.ReadWriteIntArrayBuffer
+java.nio.ReadWriteShortArrayBuffer
+java.nio.ShortBuffer
java.nio.ShortToByteBufferAdapter
-java.nio.channels.ByteChannel
-java.nio.channels.Channel
-java.nio.channels.FileChannel
-java.nio.channels.GatheringByteChannel
-java.nio.channels.InterruptibleChannel
-java.nio.channels.ReadableByteChannel
-java.nio.channels.ScatteringByteChannel
-java.nio.channels.WritableByteChannel
-java.nio.channels.spi.AbstractInterruptibleChannel
-java.nio.channels.spi.AbstractInterruptibleChannel$1
-java.nio.charset.Charset
-java.nio.charset.Charset$1
-java.nio.charset.CharsetDecoder
java.nio.charset.CharsetEncoder
-java.nio.charset.CoderResult
-java.nio.charset.CodingErrorAction
-java.nio.charset.spi.CharsetProvider
-java.security.AccessController
-java.security.BasicPermission
-java.security.Guard
+java.security.AccessControlContext
+java.security.GeneralSecurityException
java.security.KeyStore
java.security.MessageDigest
-java.security.Permission
-java.security.PrivilegedAction
-java.security.PrivilegedExceptionAction
+java.security.ProtectionDomain
java.security.Provider
java.security.SecureRandom
java.security.Security
-java.security.cert.CertificateParsingException
+java.security.cert.CertPathValidator
+java.security.cert.CertificateFactory
java.security.cert.PKIXParameters
+java.security.cert.TrustAnchor
java.security.cert.X509CertSelector
java.security.cert.X509Certificate
java.text.Collator
@@ -1020,339 +822,364 @@ java.text.DateFormat
java.text.DateFormat$Field
java.text.DecimalFormat
java.text.DecimalFormatSymbols
-java.text.Format
+java.text.MessageFormat
java.text.NumberFormat
+java.text.RuleBasedCollator
java.text.SimpleDateFormat
-java.util.AbstractCollection
-java.util.AbstractList
-java.util.AbstractMap
-java.util.AbstractSet
+java.util.AbstractList$FullListIterator
+java.util.AbstractList$SimpleListIterator
java.util.ArrayList
java.util.Arrays
-java.util.BitSet
+java.util.Arrays$ArrayList
java.util.Calendar
-java.util.Collection
-java.util.Collections
-java.util.Collections$EmptyList
-java.util.Collections$EmptyMap
-java.util.Collections$EmptySet
-java.util.Collections$SynchronizedList
-java.util.Collections$SynchronizedRandomAccessList
-java.util.Collections$UnmodifiableCollection
-java.util.Collections$UnmodifiableCollection$1
+java.util.Collections$SynchronizedCollection
java.util.Collections$UnmodifiableList
-java.util.Collections$UnmodifiableRandomAccessList
-java.util.Collections$UnmodifiableSet
-java.util.Comparator
+java.util.Collections$UnmodifiableMap
+java.util.Collections$UnmodifiableMap$UnmodifiableEntrySet$1
java.util.Date
-java.util.Dictionary
java.util.EnumMap
-java.util.EnumSet
-java.util.Enumeration
+java.util.EventListener
+java.util.EventObject
java.util.Formatter
java.util.GregorianCalendar
java.util.HashMap
-java.util.HashMap$1
-java.util.HashMap$1$1
-java.util.HashMap$Entry
-java.util.HashMap$HashMapIterator
+java.util.HashMap$EntryIterator
+java.util.HashMap$EntrySet
+java.util.HashMap$HashIterator
+java.util.HashMap$HashMapEntry
+java.util.HashMap$KeyIterator
+java.util.HashMap$KeySet
+java.util.HashMap$ValueIterator
+java.util.HashMap$Values
java.util.HashSet
java.util.Hashtable
-java.util.Hashtable$1
-java.util.Hashtable$Entry
+java.util.Hashtable$EntryIterator
+java.util.Hashtable$EntrySet
+java.util.Hashtable$HashIterator
+java.util.Hashtable$HashtableEntry
+java.util.Hashtable$KeyEnumeration
+java.util.Hashtable$KeyIterator
+java.util.Hashtable$KeySet
+java.util.Hashtable$ValueEnumeration
+java.util.Hashtable$ValueIterator
+java.util.Hashtable$Values
java.util.IdentityHashMap
-java.util.Iterator
+java.util.LinkedHashMap
+java.util.LinkedHashMap$EntryIterator
+java.util.LinkedHashMap$KeyIterator
+java.util.LinkedHashMap$LinkedEntry
+java.util.LinkedHashMap$ValueIterator
java.util.LinkedList
+java.util.LinkedList$Link
java.util.List
java.util.Locale
-java.util.Map
-java.util.Map$Entry
-java.util.MapEntry
-java.util.MapEntry$Type
-java.util.PriorityQueue
java.util.Properties
-java.util.PropertyPermission
-java.util.RandomAccess
+java.util.Random
java.util.ResourceBundle
-java.util.Scanner
-java.util.Set
java.util.SimpleTimeZone
-java.util.SortedMap
-java.util.SortedSet
-java.util.SpecialAccess
-java.util.Stack
-java.util.StringTokenizer
java.util.TimeZone
-java.util.Timer
java.util.TreeMap
+java.util.TreeMap$MapEntry
java.util.TreeSet
java.util.Vector
-java.util.Vector$1
java.util.WeakHashMap
java.util.WeakHashMap$Entry
-java.util.concurrent.AbstractExecutorService
-java.util.concurrent.ArrayBlockingQueue
java.util.concurrent.ConcurrentHashMap
-java.util.concurrent.ConcurrentHashMap$Segment
-java.util.concurrent.CopyOnWriteArrayList
+java.util.concurrent.ConcurrentLinkedQueue
java.util.concurrent.DelayQueue
-java.util.concurrent.Executors$DelegatedExecutorService
-java.util.concurrent.Executors$DelegatedScheduledExecutorService
java.util.concurrent.LinkedBlockingQueue
java.util.concurrent.ScheduledThreadPoolExecutor
java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue
-java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask
-java.util.concurrent.Semaphore
-java.util.concurrent.SynchronousQueue
-java.util.concurrent.SynchronousQueue$Node
-java.util.concurrent.ThreadPoolExecutor
java.util.concurrent.TimeUnit
+java.util.concurrent.atomic.AtomicBoolean
java.util.concurrent.atomic.AtomicInteger
-java.util.concurrent.atomic.AtomicLong
-java.util.concurrent.atomic.AtomicReference
java.util.concurrent.atomic.UnsafeAccess
java.util.concurrent.locks.AbstractQueuedSynchronizer
+java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject
+java.util.concurrent.locks.AbstractQueuedSynchronizer$Node
+java.util.concurrent.locks.Lock
+java.util.concurrent.locks.LockSupport
java.util.concurrent.locks.ReentrantLock
+java.util.concurrent.locks.ReentrantLock$FairSync
+java.util.concurrent.locks.ReentrantLock$NonfairSync
java.util.concurrent.locks.ReentrantLock$Sync
+java.util.concurrent.locks.ReentrantReadWriteLock
+java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync
+java.util.concurrent.locks.UnsafeAccess
+java.util.jar.Attributes
java.util.jar.Attributes$Name
+java.util.jar.InitManifest
+java.util.jar.JarEntry
java.util.jar.JarFile
+java.util.jar.JarFile$1JarFileEnumerator
+java.util.jar.JarFile$JarFileInputStream
+java.util.jar.JarVerifier
java.util.jar.Manifest
+java.util.logging.ErrorManager
+java.util.logging.Formatter
+java.util.logging.Handler
java.util.logging.Level
java.util.logging.LogManager
+java.util.logging.LogManager$1
+java.util.logging.LogManager$2
+java.util.logging.LogManager$2$1
+java.util.logging.LogManager$3
java.util.logging.LogRecord
java.util.logging.Logger
-java.util.regex.MatchResult
+java.util.logging.LoggingPermission
+java.util.logging.SimpleFormatter
java.util.regex.Matcher
java.util.regex.Pattern
-java.util.zip.Adler32
-java.util.zip.CRC32
-java.util.zip.Checksum
-java.util.zip.Deflater
java.util.zip.DeflaterOutputStream
java.util.zip.Inflater
+java.util.zip.InflaterInputStream
+java.util.zip.ZipConstants
+java.util.zip.ZipEntry
+java.util.zip.ZipEntry$LittleEndianReader
java.util.zip.ZipFile
-javax.crypto.Cipher
-javax.crypto.spec.SecretKeySpec
-javax.microedition.khronos.egl.EGL
-javax.microedition.khronos.egl.EGL10
+java.util.zip.ZipFile$2
+java.util.zip.ZipFile$RAFStream
javax.microedition.khronos.egl.EGLContext
-javax.microedition.khronos.opengles.GL
-javax.microedition.khronos.opengles.GL10
-javax.microedition.khronos.opengles.GL10Ext
-javax.microedition.khronos.opengles.GL11
-javax.microedition.khronos.opengles.GL11Ext
-javax.microedition.khronos.opengles.GL11ExtensionPack
-javax.net.ssl.DefaultHostnameVerifier
javax.net.ssl.HttpsURLConnection
-javax.net.ssl.SSLContext
javax.net.ssl.SSLHandshakeException
-javax.net.ssl.SSLServerSocket
-javax.net.ssl.SSLSession
-javax.net.ssl.SSLSocket
+javax.security.auth.x500.X500Principal
javax.security.cert.X509Certificate
+javax.security.cert.X509Certificate$2
junit.framework.Assert
org.apache.commons.codec.binary.Base64
org.apache.commons.codec.binary.Hex
org.apache.commons.logging.LogFactory
org.apache.commons.logging.impl.Jdk14Logger
-org.apache.harmony.dalvik.NativeTestTarget
+org.apache.harmony.archive.util.Util
+org.apache.harmony.dalvik.ddmc.Chunk
org.apache.harmony.dalvik.ddmc.ChunkHandler
org.apache.harmony.dalvik.ddmc.DdmServer
-org.apache.harmony.kernel.vm.LangAccess
-org.apache.harmony.kernel.vm.ReflectionAccess
-org.apache.harmony.lang.annotation.AnnotationFactory
-org.apache.harmony.lang.annotation.AnnotationMember
-org.apache.harmony.lang.annotation.AnnotationMember$DefaultValues
-org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnection
+org.apache.harmony.dalvik.ddmc.DdmVmInternal
+org.apache.harmony.luni.internal.net.www.protocol.file.FileURLConnection
+org.apache.harmony.luni.internal.net.www.protocol.file.Handler
+org.apache.harmony.luni.internal.net.www.protocol.http.Handler
org.apache.harmony.luni.internal.net.www.protocol.https.Handler
org.apache.harmony.luni.internal.net.www.protocol.jar.Handler
org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection
+org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$1
+org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnection$JarURLConnectionInputStream
+org.apache.harmony.luni.internal.util.TimezoneGetter
+org.apache.harmony.luni.internal.util.ZoneInfo
org.apache.harmony.luni.internal.util.ZoneInfoDB
-org.apache.harmony.luni.net.GenericIPMreq
org.apache.harmony.luni.net.PlainSocketImpl
-org.apache.harmony.luni.platform.AdapterManager
-org.apache.harmony.luni.platform.Endianness
-org.apache.harmony.luni.platform.IAdaptable
-org.apache.harmony.luni.platform.IAdapterManager
-org.apache.harmony.luni.platform.ICommonDataTypes
-org.apache.harmony.luni.platform.IFileSystem
-org.apache.harmony.luni.platform.IMemorySystem
-org.apache.harmony.luni.platform.INetworkSystem
-org.apache.harmony.luni.platform.ISystemComponent
-org.apache.harmony.luni.platform.OSComponent
-org.apache.harmony.luni.platform.OSComponentFactory
-org.apache.harmony.luni.platform.OSFileSystem
-org.apache.harmony.luni.platform.OSMemory
-org.apache.harmony.luni.platform.OSNetworkSystem
-org.apache.harmony.luni.platform.Platform
org.apache.harmony.luni.platform.PlatformAddress
-org.apache.harmony.luni.platform.PlatformAddressFactory
-org.apache.harmony.luni.util.FloatingPointParser
-org.apache.harmony.luni.util.NumberConverter
-org.apache.harmony.luni.util.PriviAction
-org.apache.harmony.luni.util.Util
-org.apache.harmony.nio.AddressUtil
-org.apache.harmony.nio.FileChannelFactory
-org.apache.harmony.nio.internal.DirectBuffer
-org.apache.harmony.nio.internal.FileChannelImpl
+org.apache.harmony.luni.util.TwoKeyHashMap
org.apache.harmony.nio.internal.FileChannelImpl$RepositioningLock
org.apache.harmony.nio.internal.LockManager
org.apache.harmony.nio.internal.LockManager$1
-org.apache.harmony.nio.internal.WriteOnlyFileChannel
+org.apache.harmony.nio.internal.ReadOnlyFileChannel
+org.apache.harmony.security.asn1.ASN1BitString
+org.apache.harmony.security.asn1.ASN1BitString$ASN1NamedBitList
+org.apache.harmony.security.asn1.ASN1Boolean
+org.apache.harmony.security.asn1.ASN1Explicit
org.apache.harmony.security.asn1.ASN1GeneralizedTime
+org.apache.harmony.security.asn1.ASN1Implicit
org.apache.harmony.security.asn1.ASN1Integer
-org.apache.harmony.security.asn1.ASN1Oid
-org.apache.harmony.security.asn1.ASN1Sequence
+org.apache.harmony.security.asn1.ASN1OctetString
+org.apache.harmony.security.asn1.ASN1SetOf
org.apache.harmony.security.asn1.ASN1StringType
-org.apache.harmony.security.asn1.DerInputStream
-org.apache.harmony.security.asn1.DerOutputStream
+org.apache.harmony.security.asn1.ASN1StringType$1
+org.apache.harmony.security.asn1.ASN1StringType$2
+org.apache.harmony.security.asn1.ASN1StringType$3
+org.apache.harmony.security.asn1.ASN1StringType$4
+org.apache.harmony.security.asn1.ASN1StringType$5
+org.apache.harmony.security.asn1.ASN1StringType$6
+org.apache.harmony.security.asn1.ASN1StringType$7
+org.apache.harmony.security.asn1.ASN1UTCTime
+org.apache.harmony.security.asn1.BitString
+org.apache.harmony.security.fortress.Engine
+org.apache.harmony.security.fortress.SecurityUtils
org.apache.harmony.security.fortress.Services
org.apache.harmony.security.pkcs7.ContentInfo
-org.apache.harmony.security.provider.cert.DRLCertFactory
org.apache.harmony.security.provider.cert.X509CertFactoryImpl
org.apache.harmony.security.provider.cert.X509CertImpl
org.apache.harmony.security.provider.cert.X509CertPathImpl
org.apache.harmony.security.provider.crypto.RandomBitsSupplier
org.apache.harmony.security.provider.crypto.SHA1PRNG_SecureRandomImpl
-org.apache.harmony.security.provider.crypto.SHA1_MessageDigestImpl
org.apache.harmony.security.utils.AlgNameMapper
org.apache.harmony.security.x501.AttributeTypeAndValue
+org.apache.harmony.security.x501.AttributeValue
org.apache.harmony.security.x501.DirectoryString
+org.apache.harmony.security.x501.DirectoryString$1
org.apache.harmony.security.x501.Name
+org.apache.harmony.security.x501.Name$1
org.apache.harmony.security.x509.AlgorithmIdentifier
+org.apache.harmony.security.x509.AlgorithmIdentifier$1
org.apache.harmony.security.x509.BasicConstraints
+org.apache.harmony.security.x509.BasicConstraints$1
org.apache.harmony.security.x509.Certificate
-org.apache.harmony.security.x509.EDIPartyName
+org.apache.harmony.security.x509.Certificate$1
org.apache.harmony.security.x509.Extension
+org.apache.harmony.security.x509.Extension$1
+org.apache.harmony.security.x509.Extension$2
org.apache.harmony.security.x509.Extensions
+org.apache.harmony.security.x509.Extensions$1
org.apache.harmony.security.x509.GeneralName
org.apache.harmony.security.x509.GeneralNames
org.apache.harmony.security.x509.KeyUsage
org.apache.harmony.security.x509.ORAddress
-org.apache.harmony.security.x509.OtherName
-org.apache.harmony.security.x509.PolicyQualifierInfo
org.apache.harmony.security.x509.SubjectPublicKeyInfo
+org.apache.harmony.security.x509.SubjectPublicKeyInfo$1
org.apache.harmony.security.x509.TBSCertificate
+org.apache.harmony.security.x509.TBSCertificate$1
org.apache.harmony.security.x509.Time
+org.apache.harmony.security.x509.Time$1
org.apache.harmony.security.x509.Validity
-org.apache.harmony.text.BidiWrapper
-org.apache.harmony.xml.ExpatAttributes
+org.apache.harmony.security.x509.Validity$1
org.apache.harmony.xml.ExpatParser
org.apache.harmony.xml.ExpatPullParser
-org.apache.harmony.xml.ExpatPullParser$ByteDocument
org.apache.harmony.xml.ExpatReader
-org.apache.harmony.xml.dom.AttrImpl
-org.apache.harmony.xml.dom.CharacterDataImpl
-org.apache.harmony.xml.dom.CommentImpl
-org.apache.harmony.xml.dom.DocumentImpl
-org.apache.harmony.xml.dom.ElementImpl
-org.apache.harmony.xml.dom.InnerNodeImpl
-org.apache.harmony.xml.dom.NodeImpl
-org.apache.harmony.xml.dom.TextImpl
-org.apache.harmony.xml.parsers.DocumentBuilderFactoryImpl
-org.apache.harmony.xml.parsers.DocumentBuilderImpl
-org.apache.harmony.xml.parsers.SAXParserFactoryImpl
-org.apache.harmony.xnet.provider.jsse.AbstractSessionContext
-org.apache.harmony.xnet.provider.jsse.FileClientSessionCache
-org.apache.harmony.xnet.provider.jsse.NativeCrypto
-org.apache.harmony.xnet.provider.jsse.OpenSSLServerSocketImpl
+org.apache.harmony.xnet.provider.jsse.ClientSessionContext
org.apache.harmony.xnet.provider.jsse.OpenSSLSessionImpl
-org.apache.harmony.xnet.provider.jsse.OpenSSLSocketFactoryImpl
org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl
org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$Finalizer
-org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$LoggerHolder
-org.apache.harmony.xnet.provider.jsse.ProtocolVersion
+org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLInputStream
+org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream
org.apache.harmony.xnet.provider.jsse.SSLContextImpl
org.apache.harmony.xnet.provider.jsse.SSLParameters
-org.apache.harmony.xnet.provider.jsse.ServerSessionContext
+org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl
+org.apache.harmony.xnet.provider.jsse.TrustManagerImpl
+org.apache.http.HttpHost
+org.apache.http.HttpRequestInterceptor
org.apache.http.HttpVersion
-org.apache.http.NoHttpResponseException
-org.apache.http.ProtocolException
+org.apache.http.auth.AuthSchemeRegistry
org.apache.http.client.HttpClient
-org.apache.http.client.methods.HttpEntityEnclosingRequestBase
-org.apache.http.client.methods.HttpGet
-org.apache.http.client.methods.HttpPost
+org.apache.http.client.RequestDirector
org.apache.http.client.methods.HttpRequestBase
-org.apache.http.conn.BasicManagedEntity
-org.apache.http.conn.params.ConnManagerParams
+org.apache.http.client.protocol.RequestAddCookies
+org.apache.http.client.protocol.RequestDefaultHeaders
+org.apache.http.client.protocol.RequestProxyAuthentication
+org.apache.http.client.protocol.RequestTargetAuthentication
+org.apache.http.client.protocol.ResponseProcessCookies
+org.apache.http.conn.params.ConnManagerParams$1
org.apache.http.conn.params.ConnRouteParams
org.apache.http.conn.routing.HttpRoute
+org.apache.http.conn.routing.RouteInfo$LayerType
+org.apache.http.conn.routing.RouteInfo$TunnelType
+org.apache.http.conn.routing.RouteTracker
org.apache.http.conn.scheme.PlainSocketFactory
-org.apache.http.conn.ssl.AbstractVerifier
+org.apache.http.conn.scheme.Scheme
+org.apache.http.conn.scheme.SchemeRegistry
+org.apache.http.conn.ssl.AllowAllHostnameVerifier
+org.apache.http.conn.ssl.BrowserCompatHostnameVerifier
org.apache.http.conn.ssl.SSLSocketFactory
+org.apache.http.conn.ssl.StrictHostnameVerifier
org.apache.http.conn.util.InetAddressUtils
-org.apache.http.entity.BasicHttpEntity
-org.apache.http.entity.InputStreamEntity
-org.apache.http.entity.StringEntity
-org.apache.http.impl.AbstractHttpClientConnection
+org.apache.http.cookie.CookieSpecRegistry
+org.apache.http.impl.DefaultConnectionReuseStrategy
+org.apache.http.impl.DefaultHttpResponseFactory
org.apache.http.impl.EnglishReasonPhraseCatalog
org.apache.http.impl.HttpConnectionMetricsImpl
org.apache.http.impl.SocketHttpClientConnection
+org.apache.http.impl.auth.BasicSchemeFactory
+org.apache.http.impl.auth.DigestSchemeFactory
org.apache.http.impl.client.AbstractAuthenticationHandler
org.apache.http.impl.client.AbstractHttpClient
+org.apache.http.impl.client.BasicCredentialsProvider
org.apache.http.impl.client.DefaultHttpClient
+org.apache.http.impl.client.DefaultHttpRequestRetryHandler
+org.apache.http.impl.client.DefaultProxyAuthenticationHandler
+org.apache.http.impl.client.DefaultRedirectHandler
+org.apache.http.impl.client.DefaultTargetAuthenticationHandler
+org.apache.http.impl.client.DefaultUserTokenHandler
org.apache.http.impl.client.EntityEnclosingRequestWrapper
org.apache.http.impl.conn.AbstractClientConnAdapter
-org.apache.http.impl.conn.AbstractPooledConnAdapter
org.apache.http.impl.conn.DefaultClientConnection
-org.apache.http.impl.conn.SingleClientConnManager
+org.apache.http.impl.conn.DefaultClientConnectionOperator
+org.apache.http.impl.conn.DefaultHttpRoutePlanner
+org.apache.http.impl.conn.DefaultResponseParser
+org.apache.http.impl.conn.IdleConnectionHandler
+org.apache.http.impl.conn.tsccm.BasicPoolEntry
+org.apache.http.impl.conn.tsccm.BasicPoolEntryRef
org.apache.http.impl.conn.tsccm.ConnPoolByRoute
+org.apache.http.impl.conn.tsccm.RefQueueWorker
+org.apache.http.impl.conn.tsccm.RouteSpecificPool
org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager
org.apache.http.impl.cookie.BasicClientCookie
-org.apache.http.impl.cookie.BrowserCompatSpec
+org.apache.http.impl.cookie.BestMatchSpecFactory
+org.apache.http.impl.cookie.BrowserCompatSpecFactory
org.apache.http.impl.cookie.DateUtils
-org.apache.http.impl.cookie.RFC2109Spec
-org.apache.http.impl.cookie.RFC2965Spec
-org.apache.http.impl.io.AbstractSessionInputBuffer
+org.apache.http.impl.cookie.NetscapeDraftSpecFactory
+org.apache.http.impl.cookie.RFC2109SpecFactory
+org.apache.http.impl.cookie.RFC2965SpecFactory
+org.apache.http.impl.entity.EntityDeserializer
+org.apache.http.impl.entity.EntitySerializer
+org.apache.http.impl.entity.LaxContentLengthStrategy
+org.apache.http.impl.entity.StrictContentLengthStrategy
+org.apache.http.impl.io.HttpRequestWriter
+org.apache.http.impl.io.HttpTransportMetricsImpl
org.apache.http.impl.io.SocketInputBuffer
-org.apache.http.message.AbstractHttpMessage
-org.apache.http.message.BasicHeaderElement
+org.apache.http.impl.io.SocketOutputBuffer
+org.apache.http.message.BasicHeaderValueParser
org.apache.http.message.BasicHttpEntityEnclosingRequest
-org.apache.http.message.BasicHttpRequest
org.apache.http.message.BasicHttpResponse
org.apache.http.message.BasicLineFormatter
org.apache.http.message.BasicLineParser
-org.apache.http.message.BasicTokenIterator
-org.apache.http.params.AbstractHttpParams
org.apache.http.params.BasicHttpParams
org.apache.http.protocol.BasicHttpProcessor
org.apache.http.protocol.HTTP
-org.bouncycastle.asn1.DERNull
+org.apache.http.protocol.HttpRequestExecutor
+org.apache.http.protocol.HttpRequestInterceptorList
+org.apache.http.protocol.HttpResponseInterceptorList
+org.apache.http.protocol.RequestConnControl
+org.apache.http.protocol.RequestContent
+org.apache.http.protocol.RequestExpectContinue
+org.apache.http.protocol.RequestTargetHost
+org.apache.http.protocol.RequestUserAgent
+org.apache.http.util.ByteArrayBuffer
+org.apache.http.util.CharArrayBuffer
+org.apache.http.util.EntityUtils
+org.apache.http.util.VersionInfo
+org.bouncycastle.asn1.DERBitString
+org.bouncycastle.asn1.DERIA5String
+org.bouncycastle.asn1.DERInteger
org.bouncycastle.asn1.DERObject
org.bouncycastle.asn1.DERObjectIdentifier
-org.bouncycastle.asn1.iana.IANAObjectIdentifiers
+org.bouncycastle.asn1.DEROctetString
+org.bouncycastle.asn1.DERPrintableString
+org.bouncycastle.asn1.DERSequence
+org.bouncycastle.asn1.DERSet
+org.bouncycastle.asn1.DERTaggedObject
+org.bouncycastle.asn1.DERUTCTime
+org.bouncycastle.asn1.DERUTF8String
+org.bouncycastle.asn1.OrderedTable
org.bouncycastle.asn1.nist.NISTObjectIdentifiers
-org.bouncycastle.asn1.oiw.OIWObjectIdentifiers
org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
+org.bouncycastle.asn1.x509.AlgorithmIdentifier
+org.bouncycastle.asn1.x509.RSAPublicKeyStructure
+org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
+org.bouncycastle.asn1.x509.TBSCertificateStructure
+org.bouncycastle.asn1.x509.Time
+org.bouncycastle.asn1.x509.X509CertificateStructure
+org.bouncycastle.asn1.x509.X509Extension
org.bouncycastle.asn1.x509.X509Extensions
org.bouncycastle.asn1.x509.X509Name
-org.bouncycastle.crypto.digests.SHA1Digest
+org.bouncycastle.asn1.x509.X509NameElementList
+org.bouncycastle.asn1.x9.X9ObjectIdentifiers
org.bouncycastle.crypto.engines.AESFastEngine
-org.bouncycastle.crypto.generators.PKCS12ParametersGenerator
-org.bouncycastle.crypto.macs.HMac
-org.bouncycastle.jce.provider.BouncyCastleProvider
org.bouncycastle.jce.provider.CertPathValidatorUtilities
-org.bouncycastle.jce.provider.JCEBlockCipher
org.bouncycastle.jce.provider.JCEBlockCipher$AES
-org.bouncycastle.jce.provider.JCEMac
-org.bouncycastle.jce.provider.JDKKeyFactory
+org.bouncycastle.jce.provider.JCERSAPublicKey
org.bouncycastle.jce.provider.JDKKeyFactory$RSA
org.bouncycastle.jce.provider.JDKKeyStore
-org.bouncycastle.jce.provider.JDKX509CertificateFactory
+org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry
org.bouncycastle.jce.provider.PKIXCertPathValidatorSpi
-org.bouncycastle.jce.provider.WrapCipherSpi
+org.bouncycastle.jce.provider.RSAUtil
org.bouncycastle.jce.provider.X509CertificateObject
-org.ccil.cowan.tagsoup.AttributesImpl
org.ccil.cowan.tagsoup.HTMLScanner
-org.ccil.cowan.tagsoup.HTMLSchema
org.ccil.cowan.tagsoup.Parser
+org.json.JSONArray
org.json.JSONObject
+org.json.JSONStringer
org.kxml2.io.KXmlParser
org.kxml2.io.KXmlSerializer
-org.openssl.NativeBN
-org.xml.sax.Attributes
org.xml.sax.helpers.DefaultHandler
-org.xmlpull.v1.XmlPullParser
-org.xmlpull.v1.XmlPullParserException
+org.xml.sax.helpers.NewInstance
+org.xmlpull.v1.XmlPullParserFactory
org.xmlpull.v1.sax2.Driver
sun.misc.Unsafe
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index 55007ba..f67a7ae 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -25,6 +25,7 @@ import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.accessibilityservice.IEventListener;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -615,6 +616,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mId = sIdCounter++;
mComponentName = componentName;
mIntent = new Intent().setComponent(mComponentName);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.accessibility_binding_label);
+ mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
}
/**
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e28515..0e60dd6 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -31,6 +31,7 @@ import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.PackageInfo;
+import android.content.pm.PermissionInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
@@ -46,6 +47,8 @@ import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
@@ -91,6 +94,20 @@ class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_RESTORE = 3;
private static final int MSG_RUN_CLEAR = 4;
+ // Event tags -- see system/core/logcat/event-log-tags
+ private static final int BACKUP_DATA_CHANGED_EVENT = 2820;
+ private static final int BACKUP_START_EVENT = 2821;
+ private static final int BACKUP_TRANSPORT_FAILURE_EVENT = 2822;
+ private static final int BACKUP_AGENT_FAILURE_EVENT = 2823;
+ private static final int BACKUP_PACKAGE_EVENT = 2824;
+ private static final int BACKUP_SUCCESS_EVENT = 2825;
+
+ private static final int RESTORE_START_EVENT = 2830;
+ private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831;
+ private static final int RESTORE_AGENT_FAILURE_EVENT = 2832;
+ private static final int RESTORE_PACKAGE_EVENT = 2833;
+ private static final int RESTORE_SUCCESS_EVENT = 2834;
+
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
@@ -100,18 +117,18 @@ class BackupManagerService extends IBackupManager.Stub {
private PowerManager mPowerManager;
private AlarmManager mAlarmManager;
- private boolean mEnabled; // access to this is synchronized on 'this'
- private boolean mProvisioned;
- private PowerManager.WakeLock mWakelock;
- private final BackupHandler mBackupHandler = new BackupHandler();
- private PendingIntent mRunBackupIntent;
- private BroadcastReceiver mRunBackupReceiver;
- private IntentFilter mRunBackupFilter;
+ boolean mEnabled; // access to this is synchronized on 'this'
+ boolean mProvisioned;
+ PowerManager.WakeLock mWakelock;
+ final BackupHandler mBackupHandler = new BackupHandler();
+ PendingIntent mRunBackupIntent;
+ BroadcastReceiver mRunBackupReceiver;
+ IntentFilter mRunBackupFilter;
// map UIDs to the set of backup client services within that UID's app set
- private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+ final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
= new SparseArray<HashSet<ApplicationInfo>>();
// set of backup services that have pending changes
- private class BackupRequest {
+ class BackupRequest {
public ApplicationInfo appInfo;
public boolean fullBackup;
@@ -125,35 +142,35 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
// Backups that we haven't started yet.
- private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
+ HashMap<ApplicationInfo,BackupRequest> mPendingBackups
= new HashMap<ApplicationInfo,BackupRequest>();
// Pseudoname that we use for the Package Manager metadata "package"
- private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
+ static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
// locking around the pending-backup management
- private final Object mQueueLock = new Object();
+ final Object mQueueLock = new Object();
// The thread performing the sequence of queued backups binds to each app's agent
// in succession. Bind notifications are asynchronously delivered through the
// Activity Manager; use this lock object to signal when a requested binding has
// completed.
- private final Object mAgentConnectLock = new Object();
- private IBackupAgent mConnectedAgent;
- private volatile boolean mConnecting;
+ final Object mAgentConnectLock = new Object();
+ IBackupAgent mConnectedAgent;
+ volatile boolean mConnecting;
// A similar synchronicity mechanism around clearing apps' data for restore
- private final Object mClearDataLock = new Object();
- private volatile boolean mClearingData;
+ final Object mClearDataLock = new Object();
+ volatile boolean mClearingData;
// Transport bookkeeping
- private final HashMap<String,IBackupTransport> mTransports
+ final HashMap<String,IBackupTransport> mTransports
= new HashMap<String,IBackupTransport>();
- private String mCurrentTransport;
- private IBackupTransport mLocalTransport, mGoogleTransport;
- private RestoreSession mActiveRestoreSession;
+ String mCurrentTransport;
+ IBackupTransport mLocalTransport, mGoogleTransport;
+ RestoreSession mActiveRestoreSession;
- private class RestoreParams {
+ class RestoreParams {
public IBackupTransport transport;
public IRestoreObserver observer;
public long token;
@@ -165,7 +182,7 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
- private class ClearParams {
+ class ClearParams {
public IBackupTransport transport;
public PackageInfo packageInfo;
@@ -176,11 +193,17 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Where we keep our journal files and other bookkeeping
- private File mBaseStateDir;
- private File mDataDir;
- private File mJournalDir;
- private File mJournal;
- private RandomAccessFile mJournalStream;
+ File mBaseStateDir;
+ File mDataDir;
+ File mJournalDir;
+ File mJournal;
+ RandomAccessFile mJournalStream;
+
+ // Keep a log of all the apps we've ever backed up
+ private File mEverStored;
+ private RandomAccessFile mEverStoredStream;
+ HashSet<String> mEverStoredApps = new HashSet<String>();
+
public BackupManagerService(Context context) {
mContext = context;
@@ -195,7 +218,7 @@ class BackupManagerService extends IBackupManager.Stub {
Settings.Secure.BACKUP_ENABLED, 0) != 0;
// !!! TODO: mProvisioned needs to default to 0, not 1.
mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
- Settings.Secure.BACKUP_PROVISIONED, 1) != 0;
+ Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
mBaseStateDir = new File(Environment.getDataDirectory(), "backup");
mDataDir = Environment.getDownloadCacheDirectory();
@@ -215,6 +238,9 @@ class BackupManagerService extends IBackupManager.Stub {
mJournalDir.mkdirs(); // creates mBaseStateDir along the way
makeJournalLocked(); // okay because no other threads are running yet
+ // Set up the various sorts of package tracking we do
+ initPackageTracking();
+
// Build our mapping of uid to backup client services. This implicitly
// schedules a backup pass on the Package Manager metadata the first
// time anything needs to be backed up.
@@ -249,14 +275,6 @@ class BackupManagerService extends IBackupManager.Stub {
// leftover journal files into the pending backup set
parseLeftoverJournals();
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
// Power management
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");
@@ -281,6 +299,73 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ private void initPackageTracking() {
+ if (DEBUG) Log.v(TAG, "Initializing package tracking");
+
+ // Keep a log of what apps we've ever backed up. Because we might have
+ // rebooted in the middle of an operation that was removing something from
+ // this log, we sanity-check its contents here and reconstruct it.
+ mEverStored = new File(mBaseStateDir, "processed");
+ File tempProcessedFile = new File(mBaseStateDir, "processed.new");
+ try {
+ // If there are previous contents, parse them out then start a new
+ // file to continue the recordkeeping.
+ if (mEverStored.exists()) {
+ RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rw");
+ mEverStoredStream = new RandomAccessFile(mEverStored, "r");
+
+ // parse its existing contents
+ mEverStoredStream.seek(0);
+ temp.seek(0);
+ try {
+ while (true) {
+ PackageInfo info;
+ String pkg = mEverStoredStream.readUTF();
+ try {
+ info = mPackageManager.getPackageInfo(pkg, 0);
+ mEverStoredApps.add(pkg);
+ temp.writeUTF(pkg);
+ if (DEBUG) Log.v(TAG, " + " + pkg);
+ } catch (NameNotFoundException e) {
+ // nope, this package was uninstalled; don't include it
+ if (DEBUG) Log.v(TAG, " - " + pkg);
+ }
+ }
+ } catch (EOFException e) {
+ // now we're at EOF
+ }
+
+ // Once we've rewritten the backup history log, atomically replace the
+ // old one with the new one then reopen the file for continuing use.
+ temp.close();
+ mEverStoredStream.close();
+ tempProcessedFile.renameTo(mEverStored);
+ }
+ // This will create the file if it doesn't exist
+ mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
+ mEverStoredStream.seek(mEverStoredStream.length());
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to open known-stored file!");
+ mEverStoredStream = null;
+ }
+
+ // If we were in the middle of removing something from the ever-backed-up
+ // file, there might be a transient "processed.new" file still present.
+ // We've reconstructed a coherent state at this point though, so we can
+ // safely discard that file now.
+ if (tempProcessedFile.exists()) {
+ tempProcessedFile.delete();
+ }
+
+ // Register for broadcasts about package install, etc., so we can
+ // update the provider list.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mBroadcastReceiver, filter);
+ }
+
private void makeJournalLocked() {
try {
mJournal = File.createTempFile("journal", null, mJournalDir);
@@ -472,7 +557,12 @@ class BackupManagerService extends IBackupManager.Stub {
Log.v(TAG, "Adding " + targetPkgs.size() + " backup participants:");
for (PackageInfo p : targetPkgs) {
Log.v(TAG, " " + p + " agent=" + p.applicationInfo.backupAgentName
- + " uid=" + p.applicationInfo.uid);
+ + " uid=" + p.applicationInfo.uid
+ + " killAfterRestore="
+ + (((p.applicationInfo.flags & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) ? "true" : "false")
+ + " restoreNeedsApplication="
+ + (((p.applicationInfo.flags & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0) ? "true" : "false")
+ );
}
}
@@ -485,6 +575,17 @@ class BackupManagerService extends IBackupManager.Stub {
mBackupParticipants.put(uid, set);
}
set.add(pkg.applicationInfo);
+
+ // If we've never seen this app before, schedule a backup for it
+ if (!mEverStoredApps.contains(pkg.packageName)) {
+ if (DEBUG) Log.i(TAG, "New app " + pkg.packageName
+ + " never backed up; scheduling");
+ try {
+ dataChanged(pkg.packageName);
+ } catch (RemoteException e) {
+ // can't happen; it's a local method call
+ }
+ }
}
}
}
@@ -528,6 +629,7 @@ class BackupManagerService extends IBackupManager.Stub {
for (ApplicationInfo entry: set) {
if (entry.packageName.equals(pkg.packageName)) {
set.remove(entry);
+ removeEverBackedUp(pkg.packageName);
break;
}
}
@@ -540,15 +642,18 @@ class BackupManagerService extends IBackupManager.Stub {
}
// Returns the set of all applications that define an android:backupAgent attribute
- private List<PackageInfo> allAgentPackages() {
+ List<PackageInfo> allAgentPackages() {
// !!! TODO: cache this and regenerate only when necessary
int flags = PackageManager.GET_SIGNATURES;
List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
int N = packages.size();
for (int a = N-1; a >= 0; a--) {
- ApplicationInfo app = packages.get(a).applicationInfo;
+ PackageInfo pkg = packages.get(a);
+ ApplicationInfo app = pkg.applicationInfo;
if (((app.flags&ApplicationInfo.FLAG_ALLOW_BACKUP) == 0)
- || app.backupAgentName == null) {
+ || app.backupAgentName == null
+ || (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ pkg.packageName) != PackageManager.PERMISSION_GRANTED)) {
packages.remove(a);
}
}
@@ -570,6 +675,65 @@ class BackupManagerService extends IBackupManager.Stub {
addPackageParticipantsLockedInner(packageName, allApps);
}
+ // Called from the backup thread: record that the given app has been successfully
+ // backed up at least once
+ void logBackupComplete(String packageName) {
+ if (mEverStoredStream != null && !packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
+ synchronized (mEverStoredApps) {
+ if (mEverStoredApps.add(packageName)) {
+ try {
+ mEverStoredStream.writeUTF(packageName);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to log backup of " + packageName + ", ceasing log");
+ try {
+ mEverStoredStream.close();
+ } catch (IOException ioe) {
+ // we're dropping it; no need to handle an exception on close here
+ }
+ mEverStoredStream = null;
+ }
+ }
+ }
+ }
+ }
+
+ // Remove our awareness of having ever backed up the given package
+ void removeEverBackedUp(String packageName) {
+ if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName
+ + ", new set:");
+
+ if (mEverStoredStream != null) {
+ synchronized (mEverStoredApps) {
+ // Rewrite the file and rename to overwrite. If we reboot in the middle,
+ // we'll recognize on initialization time that the package no longer
+ // exists and fix it up then.
+ File tempKnownFile = new File(mBaseStateDir, "processed.new");
+ try {
+ mEverStoredStream.close();
+ RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rw");
+ mEverStoredApps.remove(packageName);
+ for (String s : mEverStoredApps) {
+ known.writeUTF(s);
+ if (DEBUG) Log.v(TAG, " " + s);
+ }
+ known.close();
+ tempKnownFile.renameTo(mEverStored);
+ mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
+ } catch (IOException e) {
+ // Bad: we couldn't create the new copy. For safety's sake we
+ // abandon the whole process and remove all what's-backed-up
+ // state entirely, meaning we'll force a backup pass for every
+ // participant on the next boot or [re]install.
+ Log.w(TAG, "Error rewriting backed-up set; halting log");
+ mEverStoredStream = null;
+ mEverStoredApps.clear();
+ tempKnownFile.delete();
+ mEverStored.delete();
+ }
+ }
+ }
+ }
+
// Return the given transport
private IBackupTransport getTransport(String transportName) {
synchronized (mTransports) {
@@ -637,6 +801,14 @@ class BackupManagerService extends IBackupManager.Stub {
synchronized(mClearDataLock) {
mClearingData = true;
+ /* This is causing some critical processes to be killed during setup.
+ Temporarily revert this change until we find a better solution.
+ try {
+ mActivityManager.clearApplicationUserData(packageName, observer);
+ } catch (RemoteException e) {
+ // can't happen because the activity manager is in this process
+ }
+ */
mPackageManager.clearApplicationUserData(packageName, observer);
// only wait 10 seconds for the clear data to happen
@@ -654,7 +826,7 @@ class BackupManagerService extends IBackupManager.Stub {
class ClearDataObserver extends IPackageDataObserver.Stub {
public void onRemoveCompleted(String packageName, boolean succeeded)
- throws android.os.RemoteException {
+ throws RemoteException {
synchronized(mClearDataLock) {
mClearingData = false;
mClearDataLock.notifyAll();
@@ -687,49 +859,64 @@ class BackupManagerService extends IBackupManager.Stub {
@Override
public void run() {
+ long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets");
// Backups run at background priority
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- // The package manager doesn't have a proper <application> etc, but since
- // it's running here in the system process we can just set up its agent
- // directly and use a synthetic BackupRequest. We always run this pass
- // because it's cheap and this way we guarantee that we don't get out of
- // step even if we're selecting among various transports at run time.
- PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
- mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
- pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
- processOneBackup(pmRequest,
- IBackupAgent.Stub.asInterface(pmAgent.onBind()),
- mTransport);
-
- // Now run all the backups in our queue
- doQueuedBackups(mTransport);
-
- // Finally, tear down the transport
try {
- if (!mTransport.finishBackup()) {
- // STOPSHIP TODO: handle errors
- Log.e(TAG, "Backup failure in finishBackup()");
+ EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
+
+ // The package manager doesn't have a proper <application> etc, but since
+ // it's running here in the system process we can just set up its agent
+ // directly and use a synthetic BackupRequest. We always run this pass
+ // because it's cheap and this way we guarantee that we don't get out of
+ // step even if we're selecting among various transports at run time.
+ PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
+ mPackageManager, allAgentPackages());
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
+ processOneBackup(pmRequest,
+ IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
+
+ // Now run all the backups in our queue
+ int count = mQueue.size();
+ doQueuedBackups(mTransport);
+
+ // Finally, tear down the transport
+ if (mTransport.finishBackup()) {
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(BACKUP_SUCCESS_EVENT, count, millis);
+ } else {
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, "");
+ Log.e(TAG, "Transport error in finishBackup()");
}
- } catch (RemoteException e) {
- Log.e(TAG, "Error in finishBackup()", e);
- }
- if (!mJournal.delete()) {
- Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath());
+ if (!mJournal.delete()) {
+ Log.e(TAG, "Unable to remove backup journal file " + mJournal);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error in backup thread", e);
+ } finally {
+ // Only once we're entirely finished do we release the wakelock
+ mWakelock.release();
}
-
- // Only once we're entirely finished do we release the wakelock
- mWakelock.release();
}
private void doQueuedBackups(IBackupTransport transport) {
for (BackupRequest request : mQueue) {
Log.d(TAG, "starting agent for backup of " + request);
+ // Don't run backup, even if requested, if the target app does not have
+ // the requisite permission
+ if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ request.appInfo.packageName) != PackageManager.PERMISSION_GRANTED) {
+ Log.w(TAG, "Skipping backup of unprivileged package "
+ + request.appInfo.packageName);
+ continue;
+ }
+
IBackupAgent agent = null;
int mode = (request.fullBackup)
? IApplicationThread.BACKUP_MODE_FULL
@@ -749,19 +936,27 @@ class BackupManagerService extends IBackupManager.Stub {
Log.v(TAG, "bind/backup threw");
e.printStackTrace();
}
-
}
}
void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) {
final String packageName = request.appInfo.packageName;
- Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+ if (DEBUG) Log.d(TAG, "processOneBackup doBackup() on " + packageName);
+
+ // !!! TODO: get the state file dir from the transport
+ File savedStateName = new File(mStateDir, packageName);
+ File backupDataName = new File(mDataDir, packageName + ".data");
+ File newStateName = new File(mStateDir, packageName + ".new");
+
+ ParcelFileDescriptor savedState = null;
+ ParcelFileDescriptor backupData = null;
+ ParcelFileDescriptor newState = null;
+ PackageInfo packInfo;
try {
// Look up the package info & signatures. This is first so that if it
// throws an exception, there's no file setup yet that would need to
// be unraveled.
- PackageInfo packInfo;
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
// The metadata 'package' is synthetic
packInfo = new PackageInfo();
@@ -771,68 +966,65 @@ class BackupManagerService extends IBackupManager.Stub {
PackageManager.GET_SIGNATURES);
}
- // !!! TODO: get the state file dir from the transport
- File savedStateName = new File(mStateDir, packageName);
- File backupDataName = new File(mDataDir, packageName + ".data");
- File newStateName = new File(mStateDir, packageName + ".new");
-
// In a full backup, we pass a null ParcelFileDescriptor as
// the saved-state "file"
- ParcelFileDescriptor savedState = (request.fullBackup) ? null
- : ParcelFileDescriptor.open(savedStateName,
+ if (!request.fullBackup) {
+ savedState = ParcelFileDescriptor.open(savedStateName,
ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE);
+ ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
+ }
- backupDataName.delete();
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
- newStateName.delete();
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ newState = ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
// Run the target's backup pass
- boolean success = false;
- try {
- agent.doBackup(savedState, backupData, newState);
- success = true;
- } finally {
- if (savedState != null) {
- savedState.close();
- }
- backupData.close();
- newState.close();
- }
+ agent.doBackup(savedState, backupData, newState);
+ logBackupComplete(packageName);
+ if (DEBUG) Log.v(TAG, "doBackup() success");
+ } catch (Exception e) {
+ Log.e(TAG, "Error backing up " + packageName, e);
+ EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString());
+ backupDataName.delete();
+ newStateName.delete();
+ return;
+ } finally {
+ try { if (savedState != null) savedState.close(); } catch (IOException e) {}
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+ try { if (newState != null) newState.close(); } catch (IOException e) {}
+ savedState = backupData = newState = null;
+ }
- // Now propagate the newly-backed-up data to the transport
- if (success) {
- if (DEBUG) Log.v(TAG, "doBackup() success");
- if (backupDataName.length() > 0) {
- backupData =
- ParcelFileDescriptor.open(backupDataName,
- ParcelFileDescriptor.MODE_READ_ONLY);
- if (!transport.performBackup(packInfo, backupData)) {
- // STOPSHIP TODO: handle errors
- Log.e(TAG, "Backup failure in performBackup()");
- }
- } else {
- if (DEBUG) {
- Log.i(TAG, "no backup data written; not calling transport");
- }
- }
+ // Now propagate the newly-backed-up data to the transport
+ try {
+ int size = (int) backupDataName.length();
+ if (size > 0) {
+ backupData = ParcelFileDescriptor.open(backupDataName,
+ ParcelFileDescriptor.MODE_READ_ONLY);
- // After successful transport, delete the now-stale data
- // and juggle the files so that next time we supply the agent
- // with the new state file it just created.
- backupDataName.delete();
- newStateName.renameTo(savedStateName);
+ if (!transport.performBackup(packInfo, backupData)) throw new Exception();
+ } else {
+ if (DEBUG) Log.i(TAG, "no backup data written; not calling transport");
}
+
+ // After successful transport, delete the now-stale data
+ // and juggle the files so that next time we supply the agent
+ // with the new state file it just created.
+ backupDataName.delete();
+ newStateName.renameTo(savedStateName);
+ EventLog.writeEvent(BACKUP_PACKAGE_EVENT, packageName, size);
} catch (Exception e) {
- Log.e(TAG, "Error backing up " + packageName, e);
+ Log.e(TAG, "Transport error backing up " + packageName, e);
+ EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
+ return;
+ } finally {
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
}
}
}
@@ -878,7 +1070,6 @@ class BackupManagerService extends IBackupManager.Stub {
private IBackupTransport mTransport;
private IRestoreObserver mObserver;
private long mToken;
- private RestoreSet mImage;
private File mStateDir;
class RestoreRequest {
@@ -908,14 +1099,15 @@ class BackupManagerService extends IBackupManager.Stub {
@Override
public void run() {
+ long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
- + " mObserver=" + mObserver + " mToken=" + mToken);
+ + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
/**
* Restore sequence:
*
* 1. get the restore set description for our identity
* 2. for each app in the restore set:
- * 3.a. if it's restorable on this device, add it to the restore queue
+ * 2.a. if it's restorable on this device, add it to the restore queue
* 3. for each app in the restore queue:
* 3.a. clear the app data
* 3.b. get the restore data for the app from the transport
@@ -923,25 +1115,18 @@ class BackupManagerService extends IBackupManager.Stub {
* 3.d. agent.doRestore() with the data from the server
* 3.e. unbind the agent [and kill the app?]
* 4. shut down the transport
+ *
+ * On errors, we try our best to recover and move on to the next
+ * application, but if necessary we abort the whole operation --
+ * the user is waiting, after al.
*/
int error = -1; // assume error
// build the set of apps to restore
try {
- RestoreSet[] images = mTransport.getAvailableRestoreSets();
- if (images == null) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Error getting restore sets");
- return;
- }
-
- if (images.length == 0) {
- Log.i(TAG, "No restore sets available");
- return;
- }
-
- mImage = images[0];
+ // TODO: Log this before getAvailableRestoreSets, somehow
+ EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName());
// Get the list of all packages which have backup enabled.
// (Include the Package Manager metadata pseudo-package first.)
@@ -966,22 +1151,26 @@ class BackupManagerService extends IBackupManager.Stub {
}
if (!mTransport.startRestore(mToken, restorePackages.toArray(new PackageInfo[0]))) {
- // STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error starting restore operation");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
}
String packageName = mTransport.nextRestorePackage();
if (packageName == null) {
- // STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error getting first restore package");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
} else if (packageName.equals("")) {
Log.i(TAG, "No restore data available");
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(RESTORE_SUCCESS_EVENT, 0, millis);
return;
} else if (!packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
Log.e(TAG, "Expected restore data for \"" + PACKAGE_MANAGER_SENTINEL
+ "\", found only \"" + packageName + "\"");
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+ "Package manager data missing");
return;
}
@@ -994,23 +1183,25 @@ class BackupManagerService extends IBackupManager.Stub {
// signature/version verification etc, so we simply do not proceed with
// the restore operation.
if (!pmAgent.hasMetadata()) {
- Log.i(TAG, "No restore metadata available, so not restoring settings");
+ Log.e(TAG, "No restore metadata available, so not restoring settings");
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, PACKAGE_MANAGER_SENTINEL,
+ "Package manager restore metadata missing");
return;
}
int count = 0;
for (;;) {
packageName = mTransport.nextRestorePackage();
+
if (packageName == null) {
- // STOPSHIP TODO: Handle the failure somehow?
Log.e(TAG, "Error getting next restore package");
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
return;
} else if (packageName.equals("")) {
break;
}
if (mObserver != null) {
- ++count;
try {
mObserver.onUpdate(count);
} catch (RemoteException e) {
@@ -1022,21 +1213,34 @@ class BackupManagerService extends IBackupManager.Stub {
Metadata metaInfo = pmAgent.getRestoredMetadata(packageName);
if (metaInfo == null) {
Log.e(TAG, "Missing metadata for " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Package metadata missing");
+ continue;
+ }
+
+ PackageInfo packageInfo;
+ try {
+ int flags = PackageManager.GET_SIGNATURES;
+ packageInfo = mPackageManager.getPackageInfo(packageName, flags);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Invalid package restoring data", e);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Package missing on device");
continue;
}
- int flags = PackageManager.GET_SIGNATURES;
- PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, flags);
if (metaInfo.versionCode > packageInfo.versionCode) {
- Log.w(TAG, "Package " + packageName
- + " restore version [" + metaInfo.versionCode
- + "] is too new for installed version ["
- + packageInfo.versionCode + "]");
+ String message = "Version " + metaInfo.versionCode
+ + " > installed version " + packageInfo.versionCode;
+ Log.w(TAG, "Package " + packageName + ": " + message);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, message);
continue;
}
if (!signaturesMatch(metaInfo.signatures, packageInfo.signatures)) {
Log.w(TAG, "Signature mismatch restoring " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Signature mismatch");
continue;
}
@@ -1045,41 +1249,66 @@ class BackupManagerService extends IBackupManager.Stub {
+ "] is compatible with installed version ["
+ packageInfo.versionCode + "]");
- // Now perform the actual restore
+ // Now perform the actual restore: first clear the app's data
+ // if appropriate
clearApplicationDataSynchronous(packageName);
+
+ // Then set up and bind the agent (with a restricted Application object
+ // unless the application says otherwise)
+ boolean useRealApp = (packageInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION) != 0;
+ if (DEBUG && useRealApp) {
+ Log.v(TAG, "agent requires real Application subclass for restore");
+ }
IBackupAgent agent = bindToAgentSynchronous(
packageInfo.applicationInfo,
- IApplicationThread.BACKUP_MODE_RESTORE);
+ (useRealApp ? IApplicationThread.BACKUP_MODE_INCREMENTAL
+ : IApplicationThread.BACKUP_MODE_RESTORE));
if (agent == null) {
Log.w(TAG, "Can't find backup agent for " + packageName);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName,
+ "Restore agent missing");
continue;
}
+ // And then finally run the restore on this agent
try {
processOneRestore(packageInfo, metaInfo.versionCode, agent);
+ ++count;
} finally {
- // unbind even on timeout or failure, just in case
+ // unbind and tidy up even on timeout or failure, just in case
mActivityManager.unbindBackupAgent(packageInfo.applicationInfo);
+
+ // The agent was probably running with a stub Application object,
+ // which isn't a valid run mode for the main app logic. Shut
+ // down the app so that next time it's launched, it gets the
+ // usual full initialization.
+ if ((packageInfo.applicationInfo.flags
+ & ApplicationInfo.FLAG_KILL_AFTER_RESTORE) != 0) {
+ if (DEBUG) Log.d(TAG, "Restore complete, killing host process of "
+ + packageInfo.applicationInfo.processName);
+ mActivityManager.killApplicationProcess(
+ packageInfo.applicationInfo.processName,
+ packageInfo.applicationInfo.uid);
+ }
}
}
// if we get this far, report success to the observer
error = 0;
- } catch (NameNotFoundException e) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Invalid paackage restoring data", e);
- } catch (RemoteException e) {
- // STOPSHIP TODO: Handle the failure somehow?
- Log.e(TAG, "Error restoring data", e);
+ int millis = (int) (SystemClock.elapsedRealtime() - startRealtime);
+ EventLog.writeEvent(RESTORE_SUCCESS_EVENT, count, millis);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in restore thread", e);
} finally {
+ if (DEBUG) Log.d(TAG, "finishing restore mObserver=" + mObserver);
+
try {
mTransport.finishRestore();
} catch (RemoteException e) {
Log.e(TAG, "Error finishing restore", e);
}
- Log.d(TAG, "finishing restore mObserver=" + mObserver);
-
if (mObserver != null) {
try {
mObserver.restoreFinished(error);
@@ -1098,51 +1327,64 @@ class BackupManagerService extends IBackupManager.Stub {
// !!! TODO: actually run the restore through mTransport
final String packageName = app.packageName;
- Log.d(TAG, "processOneRestore packageName=" + packageName);
+ if (DEBUG) Log.d(TAG, "processOneRestore packageName=" + packageName);
+
+ // Don't restore to unprivileged packages
+ if (mPackageManager.checkPermission(android.Manifest.permission.BACKUP_DATA,
+ packageName) != PackageManager.PERMISSION_GRANTED) {
+ Log.d(TAG, "Skipping restore of unprivileged package " + packageName);
+ }
// !!! TODO: get the dirs from the transport
File backupDataName = new File(mDataDir, packageName + ".restore");
- backupDataName.delete();
+ File newStateName = new File(mStateDir, packageName + ".new");
+ File savedStateName = new File(mStateDir, packageName);
+
+ ParcelFileDescriptor backupData = null;
+ ParcelFileDescriptor newState = null;
+
try {
- ParcelFileDescriptor backupData =
- ParcelFileDescriptor.open(backupDataName,
+ // Run the transport's restore pass
+ backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
- // Run the transport's restore pass
- // Run the target's backup pass
- try {
- if (!mTransport.getRestoreData(backupData)) {
- // STOPSHIP TODO: Handle this error somehow?
- Log.e(TAG, "Error getting restore data for " + packageName);
- return;
- }
- } finally {
- backupData.close();
+ if (!mTransport.getRestoreData(backupData)) {
+ Log.e(TAG, "Error getting restore data for " + packageName);
+ EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
+ return;
}
// Okay, we have the data. Now have the agent do the restore.
- File newStateName = new File(mStateDir, packageName + ".new");
- ParcelFileDescriptor newState =
- ParcelFileDescriptor.open(newStateName,
- ParcelFileDescriptor.MODE_READ_WRITE |
- ParcelFileDescriptor.MODE_CREATE);
-
+ backupData.close();
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_ONLY);
- try {
- agent.doRestore(backupData, appVersionCode, newState);
- } finally {
- newState.close();
- backupData.close();
- }
+ newState = ParcelFileDescriptor.open(newStateName,
+ ParcelFileDescriptor.MODE_READ_WRITE |
+ ParcelFileDescriptor.MODE_CREATE |
+ ParcelFileDescriptor.MODE_TRUNCATE);
+
+ agent.doRestore(backupData, appVersionCode, newState);
// if everything went okay, remember the recorded state now
- File savedStateName = new File(mStateDir, packageName);
newStateName.renameTo(savedStateName);
+ int size = (int) backupDataName.length();
+ EventLog.writeEvent(RESTORE_PACKAGE_EVENT, packageName, size);
} catch (Exception e) {
Log.e(TAG, "Error restoring data for " + packageName, e);
+ EventLog.writeEvent(RESTORE_AGENT_FAILURE_EVENT, packageName, e.toString());
+
+ // If the agent fails restore, it might have put the app's data
+ // into an incoherent state. For consistency we wipe its data
+ // again in this case before propagating the exception
+ clearApplicationDataSynchronous(packageName);
+ } finally {
+ backupDataName.delete();
+ try { if (backupData != null) backupData.close(); } catch (IOException e) {}
+ try { if (newState != null) newState.close(); } catch (IOException e) {}
+ backupData = newState = null;
}
}
}
@@ -1188,6 +1430,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Record that we need a backup pass for the caller. Since multiple callers
// may share a uid, we need to note all candidates within that uid and schedule
// a backup pass for each of them.
+ EventLog.writeEvent(BACKUP_DATA_CHANGED_EVENT, packageName);
// If the caller does not hold the BACKUP permission, it can only request a
// backup of its own data.
@@ -1235,7 +1478,8 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
} else {
- Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'");
+ Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
+ + " uid=" + Binder.getCallingUid());
}
}
@@ -1288,10 +1532,12 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Log.v(TAG, "Found the app - running clear process");
// found it; fire off the clear request
synchronized (mQueueLock) {
+ long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_CLEAR,
new ClearParams(getTransport(mCurrentTransport), info));
mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
}
break;
}
@@ -1301,7 +1547,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Run a backup pass immediately for any applications that have declared
// that they have pending updates.
public void backupNow() throws RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "backupNow");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
@@ -1370,13 +1616,13 @@ class BackupManagerService extends IBackupManager.Stub {
// Report whether the backup mechanism is currently enabled
public boolean isBackupEnabled() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "isBackupEnabled");
return mEnabled; // no need to synchronize just to read it
}
// Report the name of the currently active transport
public String getCurrentTransport() {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
return mCurrentTransport;
@@ -1405,7 +1651,7 @@ class BackupManagerService extends IBackupManager.Stub {
// name is not one of the available transports, no action is taken and the method
// returns null.
public String selectBackupTransport(String transport) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "selectBackupTransport");
synchronized (mTransports) {
String prevTransport = null;
@@ -1459,7 +1705,7 @@ class BackupManagerService extends IBackupManager.Stub {
// Hand off a restore session
public IRestoreSession beginRestoreSession(String transport) {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
synchronized(this) {
if (mActiveRestoreSession != null) {
@@ -1484,55 +1730,71 @@ class BackupManagerService extends IBackupManager.Stub {
}
// --- Binder interface ---
- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ public synchronized RestoreSet[] getAvailableRestoreSets() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getAvailableRestoreSets");
try {
- synchronized(this) {
- if (mRestoreSets == null) {
+ if (mRestoreTransport == null) {
+ Log.w(TAG, "Null transport getting restore sets");
+ return null;
+ }
+ if (mRestoreSets == null) { // valid transport; do the one-time fetch
mRestoreSets = mRestoreTransport.getAvailableRestoreSets();
+ if (mRestoreSets == null) EventLog.writeEvent(RESTORE_TRANSPORT_FAILURE_EVENT);
}
return mRestoreSets;
- }
- } catch (RuntimeException e) {
- Log.d(TAG, "getAvailableRestoreSets exception");
- e.printStackTrace();
- throw e;
+ } catch (Exception e) {
+ Log.e(TAG, "Error in getAvailableRestoreSets", e);
+ return null;
}
}
- public int performRestore(long token, IRestoreObserver observer)
- throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "performRestore");
+ public synchronized int performRestore(long token, IRestoreObserver observer) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "performRestore");
- Log.d(TAG, "performRestore token=" + token + " observer=" + observer);
+ if (DEBUG) Log.d(TAG, "performRestore token=" + Long.toHexString(token)
+ + " observer=" + observer);
- if (mRestoreSets != null) {
- for (int i = 0; i < mRestoreSets.length; i++) {
- if (token == mRestoreSets[i].token) {
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, observer, token);
- mBackupHandler.sendMessage(msg);
- return 0;
- }
+ if (mRestoreTransport == null || mRestoreSets == null) {
+ Log.e(TAG, "Ignoring performRestore() with no restore set");
+ return -1;
+ }
+
+ for (int i = 0; i < mRestoreSets.length; i++) {
+ if (token == mRestoreSets[i].token) {
+ long oldId = Binder.clearCallingIdentity();
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token);
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ return 0;
}
- } else {
- if (DEBUG) Log.v(TAG, "No current restore set, not doing restore");
}
+
+ Log.w(TAG, "Restore token " + Long.toHexString(token) + " not found");
return -1;
}
- public void endRestoreSession() throws android.os.RemoteException {
- mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+ public synchronized void endRestoreSession() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"endRestoreSession");
- Log.d(TAG, "endRestoreSession");
+ if (DEBUG) Log.d(TAG, "endRestoreSession");
+
+ synchronized (this) {
+ try {
+ if (mRestoreTransport != null) mRestoreTransport.finishRestore();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in finishRestore", e);
+ } finally {
+ mRestoreTransport = null;
+ }
+ }
- mRestoreTransport.finishRestore();
- mRestoreTransport = null;
- synchronized(BackupManagerService.this) {
+ synchronized (BackupManagerService.this) {
if (BackupManagerService.this.mActiveRestoreSession == this) {
BackupManagerService.this.mActiveRestoreSession = null;
} else {
@@ -1546,8 +1808,6 @@ class BackupManagerService extends IBackupManager.Stub {
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mQueueLock) {
- long oldId = Binder.clearCallingIdentity();
-
pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")
+ " / " + (!mProvisioned ? "not " : "") + "provisioned");
pw.println("Available transports:");
@@ -1566,12 +1826,14 @@ class BackupManagerService extends IBackupManager.Stub {
pw.println(" " + app.toString());
}
}
+ pw.println("Ever backed up: " + mEverStoredApps.size());
+ for (String pkg : mEverStoredApps) {
+ pw.println(" " + pkg);
+ }
pw.println("Pending: " + mPendingBackups.size());
for (BackupRequest req : mPendingBackups.values()) {
pw.println(" " + req);
}
-
- Binder.restoreCallingIdentity(oldId);
}
}
}
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 90d8c9d..26fee89 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -45,7 +45,6 @@ import java.io.IOException;
import java.io.PrintWriter;
-
/**
* <p>BatteryService monitors the charging status, and charge level of the device
* battery. When these values change this service broadcasts the new values
@@ -92,6 +91,9 @@ class BatteryService extends Binder {
// This should probably be exposed in the API, though it's not critical
private static final int BATTERY_PLUGGED_NONE = 0;
+ private static final int BATTERY_LEVEL_CLOSE_WARNING = 20;
+ private static final int BATTERY_LEVEL_WARNING = 15;
+
private final Context mContext;
private final IBatteryStats mBatteryStats;
@@ -120,6 +122,7 @@ class BatteryService extends Binder {
private long mDischargeStartTime;
private int mDischargeStartLevel;
+ private boolean mSentLowBatteryBroadcast = false;
public BatteryService(Context context) {
mContext = context;
@@ -171,6 +174,22 @@ class BatteryService extends Binder {
return mBatteryLevel;
}
+ void systemReady() {
+ // check our power situation now that it is safe to display the shutdown dialog.
+ shutdownIfNoPower();
+ }
+
+ private final void shutdownIfNoPower() {
+ // shut down gracefully if our battery is critically low and we are not powered.
+ // wait until the system has booted before attempting to display the shutdown dialog.
+ if (mBatteryLevel == 0 && !isPowered() && ActivityManagerNative.isSystemReady()) {
+ Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN);
+ intent.putExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+ }
+ }
+
private native void native_update();
private synchronized final void update() {
@@ -178,7 +197,9 @@ class BatteryService extends Binder {
boolean logOutlier = false;
long dischargeDuration = 0;
-
+
+ shutdownIfNoPower();
+
mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
if (mAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -250,16 +271,28 @@ class BatteryService extends Binder {
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
+ Intent statusIntent = new Intent();
+ statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
if (mPlugType != 0 && mLastPlugType == 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_CONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_CONNECTED);
+ mContext.sendBroadcast(statusIntent);
}
else if (mPlugType == 0 && mLastPlugType != 0) {
- Intent intent = new Intent(Intent.ACTION_POWER_DISCONNECTED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(intent);
+ statusIntent.setAction(Intent.ACTION_POWER_DISCONNECTED);
+ mContext.sendBroadcast(statusIntent);
}
+
+ final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
+ final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
+
+ /* The ACTION_BATTERY_LOW broadcast is sent in these situations:
+ * - is just un-plugged (previously was plugged) and battery level is under WARNING, or
+ * - is not plugged and battery level crosses the WARNING boundary (becomes < 15).
+ */
+ final boolean sendBatteryLow = !plugged
+ && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+ && mBatteryLevel < BATTERY_LEVEL_WARNING
+ && (oldPlugged || mLastBatteryLevel >= BATTERY_LEVEL_WARNING);
mLastBatteryStatus = mBatteryStatus;
mLastBatteryHealth = mBatteryHealth;
@@ -269,8 +302,17 @@ class BatteryService extends Binder {
mLastBatteryVoltage = mBatteryVoltage;
mLastBatteryTemperature = mBatteryTemperature;
mLastBatteryLevelCritical = mBatteryLevelCritical;
-
+
sendIntent();
+ if (sendBatteryLow) {
+ mSentLowBatteryBroadcast = true;
+ statusIntent.setAction(Intent.ACTION_BATTERY_LOW);
+ mContext.sendBroadcast(statusIntent);
+ } else if (mSentLowBatteryBroadcast && mLastBatteryLevel >= BATTERY_LEVEL_CLOSE_WARNING) {
+ mSentLowBatteryBroadcast = false;
+ statusIntent.setAction(Intent.ACTION_BATTERY_OKAY);
+ mContext.sendBroadcast(statusIntent);
+ }
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 493bd09..e26dd13 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -30,51 +30,136 @@ import android.net.NetworkStateTracker;
import android.net.wifi.WifiStateTracker;
import android.os.Binder;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.provider.Settings;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import com.android.internal.telephony.Phone;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
/**
* @hide
*/
public class ConnectivityService extends IConnectivityManager.Stub {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG = "ConnectivityService";
// Event log tags (must be in sync with event-log-tags)
private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020;
+ // how long to wait before switching back to a radio's default network
+ private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
+ // system property that can override the above value
+ private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
+ "android.telephony.apn-restore";
+
/**
* Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them
* abstractly.
*/
private NetworkStateTracker mNetTrackers[];
- private WifiStateTracker mWifiStateTracker;
- private MobileDataStateTracker mMobileDataStateTracker;
+
+ /**
+ * A per Net list of the PID's that requested access to the net
+ * used both as a refcount and for per-PID DNS selection
+ */
+ private List mNetRequestersPids[];
+
private WifiWatchdogService mWifiWatchdogService;
+ // priority order of the nettrackers
+ // (excluding dynamically set mNetworkPreference)
+ // TODO - move mNetworkTypePreference into this
+ private int[] mPriorityList;
+
private Context mContext;
private int mNetworkPreference;
- private NetworkStateTracker mActiveNetwork;
+ private int mActiveDefaultNetwork = -1;
private int mNumDnsEntries;
- private static int sDnsChangeCounter;
private boolean mTestMode;
private static ConnectivityService sServiceInstance;
+ private Handler mHandler;
+
+ // list of DeathRecipients used to make sure features are turned off when
+ // a process dies
+ private List mFeatureUsers;
+
+ private boolean mSystemReady;
+ private ArrayList<Intent> mDeferredBroadcasts;
+
+ private class NetworkAttributes {
+ /**
+ * Class for holding settings read from resources.
+ */
+ public String mName;
+ public int mType;
+ public int mRadio;
+ public int mPriority;
+ public NetworkAttributes(String init) {
+ String fragments[] = init.split(",");
+ mName = fragments[0].toLowerCase();
+ if (fragments[1].toLowerCase().equals("wifi")) {
+ mRadio = ConnectivityManager.TYPE_WIFI;
+ } else {
+ mRadio = ConnectivityManager.TYPE_MOBILE;
+ }
+ if (mName.equals("default")) {
+ mType = mRadio;
+ } else if (mName.equals("mms")) {
+ mType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (mName.equals("supl")) {
+ mType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (mName.equals("dun")) {
+ mType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (mName.equals("hipri")) {
+ mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
+ mPriority = Integer.parseInt(fragments[2]);
+ }
+ public boolean isDefault() {
+ return (mType == mRadio);
+ }
+ }
+ NetworkAttributes[] mNetAttributes;
+
+ private class RadioAttributes {
+ public String mName;
+ public int mPriority;
+ public int mSimultaneity;
+ public int mType;
+ public RadioAttributes(String init) {
+ String fragments[] = init.split(",");
+ mName = fragments[0].toLowerCase();
+ mPriority = Integer.parseInt(fragments[1]);
+ mSimultaneity = Integer.parseInt(fragments[2]);
+ if (mName.equals("wifi")) {
+ mType = ConnectivityManager.TYPE_WIFI;
+ } else {
+ mType = ConnectivityManager.TYPE_MOBILE;
+ }
+ }
+ }
+ RadioAttributes[] mRadioAttributes;
+
private static class ConnectivityThread extends Thread {
private Context mContext;
-
+
private ConnectivityThread(Context context) {
super("ConnectivityThread");
mContext = context;
@@ -89,11 +174,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
Looper.loop();
}
-
+
public static ConnectivityService getServiceInstance(Context context) {
ConnectivityThread thread = new ConnectivityThread(context);
thread.start();
-
+
synchronized (thread) {
while (sServiceInstance == null) {
try {
@@ -101,27 +186,71 @@ public class ConnectivityService extends IConnectivityManager.Stub {
thread.wait();
} catch (InterruptedException ignore) {
Log.e(TAG,
- "Unexpected InterruptedException while waiting for ConnectivityService thread");
+ "Unexpected InterruptedException while waiting"+
+ " for ConnectivityService thread");
}
}
}
-
+
return sServiceInstance;
}
}
-
+
public static ConnectivityService getInstance(Context context) {
return ConnectivityThread.getServiceInstance(context);
}
-
+
private ConnectivityService(Context context) {
if (DBG) Log.v(TAG, "ConnectivityService starting up");
mContext = context;
- mNetTrackers = new NetworkStateTracker[2];
- Handler handler = new MyHandler();
-
+ mNetTrackers = new NetworkStateTracker[
+ ConnectivityManager.MAX_NETWORK_TYPE+1];
+ mHandler = new MyHandler();
+
mNetworkPreference = getPersistedNetworkPreference();
-
+
+ // Load device network attributes from resources
+ mNetAttributes = new NetworkAttributes[
+ ConnectivityManager.MAX_NETWORK_TYPE+1];
+ mRadioAttributes = new RadioAttributes[
+ ConnectivityManager.MAX_RADIO_TYPE+1];
+ String[] naStrings = context.getResources().getStringArray(
+ com.android.internal.R.array.networkAttributes);
+ // TODO - what if the setting has gaps/unknown types?
+ for (String a : naStrings) {
+ NetworkAttributes n = new NetworkAttributes(a);
+ mNetAttributes[n.mType] = n;
+ }
+ String[] raStrings = context.getResources().getStringArray(
+ com.android.internal.R.array.radioAttributes);
+ for (String a : raStrings) {
+ RadioAttributes r = new RadioAttributes(a);
+ mRadioAttributes[r.mType] = r;
+ }
+
+ // high priority first
+ mPriorityList = new int[naStrings.length];
+ {
+ int priority = 0; //lowest
+ int nextPos = naStrings.length-1;
+ while (nextPos>-1) {
+ for (int i = 0; i < mNetAttributes.length; i++) {
+ if(mNetAttributes[i].mPriority == priority) {
+ mPriorityList[nextPos--] = i;
+ }
+ }
+ priority++;
+ }
+ }
+
+ mNetRequestersPids =
+ new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
+ for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
+ mNetRequestersPids[i] = new ArrayList();
+ }
+
+ mFeatureUsers = new ArrayList();
+
/*
* Create the network state trackers for Wi-Fi and mobile
* data. Maybe this could be done with a factory class,
@@ -130,15 +259,36 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* to change very often.
*/
if (DBG) Log.v(TAG, "Starting Wifi Service.");
- mWifiStateTracker = new WifiStateTracker(context, handler);
- WifiService wifiService = new WifiService(context, mWifiStateTracker);
+ WifiStateTracker wst = new WifiStateTracker(context, mHandler);
+ WifiService wifiService = new WifiService(context, wst);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
- mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker;
+ mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
+ "MOBILE");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
+ "MOBILE_MMS");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
+ "MOBILE_SUPL");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
+ "MOBILE_DUN");
+
+ mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
+ new MobileDataStateTracker(context, mHandler,
+ ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
+ "MOBILE_HIPRI");
- mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
- mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
-
- mActiveNetwork = null;
mNumDnsEntries = 0;
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
@@ -148,16 +298,17 @@ public class ConnectivityService extends IConnectivityManager.Stub {
t.startMonitoring();
// Constructing this starts it too
- mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
+ mWifiWatchdogService = new WifiWatchdogService(context, wst);
}
/**
- * Sets the preferred network.
+ * Sets the preferred network.
* @param preference the new preference
*/
public synchronized void setNetworkPreference(int preference) {
enforceChangePermission();
- if (ConnectivityManager.isNetworkTypeValid(preference)) {
+ if (ConnectivityManager.isNetworkTypeValid(preference) &&
+ mNetAttributes[preference].isDefault()) {
if (mNetworkPreference != preference) {
persistNetworkPreference(preference);
mNetworkPreference = preference;
@@ -173,9 +324,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private void persistNetworkPreference(int networkPreference) {
final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
+ Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
+ networkPreference);
}
-
+
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -187,31 +339,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
}
-
+
/**
- * Make the state of network connectivity conform to the preference settings.
+ * Make the state of network connectivity conform to the preference settings
* In this method, we only tear down a non-preferred network. Establishing
* a connection to the preferred network is taken care of when we handle
* the disconnect event from the non-preferred network
* (see {@link #handleDisconnect(NetworkInfo)}).
*/
private void enforcePreference() {
- if (mActiveNetwork == null)
+ if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected())
return;
- for (NetworkStateTracker t : mNetTrackers) {
- if (t == mActiveNetwork) {
- int netType = t.getNetworkInfo().getType();
- int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ?
- ConnectivityManager.TYPE_MOBILE :
- ConnectivityManager.TYPE_WIFI);
-
- if (t.getNetworkInfo().getType() != mNetworkPreference) {
- NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
- if (otherTracker.isAvailable()) {
- teardown(t);
- }
+ if (!mNetTrackers[mNetworkPreference].isAvailable())
+ return;
+
+ for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
+ if (t != mNetworkPreference &&
+ mNetTrackers[t].getNetworkInfo().isConnected()) {
+ if (DBG) {
+ Log.d(TAG, "tearing down " +
+ mNetTrackers[t].getNetworkInfo() +
+ " in enforcePreference");
}
+ teardown(mNetTrackers[t]);
}
}
}
@@ -229,13 +380,21 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more
* than one is active, it is indeterminate which will be returned.
- * @return the info for the active network, or {@code null} if none is active
+ * @return the info for the active network, or {@code null} if none is
+ * active
*/
public NetworkInfo getActiveNetworkInfo() {
enforceAccessPermission();
- for (NetworkStateTracker t : mNetTrackers) {
+ for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
+ if (!mNetAttributes[type].isDefault()) {
+ continue;
+ }
+ NetworkStateTracker t = mNetTrackers[type];
NetworkInfo info = t.getNetworkInfo();
if (info.isConnected()) {
+ if (DBG && type != mActiveDefaultNetwork) Log.e(TAG,
+ "connected default network is not " +
+ "mActiveDefaultNetwork!");
return info;
}
}
@@ -280,36 +439,199 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return tracker != null && tracker.setRadio(turnOn);
}
- public int startUsingNetworkFeature(int networkType, String feature) {
+ private class FeatureUser implements IBinder.DeathRecipient {
+ int mNetworkType;
+ String mFeature;
+ IBinder mBinder;
+ int mPid;
+ int mUid;
+
+ FeatureUser(int type, String feature, IBinder binder) {
+ super();
+ mNetworkType = type;
+ mFeature = feature;
+ mBinder = binder;
+ mPid = getCallingPid();
+ mUid = getCallingUid();
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ public void binderDied() {
+ Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+ mNetworkType + ", " + mFeature + ", " + mBinder);
+ stopUsingNetworkFeature(mNetworkType, mFeature, mPid, mUid);
+ }
+
+ }
+
+ public int startUsingNetworkFeature(int networkType, String feature,
+ IBinder binder) {
+ if (DBG) {
+ Log.d(TAG, "startUsingNetworkFeature for net " + networkType +
+ ": " + feature);
+ }
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
- return -1;
+ return Phone.APN_REQUEST_FAILED;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+
+ synchronized (mFeatureUsers) {
+ mFeatureUsers.add(new FeatureUser(networkType, feature, binder));
+ }
+
+ // TODO - move this into the MobileDataStateTracker
+ int usedNetworkType = networkType;
+ if(networkType == ConnectivityManager.TYPE_MOBILE) {
+ if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
}
- return -1;
+ NetworkStateTracker network = mNetTrackers[usedNetworkType];
+ if (network != null) {
+ if (usedNetworkType != networkType) {
+ Integer currentPid = new Integer(getCallingPid());
+
+ NetworkStateTracker radio = mNetTrackers[networkType];
+ NetworkInfo ni = network.getNetworkInfo();
+
+ if (ni.isAvailable() == false) {
+ if (DBG) Log.d(TAG, "special network not available");
+ return Phone.APN_TYPE_NOT_AVAILABLE;
+ }
+
+ if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+ // this gets used for per-pid dns when connected
+ mNetRequestersPids[usedNetworkType].add(currentPid);
+ }
+
+ if ((ni.isConnectedOrConnecting() == true) &&
+ !network.isTeardownRequested()) {
+ if (ni.isConnected() == true) {
+ // add the pid-specific dns
+ handleDnsConfigurationChange();
+ if (DBG) Log.d(TAG, "special network already active");
+ return Phone.APN_ALREADY_ACTIVE;
+ }
+ if (DBG) Log.d(TAG, "special network already connecting");
+ return Phone.APN_REQUEST_STARTED;
+ }
+
+ // check if the radio in play can make another contact
+ // assume if cannot for now
+
+ // since we have to drop the default on this radio, setup
+ // an automatic event to switch back
+ if(mHandler.hasMessages(NetworkStateTracker.
+ EVENT_RESTORE_DEFAULT_NETWORK, radio) ||
+ radio.getNetworkInfo().isConnectedOrConnecting()) {
+ mHandler.removeMessages(NetworkStateTracker.
+ EVENT_RESTORE_DEFAULT_NETWORK,
+ radio);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ radio), getRestoreDefaultNetworkDelay());
+ }
+ if (DBG) Log.d(TAG, "reconnecting to special network");
+ network.reconnect();
+ return Phone.APN_REQUEST_STARTED;
+ } else {
+ return network.startUsingNetworkFeature(feature,
+ getCallingPid(), getCallingUid());
+ }
+ }
+ return Phone.APN_TYPE_NOT_AVAILABLE;
}
public int stopUsingNetworkFeature(int networkType, String feature) {
+ return stopUsingNetworkFeature(networkType, feature, getCallingPid(),
+ getCallingUid());
+ }
+
+ private int stopUsingNetworkFeature(int networkType, String feature,
+ int pid, int uid) {
+ if (DBG) {
+ Log.d(TAG, "stopUsingNetworkFeature for net " + networkType +
+ ": " + feature);
+ }
enforceChangePermission();
if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
return -1;
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.stopUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+
+ synchronized (mFeatureUsers) {
+ for (int i=0; i < mFeatureUsers.size(); i++) {
+ FeatureUser u = (FeatureUser)mFeatureUsers.get(i);
+ if (uid == u.mUid && pid == u.mPid &&
+ networkType == u.mNetworkType &&
+ TextUtils.equals(feature, u.mFeature)) {
+ u.unlinkDeathRecipient();
+ mFeatureUsers.remove(i);
+ break;
+ }
+ }
+ }
+
+ // TODO - move to MobileDataStateTracker
+ int usedNetworkType = networkType;
+ if (networkType == ConnectivityManager.TYPE_MOBILE) {
+ if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN;
+ } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) {
+ usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI;
+ }
+ }
+ NetworkStateTracker tracker = mNetTrackers[usedNetworkType];
+ if(usedNetworkType != networkType) {
+ Integer currentPid = new Integer(pid);
+ if (mNetRequestersPids[usedNetworkType].remove(currentPid)) {
+ reassessPidDns(pid, true);
+ }
+ if (mNetRequestersPids[usedNetworkType].size() != 0) {
+ if (DBG) Log.d(TAG, "not tearing down special network - " +
+ "others still using it");
+ return 1;
+ }
+
+ tracker.teardown();
+ NetworkStateTracker radio = mNetTrackers[networkType];
+ // Check if we want to revert to the default
+ if (mHandler.hasMessages(NetworkStateTracker.
+ EVENT_RESTORE_DEFAULT_NETWORK, radio)) {
+ mHandler.removeMessages(NetworkStateTracker.
+ EVENT_RESTORE_DEFAULT_NETWORK, radio);
+ radio.reconnect();
+ }
+ return 1;
+ } else {
+ return tracker.stopUsingNetworkFeature(feature, pid, uid);
}
- return -1;
}
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
- * @param networkType the type of the network over which traffic to the specified
- * host is to be routed
- * @param hostAddress the IP address of the host to which the route is desired
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -329,7 +651,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (getNumConnectedNetworks() > 1) {
return tracker.requestRouteToHost(hostAddress);
} else {
- return tracker.getNetworkInfo().getType() == networkType;
+ return (mNetAttributes[networkType].isDefault() &&
+ tracker.getNetworkInfo().isConnected());
}
}
@@ -340,7 +663,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.BACKGROUND_DATA, 1) == 1;
}
-
+
/**
* @see ConnectivityManager#setBackgroundDataSetting(boolean)
*/
@@ -348,87 +671,73 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
-
+
if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
-
+ Settings.Secure.BACKGROUND_DATA,
+ allowBackgroundDataUsage ? 1 : 0);
+
Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
mContext.sendBroadcast(broadcast);
- }
-
+ }
+
private int getNumConnectedNetworks() {
int numConnectedNets = 0;
for (NetworkStateTracker nt : mNetTrackers) {
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
++numConnectedNets;
}
}
+ if (DBG) Log.d(TAG, "numConnectedNets returning "+numConnectedNets);
return numConnectedNets;
}
private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
- "ConnectivityService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ "ConnectivityService");
}
private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
- "ConnectivityService");
-
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
}
/**
- * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
- * we ignore it. If it is for the active network, we send out a broadcast.
- * But first, we check whether it might be possible to connect to a different
- * network.
+ * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
+ * network, we ignore it. If it is for the active network, we send out a
+ * broadcast. But first, we check whether it might be possible to connect
+ * to a different network.
* @param info the {@code NetworkInfo} for the network
*/
private void handleDisconnect(NetworkInfo info) {
if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName());
+ int prevNetType = info.getType();
- mNetTrackers[info.getType()].setTeardownRequested(false);
+ mNetTrackers[prevNetType].setTeardownRequested(false);
/*
* If the disconnected network is not the active one, then don't report
* this as a loss of connectivity. What probably happened is that we're
* getting the disconnect for a network that we explicitly disabled
* in accordance with network preference policies.
*/
- if (mActiveNetwork == null || info.getType() != mActiveNetwork.getNetworkInfo().getType())
- return;
-
- NetworkStateTracker newNet;
- if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- newNet = mWifiStateTracker;
- } else /* info().getType() == TYPE_WIFI */ {
- newNet = mMobileDataStateTracker;
- }
-
- /**
- * See if the other network is available to fail over to.
- * If is not available, we enable it anyway, so that it
- * will be able to connect when it does become available,
- * but we report a total loss of connectivity rather than
- * report that we are attempting to fail over.
- */
- NetworkInfo switchTo = null;
- if (newNet.isAvailable()) {
- mActiveNetwork = newNet;
- switchTo = newNet.getNetworkInfo();
- switchTo.setFailover(true);
- if (!switchTo.isConnectedOrConnecting()) {
- newNet.reconnect();
+ if (!mNetAttributes[prevNetType].isDefault()) {
+ List pids = mNetRequestersPids[prevNetType];
+ for (int i = 0; i<pids.size(); i++) {
+ Integer pid = (Integer)pids.get(i);
+ // will remove them because the net's no longer connected
+ // need to do this now as only now do we know the pids and
+ // can properly null things that are no longer referenced.
+ reassessPidDns(pid.intValue(), false);
}
- } else {
- newNet.reconnect();
}
- boolean otherNetworkConnected = false;
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
@@ -439,31 +748,96 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
- if (switchTo != null) {
- otherNetworkConnected = switchTo.isConnected();
- if (DBG) {
- if (otherNetworkConnected) {
- Log.v(TAG, "Switching to already connected " + switchTo.getTypeName());
+
+ /*
+ * If this is a default network, check if other defaults are available
+ * or active
+ */
+ NetworkStateTracker newNet = null;
+ if (mNetAttributes[prevNetType].isDefault()) {
+ if (DBG) Log.d(TAG, "disconnecting a default network");
+ if (mActiveDefaultNetwork == prevNetType) {
+ mActiveDefaultNetwork = -1;
+ }
+
+ int newType = -1;
+ int newPriority = -1;
+ for (int checkType=0; checkType <=
+ ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
+ if (checkType == prevNetType) {
+ continue;
+ }
+ if (mNetAttributes[checkType].isDefault()) {
+ /* TODO - if we have multiple nets we could use
+ * we may want to put more thought into which we choose
+ */
+ if (checkType == mNetworkPreference) {
+ newType = checkType;
+ break;
+ }
+ if (mRadioAttributes[mNetAttributes[checkType].mRadio].
+ mPriority > newPriority) {
+ newType = checkType;
+ newPriority = mRadioAttributes[mNetAttributes[newType].
+ mRadio].mPriority;
+ }
+ }
+ }
+
+ if (newType != -1) {
+ newNet = mNetTrackers[newType];
+ /**
+ * See if the other network is available to fail over to.
+ * If is not available, we enable it anyway, so that it
+ * will be able to connect when it does become available,
+ * but we report a total loss of connectivity rather than
+ * report that we are attempting to fail over.
+ */
+ if (newNet.isAvailable()) {
+ NetworkInfo switchTo = newNet.getNetworkInfo();
+ switchTo.setFailover(true);
+ if (!switchTo.isConnectedOrConnecting() ||
+ newNet.isTeardownRequested()) {
+ newNet.reconnect();
+ }
+ if (DBG) {
+ if (switchTo.isConnected()) {
+ Log.v(TAG, "Switching to already connected " +
+ switchTo.getTypeName());
+ } else {
+ Log.v(TAG, "Attempting to switch to " +
+ switchTo.getTypeName());
+ }
+ }
+ intent.putExtra(ConnectivityManager.
+ EXTRA_OTHER_NETWORK_INFO, switchTo);
} else {
- Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
+ newNet.reconnect();
}
+ } else {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+ true);
}
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
- } else {
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
- if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
- (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
- mContext.sendStickyBroadcast(intent);
+ // do this before we broadcast the change
+ handleConnectivityChange();
+
+ if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " +
+ info.getTypeName() +
+ (newNet == null || !newNet.isAvailable() ? "" : " other=" +
+ newNet.getNetworkInfo().getTypeName()));
+
+ sendStickyBroadcast(intent);
/*
- * If the failover network is already connected, then immediately send out
- * a followup broadcast indicating successful failover
+ * If the failover network is already connected, then immediately send
+ * out a followup broadcast indicating successful failover
*/
- if (switchTo != null && otherNetworkConnected)
- sendConnectedBroadcast(switchTo);
+ if (newNet != null && newNet.getNetworkInfo().isConnected())
+ sendConnectedBroadcast(newNet.getNetworkInfo());
}
private void sendConnectedBroadcast(NetworkInfo info) {
@@ -477,9 +851,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
- mContext.sendStickyBroadcast(intent);
+ sendStickyBroadcast(intent);
}
/**
@@ -488,106 +863,128 @@ public class ConnectivityService extends IConnectivityManager.Stub {
*/
private void handleConnectionFailure(NetworkInfo info) {
mNetTrackers[info.getType()].setTeardownRequested(false);
- if (getActiveNetworkInfo() == null) {
- String reason = info.getReason();
- String extraInfo = info.getExtraInfo();
- if (DBG) {
- String reasonText;
- if (reason == null) {
- reasonText = ".";
- } else {
- reasonText = " (" + reason + ").";
- }
- Log.v(TAG, "Attempt to connect to " + info.getTypeName() + " failed" + reasonText);
+ String reason = info.getReason();
+ String extraInfo = info.getExtraInfo();
+
+ if (DBG) {
+ String reasonText;
+ if (reason == null) {
+ reasonText = ".";
+ } else {
+ reasonText = " (" + reason + ").";
}
-
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ Log.v(TAG, "Attempt to connect to " + info.getTypeName() +
+ " failed" + reasonText);
+ }
+
+ Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- if (reason != null) {
- intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
- }
- if (extraInfo != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ }
+ if (reason != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
+ }
+ if (extraInfo != null) {
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ }
+ if (info.isFailover()) {
+ intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
+ info.setFailover(false);
+ }
+ sendStickyBroadcast(intent);
+ }
+
+ private void sendStickyBroadcast(Intent intent) {
+ synchronized(this) {
+ if (mSystemReady) {
+ mContext.sendStickyBroadcast(intent);
+ } else {
+ if (mDeferredBroadcasts == null) {
+ mDeferredBroadcasts = new ArrayList<Intent>();
+ }
+ mDeferredBroadcasts.add(intent);
}
- if (info.isFailover()) {
- intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
- info.setFailover(false);
+ }
+ }
+
+ void systemReady() {
+ synchronized(this) {
+ mSystemReady = true;
+ if (mDeferredBroadcasts != null) {
+ int count = mDeferredBroadcasts.size();
+ for (int i = 0; i < count; i++) {
+ mContext.sendStickyBroadcast(mDeferredBroadcasts.get(i));
+ }
+ mDeferredBroadcasts = null;
}
- mContext.sendStickyBroadcast(intent);
}
}
private void handleConnect(NetworkInfo info) {
- if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName());
+ if (DBG) Log.d(TAG, "Handle CONNECT for " + info.getTypeName());
+
+ int type = info.getType();
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
- NetworkStateTracker thisNet = mNetTrackers[info.getType()];
- NetworkStateTracker deadnet = null;
- NetworkStateTracker otherNet;
- if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- otherNet = mWifiStateTracker;
- } else /* info().getType() == TYPE_WIFI */ {
- otherNet = mMobileDataStateTracker;
- }
- /*
- * Check policy to see whether we are connected to a non-preferred
- * network that now needs to be torn down.
- */
- NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo();
- NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo();
- if (wifiInfo.isConnected() && mobileInfo.isConnected()) {
- if (mNetworkPreference == ConnectivityManager.TYPE_WIFI)
- deadnet = mMobileDataStateTracker;
- else
- deadnet = mWifiStateTracker;
- }
-
- boolean toredown = false;
- thisNet.setTeardownRequested(false);
- if (!mTestMode && deadnet != null) {
- if (DBG) Log.v(TAG, "Policy requires " +
- deadnet.getNetworkInfo().getTypeName() + " teardown");
- toredown = teardown(deadnet);
- if (DBG && !toredown) {
- Log.d(TAG, "Network declined teardown request");
- }
- }
-
- /*
- * Note that if toredown is true, deadnet cannot be null, so there is
- * no danger of a null pointer exception here..
- */
- if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) {
- mActiveNetwork = thisNet;
- if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + info.getTypeName());
- thisNet.updateNetworkSettings();
- sendConnectedBroadcast(info);
- if (isFailover) {
- otherNet.releaseWakeLock();
+ NetworkStateTracker thisNet = mNetTrackers[type];
+
+ // if this is a default net and other default is running
+ // kill the one not preferred
+ if (mNetAttributes[type].isDefault()) {
+ if (DBG) Log.d(TAG, "connecting to a default network");
+ if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
+ if ((type != mNetworkPreference &&
+ mNetAttributes[mActiveDefaultNetwork].mPriority >
+ mNetAttributes[type].mPriority) ||
+ mNetworkPreference == mActiveDefaultNetwork) {
+ // don't accept this one
+ if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " +
+ "to torn down network " + info.getTypeName());
+ teardown(thisNet);
+ return;
+ } else {
+ // tear down the other
+ NetworkStateTracker otherNet =
+ mNetTrackers[mActiveDefaultNetwork];
+ if (DBG) Log.v(TAG, "Policy requires " +
+ otherNet.getNetworkInfo().getTypeName() +
+ " teardown");
+ if (!teardown(otherNet)) {
+ Log.e(TAG, "Network declined teardown request");
+ return;
+ }
+ if (isFailover) {
+ otherNet.releaseWakeLock();
+ }
+ }
}
- } else {
- if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
- info.getTypeName());
+ mActiveDefaultNetwork = type;
}
+ thisNet.setTeardownRequested(false);
+ if (DBG) Log.d(TAG, "Sending CONNECT bcast for " + info.getTypeName());
+ thisNet.updateNetworkSettings();
+ handleConnectivityChange();
+ sendConnectedBroadcast(info);
}
private void handleScanResultsAvailable(NetworkInfo info) {
int networkType = info.getType();
if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
- + " Don't know how to handle.");
+ if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
+ info.getTypeName() + " network. Don't know how to handle.");
}
-
+
mNetTrackers[networkType].interpretScanResultsAvailable();
}
- private void handleNotificationChange(boolean visible, int id, Notification notification) {
+ private void handleNotificationChange(boolean visible, int id,
+ Notification notification) {
NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
-
+
if (visible) {
notificationManager.notify(id, notification);
} else {
@@ -604,80 +1001,175 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* table entries exist.
*/
private void handleConnectivityChange() {
+ if (DBG) Log.d(TAG, "handleConnectivityChange");
/*
+ * If a non-default network is enabled, add the host routes that
+ * will allow it's DNS servers to be accessed. Only
* If both mobile and wifi are enabled, add the host routes that
* will allow MMS traffic to pass on the mobile network. But
* remove the default route for the mobile network, so that there
* will be only one default route, to ensure that all traffic
* except MMS will travel via Wi-Fi.
*/
- int numConnectedNets = handleConfigurationChange();
- if (numConnectedNets > 1) {
- mMobileDataStateTracker.addPrivateRoutes();
- mMobileDataStateTracker.removeDefaultRoute();
- } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) {
- mMobileDataStateTracker.removePrivateRoutes();
- mMobileDataStateTracker.restoreDefaultRoute();
+ handleDnsConfigurationChange();
+
+ for (int netType : mPriorityList) {
+ if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].addDefaultRoute();
+ } else {
+ mNetTrackers[netType].addPrivateDnsRoutes();
+ }
+ } else {
+ if (mNetAttributes[netType].isDefault()) {
+ mNetTrackers[netType].removeDefaultRoute();
+ } else {
+ mNetTrackers[netType].removePrivateDnsRoutes();
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjust the per-process dns entries (net.dns<x>.<pid>) based
+ * on the highest priority active net which this process requested.
+ * If there aren't any, clear it out
+ */
+ private void reassessPidDns(int myPid, boolean doBump)
+ {
+ if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid);
+ for(int i : mPriorityList) {
+ if (mNetAttributes[i].isDefault()) {
+ continue;
+ }
+ NetworkStateTracker nt = mNetTrackers[i];
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
+ List pids = mNetRequestersPids[i];
+ for (int j=0; j<pids.size(); j++) {
+ Integer pid = (Integer)pids.get(j);
+ if (pid.intValue() == myPid) {
+ String[] dnsList = nt.getNameServers();
+ writePidDns(dnsList, myPid);
+ if (doBump) {
+ bumpDns();
+ }
+ return;
+ }
+ }
+ }
+ }
+ // nothing found - delete
+ for (int i = 1; ; i++) {
+ String prop = "net.dns" + i + "." + myPid;
+ if (SystemProperties.get(prop).length() == 0) {
+ if (doBump) {
+ bumpDns();
+ }
+ return;
+ }
+ SystemProperties.set(prop, "");
+ }
+ }
+
+ private void writePidDns(String[] dnsList, int pid) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ SystemProperties.set("net.dns" + j++ + "." + pid, dns);
+ }
}
}
- private int handleConfigurationChange() {
+ private void bumpDns() {
/*
- * Set DNS properties. Always put Wi-Fi entries at the front of
- * the list if it is active.
+ * Bump the property that tells the name resolver library to reread
+ * the DNS server list from the properties.
*/
- int index = 1;
- String lastDns = "";
- int numConnectedNets = 0;
- int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
- int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
+ String propVal = SystemProperties.get("net.dnschange");
+ int n = 0;
+ if (propVal.length() != 0) {
+ try {
+ n = Integer.parseInt(propVal);
+ } catch (NumberFormatException e) {}
+ }
+ SystemProperties.set("net.dnschange", "" + (n+1));
+ }
- for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
+ private void handleDnsConfigurationChange() {
+ if (DBG) Log.d(TAG, "handleDnsConfig Change");
+ // add default net's dns entries
+ for (int x = mPriorityList.length-1; x>= 0; x--) {
+ int netType = mPriorityList[x];
NetworkStateTracker nt = mNetTrackers[netType];
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
- ++numConnectedNets;
+ if (DBG) Log.d(TAG, " checking " + nt);
+ if (nt != null && nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
+ if (DBG) Log.d(TAG, " connected");
String[] dnsList = nt.getNameServers();
- for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) {
- // skip duplicate entries
- if (!dnsList[i].equals(lastDns)) {
- SystemProperties.set("net.dns" + index++, dnsList[i]);
- lastDns = dnsList[i];
+ if (mNetAttributes[netType].isDefault()) {
+ int j = 1;
+ for (String dns : dnsList) {
+ if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) {
+ if (DBG) Log.d(TAG, " adding "+dns);
+ SystemProperties.set("net.dns" + j++, dns);
+ }
+ }
+ for (int k=j ; k<mNumDnsEntries; k++) {
+ if (DBG) Log.d(TAG, "erasing net.dns" + k);
+ SystemProperties.set("net.dns" + k, "");
+ }
+ mNumDnsEntries = j;
+ } else {
+ // set per-pid dns for attached secondary nets
+ List pids = mNetRequestersPids[netType];
+ for (int y=0; y< pids.size(); y++) {
+ Integer pid = (Integer)pids.get(y);
+ writePidDns(dnsList, pid.intValue());
}
}
}
}
- // Null out any DNS properties that are no longer used
- for (int i = index; i <= mNumDnsEntries; i++) {
- SystemProperties.set("net.dns" + i, "");
+
+ bumpDns();
+ }
+
+ private int getRestoreDefaultNetworkDelay() {
+ String restoreDefaultNetworkDelayStr = SystemProperties.get(
+ NETWORK_RESTORE_DELAY_PROP_NAME);
+ if(restoreDefaultNetworkDelayStr != null &&
+ restoreDefaultNetworkDelayStr.length() != 0) {
+ try {
+ return Integer.valueOf(restoreDefaultNetworkDelayStr);
+ } catch (NumberFormatException e) {
+ }
}
- mNumDnsEntries = index - 1;
- // Notify the name resolver library of the change
- SystemProperties.set("net.dnschange", String.valueOf(sDnsChangeCounter++));
- return numConnectedNets;
+ return RESTORE_DEFAULT_NETWORK_DELAY;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ConnectivityService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ pw.println("Permission Denial: can't dump ConnectivityService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ Binder.getCallingUid());
return;
}
- if (mActiveNetwork == null) {
- pw.println("No active network");
- } else {
- pw.println("Active network: " + mActiveNetwork.getNetworkInfo().getTypeName());
- }
pw.println();
for (NetworkStateTracker nst : mNetTrackers) {
+ if (nst.getNetworkInfo().isConnected()) {
+ pw.println("Active network: " + nst.getNetworkInfo().
+ getTypeName());
+ }
pw.println(nst.getNetworkInfo());
pw.println(nst);
pw.println();
}
}
+ // must be stateless - things change under us.
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
@@ -685,46 +1177,54 @@ public class ConnectivityService extends IConnectivityManager.Stub {
switch (msg.what) {
case NetworkStateTracker.EVENT_STATE_CHANGED:
info = (NetworkInfo) msg.obj;
- if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
+ if (DBG) Log.d(TAG, "ConnectivityChange for " +
+ info.getTypeName() + ": " +
info.getState() + "/" + info.getDetailedState());
// Connectivity state changed:
// [31-13] Reserved for future use
- // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
- // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
+ // [12-9] Network subtype (for mobile network, as defined
+ // by TelephonyManager)
+ // [8-3] Detailed state ordinal (as defined by
+ // NetworkInfo.DetailedState)
// [2-0] Network type (as defined by ConnectivityManager)
int eventLogParam = (info.getType() & 0x7) |
((info.getDetailedState().ordinal() & 0x3f) << 3) |
(info.getSubtype() << 9);
- EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
-
- if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
+ eventLogParam);
+
+ if (info.getDetailedState() ==
+ NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
- } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
+ } else if (info.getState() ==
+ NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.SUSPENDED) {
// TODO: need to think this over.
- // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
- // only difference being we are broadcasting an intent with NetworkInfo
- // that's suspended. This allows the applications an opportunity to
- // handle DISCONNECTED and SUSPENDED differently, or not.
+ // the logic here is, handle SUSPENDED the same as
+ // DISCONNECTED. The only difference being we are
+ // broadcasting an intent with NetworkInfo that's
+ // suspended. This allows the applications an
+ // opportunity to handle DISCONNECTED and SUSPENDED
+ // differently, or not.
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
- handleConnectivityChange();
break;
case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
info = (NetworkInfo) msg.obj;
handleScanResultsAvailable(info);
break;
-
+
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
+ handleNotificationChange(msg.arg1 == 1, msg.arg2,
+ (Notification) msg.obj);
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- handleConfigurationChange();
+ handleDnsConfigurationChange();
break;
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
@@ -734,6 +1234,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
// fill me in
break;
+ case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+ for (NetworkStateTracker net : mNetTrackers) {
+ NetworkInfo i = net.getNetworkInfo();
+ if (i.isConnected() &&
+ !mNetAttributes[i.getType()].isDefault()) {
+ if (DBG) {
+ Log.d(TAG, "tearing down " + i +
+ " to restore the default network");
+ }
+ teardown(net);
+ }
+ }
+ break;
}
}
}
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
new file mode 100644
index 0000000..7385359
--- /dev/null
+++ b/services/java/com/android/server/DockObserver.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UEventObserver;
+import android.util.Log;
+
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+
+/**
+ * <p>DockObserver monitors for a docking station.
+ */
+class DockObserver extends UEventObserver {
+ private static final String TAG = DockObserver.class.getSimpleName();
+ private static final boolean LOG = false;
+
+ private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
+ private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
+
+ private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ private boolean mSystemReady;
+
+ private final Context mContext;
+
+ public DockObserver(Context context) {
+ mContext = context;
+ init(); // set initial status
+ startObserving(DOCK_UEVENT_MATCH);
+ }
+
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Dock UEVENT: " + event.toString());
+ }
+
+ synchronized (this) {
+ try {
+ int newState = Integer.parseInt(event.get("SWITCH_STATE"));
+ if (newState != mDockState) {
+ mDockState = newState;
+ if (mSystemReady) {
+ update();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Could not parse switch state from event " + event);
+ }
+ }
+ }
+
+ private final void init() {
+ char[] buffer = new char[1024];
+
+ try {
+ FileReader file = new FileReader(DOCK_STATE_PATH);
+ int len = file.read(buffer, 0, 1024);
+ mDockState = Integer.valueOf((new String(buffer, 0, len)).trim());
+
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "This kernel does not have dock station support");
+ } catch (Exception e) {
+ Log.e(TAG, "" , e);
+ }
+ }
+
+ void systemReady() {
+ synchronized (this) {
+ // don't bother broadcasting undocked here
+ if (mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ update();
+ }
+ mSystemReady = true;
+ }
+ }
+
+ private final void update() {
+ mHandler.sendEmptyMessage(0);
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (this) {
+ Log.d(TAG, "Broadcasting dock state " + mDockState);
+ // Pack up the values and broadcast them to everyone
+ Intent intent = new Intent(Intent.ACTION_DOCK_EVENT);
+ intent.putExtra(Intent.EXTRA_DOCK_STATE, mDockState);
+ mContext.sendStickyBroadcast(intent);
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/HardwareService.java b/services/java/com/android/server/HardwareService.java
index 5bc9b5f..6ac72e0 100755
--- a/services/java/com/android/server/HardwareService.java
+++ b/services/java/com/android/server/HardwareService.java
@@ -37,6 +37,9 @@ import android.os.Binder;
import android.os.SystemClock;
import android.util.Log;
+import java.util.LinkedList;
+import java.util.ListIterator;
+
public class HardwareService extends IHardwareService.Stub {
private static final String TAG = "HardwareService";
@@ -50,9 +53,62 @@ public class HardwareService extends IHardwareService.Stub {
static final int LIGHT_FLASH_NONE = 0;
static final int LIGHT_FLASH_TIMED = 1;
+ private final LinkedList<Vibration> mVibrations;
+ private Vibration mCurrentVibration;
+
private boolean mAttentionLightOn;
private boolean mPulsing;
+ private class Vibration implements IBinder.DeathRecipient {
+ private final IBinder mToken;
+ private final long mTimeout;
+ private final long mStartTime;
+ private final long[] mPattern;
+ private final int mRepeat;
+
+ Vibration(IBinder token, long millis) {
+ this(token, millis, null, 0);
+ }
+
+ Vibration(IBinder token, long[] pattern, int repeat) {
+ this(token, 0, pattern, repeat);
+ }
+
+ private Vibration(IBinder token, long millis, long[] pattern,
+ int repeat) {
+ mToken = token;
+ mTimeout = millis;
+ mStartTime = SystemClock.uptimeMillis();
+ mPattern = pattern;
+ mRepeat = repeat;
+ }
+
+ public void binderDied() {
+ synchronized (mVibrations) {
+ mVibrations.remove(this);
+ if (this == mCurrentVibration) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
+ }
+
+ public boolean hasLongerTimeout(long millis) {
+ if (mTimeout == 0) {
+ // This is a pattern, return false to play the simple
+ // vibration.
+ return false;
+ }
+ if ((mStartTime + mTimeout)
+ < (SystemClock.uptimeMillis() + millis)) {
+ // If this vibration will end before the time passed in, let
+ // the new vibration play.
+ return false;
+ }
+ return true;
+ }
+ }
+
HardwareService(Context context) {
// Reset the hardware to a default state, in case this is a runtime
// restart instead of a fresh boot.
@@ -66,6 +122,8 @@ public class HardwareService extends IHardwareService.Stub {
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(true);
+ mVibrations = new LinkedList<Vibration>();
+
mBatteryStats = BatteryStatsService.getService();
IntentFilter filter = new IntentFilter();
@@ -78,13 +136,27 @@ public class HardwareService extends IHardwareService.Stub {
super.finalize();
}
- public void vibrate(long milliseconds) {
+ public void vibrate(long milliseconds, IBinder token) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires VIBRATE permission");
}
- doCancelVibrate();
- vibratorOn(milliseconds);
+ // We're running in the system server so we cannot crash. Check for a
+ // timeout of 0 or negative. This will ensure that a vibration has
+ // either a timeout of > 0 or a non-null pattern.
+ if (milliseconds <= 0 || (mCurrentVibration != null
+ && mCurrentVibration.hasLongerTimeout(milliseconds))) {
+ // Ignore this vibration since the current vibration will play for
+ // longer than milliseconds.
+ return;
+ }
+ Vibration vib = new Vibration(token, milliseconds);
+ synchronized (mVibrations) {
+ removeVibrationLocked(token);
+ doCancelVibrateLocked();
+ mCurrentVibration = vib;
+ startVibrationLocked(vib);
+ }
}
private boolean isAll0(long[] pattern) {
@@ -121,34 +193,25 @@ public class HardwareService extends IHardwareService.Stub {
return;
}
- synchronized (this) {
- Death death = new Death(token);
- try {
- token.linkToDeath(death, 0);
- } catch (RemoteException e) {
- return;
- }
-
- Thread oldThread = mThread;
-
- if (oldThread != null) {
- // stop the old one
- synchronized (mThread) {
- mThread.mDone = true;
- mThread.notify();
- }
- }
+ Vibration vib = new Vibration(token, pattern, repeat);
+ try {
+ token.linkToDeath(vib, 0);
+ } catch (RemoteException e) {
+ return;
+ }
- if (mDeath != null) {
- mToken.unlinkToDeath(mDeath, 0);
+ synchronized (mVibrations) {
+ removeVibrationLocked(token);
+ doCancelVibrateLocked();
+ if (repeat >= 0) {
+ mVibrations.addFirst(vib);
+ startNextVibrationLocked();
+ } else {
+ // A negative repeat means that this pattern is not meant
+ // to repeat. Treat it like a simple vibration.
+ mCurrentVibration = vib;
+ startVibrationLocked(vib);
}
-
- mDeath = death;
- mToken = token;
-
- // start the new thread
- mThread = new VibrateThread(pattern, repeat);
- mThread.start();
}
}
finally {
@@ -156,7 +219,7 @@ public class HardwareService extends IHardwareService.Stub {
}
}
- public void cancelVibrate() {
+ public void cancelVibrate(IBinder token) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.VIBRATE,
"cancelVibrate");
@@ -164,7 +227,13 @@ public class HardwareService extends IHardwareService.Stub {
// so wakelock calls will succeed
long identity = Binder.clearCallingIdentity();
try {
- doCancelVibrate();
+ synchronized (mVibrations) {
+ final Vibration vib = removeVibrationLocked(token);
+ if (vib == mCurrentVibration) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
}
finally {
Binder.restoreCallingIdentity(identity);
@@ -277,27 +346,74 @@ public class HardwareService extends IHardwareService.Stub {
}
};
- private void doCancelVibrate() {
- synchronized (this) {
- if (mThread != null) {
- synchronized (mThread) {
- mThread.mDone = true;
- mThread.notify();
- }
- mThread = null;
+ private final Runnable mVibrationRunnable = new Runnable() {
+ public void run() {
+ synchronized (mVibrations) {
+ doCancelVibrateLocked();
+ startNextVibrationLocked();
+ }
+ }
+ };
+
+ // Lock held on mVibrations
+ private void doCancelVibrateLocked() {
+ if (mThread != null) {
+ synchronized (mThread) {
+ mThread.mDone = true;
+ mThread.notify();
}
- vibratorOff();
+ mThread = null;
}
+ vibratorOff();
+ mH.removeCallbacks(mVibrationRunnable);
+ }
+
+ // Lock held on mVibrations
+ private void startNextVibrationLocked() {
+ if (mVibrations.size() <= 0) {
+ return;
+ }
+ mCurrentVibration = mVibrations.getFirst();
+ startVibrationLocked(mCurrentVibration);
+ }
+
+ // Lock held on mVibrations
+ private void startVibrationLocked(final Vibration vib) {
+ if (vib.mTimeout != 0) {
+ vibratorOn(vib.mTimeout);
+ mH.postDelayed(mVibrationRunnable, vib.mTimeout);
+ } else {
+ // mThread better be null here. doCancelVibrate should always be
+ // called before startNextVibrationLocked or startVibrationLocked.
+ mThread = new VibrateThread(vib);
+ mThread.start();
+ }
+ }
+
+ // Lock held on mVibrations
+ private Vibration removeVibrationLocked(IBinder token) {
+ ListIterator<Vibration> iter = mVibrations.listIterator(0);
+ while (iter.hasNext()) {
+ Vibration vib = iter.next();
+ if (vib.mToken == token) {
+ iter.remove();
+ return vib;
+ }
+ }
+ // We might be looking for a simple vibration which is only stored in
+ // mCurrentVibration.
+ if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
+ return mCurrentVibration;
+ }
+ return null;
}
private class VibrateThread extends Thread {
- long[] mPattern;
- int mRepeat;
+ final Vibration mVibration;
boolean mDone;
- VibrateThread(long[] pattern, int repeat) {
- mPattern = pattern;
- mRepeat = repeat;
+ VibrateThread(Vibration vib) {
+ mVibration = vib;
mWakeLock.acquire();
}
@@ -323,8 +439,9 @@ public class HardwareService extends IHardwareService.Stub {
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
synchronized (this) {
int index = 0;
- long[] pattern = mPattern;
+ long[] pattern = mVibration.mPattern;
int len = pattern.length;
+ int repeat = mVibration.mRepeat;
long duration = 0;
while (!mDone) {
@@ -347,50 +464,37 @@ public class HardwareService extends IHardwareService.Stub {
HardwareService.this.vibratorOn(duration);
}
} else {
- if (mRepeat < 0) {
+ if (repeat < 0) {
break;
} else {
- index = mRepeat;
+ index = repeat;
duration = 0;
}
}
}
- if (mDone) {
- // make sure vibrator is off if we were cancelled.
- // otherwise, it will turn off automatically
- // when the last timeout expires.
- HardwareService.this.vibratorOff();
- }
mWakeLock.release();
}
- synchronized (HardwareService.this) {
+ synchronized (mVibrations) {
if (mThread == this) {
mThread = null;
}
- }
- }
- };
-
- private class Death implements IBinder.DeathRecipient {
- IBinder mMe;
-
- Death(IBinder me) {
- mMe = me;
- }
-
- public void binderDied() {
- synchronized (HardwareService.this) {
- if (mMe == mToken) {
- doCancelVibrate();
+ if (!mDone) {
+ // If this vibration finished naturally, start the next
+ // vibration.
+ mVibrations.remove(mVibration);
+ startNextVibrationLocked();
}
}
}
- }
+ };
BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- doCancelVibrate();
+ synchronized (mVibrations) {
+ doCancelVibrateLocked();
+ mVibrations.clear();
+ }
}
}
};
@@ -407,8 +511,6 @@ public class HardwareService extends IHardwareService.Stub {
private final IBatteryStats mBatteryStats;
volatile VibrateThread mThread;
- volatile Death mDeath;
- volatile IBinder mToken;
private int mNativePointer;
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 9b0a2d4..bee3108 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -41,10 +41,16 @@ class HeadsetObserver extends UEventObserver {
private static final String HEADSET_STATE_PATH = "/sys/class/switch/h2w/state";
private static final String HEADSET_NAME_PATH = "/sys/class/switch/h2w/name";
+ private static final int BIT_HEADSET = (1 << 0);
+ private static final int BIT_HEADSET_NO_MIC = (1 << 1);
+ private static final int BIT_TTY = (1 << 2);
+ private static final int BIT_FM_HEADSET = (1 << 3);
+ private static final int BIT_FM_SPEAKER = (1 << 4);
+
private int mHeadsetState;
+ private int mPrevHeadsetState;
private String mHeadsetName;
- private boolean mAudioRouteNeedsUpdate;
- private AudioManager mAudioManager;
+ private boolean mPendingIntent;
private final Context mContext;
private final WakeLock mWakeLock; // held while there is a pending route change
@@ -76,6 +82,7 @@ class HeadsetObserver extends UEventObserver {
String newName = mHeadsetName;
int newState = mHeadsetState;
+ mPrevHeadsetState = mHeadsetState;
try {
FileReader file = new FileReader(HEADSET_STATE_PATH);
int len = file.read(buffer, 0, 1024);
@@ -91,20 +98,25 @@ class HeadsetObserver extends UEventObserver {
Log.e(TAG, "" , e);
}
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
update(newName, newState);
}
private synchronized final void update(String newName, int newState) {
if (newName != mHeadsetName || newState != mHeadsetState) {
- boolean isUnplug = (newState == 0 && mHeadsetState == 1);
+ boolean isUnplug = false;
+ if ( (mHeadsetState & BIT_HEADSET) > 0 || (mHeadsetState & BIT_HEADSET_NO_MIC) > 0) {
+ if ((newState & BIT_HEADSET) == 0 && (newState & BIT_HEADSET_NO_MIC) == 0)
+ isUnplug = true;
+ }
mHeadsetName = newName;
+ mPrevHeadsetState = mHeadsetState;
mHeadsetState = newState;
- mAudioRouteNeedsUpdate = true;
-
- sendIntent(isUnplug);
+ mPendingIntent = true;
if (isUnplug) {
+ Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ mContext.sendBroadcast(intent);
+
// It can take hundreds of ms flush the audio pipeline after
// apps pause audio playback, but audio route changes are
// immediate, so delay the route change by 1000ms.
@@ -113,12 +125,13 @@ class HeadsetObserver extends UEventObserver {
mWakeLock.acquire();
mHandler.sendEmptyMessageDelayed(0, 1000);
} else {
- updateAudioRoute();
+ sendIntent();
+ mPendingIntent = false;
}
}
}
- private synchronized final void sendIntent(boolean isUnplug) {
+ private synchronized final void sendIntent() {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -128,24 +141,15 @@ class HeadsetObserver extends UEventObserver {
// TODO: Should we require a permission?
ActivityManagerNative.broadcastStickyIntent(intent, null);
-
- if (isUnplug) {
- intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
- mContext.sendBroadcast(intent);
- }
- }
-
- private synchronized final void updateAudioRoute() {
- if (mAudioRouteNeedsUpdate) {
- mAudioManager.setWiredHeadsetOn(mHeadsetState == 1);
- mAudioRouteNeedsUpdate = false;
- }
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- updateAudioRoute();
+ if (mPendingIntent) {
+ sendIntent();
+ mPendingIntent = false;
+ }
mWakeLock.release();
}
};
diff --git a/services/java/com/android/server/InputDevice.java b/services/java/com/android/server/InputDevice.java
index 7b8a2a4..e1bce73 100644
--- a/services/java/com/android/server/InputDevice.java
+++ b/services/java/com/android/server/InputDevice.java
@@ -23,9 +23,14 @@ import android.view.Surface;
import android.view.WindowManagerPolicy;
public class InputDevice {
+ static final boolean DEBUG_POINTERS = false;
+
/** Amount that trackball needs to move in order to generate a key event. */
static final int TRACKBALL_MOVEMENT_THRESHOLD = 6;
+ /** Maximum number of pointers we will track and report. */
+ static final int MAX_POINTERS = 10;
+
final int id;
final int classes;
final String name;
@@ -34,9 +39,13 @@ public class InputDevice {
final AbsoluteInfo absPressure;
final AbsoluteInfo absSize;
- long mDownTime = 0;
+ long mKeyDownTime = 0;
int mMetaKeysState = 0;
+ // For use by KeyInputQueue for keeping track of the current touch
+ // data in the old non-multi-touch protocol.
+ final int[] curTouchVals = new int[MotionEvent.NUM_SAMPLE_DATA * 2];
+
final MotionState mAbs = new MotionState(0, 0);
final MotionState mRel = new MotionState(TRACKBALL_MOVEMENT_THRESHOLD,
TRACKBALL_MOVEMENT_THRESHOLD);
@@ -48,146 +57,503 @@ public class InputDevice {
float yMoveScale;
MotionEvent currentMove = null;
boolean changed = false;
- boolean down = false;
- boolean lastDown = false;
- long downTime = 0;
- int x = 0;
- int y = 0;
- int pressure = 1;
- int size = 0;
+ long mDownTime = 0;
+
+ // The currently assigned pointer IDs, corresponding to the last data.
+ int[] mPointerIds = new int[MAX_POINTERS];
+
+ // This is the last generated pointer data, ordered to match
+ // mPointerIds.
+ int mLastNumPointers = 0;
+ final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
+
+ // This is the next set of pointer data being generated. It is not
+ // in any known order, and will be propagated in to mLastData
+ // as part of mapping it to the appropriate pointer IDs.
+ // Note that we have one extra sample of data here, to help clients
+ // avoid doing bounds checking.
+ int mNextNumPointers = 0;
+ final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
+ + MotionEvent.NUM_SAMPLE_DATA];
+
+ // Temporary data structures for doing the pointer ID mapping.
+ final int[] mLast2Next = new int[MAX_POINTERS];
+ final int[] mNext2Last = new int[MAX_POINTERS];
+ final long[] mNext2LastDistance = new long[MAX_POINTERS];
+
+ // Temporary data structure for generating the final motion data.
+ final float[] mReportData = new float[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
+
+ // This is not used here, but can be used by callers for state tracking.
+ int mAddingPointerOffset = 0;
+ final boolean[] mDown = new boolean[MAX_POINTERS];
MotionState(int mx, int my) {
xPrecision = mx;
yPrecision = my;
xMoveScale = mx != 0 ? (1.0f/mx) : 1.0f;
yMoveScale = my != 0 ? (1.0f/my) : 1.0f;
+ for (int i=0; i<MAX_POINTERS; i++) {
+ mPointerIds[i] = i;
+ }
}
- MotionEvent generateMotion(InputDevice device, long curTime,
- boolean isAbs, Display display, int orientation,
- int metaState) {
- if (!changed) {
- return null;
+ private boolean assignPointer(int nextIndex, boolean allowOverlap) {
+ final int lastNumPointers = mLastNumPointers;
+ final int[] next2Last = mNext2Last;
+ final long[] next2LastDistance = mNext2LastDistance;
+ final int[] last2Next = mLast2Next;
+ final int[] lastData = mLastData;
+ final int[] nextData = mNextData;
+ final int id = nextIndex * MotionEvent.NUM_SAMPLE_DATA;
+
+ if (DEBUG_POINTERS) Log.v("InputDevice", "assignPointer: nextIndex="
+ + nextIndex + " dataOff=" + id);
+ final int x1 = nextData[id + MotionEvent.SAMPLE_X];
+ final int y1 = nextData[id + MotionEvent.SAMPLE_Y];
+
+ long bestDistance = -1;
+ int bestIndex = -1;
+ for (int j=0; j<lastNumPointers; j++) {
+ if (!allowOverlap && last2Next[j] < 0) {
+ continue;
+ }
+ final int jd = j * MotionEvent.NUM_SAMPLE_DATA;
+ final int xd = lastData[jd + MotionEvent.SAMPLE_X] - x1;
+ final int yd = lastData[jd + MotionEvent.SAMPLE_Y] - y1;
+ final long distance = xd*(long)xd + yd*(long)yd;
+ if (j == 0 || distance < bestDistance) {
+ bestDistance = distance;
+ bestIndex = j;
+ }
}
- float scaledX = x;
- float scaledY = y;
- float temp;
- float scaledPressure = 1.0f;
- float scaledSize = 0;
- int edgeFlags = 0;
- if (isAbs) {
- int w = display.getWidth()-1;
- int h = display.getHeight()-1;
- if (orientation == Surface.ROTATION_90
- || orientation == Surface.ROTATION_270) {
- int tmp = w;
- w = h;
- h = tmp;
- }
- if (device.absX != null) {
- scaledX = ((scaledX-device.absX.minValue)
- / device.absX.range) * w;
- }
- if (device.absY != null) {
- scaledY = ((scaledY-device.absY.minValue)
- / device.absY.range) * h;
- }
- if (device.absPressure != null) {
- scaledPressure =
- ((pressure-device.absPressure.minValue)
- / (float)device.absPressure.range);
- }
- if (device.absSize != null) {
- scaledSize =
- ((size-device.absSize.minValue)
- / (float)device.absSize.range);
+ if (DEBUG_POINTERS) Log.v("InputDevice", "New index " + nextIndex
+ + " best old index=" + bestIndex + " (distance="
+ + bestDistance + ")");
+ next2Last[nextIndex] = bestIndex;
+ next2LastDistance[nextIndex] = bestDistance;
+
+ if (bestIndex < 0) {
+ return true;
+ }
+
+ if (last2Next[bestIndex] == -1) {
+ last2Next[bestIndex] = nextIndex;
+ return false;
+ }
+
+ if (DEBUG_POINTERS) Log.v("InputDevice", "Old index " + bestIndex
+ + " has multiple best new pointers!");
+
+ last2Next[bestIndex] = -2;
+ return true;
+ }
+
+ private int updatePointerIdentifiers() {
+ final int[] lastData = mLastData;
+ final int[] nextData = mNextData;
+ final int nextNumPointers = mNextNumPointers;
+ final int lastNumPointers = mLastNumPointers;
+
+ if (nextNumPointers == 1 && lastNumPointers == 1) {
+ System.arraycopy(nextData, 0, lastData, 0,
+ MotionEvent.NUM_SAMPLE_DATA);
+ return -1;
+ }
+
+ // Clear our old state.
+ final int[] last2Next = mLast2Next;
+ for (int i=0; i<lastNumPointers; i++) {
+ last2Next[i] = -1;
+ }
+
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Update pointers: lastNumPointers=" + lastNumPointers
+ + " nextNumPointers=" + nextNumPointers);
+
+ // Figure out the closes new points to the previous points.
+ final int[] next2Last = mNext2Last;
+ final long[] next2LastDistance = mNext2LastDistance;
+ boolean conflicts = false;
+ for (int i=0; i<nextNumPointers; i++) {
+ conflicts |= assignPointer(i, true);
+ }
+
+ // Resolve ambiguities in pointer mappings, when two or more
+ // new pointer locations find their best previous location is
+ // the same.
+ if (conflicts) {
+ if (DEBUG_POINTERS) Log.v("InputDevice", "Resolving conflicts");
+
+ for (int i=0; i<lastNumPointers; i++) {
+ if (last2Next[i] != -2) {
+ continue;
+ }
+
+ // Note that this algorithm is far from perfect. Ideally
+ // we should do something like the one described at
+ // http://portal.acm.org/citation.cfm?id=997856
+
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Resolving last index #" + i);
+
+ int numFound;
+ do {
+ numFound = 0;
+ long worstDistance = 0;
+ int worstJ = -1;
+ for (int j=0; j<nextNumPointers; j++) {
+ if (next2Last[j] != i) {
+ continue;
+ }
+ numFound++;
+ if (worstDistance < next2LastDistance[j]) {
+ worstDistance = next2LastDistance[j];
+ worstJ = j;
+ }
+ }
+
+ if (worstJ >= 0) {
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Worst new pointer: " + worstJ
+ + " (distance=" + worstDistance + ")");
+ if (assignPointer(worstJ, false)) {
+ // In this case there is no last pointer
+ // remaining for this new one!
+ next2Last[worstJ] = -1;
+ }
+ }
+ } while (numFound > 2);
}
- switch (orientation) {
- case Surface.ROTATION_90:
- temp = scaledX;
- scaledX = scaledY;
- scaledY = w-temp;
+ }
+
+ int retIndex = -1;
+
+ if (lastNumPointers < nextNumPointers) {
+ // We have one or more new pointers that are down. Create a
+ // new pointer identifier for one of them.
+ if (DEBUG_POINTERS) Log.v("InputDevice", "Adding new pointer");
+ int nextId = 0;
+ int i=0;
+ while (i < lastNumPointers) {
+ if (mPointerIds[i] > nextId) {
+ // Found a hole, insert the pointer here.
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Inserting new pointer at hole " + i);
+ System.arraycopy(mPointerIds, i, mPointerIds,
+ i+1, lastNumPointers-i);
+ System.arraycopy(lastData, i*MotionEvent.NUM_SAMPLE_DATA,
+ lastData, (i+1)*MotionEvent.NUM_SAMPLE_DATA,
+ (lastNumPointers-i)*MotionEvent.NUM_SAMPLE_DATA);
break;
- case Surface.ROTATION_180:
- scaledX = w-scaledX;
- scaledY = h-scaledY;
+ }
+ i++;
+ nextId++;
+ }
+
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "New pointer id " + nextId + " at index " + i);
+
+ mLastNumPointers++;
+ retIndex = i;
+ mPointerIds[i] = nextId;
+
+ // And assign this identifier to the first new pointer.
+ for (int j=0; j<nextNumPointers; j++) {
+ if (next2Last[j] < 0) {
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Assigning new id to new pointer index " + j);
+ next2Last[j] = i;
break;
- case Surface.ROTATION_270:
- temp = scaledX;
- scaledX = h-scaledY;
- scaledY = temp;
+ }
+ }
+ }
+
+ // Propagate all of the current data into the appropriate
+ // location in the old data to match the pointer ID that was
+ // assigned to it.
+ for (int i=0; i<nextNumPointers; i++) {
+ int lastIndex = next2Last[i];
+ if (lastIndex >= 0) {
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Copying next pointer index " + i
+ + " to last index " + lastIndex);
+ System.arraycopy(nextData, i*MotionEvent.NUM_SAMPLE_DATA,
+ lastData, lastIndex*MotionEvent.NUM_SAMPLE_DATA,
+ MotionEvent.NUM_SAMPLE_DATA);
+ }
+ }
+
+ if (lastNumPointers > nextNumPointers) {
+ // One or more pointers has gone up. Find the first one,
+ // and adjust accordingly.
+ if (DEBUG_POINTERS) Log.v("InputDevice", "Removing old pointer");
+ for (int i=0; i<lastNumPointers; i++) {
+ if (last2Next[i] == -1) {
+ if (DEBUG_POINTERS) Log.v("InputDevice",
+ "Removing old pointer at index " + i);
+ retIndex = i;
break;
+ }
}
-
- if (scaledX == 0) {
- edgeFlags += MotionEvent.EDGE_LEFT;
- } else if (scaledX == display.getWidth() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_RIGHT;
+ }
+
+ return retIndex;
+ }
+
+ void removeOldPointer(int index) {
+ final int lastNumPointers = mLastNumPointers;
+ if (index >= 0 && index < lastNumPointers) {
+ System.arraycopy(mPointerIds, index+1, mPointerIds,
+ index, lastNumPointers-index-1);
+ System.arraycopy(mLastData, (index+1)*MotionEvent.NUM_SAMPLE_DATA,
+ mLastData, (index)*MotionEvent.NUM_SAMPLE_DATA,
+ (lastNumPointers-index-1)*MotionEvent.NUM_SAMPLE_DATA);
+ mLastNumPointers--;
+ }
+ }
+
+ MotionEvent generateAbsMotion(InputDevice device, long curTime,
+ long curTimeNano, Display display, int orientation,
+ int metaState) {
+
+ if (mNextNumPointers <= 0 && mLastNumPointers <= 0) {
+ return null;
+ }
+
+ final int lastNumPointers = mLastNumPointers;
+ final int nextNumPointers = mNextNumPointers;
+ if (mNextNumPointers > MAX_POINTERS) {
+ Log.w("InputDevice", "Number of pointers " + mNextNumPointers
+ + " exceeded maximum of " + MAX_POINTERS);
+ mNextNumPointers = MAX_POINTERS;
+ }
+
+ int upOrDownPointer = updatePointerIdentifiers();
+
+ final float[] reportData = mReportData;
+ final int[] rawData = mLastData;
+
+ final int numPointers = mLastNumPointers;
+
+ if (DEBUG_POINTERS) Log.v("InputDevice", "Processing "
+ + numPointers + " pointers (going from " + lastNumPointers
+ + " to " + nextNumPointers + ")");
+
+ for (int i=0; i<numPointers; i++) {
+ final int pos = i * MotionEvent.NUM_SAMPLE_DATA;
+ reportData[pos + MotionEvent.SAMPLE_X] = rawData[pos + MotionEvent.SAMPLE_X];
+ reportData[pos + MotionEvent.SAMPLE_Y] = rawData[pos + MotionEvent.SAMPLE_Y];
+ reportData[pos + MotionEvent.SAMPLE_PRESSURE] = rawData[pos + MotionEvent.SAMPLE_PRESSURE];
+ reportData[pos + MotionEvent.SAMPLE_SIZE] = rawData[pos + MotionEvent.SAMPLE_SIZE];
+ }
+
+ int action;
+ int edgeFlags = 0;
+ if (nextNumPointers != lastNumPointers) {
+ if (nextNumPointers > lastNumPointers) {
+ if (lastNumPointers == 0) {
+ action = MotionEvent.ACTION_DOWN;
+ mDownTime = curTime;
+ } else {
+ action = MotionEvent.ACTION_POINTER_DOWN
+ | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT);
+ }
+ } else {
+ if (numPointers == 1) {
+ action = MotionEvent.ACTION_UP;
+ } else {
+ action = MotionEvent.ACTION_POINTER_UP
+ | (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT);
+ }
}
-
- if (scaledY == 0) {
- edgeFlags += MotionEvent.EDGE_TOP;
- } else if (scaledY == display.getHeight() - 1.0f) {
- edgeFlags += MotionEvent.EDGE_BOTTOM;
+ currentMove = null;
+ } else {
+ action = MotionEvent.ACTION_MOVE;
+ }
+
+ final int dispW = display.getWidth()-1;
+ final int dispH = display.getHeight()-1;
+ int w = dispW;
+ int h = dispH;
+ if (orientation == Surface.ROTATION_90
+ || orientation == Surface.ROTATION_270) {
+ int tmp = w;
+ w = h;
+ h = tmp;
+ }
+
+ final AbsoluteInfo absX = device.absX;
+ final AbsoluteInfo absY = device.absY;
+ final AbsoluteInfo absPressure = device.absPressure;
+ final AbsoluteInfo absSize = device.absSize;
+ for (int i=0; i<numPointers; i++) {
+ final int j = i * MotionEvent.NUM_SAMPLE_DATA;
+
+ if (absX != null) {
+ reportData[j + MotionEvent.SAMPLE_X] =
+ ((reportData[j + MotionEvent.SAMPLE_X]-absX.minValue)
+ / absX.range) * w;
+ }
+ if (absY != null) {
+ reportData[j + MotionEvent.SAMPLE_Y] =
+ ((reportData[j + MotionEvent.SAMPLE_Y]-absY.minValue)
+ / absY.range) * h;
+ }
+ if (absPressure != null) {
+ reportData[j + MotionEvent.SAMPLE_PRESSURE] =
+ ((reportData[j + MotionEvent.SAMPLE_PRESSURE]-absPressure.minValue)
+ / (float)absPressure.range);
+ }
+ if (absSize != null) {
+ reportData[j + MotionEvent.SAMPLE_SIZE] =
+ ((reportData[j + MotionEvent.SAMPLE_SIZE]-absSize.minValue)
+ / (float)absSize.range);
}
- } else {
- scaledX *= xMoveScale;
- scaledY *= yMoveScale;
switch (orientation) {
- case Surface.ROTATION_90:
- temp = scaledX;
- scaledX = scaledY;
- scaledY = -temp;
+ case Surface.ROTATION_90: {
+ final float temp = reportData[j + MotionEvent.SAMPLE_X];
+ reportData[j + MotionEvent.SAMPLE_X] = reportData[j + MotionEvent.SAMPLE_Y];
+ reportData[j + MotionEvent.SAMPLE_Y] = w-temp;
break;
- case Surface.ROTATION_180:
- scaledX = -scaledX;
- scaledY = -scaledY;
+ }
+ case Surface.ROTATION_180: {
+ reportData[j + MotionEvent.SAMPLE_X] = w-reportData[j + MotionEvent.SAMPLE_X];
+ reportData[j + MotionEvent.SAMPLE_Y] = h-reportData[j + MotionEvent.SAMPLE_Y];
break;
- case Surface.ROTATION_270:
- temp = scaledX;
- scaledX = -scaledY;
- scaledY = temp;
+ }
+ case Surface.ROTATION_270: {
+ final float temp = reportData[j + MotionEvent.SAMPLE_X];
+ reportData[j + MotionEvent.SAMPLE_X] = h-reportData[j + MotionEvent.SAMPLE_Y];
+ reportData[j + MotionEvent.SAMPLE_Y] = temp;
break;
+ }
+ }
+ }
+
+ // We only consider the first pointer when computing the edge
+ // flags, since they are global to the event.
+ if (action == MotionEvent.ACTION_DOWN) {
+ if (reportData[MotionEvent.SAMPLE_X] <= 0) {
+ edgeFlags |= MotionEvent.EDGE_LEFT;
+ } else if (reportData[MotionEvent.SAMPLE_X] >= dispW) {
+ edgeFlags |= MotionEvent.EDGE_RIGHT;
+ }
+ if (reportData[MotionEvent.SAMPLE_Y] <= 0) {
+ edgeFlags |= MotionEvent.EDGE_TOP;
+ } else if (reportData[MotionEvent.SAMPLE_Y] >= dispH) {
+ edgeFlags |= MotionEvent.EDGE_BOTTOM;
+ }
+ }
+
+ if (currentMove != null) {
+ if (false) Log.i("InputDevice", "Adding batch x="
+ + reportData[MotionEvent.SAMPLE_X]
+ + " y=" + reportData[MotionEvent.SAMPLE_Y]
+ + " to " + currentMove);
+ currentMove.addBatch(curTime, reportData, metaState);
+ if (WindowManagerPolicy.WATCH_POINTER) {
+ Log.i("KeyInputQueue", "Updating: " + currentMove);
}
+ return null;
+ }
+
+ MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
+ curTimeNano, action, numPointers, mPointerIds, reportData,
+ metaState, xPrecision, yPrecision, device.id, edgeFlags);
+ if (action == MotionEvent.ACTION_MOVE) {
+ currentMove = me;
+ }
+
+ if (nextNumPointers < lastNumPointers) {
+ removeOldPointer(upOrDownPointer);
}
- changed = false;
- if (down != lastDown) {
- int action;
- lastDown = down;
- if (down) {
+ return me;
+ }
+
+ boolean hasMore() {
+ return mLastNumPointers != mNextNumPointers;
+ }
+
+ void finish() {
+ mNextNumPointers = mAddingPointerOffset = 0;
+ mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
+ }
+
+ MotionEvent generateRelMotion(InputDevice device, long curTime,
+ long curTimeNano, int orientation, int metaState) {
+
+ final float[] scaled = mReportData;
+
+ // For now we only support 1 pointer with relative motions.
+ scaled[MotionEvent.SAMPLE_X] = mNextData[MotionEvent.SAMPLE_X];
+ scaled[MotionEvent.SAMPLE_Y] = mNextData[MotionEvent.SAMPLE_Y];
+ scaled[MotionEvent.SAMPLE_PRESSURE] = 1.0f;
+ scaled[MotionEvent.SAMPLE_SIZE] = 0;
+ int edgeFlags = 0;
+
+ int action;
+ if (mNextNumPointers != mLastNumPointers) {
+ mNextData[MotionEvent.SAMPLE_X] =
+ mNextData[MotionEvent.SAMPLE_Y] = 0;
+ if (mNextNumPointers > 0 && mLastNumPointers == 0) {
action = MotionEvent.ACTION_DOWN;
- downTime = curTime;
- } else {
+ mDownTime = curTime;
+ } else if (mNextNumPointers == 0) {
action = MotionEvent.ACTION_UP;
+ } else {
+ action = MotionEvent.ACTION_MOVE;
}
+ mLastNumPointers = mNextNumPointers;
currentMove = null;
- if (!isAbs) {
- x = y = 0;
- }
- return MotionEvent.obtain(downTime, curTime, action,
- scaledX, scaledY, scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
} else {
- if (currentMove != null) {
- if (false) Log.i("InputDevice", "Adding batch x=" + scaledX
- + " y=" + scaledY + " to " + currentMove);
- currentMove.addBatch(curTime, scaledX, scaledY,
- scaledPressure, scaledSize, metaState);
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i("KeyInputQueue", "Updating: " + currentMove);
- }
- return null;
+ action = MotionEvent.ACTION_MOVE;
+ }
+
+ scaled[MotionEvent.SAMPLE_X] *= xMoveScale;
+ scaled[MotionEvent.SAMPLE_Y] *= yMoveScale;
+ switch (orientation) {
+ case Surface.ROTATION_90: {
+ final float temp = scaled[MotionEvent.SAMPLE_X];
+ scaled[MotionEvent.SAMPLE_X] = scaled[MotionEvent.SAMPLE_Y];
+ scaled[MotionEvent.SAMPLE_Y] = -temp;
+ break;
}
- MotionEvent me = MotionEvent.obtain(downTime, curTime,
- MotionEvent.ACTION_MOVE, scaledX, scaledY,
- scaledPressure, scaledSize, metaState,
- xPrecision, yPrecision, device.id, edgeFlags);
+ case Surface.ROTATION_180: {
+ scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_X];
+ scaled[MotionEvent.SAMPLE_Y] = -scaled[MotionEvent.SAMPLE_Y];
+ break;
+ }
+ case Surface.ROTATION_270: {
+ final float temp = scaled[MotionEvent.SAMPLE_X];
+ scaled[MotionEvent.SAMPLE_X] = -scaled[MotionEvent.SAMPLE_Y];
+ scaled[MotionEvent.SAMPLE_Y] = temp;
+ break;
+ }
+ }
+
+ if (currentMove != null) {
+ if (false) Log.i("InputDevice", "Adding batch x="
+ + scaled[MotionEvent.SAMPLE_X]
+ + " y=" + scaled[MotionEvent.SAMPLE_Y]
+ + " to " + currentMove);
+ currentMove.addBatch(curTime, scaled, metaState);
+ if (WindowManagerPolicy.WATCH_POINTER) {
+ Log.i("KeyInputQueue", "Updating: " + currentMove);
+ }
+ return null;
+ }
+
+ MotionEvent me = MotionEvent.obtainNano(mDownTime, curTime,
+ curTimeNano, action, 1, mPointerIds, scaled, metaState,
+ xPrecision, yPrecision, device.id, edgeFlags);
+ if (action == MotionEvent.ACTION_MOVE) {
currentMove = me;
- return me;
}
+ return me;
}
}
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 4198154..1da2b47 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -32,6 +32,7 @@ import org.xmlpull.v1.XmlPullParserException;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -180,6 +181,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
= new HashMap<IBinder, ClientState>();
/**
+ * Set once the system is ready to run third party code.
+ */
+ boolean mSystemReady;
+
+ /**
* Id of the currently selected input method.
*/
String mCurMethodId;
@@ -508,6 +514,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
public void systemReady() {
+ synchronized (mMethodMap) {
+ if (!mSystemReady) {
+ mSystemReady = true;
+ try {
+ startInputInnerLocked();
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Unexpected exception", e);
+ }
+ }
+ }
}
public List<InputMethodInfo> getInputMethodList() {
@@ -727,6 +743,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ return startInputInnerLocked();
+ }
+
+ InputBindResult startInputInnerLocked() {
+ if (mCurMethodId == null) {
+ return mNoBinding;
+ }
+
+ if (!mSystemReady) {
+ // If the system is not yet ready, we shouldn't be running third
+ // party code.
+ return new InputBindResult(null, mCurMethodId, mCurSeq);
+ }
+
InputMethodInfo info = mMethodMap.get(mCurMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
@@ -736,6 +766,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
+ mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.input_method_binding_label);
+ mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
if (mContext.bindService(mCurIntent, this, Context.BIND_AUTO_CREATE)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
@@ -777,17 +811,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
+ if (mCurToken == null) {
+ Log.w(TAG, "Service connected without a token!");
+ unbindCurrentMethodLocked(false);
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
+ executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
+ MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {
- if (DEBUG) Log.v(TAG, "Initiating attach with token: " + mCurToken);
+ if (DEBUG) Log.v(TAG, "Creating first session while with client "
+ + mCurClient);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
- if (mCurClient != null) {
- if (DEBUG) Log.v(TAG, "Creating first session while with client "
- + mCurClient);
- executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
- MSG_CREATE_SESSION, mCurMethod,
- new MethodCallback(mCurMethod)));
- }
+ MSG_CREATE_SESSION, mCurMethod,
+ new MethodCallback(mCurMethod)));
}
}
}
@@ -977,6 +1014,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mShowExplicitlyRequested = true;
mShowForced = true;
}
+
+ if (!mSystemReady) {
+ return false;
+ }
+
boolean res = false;
if (mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
@@ -1612,7 +1654,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
- p.println(" mScreenOn=" + mScreenOn);
+ p.println(" mSystemReady=" + mSystemReady + " mScreenOn=" + mScreenOn);
}
if (client != null) {
diff --git a/services/java/com/android/server/JournaledFile.java b/services/java/com/android/server/JournaledFile.java
new file mode 100644
index 0000000..3d1f52d
--- /dev/null
+++ b/services/java/com/android/server/JournaledFile.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import java.io.File;
+import java.io.IOException;
+
+public class JournaledFile {
+ File mReal;
+ File mTemp;
+ boolean mWriting;
+
+ public JournaledFile(File real, File temp) {
+ mReal = real;
+ mTemp = temp;
+ }
+
+ /** Returns the file for you to read.
+ * @more
+ * Prefers the real file. If it doesn't exist, uses the temp one, and then copies
+ * it to the real one. If there is both a real file and a temp one, assumes that the
+ * temp one isn't fully written and deletes it.
+ */
+ public File chooseForRead() {
+ File result;
+ if (mReal.exists()) {
+ result = mReal;
+ if (mTemp.exists()) {
+ mTemp.delete();
+ }
+ } else if (mTemp.exists()) {
+ result = mTemp;
+ mTemp.renameTo(mReal);
+ } else {
+ return mReal;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a file for you to write.
+ * @more
+ * If a write is already happening, throws. In other words, you must provide your
+ * own locking.
+ * <p>
+ * Call {@link #commit} to commit the changes, or {@link #rollback} to forget the changes.
+ */
+ public File chooseForWrite() {
+ if (mWriting) {
+ throw new IllegalStateException("uncommitted write already in progress");
+ }
+ if (!mReal.exists()) {
+ // If the real one doesn't exist, it's either because this is the first time
+ // or because something went wrong while copying them. In this case, we can't
+ // trust anything that's in temp. In order to have the chooseForRead code not
+ // use the temporary one until it's fully written, create an empty file
+ // for real, which will we'll shortly delete.
+ try {
+ mReal.createNewFile();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+
+ if (mTemp.exists()) {
+ mTemp.delete();
+ }
+ mWriting = true;
+ return mTemp;
+ }
+
+ /**
+ * Commit changes.
+ */
+ public void commit() {
+ if (!mWriting) {
+ throw new IllegalStateException("no file to commit");
+ }
+ mWriting = false;
+ mTemp.renameTo(mReal);
+ }
+
+ /**
+ * Roll back changes.
+ */
+ public void rollback() {
+ if (!mWriting) {
+ throw new IllegalStateException("no file to roll back");
+ }
+ mWriting = false;
+ mTemp.delete();
+ }
+}
diff --git a/services/java/com/android/server/KeyInputQueue.java b/services/java/com/android/server/KeyInputQueue.java
index 411cd6b..244e136 100644
--- a/services/java/com/android/server/KeyInputQueue.java
+++ b/services/java/com/android/server/KeyInputQueue.java
@@ -18,10 +18,13 @@ package com.android.server;
import android.content.Context;
import android.content.res.Configuration;
-import android.os.SystemClock;
+import android.os.Environment;
+import android.os.LatencyTimer;
import android.os.PowerManager;
+import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
+import android.util.Xml;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -29,10 +32,31 @@ import android.view.RawInputEvent;
import android.view.Surface;
import android.view.WindowManagerPolicy;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
public abstract class KeyInputQueue {
static final String TAG = "KeyInputQueue";
- SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+ static final boolean DEBUG_VIRTUAL_KEYS = false;
+ static final boolean DEBUG_POINTERS = false;
+
+ private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
+
+ final SparseArray<InputDevice> mDevices = new SparseArray<InputDevice>();
+ final SparseArray<InputDevice> mIgnoredDevices = new SparseArray<InputDevice>();
+ final ArrayList<VirtualKey> mVirtualKeys = new ArrayList<VirtualKey>();
+ final HapticFeedbackCallback mHapticFeedbackCallback;
int mGlobalMetaState = 0;
boolean mHaveGlobalMetaState = false;
@@ -43,10 +67,14 @@ public abstract class KeyInputQueue {
int mCacheCount;
Display mDisplay = null;
+ int mDisplayWidth;
+ int mDisplayHeight;
int mOrientation = Surface.ROTATION_0;
int[] mKeyRotationMap = null;
+ VirtualKey mPressedVirtualKey = null;
+
PowerManager.WakeLock mWakeLock;
static final int[] KEY_90_MAP = new int[] {
@@ -73,14 +101,21 @@ public abstract class KeyInputQueue {
public static final int FILTER_REMOVE = 0;
public static final int FILTER_KEEP = 1;
public static final int FILTER_ABORT = -1;
-
+
+ private static final boolean MEASURE_LATENCY = false;
+ private LatencyTimer lt;
+
public interface FilterCallback {
int filterEvent(QueuedEvent ev);
}
+ public interface HapticFeedbackCallback {
+ void virtualKeyFeedback(KeyEvent event);
+ }
+
static class QueuedEvent {
InputDevice inputDevice;
- long when;
+ long whenNano;
int flags; // From the raw event
int classType; // One of the class constants in InputEvent
Object event;
@@ -88,7 +123,7 @@ public abstract class KeyInputQueue {
void copyFrom(QueuedEvent that) {
this.inputDevice = that.inputDevice;
- this.when = that.when;
+ this.whenNano = that.whenNano;
this.flags = that.flags;
this.classType = that.classType;
this.event = that.event;
@@ -106,7 +141,144 @@ public abstract class KeyInputQueue {
QueuedEvent next;
}
- KeyInputQueue(Context context) {
+ /**
+ * A key that exists as a part of the touch-screen, outside of the normal
+ * display area of the screen.
+ */
+ static class VirtualKey {
+ int scancode;
+ int centerx;
+ int centery;
+ int width;
+ int height;
+
+ int hitLeft;
+ int hitTop;
+ int hitRight;
+ int hitBottom;
+
+ InputDevice lastDevice;
+ int lastKeycode;
+
+ boolean checkHit(int x, int y) {
+ return (x >= hitLeft && x <= hitRight
+ && y >= hitTop && y <= hitBottom);
+ }
+
+ void computeHitRect(InputDevice dev, int dw, int dh) {
+ if (dev == lastDevice) {
+ return;
+ }
+
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "computeHitRect for " + scancode
+ + ": dev=" + dev + " absX=" + dev.absX + " absY=" + dev.absY);
+
+ lastDevice = dev;
+
+ int minx = dev.absX.minValue;
+ int maxx = dev.absX.maxValue;
+
+ int halfw = width/2;
+ int left = centerx - halfw;
+ int right = centerx + halfw;
+ hitLeft = minx + ((left*maxx-minx)/dw);
+ hitRight = minx + ((right*maxx-minx)/dw);
+
+ int miny = dev.absY.minValue;
+ int maxy = dev.absY.maxValue;
+
+ int halfh = height/2;
+ int top = centery - halfh;
+ int bottom = centery + halfh;
+ hitTop = miny + ((top*maxy-miny)/dh);
+ hitBottom = miny + ((bottom*maxy-miny)/dh);
+ }
+ }
+
+ private void readVirtualKeys(String deviceName) {
+ try {
+ FileInputStream fis = new FileInputStream(
+ "/sys/board_properties/virtualkeys." + deviceName);
+ InputStreamReader isr = new InputStreamReader(fis);
+ BufferedReader br = new BufferedReader(isr);
+ String str = br.readLine();
+ if (str != null) {
+ String[] it = str.split(":");
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "***** VIRTUAL KEYS: " + it);
+ final int N = it.length-6;
+ for (int i=0; i<=N; i+=6) {
+ if (!"0x01".equals(it[i])) {
+ Log.w(TAG, "Unknown virtual key type at elem #" + i
+ + ": " + it[i]);
+ continue;
+ }
+ try {
+ VirtualKey sb = new VirtualKey();
+ sb.scancode = Integer.parseInt(it[i+1]);
+ sb.centerx = Integer.parseInt(it[i+2]);
+ sb.centery = Integer.parseInt(it[i+3]);
+ sb.width = Integer.parseInt(it[i+4]);
+ sb.height = Integer.parseInt(it[i+5]);
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Virtual key "
+ + sb.scancode + ": center=" + sb.centerx + ","
+ + sb.centery + " size=" + sb.width + "x"
+ + sb.height);
+ mVirtualKeys.add(sb);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Bad number at region " + i + " in: "
+ + str, e);
+ }
+ }
+ }
+ br.close();
+ } catch (FileNotFoundException e) {
+ Log.i(TAG, "No virtual keys found");
+ } catch (IOException e) {
+ Log.w(TAG, "Error reading virtual keys", e);
+ }
+ }
+
+ private void readExcludedDevices() {
+ // Read partner-provided list of excluded input devices
+ XmlPullParser parser = null;
+ // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system".
+ File confFile = new File(Environment.getRootDirectory(), EXCLUDED_DEVICES_PATH);
+ FileReader confreader = null;
+ try {
+ confreader = new FileReader(confFile);
+ parser = Xml.newPullParser();
+ parser.setInput(confreader);
+ XmlUtils.beginDocument(parser, "devices");
+
+ while (true) {
+ XmlUtils.nextElement(parser);
+ if (!"device".equals(parser.getName())) {
+ break;
+ }
+ String name = parser.getAttributeValue(null, "name");
+ if (name != null) {
+ Log.d(TAG, "addExcludedDevice " + name);
+ addExcludedDevice(name);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // It's ok if the file does not exist.
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while parsing '" + confFile.getAbsolutePath() + "'", e);
+ } finally {
+ try { if (confreader != null) confreader.close(); } catch (IOException e) { }
+ }
+ }
+
+ KeyInputQueue(Context context, HapticFeedbackCallback hapticFeedbackCallback) {
+ if (MEASURE_LATENCY) {
+ lt = new LatencyTimer(100, 1000);
+ }
+
+ mHapticFeedbackCallback = hapticFeedbackCallback;
+
+ readExcludedDevices();
+
PowerManager pm = (PowerManager)context.getSystemService(
Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
@@ -123,6 +295,12 @@ public abstract class KeyInputQueue {
public void setDisplay(Display display) {
mDisplay = display;
+
+ // We assume at this point that the display dimensions reflect the
+ // natural, unrotated display. We will perform hit tests for soft
+ // buttons based on that display.
+ mDisplayWidth = display.getWidth();
+ mDisplayHeight = display.getHeight();
}
public void getInputConfiguration(Configuration config) {
@@ -149,6 +327,10 @@ public abstract class KeyInputQueue {
config.navigation
= Configuration.NAVIGATION_TRACKBALL;
//Log.i("foo", "***** HAVE TRACKBALL!");
+ } else if ((d.classes&RawInputEvent.CLASS_DPAD) != 0) {
+ config.navigation
+ = Configuration.NAVIGATION_DPAD;
+ //Log.i("foo", "***** HAVE DPAD!");
}
}
}
@@ -157,6 +339,7 @@ public abstract class KeyInputQueue {
public static native String getDeviceName(int deviceId);
public static native int getDeviceClasses(int deviceId);
+ public static native void addExcludedDevice(String deviceName);
public static native boolean getAbsoluteInfo(int deviceId, int axis,
InputDevice.AbsoluteInfo outInfo);
public static native int getSwitchState(int sw);
@@ -165,6 +348,7 @@ public abstract class KeyInputQueue {
public static native int getScancodeState(int deviceId, int sw);
public static native int getKeycodeState(int sw);
public static native int getKeycodeState(int deviceId, int sw);
+ public static native int scancodeToKeycode(int deviceId, int scancode);
public static native boolean hasKeys(int[] keycodes, boolean[] keyExists);
public static KeyEvent newKeyEvent(InputDevice device, long downTime,
@@ -181,12 +365,13 @@ public abstract class KeyInputQueue {
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
+ Log.d(TAG, "InputDeviceReader.run()");
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
- try {
- RawInputEvent ev = new RawInputEvent();
- while (true) {
+ RawInputEvent ev = new RawInputEvent();
+ while (true) {
+ try {
InputDevice di;
// block, doesn't release the monitor
@@ -207,23 +392,50 @@ public abstract class KeyInputQueue {
if (ev.type == RawInputEvent.EV_DEVICE_ADDED) {
synchronized (mFirst) {
di = newInputDevice(ev.deviceId);
- mDevices.put(ev.deviceId, di);
- configChanged = true;
+ if (di.classes != 0) {
+ // If this device is some kind of input class,
+ // we care about it.
+ mDevices.put(ev.deviceId, di);
+ if ((di.classes & RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+ readVirtualKeys(di.name);
+ }
+ // The configuration may have changed because
+ // of this device.
+ configChanged = true;
+ } else {
+ // We won't do anything with this device.
+ mIgnoredDevices.put(ev.deviceId, di);
+ Log.i(TAG, "Ignoring non-input device: id=0x"
+ + Integer.toHexString(di.id)
+ + ", name=" + di.name);
+ }
}
} else if (ev.type == RawInputEvent.EV_DEVICE_REMOVED) {
synchronized (mFirst) {
- Log.i(TAG, "Device removed: id=0x"
- + Integer.toHexString(ev.deviceId));
+ if (false) {
+ Log.i(TAG, "Device removed: id=0x"
+ + Integer.toHexString(ev.deviceId));
+ }
di = mDevices.get(ev.deviceId);
if (di != null) {
mDevices.delete(ev.deviceId);
+ // The configuration may have changed because
+ // of this device.
configChanged = true;
+ } else if ((di=mIgnoredDevices.get(ev.deviceId)) != null) {
+ mIgnoredDevices.remove(ev.deviceId);
} else {
- Log.w(TAG, "Bad device id: " + ev.deviceId);
+ Log.w(TAG, "Removing bad device id: "
+ + Integer.toHexString(ev.deviceId));
+ continue;
}
}
} else {
di = getInputDevice(ev.deviceId);
+ if (di == null) {
+ // This may be some junk from an ignored device.
+ continue;
+ }
// first crack at it
send = preprocessEvent(di, ev);
@@ -235,13 +447,9 @@ public abstract class KeyInputQueue {
}
}
- if (di == null) {
- continue;
- }
-
if (configChanged) {
synchronized (mFirst) {
- addLocked(di, SystemClock.uptimeMillis(), 0,
+ addLocked(di, System.nanoTime(), 0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
@@ -256,6 +464,7 @@ public abstract class KeyInputQueue {
// timebase as SystemClock.uptimeMillis().
//curTime = gotOne ? ev.when : SystemClock.uptimeMillis();
final long curTime = SystemClock.uptimeMillis();
+ final long curTimeNano = System.nanoTime();
//Log.i(TAG, "curTime=" + curTime + ", systemClock=" + SystemClock.uptimeMillis());
final int classes = di.classes;
@@ -271,44 +480,92 @@ public abstract class KeyInputQueue {
boolean down;
if (ev.value != 0) {
down = true;
- di.mDownTime = curTime;
+ di.mKeyDownTime = curTime;
} else {
down = false;
}
int keycode = rotateKeyCodeLocked(ev.keycode);
- addLocked(di, curTime, ev.flags,
+ addLocked(di, curTimeNano, ev.flags,
RawInputEvent.CLASS_KEYBOARD,
- newKeyEvent(di, di.mDownTime, curTime, down,
+ newKeyEvent(di, di.mKeyDownTime, curTime, down,
keycode, 0, scancode,
((ev.flags & WindowManagerPolicy.FLAG_WOKE_HERE) != 0)
? KeyEvent.FLAG_WOKE_HERE : 0));
} else if (ev.type == RawInputEvent.EV_KEY) {
if (ev.scancode == RawInputEvent.BTN_TOUCH &&
- (classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+ (classes&(RawInputEvent.CLASS_TOUCHSCREEN
+ |RawInputEvent.CLASS_TOUCHSCREEN_MT))
+ == RawInputEvent.CLASS_TOUCHSCREEN) {
di.mAbs.changed = true;
- di.mAbs.down = ev.value != 0;
- }
- if (ev.scancode == RawInputEvent.BTN_MOUSE &&
+ di.mAbs.mDown[0] = ev.value != 0;
+ } else if (ev.scancode == RawInputEvent.BTN_2 &&
+ (classes&(RawInputEvent.CLASS_TOUCHSCREEN
+ |RawInputEvent.CLASS_TOUCHSCREEN_MT))
+ == RawInputEvent.CLASS_TOUCHSCREEN) {
+ di.mAbs.changed = true;
+ di.mAbs.mDown[1] = ev.value != 0;
+ } else if (ev.scancode == RawInputEvent.BTN_MOUSE &&
(classes&RawInputEvent.CLASS_TRACKBALL) != 0) {
di.mRel.changed = true;
- di.mRel.down = ev.value != 0;
+ di.mRel.mNextNumPointers = ev.value != 0 ? 1 : 0;
send = true;
}
} else if (ev.type == RawInputEvent.EV_ABS &&
+ (classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
+ if (ev.scancode == RawInputEvent.ABS_MT_TOUCH_MAJOR) {
+ di.mAbs.changed = true;
+ di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ + MotionEvent.SAMPLE_PRESSURE] = ev.value;
+ } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_X) {
+ di.mAbs.changed = true;
+ di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ + MotionEvent.SAMPLE_X] = ev.value;
+ if (DEBUG_POINTERS) Log.v(TAG, "MT @"
+ + di.mAbs.mAddingPointerOffset
+ + " X:" + ev.value);
+ } else if (ev.scancode == RawInputEvent.ABS_MT_POSITION_Y) {
+ di.mAbs.changed = true;
+ di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ + MotionEvent.SAMPLE_Y] = ev.value;
+ if (DEBUG_POINTERS) Log.v(TAG, "MT @"
+ + di.mAbs.mAddingPointerOffset
+ + " Y:" + ev.value);
+ } else if (ev.scancode == RawInputEvent.ABS_MT_WIDTH_MAJOR) {
+ di.mAbs.changed = true;
+ di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ + MotionEvent.SAMPLE_SIZE] = ev.value;
+ }
+
+ } else if (ev.type == RawInputEvent.EV_ABS &&
(classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+ // Finger 1
if (ev.scancode == RawInputEvent.ABS_X) {
di.mAbs.changed = true;
- di.mAbs.x = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_X] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_Y) {
di.mAbs.changed = true;
- di.mAbs.y = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_Y] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_PRESSURE) {
di.mAbs.changed = true;
- di.mAbs.pressure = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_PRESSURE] = ev.value;
+ di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ + MotionEvent.SAMPLE_PRESSURE] = ev.value;
} else if (ev.scancode == RawInputEvent.ABS_TOOL_WIDTH) {
di.mAbs.changed = true;
- di.mAbs.size = ev.value;
+ di.curTouchVals[MotionEvent.SAMPLE_SIZE] = ev.value;
+ di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ + MotionEvent.SAMPLE_SIZE] = ev.value;
+
+ // Finger 2
+ } else if (ev.scancode == RawInputEvent.ABS_HAT0X) {
+ di.mAbs.changed = true;
+ di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ + MotionEvent.SAMPLE_X] = ev.value;
+ } else if (ev.scancode == RawInputEvent.ABS_HAT0Y) {
+ di.mAbs.changed = true;
+ di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+ + MotionEvent.SAMPLE_Y] = ev.value;
}
} else if (ev.type == RawInputEvent.EV_REL &&
@@ -316,50 +573,277 @@ public abstract class KeyInputQueue {
// Add this relative movement into our totals.
if (ev.scancode == RawInputEvent.REL_X) {
di.mRel.changed = true;
- di.mRel.x += ev.value;
+ di.mRel.mNextData[MotionEvent.SAMPLE_X] += ev.value;
} else if (ev.scancode == RawInputEvent.REL_Y) {
di.mRel.changed = true;
- di.mRel.y += ev.value;
+ di.mRel.mNextData[MotionEvent.SAMPLE_Y] += ev.value;
}
}
- if (send || ev.type == RawInputEvent.EV_SYN) {
+ if (ev.type == RawInputEvent.EV_SYN
+ && ev.scancode == RawInputEvent.SYN_MT_REPORT
+ && di.mAbs != null) {
+ di.mAbs.changed = true;
+ if (di.mAbs.mNextData[MotionEvent.SAMPLE_PRESSURE] > 0) {
+ // If the value is <= 0, the pointer is not
+ // down, so keep it in the count.
+
+ if (di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+ + MotionEvent.SAMPLE_PRESSURE] != 0) {
+ final int num = di.mAbs.mNextNumPointers+1;
+ di.mAbs.mNextNumPointers = num;
+ if (DEBUG_POINTERS) Log.v(TAG,
+ "MT_REPORT: now have " + num + " pointers");
+ final int newOffset = (num <= InputDevice.MAX_POINTERS)
+ ? (num * MotionEvent.NUM_SAMPLE_DATA)
+ : (InputDevice.MAX_POINTERS *
+ MotionEvent.NUM_SAMPLE_DATA);
+ di.mAbs.mAddingPointerOffset = newOffset;
+ di.mAbs.mNextData[newOffset
+ + MotionEvent.SAMPLE_PRESSURE] = 0;
+ } else {
+ if (DEBUG_POINTERS) Log.v(TAG, "MT_REPORT: no pointer");
+ }
+ }
+ } else if (send || (ev.type == RawInputEvent.EV_SYN
+ && ev.scancode == RawInputEvent.SYN_REPORT)) {
if (mDisplay != null) {
if (!mHaveGlobalMetaState) {
computeGlobalMetaStateLocked();
}
MotionEvent me;
- me = di.mAbs.generateMotion(di, curTime, true,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Absolute: x=" + di.mAbs.x
- + " y=" + di.mAbs.y + " ev=" + me);
- if (me != null) {
- if (WindowManagerPolicy.WATCH_POINTER) {
- Log.i(TAG, "Enqueueing: " + me);
+
+ InputDevice.MotionState ms = di.mAbs;
+ if (ms.changed) {
+ ms.changed = false;
+
+ if ((classes&(RawInputEvent.CLASS_TOUCHSCREEN
+ |RawInputEvent.CLASS_TOUCHSCREEN_MT))
+ == RawInputEvent.CLASS_TOUCHSCREEN) {
+ ms.mNextNumPointers = 0;
+ if (ms.mDown[0]) {
+ System.arraycopy(di.curTouchVals, 0,
+ ms.mNextData, 0,
+ MotionEvent.NUM_SAMPLE_DATA);
+ ms.mNextNumPointers++;
+ }
+ if (ms.mDown[1]) {
+ System.arraycopy(di.curTouchVals,
+ MotionEvent.NUM_SAMPLE_DATA,
+ ms.mNextData,
+ ms.mNextNumPointers
+ * MotionEvent.NUM_SAMPLE_DATA,
+ MotionEvent.NUM_SAMPLE_DATA);
+ ms.mNextNumPointers++;
+ }
}
- addLocked(di, curTime, ev.flags,
- RawInputEvent.CLASS_TOUCHSCREEN, me);
+
+ boolean doMotion = !monitorVirtualKey(di,
+ ev, curTime, curTimeNano);
+
+ if (doMotion && ms.mNextNumPointers > 0
+ && ms.mLastNumPointers == 0) {
+ doMotion = !generateVirtualKeyDown(di,
+ ev, curTime, curTimeNano);
+ }
+
+ if (doMotion) {
+ // XXX Need to be able to generate
+ // multiple events here, for example
+ // if two fingers change up/down state
+ // at the same time.
+ do {
+ me = ms.generateAbsMotion(di, curTime,
+ curTimeNano, mDisplay,
+ mOrientation, mGlobalMetaState);
+ if (false) Log.v(TAG, "Absolute: x="
+ + di.mAbs.mNextData[MotionEvent.SAMPLE_X]
+ + " y="
+ + di.mAbs.mNextData[MotionEvent.SAMPLE_Y]
+ + " ev=" + me);
+ if (me != null) {
+ if (WindowManagerPolicy.WATCH_POINTER) {
+ Log.i(TAG, "Enqueueing: " + me);
+ }
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_TOUCHSCREEN, me);
+ }
+ } while (ms.hasMore());
+ }
+
+ ms.finish();
}
- me = di.mRel.generateMotion(di, curTime, false,
- mDisplay, mOrientation, mGlobalMetaState);
- if (false) Log.v(TAG, "Relative: x=" + di.mRel.x
- + " y=" + di.mRel.y + " ev=" + me);
- if (me != null) {
- addLocked(di, curTime, ev.flags,
- RawInputEvent.CLASS_TRACKBALL, me);
+
+ ms = di.mRel;
+ if (ms.changed) {
+ ms.changed = false;
+
+ me = ms.generateRelMotion(di, curTime,
+ curTimeNano,
+ mOrientation, mGlobalMetaState);
+ if (false) Log.v(TAG, "Relative: x="
+ + di.mRel.mNextData[MotionEvent.SAMPLE_X]
+ + " y="
+ + di.mRel.mNextData[MotionEvent.SAMPLE_Y]
+ + " ev=" + me);
+ if (me != null) {
+ addLocked(di, curTimeNano, ev.flags,
+ RawInputEvent.CLASS_TRACKBALL, me);
+ }
+
+ ms.finish();
}
}
}
}
+
+ } catch (RuntimeException exc) {
+ Log.e(TAG, "InputReaderThread uncaught exception", exc);
}
}
- catch (RuntimeException exc) {
- Log.e(TAG, "InputReaderThread uncaught exception", exc);
- }
}
};
+ private boolean isInsideDisplay(InputDevice dev) {
+ final InputDevice.AbsoluteInfo absx = dev.absX;
+ final InputDevice.AbsoluteInfo absy = dev.absY;
+ final InputDevice.MotionState absm = dev.mAbs;
+ if (absx == null || absy == null || absm == null) {
+ return true;
+ }
+
+ if (absm.mNextData[MotionEvent.SAMPLE_X] >= absx.minValue
+ && absm.mNextData[MotionEvent.SAMPLE_X] <= absx.maxValue
+ && absm.mNextData[MotionEvent.SAMPLE_Y] >= absy.minValue
+ && absm.mNextData[MotionEvent.SAMPLE_Y] <= absy.maxValue) {
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Input ("
+ + absm.mNextData[MotionEvent.SAMPLE_X]
+ + "," + absm.mNextData[MotionEvent.SAMPLE_Y]
+ + ") inside of display");
+ return true;
+ }
+
+ return false;
+ }
+
+ private VirtualKey findVirtualKey(InputDevice dev) {
+ final int N = mVirtualKeys.size();
+ if (N <= 0) {
+ return null;
+ }
+
+ final InputDevice.MotionState absm = dev.mAbs;
+ for (int i=0; i<N; i++) {
+ VirtualKey sb = mVirtualKeys.get(i);
+ sb.computeHitRect(dev, mDisplayWidth, mDisplayHeight);
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit test ("
+ + absm.mNextData[MotionEvent.SAMPLE_X] + ","
+ + absm.mNextData[MotionEvent.SAMPLE_Y] + ") in code "
+ + sb.scancode + " - (" + sb.hitLeft
+ + "," + sb.hitTop + ")-(" + sb.hitRight + ","
+ + sb.hitBottom + ")");
+ if (sb.checkHit(absm.mNextData[MotionEvent.SAMPLE_X],
+ absm.mNextData[MotionEvent.SAMPLE_Y])) {
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Hit!");
+ return sb;
+ }
+ }
+
+ return null;
+ }
+
+ private boolean generateVirtualKeyDown(InputDevice di, RawInputEvent ev,
+ long curTime, long curTimeNano) {
+ if (isInsideDisplay(di)) {
+ // Didn't consume event.
+ return false;
+ }
+
+
+ VirtualKey vk = findVirtualKey(di);
+ if (vk != null) {
+ final InputDevice.MotionState ms = di.mAbs;
+ mPressedVirtualKey = vk;
+ vk.lastKeycode = scancodeToKeycode(di.id, vk.scancode);
+ ms.mLastNumPointers = ms.mNextNumPointers;
+ di.mKeyDownTime = curTime;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG,
+ "Generate key down for: " + vk.scancode
+ + " (keycode=" + vk.lastKeycode + ")");
+ KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, true,
+ vk.lastKeycode, 0, vk.scancode,
+ KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
+ addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
+ event);
+ }
+
+ // We always consume the event, even if we didn't
+ // generate a key event. There are two reasons for
+ // this: to avoid spurious touches when holding
+ // the edges of the device near the touchscreen,
+ // and to avoid reporting events if there are virtual
+ // keys on the touchscreen outside of the display
+ // area.
+ // Note that for all of this we are only looking at the
+ // first pointer, since what we are handling here is the
+ // first pointer going down, and this is the coordinate
+ // that will be used to dispatch the event.
+ if (false) {
+ final InputDevice.AbsoluteInfo absx = di.absX;
+ final InputDevice.AbsoluteInfo absy = di.absY;
+ final InputDevice.MotionState absm = di.mAbs;
+ Log.v(TAG, "Rejecting ("
+ + absm.mNextData[MotionEvent.SAMPLE_X] + ","
+ + absm.mNextData[MotionEvent.SAMPLE_Y] + "): outside of ("
+ + absx.minValue + "," + absy.minValue
+ + ")-(" + absx.maxValue + ","
+ + absx.maxValue + ")");
+ }
+ return true;
+ }
+
+ private boolean monitorVirtualKey(InputDevice di, RawInputEvent ev,
+ long curTime, long curTimeNano) {
+ VirtualKey vk = mPressedVirtualKey;
+ if (vk == null) {
+ return false;
+ }
+
+ final InputDevice.MotionState ms = di.mAbs;
+ if (ms.mNextNumPointers <= 0) {
+ mPressedVirtualKey = null;
+ ms.mLastNumPointers = 0;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Generate key up for: " + vk.scancode);
+ KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
+ vk.lastKeycode, 0, vk.scancode,
+ KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
+ addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
+ event);
+ return true;
+
+ } else if (isInsideDisplay(di)) {
+ // Whoops the pointer has moved into
+ // the display area! Cancel the
+ // virtual key and start a pointer
+ // motion.
+ mPressedVirtualKey = null;
+ if (DEBUG_VIRTUAL_KEYS) Log.v(TAG, "Cancel key up for: " + vk.scancode);
+ KeyEvent event = newKeyEvent(di, di.mKeyDownTime, curTime, false,
+ vk.lastKeycode, 0, vk.scancode,
+ KeyEvent.FLAG_CANCELED | KeyEvent.FLAG_VIRTUAL_HARD_KEY);
+ mHapticFeedbackCallback.virtualKeyFeedback(event);
+ addLocked(di, curTimeNano, ev.flags, RawInputEvent.CLASS_KEYBOARD,
+ event);
+ ms.mLastNumPointers = 0;
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Returns a new meta state for the given keys and old state.
*/
@@ -506,8 +990,8 @@ public abstract class KeyInputQueue {
if (ev.event == ev.inputDevice.mRel.currentMove) {
if (false) Log.i(TAG, "Detach rel " + ev.event);
ev.inputDevice.mRel.currentMove = null;
- ev.inputDevice.mRel.x = 0;
- ev.inputDevice.mRel.y = 0;
+ ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_X] = 0;
+ ev.inputDevice.mRel.mNextData[MotionEvent.SAMPLE_Y] = 0;
}
recycleLocked(ev);
}
@@ -530,7 +1014,7 @@ public abstract class KeyInputQueue {
}
}
- private QueuedEvent obtainLocked(InputDevice device, long when,
+ private QueuedEvent obtainLocked(InputDevice device, long whenNano,
int flags, int classType, Object event) {
QueuedEvent ev;
if (mCacheCount == 0) {
@@ -542,7 +1026,7 @@ public abstract class KeyInputQueue {
mCacheCount--;
}
ev.inputDevice = device;
- ev.when = when;
+ ev.whenNano = whenNano;
ev.flags = flags;
ev.classType = classType;
ev.event = event;
@@ -561,13 +1045,13 @@ public abstract class KeyInputQueue {
}
}
- private void addLocked(InputDevice device, long when, int flags,
+ private void addLocked(InputDevice device, long whenNano, int flags,
int classType, Object event) {
boolean poke = mFirst.next == mLast;
- QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
+ QueuedEvent ev = obtainLocked(device, whenNano, flags, classType, event);
QueuedEvent p = mLast.prev;
- while (p != mFirst && ev.when < p.when) {
+ while (p != mFirst && ev.whenNano < p.whenNano) {
p = p.prev;
}
@@ -578,31 +1062,48 @@ public abstract class KeyInputQueue {
ev.inQueue = true;
if (poke) {
+ long time;
+ if (MEASURE_LATENCY) {
+ time = System.nanoTime();
+ }
mFirst.notify();
mWakeLock.acquire();
+ if (MEASURE_LATENCY) {
+ lt.sample("1 addLocked-queued event ", System.nanoTime() - time);
+ }
}
}
private InputDevice newInputDevice(int deviceId) {
int classes = getDeviceClasses(deviceId);
String name = getDeviceName(deviceId);
- Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
- + ", name=" + name
- + ", classes=" + Integer.toHexString(classes));
- InputDevice.AbsoluteInfo absX;
- InputDevice.AbsoluteInfo absY;
- InputDevice.AbsoluteInfo absPressure;
- InputDevice.AbsoluteInfo absSize;
- if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
- absX = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_X, "X");
- absY = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_Y, "Y");
- absPressure = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_PRESSURE, "Pressure");
- absSize = loadAbsoluteInfo(deviceId, RawInputEvent.ABS_TOOL_WIDTH, "Size");
- } else {
- absX = null;
- absY = null;
- absPressure = null;
- absSize = null;
+ InputDevice.AbsoluteInfo absX = null;
+ InputDevice.AbsoluteInfo absY = null;
+ InputDevice.AbsoluteInfo absPressure = null;
+ InputDevice.AbsoluteInfo absSize = null;
+ if (classes != 0) {
+ Log.i(TAG, "Device added: id=0x" + Integer.toHexString(deviceId)
+ + ", name=" + name
+ + ", classes=" + Integer.toHexString(classes));
+ if ((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT) != 0) {
+ absX = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_MT_POSITION_X, "X");
+ absY = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_MT_POSITION_Y, "Y");
+ absPressure = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_MT_TOUCH_MAJOR, "Pressure");
+ absSize = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_MT_WIDTH_MAJOR, "Size");
+ } else if ((classes&RawInputEvent.CLASS_TOUCHSCREEN) != 0) {
+ absX = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_X, "X");
+ absY = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_Y, "Y");
+ absPressure = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_PRESSURE, "Pressure");
+ absSize = loadAbsoluteInfo(deviceId,
+ RawInputEvent.ABS_TOOL_WIDTH, "Size");
+ }
}
return new InputDevice(deviceId, classes, name, absX, absY, absPressure, absSize);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3f268c9..af60556 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,12 +16,7 @@
package com.android.server;
-import java.io.BufferedReader;
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
@@ -31,7 +26,6 @@ import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
-import java.util.regex.Pattern;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -49,6 +43,7 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
import android.location.ILocationManager;
import android.location.ILocationProvider;
+import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
@@ -63,7 +58,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -71,6 +65,7 @@ import android.util.PrintWriterPrinter;
import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.LocationProviderProxy;
import com.android.internal.location.MockProvider;
+import com.android.internal.location.GpsNetInitiatedHandler;
/**
* The service class that manages LocationProviders and issues location
@@ -82,17 +77,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private static final String TAG = "LocationManagerService";
private static final boolean LOCAL_LOGV = false;
- // Minimum time interval between last known location writes, in milliseconds.
- private static final long MIN_LAST_KNOWN_LOCATION_TIME = 60L * 1000L;
-
- // Max time to hold wake lock for, in milliseconds.
- private static final long MAX_TIME_FOR_WAKE_LOCK = 60 * 1000L;
-
// The last time a location was written, by provider name.
private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
- private static final Pattern PATTERN_COMMA = Pattern.compile(",");
-
private static final String ACCESS_FINE_LOCATION =
android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
@@ -118,6 +105,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private final Context mContext;
private IGeocodeProvider mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
+ private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
// Cache the real providers for use in addTestProvider() and removeTestProvider()
@@ -413,97 +401,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- private Location readLastKnownLocationLocked(String provider) {
- Location location = null;
- String s = null;
- try {
- File f = new File(LocationManager.SYSTEM_DIR + "/location."
- + provider);
- if (!f.exists()) {
- return null;
- }
- BufferedReader reader = new BufferedReader(new FileReader(f), 256);
- s = reader.readLine();
- } catch (IOException e) {
- Log.w(TAG, "Unable to read last known location", e);
- }
-
- if (s == null) {
- return null;
- }
- try {
- String[] tokens = PATTERN_COMMA.split(s);
- int idx = 0;
- long time = Long.parseLong(tokens[idx++]);
- double latitude = Double.parseDouble(tokens[idx++]);
- double longitude = Double.parseDouble(tokens[idx++]);
- double altitude = Double.parseDouble(tokens[idx++]);
- float bearing = Float.parseFloat(tokens[idx++]);
- float speed = Float.parseFloat(tokens[idx++]);
-
- location = new Location(provider);
- location.setTime(time);
- location.setLatitude(latitude);
- location.setLongitude(longitude);
- location.setAltitude(altitude);
- location.setBearing(bearing);
- location.setSpeed(speed);
- } catch (NumberFormatException nfe) {
- Log.e(TAG, "NumberFormatException reading last known location", nfe);
- return null;
- }
-
- return location;
- }
-
- private void writeLastKnownLocationLocked(String provider,
- Location location) {
- long now = SystemClock.elapsedRealtime();
- Long last = mLastWriteTime.get(provider);
- if ((last != null)
- && (now - last.longValue() < MIN_LAST_KNOWN_LOCATION_TIME)) {
- return;
- }
- mLastWriteTime.put(provider, now);
-
- StringBuilder sb = new StringBuilder(100);
- sb.append(location.getTime());
- sb.append(',');
- sb.append(location.getLatitude());
- sb.append(',');
- sb.append(location.getLongitude());
- sb.append(',');
- sb.append(location.getAltitude());
- sb.append(',');
- sb.append(location.getBearing());
- sb.append(',');
- sb.append(location.getSpeed());
-
- FileWriter writer = null;
- try {
- File d = new File(LocationManager.SYSTEM_DIR);
- if (!d.exists()) {
- if (!d.mkdirs()) {
- Log.w(TAG, "Unable to create directory to write location");
- return;
- }
- }
- File f = new File(LocationManager.SYSTEM_DIR + "/location." + provider);
- writer = new FileWriter(f);
- writer.write(sb.toString());
- } catch (IOException e) {
- Log.w(TAG, "Unable to write location", e);
- } finally {
- if (writer != null) {
- try {
- writer.close();
- } catch (IOException e) {
- Log.w(TAG, "Exception closing file", e);
- }
- }
- }
- }
-
private void addProvider(LocationProviderProxy provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
@@ -541,6 +438,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Create a gps location provider
GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
mGpsStatusProvider = provider.getGpsStatusProvider();
+ mNetInitiatedListener = provider.getNetInitiatedListener();
LocationProviderProxy proxy = new LocationProviderProxy(LocationManager.GPS_PROVIDER, provider);
addProvider(proxy);
mGpsLocationProvider = proxy;
@@ -850,7 +748,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
void disposeLocked() {
ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
- records.remove(this);
+ if (records != null) {
+ records.remove(this);
+ }
}
@Override
@@ -869,15 +769,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
}
-
- /**
- * Calls dispose().
- */
- @Override protected void finalize() {
- synchronized (mLock) {
- disposeLocked();
- }
- }
}
private Receiver getReceiver(ILocationListener listener) {
@@ -1108,6 +999,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ if (provider == null) {
+ // throw NullPointerException to remain compatible with previous implementation
+ throw new NullPointerException();
+ }
+
// first check for permission to the provider
checkPermissionsSafe(provider);
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
@@ -1118,7 +1014,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
LocationProviderProxy proxy = mProvidersByName.get(provider);
- if (provider == null) {
+ if (proxy == null) {
return false;
}
@@ -1126,6 +1022,22 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ public boolean sendNiResponse(int notifId, int userResponse)
+ {
+ if (Binder.getCallingUid() != Process.myUid()) {
+ throw new SecurityException(
+ "calling sendNiResponse from outside of the system is not allowed");
+ }
+ try {
+ return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
+ }
+ catch (RemoteException e)
+ {
+ Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
+ return false;
+ }
+ }
+
class ProximityAlert {
final int mUid;
final double mLatitude;
@@ -1487,16 +1399,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return null;
}
- Location location = mLastKnownLocation.get(provider);
- if (location == null) {
- // Get the persistent last known location for the provider
- location = readLastKnownLocationLocked(provider);
- if (location != null) {
- mLastKnownLocation.put(provider, location);
- }
- }
-
- return location;
+ return mLastKnownLocation.get(provider);
}
private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
@@ -1541,7 +1444,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
lastLocation.set(location);
}
- writeLastKnownLocationLocked(provider, location);
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
diff --git a/services/java/com/android/server/MasterClearReceiver.java b/services/java/com/android/server/MasterClearReceiver.java
index 5a42e76..3c366da 100644
--- a/services/java/com/android/server/MasterClearReceiver.java
+++ b/services/java/com/android/server/MasterClearReceiver.java
@@ -30,8 +30,8 @@ public class MasterClearReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals("android.intent.action.GTALK_DATA_MESSAGE_RECEIVED")) {
- if (!intent.getBooleanExtra("from_trusted_server", false)) {
+ if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
+ if (!intent.getBooleanExtra("android.intent.extra.from_trusted_server", false)) {
Log.w(TAG, "Ignoring master clear request -- not from trusted server.");
return;
}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f81c519..f85d931 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -231,6 +231,7 @@ class MountService extends IMountService.Stub {
if (getMassStorageConnected() && !suppressIfConnected) {
Intent intent = new Intent();
intent.setClass(mContext, com.android.internal.app.UsbStorageActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
setUsbStorageNotification(
com.android.internal.R.string.usb_storage_notification_title,
@@ -295,7 +296,7 @@ class MountService extends IMountService.Stub {
setMediaStorageNotification(
com.android.internal.R.string.ext_media_nomedia_notification_title,
com.android.internal.R.string.ext_media_nomedia_notification_message,
- com.android.internal.R.drawable.stat_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, null);
handlePossibleExplicitUnmountBroadcast(path);
@@ -352,7 +353,7 @@ class MountService extends IMountService.Stub {
setMediaStorageNotification(com.android.internal.R.string.ext_media_nofs_notification_title,
com.android.internal.R.string.ext_media_nofs_notification_message,
- com.android.internal.R.drawable.stat_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, pi);
updateUsbMassStorageNotification(false, false);
intent = new Intent(Intent.ACTION_MEDIA_NOFS,
@@ -420,7 +421,7 @@ class MountService extends IMountService.Stub {
setMediaStorageNotification(com.android.internal.R.string.ext_media_unmountable_notification_title,
com.android.internal.R.string.ext_media_unmountable_notification_message,
- com.android.internal.R.drawable.stat_sys_no_sim,
+ com.android.internal.R.drawable.stat_notify_sdcard_usb,
true, false, pi);
updateUsbMassStorageNotification(false, false);
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index aac7124..696ef31 100644..100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -35,6 +35,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
@@ -48,6 +49,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Power;
+import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Vibrator;
@@ -96,7 +98,7 @@ class NotificationManagerService extends INotificationManager.Stub
private Vibrator mVibrator = new Vibrator();
// adb
- private int mBatteryPlugged;
+ private boolean mUsbConnected;
private boolean mAdbEnabled = false;
private boolean mAdbNotificationShown = false;
private Notification mAdbNotification;
@@ -257,7 +259,8 @@ class NotificationManagerService extends INotificationManager.Stub
}
public void onNotificationClick(String pkg, int id) {
- cancelNotification(pkg, id, Notification.FLAG_AUTO_CANCEL);
+ cancelNotification(pkg, id, Notification.FLAG_AUTO_CANCEL,
+ Notification.FLAG_FOREGROUND_SERVICE);
}
public void onPanelRevealed() {
@@ -310,8 +313,11 @@ class NotificationManagerService extends INotificationManager.Stub
mBatteryFull = batteryFull;
updateLights();
}
-
- mBatteryPlugged = intent.getIntExtra("plugged", 0);
+ } else if (action.equals(Intent.ACTION_UMS_CONNECTED)) {
+ mUsbConnected = true;
+ updateAdbNotification();
+ } else if (action.equals(Intent.ACTION_UMS_DISCONNECTED)) {
+ mUsbConnected = false;
updateAdbNotification();
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
@@ -323,7 +329,7 @@ class NotificationManagerService extends INotificationManager.Stub
if (pkgName == null) {
return;
}
- cancelAllNotifications(pkgName);
+ cancelAllNotificationsInt(pkgName, 0, 0);
}
}
};
@@ -380,6 +386,8 @@ class NotificationManagerService extends INotificationManager.Stub
// register for battery changed notifications
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_UMS_CONNECTED);
+ filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
mContext.registerReceiver(mIntentReceiver, filter);
@@ -575,6 +583,8 @@ class NotificationManagerService extends INotificationManager.Stub
// ============================================================================
public void enqueueNotification(String pkg, int id, Notification notification, int[] idOut)
{
+ checkIncomingCall(pkg);
+
// This conditional is a dirty hack to limit the logging done on
// behalf of the download manager without affecting other apps.
if (!pkg.equals("com.android.providers.downloads")
@@ -607,7 +617,20 @@ class NotificationManagerService extends INotificationManager.Stub
} else {
old = mNotificationList.remove(index);
mNotificationList.add(index, r);
+ // Make sure we don't lose the foreground service state.
+ if (old != null) {
+ notification.flags |=
+ old.notification.flags&Notification.FLAG_FOREGROUND_SERVICE;
+ }
+ }
+
+ // Ensure if this is a foreground service that the proper additional
+ // flags are set.
+ if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ notification.flags |= Notification.FLAG_ONGOING_EVENT
+ | Notification.FLAG_NO_CLEAR;
}
+
if (notification.icon != 0) {
IconData icon = IconData.makeIcon(null, pkg, notification.icon,
notification.iconLevel,
@@ -802,9 +825,11 @@ class NotificationManagerService extends INotificationManager.Stub
}
/**
- * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}.
+ * Cancels a notification ONLY if it has all of the {@code mustHaveFlags}
+ * and none of the {@code mustNotHaveFlags}.
*/
- private void cancelNotification(String pkg, int id, int mustHaveFlags) {
+ private void cancelNotification(String pkg, int id, int mustHaveFlags,
+ int mustNotHaveFlags) {
EventLog.writeEvent(EVENT_LOG_CANCEL, pkg, id, mustHaveFlags);
synchronized (mNotificationList) {
@@ -817,6 +842,9 @@ class NotificationManagerService extends INotificationManager.Stub
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
return;
}
+ if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ return;
+ }
mNotificationList.remove(index);
@@ -830,7 +858,8 @@ class NotificationManagerService extends INotificationManager.Stub
* Cancels all notifications from a given package that have all of the
* {@code mustHaveFlags}.
*/
- private void cancelAllNotificationsInt(String pkg, int mustHaveFlags) {
+ void cancelAllNotificationsInt(String pkg, int mustHaveFlags,
+ int mustNotHaveFlags) {
EventLog.writeEvent(EVENT_LOG_CANCEL_ALL, pkg, mustHaveFlags);
synchronized (mNotificationList) {
@@ -841,6 +870,9 @@ class NotificationManagerService extends INotificationManager.Stub
if ((r.notification.flags & mustHaveFlags) != mustHaveFlags) {
continue;
}
+ if ((r.notification.flags & mustNotHaveFlags) != 0) {
+ continue;
+ }
if (!r.pkg.equals(pkg)) {
continue;
}
@@ -855,17 +887,40 @@ class NotificationManagerService extends INotificationManager.Stub
}
- public void cancelNotification(String pkg, int id)
- {
- cancelNotification(pkg, id, 0);
+ public void cancelNotification(String pkg, int id) {
+ checkIncomingCall(pkg);
+ // Don't allow client applications to cancel foreground service notis.
+ cancelNotification(pkg, id, 0,
+ Binder.getCallingUid() == Process.SYSTEM_UID
+ ? 0 : Notification.FLAG_FOREGROUND_SERVICE);
}
- public void cancelAllNotifications(String pkg)
- {
- cancelAllNotificationsInt(pkg, 0);
+ public void cancelAllNotifications(String pkg) {
+ checkIncomingCall(pkg);
+
+ // Calling from user space, don't allow the canceling of actively
+ // running foreground services.
+ cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE);
}
- public void cancelAll() {
+ void checkIncomingCall(String pkg) {
+ int uid = Binder.getCallingUid();
+ if (uid == Process.SYSTEM_UID || uid == 0) {
+ return;
+ }
+ try {
+ ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(
+ pkg, 0);
+ if (ai.uid != uid) {
+ throw new SecurityException("Calling uid " + uid + " gave package"
+ + pkg + " which is owned by uid " + ai.uid);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException("Unknown package " + pkg);
+ }
+ }
+
+ void cancelAll() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
for (int i=N-1; i>=0; i--) {
@@ -902,8 +957,15 @@ class NotificationManagerService extends INotificationManager.Stub
{
// Battery low always shows, other states only show if charging.
if (mBatteryLow) {
- mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY, BATTERY_LOW_ARGB,
- HardwareService.LIGHT_FLASH_TIMED, BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ if (mBatteryCharging) {
+ mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
+ BATTERY_LOW_ARGB);
+ } else {
+ // Flash when battery is low and not charging
+ mHardware.setLightFlashing_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
+ BATTERY_LOW_ARGB, HardwareService.LIGHT_FLASH_TIMED,
+ BATTERY_BLINK_ON, BATTERY_BLINK_OFF);
+ }
} else if (mBatteryCharging) {
if (mBatteryFull) {
mHardware.setLightColor_UNCHECKED(HardwareService.LIGHT_ID_BATTERY,
@@ -954,7 +1016,7 @@ class NotificationManagerService extends INotificationManager.Stub
// security feature that we don't want people customizing the platform
// to accidentally lose.
private void updateAdbNotification() {
- if (mAdbEnabled && mBatteryPlugged == BatteryManager.BATTERY_PLUGGED_USB) {
+ if (mAdbEnabled && mUsbConnected) {
if ("0".equals(SystemProperties.get("persist.adb.notify"))) {
return;
}
diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java
index 786f423..772ddeb 100644
--- a/services/java/com/android/server/PackageManagerBackupAgent.java
+++ b/services/java/com/android/server/PackageManagerBackupAgent.java
@@ -327,6 +327,13 @@ public class PackageManagerBackupAgent extends BackupAgent {
try {
int num = in.readInt();
Log.v(TAG, " ... unflatten read " + num);
+
+ // Sensical?
+ if (num > 20) {
+ Log.e(TAG, "Suspiciously large sig count in restore data; aborting");
+ throw new IllegalStateException("Bad restore state");
+ }
+
sigs = new Signature[num];
for (int i = 0; i < num; i++) {
int len = in.readInt();
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 89f854e..82cf1bc 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -35,6 +35,7 @@ import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
@@ -61,6 +62,8 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.Environment;
@@ -88,6 +91,7 @@ import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
@@ -108,6 +112,7 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
+ private static final int LOG_UID = Process.LOG_UID;
private static final int FIRST_APPLICATION_UID =
Process.FIRST_APPLICATION_UID;
private static final int MAX_APPLICATION_UIDS = 1000;
@@ -138,7 +143,7 @@ class PackageManagerService extends IPackageManager.Stub {
final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND);
- final Handler mHandler;
+ final PackageHandler mHandler;
final int mSdkVersion = Build.VERSION.SDK_INT;
final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME)
@@ -221,6 +226,14 @@ class PackageManagerService extends IPackageManager.Stub {
// etc/permissions.xml file.
final HashMap<String, String> mSharedLibraries = new HashMap<String, String>();
+ // Temporary for building the final shared libraries for an .apk.
+ String[] mTmpSharedLibraries = null;
+
+ // These are the features this devices supports that were read from the
+ // etc/permissions.xml file.
+ final HashMap<String, FeatureInfo> mAvailableFeatures =
+ new HashMap<String, FeatureInfo>();
+
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
@@ -262,6 +275,49 @@ class PackageManagerService extends IPackageManager.Stub {
ComponentName mResolveComponentName;
PackageParser.Package mPlatformPackage;
+ // Set of pending broadcasts for aggregating enable/disable of components.
+ final HashMap<String, String> mPendingBroadcasts = new HashMap<String, String>();
+ static final int SEND_PENDING_BROADCAST = 1;
+ // Delay time in millisecs
+ static final int BROADCAST_DELAY = 10 * 1000;
+
+ class PackageHandler extends Handler {
+ PackageHandler(Looper looper) {
+ super(looper);
+ }
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SEND_PENDING_BROADCAST : {
+ int size = 0;
+ String broadcastList[];
+ HashMap<String, String> tmpMap;
+ int uids[];
+ synchronized (mPackages) {
+ size = mPendingBroadcasts.size();
+ if (size <= 0) {
+ // Nothing to be done. Just return
+ return;
+ }
+ broadcastList = new String[size];
+ mPendingBroadcasts.keySet().toArray(broadcastList);
+ tmpMap = new HashMap<String, String>(mPendingBroadcasts);
+ uids = new int[size];
+ for (int i = 0; i < size; i++) {
+ PackageSetting ps = mSettings.mPackages.get(mPendingBroadcasts.get(broadcastList[i]));
+ uids[i] = (ps != null) ? ps.userId : -1;
+ }
+ mPendingBroadcasts.clear();
+ }
+ // Send broadcasts
+ for (int i = 0; i < size; i++) {
+ String className = broadcastList[i];
+ sendPackageChangedBroadcast(className, true, tmpMap.get(className), uids[i]);
+ }
+ break;
+ }
+ }
+ }
+ }
public static final IPackageManager main(Context context, boolean factoryTest) {
PackageManagerService m = new PackageManagerService(context, factoryTest);
ServiceManager.addService("package", m);
@@ -309,6 +365,10 @@ class PackageManagerService extends IPackageManager.Stub {
MULTIPLE_APPLICATION_UIDS
? RADIO_UID : FIRST_APPLICATION_UID,
ApplicationInfo.FLAG_SYSTEM);
+ mSettings.addSharedUserLP("android.uid.log",
+ MULTIPLE_APPLICATION_UIDS
+ ? LOG_UID : FIRST_APPLICATION_UID,
+ ApplicationInfo.FLAG_SYSTEM);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
@@ -345,7 +405,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) {
synchronized (mPackages) {
mHandlerThread.start();
- mHandler = new Handler(mHandlerThread.getLooper());
+ mHandler = new PackageHandler(mHandlerThread.getLooper());
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
@@ -671,7 +731,21 @@ class PackageManagerService extends IPackageManager.Stub {
+ parser.getPositionDescription());
} else {
Log.i(TAG, "Got library " + lname + " in " + lfile);
- this.mSharedLibraries.put(lname, lfile);
+ mSharedLibraries.put(lname, lfile);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+
+ } else if ("feature".equals(name)) {
+ String fname = parser.getAttributeValue(null, "name");
+ if (fname == null) {
+ Log.w(TAG, "<feature> without name at "
+ + parser.getPositionDescription());
+ } else {
+ Log.i(TAG, "Got feature " + fname);
+ FeatureInfo fi = new FeatureInfo();
+ fi.name = fname;
+ mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
@@ -1001,12 +1075,30 @@ class PackageManagerService extends IPackageManager.Stub {
Set<String> libSet;
synchronized (mPackages) {
libSet = mSharedLibraries.keySet();
+ int size = libSet.size();
+ if (size > 0) {
+ String[] libs = new String[size];
+ libSet.toArray(libs);
+ return libs;
+ }
}
- int size = libSet.size();
- if (size > 0) {
- String[] libs = new String[size];
- libSet.toArray(libs);
- return libs;
+ return null;
+ }
+
+ public FeatureInfo[] getSystemAvailableFeatures() {
+ Collection<FeatureInfo> featSet;
+ synchronized (mPackages) {
+ featSet = mAvailableFeatures.values();
+ int size = featSet.size();
+ if (size > 0) {
+ FeatureInfo[] features = new FeatureInfo[size+1];
+ featSet.toArray(features);
+ FeatureInfo fi = new FeatureInfo();
+ fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
+ FeatureInfo.GL_ES_VERSION_UNDEFINED);
+ features[size] = fi;
+ return features;
+ }
}
return null;
}
@@ -1138,25 +1230,57 @@ class PackageManagerService extends IPackageManager.Stub {
|| p2 == null || p2.mExtras == null) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- return checkSignaturesLP(p1, p2);
+ return checkSignaturesLP(p1.mSignatures, p2.mSignatures);
}
}
- int checkSignaturesLP(PackageParser.Package p1, PackageParser.Package p2) {
- if (p1.mSignatures == null) {
- return p2.mSignatures == null
+ public int checkUidSignatures(int uid1, int uid2) {
+ synchronized (mPackages) {
+ Signature[] s1;
+ Signature[] s2;
+ Object obj = mSettings.getUserIdLP(uid1);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ s1 = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ obj = mSettings.getUserIdLP(uid2);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ s2 = ((SharedUserSetting)obj).signatures.mSignatures;
+ } else if (obj instanceof PackageSetting) {
+ s2 = ((PackageSetting)obj).signatures.mSignatures;
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ } else {
+ return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
+ }
+ return checkSignaturesLP(s1, s2);
+ }
+ }
+
+ int checkSignaturesLP(Signature[] s1, Signature[] s2) {
+ if (s1 == null) {
+ return s2 == null
? PackageManager.SIGNATURE_NEITHER_SIGNED
: PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
}
- if (p2.mSignatures == null) {
+ if (s2 == null) {
return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
}
- final int N1 = p1.mSignatures.length;
- final int N2 = p2.mSignatures.length;
+ final int N1 = s1.length;
+ final int N2 = s2.length;
for (int i=0; i<N1; i++) {
boolean match = false;
for (int j=0; j<N2; j++) {
- if (p1.mSignatures[i].equals(p2.mSignatures[j])) {
+ if (s1[i].equals(s2[j])) {
match = true;
break;
}
@@ -1677,6 +1801,9 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ /**
+ * @deprecated
+ */
public void querySyncProviders(List outNames, List outInfo) {
synchronized (mPackages) {
Iterator<Map.Entry<String, PackageParser.Provider>> i
@@ -2030,17 +2157,62 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mPackages) {
// Check all shared libraries and map to their actual file path.
- if (pkg.usesLibraryFiles != null) {
- for (int i=0; i<pkg.usesLibraryFiles.length; i++) {
- String file = mSharedLibraries.get(pkg.usesLibraryFiles[i]);
+ if (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null) {
+ if (mTmpSharedLibraries == null ||
+ mTmpSharedLibraries.length < mSharedLibraries.size()) {
+ mTmpSharedLibraries = new String[mSharedLibraries.size()];
+ }
+ int num = 0;
+ int N = pkg.usesLibraries != null ? pkg.usesLibraries.size() : 0;
+ for (int i=0; i<N; i++) {
+ String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file == null) {
Log.e(TAG, "Package " + pkg.packageName
+ " requires unavailable shared library "
- + pkg.usesLibraryFiles[i] + "; ignoring!");
+ + pkg.usesLibraries.get(i) + "; failing!");
mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
return null;
}
- pkg.usesLibraryFiles[i] = file;
+ mTmpSharedLibraries[num] = file;
+ num++;
+ }
+ N = pkg.usesOptionalLibraries != null ? pkg.usesOptionalLibraries.size() : 0;
+ for (int i=0; i<N; i++) {
+ String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
+ if (file == null) {
+ Log.w(TAG, "Package " + pkg.packageName
+ + " desires unavailable shared library "
+ + pkg.usesOptionalLibraries.get(i) + "; ignoring!");
+ } else {
+ mTmpSharedLibraries[num] = file;
+ num++;
+ }
+ }
+ if (num > 0) {
+ pkg.usesLibraryFiles = new String[num];
+ System.arraycopy(mTmpSharedLibraries, 0,
+ pkg.usesLibraryFiles, 0, num);
+ }
+
+ if (pkg.reqFeatures != null) {
+ N = pkg.reqFeatures.size();
+ for (int i=0; i<N; i++) {
+ FeatureInfo fi = pkg.reqFeatures.get(i);
+ if ((fi.flags&FeatureInfo.FLAG_REQUIRED) == 0) {
+ // Don't care.
+ continue;
+ }
+
+ if (fi.name != null) {
+ if (mAvailableFeatures.get(fi.name) == null) {
+ Log.e(TAG, "Package " + pkg.packageName
+ + " requires unavailable feature "
+ + fi.name + "; failing!");
+ mLastScanError = PackageManager.INSTALL_FAILED_MISSING_FEATURE;
+ return null;
+ }
+ }
+ }
}
}
@@ -2904,9 +3076,9 @@ class PackageManagerService extends IPackageManager.Stub {
allowed = true;
} else if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE
|| p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
- allowed = (checkSignaturesLP(p.owner, pkg)
+ allowed = (checkSignaturesLP(p.owner.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH)
- || (checkSignaturesLP(mPlatformPackage, pkg)
+ || (checkSignaturesLP(mPlatformPackage.mSignatures, pkg.mSignatures)
== PackageManager.SIGNATURE_MATCH);
if (p.info.protectionLevel == PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM) {
if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
@@ -3553,7 +3725,8 @@ class PackageManagerService extends IPackageManager.Stub {
// First find the old package info and check signatures
synchronized(mPackages) {
oldPackage = mPackages.get(pkgName);
- if(checkSignaturesLP(pkg, oldPackage) != PackageManager.SIGNATURE_MATCH) {
+ if(checkSignaturesLP(pkg.mSignatures, oldPackage.mSignatures)
+ != PackageManager.SIGNATURE_MATCH) {
res.returnCode = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return;
}
@@ -4743,7 +4916,7 @@ class PackageManagerService extends IPackageManager.Stub {
}
private void setEnabledSetting(
- final String packageNameStr, String classNameStr, int newState, final int flags) {
+ final String packageName, String className, int newState, final int flags) {
if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED)) {
@@ -4755,17 +4928,20 @@ class PackageManagerService extends IPackageManager.Stub {
final int permission = mContext.checkCallingPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ boolean sendNow = false;
+ boolean isApp = (className == null);
+ String key = isApp ? packageName : className;
int packageUid = -1;
synchronized (mPackages) {
- pkgSetting = mSettings.mPackages.get(packageNameStr);
+ pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) {
- if (classNameStr == null) {
+ if (className == null) {
throw new IllegalArgumentException(
- "Unknown package: " + packageNameStr);
+ "Unknown package: " + packageName);
}
throw new IllegalArgumentException(
- "Unknown component: " + packageNameStr
- + "/" + classNameStr);
+ "Unknown component: " + packageName
+ + "/" + className);
}
if (!allowedByPermission && (uid != pkgSetting.userId)) {
throw new SecurityException(
@@ -4773,41 +4949,67 @@ class PackageManagerService extends IPackageManager.Stub {
+ Binder.getCallingPid()
+ ", uid=" + uid + ", package uid=" + pkgSetting.userId);
}
- packageUid = pkgSetting.userId;
- if (classNameStr == null) {
+ if (className == null) {
// We're dealing with an application/package level state change
pkgSetting.enabled = newState;
} else {
// We're dealing with a component level state change
switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED:
- pkgSetting.enableComponentLP(classNameStr);
+ pkgSetting.enableComponentLP(className);
break;
case COMPONENT_ENABLED_STATE_DISABLED:
- pkgSetting.disableComponentLP(classNameStr);
+ pkgSetting.disableComponentLP(className);
break;
case COMPONENT_ENABLED_STATE_DEFAULT:
- pkgSetting.restoreComponentLP(classNameStr);
+ pkgSetting.restoreComponentLP(className);
break;
default:
Log.e(TAG, "Invalid new component state: " + newState);
+ return;
}
}
mSettings.writeLP();
+ packageUid = pkgSetting.userId;
+ if ((flags&PackageManager.DONT_KILL_APP) == 0) {
+ sendNow = true;
+ // Purge entry from pending broadcast list if another one exists already
+ // since we are sending one right away.
+ if (mPendingBroadcasts.get(key) != null) {
+ mPendingBroadcasts.remove(key);
+ // Can ignore empty list since its handled in the handler anyway
+ }
+ } else {
+ if (mPendingBroadcasts.get(key) == null) {
+ mPendingBroadcasts.put(key, packageName);
+ }
+ if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
+ // Schedule a message
+ mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
+ }
+ }
}
-
+
long callingId = Binder.clearCallingIdentity();
try {
- Bundle extras = new Bundle(2);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP,
- (flags&PackageManager.DONT_KILL_APP) != 0);
- extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageNameStr, extras);
+ if (sendNow) {
+ sendPackageChangedBroadcast(packageName,
+ (flags&PackageManager.DONT_KILL_APP) != 0, key, packageUid);
+ }
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
+ private void sendPackageChangedBroadcast(String packageName,
+ boolean killFlag, String componentName, int packageUid) {
+ Bundle extras = new Bundle(2);
+ extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentName);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
+ extras.putInt(Intent.EXTRA_UID, packageUid);
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras);
+ }
+
public String getInstallerPackageName(String packageName) {
synchronized (mPackages) {
PackageSetting pkg = mSettings.mPackages.get(packageName);
@@ -5029,6 +5231,15 @@ class PackageManagerService extends IPackageManager.Stub {
pw.println("Settings parse messages:");
pw.println(mSettings.mReadMessages.toString());
}
+
+ synchronized (mProviders) {
+ pw.println(" ");
+ pw.println("Registered ContentProviders:");
+ for (PackageParser.Provider p : mProviders.values()) {
+ pw.println(" ["); pw.println(p.info.authority); pw.println("]: ");
+ pw.println(p.toString());
+ }
+ }
}
static final class BasePermission {
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 79d78ad..38df47b 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -29,6 +29,10 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.Cursor;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.Handler;
@@ -58,7 +62,8 @@ import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
-class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor {
+class PowerManagerService extends IPowerManager.Stub
+ implements LocalPowerManager,Watchdog.Monitor, SensorEventListener {
private static final String TAG = "PowerManagerService";
static final String PARTIAL_NAME = "PowerManagerService";
@@ -72,7 +77,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.SCREEN_DIM_WAKE_LOCK
| PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- | PowerManager.FULL_WAKE_LOCK;
+ | PowerManager.FULL_WAKE_LOCK
+ | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
// time since last state: time since last event:
// The short keylight delay comes from Gservices; this is the default.
@@ -138,11 +144,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private int[] mBroadcastQueue = new int[] { -1, -1, -1 };
private int[] mBroadcastWhy = new int[3];
private int mPartialCount = 0;
+ private int mProximityCount = 0;
private int mPowerState;
private boolean mOffBecauseOfUser;
private int mUserState;
private boolean mKeyboardVisible = false;
private boolean mUserActivityAllowed = true;
+ private boolean mProximitySensorActive = false;
private int mTotalDelaySetting;
private int mKeylightDelay;
private int mDimDelay;
@@ -175,6 +183,8 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private IActivityManager mActivityService;
private IBatteryStats mBatteryStats;
private BatteryService mBatteryService;
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
private boolean mDimScreen = true;
private long mNextTimeout;
private volatile int mPokey = 0;
@@ -536,6 +546,7 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
wl.minState = SCREEN_DIM;
break;
case PowerManager.PARTIAL_WAKE_LOCK:
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
break;
default:
// just log and bail. we're in the server, so don't
@@ -583,6 +594,11 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
}
}
Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
+ } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+ mProximityCount++;
+ if (mProximityCount == 1) {
+ enableProximityLockLocked();
+ }
}
if (newlock) {
acquireUid = wl.uid;
@@ -639,6 +655,11 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
Power.releaseWakeLock(PARTIAL_NAME);
}
+ } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+ mProximityCount--;
+ if (mProximityCount == 0) {
+ disableProximityLockLocked();
+ }
}
// Unlink the lock from the binder.
wl.binder.unlinkToDeath(wl, 0);
@@ -745,15 +766,17 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
switch (type)
{
case PowerManager.FULL_WAKE_LOCK:
- return "FULL_WAKE_LOCK ";
+ return "FULL_WAKE_LOCK ";
case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
- return "SCREEN_BRIGHT_WAKE_LOCK";
+ return "SCREEN_BRIGHT_WAKE_LOCK ";
case PowerManager.SCREEN_DIM_WAKE_LOCK:
- return "SCREEN_DIM_WAKE_LOCK ";
+ return "SCREEN_DIM_WAKE_LOCK ";
case PowerManager.PARTIAL_WAKE_LOCK:
- return "PARTIAL_WAKE_LOCK ";
+ return "PARTIAL_WAKE_LOCK ";
+ case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
+ return "PROXIMITY_SCREEN_OFF_WAKE_LOCK";
default:
- return "??? ";
+ return "??? ";
}
}
@@ -926,6 +949,9 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
private void sendNotificationLocked(boolean on, int why)
{
+ if (mProximitySensorActive) {
+ why = WindowManagerPolicy.OFF_BECAUSE_OF_PROXIMITY_SENSOR;
+ }
if (!on) {
mStillNeedSleepNotification = false;
}
@@ -1230,6 +1256,10 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
if (noChangeLights) {
newState = (newState & ~LIGHTS_MASK) | (mPowerState & LIGHTS_MASK);
}
+ if (mProximitySensorActive) {
+ // don't turn on the screen when the proximity sensor lock is held
+ newState = (newState & ~SCREEN_BRIGHT);
+ }
if (batteryIsLow()) {
newState |= BATTERY_LOW_BIT;
@@ -1726,11 +1756,13 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
Log.d(TAG, "userActivity mLastEventTime=" + mLastEventTime + " time=" + time
+ " mUserActivityAllowed=" + mUserActivityAllowed
+ " mUserState=0x" + Integer.toHexString(mUserState)
- + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState));
+ + " mWakeLockState=0x" + Integer.toHexString(mWakeLockState)
+ + " mProximitySensorActive=" + mProximitySensorActive
+ + " force=" + force);
}
if (mLastEventTime <= time || force) {
mLastEventTime = time;
- if (mUserActivityAllowed || force) {
+ if ((mUserActivityAllowed && !mProximitySensorActive) || force) {
// Only turn on button backlights if a button was pressed.
if (eventType == BUTTON_EVENT) {
mUserState = (mKeyboardVisible ? ALL_BRIGHT : SCREEN_BUTTON_BRIGHT);
@@ -1996,4 +2028,70 @@ class PowerManagerService extends IPowerManager.Stub implements LocalPowerManage
public void monitor() {
synchronized (mLocks) { }
}
+
+ public int getSupportedWakeLockFlags() {
+ int result = PowerManager.PARTIAL_WAKE_LOCK
+ | PowerManager.FULL_WAKE_LOCK
+ | PowerManager.SCREEN_DIM_WAKE_LOCK;
+
+ // call getSensorManager() to make sure mProximitySensor is initialized
+ getSensorManager();
+ if (mProximitySensor != null) {
+ result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+ }
+
+ return result;
+ }
+
+ private SensorManager getSensorManager() {
+ if (mSensorManager == null) {
+ mSensorManager = new SensorManager(mHandlerThread.getLooper());
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+ return mSensorManager;
+ }
+
+ private void enableProximityLockLocked() {
+ if (mSpew) {
+ Log.d(TAG, "enableProximityLockLocked");
+ }
+ mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ private void disableProximityLockLocked() {
+ if (mSpew) {
+ Log.d(TAG, "disableProximityLockLocked");
+ }
+ mSensorManager.unregisterListener(this);
+ mProximitySensorActive = false;
+ }
+
+ public void onSensorChanged(SensorEvent event) {
+ long milliseconds = event.timestamp / 1000000;
+ synchronized (mLocks) {
+ if (event.values[0] == 0.0) {
+ if (mSpew) {
+ Log.d(TAG, "onSensorChanged: proximity active");
+ }
+ goToSleepLocked(milliseconds);
+ mProximitySensorActive = true;
+ } else {
+ // proximity sensor negative events user activity.
+ // temporarily set mUserActivityAllowed to true so this will work
+ // even when the keyguard is on.
+ if (mSpew) {
+ Log.d(TAG, "onSensorChanged: proximity inactive");
+ }
+ mProximitySensorActive = false;
+ boolean savedActivityAllowed = mUserActivityAllowed;
+ mUserActivityAllowed = true;
+ userActivity(milliseconds, false);
+ mUserActivityAllowed = savedActivityAllowed;
+ }
+ }
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
}
diff --git a/services/java/com/android/server/SensorService.java b/services/java/com/android/server/SensorService.java
index ceef39f..4dfeb9d 100644
--- a/services/java/com/android/server/SensorService.java
+++ b/services/java/com/android/server/SensorService.java
@@ -84,12 +84,16 @@ class SensorService extends ISensorService.Stub {
if (hasSensor(sensor)) {
removeSensor(sensor);
try {
- deactivateIfUnused(sensor);
+ deactivateIfUnusedLocked(sensor);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in binderDied");
}
}
}
+ if (mListeners.size() == 0) {
+ _sensors_control_wake();
+ _sensors_control_close();
+ }
mListeners.notify();
}
}
@@ -102,9 +106,12 @@ class SensorService extends ISensorService.Stub {
}
public Bundle getDataChannel() throws RemoteException {
- return _sensors_control_open();
+ // synchronize so we do not require sensor HAL to be thread-safe.
+ synchronized(mListeners) {
+ return _sensors_control_open();
+ }
}
-
+
public boolean enableSensor(IBinder binder, String name, int sensor, int enable)
throws RemoteException {
if (localLOGV) Log.d(TAG, "enableSensor " + name + "(#" + sensor + ") " + enable);
@@ -163,7 +170,7 @@ class SensorService extends ISensorService.Stub {
l.addSensor(sensor, enable);
} else {
l.removeSensor(sensor);
- deactivateIfUnused(sensor);
+ deactivateIfUnusedLocked(sensor);
if (l.mSensors == 0) {
mListeners.remove(l);
binder.unlinkToDeath(l, 0);
@@ -173,12 +180,13 @@ class SensorService extends ISensorService.Stub {
if (mListeners.size() == 0) {
_sensors_control_wake();
+ _sensors_control_close();
}
}
return true;
}
- void deactivateIfUnused(int sensor) throws RemoteException {
+ private void deactivateIfUnusedLocked(int sensor) throws RemoteException {
int size = mListeners.size();
for (int i=0 ; i<size ; i++) {
if (mListeners.get(i).hasSensor(sensor))
@@ -187,10 +195,11 @@ class SensorService extends ISensorService.Stub {
_sensors_control_activate(sensor, false);
}
- ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ private ArrayList<Listener> mListeners = new ArrayList<Listener>();
private static native int _sensors_control_init();
private static native Bundle _sensors_control_open();
+ private static native int _sensors_control_close();
private static native boolean _sensors_control_activate(int sensor, boolean activate);
private static native int _sensors_control_set_delay(int ms);
private static native int _sensors_control_wake();
diff --git a/services/java/com/android/server/ShutdownActivity.java b/services/java/com/android/server/ShutdownActivity.java
new file mode 100644
index 0000000..7f0e90d
--- /dev/null
+++ b/services/java/com/android/server/ShutdownActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import com.android.internal.app.ShutdownThread;
+
+public class ShutdownActivity extends Activity {
+
+ private static final String TAG = "ShutdownActivity";
+ private boolean mConfirm;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mConfirm = getIntent().getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false);
+ Log.i(TAG, "onCreate(): confirm=" + mConfirm);
+
+ Handler h = new Handler();
+ h.post(new Runnable() {
+ public void run() {
+ ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
+ }
+ });
+ }
+}
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
new file mode 100644
index 0000000..17d0f1d
--- /dev/null
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.backup.AbsoluteFileBackupHelper;
+import android.backup.BackupDataInput;
+import android.backup.BackupDataInputStream;
+import android.backup.BackupDataOutput;
+import android.backup.BackupHelper;
+import android.backup.BackupHelperAgent;
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.ServiceManager;
+import android.os.SystemService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Backup agent for various system-managed data
+ */
+public class SystemBackupAgent extends BackupHelperAgent {
+ private static final String TAG = "SystemBackupAgent";
+
+ private static final String WALLPAPER_IMAGE = "/data/data/com.android.settings/files/wallpaper";
+ private static final String WALLPAPER_INFO = "/data/system/wallpaper_info.xml";
+
+ @Override
+ public void onCreate() {
+ addHelper("wallpaper", new AbsoluteFileBackupHelper(SystemBackupAgent.this,
+ new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }));
+ }
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ boolean success = false;
+ try {
+ super.onRestore(data, appVersionCode, newState);
+
+ WallpaperManagerService wallpaper = (WallpaperManagerService)ServiceManager.getService(
+ Context.WALLPAPER_SERVICE);
+ wallpaper.settingsRestored();
+ } catch (IOException ex) {
+ // If there was a failure, delete everything for the wallpaper, this is too aggresive,
+ // but this is hopefully a rare failure.
+ Log.d(TAG, "restore failed", ex);
+ (new File(WALLPAPER_IMAGE)).delete();
+ (new File(WALLPAPER_INFO)).delete();
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b2848ac..7c2a7ac 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,6 +18,7 @@ package com.android.server;
import com.android.server.am.ActivityManagerService;
import com.android.server.status.StatusBarService;
+import com.android.internal.os.SamplingProfilerIntegration;
import dalvik.system.VMRuntime;
@@ -31,23 +32,22 @@ import android.content.pm.IPackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.AudioService;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.SystemProperties;
+import android.os.*;
import android.provider.Contacts.People;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
-import android.server.BluetoothDeviceService;
+import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.EventLog;
import android.util.Log;
+import android.accounts.AccountManagerService;
+
+import java.util.Timer;
+import java.util.TimerTask;
class ServerThread extends Thread {
private static final String TAG = "SystemServer";
private final static boolean INCLUDE_DEMO = false;
- private final static boolean INCLUDE_BACKUP = false;
private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010;
@@ -84,31 +84,34 @@ class ServerThread extends Thread {
HardwareService hardware = null;
PowerManagerService power = null;
+ BatteryService battery = null;
+ ConnectivityService connectivity = null;
IPackageManager pm = null;
Context context = null;
WindowManagerService wm = null;
- BluetoothDeviceService bluetooth = null;
+ BluetoothService bluetooth = null;
BluetoothA2dpService bluetoothA2dp = null;
HeadsetObserver headset = null;
+ DockObserver dock = null;
// Critical services...
try {
- Log.i(TAG, "Starting Entropy Service.");
+ Log.i(TAG, "Entropy Service");
ServiceManager.addService("entropy", new EntropyService());
- Log.i(TAG, "Starting Power Manager.");
+ Log.i(TAG, "Power Manager");
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);
- Log.i(TAG, "Starting Activity Manager.");
+ Log.i(TAG, "Activity Manager");
context = ActivityManagerService.main(factoryTest);
- Log.i(TAG, "Starting telephony registry");
+ Log.i(TAG, "Telephony Registry");
ServiceManager.addService("telephony.registry", new TelephonyRegistry(context));
AttributeCache.init(context);
- Log.i(TAG, "Starting Package Manager.");
+ Log.i(TAG, "Package Manager");
pm = PackageManagerService.main(context,
factoryTest != SystemServer.FACTORY_TEST_OFF);
@@ -116,18 +119,26 @@ class ServerThread extends Thread {
mContentResolver = context.getContentResolver();
- Log.i(TAG, "Starting Content Manager.");
+ try {
+ Log.i(TAG, "Account Manager");
+ ServiceManager.addService(Context.ACCOUNT_SERVICE,
+ new AccountManagerService(context));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting Account Manager", e);
+ }
+
+ Log.i(TAG, "Content Manager");
ContentService.main(context,
factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);
- Log.i(TAG, "Starting System Content Providers.");
+ Log.i(TAG, "System Content Providers");
ActivityManagerService.installSystemProviders();
- Log.i(TAG, "Starting Battery Service.");
- BatteryService battery = new BatteryService(context);
+ Log.i(TAG, "Battery Service");
+ battery = new BatteryService(context);
ServiceManager.addService("battery", battery);
- Log.i(TAG, "Starting Hardware Service.");
+ Log.i(TAG, "Hardware Service");
hardware = new HardwareService(context);
ServiceManager.addService("hardware", hardware);
@@ -135,18 +146,19 @@ class ServerThread extends Thread {
// hardware service, content providers and the battery service.
power.init(context, hardware, ActivityManagerService.getDefault(), battery);
- Log.i(TAG, "Starting Alarm Manager.");
+ Log.i(TAG, "Alarm Manager");
AlarmManagerService alarm = new AlarmManagerService(context);
ServiceManager.addService(Context.ALARM_SERVICE, alarm);
+ Log.i(TAG, "Init Watchdog");
Watchdog.getInstance().init(context, battery, power, alarm,
ActivityManagerService.self());
// Sensor Service is needed by Window Manager, so this goes first
- Log.i(TAG, "Starting Sensor Service.");
+ Log.i(TAG, "Sensor Service");
ServiceManager.addService(Context.SENSOR_SERVICE, new SensorService(context));
- Log.i(TAG, "Starting Window Manager.");
+ Log.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, power,
factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
@@ -164,11 +176,11 @@ class ServerThread extends Thread {
Log.i(TAG, "Registering null Bluetooth Service (factory test)");
ServiceManager.addService(Context.BLUETOOTH_SERVICE, null);
} else {
- Log.i(TAG, "Starting Bluetooth Service.");
- bluetooth = new BluetoothDeviceService(context);
- bluetooth.init();
+ Log.i(TAG, "Bluetooth Service");
+ bluetooth = new BluetoothService(context);
ServiceManager.addService(Context.BLUETOOTH_SERVICE, bluetooth);
- bluetoothA2dp = new BluetoothA2dpService(context);
+ bluetooth.initAfterRegistration();
+ bluetoothA2dp = new BluetoothA2dpService(context, bluetooth);
ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,
bluetoothA2dp);
@@ -187,10 +199,11 @@ class ServerThread extends Thread {
InputMethodManagerService imm = null;
AppWidgetService appWidget = null;
NotificationManagerService notification = null;
+ WallpaperManagerService wallpaper = null;
if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
- Log.i(TAG, "Starting Status Bar Service.");
+ Log.i(TAG, "Status Bar");
statusBar = new StatusBarService(context);
ServiceManager.addService("statusbar", statusBar);
} catch (Throwable e) {
@@ -198,14 +211,14 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Clipboard Service.");
+ Log.i(TAG, "Clipboard Service");
ServiceManager.addService("clipboard", new ClipboardService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Clipboard Service", e);
}
try {
- Log.i(TAG, "Starting Input Method Service.");
+ Log.i(TAG, "Input Method Service");
imm = new InputMethodManagerService(context, statusBar);
ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
} catch (Throwable e) {
@@ -213,22 +226,22 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting NetStat Service.");
+ Log.i(TAG, "NetStat Service");
ServiceManager.addService("netstat", new NetStatService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting NetStat Service", e);
}
try {
- Log.i(TAG, "Starting Connectivity Service.");
- ServiceManager.addService(Context.CONNECTIVITY_SERVICE,
- ConnectivityService.getInstance(context));
+ Log.i(TAG, "Connectivity Service");
+ connectivity = ConnectivityService.getInstance(context);
+ ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
} catch (Throwable e) {
Log.e(TAG, "Failure starting Connectivity Service", e);
}
try {
- Log.i(TAG, "Starting Accessibility Manager.");
+ Log.i(TAG, "Accessibility Manager");
ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
new AccessibilityManagerService(context));
} catch (Throwable e) {
@@ -236,7 +249,7 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Notification Manager.");
+ Log.i(TAG, "Notification Manager");
notification = new NotificationManagerService(context, statusBar, hardware);
ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
} catch (Throwable e) {
@@ -245,14 +258,14 @@ class ServerThread extends Thread {
try {
// MountService must start after NotificationManagerService
- Log.i(TAG, "Starting Mount Service.");
+ Log.i(TAG, "Mount Service");
ServiceManager.addService("mount", new MountService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Mount Service", e);
}
try {
- Log.i(TAG, "Starting DeviceStorageMonitor service");
+ Log.i(TAG, "Device Storage Monitor");
ServiceManager.addService(DeviceStorageMonitorService.SERVICE,
new DeviceStorageMonitorService(context));
} catch (Throwable e) {
@@ -260,14 +273,14 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Location Manager.");
+ Log.i(TAG, "Location Manager");
ServiceManager.addService(Context.LOCATION_SERVICE, new LocationManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Location Manager", e);
}
try {
- Log.i(TAG, "Starting Search Service.");
+ Log.i(TAG, "Search Service");
ServiceManager.addService( Context.SEARCH_SERVICE, new SearchManagerService(context) );
} catch (Throwable e) {
Log.e(TAG, "Failure starting Search Service", e);
@@ -279,7 +292,7 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Checkin Service.");
+ Log.i(TAG, "Checkin Service");
Intent intent = new Intent().setComponent(new ComponentName(
"com.google.android.server.checkin",
"com.google.android.server.checkin.CheckinService"));
@@ -292,21 +305,22 @@ class ServerThread extends Thread {
}
try {
- Log.i(TAG, "Starting Wallpaper Service");
- ServiceManager.addService(Context.WALLPAPER_SERVICE, new WallpaperService(context));
+ Log.i(TAG, "Wallpaper Service");
+ wallpaper = new WallpaperManagerService(context);
+ ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper);
} catch (Throwable e) {
Log.e(TAG, "Failure starting Wallpaper Service", e);
}
try {
- Log.i(TAG, "Starting Audio Service");
+ Log.i(TAG, "Audio Service");
ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Audio Service", e);
}
try {
- Log.i(TAG, "Starting HeadsetObserver");
+ Log.i(TAG, "Headset Observer");
// Listen for wired headset changes
headset = new HeadsetObserver(context);
} catch (Throwable e) {
@@ -314,16 +328,22 @@ class ServerThread extends Thread {
}
try {
- if (INCLUDE_BACKUP) {
- Log.i(TAG, "Starting Backup Service");
- ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
- }
+ Log.i(TAG, "Dock Observer");
+ // Listen for dock station changes
+ dock = new DockObserver(context);
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting DockObserver", e);
+ }
+
+ try {
+ Log.i(TAG, "Backup Service");
+ ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Backup Service", e);
}
try {
- Log.i(TAG, "Starting AppWidget Service");
+ Log.i(TAG, "AppWidget Service");
appWidget = new AppWidgetService(context);
ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
} catch (Throwable e) {
@@ -346,7 +366,7 @@ class ServerThread extends Thread {
false, new AdbSettingsObserver());
// It is now time to start up the app processes...
- boolean safeMode = wm.detectSafeMode();
+ final boolean safeMode = wm.detectSafeMode();
if (notification != null) {
notification.systemReady();
@@ -355,27 +375,45 @@ class ServerThread extends Thread {
if (statusBar != null) {
statusBar.systemReady();
}
- if (imm != null) {
- imm.systemReady();
- }
wm.systemReady();
power.systemReady();
try {
pm.systemReady();
} catch (RemoteException e) {
}
- if (appWidget != null) {
- appWidget.systemReady(safeMode);
- }
-
- // After making the following code, third party code may be running...
- try {
- ActivityManagerNative.getDefault().systemReady();
- } catch (RemoteException e) {
- }
-
- Watchdog.getInstance().start();
+ // These are needed to propagate to the runnable below.
+ final BatteryService batteryF = battery;
+ final ConnectivityService connectivityF = connectivity;
+ final DockObserver dockF = dock;
+ final AppWidgetService appWidgetF = appWidget;
+ final WallpaperManagerService wallpaperF = wallpaper;
+ final InputMethodManagerService immF = imm;
+
+ // We now tell the activity manager it is okay to run third party
+ // code. It will call back into us once it has gotten to the state
+ // where third party code can really run (but before it has actually
+ // started launching the initial applications), for us to complete our
+ // initialization.
+ ((ActivityManagerService)ActivityManagerNative.getDefault())
+ .systemReady(new Runnable() {
+ public void run() {
+ Log.i(TAG, "Making services ready");
+
+ if (batteryF != null) batteryF.systemReady();
+ if (connectivityF != null) connectivityF.systemReady();
+ if (dockF != null) dockF.systemReady();
+ Watchdog.getInstance().start();
+
+ // It is now okay to let the various system services start their
+ // third party code...
+
+ if (appWidgetF != null) appWidgetF.systemReady(safeMode);
+ if (wallpaperF != null) wallpaperF.systemReady();
+ if (immF != null) immF.systemReady();
+ }
+ });
+
Looper.loop();
Log.d(TAG, "System ServerThread is exiting!");
}
@@ -417,19 +455,33 @@ public class SystemServer
public static final int FACTORY_TEST_OFF = 0;
public static final int FACTORY_TEST_LOW_LEVEL = 1;
public static final int FACTORY_TEST_HIGH_LEVEL = 2;
-
- /**
- * This method is called from Zygote to initialize the system. This will cause the native
+
+ static Timer timer;
+ static final long SNAPSHOT_INTERVAL = 60 * 60 * 1000; // 1hr
+
+ /**
+ * This method is called from Zygote to initialize the system. This will cause the native
* services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
* up into init2() to start the Android services.
- */
+ */
native public static void init1(String[] args);
public static void main(String[] args) {
+ if (SamplingProfilerIntegration.isEnabled()) {
+ SamplingProfilerIntegration.start();
+ timer = new Timer();
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ SamplingProfilerIntegration.writeSnapshot("system_server");
+ }
+ }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);
+ }
+
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+
System.loadLibrary("android_servers");
init1(args);
}
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 9f2856c..170a9f8 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -88,6 +88,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private String mDataConnectionApn = "";
+ private String[] mDataConnectionApnTypes = null;
+
private String mDataConnectionInterfaceName = "";
private Bundle mCellLocation = new Bundle();
@@ -337,7 +339,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String interfaceName) {
+ String reason, String apn, String[] apnTypes, String interfaceName) {
if (!checkNotifyPermission("notifyDataConnection()" )) {
return;
}
@@ -346,6 +348,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataConnectionPossible = isDataConnectivityPossible;
mDataConnectionReason = reason;
mDataConnectionApn = apn;
+ mDataConnectionApnTypes = apnTypes;
mDataConnectionInterfaceName = interfaceName;
for (int i = mRecords.size() - 1; i >= 0; i--) {
Record r = mRecords.get(i);
@@ -359,7 +362,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
- interfaceName);
+ apnTypes, interfaceName);
}
public void notifyDataConnectionFailed(String reason) {
@@ -517,8 +520,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE);
}
- private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String interfaceName) {
+ private void broadcastDataConnectionStateChanged(int state,
+ boolean isDataConnectivityPossible,
+ String reason, String apn, String[] apnTypes, String interfaceName) {
// Note: not reporting to the battery stats service here, because the
// status bar takes care of that after taking into account all of the
// required info.
@@ -531,6 +535,14 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
}
intent.putExtra(Phone.DATA_APN_KEY, apn);
+ String types = new String("");
+ if (apnTypes.length > 0) {
+ types = apnTypes[0];
+ for (int i = 1; i < apnTypes.length; i++) {
+ types = types+","+apnTypes[i];
+ }
+ }
+ intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
mContext.sendStickyBroadcast(intent);
}
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
new file mode 100644
index 0000000..4b6049f
--- /dev/null
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.os.FileObserver.*;
+import static android.os.ParcelFileDescriptor.*;
+
+import android.app.IWallpaperManager;
+import android.app.IWallpaperManagerCallback;
+import android.app.PendingIntent;
+import android.app.WallpaperInfo;
+import android.backup.BackupManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.FileObserver;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallbackList;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.IWallpaperEngine;
+import android.service.wallpaper.IWallpaperService;
+import android.service.wallpaper.WallpaperService;
+import android.util.Log;
+import android.util.Xml;
+import android.view.IWindowManager;
+import android.view.WindowManager;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import com.android.internal.service.wallpaper.ImageWallpaper;
+import com.android.internal.util.FastXmlSerializer;
+
+class WallpaperManagerService extends IWallpaperManager.Stub {
+ static final String TAG = "WallpaperService";
+ static final boolean DEBUG = false;
+
+ Object mLock = new Object();
+
+ /**
+ * Minimum time between crashes of a wallpaper service for us to consider
+ * restarting it vs. just reverting to the static wallpaper.
+ */
+ static final long MIN_WALLPAPER_CRASH_TIME = 10000;
+
+ static final File WALLPAPER_DIR = new File(
+ "/data/data/com.android.settings/files");
+ static final String WALLPAPER = "wallpaper";
+ static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
+
+ /**
+ * List of callbacks registered they should each be notified
+ * when the wallpaper is changed.
+ */
+ private final RemoteCallbackList<IWallpaperManagerCallback> mCallbacks
+ = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+ /**
+ * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
+ * that the wallpaper has changed. The CREATE is triggered when there is no
+ * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
+ * everytime the wallpaper is changed.
+ */
+ private final FileObserver mWallpaperObserver = new FileObserver(
+ WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE | DELETE | DELETE_SELF) {
+ @Override
+ public void onEvent(int event, String path) {
+ if (path == null) {
+ return;
+ }
+ synchronized (mLock) {
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
+ File changedFile = new File(WALLPAPER_DIR, path);
+ if (WALLPAPER_FILE.equals(changedFile)) {
+ notifyCallbacksLocked();
+ }
+ }
+ }
+ };
+
+ final Context mContext;
+ final IWindowManager mIWindowManager;
+
+ int mWidth = -1;
+ int mHeight = -1;
+ String mName = "";
+ ComponentName mWallpaperComponent;
+ WallpaperConnection mWallpaperConnection;
+ long mLastDiedTime;
+
+ class WallpaperConnection extends IWallpaperConnection.Stub
+ implements ServiceConnection {
+ final WallpaperInfo mInfo;
+ final Binder mToken = new Binder();
+ IWallpaperService mService;
+ IWallpaperEngine mEngine;
+
+ public WallpaperConnection(WallpaperInfo info) {
+ mInfo = info;
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ if (mWallpaperConnection == this) {
+ mService = IWallpaperService.Stub.asInterface(service);
+ attachServiceLocked(this);
+ // XXX should probably do saveSettingsLocked() later
+ // when we have an engine, but I'm not sure about
+ // locking there and anyway we always need to be able to
+ // recover if there is something wrong.
+ saveSettingsLocked();
+ }
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ mService = null;
+ mEngine = null;
+ if (mWallpaperConnection == this) {
+ Log.w(TAG, "Wallpaper service gone: " + mWallpaperComponent);
+ if ((mLastDiedTime+MIN_WALLPAPER_CRASH_TIME)
+ < SystemClock.uptimeMillis()) {
+ Log.w(TAG, "Reverting to built-in wallpaper!");
+ bindWallpaperComponentLocked(null);
+ }
+ }
+ }
+ }
+
+ public void attachEngine(IWallpaperEngine engine) {
+ mEngine = engine;
+ }
+
+ public ParcelFileDescriptor setWallpaper(String name) {
+ synchronized (mLock) {
+ if (mWallpaperConnection == this) {
+ return updateWallpaperBitmapLocked(name);
+ }
+ return null;
+ }
+ }
+ }
+
+ public WallpaperManagerService(Context context) {
+ if (DEBUG) Log.d(TAG, "WallpaperService startup");
+ mContext = context;
+ mIWindowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+ WALLPAPER_DIR.mkdirs();
+ loadSettingsLocked();
+ mWallpaperObserver.startWatching();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mWallpaperObserver.stopWatching();
+ }
+
+ public void systemReady() {
+ synchronized (mLock) {
+ try {
+ bindWallpaperComponentLocked(mWallpaperComponent);
+ } catch (RuntimeException e) {
+ Log.w(TAG, "Failure starting previous wallpaper", e);
+ try {
+ bindWallpaperComponentLocked(null);
+ } catch (RuntimeException e2) {
+ Log.w(TAG, "Failure starting default wallpaper", e2);
+ clearWallpaperComponentLocked();
+ }
+ }
+ }
+ }
+
+ public void clearWallpaper() {
+ synchronized (mLock) {
+ File f = WALLPAPER_FILE;
+ if (f.exists()) {
+ f.delete();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ bindWallpaperComponentLocked(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public void setDimensionHints(int width, int height) throws RemoteException {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
+
+ if (width <= 0 || height <= 0) {
+ throw new IllegalArgumentException("width and height must be > 0");
+ }
+
+ synchronized (mLock) {
+ if (width != mWidth || height != mHeight) {
+ mWidth = width;
+ mHeight = height;
+ saveSettingsLocked();
+ if (mWallpaperConnection != null) {
+ if (mWallpaperConnection.mEngine != null) {
+ try {
+ mWallpaperConnection.mEngine.setDesiredSize(
+ width, height);
+ } catch (RemoteException e) {
+ }
+ notifyCallbacksLocked();
+ }
+ }
+ }
+ }
+ }
+
+ public int getWidthHint() throws RemoteException {
+ synchronized (mLock) {
+ return mWidth;
+ }
+ }
+
+ public int getHeightHint() throws RemoteException {
+ synchronized (mLock) {
+ return mHeight;
+ }
+ }
+
+ public ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,
+ Bundle outParams) {
+ synchronized (mLock) {
+ try {
+ if (outParams != null) {
+ outParams.putInt("width", mWidth);
+ outParams.putInt("height", mHeight);
+ }
+ mCallbacks.register(cb);
+ File f = WALLPAPER_FILE;
+ if (!f.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ /* Shouldn't happen as we check to see if the file exists */
+ Log.w(TAG, "Error getting wallpaper", e);
+ }
+ return null;
+ }
+ }
+
+ public WallpaperInfo getWallpaperInfo() {
+ synchronized (mLock) {
+ if (mWallpaperConnection != null) {
+ return mWallpaperConnection.mInfo;
+ }
+ return null;
+ }
+ }
+
+ public ParcelFileDescriptor setWallpaper(String name) {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER);
+ synchronized (mLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name);
+ if (pfd != null) {
+ bindWallpaperComponentLocked(null);
+ saveSettingsLocked();
+ }
+ return pfd;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ ParcelFileDescriptor updateWallpaperBitmapLocked(String name) {
+ if (name == null) name = "";
+ try {
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
+ MODE_CREATE|MODE_READ_WRITE);
+ mName = name;
+ return fd;
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "Error setting wallpaper", e);
+ }
+ return null;
+ }
+
+ public void setWallpaperComponent(ComponentName name) {
+ checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
+ synchronized (mLock) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ bindWallpaperComponentLocked(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ void bindWallpaperComponentLocked(ComponentName name) {
+ // Has the component changed?
+ if (mWallpaperConnection != null) {
+ if (mWallpaperComponent == null) {
+ if (name == null) {
+ // Still using default wallpaper.
+ return;
+ }
+ } else if (mWallpaperComponent.equals(name)) {
+ // Changing to same wallpaper.
+ return;
+ }
+ }
+
+ try {
+ ComponentName realName = name;
+ if (realName == null) {
+ // The default component is our static image wallpaper.
+ realName = new ComponentName("android",
+ ImageWallpaper.class.getName());
+ //clearWallpaperComponentLocked();
+ //return;
+ }
+ ServiceInfo si = mContext.getPackageManager().getServiceInfo(realName,
+ PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS);
+ if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
+ throw new SecurityException("Selected service does not require "
+ + android.Manifest.permission.BIND_WALLPAPER
+ + ": " + realName);
+ }
+
+ WallpaperInfo wi = null;
+
+ Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
+ if (name != null) {
+ // Make sure the selected service is actually a wallpaper service.
+ List<ResolveInfo> ris = mContext.getPackageManager()
+ .queryIntentServices(intent, PackageManager.GET_META_DATA);
+ for (int i=0; i<ris.size(); i++) {
+ ServiceInfo rsi = ris.get(i).serviceInfo;
+ if (rsi.name.equals(si.name) &&
+ rsi.packageName.equals(si.packageName)) {
+ try {
+ wi = new WallpaperInfo(mContext, ris.get(i));
+ } catch (XmlPullParserException e) {
+ throw new IllegalArgumentException(e);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ break;
+ }
+ }
+ if (wi == null) {
+ throw new SecurityException("Selected service is not a wallpaper: "
+ + realName);
+ }
+ }
+
+ // Bind the service!
+ WallpaperConnection newConn = new WallpaperConnection(wi);
+ intent.setComponent(realName);
+ intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+ com.android.internal.R.string.wallpaper_binding_label);
+ intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+ mContext, 0,
+ Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
+ mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
+ 0));
+ if (!mContext.bindService(intent, newConn,
+ Context.BIND_AUTO_CREATE)) {
+ throw new IllegalArgumentException("Unable to bind service: "
+ + name);
+ }
+
+ clearWallpaperComponentLocked();
+ mWallpaperComponent = name;
+ mWallpaperConnection = newConn;
+ mLastDiedTime = SystemClock.uptimeMillis();
+ try {
+ if (DEBUG) Log.v(TAG, "Adding window token: " + newConn.mToken);
+ mIWindowManager.addWindowToken(newConn.mToken,
+ WindowManager.LayoutParams.TYPE_WALLPAPER);
+ } catch (RemoteException e) {
+ }
+
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("Unknown component " + name);
+ }
+ }
+
+ void clearWallpaperComponentLocked() {
+ mWallpaperComponent = null;
+ if (mWallpaperConnection != null) {
+ if (mWallpaperConnection.mEngine != null) {
+ try {
+ mWallpaperConnection.mEngine.destroy();
+ } catch (RemoteException e) {
+ }
+ }
+ mContext.unbindService(mWallpaperConnection);
+ try {
+ if (DEBUG) Log.v(TAG, "Removing window token: "
+ + mWallpaperConnection.mToken);
+ mIWindowManager.removeWindowToken(mWallpaperConnection.mToken);
+ } catch (RemoteException e) {
+ }
+ mWallpaperConnection = null;
+ }
+ }
+
+ void attachServiceLocked(WallpaperConnection conn) {
+ try {
+ conn.mService.attach(conn, conn.mToken,
+ WindowManager.LayoutParams.TYPE_WALLPAPER, false,
+ mWidth, mHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed attaching wallpaper; clearing", e);
+ bindWallpaperComponentLocked(null);
+ }
+ }
+
+ private void notifyCallbacksLocked() {
+ final int n = mCallbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).onWallpaperChanged();
+ } catch (RemoteException e) {
+
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ mCallbacks.finishBroadcast();
+ final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
+ mContext.sendBroadcast(intent);
+ }
+
+ private void checkPermission(String permission) {
+ if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
+ throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ + ", must have permission " + permission);
+ }
+ }
+
+ private static JournaledFile makeJournaledFile() {
+ final String base = "/data/system/wallpaper_info.xml";
+ return new JournaledFile(new File(base), new File(base + ".tmp"));
+ }
+
+ private void saveSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "wp");
+ out.attribute(null, "width", Integer.toString(mWidth));
+ out.attribute(null, "height", Integer.toString(mHeight));
+ out.attribute(null, "name", mName);
+ if (mWallpaperComponent != null) {
+ out.attribute(null, "component",
+ mWallpaperComponent.flattenToShortString());
+ }
+ out.endTag(null, "wp");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
+ private void loadSettingsLocked() {
+ JournaledFile journal = makeJournaledFile();
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ boolean success = false;
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("wp".equals(tag)) {
+ mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
+ mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
+ mName = parser.getAttributeValue(null, "name");
+ String comp = parser.getAttributeValue(null, "component");
+ mWallpaperComponent = comp != null
+ ? ComponentName.unflattenFromString(comp)
+ : null;
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (NullPointerException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ mWidth = -1;
+ mHeight = -1;
+ mName = "";
+ }
+ }
+
+ void settingsRestored() {
+ boolean success = false;
+ synchronized (mLock) {
+ loadSettingsLocked();
+ // If there's a wallpaper name, we use that. If that can't be loaded, then we
+ // use the default.
+ if ("".equals(mName)) {
+ success = true;
+ } else {
+ success = restoreNamedResourceLocked();
+ }
+ }
+
+ if (!success) {
+ Log.e(TAG, "Failed to restore wallpaper: '" + mName + "'");
+ mName = "";
+ WALLPAPER_FILE.delete();
+ }
+ saveSettingsLocked();
+ }
+
+ boolean restoreNamedResourceLocked() {
+ if (mName.length() > 4 && "res:".equals(mName.substring(0, 4))) {
+ String resName = mName.substring(4);
+
+ String pkg = null;
+ int colon = resName.indexOf(':');
+ if (colon > 0) {
+ pkg = resName.substring(0, colon);
+ }
+
+ String ident = null;
+ int slash = resName.lastIndexOf('/');
+ if (slash > 0) {
+ ident = resName.substring(slash+1);
+ }
+
+ String type = null;
+ if (colon > 0 && slash > 0 && (slash-colon) > 1) {
+ type = resName.substring(colon+1, slash);
+ }
+
+ if (pkg != null && ident != null && type != null) {
+ int resId = -1;
+ InputStream res = null;
+ FileOutputStream fos = null;
+ try {
+ Context c = mContext.createPackageContext(pkg, Context.CONTEXT_RESTRICTED);
+ Resources r = c.getResources();
+ resId = r.getIdentifier(resName, null, null);
+ if (resId == 0) {
+ Log.e(TAG, "couldn't resolve identifier pkg=" + pkg + " type=" + type
+ + " ident=" + ident);
+ return false;
+ }
+
+ res = r.openRawResource(resId);
+ fos = new FileOutputStream(WALLPAPER_FILE);
+
+ byte[] buffer = new byte[32768];
+ int amt;
+ while ((amt=res.read(buffer)) > 0) {
+ fos.write(buffer, 0, amt);
+ }
+ // mWallpaperObserver will notice the close and send the change broadcast
+
+ Log.d(TAG, "Restored wallpaper: " + resName);
+ return true;
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Package name " + pkg + " not found");
+ } catch (Resources.NotFoundException e) {
+ Log.e(TAG, "Resource not found: " + resId);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while restoring wallpaper ", e);
+ } finally {
+ if (res != null) {
+ try {
+ res.close();
+ } catch (IOException ex) {}
+ }
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (IOException ex) {}
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ pw.println("Permission Denial: can't dump wallpaper service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mLock) {
+ pw.println("Current Wallpaper Service state:");
+ pw.print(" mWidth="); pw.print(mWidth);
+ pw.print(" mHeight="); pw.println(mHeight);
+ pw.print(" mName="); pw.println(mName);
+ pw.print(" mWallpaperComponent="); pw.println(mWallpaperComponent);
+ if (mWallpaperConnection != null) {
+ WallpaperConnection conn = mWallpaperConnection;
+ pw.print(" Wallpaper connection ");
+ pw.print(conn); pw.println(":");
+ pw.print(" mInfo.component="); pw.println(conn.mInfo.getComponent());
+ pw.print(" mToken="); pw.println(conn.mToken);
+ pw.print(" mService="); pw.println(conn.mService);
+ pw.print(" mEngine="); pw.println(conn.mEngine);
+ pw.print(" mLastDiedTime=");
+ pw.println(mLastDiedTime - SystemClock.uptimeMillis());
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/WallpaperService.java b/services/java/com/android/server/WallpaperService.java
deleted file mode 100644
index d921baf..0000000
--- a/services/java/com/android/server/WallpaperService.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static android.os.FileObserver.*;
-import static android.os.ParcelFileDescriptor.*;
-
-import android.app.IWallpaperService;
-import android.app.IWallpaperServiceCallback;
-import android.backup.BackupManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.RemoteException;
-import android.os.FileObserver;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteCallbackList;
-import android.util.Config;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-class WallpaperService extends IWallpaperService.Stub {
- private static final String TAG = WallpaperService.class.getSimpleName();
-
- private static final File WALLPAPER_DIR = new File(
- "/data/data/com.android.settings/files");
- private static final String WALLPAPER = "wallpaper";
- private static final File WALLPAPER_FILE = new File(WALLPAPER_DIR, WALLPAPER);
-
- private static final String PREFERENCES = "wallpaper-hints";
-
- private static final String HINT_WIDTH = "hintWidth";
- private static final String HINT_HEIGHT = "hintHeight";
-
- /**
- * List of callbacks registered they should each be notified
- * when the wallpaper is changed.
- */
- private final RemoteCallbackList<IWallpaperServiceCallback> mCallbacks
- = new RemoteCallbackList<IWallpaperServiceCallback>();
-
- /**
- * Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
- * that the wallpaper has changed. The CREATE is triggered when there is no
- * wallpaper set and is created for the first time. The CLOSE_WRITE is triggered
- * everytime the wallpaper is changed.
- */
- private final FileObserver mWallpaperObserver = new FileObserver(
- WALLPAPER_DIR.getAbsolutePath(), CREATE | CLOSE_WRITE) {
- @Override
- public void onEvent(int event, String path) {
- if (path == null) {
- return;
- }
-
- File changedFile = new File(WALLPAPER_DIR, path);
- if (WALLPAPER_FILE.equals(changedFile)) {
- notifyCallbacks();
- }
- }
- };
-
- private final Context mContext;
-
- private int mWidth = -1;
- private int mHeight = -1;
-
- public WallpaperService(Context context) {
- if (Config.LOGD) Log.d(TAG, "WallpaperService startup");
- mContext = context;
- createFilesDir();
- mWallpaperObserver.startWatching();
-
- SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES,
- Context.MODE_PRIVATE);
- mWidth = preferences.getInt(HINT_WIDTH, -1);
- mHeight = preferences.getInt(HINT_HEIGHT, -1);
- }
-
- @Override
- protected void finalize() throws Throwable {
- super.finalize();
- mWallpaperObserver.stopWatching();
- }
-
- public void clearWallpaper() {
- File f = WALLPAPER_FILE;
- if (f.exists()) {
- f.delete();
- }
- }
-
- public void setDimensionHints(int width, int height) throws RemoteException {
- checkPermission(android.Manifest.permission.SET_WALLPAPER_HINTS);
-
- if (width <= 0 || height <= 0) {
- throw new IllegalArgumentException("width and height must be > 0");
- }
-
- if (width != mWidth || height != mHeight) {
- mWidth = width;
- mHeight = height;
-
- SharedPreferences preferences = mContext.getSharedPreferences(PREFERENCES,
- Context.MODE_PRIVATE);
-
- final SharedPreferences.Editor editor = preferences.edit();
- editor.putInt(HINT_WIDTH, width);
- editor.putInt(HINT_HEIGHT, height);
- editor.commit();
- }
- }
-
- public int getWidthHint() throws RemoteException {
- return mWidth;
- }
-
- public int getHeightHint() throws RemoteException {
- return mHeight;
- }
-
- public ParcelFileDescriptor getWallpaper(IWallpaperServiceCallback cb) {
- try {
- mCallbacks.register(cb);
- File f = WALLPAPER_FILE;
- if (!f.exists()) {
- return null;
- }
- return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
- } catch (FileNotFoundException e) {
-
- /* Shouldn't happen as we check to see if the file exists */
- if (Config.LOGD) Log.d(TAG, "Error getting wallpaper", e);
- }
- return null;
- }
-
- public ParcelFileDescriptor setWallpaper() {
- checkPermission(android.Manifest.permission.SET_WALLPAPER);
- try {
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE,
- MODE_CREATE|MODE_READ_WRITE);
-
- // changing the wallpaper means we'll need to back up the new one
- long origId = Binder.clearCallingIdentity();
- BackupManager bm = new BackupManager(mContext);
- bm.dataChanged();
- Binder.restoreCallingIdentity(origId);
-
- return fd;
- } catch (FileNotFoundException e) {
- if (Config.LOGD) Log.d(TAG, "Error setting wallpaper", e);
- }
- return null;
- }
-
- private void createFilesDir() {
- if (!WALLPAPER_DIR.exists()) {
- WALLPAPER_DIR.mkdirs();
- }
- }
-
- private void notifyCallbacks() {
- final int n = mCallbacks.beginBroadcast();
- for (int i = 0; i < n; i++) {
- try {
- mCallbacks.getBroadcastItem(i).onWallpaperChanged();
- } catch (RemoteException e) {
-
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
- }
- mCallbacks.finishBroadcast();
- final Intent intent = new Intent(Intent.ACTION_WALLPAPER_CHANGED);
- mContext.sendBroadcast(intent);
- }
-
- private void checkPermission(String permission) {
- if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) {
- throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
- + ", must have permission " + permission);
- }
- }
-}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 394ed3a..2dc747e 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -41,6 +41,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.SupplicantState;
import android.net.NetworkStateTracker;
import android.net.DhcpInfo;
+import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -92,6 +93,9 @@ public class WifiService extends IWifiManager.Stub {
private boolean mDeviceIdle;
private int mPluggedType;
+ // true if the user enabled Wifi while in airplane mode
+ private boolean mAirplaneModeOverwridden;
+
private final LockList mLocks = new LockList();
// some wifi lock statistics
private int mFullLocksAcquired;
@@ -142,28 +146,6 @@ public class WifiService extends IWifiManager.Stub {
private final WifiHandler mWifiHandler;
/*
- * Map used to keep track of hidden networks presence, which
- * is needed to switch between active and passive scan modes.
- * If there is at least one hidden network that is currently
- * present (enabled), we want to do active scans instead of
- * passive.
- */
- private final Map<Integer, Boolean> mIsHiddenNetworkPresent;
- /*
- * The number of currently present hidden networks. When this
- * counter goes from 0 to 1 or from 1 to 0, we change the
- * scan mode to active or passive respectively. Initially, we
- * set the counter to 0 and we increment it every time we add
- * a new present (enabled) hidden network.
- */
- private int mNumHiddenNetworkPresent;
- /*
- * Whether we change the scan mode is due to a hidden network
- * (in this class, this is always the case)
- */
- private final static boolean SET_DUE_TO_A_HIDDEN_NETWORK = true;
-
- /*
* Cache of scan results objects (size is somewhat arbitrary)
*/
private static final int SCAN_RESULT_CACHE_SIZE = 80;
@@ -195,12 +177,6 @@ public class WifiService extends IWifiManager.Stub {
mWifiStateTracker.enableRssiPolling(true);
mBatteryStats = BatteryStatsService.getService();
- /*
- * Initialize the hidden-networks state
- */
- mIsHiddenNetworkPresent = new HashMap<Integer, Boolean>();
- mNumHiddenNetworkPresent = 0;
-
mScanResultCache = new LinkedHashMap<String, ScanResult>(
SCAN_RESULT_CACHE_SIZE, 0.75f, true) {
/*
@@ -246,6 +222,8 @@ public class WifiService extends IWifiManager.Stub {
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ // clear our flag indicating the user has overwridden airplane mode
+ mAirplaneModeOverwridden = false;
updateWifiState();
}
},
@@ -254,155 +232,6 @@ public class WifiService extends IWifiManager.Stub {
setWifiEnabledBlocking(wifiEnabled, false, Process.myUid());
}
- /**
- * Initializes the hidden networks state. Must be called when we
- * enable Wi-Fi.
- */
- private synchronized void initializeHiddenNetworksState() {
- // First, reset the state
- resetHiddenNetworksState();
-
- // ... then add networks that are marked as hidden
- List<WifiConfiguration> networks = getConfiguredNetworks();
- if (!networks.isEmpty()) {
- for (WifiConfiguration config : networks) {
- if (config != null && config.hiddenSSID) {
- addOrUpdateHiddenNetwork(
- config.networkId,
- config.status != WifiConfiguration.Status.DISABLED);
- }
- }
-
- }
- }
-
- /**
- * Resets the hidden networks state.
- */
- private synchronized void resetHiddenNetworksState() {
- mNumHiddenNetworkPresent = 0;
- mIsHiddenNetworkPresent.clear();
- }
-
- /**
- * Marks all but netId network as not present.
- */
- private synchronized void markAllHiddenNetworksButOneAsNotPresent(int netId) {
- for (Map.Entry<Integer, Boolean> entry : mIsHiddenNetworkPresent.entrySet()) {
- if (entry != null) {
- Integer networkId = entry.getKey();
- if (networkId != netId) {
- updateNetworkIfHidden(
- networkId, false);
- }
- }
- }
- }
-
- /**
- * Updates the netId network presence status if netId is an existing
- * hidden network.
- */
- private synchronized void updateNetworkIfHidden(int netId, boolean present) {
- if (isHiddenNetwork(netId)) {
- addOrUpdateHiddenNetwork(netId, present);
- }
- }
-
- /**
- * Updates the netId network presence status if netId is an existing
- * hidden network. If the network does not exist, adds the network.
- */
- private synchronized void addOrUpdateHiddenNetwork(int netId, boolean present) {
- if (0 <= netId) {
-
- // If we are adding a new entry or modifying an existing one
- Boolean isPresent = mIsHiddenNetworkPresent.get(netId);
- if (isPresent == null || isPresent != present) {
- if (present) {
- incrementHiddentNetworkPresentCounter();
- } else {
- // If we add a new hidden network, no need to change
- // the counter (it must be 0)
- if (isPresent != null) {
- decrementHiddentNetworkPresentCounter();
- }
- }
- mIsHiddenNetworkPresent.put(netId, present);
- }
- } else {
- Log.e(TAG, "addOrUpdateHiddenNetwork(): Invalid (negative) network id!");
- }
- }
-
- /**
- * Removes the netId network if it is hidden (being kept track of).
- */
- private synchronized void removeNetworkIfHidden(int netId) {
- if (isHiddenNetwork(netId)) {
- removeHiddenNetwork(netId);
- }
- }
-
- /**
- * Removes the netId network. For the call to be successful, the network
- * must be hidden.
- */
- private synchronized void removeHiddenNetwork(int netId) {
- if (0 <= netId) {
- Boolean isPresent =
- mIsHiddenNetworkPresent.remove(netId);
- if (isPresent != null) {
- // If we remove an existing hidden network that is not
- // present, no need to change the counter
- if (isPresent) {
- decrementHiddentNetworkPresentCounter();
- }
- } else {
- if (DBG) {
- Log.d(TAG, "removeHiddenNetwork(): Removing a non-existent network!");
- }
- }
- } else {
- Log.e(TAG, "removeHiddenNetwork(): Invalid (negative) network id!");
- }
- }
-
- /**
- * Returns true if netId is an existing hidden network.
- */
- private synchronized boolean isHiddenNetwork(int netId) {
- return mIsHiddenNetworkPresent.containsKey(netId);
- }
-
- /**
- * Increments the present (enabled) hidden networks counter. If the
- * counter value goes from 0 to 1, changes the scan mode to active.
- */
- private void incrementHiddentNetworkPresentCounter() {
- ++mNumHiddenNetworkPresent;
- if (1 == mNumHiddenNetworkPresent) {
- // Switch the scan mode to "active"
- mWifiStateTracker.setScanMode(true, SET_DUE_TO_A_HIDDEN_NETWORK);
- }
- }
-
- /**
- * Decrements the present (enabled) hidden networks counter. If the
- * counter goes from 1 to 0, changes the scan mode back to passive.
- */
- private void decrementHiddentNetworkPresentCounter() {
- if (0 < mNumHiddenNetworkPresent) {
- --mNumHiddenNetworkPresent;
- if (0 == mNumHiddenNetworkPresent) {
- // Switch the scan mode to "passive"
- mWifiStateTracker.setScanMode(false, SET_DUE_TO_A_HIDDEN_NETWORK);
- }
- } else {
- Log.e(TAG, "Hidden-network counter invariant violation!");
- }
- }
-
private boolean getPersistedWifiEnabled() {
final ContentResolver cr = mContext.getContentResolver();
try {
@@ -468,6 +297,8 @@ public class WifiService extends IWifiManager.Stub {
synchronized (mWifiHandler) {
sWakeLock.acquire();
mLastEnableUid = Binder.getCallingUid();
+ // set a flag if the user is enabling Wifi while in airplane mode
+ mAirplaneModeOverwridden = (enable && isAirplaneModeOn() && isAirplaneToggleable());
sendEnableMessage(enable, true, Binder.getCallingUid());
}
@@ -488,7 +319,7 @@ public class WifiService extends IWifiManager.Stub {
if (mWifiState == eventualWifiState) {
return true;
}
- if (enable && isAirplaneModeOn()) {
+ if (enable && isAirplaneModeOn() && !mAirplaneModeOverwridden) {
return false;
}
@@ -522,7 +353,7 @@ public class WifiService extends IWifiManager.Stub {
}
// We must reset the interface before we unload the driver
- mWifiStateTracker.resetInterface();
+ mWifiStateTracker.resetInterface(false);
if (!WifiNative.unloadDriver()) {
Log.e(TAG, "Failed to unload Wi-Fi driver.");
@@ -544,12 +375,10 @@ public class WifiService extends IWifiManager.Stub {
setWifiEnabledState(eventualWifiState, uid);
/*
- * Initialize the hidden networks state and the number of allowed
- * radio channels if Wi-Fi is being turned on.
+ * Initialize the number of allowed radio channels if Wi-Fi is being turned on.
*/
if (enable) {
mWifiStateTracker.setNumAllowedChannels();
- initializeHiddenNetworksState();
}
return true;
@@ -884,15 +713,6 @@ public class WifiService extends IWifiManager.Stub {
}
mNeedReconfig = mNeedReconfig || doReconfig;
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- if (config.hiddenSSID) {
- // Mark the network as present unless it is disabled
- addOrUpdateHiddenNetwork(
- netId, config.status != WifiConfiguration.Status.DISABLED);
- }
-
setVariables: {
/*
* Note that if a networkId for a non-existent network
@@ -1231,11 +1051,6 @@ public class WifiService extends IWifiManager.Stub {
public boolean removeNetwork(int netId) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- removeNetworkIfHidden(netId);
-
return mWifiStateTracker.removeNetwork(netId);
}
@@ -1249,18 +1064,14 @@ public class WifiService extends IWifiManager.Stub {
public boolean enableNetwork(int netId, boolean disableOthers) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- synchronized(this) {
- if (disableOthers) {
- markAllHiddenNetworksButOneAsNotPresent(netId);
- }
- updateNetworkIfHidden(netId, true);
- }
-
synchronized (mWifiStateTracker) {
- return WifiNative.enableNetworkCommand(netId, disableOthers);
+ String ifname = mWifiStateTracker.getInterfaceName();
+ NetworkUtils.enableInterface(ifname);
+ boolean result = WifiNative.enableNetworkCommand(netId, disableOthers);
+ if (!result) {
+ NetworkUtils.disableInterface(ifname);
+ }
+ return result;
}
}
@@ -1273,11 +1084,6 @@ public class WifiService extends IWifiManager.Stub {
public boolean disableNetwork(int netId) {
enforceChangePermission();
- /*
- * If we have hidden networks, we may have to change the scan mode
- */
- updateNetworkIfHidden(netId, false);
-
synchronized (mWifiStateTracker) {
return WifiNative.disableNetworkCommand(netId);
}
@@ -1375,45 +1181,49 @@ public class WifiService extends IWifiManager.Stub {
level = 0;
}
- // bssid is the hash key
- scanResult = mScanResultCache.get(bssid);
- if (scanResult != null) {
- scanResult.level = level;
- } else {
- /*
- * The formatting of the results returned by
- * wpa_supplicant is intended to make the fields
- * line up nicely when printed,
- * not to make them easy to parse. So we have to
- * apply some heuristics to figure out which field
- * is the SSID and which field is the flags.
- */
- String ssid;
- String flags;
- if (result.length == 4) {
- if (result[3].charAt(0) == '[') {
- flags = result[3];
- ssid = "";
- } else {
- flags = "";
- ssid = result[3];
- }
- } else if (result.length == 5) {
+ /*
+ * The formatting of the results returned by
+ * wpa_supplicant is intended to make the fields
+ * line up nicely when printed,
+ * not to make them easy to parse. So we have to
+ * apply some heuristics to figure out which field
+ * is the SSID and which field is the flags.
+ */
+ String ssid;
+ String flags;
+ if (result.length == 4) {
+ if (result[3].charAt(0) == '[') {
flags = result[3];
- ssid = result[4];
+ ssid = "";
} else {
- // Here, we must have 3 fields: no flags and ssid
- // set
flags = "";
- ssid = "";
+ ssid = result[3];
}
+ } else if (result.length == 5) {
+ flags = result[3];
+ ssid = result[4];
+ } else {
+ // Here, we must have 3 fields: no flags and ssid
+ // set
+ flags = "";
+ ssid = "";
+ }
+ // bssid + ssid is the hash key
+ String key = bssid + ssid;
+ scanResult = mScanResultCache.get(key);
+ if (scanResult != null) {
+ scanResult.level = level;
+ scanResult.SSID = ssid;
+ scanResult.capabilities = flags;
+ scanResult.frequency = frequency;
+ } else {
// Do not add scan results that have no SSID set
if (0 < ssid.trim().length()) {
scanResult =
new ScanResult(
ssid, bssid, flags, level, frequency);
- mScanResultCache.put(bssid, scanResult);
+ mScanResultCache.put(key, scanResult);
}
}
} else {
@@ -1479,14 +1289,17 @@ public class WifiService extends IWifiManager.Stub {
* Set the number of radio frequency channels that are allowed to be used
* in the current regulatory domain. This method should be used only
* if the correct number of channels cannot be determined automatically
- * for some reason. If the operation is successful, the new value is
+ * for some reason. If the operation is successful, the new value may be
* persisted as a Secure setting.
* @param numChannels the number of allowed channels. Must be greater than 0
* and less than or equal to 16.
+ * @param persist {@code true} if the setting should be remembered.
* @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
* {@code numChannels} is outside the valid range.
*/
- public boolean setNumAllowedChannels(int numChannels) {
+ public boolean setNumAllowedChannels(int numChannels, boolean persist) {
+ Log.i(TAG, "WifiService trying to setNumAllowed to "+numChannels+
+ " with persist set to "+persist);
enforceChangePermission();
/*
* Validate the argument. We'd like to let the Wi-Fi driver do this,
@@ -1505,9 +1318,11 @@ public class WifiService extends IWifiManager.Stub {
return false;
}
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
- numChannels);
+ if (persist) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS,
+ numChannels);
+ }
mWifiStateTracker.setNumAllowedChannels(numChannels);
return true;
}
@@ -1688,7 +1503,7 @@ public class WifiService extends IWifiManager.Stub {
private void updateWifiState() {
boolean wifiEnabled = getPersistedWifiEnabled();
- boolean airplaneMode = isAirplaneModeOn();
+ boolean airplaneMode = isAirplaneModeOn() && !mAirplaneModeOverwridden;
boolean lockHeld = mLocks.hasLocks();
int strongestLockMode;
boolean wifiShouldBeEnabled = wifiEnabled && !airplaneMode;
@@ -1752,6 +1567,13 @@ public class WifiService extends IWifiManager.Stub {
|| airplaneModeRadios.contains(Settings.System.RADIO_WIFI);
}
+ private boolean isAirplaneToggleable() {
+ String toggleableRadios = Settings.System.getString(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+ return toggleableRadios != null
+ && toggleableRadios.contains(Settings.System.RADIO_WIFI);
+ }
+
/**
* Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
* currently on.
@@ -2068,7 +1890,9 @@ public class WifiService extends IWifiManager.Stub {
// our new size == 1 (first call), but this function won't
// be called often and by making the stopPacket call each
// time we're less fragile and self-healing.
- WifiNative.stopPacketFiltering();
+ synchronized (mWifiStateTracker) {
+ WifiNative.stopPacketFiltering();
+ }
}
int uid = Binder.getCallingUid();
@@ -2104,7 +1928,9 @@ public class WifiService extends IWifiManager.Stub {
removed.unlinkDeathRecipient();
}
if (mMulticasters.size() == 0) {
- WifiNative.startPacketFiltering();
+ synchronized (mWifiStateTracker) {
+ WifiNative.startPacketFiltering();
+ }
}
Long ident = Binder.clearCallingIdentity();
diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java
index d4c27b7..b0e4038 100644
--- a/services/java/com/android/server/WindowManagerService.java
+++ b/services/java/com/android/server/WindowManagerService.java
@@ -31,15 +31,15 @@ import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
-import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE;
import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import com.android.internal.app.IBatteryStats;
import com.android.internal.policy.PolicyManager;
@@ -66,6 +66,7 @@ import android.os.Binder;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
+import android.os.LatencyTimer;
import android.os.LocalPowerManager;
import android.os.Looper;
import android.os.Message;
@@ -124,7 +125,8 @@ import java.util.Iterator;
import java.util.List;
/** {@hide} */
-public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor {
+public class WindowManagerService extends IWindowManager.Stub
+ implements Watchdog.Monitor, KeyInputQueue.HapticFeedbackCallback {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
@@ -133,11 +135,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
+ static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
+ static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
+ static final boolean MEASURE_LATENCY = false;
+ static private LatencyTimer lt;
static final boolean PROFILE_ORIENTATION = false;
static final boolean BLUR = true;
@@ -370,11 +376,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// made visible or hidden at the next transition.
int mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
boolean mAppTransitionReady = false;
+ boolean mAppTransitionRunning = false;
boolean mAppTransitionTimeout = false;
boolean mStartingIconInTransition = false;
boolean mSkipAppTransitionAnimation = false;
final ArrayList<AppWindowToken> mOpeningApps = new ArrayList<AppWindowToken>();
final ArrayList<AppWindowToken> mClosingApps = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mToTopApps = new ArrayList<AppWindowToken>();
+ final ArrayList<AppWindowToken> mToBottomApps = new ArrayList<AppWindowToken>();
//flag to detect fat touch events
boolean mFatTouch = false;
@@ -395,6 +404,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowState mInputMethodWindow = null;
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<WindowState>();
+ final ArrayList<WindowToken> mWallpaperTokens = new ArrayList<WindowToken>();
+
+ // If non-null, this is the currently visible window that is associated
+ // with the wallpaper.
+ WindowState mWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the lower one in Z-order.
+ WindowState mLowerWallpaperTarget = null;
+ // If non-null, we are in the middle of animating from one wallpaper target
+ // to another, and this is the higher one in Z-order.
+ WindowState mUpperWallpaperTarget = null;
+ int mWallpaperAnimLayerAdjustment;
+ float mLastWallpaperX;
+ float mLastWallpaperY;
+
AppWindowToken mFocusedApp = null;
PowerManagerService mPowerManager;
@@ -512,6 +536,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private WindowManagerService(Context context, PowerManagerService pm,
boolean haveInputMethods) {
+ if (MEASURE_LATENCY) {
+ lt = new LatencyTimer(100, 1000);
+ }
+
mContext = context;
mHaveInputMethods = haveInputMethods;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -666,7 +694,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
i--;
break;
}
- if (t.windows.size() > 0) {
+
+ // We haven't reached the token yet; if this token
+ // is not going to the bottom and has windows, we can
+ // use it as an anchor for when we do reach the token.
+ if (!t.sendingToBottom && t.windows.size() > 0) {
pos = t.windows.get(0);
}
}
@@ -688,6 +720,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
placeWindowBefore(pos, win);
} else {
+ // Continue looking down until we find the first
+ // token that has windows.
while (i >= 0) {
AppWindowToken t = mAppTokens.get(i);
final int NW = t.windows.size();
@@ -1159,6 +1193,451 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
}
+ final boolean isWallpaperVisible(WindowState wallpaperTarget) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper vis: target obscured="
+ + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
+ + " anim=" + ((wallpaperTarget != null && wallpaperTarget.mAppToken != null)
+ ? wallpaperTarget.mAppToken.animation : null)
+ + " upper=" + mUpperWallpaperTarget
+ + " lower=" + mLowerWallpaperTarget);
+ return (wallpaperTarget != null
+ && (!wallpaperTarget.mObscured || (wallpaperTarget.mAppToken != null
+ && wallpaperTarget.mAppToken.animation != null)))
+ || mUpperWallpaperTarget != null
+ || mLowerWallpaperTarget != null;
+ }
+
+ static final int ADJUST_WALLPAPER_LAYERS_CHANGED = 1<<1;
+ static final int ADJUST_WALLPAPER_VISIBILITY_CHANGED = 1<<2;
+
+ int adjustWallpaperWindowsLocked() {
+ int changed = 0;
+
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ // First find top-most window that has asked to be on top of the
+ // wallpaper; all wallpapers go behind it.
+ final ArrayList localmWindows = mWindows;
+ int N = localmWindows.size();
+ WindowState w = null;
+ WindowState foundW = null;
+ int foundI = 0;
+ int i = N;
+ while (i > 0) {
+ i--;
+ w = (WindowState)localmWindows.get(i);
+ if (w.mAppToken != null) {
+ // If this window's app token is hidden and not animating,
+ // it is of no interest to us.
+ if (w.mAppToken.hidden && w.mAppToken.animation == null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Skipping hidden or animating token: " + w);
+ continue;
+ }
+ // If this window's app token is ot fullscreen, also irrelevant.
+ if (!w.mAppToken.appFullscreen) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Skipping non-fullscreen token: " + w);
+ continue;
+ }
+ }
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w + ": readyfordisplay="
+ + w.isReadyForDisplay() + " drawpending=" + w.mDrawPending
+ + " commitdrawpending=" + w.mCommitDrawPending);
+ if ((w.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0 && w.isReadyForDisplay()
+ && (mWallpaperTarget == w
+ || (!w.mDrawPending && !w.mCommitDrawPending))) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Found wallpaper activity: #" + i + "=" + w);
+ foundW = w;
+ foundI = i;
+ if (w == mWallpaperTarget && w.mAppToken != null
+ && w.mAppToken.animation != null) {
+ // The current wallpaper target is animating, so we'll
+ // look behind it for another possible target and figure
+ // out what is going on below.
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Win " + w
+ + ": token animating, looking behind.");
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ // If we are currently waiting for an app transition, and either
+ // the current target or the next target are involved with it,
+ // then hold off on doing anything with the wallpaper.
+ // Note that we are checking here for just whether the target
+ // is part of an app token... which is potentially overly aggressive
+ // (the app token may not be involved in the transition), but good
+ // enough (we'll just wait until whatever transition is pending
+ // executes).
+ if (mWallpaperTarget != null && mWallpaperTarget.mAppToken != null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper not changing: waiting for app anim in current target");
+ return 0;
+ }
+ if (foundW != null && foundW.mAppToken != null) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper not changing: waiting for app anim in found target");
+ return 0;
+ }
+ }
+
+ if (mWallpaperTarget != foundW) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New wallpaper target: " + foundW
+ + " oldTarget: " + mWallpaperTarget);
+ }
+
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+
+ WindowState oldW = mWallpaperTarget;
+ mWallpaperTarget = foundW;
+
+ // Now what is happening... if the current and new targets are
+ // animating, then we are in our super special mode!
+ if (foundW != null && foundW.mAppToken != null && oldW != null
+ && oldW.mAppToken != null) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New animation: " + foundW.mAppToken.animation
+ + " old animation: " + oldW.mAppToken.animation);
+ }
+ if (foundW.mAppToken.animation != null
+ && oldW.mAppToken.animation != null) {
+ int oldI = localmWindows.indexOf(oldW);
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "New i: " + foundI + " old i: " + oldI);
+ }
+ if (oldI >= 0) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Animating wallpapers: old#" + oldI
+ + "=" + oldW + "; new#" + foundI
+ + "=" + foundW);
+ }
+
+ // Set the new target correctly.
+ if (foundW.mAppToken.hiddenRequested) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Old wallpaper still the target.");
+ }
+ mWallpaperTarget = oldW;
+ }
+
+ // Now set the upper and lower wallpaper targets
+ // correctly, and make sure that we are positioning
+ // the wallpaper below the lower.
+ if (foundI > oldI) {
+ // The new target is on top of the old one.
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Found target above old target.");
+ }
+ mUpperWallpaperTarget = foundW;
+ mLowerWallpaperTarget = oldW;
+ foundW = oldW;
+ foundI = oldI;
+ } else {
+ // The new target is below the old one.
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "Found target below old target.");
+ }
+ mUpperWallpaperTarget = oldW;
+ mLowerWallpaperTarget = foundW;
+ }
+ }
+ }
+ }
+
+ } else {
+ // Is it time to stop animating?
+ if (mLowerWallpaperTarget == null
+ || mLowerWallpaperTarget.mAppToken.animation == null
+ || mUpperWallpaperTarget == null
+ || mUpperWallpaperTarget.mAppToken.animation == null) {
+ if (DEBUG_WALLPAPER) {
+ Log.v(TAG, "No longer animating wallpaper targets!");
+ }
+ mLowerWallpaperTarget = null;
+ mUpperWallpaperTarget = null;
+ }
+ }
+
+ boolean visible = foundW != null;
+ if (visible) {
+ // The window is visible to the compositor... but is it visible
+ // to the user? That is what the wallpaper cares about.
+ visible = isWallpaperVisible(foundW);
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper visibility: " + visible);
+
+ // If the wallpaper target is animating, we may need to copy
+ // its layer adjustment. Only do this if we are not transfering
+ // between two wallpaper targets.
+ mWallpaperAnimLayerAdjustment =
+ (mLowerWallpaperTarget == null && foundW.mAppToken != null)
+ ? foundW.mAppToken.animLayerAdjustment : 0;
+
+ // Now w is the window we are supposed to be behind... but we
+ // need to be sure to also be behind any of its attached windows,
+ // AND any starting window associated with it.
+ while (foundI > 0) {
+ WindowState wb = (WindowState)localmWindows.get(foundI-1);
+ if (wb.mAttachedWindow != foundW &&
+ (wb.mAttrs.type != TYPE_APPLICATION_STARTING ||
+ wb.mToken != foundW.mToken)) {
+ // This window is not related to the previous one in any
+ // interesting way, so stop here.
+ break;
+ }
+ foundW = wb;
+ foundI--;
+ }
+ } else {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper not visible");
+ }
+
+ // Okay i is the position immediately above the wallpaper. Look at
+ // what is below it for later.
+ foundW = foundI > 0 ? (WindowState)localmWindows.get(foundI-1) : null;
+
+ if (visible) {
+ mLastWallpaperX = mWallpaperTarget.mWallpaperX;
+ mLastWallpaperY = mWallpaperTarget.mWallpaperY;
+ }
+
+ // Start stepping backwards from here, ensuring that our wallpaper windows
+ // are correctly placed.
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ if (token.hidden == visible) {
+ changed |= ADJUST_WALLPAPER_VISIBILITY_CHANGED;
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the
+ // correct size.
+ mLayoutNeeded = true;
+ }
+
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+
+ if (visible) {
+ updateWallpaperOffsetLocked(wallpaper, dw, dh);
+ }
+
+ // First, make sure the client has the current visibility
+ // state.
+ if (wallpaper.mWallpaperVisible != visible) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG,
+ "Setting visibility of wallpaper " + wallpaper
+ + ": " + visible);
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+
+ wallpaper.mAnimLayer = wallpaper.mLayer + mWallpaperAnimLayerAdjustment;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+
+ // First, if this window is at the current index, then all
+ // is well.
+ if (wallpaper == foundW) {
+ foundI--;
+ foundW = foundI > 0
+ ? (WindowState)localmWindows.get(foundI-1) : null;
+ continue;
+ }
+
+ // The window didn't match... the current wallpaper window,
+ // wherever it is, is in the wrong place, so make sure it is
+ // not in the list.
+ int oldIndex = localmWindows.indexOf(wallpaper);
+ if (oldIndex >= 0) {
+ localmWindows.remove(oldIndex);
+ if (oldIndex < foundI) {
+ foundI--;
+ }
+ }
+
+ // Now stick it in.
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Moving wallpaper " + wallpaper
+ + " from " + oldIndex + " to " + foundI);
+
+ localmWindows.add(foundI, wallpaper);
+ changed |= ADJUST_WALLPAPER_LAYERS_CHANGED;
+ }
+ }
+
+ return changed;
+ }
+
+ void setWallpaperAnimLayerAdjustmentLocked(int adj) {
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG,
+ "Setting wallpaper layer adj to " + adj);
+ mWallpaperAnimLayerAdjustment = adj;
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ wallpaper.mAnimLayer = wallpaper.mLayer + adj;
+ if (DEBUG_LAYERS || DEBUG_WALLPAPER) Log.v(TAG, "Wallpaper win "
+ + wallpaper + " anim layer: " + wallpaper.mAnimLayer);
+ }
+ }
+ }
+
+ boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh) {
+ boolean changed = false;
+ boolean rawChanged = false;
+ if (mLastWallpaperX >= 0) {
+ int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw;
+ int offset = availw > 0 ? -(int)(availw*mLastWallpaperX+.5f) : 0;
+ changed = wallpaperWin.mXOffset != offset;
+ if (changed) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper "
+ + wallpaperWin + " x: " + offset);
+ wallpaperWin.mXOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperX != mLastWallpaperX) {
+ wallpaperWin.mWallpaperX = mLastWallpaperX;
+ rawChanged = true;
+ }
+ }
+
+ if (mLastWallpaperY >= 0) {
+ int availh = wallpaperWin.mFrame.bottom-wallpaperWin.mFrame.top-dh;
+ int offset = availh > 0 ? -(int)(availh*mLastWallpaperY+.5f) : 0;
+ if (wallpaperWin.mYOffset != offset) {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Update wallpaper "
+ + wallpaperWin + " y: " + offset);
+ changed = true;
+ wallpaperWin.mYOffset = offset;
+ }
+ if (wallpaperWin.mWallpaperY != mLastWallpaperY) {
+ wallpaperWin.mWallpaperY = mLastWallpaperY;
+ rawChanged = true;
+ }
+ }
+
+ if (rawChanged) {
+ try {
+ if (DEBUG_WALLPAPER) Log.v(TAG, "Report new wp offset "
+ + wallpaperWin + " x=" + wallpaperWin.mWallpaperX
+ + " y=" + wallpaperWin.mWallpaperY);
+ wallpaperWin.mClient.dispatchWallpaperOffsets(
+ wallpaperWin.mWallpaperX, wallpaperWin.mWallpaperY);
+ } catch (RemoteException e) {
+ }
+ }
+
+ return changed;
+ }
+
+ boolean updateWallpaperOffsetLocked() {
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ boolean changed = false;
+
+ WindowState target = mWallpaperTarget;
+ if (target != null) {
+ mLastWallpaperX = target.mWallpaperX;
+ mLastWallpaperY = target.mWallpaperY;
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if (updateWallpaperOffsetLocked(wallpaper, dw, dh)) {
+ wallpaper.computeShownFrameLocked();
+ changed = true;
+ }
+ }
+ }
+ }
+
+ return changed;
+ }
+
+ void updateWallpaperVisibilityLocked() {
+ final boolean visible = isWallpaperVisible(mWallpaperTarget);
+ final int dw = mDisplay.getWidth();
+ final int dh = mDisplay.getHeight();
+
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ if (token.hidden == visible) {
+ token.hidden = !visible;
+ // Need to do a layout to ensure the wallpaper now has the
+ // correct size.
+ mLayoutNeeded = true;
+ }
+
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if (visible) {
+ updateWallpaperOffsetLocked(wallpaper, dw, dh);
+ }
+
+ if (wallpaper.mWallpaperVisible != visible) {
+ wallpaper.mWallpaperVisible = visible;
+ try {
+ if (DEBUG_VISIBILITY || DEBUG_WALLPAPER) Log.v(TAG,
+ "Updating visibility of wallpaper " + wallpaper
+ + ": " + visible);
+ wallpaper.mClient.dispatchAppVisibility(visible);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+ }
+
+ void sendPointerToWallpaperLocked(WindowState srcWin,
+ MotionEvent pointer, long eventTime) {
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaper = token.windows.get(curWallpaperIndex);
+ if ((wallpaper.mAttrs.flags &
+ WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
+ continue;
+ }
+ try {
+ MotionEvent ev = MotionEvent.obtainNoHistory(pointer);
+ ev.offsetLocation(srcWin.mFrame.left-wallpaper.mFrame.left,
+ srcWin.mFrame.top-wallpaper.mFrame.top);
+ wallpaper.mClient.dispatchPointer(ev, eventTime, false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure sending pointer to wallpaper", e);
+ }
+ }
+ }
+ }
+
public int addWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int viewVisibility,
Rect outContentInsets) {
@@ -1216,6 +1695,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
+ if (attrs.type == TYPE_WALLPAPER) {
+ Log.w(TAG, "Attempted to add wallpaper window with unknown token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ }
token = new WindowToken(attrs.token, -1, false);
addToken = true;
} else if (attrs.type >= FIRST_APPLICATION_WINDOW
@@ -1242,6 +1726,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ attrs.token + ". Aborting.");
return WindowManagerImpl.ADD_BAD_APP_TOKEN;
}
+ } else if (attrs.type == TYPE_WALLPAPER) {
+ if (token.windowType != TYPE_WALLPAPER) {
+ Log.w(TAG, "Attempted to add wallpaper window with bad token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerImpl.ADD_BAD_APP_TOKEN;
+ }
}
win = new WindowState(session, client, token,
@@ -1292,6 +1782,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);
+ if (attrs.type == TYPE_WALLPAPER ||
+ (attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
}
win.mEnterAnimationPending = true;
@@ -1432,6 +1926,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
private void removeWindowInnerLocked(Session session, WindowState win) {
+ mKeyWaiter.finishedKey(session, win.mClient, true,
+ KeyWaiter.RETURN_NOTHING);
mKeyWaiter.releasePendingPointerLocked(win.mSession);
mKeyWaiter.releasePendingTrackballLocked(win.mSession);
@@ -1441,6 +1937,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
moveInputMethodWindowsIfNeededLocked(false);
}
+ if (false) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Removing window " + win, e);
+ }
+
mPolicy.removeWindowLw(win);
win.removeLocked();
@@ -1490,6 +1992,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ if (win.mAttrs.type == TYPE_WALLPAPER ||
+ (win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
+
if (!mInLayout) {
assignLayersLocked();
mLayoutNeeded = true;
@@ -1506,10 +2013,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized (mWindowMap) {
WindowState w = windowForClientLocked(session, client);
if ((w != null) && (w.mSurface != null)) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, ">>> OPEN TRANSACTION");
Surface.openTransaction();
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface
+ + ": transparentRegionHint=" + region);
w.mSurface.setTransparentRegionHint(region);
} finally {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, "<<< CLOSE TRANSACTION");
Surface.closeTransaction();
}
}
@@ -1552,6 +2064,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ public void setWindowWallpaperPositionLocked(WindowState window, float x, float y) {
+ if (window.mWallpaperX != x || window.mWallpaperY != y) {
+ window.mWallpaperX = x;
+ window.mWallpaperY = y;
+
+ if (mWallpaperTarget == window) {
+ if (updateWallpaperOffsetLocked()) {
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+ }
+ }
+
public int relayoutWindow(Session session, IWindow client,
WindowManager.LayoutParams attrs, int requestedWidth,
int requestedHeight, int viewVisibility, boolean insetsPending,
@@ -1610,6 +2135,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
|| ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0)
|| (!win.mRelayoutCalled);
+ boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
+ && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+
win.mRelayoutCalled = true;
final int oldVisibility = win.mViewVisibility;
win.mViewVisibility = viewVisibility;
@@ -1640,13 +2168,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface surface = win.createSurfaceLocked();
if (surface != null) {
outSurface.copyFrom(surface);
+ if (SHOW_TRANSACTIONS) Log.i(TAG,
+ " OUT SURFACE " + outSurface + ": copied");
} else {
- outSurface.clear();
+ // For some reason there isn't a surface. Clear the
+ // caller's object so they see the same state.
+ outSurface.release();
}
} catch (Exception e) {
Log.w(TAG, "Exception thrown when creating surface for client "
- + client + " (" + win.mAttrs.getTitle() + ")",
- e);
+ + client + " (" + win.mAttrs.getTitle() + ")",
+ e);
Binder.restoreCallingIdentity(origId);
return 0;
}
@@ -1687,7 +2219,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
- outSurface.clear();
+ // We are being called from a local process, which
+ // means outSurface holds its current surface. Ensure the
+ // surface object is cleared, but we don't want it actually
+ // destroyed at this point.
+ outSurface.release();
}
if (focusMayChange) {
@@ -1707,6 +2243,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
assignLayers = true;
}
}
+ if (wallpaperMayMove) {
+ if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ assignLayers = true;
+ }
+ }
mLayoutNeeded = true;
win.mGivenInsetsPending = insetsPending;
@@ -1715,6 +2256,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
newConfig = updateOrientationFromAppTokensLocked(null, null);
performLayoutAndPlaceSurfacesLocked();
+ if (displayed && win.mIsWallpaper) {
+ updateWallpaperOffsetLocked(win, mDisplay.getWidth(),
+ mDisplay.getHeight());
+ }
if (win.mAppToken != null) {
win.mAppToken.updateReportedVisibilityLocked();
}
@@ -1750,6 +2295,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client);
if (win != null && win.finishDrawingLocked()) {
+ if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
+ adjustWallpaperWindowsLocked();
+ }
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
}
@@ -1906,6 +2454,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
? com.android.internal.R.styleable.WindowAnimation_taskToBackEnterAnimation
: com.android.internal.R.styleable.WindowAnimation_taskToBackExitAnimation;
break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
+ break;
+ case WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE:
+ animAttr = enter
+ ? com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+ : com.android.internal.R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+ break;
}
a = loadAnimation(lp, animAttr);
if (DEBUG_ANIM) Log.v(TAG, "applyAnimation: wtoken=" + wtoken
@@ -2002,6 +2570,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
wtoken = new WindowToken(token, type, true);
mTokenMap.put(token, wtoken);
mTokenList.add(wtoken);
+ if (type == TYPE_WALLPAPER) {
+ mWallpaperTokens.add(wtoken);
+ }
}
}
@@ -2047,6 +2618,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (delayed) {
mExitingTokens.add(wtoken);
+ } else if (wtoken.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(wtoken);
}
}
@@ -2206,10 +2779,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Configuration config;
synchronized(mWindowMap) {
config = updateOrientationFromAppTokensLocked(currentConfig, freezeThisOneIfNeeded);
- }
- if (config != null) {
- mLayoutNeeded = true;
- performLayoutAndPlaceSurfacesLocked();
+ if (config != null) {
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
}
return config;
}
@@ -2259,7 +2832,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mTempConfiguration.setToDefaults();
if (computeNewConfigurationLocked(mTempConfiguration)) {
if (appConfig.diff(mTempConfiguration) != 0) {
- Log.i(TAG, "Config changed: " + mTempConfiguration);
return new Configuration(mTempConfiguration);
}
}
@@ -2519,6 +3091,23 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return;
}
+ // If this is a translucent or wallpaper window, then don't
+ // show a starting window -- the current effect (a full-screen
+ // opaque starting window that fades away to the real contents
+ // when it is ready) does not work for this.
+ if (theme != 0) {
+ AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
+ com.android.internal.R.styleable.Window);
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false)) {
+ return;
+ }
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
+ return;
+ }
+ }
+
mStartingIconInTransition = true;
wtoken.startingData = new StartingData(
pkg, theme, nonLocalizedLabel,
@@ -2680,12 +3269,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
wtoken.setDummyAnimation();
mOpeningApps.remove(wtoken);
mClosingApps.remove(wtoken);
+ wtoken.waitingToShow = wtoken.waitingToHide = false;
wtoken.inPendingTransaction = true;
if (visible) {
mOpeningApps.add(wtoken);
wtoken.allDrawn = false;
wtoken.startingDisplayed = false;
wtoken.startingMoved = false;
+ wtoken.waitingToShow = true;
if (wtoken.clientHidden) {
// In the case where we are making an app visible
@@ -2699,6 +3290,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
} else {
mClosingApps.add(wtoken);
+ wtoken.waitingToHide = true;
}
return;
}
@@ -2833,10 +3425,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_NONE, true);
wtoken.inPendingTransaction = false;
mOpeningApps.remove(wtoken);
+ wtoken.waitingToShow = false;
if (mClosingApps.contains(wtoken)) {
delayed = true;
} else if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
mClosingApps.add(wtoken);
+ wtoken.waitingToHide = true;
delayed = true;
}
if (DEBUG_APP_TRANSITIONS) Log.v(
@@ -2881,11 +3475,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int NW = token.windows.size();
for (int i=0; i<NW; i++) {
WindowState win = token.windows.get(i);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing window " + win);
mWindows.remove(win);
int j = win.mChildWindows.size();
while (j > 0) {
j--;
- mWindows.remove(win.mChildWindows.get(j));
+ WindowState cwin = (WindowState)win.mChildWindows.get(j);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Tmp removing child window " + cwin);
+ mWindows.remove(cwin);
}
}
return NW > 0;
@@ -2923,6 +3521,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
if (DEBUG_REORDER) Log.v(TAG, "Looking for lower windows @ "
+ tokenPos + " -- " + wtoken.token);
+ if (wtoken.sendingToBottom) {
+ if (DEBUG_REORDER) Log.v(TAG,
+ "Skipping token -- currently sending to bottom");
+ tokenPos--;
+ continue;
+ }
int i = wtoken.windows.size();
while (i > 0) {
i--;
@@ -2931,7 +3535,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
while (j > 0) {
j--;
WindowState cwin = (WindowState)win.mChildWindows.get(j);
- if (cwin.mSubLayer >= 0 ) {
+ if (cwin.mSubLayer >= 0) {
for (int pos=NW-1; pos>=0; pos--) {
if (mWindows.get(pos) == cwin) {
if (DEBUG_REORDER) Log.v(TAG,
@@ -2960,14 +3564,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
for (int j=0; j<NCW; j++) {
WindowState cwin = (WindowState)win.mChildWindows.get(j);
if (!added && cwin.mSubLayer >= 0) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding child window at "
+ + index + ": " + cwin);
mWindows.add(index, win);
index++;
added = true;
}
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at "
+ + index + ": " + cwin);
mWindows.add(index, cwin);
index++;
}
if (!added) {
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Re-adding window at "
+ + index + ": " + win);
mWindows.add(index, win);
index++;
}
@@ -3035,6 +3645,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos,
+ boolean updateFocusAndLayout) {
+ // First remove all of the windows from the list.
+ tmpRemoveAppWindowsLocked(wtoken);
+
+ // Where to start adding?
+ int pos = findWindowOffsetLocked(tokenPos);
+
+ // And now add them back at the correct place.
+ pos = reAddAppWindowsLocked(pos, wtoken);
+
+ if (updateFocusAndLayout) {
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ assignLayersLocked();
+ }
+ mLayoutNeeded = true;
+ performLayoutAndPlaceSurfacesLocked();
+ }
+ }
+
private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
// First remove all of the windows from the list.
final int N = tokens.size();
@@ -3057,7 +3687,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
+ if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
+ assignLayersLocked();
+ }
mLayoutNeeded = true;
performLayoutAndPlaceSurfacesLocked();
@@ -3078,9 +3710,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(wt);
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ mToTopApps.remove(wt);
+ mToBottomApps.remove(wt);
+ mToTopApps.add(wt);
+ wt.sendingToBottom = false;
+ wt.sendingToTop = true;
+ }
}
}
- moveAppWindowsLocked(tokens, mAppTokens.size());
+
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
+ moveAppWindowsLocked(tokens, mAppTokens.size());
+ }
}
Binder.restoreCallingIdentity(origId);
}
@@ -3100,10 +3742,20 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
AppWindowToken wt = findAppWindowToken(tokens.get(i));
if (wt != null) {
mAppTokens.add(pos, wt);
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ mToTopApps.remove(wt);
+ mToBottomApps.remove(wt);
+ mToBottomApps.add(i, wt);
+ wt.sendingToTop = false;
+ wt.sendingToBottom = true;
+ }
pos++;
}
}
- moveAppWindowsLocked(tokens, 0);
+
+ if (mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
+ moveAppWindowsLocked(tokens, 0);
+ }
}
Binder.restoreCallingIdentity(origId);
}
@@ -3735,19 +4387,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (!computeNewConfigurationLocked(config)) {
return null;
}
- Log.i(TAG, "Config changed: " + config);
- long now = SystemClock.uptimeMillis();
- //Log.i(TAG, "Config changing, gc pending: " + mFreezeGcPending + ", now " + now);
- if (mFreezeGcPending != 0) {
- if (now > (mFreezeGcPending+1000)) {
- //Log.i(TAG, "Gc! " + now + " > " + (mFreezeGcPending+1000));
- mH.removeMessages(H.FORCE_GC);
- Runtime.getRuntime().gc();
- mFreezeGcPending = now;
- }
- } else {
- mFreezeGcPending = now;
- }
return config;
}
@@ -3833,7 +4472,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
private final void wakeupIfNeeded(WindowState targetWin, int eventType) {
long curTime = SystemClock.uptimeMillis();
- if (eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
+ if (eventType == TOUCH_EVENT || eventType == LONG_TOUCH_EVENT || eventType == CHEEK_EVENT) {
if (mLastTouchEventType == eventType &&
(curTime - mLastUserActivityCallTime) < MIN_TIME_BETWEEN_USERACTIVITIES) {
return;
@@ -3893,8 +4532,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT || WindowManagerPolicy.WATCH_POINTER) Log.v(TAG,
"dispatchPointer " + ev);
+ if (MEASURE_LATENCY) {
+ lt.sample("3 Wait for last dispatch ", System.nanoTime() - qev.whenNano);
+ }
+
Object targetObj = mKeyWaiter.waitForNextEventTarget(null, qev,
ev, true, false, pid, uid);
+
+ if (MEASURE_LATENCY) {
+ lt.sample("3 Last dispatch finished ", System.nanoTime() - qev.whenNano);
+ }
int action = ev.getAction();
@@ -3932,7 +4579,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowState target = (WindowState)targetObj;
final long eventTime = ev.getEventTime();
-
+ final long eventTimeNano = ev.getEventTimeNano();
+
//Log.i(TAG, "Sending " + ev + " to " + target);
if (uid != 0 && uid != target.mSession.mUid) {
@@ -3949,6 +4597,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return INJECT_NO_PERMISSION;
}
}
+
+ if (MEASURE_LATENCY) {
+ lt.sample("4 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+ }
if ((target.mAttrs.flags &
WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES) != 0) {
@@ -4032,6 +4684,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ if (MEASURE_LATENCY) {
+ lt.sample("5 in dispatchPointer ", System.nanoTime() - eventTimeNano);
+ }
+
synchronized(mWindowMap) {
if (qev != null && action == MotionEvent.ACTION_MOVE) {
mKeyWaiter.bindTargetWindowLocked(target,
@@ -4047,7 +4703,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final Rect frame = out.mFrame;
oev.offsetLocation(-(float)frame.left, -(float)frame.top);
try {
- out.mClient.dispatchPointer(oev, eventTime);
+ out.mClient.dispatchPointer(oev, eventTime, false);
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during outside motion dispatch: " + out);
}
@@ -4060,6 +4716,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final Rect frame = target.mFrame;
ev.offsetLocation(-(float)frame.left, -(float)frame.top);
mKeyWaiter.bindTargetWindowLocked(target);
+
+ // If we are on top of the wallpaper, then the wallpaper also
+ // gets to see this movement.
+ if (mWallpaperTarget == target) {
+ sendPointerToWallpaperLocked(target, ev, eventTime);
+ }
}
}
@@ -4069,7 +4731,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT || DEBUG_FOCUS || WindowManagerPolicy.WATCH_POINTER) {
Log.v(TAG, "Delivering pointer " + qev + " to " + target);
}
- target.mClient.dispatchPointer(ev, eventTime);
+
+ if (MEASURE_LATENCY) {
+ lt.sample("6 before svr->client ipc ", System.nanoTime() - eventTimeNano);
+ }
+
+ target.mClient.dispatchPointer(ev, eventTime, true);
+
+ if (MEASURE_LATENCY) {
+ lt.sample("7 after svr->client ipc ", System.nanoTime() - eventTimeNano);
+ }
return INJECT_SUCCEEDED;
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during motion dispatch: " + target);
@@ -4141,7 +4812,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
try {
- focus.mClient.dispatchTrackball(ev, eventTime);
+ focus.mClient.dispatchTrackball(ev, eventTime, true);
return INJECT_SUCCEEDED;
} catch (android.os.RemoteException e) {
Log.i(TAG, "WINDOW DIED during key dispatch: " + focus);
@@ -4664,7 +5335,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
mPolicy.interceptKeyTi(null, keycode,
- nextKey.getMetaState(), down, repeatCount);
+ nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags());
}
Log.w(TAG, "Event timeout during app switch: dropping "
+ nextKey);
@@ -4687,7 +5359,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED) {
if (mPolicy.interceptKeyTi(focus,
- keycode, nextKey.getMetaState(), down, repeatCount)) {
+ keycode, nextKey.getMetaState(), down, repeatCount,
+ nextKey.getFlags())) {
return CONSUMED_EVENT_TOKEN;
}
}
@@ -4914,14 +5587,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return null;
}
+ MotionEvent res = null;
+ QueuedEvent qev = null;
+ WindowState win = null;
+
synchronized (this) {
if (DEBUG_INPUT) Log.v(
TAG, "finishedKey: client=" + client.asBinder()
+ ", force=" + force + ", last=" + mLastBinder
+ " (token=" + (mLastWin != null ? mLastWin.mToken : null) + ")");
- QueuedEvent qev = null;
- WindowState win = null;
if (returnWhat == RETURN_PENDING_POINTER) {
qev = session.mPendingPointerMove;
win = session.mPendingPointerWindow;
@@ -4951,17 +5626,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
if (qev != null) {
- MotionEvent res = (MotionEvent)qev.event;
+ res = (MotionEvent)qev.event;
if (DEBUG_INPUT) Log.v(TAG,
"Returning pending motion: " + res);
mQueue.recycleEvent(qev);
if (win != null && returnWhat == RETURN_PENDING_POINTER) {
res.offsetLocation(-win.mFrame.left, -win.mFrame.top);
}
- return res;
}
- return null;
}
+
+ if (res != null && returnWhat == RETURN_PENDING_POINTER) {
+ synchronized (mWindowMap) {
+ if (mWallpaperTarget == win) {
+ sendPointerToWallpaperLocked(win, res, res.getEventTime());
+ }
+ }
+ }
+
+ return res;
}
void tickle() {
@@ -5107,7 +5790,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
PowerManager.WakeLock mHoldingScreen;
KeyQ() {
- super(mContext);
+ super(mContext, WindowManagerService.this);
PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
mHoldingScreen = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
"KEEP_SCREEN_ON_FLAG");
@@ -5238,7 +5921,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
- };
+ }
public boolean detectSafeMode() {
mSafeMode = mPolicy.detectSafeMode();
@@ -5303,9 +5986,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_INPUT && ev != null) Log.v(
TAG, "Event: type=" + ev.classType + " data=" + ev.event);
+ if (MEASURE_LATENCY) {
+ lt.sample("2 got event ", System.nanoTime() - ev.whenNano);
+ }
+
try {
if (ev != null) {
- curTime = ev.when;
+ curTime = SystemClock.uptimeMillis();
int eventType;
if (ev.classType == RawInputEvent.CLASS_TOUCHSCREEN) {
eventType = eventType((MotionEvent)ev.event);
@@ -5316,17 +6003,29 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
eventType = LocalPowerManager.OTHER_EVENT;
}
try {
- long now = SystemClock.uptimeMillis();
-
- if ((now - mLastBatteryStatsCallTime)
+ if ((curTime - mLastBatteryStatsCallTime)
>= MIN_TIME_BETWEEN_USERACTIVITIES) {
- mLastBatteryStatsCallTime = now;
+ mLastBatteryStatsCallTime = curTime;
mBatteryStats.noteInputEvent();
}
} catch (RemoteException e) {
// Ignore
}
- mPowerManager.userActivity(curTime, false, eventType, false);
+
+ if (eventType != TOUCH_EVENT
+ && eventType != LONG_TOUCH_EVENT
+ && eventType != CHEEK_EVENT) {
+ mPowerManager.userActivity(curTime, false,
+ eventType, false);
+ } else if (mLastTouchEventType != eventType
+ || (curTime - mLastUserActivityCallTime)
+ >= MIN_TIME_BETWEEN_USERACTIVITIES) {
+ mLastUserActivityCallTime = curTime;
+ mLastTouchEventType = eventType;
+ mPowerManager.userActivity(curTime, false,
+ eventType, false);
+ }
+
switch (ev.classType) {
case RawInputEvent.CLASS_KEYBOARD:
KeyEvent ke = (KeyEvent)ev.event;
@@ -5592,11 +6291,25 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ public void setWallpaperPosition(IBinder window, float x, float y) {
+ synchronized(mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ setWindowWallpaperPositionLocked(windowForClientLocked(this, window),
+ x, y);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
void windowAddedLocked() {
if (mSurfaceSession == null) {
if (localLOGV) Log.v(
TAG, "First window added to " + this + ", creating SurfaceSession");
mSurfaceSession = new SurfaceSession();
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " NEW SURFACE SESSION " + mSurfaceSession);
mSessions.add(this);
}
mNumWindow++;
@@ -5614,6 +6327,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (localLOGV) Log.v(
TAG, "Last window removed from " + this
+ ", destroying " + mSurfaceSession);
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " KILL SURFACE SESSION " + mSurfaceSession);
try {
mSurfaceSession.kill();
} catch (Exception e) {
@@ -5667,6 +6382,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int mSubLayer;
final boolean mLayoutAttached;
final boolean mIsImWindow;
+ final boolean mIsWallpaper;
+ final boolean mIsFloatingLayer;
int mViewVisibility;
boolean mPolicyVisibility = true;
boolean mPolicyVisibilityAfterAnim = true;
@@ -5674,16 +6391,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface mSurface;
boolean mAttachedHidden; // is our parent window hidden?
boolean mLastHidden; // was this window last hidden?
+ boolean mWallpaperVisible; // for wallpaper, what was last vis report?
int mRequestedWidth;
int mRequestedHeight;
int mLastRequestedWidth;
int mLastRequestedHeight;
- int mReqXPos;
- int mReqYPos;
int mLayer;
int mAnimLayer;
int mLastLayer;
boolean mHaveFrame;
+ boolean mObscured;
WindowState mNextOutsideTouch;
@@ -5764,6 +6481,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean mHasLocalTransformation;
final Transformation mTransformation = new Transformation();
+ // If a window showing a wallpaper: the requested offset for the
+ // wallpaper; if a wallpaper window: the currently applied offset.
+ float mWallpaperX = -1;
+ float mWallpaperY = -1;
+
+ // Wallpaper windows: pixels offset based on above variables.
+ int mXOffset;
+ int mYOffset;
+
// This is set after IWindowSession.relayout() has been called at
// least once for the window. It allows us to detect the situation
// where we don't yet have a surface, but should have one soon, so
@@ -5824,6 +6550,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAttachedWindow = null;
mLayoutAttached = false;
mIsImWindow = false;
+ mIsWallpaper = false;
+ mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
return;
@@ -5844,6 +6572,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = attachedWindow.mAttrs.type == TYPE_INPUT_METHOD
|| attachedWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = attachedWindow.mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
@@ -5855,6 +6585,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
+ mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
+ mIsFloatingLayer = mIsImWindow || mIsWallpaper;
}
WindowState appWin = this;
@@ -5877,8 +6609,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
- mReqXPos = 0;
- mReqYPos = 0;
+ mXOffset = 0;
+ mYOffset = 0;
mLayer = 0;
mAnimLayer = 0;
mLastLayer = 0;
@@ -5926,6 +6658,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
visible.set(vf);
final Rect frame = mFrame;
+ final int fw = frame.width();
+ final int fh = frame.height();
//System.out.println("In: w=" + w + " h=" + h + " container=" +
// container + " x=" + mAttrs.x + " y=" + mAttrs.y);
@@ -5962,6 +6696,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
visibleInsets.right = frame.right-visible.right;
visibleInsets.bottom = frame.bottom-visible.bottom;
+ if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {
+ updateWallpaperOffsetLocked(this, mDisplay.getWidth(),
+ mDisplay.getHeight());
+ }
+
if (localLOGV) {
//if ("com.google.android.youtube".equals(mAttrs.packageName)
// && mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
@@ -6059,11 +6798,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
int flags = 0;
- if (mAttrs.memoryType == MEMORY_TYPE_HARDWARE) {
- flags |= Surface.HARDWARE;
- } else if (mAttrs.memoryType == MEMORY_TYPE_GPU) {
- flags |= Surface.GPU;
- } else if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
+ if (mAttrs.memoryType == MEMORY_TYPE_PUSH_BUFFERS) {
flags |= Surface.PUSH_BUFFERS;
}
@@ -6090,6 +6825,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mSurface = new Surface(
mSession.mSurfaceSession, mSession.mPid,
0, w, h, mAttrs.format, flags);
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " CREATE SURFACE "
+ + mSurface + " IN SESSION "
+ + mSession.mSurfaceSession
+ + ": pid=" + mSession.mPid + " format="
+ + mAttrs.format + " flags=0x"
+ + Integer.toHexString(flags));
} catch (Surface.OutOfResourcesException e) {
Log.w(TAG, "OutOfResourcesException creating surface");
reclaimSomeSurfaceMemoryLocked(this, "create");
@@ -6114,10 +6855,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Surface.openTransaction();
try {
try {
- mSurface.setPosition(mFrame.left, mFrame.top);
+ mSurface.setPosition(mFrame.left + mXOffset,
+ mFrame.top + mYOffset);
mSurface.setLayer(mAnimLayer);
mSurface.hide();
if ((mAttrs.flags&WindowManager.LayoutParams.FLAG_DITHER) != 0) {
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " SURFACE "
+ + mSurface + ": DITHER");
mSurface.setFlags(Surface.SURFACE_DITHER,
Surface.SURFACE_DITHER);
}
@@ -6149,18 +6893,21 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAppToken.startingDisplayed = false;
}
- if (localLOGV) Log.v(
- TAG, "Window " + this
- + " destroying surface " + mSurface + ", session " + mSession);
if (mSurface != null) {
try {
+ if (DEBUG_VISIBILITY) {
+ RuntimeException e = new RuntimeException();
+ e.fillInStackTrace();
+ Log.w(TAG, "Window " + this + " destroying surface "
+ + mSurface + ", session " + mSession, e);
+ }
if (SHOW_TRANSACTIONS) {
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
Log.i(TAG, " SURFACE " + mSurface + ": DESTROY ("
+ mAttrs.getTitle() + ")", ex);
}
- mSurface.clear();
+ mSurface.destroy();
} catch (RuntimeException e) {
Log.w(TAG, "Exception thrown when destroying Window " + this
+ " surface " + mSurface + " session " + mSession
@@ -6192,10 +6939,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
// This must be called while inside a transaction.
- void commitFinishDrawingLocked(long currentTime) {
+ boolean commitFinishDrawingLocked(long currentTime) {
//Log.i(TAG, "commitFinishDrawingLocked: " + mSurface);
if (!mCommitDrawPending) {
- return;
+ return false;
}
mCommitDrawPending = false;
mReadyToShow = true;
@@ -6204,6 +6951,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (atoken == null || atoken.allDrawn || starting) {
performShowLocked();
}
+ return true;
}
// This must be called while inside a transaction.
@@ -6302,7 +7050,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mHasLocalTransformation = false;
if ((!mLocalAnimating || mAnimationIsEntrance) && mAppToken != null
- && mAppToken.hasTransformation) {
+ && mAppToken.animation != null) {
// When our app token is animating, we kind-of pretend like
// we are as well. Note the mLocalAnimating mAnimationIsEntrance
// part of this check means that we will only do this if
@@ -6344,6 +7092,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mAnimLayer = mLayer;
if (mIsImWindow) {
mAnimLayer += mInputMethodAnimLayerAdjustment;
+ } else if (mIsWallpaper) {
+ mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Stepping win " + this
+ " anim layer: " + mAnimLayer);
@@ -6430,6 +7180,26 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Transformation appTransformation =
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
+
+ // Wallpapers are animated based on the "real" window they
+ // are currently targeting.
+ if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null
+ && mWallpaperTarget != null) {
+ if (mWallpaperTarget.mHasLocalTransformation) {
+ attachedTransformation = mWallpaperTarget.mTransformation;
+ }
+ if (mWallpaperTarget.mAppToken != null &&
+ mWallpaperTarget.mAppToken.hasTransformation) {
+ appTransformation = mWallpaperTarget.mAppToken.transformation;
+ }
+ if (DEBUG_WALLPAPER && attachedTransformation != null) {
+ Log.v(TAG, "WP target attached xform: " + attachedTransformation);
+ }
+ if (DEBUG_WALLPAPER && appTransformation != null) {
+ Log.v(TAG, "WP target app xform: " + appTransformation);
+ }
+ }
+
if (selfTransformation || attachedTransformation != null
|| appTransformation != null) {
// cache often used attributes locally
@@ -6460,8 +7230,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mDtDx = tmpFloats[Matrix.MSKEW_X];
mDsDy = tmpFloats[Matrix.MSKEW_Y];
mDtDy = tmpFloats[Matrix.MSCALE_Y];
- int x = (int)tmpFloats[Matrix.MTRANS_X];
- int y = (int)tmpFloats[Matrix.MTRANS_Y];
+ int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset;
+ int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset;
int w = frame.width();
int h = frame.height();
mShownFrame.set(x, y, x+w, y+h);
@@ -6498,6 +7268,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mShownFrame.set(mFrame);
+ if (mXOffset != 0 || mYOffset != 0) {
+ mShownFrame.offset(mXOffset, mYOffset);
+ }
mShownAlpha = mAlpha;
mDsDx = 1;
mDtDx = 0;
@@ -6561,10 +7334,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (atoken != null) {
return mSurface != null && mPolicyVisibility && !mDestroying
&& ((!mAttachedHidden && !atoken.hiddenRequested)
- || mAnimating || atoken.animating);
+ || mAnimation != null || atoken.animation != null);
} else {
return mSurface != null && mPolicyVisibility && !mDestroying
- && (!mAttachedHidden || mAnimating);
+ && (!mAttachedHidden || mAnimation != null);
}
}
@@ -6573,11 +7346,17 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
* of a transition that has not yet been started.
*/
boolean isReadyForDisplay() {
+ if (mRootToken.waitingToShow &&
+ mNextAppTransition != WindowManagerPolicy.TRANSIT_NONE) {
+ return false;
+ }
final AppWindowToken atoken = mAppToken;
- final boolean animating = atoken != null ? atoken.animating : false;
+ final boolean animating = atoken != null
+ ? (atoken.animation != null) : false;
return mSurface != null && mPolicyVisibility && !mDestroying
- && ((!mAttachedHidden && !mRootToken.hidden)
- || mAnimating || animating);
+ && ((!mAttachedHidden && mViewVisibility == View.VISIBLE
+ && !mRootToken.hidden)
+ || mAnimation != null || animating);
}
/** Is the window or its container currently animating? */
@@ -6635,11 +7414,15 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
/**
- * Return true if the window is opaque and fully drawn.
+ * Return true if the window is opaque and fully drawn. This indicates
+ * it may obscure windows behind it.
*/
boolean isOpaqueDrawn() {
- return mAttrs.format == PixelFormat.OPAQUE && mSurface != null
- && mAnimation == null && !mDrawPending && !mCommitDrawPending;
+ return (mAttrs.format == PixelFormat.OPAQUE
+ || mAttrs.type == TYPE_WALLPAPER)
+ && mSurface != null && mAnimation == null
+ && (mAppToken == null || mAppToken.animation == null)
+ && !mDrawPending && !mCommitDrawPending;
}
boolean needsBackgroundFiller(int screenWidth, int screenHeight) {
@@ -6659,7 +7442,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean isFullscreen(int screenWidth, int screenHeight) {
return mFrame.left <= 0 && mFrame.top <= 0 &&
- mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
+ mFrame.right >= screenWidth && mFrame.bottom >= screenHeight;
}
void removeLocked() {
@@ -6749,8 +7532,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("mAttachedWindow="); pw.print(mAttachedWindow);
pw.print(" mLayoutAttached="); pw.println(mLayoutAttached);
}
- if (mIsImWindow) {
- pw.print(prefix); pw.print("mIsImWindow="); pw.println(mIsImWindow);
+ if (mIsImWindow || mIsWallpaper || mIsFloatingLayer) {
+ pw.print(prefix); pw.print("mIsImWindow="); pw.print(mIsImWindow);
+ pw.print(" mIsWallpaper="); pw.print(mIsWallpaper);
+ pw.print(" mIsFloatingLayer="); pw.print(mIsFloatingLayer);
+ pw.print(" mWallpaperVisible="); pw.println(mWallpaperVisible);
}
pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer);
pw.print(" mSubLayer="); pw.print(mSubLayer);
@@ -6773,7 +7559,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("mViewVisibility=0x");
pw.print(Integer.toHexString(mViewVisibility));
pw.print(" mLastHidden="); pw.print(mLastHidden);
- pw.print(" mHaveFrame="); pw.println(mHaveFrame);
+ pw.print(" mHaveFrame="); pw.print(mHaveFrame);
+ pw.print(" mObscured="); pw.println(mObscured);
if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || mAttachedHidden) {
pw.print(prefix); pw.print("mPolicyVisibility=");
pw.print(mPolicyVisibility);
@@ -6782,9 +7569,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
}
pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth);
- pw.print(" h="); pw.print(mRequestedHeight);
- pw.print(" x="); pw.print(mReqXPos);
- pw.print(" y="); pw.println(mReqYPos);
+ pw.print(" h="); pw.println(mRequestedHeight);
+ if (mXOffset != 0 || mYOffset != 0) {
+ pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset);
+ pw.print(" y="); pw.println(mYOffset);
+ }
pw.print(prefix); pw.print("mGivenContentInsets=");
mGivenContentInsets.printShortString(pw);
pw.print(" mGivenVisibleInsets=");
@@ -6852,6 +7641,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("mHScale="); pw.print(mHScale);
pw.print(" mVScale="); pw.println(mVScale);
}
+ if (mWallpaperX != -1 || mWallpaperY != -1) {
+ pw.print(prefix); pw.print("mWallpaperX="); pw.print(mWallpaperX);
+ pw.print(" mWallpaperY="); pw.println(mWallpaperY);
+ }
}
@Override
@@ -6895,6 +7688,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// Temporary for finding which tokens no longer have visible windows.
boolean hasVisible;
+ // Set to true when this token is in a pending transaction where it
+ // will be shown.
+ boolean waitingToShow;
+
+ // Set to true when this token is in a pending transaction where it
+ // will be hidden.
+ boolean waitingToHide;
+
+ // Set to true when this token is in a pending transaction where its
+ // windows will be put to the bottom of the list.
+ boolean sendingToBottom;
+
+ // Set to true when this token is in a pending transaction where its
+ // windows will be put to the top of the list.
+ boolean sendingToTop;
+
WindowToken(IBinder _token, int type, boolean _explicit) {
token = _token;
windowType = type;
@@ -6907,6 +7716,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("windowType="); pw.print(windowType);
pw.print(" hidden="); pw.print(hidden);
pw.print(" hasVisible="); pw.println(hasVisible);
+ if (waitingToShow || waitingToHide || sendingToBottom || sendingToTop) {
+ pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);
+ pw.print(" waitingToHide="); pw.print(waitingToHide);
+ pw.print(" sendingToBottom="); pw.print(sendingToBottom);
+ pw.print(" sendingToTop="); pw.println(sendingToTop);
+ }
}
@Override
@@ -7036,6 +7851,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w == mInputMethodTarget) {
setInputMethodAnimLayerAdjustment(adj);
}
+ if (w == mWallpaperTarget && mLowerWallpaperTarget == null) {
+ setWallpaperAnimLayerAdjustmentLocked(adj);
+ }
}
}
@@ -7073,7 +7891,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (animation == sDummyAnimation) {
// This guy is going to animate, but not yet. For now count
- // it is not animating for purposes of scheduling transactions;
+ // it as not animating for purposes of scheduling transactions;
// when it is really time to animate, this will be set to
// a real animation and the next call will execute normally.
return false;
@@ -7204,6 +8022,19 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ WindowState findMainWindow() {
+ int j = windows.size();
+ while (j > 0) {
+ j--;
+ WindowState win = windows.get(j);
+ if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
+ || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
+ return win;
+ }
+ }
+ return null;
+ }
+
void dump(PrintWriter pw, String prefix) {
super.dump(pw, prefix);
if (appToken != null) {
@@ -7213,6 +8044,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(prefix); pw.print("allAppWindows="); pw.println(allAppWindows);
}
pw.print(prefix); pw.print("groupId="); pw.print(groupId);
+ pw.print(" appFullscreen="); pw.print(appFullscreen);
pw.print(" requestedOrientation="); pw.println(requestedOrientation);
pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
pw.print(" clientHidden="); pw.print(clientHidden);
@@ -7269,71 +8101,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- public static WindowManager.LayoutParams findAnimations(
- ArrayList<AppWindowToken> order,
- ArrayList<AppWindowToken> openingTokenList1,
- ArrayList<AppWindowToken> closingTokenList2) {
- // We need to figure out which animation to use...
-
- // First, check if there is a compatible window in opening/closing
- // apps, and use it if exists.
- WindowManager.LayoutParams animParams = null;
- int animSrc = 0;
- animParams = findCompatibleWindowParams(openingTokenList1);
- if (animParams == null) {
- animParams = findCompatibleWindowParams(closingTokenList2);
- }
- if (animParams != null) {
- return animParams;
- }
-
- //Log.i(TAG, "Looking for animations...");
- for (int i=order.size()-1; i>=0; i--) {
- AppWindowToken wtoken = order.get(i);
- //Log.i(TAG, "Token " + wtoken + " with " + wtoken.windows.size() + " windows");
- if (openingTokenList1.contains(wtoken) || closingTokenList2.contains(wtoken)) {
- int j = wtoken.windows.size();
- while (j > 0) {
- j--;
- WindowState win = wtoken.windows.get(j);
- //Log.i(TAG, "Window " + win + ": type=" + win.mAttrs.type);
- if (win.mAttrs.type == WindowManager.LayoutParams.TYPE_BASE_APPLICATION
- || win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
- //Log.i(TAG, "Found base or application window, done!");
- if (wtoken.appFullscreen) {
- return win.mAttrs;
- }
- if (animSrc < 2) {
- animParams = win.mAttrs;
- animSrc = 2;
- }
- } else if (animSrc < 1 && win.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
- //Log.i(TAG, "Found normal window, we may use this...");
- animParams = win.mAttrs;
- animSrc = 1;
- }
- }
- }
- }
-
- return animParams;
- }
-
- private static LayoutParams findCompatibleWindowParams(ArrayList<AppWindowToken> tokenList) {
- for (int appCount = tokenList.size() - 1; appCount >= 0; appCount--) {
- AppWindowToken wtoken = tokenList.get(appCount);
- // Just checking one window is sufficient as all windows have the compatible flag
- // if the application is in compatibility mode.
- if (wtoken.windows.size() > 0) {
- WindowManager.LayoutParams params = wtoken.windows.get(0).mAttrs;
- if ((params.flags & FLAG_COMPATIBLE_WINDOW) != 0) {
- return params;
- }
- }
- }
- return null;
- }
-
// -------------------------------------------------------------
// DummyAnimation
// -------------------------------------------------------------
@@ -7778,6 +8545,40 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
return win;
}
+ final void rebuildAppWindowListLocked() {
+ int NW = mWindows.size();
+ int i;
+
+ // First remove all existing app windows.
+ i=0;
+ while (i < NW) {
+ if (((WindowState)mWindows.get(i)).mAppToken != null) {
+ WindowState win = (WindowState)mWindows.remove(i);
+ if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG,
+ "Rebuild removing window: " + win);
+ NW--;
+ continue;
+ }
+ i++;
+ }
+
+ // First add all of the exiting app tokens... these are no longer
+ // in the main app list, but still have windows shown. We put them
+ // in the back because now that the animation is over we no longer
+ // will care about them.
+ int NT = mExitingAppTokens.size();
+ i = 0;
+ for (int j=0; j<NT; j++) {
+ i = reAddAppWindowsLocked(i, mExitingAppTokens.get(j));
+ }
+
+ // And add in the still active app tokens in Z order.
+ NT = mAppTokens.size();
+ for (int j=0; j<NT; j++) {
+ i = reAddAppWindowsLocked(i, mAppTokens.get(j));
+ }
+ }
+
private final void assignLayersLocked() {
int N = mWindows.size();
int curBaseLayer = 0;
@@ -7786,7 +8587,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
for (i=0; i<N; i++) {
WindowState w = (WindowState)mWindows.get(i);
- if (w.mBaseLayer == curBaseLayer || w.mIsImWindow) {
+ if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
+ || (i > 0 && w.mIsWallpaper)) {
curLayer += WINDOW_LAYER_MULTIPLIER;
w.mLayer = curLayer;
} else {
@@ -7802,6 +8604,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
if (w.mIsImWindow) {
w.mAnimLayer += mInputMethodAnimLayerAdjustment;
+ } else if (w.mIsWallpaper) {
+ w.mAnimLayer += mWallpaperAnimLayerAdjustment;
}
if (DEBUG_LAYERS) Log.v(TAG, "Assign layer " + w + ": "
+ w.mAnimLayer);
@@ -7952,7 +8756,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
final int dw = mDisplay.getWidth();
final int dh = mDisplay.getHeight();
- final int N = mWindows.size();
int i;
// FIRST LOOP: Perform a layout, if needed.
@@ -8008,9 +8811,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
restart = false;
boolean tokenMayBeDrawn = false;
+ boolean wallpaperMayChange = false;
mPolicy.beginAnimationLw(dw, dh);
+ final int N = mWindows.size();
+
for (i=N-1; i>=0; i--) {
WindowState w = (WindowState)mWindows.get(i);
@@ -8018,7 +8824,14 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (w.mSurface != null) {
// Execute animation.
- w.commitFinishDrawingLocked(currentTime);
+ if (w.commitFinishDrawingLocked(currentTime)) {
+ if ((w.mAttrs.flags
+ & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "First draw done in potential wallpaper target " + w);
+ wallpaperMayChange = true;
+ }
+ }
if (w.stepAnimationLocked(currentTime, dw, dh)) {
animating = true;
//w.dump(" ");
@@ -8149,16 +8962,120 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
mNextAppTransition = WindowManagerPolicy.TRANSIT_NONE;
mAppTransitionReady = false;
+ mAppTransitionRunning = true;
mAppTransitionTimeout = false;
mStartingIconInTransition = false;
mSkipAppTransitionAnimation = false;
mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
- // We need to figure out which animation to use...
- WindowManager.LayoutParams lp = findAnimations(mAppTokens,
- mOpeningApps, mClosingApps);
-
+ // If there are applications waiting to come to the
+ // top of the stack, now is the time to move their windows.
+ // (Note that we don't do apps going to the bottom
+ // here -- we want to keep their windows in the old
+ // Z-order until the animation completes.)
+ if (mToTopApps.size() > 0) {
+ NN = mAppTokens.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken = mAppTokens.get(i);
+ if (wtoken.sendingToTop) {
+ wtoken.sendingToTop = false;
+ moveAppWindowsLocked(wtoken, NN, false);
+ }
+ }
+ mToTopApps.clear();
+ }
+
+ WindowState oldWallpaper = mWallpaperTarget;
+
+ adjustWallpaperWindowsLocked();
+ wallpaperMayChange = false;
+
+ // The top-most window will supply the layout params,
+ // and we will determine it below.
+ LayoutParams animLp = null;
+ int bestAnimLayer = -1;
+
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New wallpaper target=" + mWallpaperTarget
+ + ", lower target=" + mLowerWallpaperTarget
+ + ", upper target=" + mUpperWallpaperTarget);
+ int foundWallpapers = 0;
+ // Do a first pass through the tokens for two
+ // things:
+ // (1) Determine if both the closing and opening
+ // app token sets are wallpaper targets, in which
+ // case special animations are needed
+ // (since the wallpaper needs to stay static
+ // behind them).
+ // (2) Find the layout params of the top-most
+ // application window in the tokens, which is
+ // what will control the animation theme.
+ final int NC = mClosingApps.size();
+ NN = NC + mOpeningApps.size();
+ for (i=0; i<NN; i++) {
+ AppWindowToken wtoken;
+ int mode;
+ if (i < NC) {
+ wtoken = mClosingApps.get(i);
+ mode = 1;
+ } else {
+ wtoken = mOpeningApps.get(i-NC);
+ mode = 2;
+ }
+ if (mLowerWallpaperTarget != null) {
+ if (mLowerWallpaperTarget.mAppToken == wtoken
+ || mUpperWallpaperTarget.mAppToken == wtoken) {
+ foundWallpapers |= mode;
+ }
+ }
+ if (wtoken.appFullscreen) {
+ WindowState ws = wtoken.findMainWindow();
+ if (ws != null) {
+ // If this is a compatibility mode
+ // window, we will always use its anim.
+ if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) {
+ animLp = ws.mAttrs;
+ bestAnimLayer = Integer.MAX_VALUE;
+ } else if (ws.mLayer > bestAnimLayer) {
+ animLp = ws.mAttrs;
+ bestAnimLayer = ws.mLayer;
+ }
+ }
+ }
+ }
+
+ if (foundWallpapers == 3) {
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "Wallpaper animation!");
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_OPEN:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_FRONT:
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_OPEN;
+ break;
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ case WindowManagerPolicy.TRANSIT_TASK_CLOSE:
+ case WindowManagerPolicy.TRANSIT_TASK_TO_BACK:
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_INTRA_CLOSE;
+ break;
+ }
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit: " + transit);
+ } else if (oldWallpaper != null) {
+ // We are transitioning from an activity with
+ // a wallpaper to one without.
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE;
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit away from wallpaper: " + transit);
+ } else if (mWallpaperTarget != null) {
+ // We are transitioning from an activity without
+ // a wallpaper to now showing the wallpaper
+ transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN;
+ if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
+ "New transit into wallpaper: " + transit);
+ }
+
NN = mOpeningApps.size();
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mOpeningApps.get(i);
@@ -8166,8 +9083,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
"Now opening app" + wtoken);
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, lp, true, transit, false);
+ wtoken.animation = null;
+ setTokenVisibilityLocked(wtoken, animLp, true, transit, false);
wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToShow = false;
wtoken.showAllWindowsLocked();
}
NN = mClosingApps.size();
@@ -8176,8 +9095,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (DEBUG_APP_TRANSITIONS) Log.v(TAG,
"Now closing app" + wtoken);
wtoken.inPendingTransaction = false;
- setTokenVisibilityLocked(wtoken, lp, false, transit, false);
+ wtoken.animation = null;
+ setTokenVisibilityLocked(wtoken, animLp, false, transit, false);
wtoken.updateReportedVisibilityLocked();
+ wtoken.waitingToHide = false;
// Force the allDrawn flag, because we want to start
// this guy's animations regardless of whether it's
// gotten drawn.
@@ -8199,6 +9120,47 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
restart = true;
}
}
+
+ if (!animating && mAppTransitionRunning) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ mAppTransitionRunning = false;
+ // Clear information about apps that were moving.
+ mToBottomApps.clear();
+
+ rebuildAppWindowListLocked();
+ restart = true;
+ moveInputMethodWindowsIfNeededLocked(false);
+ wallpaperMayChange = true;
+ mLayoutNeeded = true;
+ }
+
+ if (wallpaperMayChange) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper may change! Adjusting");
+ int adjResult = adjustWallpaperWindowsLocked();
+ if ((adjResult&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper layer changed: assigning layers + relayout");
+ restart = true;
+ mLayoutNeeded = true;
+ assignLayersLocked();
+ } else if ((adjResult&ADJUST_WALLPAPER_VISIBILITY_CHANGED) != 0) {
+ if (DEBUG_WALLPAPER) Log.v(TAG,
+ "Wallpaper visibility changed: relayout");
+ restart = true;
+ mLayoutNeeded = true;
+ }
+ if (mLayoutNeeded) {
+ restart = true;
+ performLayoutLockedInner();
+ }
+ }
+
} while (restart);
// THIRD LOOP: Update the surfaces of all windows.
@@ -8212,6 +9174,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
boolean syswin = false;
boolean backgroundFillerShown = false;
+ final int N = mWindows.size();
+
for (i=N-1; i>=0; i--) {
WindowState w = (WindowState)mWindows.get(i);
@@ -8239,6 +9203,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastRequestedHeight = height;
w.mLastShownFrame.set(w.mShownFrame);
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface
+ + ": POS " + w.mShownFrame.left
+ + ", " + w.mShownFrame.top);
w.mSurface.setPosition(w.mShownFrame.left, w.mShownFrame.top);
} catch (RuntimeException e) {
Log.w(TAG, "Error positioning surface in " + w, e);
@@ -8251,14 +9219,6 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
width = w.mShownFrame.width();
height = w.mShownFrame.height();
w.mLastShownFrame.set(w.mShownFrame);
- if (resize) {
- if (SHOW_TRANSACTIONS) Log.i(
- TAG, " SURFACE " + w.mSurface + ": ("
- + w.mShownFrame.left + ","
- + w.mShownFrame.top + ") ("
- + w.mShownFrame.width() + "x"
- + w.mShownFrame.height() + ")");
- }
}
if (resize) {
@@ -8266,6 +9226,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (height < 1) height = 1;
if (w.mSurface != null) {
try {
+ if (SHOW_TRANSACTIONS) Log.i(
+ TAG, " SURFACE " + w.mSurface + ": POS "
+ + w.mShownFrame.left + ","
+ + w.mShownFrame.top + " SIZE "
+ + w.mShownFrame.width() + "x"
+ + w.mShownFrame.height());
w.mSurface.setSize(width, height);
w.mSurface.setPosition(w.mShownFrame.left,
w.mShownFrame.top);
@@ -8294,6 +9260,22 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastFrame.set(w.mFrame);
w.mLastContentInsets.set(w.mContentInsets);
w.mLastVisibleInsets.set(w.mVisibleInsets);
+ // If the screen is currently frozen, then keep
+ // it frozen until this window draws at its new
+ // orientation.
+ if (mDisplayFrozen) {
+ if (DEBUG_ORIENTATION) Log.v(TAG,
+ "Resizing while display frozen: " + w);
+ w.mOrientationChanging = true;
+ if (mWindowsFreezingScreen) {
+ mWindowsFreezingScreen = true;
+ // XXX should probably keep timeout from
+ // when we first froze the display.
+ mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
+ mH.sendMessageDelayed(mH.obtainMessage(
+ H.WINDOW_FREEZE_TIMEOUT), 2000);
+ }
+ }
// If the orientation is changing, then we need to
// hold off on unfreezing the display until this
// window has been redrawn; to do that, we need
@@ -8395,7 +9377,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.mLastVScale = w.mVScale;
if (SHOW_TRANSACTIONS) Log.i(
TAG, " SURFACE " + w.mSurface + ": alpha="
- + w.mShownAlpha + " layer=" + w.mAnimLayer);
+ + w.mShownAlpha + " layer=" + w.mAnimLayer
+ + " matrix=[" + (w.mDsDx*w.mHScale)
+ + "," + (w.mDtDx*w.mVScale)
+ + "][" + (w.mDsDy*w.mHScale)
+ + "," + (w.mDtDy*w.mVScale) + "]");
if (w.mSurface != null) {
try {
w.mSurface.setAlpha(w.mShownAlpha);
@@ -8464,8 +9450,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
focusDisplayed = true;
}
+ final boolean obscuredChanged = w.mObscured != obscured;
+
// Update effect.
- if (!obscured) {
+ if (!(w.mObscured=obscured)) {
if (w.mSurface != null) {
if ((attrFlags&FLAG_KEEP_SCREEN_ON) != 0) {
holdScreen = w.mSession;
@@ -8481,7 +9469,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
- boolean opaqueDrawn = w.isOpaqueDrawn();
+ boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn();
if (opaqueDrawn && w.isFullscreen(dw, dh)) {
// This window completely covers everything behind it,
// so we want to leave all of them as unblurred (for
@@ -8564,6 +9552,13 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
}
+
+ if (obscuredChanged && mWallpaperTarget == w) {
+ // This is the wallpaper target and its obscured state
+ // changed... make sure the current wallaper's visibility
+ // has been updated accordingly.
+ updateWallpaperVisibilityLocked();
+ }
}
if (backgroundFillerShown == false && mBackgroundFillerShown) {
@@ -8617,6 +9612,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
i--;
WindowState win = mResizingWindows.get(i);
try {
+ if (DEBUG_ORIENTATION) Log.v(TAG, "Reporting new frame to "
+ + win + ": " + win.mFrame);
win.mClient.resized(win.mFrame.width(),
win.mFrame.height(), win.mLastContentInsets,
win.mLastVisibleInsets, win.mDrawPending);
@@ -8630,6 +9627,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
// Destroy the surface of any windows that are no longer visible.
+ boolean wallpaperDestroyed = false;
i = mDestroySurface.size();
if (i > 0) {
do {
@@ -8639,6 +9637,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (mInputMethodWindow == win) {
mInputMethodWindow = null;
}
+ if (win == mWallpaperTarget) {
+ wallpaperDestroyed = true;
+ }
win.destroySurfaceLocked();
} while (i > 0);
mDestroySurface.clear();
@@ -8649,6 +9650,9 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
WindowToken token = mExitingTokens.get(i);
if (!token.hasVisible) {
mExitingTokens.remove(i);
+ if (token.windowType == TYPE_WALLPAPER) {
+ mWallpaperTokens.remove(token);
+ }
}
}
@@ -8661,10 +9665,31 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
}
}
+ boolean needRelayout = false;
+
+ if (!animating && mAppTransitionRunning) {
+ // We have finished the animation of an app transition. To do
+ // this, we have delayed a lot of operations like showing and
+ // hiding apps, moving apps in Z-order, etc. The app token list
+ // reflects the correct Z-order, but the window list may now
+ // be out of sync with it. So here we will just rebuild the
+ // entire app window list. Fun!
+ mAppTransitionRunning = false;
+ needRelayout = true;
+ rebuildAppWindowListLocked();
+ // Clear information about apps that were moving.
+ mToBottomApps.clear();
+ }
+
if (focusDisplayed) {
mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
}
- if (animating) {
+ if (wallpaperDestroyed) {
+ needRelayout = adjustWallpaperWindowsLocked() != 0;
+ }
+ if (needRelayout) {
+ requestAnimationLocked(0);
+ } else if (animating) {
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
mQueue.setHoldScreenLocked(holdScreen != null);
@@ -8738,7 +9763,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
+ " token=" + win.mToken
+ " pid=" + ws.mSession.mPid
+ " uid=" + ws.mSession.mUid);
- ws.mSurface.clear();
+ ws.mSurface.destroy();
ws.mSurface = null;
mForceRemoves.add(ws);
i--;
@@ -8748,7 +9773,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
Log.w(TAG, "LEAKED SURFACE (app token hidden): "
+ ws + " surface=" + ws.mSurface
+ " token=" + win.mAppToken);
- ws.mSurface.clear();
+ ws.mSurface.destroy();
ws.mSurface = null;
leakedSurface = true;
}
@@ -8784,7 +9809,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
// surface and ask the app to request another one.
Log.w(TAG, "Looks like we have reclaimed some memory, clearing surface for retry.");
if (surface != null) {
- surface.clear();
+ surface.destroy();
win.mSurface = null;
}
@@ -9043,6 +10068,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
w.dump(pw, " ");
}
}
+ if (mResizingWindows.size() > 0) {
+ pw.println(" ");
+ pw.println(" Windows waiting to resize:");
+ for (int i=mResizingWindows.size()-1; i>=0; i--) {
+ WindowState w = mResizingWindows.get(i);
+ pw.print(" Resizing #"); pw.print(i); pw.print(' ');
+ pw.print(w); pw.println(":");
+ w.dump(pw, " ");
+ }
+ }
if (mSessions.size() > 0) {
pw.println(" ");
pw.println(" All active sessions:");
@@ -9071,6 +10106,16 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.println(mTokenList.get(i));
}
}
+ if (mWallpaperTokens.size() > 0) {
+ pw.println(" ");
+ pw.println(" Wallpaper tokens:");
+ for (int i=mWallpaperTokens.size()-1; i>=0; i--) {
+ WindowToken token = mWallpaperTokens.get(i);
+ pw.print(" Wallpaper #"); pw.print(i);
+ pw.print(' '); pw.print(token); pw.println(':');
+ token.dump(pw, " ");
+ }
+ }
if (mAppTokens.size() > 0) {
pw.println(" ");
pw.println(" Application tokens in Z order:");
@@ -9115,6 +10160,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mFocusedApp="); pw.println(mFocusedApp);
pw.print(" mInputMethodTarget="); pw.println(mInputMethodTarget);
pw.print(" mInputMethodWindow="); pw.println(mInputMethodWindow);
+ pw.print(" mWallpaperTarget="); pw.println(mWallpaperTarget);
+ if (mLowerWallpaperTarget != null && mUpperWallpaperTarget != null) {
+ pw.print(" mLowerWallpaperTarget="); pw.println(mLowerWallpaperTarget);
+ pw.print(" mUpperWallpaperTarget="); pw.println(mUpperWallpaperTarget);
+ }
pw.print(" mInTouchMode="); pw.println(mInTouchMode);
pw.print(" mSystemBooted="); pw.print(mSystemBooted);
pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
@@ -9126,7 +10176,11 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print( " no DimAnimator ");
}
pw.print(" mInputMethodAnimLayerAdjustment=");
- pw.println(mInputMethodAnimLayerAdjustment);
+ pw.print(mInputMethodAnimLayerAdjustment);
+ pw.print(" mWallpaperAnimLayerAdjustment=");
+ pw.println(mWallpaperAnimLayerAdjustment);
+ pw.print(" mLastWallpaperX="); pw.print(mLastWallpaperX);
+ pw.print(" mLastWallpaperY="); pw.println(mLastWallpaperY);
pw.print(" mDisplayFrozen="); pw.print(mDisplayFrozen);
pw.print(" mWindowsFreezingScreen="); pw.print(mWindowsFreezingScreen);
pw.print(" mAppsFreezingScreen="); pw.println(mAppsFreezingScreen);
@@ -9139,6 +10193,7 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
pw.print(" mNextAppTransition=0x");
pw.print(Integer.toHexString(mNextAppTransition));
pw.print(", mAppTransitionReady="); pw.print(mAppTransitionReady);
+ pw.print(", mAppTransitionRunning="); pw.print(mAppTransitionRunning);
pw.print(", mAppTransitionTimeout="); pw.println( mAppTransitionTimeout);
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
@@ -9148,6 +10203,12 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
if (mClosingApps.size() > 0) {
pw.print(" mClosingApps="); pw.println(mClosingApps);
}
+ if (mToTopApps.size() > 0) {
+ pw.print(" mToTopApps="); pw.println(mToTopApps);
+ }
+ if (mToBottomApps.size() > 0) {
+ pw.print(" mToBottomApps="); pw.println(mToBottomApps);
+ }
pw.print(" DisplayWidth="); pw.print(mDisplay.getWidth());
pw.print(" DisplayHeight="); pw.println(mDisplay.getHeight());
pw.println(" KeyWaiter state:");
@@ -9166,6 +10227,10 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
synchronized (mKeyWaiter) { }
}
+ public void virtualKeyFeedback(KeyEvent event) {
+ mPolicy.keyFeedbackFromInput(event);
+ }
+
/**
* DimAnimator class that controls the dim animation. This holds the surface and
* all state used for dim animation.
@@ -9215,7 +10280,8 @@ public class WindowManagerService extends IWindowManager.Stub implements Watchdo
mDimSurface.setLayer(w.mAnimLayer-1);
final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
- if (SHOW_TRANSACTIONS) Log.i(TAG, "layer=" + (w.mAnimLayer-1) + ", target=" + target);
+ if (SHOW_TRANSACTIONS) Log.i(TAG, " DIM " + mDimSurface
+ + ": layer=" + (w.mAnimLayer-1) + " target=" + target);
if (mDimTargetAlpha != target) {
// If the desired dim level has changed, then
// start an animation to it.
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5d34d00..66ef557 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -33,14 +33,17 @@ import android.app.AlertDialog;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.IActivityController;
+import android.app.IActivityManager;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IInstrumentationWatcher;
import android.app.IServiceConnection;
import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.ResultInfo;
+import android.app.Service;
import android.backup.IBackupManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -66,6 +69,7 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Debug;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -133,6 +137,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
static final boolean DEBUG_SERVICE = localLOGV || false;
static final boolean DEBUG_VISBILITY = localLOGV || false;
static final boolean DEBUG_PROCESSES = localLOGV || false;
+ static final boolean DEBUG_PROVIDER = localLOGV || false;
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
static final boolean DEBUG_RESULTS = localLOGV || false;
static final boolean DEBUG_BACKUP = localLOGV || true;
@@ -334,6 +339,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Memory pages are 4K.
static final int PAGE_SIZE = 4*1024;
+ // System property defining error report receiver for system apps
+ static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
+
+ // System property defining default error report receiver
+ static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
+
// Corresponding memory levels for above adjustments.
final int EMPTY_APP_MEM;
final int HIDDEN_APP_MEM;
@@ -763,6 +774,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
String mTopData;
boolean mSystemReady = false;
boolean mBooting = false;
+ boolean mWaitingUpdate = false;
+ boolean mDidUpdate = false;
Context mContext;
@@ -980,6 +993,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
res.set(0);
}
}
+
+ ensureBootCompleted();
} break;
case SHOW_NOT_RESPONDING_MSG: {
synchronized (ActivityManagerService.this) {
@@ -1000,13 +1015,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
proc.anrDialog = d;
}
- ensureScreenEnabled();
+ ensureBootCompleted();
} break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
mContext, msg.getData().getCharSequence("msg"));
d.show();
- enableScreenAfterBoot();
+ ensureBootCompleted();
} break;
case UPDATE_CONFIGURATION_MSG: {
final ContentResolver resolver = mContext.getContentResolver();
@@ -1850,12 +1865,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
- "activity", r.intent.getComponent());
+ "activity", r.intent.getComponent(), false);
}
private final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
- String hostingType, ComponentName hostingName) {
+ String hostingType, ComponentName hostingName, boolean allowWhileBooting) {
ProcessRecord app = getProcessRecordLocked(processName, info.uid);
// We don't have to do anything more if:
// (1) There is an existing application record; and
@@ -1908,7 +1923,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// If the system is not ready yet, then hold off on starting this
// process until it is.
if (!mSystemReady
- && (info.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
+ && !isAllowedWhileBooting(info)
+ && !allowWhileBooting) {
if (!mProcessesOnHold.contains(app)) {
mProcessesOnHold.add(app);
}
@@ -1919,6 +1935,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return (app.pid != 0) ? app : null;
}
+ boolean isAllowedWhileBooting(ApplicationInfo ai) {
+ return (ai.flags&ApplicationInfo.FLAG_PERSISTENT) != 0;
+ }
+
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
if (app.pid > 0 && app.pid != MY_PID) {
@@ -2898,7 +2918,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
* or null if none was found.
*/
private final HistoryRecord performClearTaskLocked(int taskId,
- HistoryRecord newR, boolean doClear) {
+ HistoryRecord newR, int launchFlags, boolean doClear) {
int i = mHistory.size();
// First find the requested task.
@@ -2941,7 +2961,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Finally, if this is a normal launch mode (that is, not
// expecting onNewIntent()), then we will finish the current
// instance of the activity so a new fresh one can be started.
- if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE) {
+ if (ret.launchMode == ActivityInfo.LAUNCH_MULTIPLE
+ && (launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0) {
if (!ret.finishing) {
int index = indexOfTokenLocked(ret);
if (index >= 0) {
@@ -3338,7 +3359,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// cases this means we are resetting the task to its
// initial state.
HistoryRecord top = performClearTaskLocked(
- taskTop.task.taskId, r, true);
+ taskTop.task.taskId, r, launchFlags, true);
if (top != null) {
if (top.frontOfTask) {
// Activity aliases may mean we use different
@@ -3481,7 +3502,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// task, but the caller has asked to clear that task if the
// activity is already running.
HistoryRecord top = performClearTaskLocked(
- sourceRecord.task.taskId, r, true);
+ sourceRecord.task.taskId, r, launchFlags, true);
if (top != null) {
logStartActivity(LOG_AM_NEW_INTENT, r, top.task);
deliverNewIntentLocked(top, r.intent);
@@ -3592,6 +3613,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ public int startActivityPendingIntent(IApplicationThread caller,
+ PendingIntent intent, Intent fillInIntent, String resolvedType,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) {
+ // Refuse possible leaked file descriptors
+ if (fillInIntent != null && fillInIntent.hasFileDescriptors()) {
+ throw new IllegalArgumentException("File descriptors passed in Intent");
+ }
+
+ IIntentSender sender = intent.getTarget();
+ if (!(sender instanceof PendingIntentRecord)) {
+ throw new IllegalArgumentException("Bad PendingIntent object");
+ }
+
+ PendingIntentRecord pir = (PendingIntentRecord)sender;
+ if (pir.key.type != IActivityManager.INTENT_SENDER_ACTIVITY) {
+ return START_NOT_ACTIVITY;
+ }
+
+ return pir.sendInner(0, fillInIntent, resolvedType,
+ null, resultTo, resultWho, requestCode, flagsMask, flagsValues);
+ }
+
public boolean startNextMatchingActivity(IBinder callingActivity,
Intent intent) {
// Refuse possible leaked file descriptors
@@ -4877,6 +4921,52 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
Binder.restoreCallingIdentity(origId);
}
+ public void getProcessMemoryInfo(int pid, Debug.MemoryInfo mi)
+ throws RemoteException {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+
+ if (proc == null) {
+ throw new RemoteException();
+ }
+
+ IApplicationThread thread = proc.thread;
+ if (thread == null) {
+ throw new RemoteException();
+ }
+
+ thread.getMemoryInfo(mi);
+ }
+
+ public void killApplicationProcess(String processName, int uid) {
+ if (processName == null) {
+ return;
+ }
+
+ int callerUid = Binder.getCallingUid();
+ // Only the system server can kill an application
+ if (callerUid == Process.SYSTEM_UID) {
+ synchronized (this) {
+ ProcessRecord app = getProcessRecordLocked(processName, uid);
+ if (app != null) {
+ try {
+ app.thread.scheduleSuicide();
+ } catch (RemoteException e) {
+ // If the other end already died, then our work here is done.
+ }
+ } else {
+ Log.w(TAG, "Process/uid not found attempting kill of "
+ + processName + " / " + uid);
+ }
+ }
+ } else {
+ throw new SecurityException(callerUid + " cannot kill app process: " +
+ processName);
+ }
+ }
+
private void restartPackageLocked(final String packageName, int uid) {
uninstallPackageLocked(packageName, uid, false);
Intent intent = new Intent(Intent.ACTION_PACKAGE_RESTARTED,
@@ -5101,8 +5191,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
- List providers = generateApplicationProvidersLocked(app);
+ boolean normalMode = mSystemReady || isAllowedWhileBooting(app.info);
+ List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
+ if (!normalMode) {
+ Log.i(TAG, "Launching preboot mode app: " + app);
+ }
+
if (localLOGV) Log.v(
TAG, "New app record " + app
+ " thread=" + thread.asBinder() + " pid=" + pid);
@@ -5118,12 +5213,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mWaitForDebugger = mOrigWaitForDebugger;
}
}
+
// If the app is being launched for restore or full backup, set it up specially
boolean isRestrictedBackupMode = false;
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
}
+
ensurePackageDexOpt(app.instrumentationInfo != null
? app.instrumentationInfo.packageName
: app.info.packageName);
@@ -5134,7 +5231,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
? app.instrumentationInfo : app.info, providers,
app.instrumentationClass, app.instrumentationProfileFile,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
- isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
+ isRestrictedBackupMode || !normalMode,
+ mConfiguration, getCommonServicesLocked());
updateLRUListLocked(app, false);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
@@ -5300,6 +5398,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
void enableScreenAfterBoot() {
+ EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
+ SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
}
@@ -5410,26 +5510,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (booting) {
- // Ensure that any processes we had put on hold are now started
- // up.
- final int NP = mProcessesOnHold.size();
- if (NP > 0) {
- ArrayList<ProcessRecord> procs =
- new ArrayList<ProcessRecord>(mProcessesOnHold);
- for (int ip=0; ip<NP; ip++) {
- this.startProcessLocked(procs.get(ip), "on-hold", null);
- }
- }
- if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
- // Tell anyone interested that we are done booting!
- synchronized (this) {
- broadcastIntentLocked(null, null,
- new Intent(Intent.ACTION_BOOT_COMPLETED, null),
- null, null, 0, null, null,
- android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
- false, false, MY_PID, Process.SYSTEM_UID);
- }
- }
+ finishBooting();
}
trimApplications();
@@ -5437,22 +5518,48 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
//mWindowManager.dump();
if (enableScreen) {
- EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
enableScreenAfterBoot();
}
}
- final void ensureScreenEnabled() {
+ final void finishBooting() {
+ // Ensure that any processes we had put on hold are now started
+ // up.
+ final int NP = mProcessesOnHold.size();
+ if (NP > 0) {
+ ArrayList<ProcessRecord> procs =
+ new ArrayList<ProcessRecord>(mProcessesOnHold);
+ for (int ip=0; ip<NP; ip++) {
+ this.startProcessLocked(procs.get(ip), "on-hold", null);
+ }
+ }
+ if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
+ // Tell anyone interested that we are done booting!
+ synchronized (this) {
+ broadcastIntentLocked(null, null,
+ new Intent(Intent.ACTION_BOOT_COMPLETED, null),
+ null, null, 0, null, null,
+ android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
+ false, false, MY_PID, Process.SYSTEM_UID);
+ }
+ }
+ }
+
+ final void ensureBootCompleted() {
+ boolean booting;
boolean enableScreen;
synchronized (this) {
+ booting = mBooting;
+ mBooting = false;
enableScreen = !mBooted;
mBooted = true;
}
+
+ if (booting) {
+ finishBooting();
+ }
if (enableScreen) {
- EventLog.writeEvent(LOG_BOOT_PROGRESS_ENABLE_SCREEN,
- SystemClock.uptimeMillis());
enableScreenAfterBoot();
}
}
@@ -5604,6 +5711,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
throw new IllegalArgumentException("File descriptors passed in Intent");
}
+ if (type == INTENT_SENDER_BROADCAST) {
+ if ((intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+ }
+
synchronized(this) {
int callingUid = Binder.getCallingUid();
try {
@@ -7322,9 +7436,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
final long origId = Binder.clearCallingIdentity();
- // In this case the provider is a single instance, so we can
+ // In this case the provider instance already exists, so we can
// return it right away.
if (r != null) {
+ if (DEBUG_PROVIDER) Log.v(TAG,
+ "Adding provider requested by "
+ + r.processName + " from process "
+ + cpr.info.processName);
r.conProviders.add(cpr);
cpr.clients.add(r);
} else {
@@ -7382,10 +7500,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return cpr;
}
- if (false) {
- RuntimeException e = new RuntimeException("foo");
- //Log.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
- // + " pruid " + ai.uid + "): " + cpi.className, e);
+ if (DEBUG_PROVIDER) {
+ RuntimeException e = new RuntimeException("here");
+ Log.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + r.info.uid
+ + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
}
// This is single process, and our app is now connecting to it.
@@ -7397,14 +7515,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (mLaunchingProviders.get(i) == cpr) {
break;
}
- if (false) {
- final ContentProviderRecord rec =
- (ContentProviderRecord)mLaunchingProviders.get(i);
- if (rec.info.name.equals(cpr.info.name)) {
- cpr = rec;
- break;
- }
- }
}
// If the provider is not already being launched, then get it
@@ -7414,7 +7524,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ProcessRecord proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
- cpi.name));
+ cpi.name), false);
if (proc == null) {
Log.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
@@ -7435,6 +7545,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mProvidersByName.put(name, cpr);
if (r != null) {
+ if (DEBUG_PROVIDER) Log.v(TAG,
+ "Adding provider requested by "
+ + r.processName + " from process "
+ + cpr.info.processName);
r.conProviders.add(cpr);
cpr.clients.add(r);
} else {
@@ -7489,8 +7603,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (this) {
ContentProviderRecord cpr = (ContentProviderRecord)mProvidersByName.get(name);
if(cpr == null) {
- //remove from mProvidersByClass
- if(localLOGV) Log.v(TAG, name+" content provider not found in providers list");
+ // remove from mProvidersByClass
+ if (DEBUG_PROVIDER) Log.v(TAG, name +
+ " provider not found in providers list");
return;
}
final ProcessRecord r = getRecordForAppLocked(caller);
@@ -7500,12 +7615,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
" when removing content provider " + name);
}
//update content provider record entry info
- ContentProviderRecord localCpr = (ContentProviderRecord) mProvidersByClass.get(cpr.info.name);
- if(localLOGV) Log.v(TAG, "Removing content provider requested by "+
- r.info.processName+" from process "+localCpr.appInfo.processName);
- if(localCpr.appInfo.processName == r.info.processName) {
+ ContentProviderRecord localCpr = (ContentProviderRecord)
+ mProvidersByClass.get(cpr.info.name);
+ if (DEBUG_PROVIDER) Log.v(TAG, "Removing provider requested by "
+ + r.info.processName + " from process "
+ + localCpr.appInfo.processName);
+ if (localCpr.app == r) {
//should not happen. taken care of as a local provider
- if(localLOGV) Log.v(TAG, "local provider doing nothing Ignoring other names");
+ Log.w(TAG, "removeContentProvider called on local provider: "
+ + cpr.info.name + " in process " + r.processName);
return;
} else {
localCpr.clients.remove(r);
@@ -8099,7 +8217,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- systemReady();
+ systemReady(null);
}
private void retrieveSettings() {
@@ -8129,7 +8247,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return mSystemReady;
}
- public void systemReady() {
+ public void systemReady(final Runnable goingCallback) {
// In the simulator, startRunning will never have been called, which
// normally sets a few crucial variables. Do it here instead.
if (!Process.supportsProcesses()) {
@@ -8139,19 +8257,96 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized(this) {
if (mSystemReady) {
+ if (goingCallback != null) goingCallback.run();
return;
}
+
+ // Check to see if there are any update receivers to run.
+ if (!mDidUpdate) {
+ if (mWaitingUpdate) {
+ return;
+ }
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ List<ResolveInfo> ris = null;
+ try {
+ ris = ActivityThread.getPackageManager().queryIntentReceivers(
+ intent, null, 0);
+ } catch (RemoteException e) {
+ }
+ if (ris != null) {
+ for (int i=ris.size()-1; i>=0; i--) {
+ if ((ris.get(i).activityInfo.applicationInfo.flags
+ &ApplicationInfo.FLAG_SYSTEM) == 0) {
+ ris.remove(i);
+ }
+ }
+ intent.addFlags(Intent.FLAG_RECEIVER_BOOT_UPGRADE);
+ for (int i=0; i<ris.size(); i++) {
+ ActivityInfo ai = ris.get(i).activityInfo;
+ intent.setComponent(new ComponentName(ai.packageName, ai.name));
+ IIntentReceiver finisher = null;
+ if (i == 0) {
+ finisher = new IIntentReceiver.Stub() {
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered)
+ throws RemoteException {
+ synchronized (ActivityManagerService.this) {
+ mDidUpdate = true;
+ }
+ systemReady(goingCallback);
+ }
+ };
+ }
+ Log.i(TAG, "Sending system update to: " + intent.getComponent());
+ broadcastIntentLocked(null, null, intent, null, finisher,
+ 0, null, null, null, true, false, MY_PID, Process.SYSTEM_UID);
+ if (i == 0) {
+ mWaitingUpdate = true;
+ }
+ }
+ }
+ if (mWaitingUpdate) {
+ return;
+ }
+ mDidUpdate = true;
+ }
+
mSystemReady = true;
if (!mStartRunning) {
return;
}
}
- if (Config.LOGD) Log.d(TAG, "Start running!");
+ ArrayList<ProcessRecord> procsToKill = null;
+ synchronized(mPidsSelfLocked) {
+ for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
+ ProcessRecord proc = mPidsSelfLocked.valueAt(i);
+ if (!isAllowedWhileBooting(proc.info)){
+ if (procsToKill == null) {
+ procsToKill = new ArrayList<ProcessRecord>();
+ }
+ procsToKill.add(proc);
+ }
+ }
+ }
+
+ if (procsToKill != null) {
+ synchronized(this) {
+ for (int i=procsToKill.size()-1; i>=0; i--) {
+ ProcessRecord proc = procsToKill.get(i);
+ Log.i(TAG, "Removing system update proc: " + proc);
+ removeProcessLocked(proc, true);
+ }
+ }
+ }
+
+ Log.i(TAG, "System now ready");
EventLog.writeEvent(LOG_BOOT_PROGRESS_AMS_READY,
SystemClock.uptimeMillis());
synchronized(this) {
+ // Make sure we have no pre-ready processes sitting around.
+
if (mFactoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
ResolveInfo ri = mContext.getPackageManager()
.resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
@@ -8187,6 +8382,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
retrieveSettings();
+ if (goingCallback != null) goingCallback.run();
+
synchronized (this) {
if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
try {
@@ -8209,6 +8406,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ // Start up initial activity.
+ mBooting = true;
+
try {
if (ActivityThread.getPackageManager().hasSystemUidErrors()) {
Message msg = Message.obtain();
@@ -8218,8 +8418,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} catch (RemoteException e) {
}
- // Start up initial activity.
- mBooting = true;
resumeTopActivityLocked(null);
}
}
@@ -8236,27 +8434,62 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private ComponentName getErrorReportReceiver(ProcessRecord app) {
IPackageManager pm = ActivityThread.getPackageManager();
+
try {
- // was an installer package name specified when this app was
- // installed?
- String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
- if (installerPackageName == null) {
- return null;
+ // look for receiver in the installer package
+ String candidate = pm.getInstallerPackageName(app.info.packageName);
+ ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+ if (result != null) {
+ return result;
}
- // is there an Activity in this package that handles ACTION_APP_ERROR?
- Intent intent = new Intent(Intent.ACTION_APP_ERROR);
- intent.setPackage(installerPackageName);
- ResolveInfo info = pm.resolveIntent(intent, null, 0);
- if (info == null || info.activityInfo == null) {
- return null;
+ // if the error app is on the system image, look for system apps
+ // error receiver
+ if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
+ candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
+ result = getErrorReportReceiver(pm, app.info.packageName, candidate);
+ if (result != null) {
+ return result;
+ }
}
- return new ComponentName(installerPackageName, info.activityInfo.name);
+ // if there is a default receiver, try that
+ candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
+ return getErrorReportReceiver(pm, app.info.packageName, candidate);
} catch (RemoteException e) {
- // will return null and no error report will be delivered
+ // should not happen
+ Log.e(TAG, "error talking to PackageManager", e);
+ return null;
}
- return null;
+ }
+
+ /**
+ * Return activity in receiverPackage that handles ACTION_APP_ERROR.
+ *
+ * @param pm PackageManager isntance
+ * @param errorPackage package which caused the error
+ * @param receiverPackage candidate package to receive the error
+ * @return activity component within receiverPackage which handles
+ * ACTION_APP_ERROR, or null if not found
+ */
+ private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage,
+ String receiverPackage) throws RemoteException {
+ if (receiverPackage == null || receiverPackage.length() == 0) {
+ return null;
+ }
+
+ // break the loop if it's the error report receiver package that crashed
+ if (receiverPackage.equals(errorPackage)) {
+ return null;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_APP_ERROR);
+ intent.setPackage(receiverPackage);
+ ResolveInfo info = pm.resolveIntent(intent, null, 0);
+ if (info == null || info.activityInfo == null) {
+ return null;
+ }
+ return new ComponentName(receiverPackage, info.activityInfo.name);
}
void makeAppNotRespondingLocked(ProcessRecord app,
@@ -8578,6 +8811,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
report.crashInfo.throwFileName = trace.getFileName();
report.crashInfo.throwClassName = trace.getClassName();
report.crashInfo.throwMethodName = trace.getMethodName();
+ report.crashInfo.throwLineNumber = trace.getLineNumber();
} else if (r.notResponding) {
report.type = ApplicationErrorReport.TYPE_ANR;
report.anrInfo = new ApplicationErrorReport.AnrInfo();
@@ -8645,6 +8879,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ActivityManager.RunningAppProcessInfo currApp =
new ActivityManager.RunningAppProcessInfo(app.processName,
app.pid, app.getPackageList());
+ currApp.uid = app.info.uid;
int adj = app.curAdj;
if (adj >= CONTENT_PROVIDER_ADJ) {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY;
@@ -8661,6 +8896,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else {
currApp.importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
}
+ currApp.importanceReasonCode = app.adjTypeCode;
+ if (app.adjSource instanceof ProcessRecord) {
+ currApp.importanceReasonPid = ((ProcessRecord)app.adjSource).pid;
+ } else if (app.adjSource instanceof HistoryRecord) {
+ HistoryRecord r = (HistoryRecord)app.adjSource;
+ if (r.app != null) currApp.importanceReasonPid = r.app.pid;
+ }
+ if (app.adjTarget instanceof ComponentName) {
+ currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
+ }
//Log.v(TAG, "Proc " + app.processName + ": imp=" + currApp.importance
// + " lru=" + currApp.lru);
if (runList == null) {
@@ -9384,7 +9629,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
sr.app = null;
sr.executeNesting = 0;
mStoppingServices.remove(sr);
- if (sr.bindings.size() > 0) {
+
+ boolean hasClients = sr.bindings.size() > 0;
+ if (hasClients) {
Iterator<IntentBindRecord> bindings
= sr.bindings.values().iterator();
while (bindings.hasNext()) {
@@ -9405,7 +9652,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
} else if (!allowRestart) {
bringDownServiceLocked(sr, true);
} else {
- scheduleServiceRestartLocked(sr);
+ boolean canceled = scheduleServiceRestartLocked(sr, true);
+
+ // Should the service remain running? Note that in the
+ // extreme case of so many attempts to deliver a command
+ // that it failed, that we also will stop it here.
+ if (sr.startRequested && (sr.stopIfKilled || canceled)) {
+ if (sr.pendingStarts.size() == 0) {
+ sr.startRequested = false;
+ if (!hasClients) {
+ // Whoops, no reason to restart!
+ bringDownServiceLocked(sr, true);
+ }
+ }
+ }
}
}
@@ -9648,6 +9908,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (r.app != null) {
info.pid = r.app.pid;
}
+ info.uid = r.appInfo.uid;
info.process = r.processName;
info.foreground = r.isForeground;
info.activeSince = r.createTime;
@@ -9655,6 +9916,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
info.clientCount = r.connections.size();
info.crashCount = r.crashCount;
info.lastActivityTime = r.lastActivity;
+ if (r.isForeground) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_FOREGROUND;
+ }
+ if (r.startRequested) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
+ }
+ if (r.app != null && r.app.pid == Process.myPid()) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
+ }
+ if (r.app != null && r.app.persistent) {
+ info.flags |= ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS;
+ }
+ for (ConnectionRecord conn : r.connections.values()) {
+ if (conn.clientLabel != 0) {
+ info.clientPackage = conn.binding.client.info.packageName;
+ info.clientLabel = conn.clientLabel;
+ break;
+ }
+ }
return info;
}
@@ -9683,6 +9963,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ public PendingIntent getRunningServiceControlPanel(ComponentName name) {
+ synchronized (this) {
+ ServiceRecord r = mServices.get(name);
+ if (r != null) {
+ for (ConnectionRecord conn : r.connections.values()) {
+ if (conn.clientIntent != null) {
+ return conn.clientIntent;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private final ServiceRecord findServiceLocked(ComponentName name,
IBinder token) {
ServiceRecord r = mServices.get(name);
@@ -9844,35 +10138,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
private final void sendServiceArgsLocked(ServiceRecord r,
boolean oomAdjusted) {
- final int N = r.startArgs.size();
+ final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
- final int BASEID = r.lastStartId - N + 1;
int i = 0;
while (i < N) {
try {
- Intent args = r.startArgs.get(i);
+ ServiceRecord.StartItem si = r.pendingStarts.get(i);
if (DEBUG_SERVICE) Log.v(TAG, "Sending arguments to service: "
- + r.name + " " + r.intent + " args=" + args);
+ + r.name + " " + r.intent + " args=" + si.intent);
+ if (si.intent == null && N > 0) {
+ // If somehow we got a dummy start at the front, then
+ // just drop it here.
+ i++;
+ continue;
+ }
bumpServiceExecutingLocked(r);
if (!oomAdjusted) {
oomAdjusted = true;
updateOomAdjLocked(r.app);
}
- r.app.thread.scheduleServiceArgs(r, BASEID+i, args);
+ int flags = 0;
+ if (si.deliveryCount > 0) {
+ flags |= Service.START_FLAG_RETRY;
+ }
+ if (si.doneExecutingCount > 0) {
+ flags |= Service.START_FLAG_REDELIVERY;
+ }
+ r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
+ si.deliveredTime = SystemClock.uptimeMillis();
+ r.deliveredStarts.add(si);
+ si.deliveryCount++;
i++;
+ } catch (RemoteException e) {
+ // Remote process gone... we'll let the normal cleanup take
+ // care of this.
+ break;
} catch (Exception e) {
+ Log.w(TAG, "Unexpected exception", e);
break;
}
}
if (i == N) {
- r.startArgs.clear();
+ r.pendingStarts.clear();
} else {
while (i > 0) {
- r.startArgs.remove(0);
i--;
+ r.pendingStarts.remove(i);
}
}
}
@@ -9936,36 +10250,81 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
ensurePackageDexOpt(r.serviceInfo.packageName);
app.thread.scheduleCreateService(r, r.serviceInfo);
+ r.postNotification();
created = true;
} finally {
if (!created) {
app.services.remove(r);
- scheduleServiceRestartLocked(r);
+ scheduleServiceRestartLocked(r, false);
}
}
requestServiceBindingsLocked(r);
+
+ // If the service is in the started state, and there are no
+ // pending arguments, then fake up one so its onStartCommand() will
+ // be called.
+ if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
+ r.lastStartId++;
+ if (r.lastStartId < 1) {
+ r.lastStartId = 1;
+ }
+ r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
+ }
+
sendServiceArgsLocked(r, true);
}
- private final void scheduleServiceRestartLocked(ServiceRecord r) {
+ private final boolean scheduleServiceRestartLocked(ServiceRecord r,
+ boolean allowCancel) {
+ boolean canceled = false;
+
final long now = SystemClock.uptimeMillis();
+ long minDuration = SERVICE_RESTART_DURATION;
+ long resetTime = SERVICE_RESET_RUN_DURATION;
+
+ // Any delivered but not yet finished starts should be put back
+ // on the pending list.
+ final int N = r.deliveredStarts.size();
+ if (N > 0) {
+ for (int i=N-1; i>=0; i--) {
+ ServiceRecord.StartItem si = r.deliveredStarts.get(i);
+ if (si.intent == null) {
+ // We'll generate this again if needed.
+ } else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
+ && si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
+ r.pendingStarts.add(0, si);
+ long dur = SystemClock.uptimeMillis() - si.deliveredTime;
+ dur *= 2;
+ if (minDuration < dur) minDuration = dur;
+ if (resetTime < dur) resetTime = dur;
+ } else {
+ Log.w(TAG, "Canceling start item " + si.intent + " in service "
+ + r.name);
+ canceled = true;
+ }
+ }
+ r.deliveredStarts.clear();
+ }
r.totalRestartCount++;
if (r.restartDelay == 0) {
r.restartCount++;
- r.restartDelay = SERVICE_RESTART_DURATION;
+ r.restartDelay = minDuration;
} else {
// If it has been a "reasonably long time" since the service
// was started, then reset our restart duration back to
// the beginning, so we don't infinitely increase the duration
// on a service that just occasionally gets killed (which is
// a normal case, due to process being killed to reclaim memory).
- if (now > (r.restartTime+SERVICE_RESET_RUN_DURATION)) {
+ if (now > (r.restartTime+resetTime)) {
r.restartCount = 1;
- r.restartDelay = SERVICE_RESTART_DURATION;
+ r.restartDelay = minDuration;
} else {
r.restartDelay *= SERVICE_RESTART_DURATION_FACTOR;
+ if (r.restartDelay < minDuration) {
+ r.restartDelay = minDuration;
+ }
}
}
@@ -9994,6 +10353,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
mRestartingServices.add(r);
}
+ r.cancelNotification();
+
mHandler.removeCallbacks(r.restarter);
mHandler.postAtTime(r.restarter, r.nextRestartTime);
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
@@ -10006,6 +10367,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
msg.what = SERVICE_ERROR_MSG;
msg.obj = r;
mHandler.sendMessage(msg);
+
+ return canceled;
}
final void performServiceRestartLocked(ServiceRecord r) {
@@ -10065,7 +10428,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (startProcessLocked(appName, r.appInfo, true, intentFlags,
- "service", r.name) == null) {
+ "service", r.name, false) == null) {
Log.w(TAG, "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
@@ -10162,15 +10525,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ r.cancelNotification();
+ r.isForeground = false;
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+
+ // Clear start entries.
+ r.deliveredStarts.clear();
+ r.pendingStarts.clear();
+
if (r.app != null) {
synchronized (r.stats.getBatteryStats()) {
r.stats.stopLaunchedLocked();
}
r.app.services.remove(r);
if (r.app.thread != null) {
- updateServiceForegroundLocked(r.app, false);
try {
- Log.i(TAG, "Stopping service: " + r.shortName);
+ if (DEBUG_SERVICE) Log.v(TAG,
+ "Stopping service: " + r.shortName);
bumpServiceExecutingLocked(r);
mStoppingServices.add(r);
updateOomAdjLocked(r.app);
@@ -10180,6 +10552,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ r.shortName, e);
serviceDoneExecutingLocked(r, true);
}
+ updateServiceForegroundLocked(r.app, false);
} else {
if (DEBUG_SERVICE) Log.v(
TAG, "Removed service that has no process: " + r.shortName);
@@ -10223,11 +10596,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
+ r.shortName);
}
r.startRequested = true;
- r.startArgs.add(service);
+ r.callStart = false;
r.lastStartId++;
if (r.lastStartId < 1) {
r.lastStartId = 1;
}
+ r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
r.lastActivity = SystemClock.uptimeMillis();
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
@@ -10295,6 +10669,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
r.record.stats.stopRunningLocked();
}
r.record.startRequested = false;
+ r.record.callStart = false;
final long origId = Binder.clearCallingIdentity();
bringDownServiceLocked(r.record, false);
Binder.restoreCallingIdentity(origId);
@@ -10343,10 +10718,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (DEBUG_SERVICE) Log.v(TAG, "stopServiceToken: " + className
+ " " + token + " startId=" + startId);
ServiceRecord r = findServiceLocked(className, token);
- if (r != null && (startId < 0 || r.lastStartId == startId)) {
+ if (r != null) {
+ if (startId >= 0) {
+ // Asked to only stop if done with all work. Note that
+ // to avoid leaks, we will take this as dropping all
+ // start items up to and including this one.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ while (r.deliveredStarts.size() > 0) {
+ if (r.deliveredStarts.remove(0) == si) {
+ break;
+ }
+ }
+ }
+
+ if (r.lastStartId != startId) {
+ return false;
+ }
+
+ if (r.deliveredStarts.size() > 0) {
+ Log.w(TAG, "stopServiceToken startId " + startId
+ + " is last, but have " + r.deliveredStarts.size()
+ + " remaining args");
+ }
+ }
+
synchronized (r.stats.getBatteryStats()) {
r.stats.stopRunningLocked();
r.startRequested = false;
+ r.callStart = false;
}
final long origId = Binder.clearCallingIdentity();
bringDownServiceLocked(r, false);
@@ -10358,20 +10758,45 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
public void setServiceForeground(ComponentName className, IBinder token,
- boolean isForeground) {
+ int id, Notification notification, boolean removeNotification) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
synchronized(this) {
ServiceRecord r = findServiceLocked(className, token);
if (r != null) {
- if (r.isForeground != isForeground) {
- final long origId = Binder.clearCallingIdentity();
- r.isForeground = isForeground;
+ if (id != 0) {
+ if (notification == null) {
+ throw new IllegalArgumentException("null notification");
+ }
+ if (r.foregroundId != id) {
+ r.cancelNotification();
+ r.foregroundId = id;
+ }
+ notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
+ r.foregroundNoti = notification;
+ r.isForeground = true;
+ r.postNotification();
if (r.app != null) {
updateServiceForegroundLocked(r.app, true);
}
- Binder.restoreCallingIdentity(origId);
+ } else {
+ if (r.isForeground) {
+ r.isForeground = false;
+ if (r.app != null) {
+ updateServiceForegroundLocked(r.app, true);
+ }
+ }
+ if (removeNotification) {
+ r.cancelNotification();
+ r.foregroundId = 0;
+ r.foregroundNoti = null;
+ }
}
}
}
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
}
public void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
@@ -10420,6 +10845,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
activity = (HistoryRecord)mHistory.get(aindex);
}
+ int clientLabel = 0;
+ PendingIntent clientIntent = null;
+
+ if (callerApp.info.uid == Process.SYSTEM_UID) {
+ // Hacky kind of thing -- allow system stuff to tell us
+ // what they are, so we can report this elsewhere for
+ // others to know why certain services are running.
+ try {
+ clientIntent = (PendingIntent)service.getParcelableExtra(
+ Intent.EXTRA_CLIENT_INTENT);
+ } catch (RuntimeException e) {
+ }
+ if (clientIntent != null) {
+ clientLabel = service.getIntExtra(Intent.EXTRA_CLIENT_LABEL, 0);
+ if (clientLabel != 0) {
+ // There are no useful extras in the intent, trash them.
+ // System code calling with this stuff just needs to know
+ // this will happen.
+ service = service.cloneFilter();
+ }
+ }
+ }
+
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid());
@@ -10440,7 +10888,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
- connection, flags);
+ connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
s.connections.put(binder, c);
@@ -10665,7 +11113,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
- public void serviceDoneExecuting(IBinder token) {
+ public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
@@ -10683,6 +11131,51 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return;
}
+ if (type == 1) {
+ // This is a call from a service start... take care of
+ // book-keeping.
+ r.callStart = true;
+ switch (res) {
+ case Service.START_STICKY_COMPATIBILITY:
+ case Service.START_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ // Don't stop if killed.
+ r.stopIfKilled = false;
+ break;
+ }
+ case Service.START_NOT_STICKY: {
+ // We are done with the associated start arguments.
+ r.findDeliveredStart(startId, true);
+ if (r.lastStartId == startId) {
+ // There is no more work, and this service
+ // doesn't want to hang around if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ case Service.START_REDELIVER_INTENT: {
+ // We'll keep this item until they explicitly
+ // call stop for it, but keep track of the fact
+ // that it was delivered.
+ ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
+ if (si != null) {
+ si.deliveryCount = 0;
+ si.doneExecutingCount++;
+ // Don't stop if killed.
+ r.stopIfKilled = true;
+ }
+ break;
+ }
+ default:
+ throw new IllegalArgumentException(
+ "Unknown service start result: " + res);
+ }
+ if (res == Service.START_STICKY_COMPATIBILITY) {
+ r.callStart = false;
+ }
+ }
+
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inStopping);
Binder.restoreCallingIdentity(origId);
@@ -10761,7 +11254,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
// startProcessLocked() returns existing proc's record if it's already running
ProcessRecord proc = startProcessLocked(app.processName, app,
- false, 0, "backup", hostingName);
+ false, 0, "backup", hostingName, false);
if (proc == null) {
Log.e(TAG, "Unable to start backup agent process " + r);
return false;
@@ -11298,10 +11791,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
synchronized(this) {
+ int flags = intent.getFlags();
+
if (!mSystemReady) {
// if the caller really truly claims to know what they're doing, go
// ahead and allow the broadcast without launching any receivers
- int flags = intent.getFlags();
if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
intent = new Intent(intent);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
@@ -11312,6 +11806,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
+ if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
+ throw new IllegalArgumentException(
+ "Can't use FLAG_RECEIVER_BOOT_UPGRADE here");
+ }
+
final ProcessRecord callerApp = getRecordForAppLocked(caller);
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
@@ -11906,12 +12405,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
// restart the application.
}
- // Not running -- get it started, and enqueue this history record
- // to be executed when the app comes up.
+ // Not running -- get it started, to be executed when the app comes up.
if ((r.curApp=startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
- "broadcast", r.curComponent)) == null) {
+ "broadcast", r.curComponent,
+ (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0))
+ == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Log.w(TAG, "Unable to launch app "
@@ -12132,6 +12632,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
mConfiguration = newConfig;
+ Log.i(TAG, "Config changed: " + newConfig);
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = new Configuration(mConfiguration);
@@ -12360,6 +12861,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
return (app.curAdj=app.maxAdj);
}
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
app.adjSource = null;
app.adjTarget = null;
@@ -12440,15 +12942,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
if (app.services.size() != 0 && adj > FOREGROUND_APP_ADJ) {
- // If this process has active services running in it, we would
- // like to avoid killing it unless it would prevent the current
- // application from running. By default we put the process in
- // with the rest of the background processes; as we scan through
- // its services we may bump it up from there.
- if (adj > hiddenAdj) {
- adj = hiddenAdj;
- app.adjType = "bg-services";
- }
final long now = SystemClock.uptimeMillis();
// This process is more important if the top activity is
// bound to the service.
@@ -12493,6 +12986,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = clientAdj > VISIBLE_APP_ADJ
? clientAdj : VISIBLE_APP_ADJ;
app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
app.adjSource = cr.binding.client;
app.adjTarget = s.serviceInfo.name;
}
@@ -12506,23 +13001,27 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|| a.state == ActivityState.PAUSING)) {
adj = FOREGROUND_APP_ADJ;
app.adjType = "service";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_SERVICE_IN_USE;
app.adjSource = a;
app.adjTarget = s.serviceInfo.name;
}
}
}
}
+
+ // Finally, f this process has active services running in it, we
+ // would like to avoid killing it unless it would prevent the current
+ // application from running. By default we put the process in
+ // with the rest of the background processes; as we scan through
+ // its services we may bump it up from there.
+ if (adj > hiddenAdj) {
+ adj = hiddenAdj;
+ app.adjType = "bg-services";
+ }
}
if (app.pubProviders.size() != 0 && adj > FOREGROUND_APP_ADJ) {
- // If this process has published any content providers, then
- // its adjustment makes it at least as important as any of the
- // processes using those providers, and no less important than
- // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY.
- if (adj > CONTENT_PROVIDER_ADJ) {
- adj = CONTENT_PROVIDER_ADJ;
- app.adjType = "pub-providers";
- }
Iterator jt = app.pubProviders.values().iterator();
while (jt.hasNext() && adj > FOREGROUND_APP_ADJ) {
ContentProviderRecord cpr = (ContentProviderRecord)jt.next();
@@ -12548,6 +13047,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
adj = clientAdj > FOREGROUND_APP_ADJ
? clientAdj : FOREGROUND_APP_ADJ;
app.adjType = "provider";
+ app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+ .REASON_PROVIDER_IN_USE;
app.adjSource = client;
app.adjTarget = cpr.info.name;
}
@@ -12564,6 +13065,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
}
}
+
+ // Finally, if this process has published any content providers,
+ // then its adjustment makes it at least as important as any of the
+ // processes using those providers, and no less important than
+ // CONTENT_PROVIDER_ADJ, which is just shy of EMPTY.
+ if (adj > CONTENT_PROVIDER_ADJ) {
+ adj = CONTENT_PROVIDER_ADJ;
+ app.adjType = "pub-providers";
+ }
}
app.curRawAdj = adj;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index c834b34..ed0d534 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -260,6 +260,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
enforceCallingPermission();
synchronized (mStats) {
mStats.noteBluetoothOnLocked();
+ mStats.setBtHeadset(new android.bluetooth.BluetoothHeadset(mContext, null));
}
}
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index b3343dd..f613b00 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.app.IServiceConnection;
+import android.app.PendingIntent;
import java.io.PrintWriter;
@@ -28,6 +29,8 @@ class ConnectionRecord {
final HistoryRecord activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
final int flags; // Binding options.
+ final int clientLabel; // String resource labeling this client.
+ final PendingIntent clientIntent; // How to launch the client.
String stringName; // Caching of toString.
void dump(PrintWriter pw, String prefix) {
@@ -40,11 +43,14 @@ class ConnectionRecord {
}
ConnectionRecord(AppBindRecord _binding, HistoryRecord _activity,
- IServiceConnection _conn, int _flags) {
+ IServiceConnection _conn, int _flags,
+ int _clientLabel, PendingIntent _clientIntent) {
binding = _binding;
activity = _activity;
conn = _conn;
flags = _flags;
+ clientLabel = _clientLabel;
+ clientIntent = _clientIntent;
}
public String toString() {
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index fa2a100..d994362 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -22,6 +22,7 @@ import android.content.IIntentReceiver;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -172,6 +173,14 @@ class PendingIntentRecord extends IIntentSender.Stub {
public int send(int code, Intent intent, String resolvedType,
IIntentReceiver finishedReceiver) {
+ return sendInner(code, intent, resolvedType, finishedReceiver,
+ null, null, 0, 0, 0);
+ }
+
+ int sendInner(int code, Intent intent, String resolvedType,
+ IIntentReceiver finishedReceiver,
+ IBinder resultTo, String resultWho, int requestCode,
+ int flagsMask, int flagsValues) {
synchronized(owner) {
if (!canceled) {
sent = true;
@@ -189,6 +198,8 @@ class PendingIntentRecord extends IIntentSender.Stub {
} else {
resolvedType = key.requestResolvedType;
}
+ flagsValues &= flagsMask;
+ finalIntent.setFlags((finalIntent.getFlags()&~flagsMask) | flagsValues);
final long origId = Binder.clearCallingIdentity();
@@ -198,7 +209,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
try {
owner.startActivityInPackage(uid,
finalIntent, resolvedType,
- null, null, 0, false);
+ resultTo, resultWho, requestCode, false);
} catch (RuntimeException e) {
Log.w(ActivityManagerService.TAG,
"Unable to send startActivity intent", e);
@@ -246,7 +257,7 @@ class PendingIntentRecord extends IIntentSender.Stub {
return 0;
}
}
- return -1;
+ return IActivityManager.START_CANCELED;
}
protected void finalize() throws Throwable {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 76fdf09..d05b44b 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -75,6 +75,7 @@ class ProcessRecord implements Watchdog.PssRequestor {
boolean reportLowMemory; // Set to true when waiting to report low mem
int lastPss; // Last pss size reported by app.
String adjType; // Debugging: primary thing impacting oom_adj.
+ int adjTypeCode; // Debugging: adj code to report to app.
Object adjSource; // Debugging: option dependent object.
Object adjTarget; // Debugging: target component impacting oom_adj.
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 98df4b3..2534410 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -18,12 +18,16 @@ package com.android.server.am;
import com.android.internal.os.BatteryStatsImpl;
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
+import android.os.RemoteException;
import android.os.SystemClock;
import java.io.PrintWriter;
@@ -60,13 +64,38 @@ class ServiceRecord extends Binder {
final HashMap<IBinder, ConnectionRecord> connections
= new HashMap<IBinder, ConnectionRecord>();
// IBinder -> ConnectionRecord of all bound clients
- final List<Intent> startArgs = new ArrayList<Intent>();
+
+ // Maximum number of delivery attempts before giving up.
+ static final int MAX_DELIVERY_COUNT = 3;
+
+ // Maximum number of times it can fail during execution before giving up.
+ static final int MAX_DONE_EXECUTING_COUNT = 6;
+
+ static class StartItem {
+ final int id;
+ final Intent intent;
+ long deliveredTime;
+ int deliveryCount;
+ int doneExecutingCount;
+
+ StartItem(int _id, Intent _intent) {
+ id = _id;
+ intent = _intent;
+ }
+ }
+ final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
+ // start() arguments which been delivered.
+ final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
// start() arguments that haven't yet been delivered.
- ProcessRecord app; // where this service is running or null.
- boolean isForeground; // asked to run as a foreground service?
+ ProcessRecord app; // where this service is running or null.
+ boolean isForeground; // is service currently in foreground mode?
+ int foregroundId; // Notification ID of last foreground req.
+ Notification foregroundNoti; // Notification record of foreground state.
long lastActivity; // last time there was some activity on the service.
boolean startRequested; // someone explicitly called start?
+ boolean stopIfKilled; // last onStart() said to stop if service killed?
+ boolean callStart; // last onStart() has asked to alway be called on restart.
int lastStartId; // identifier of most recent start request.
int executeNesting; // number of outstanding operations keeping foreground.
long executingStart; // start time of last execute request.
@@ -79,6 +108,25 @@ class ServiceRecord extends Binder {
String stringName; // caching of toString
+ void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ StartItem si = list.get(i);
+ pw.print(prefix); pw.print("#"); pw.print(i);
+ pw.print(" id="); pw.print(si.id);
+ if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
+ if (si.deliveryCount != 0) {
+ pw.print(" dc="); pw.print(si.deliveryCount);
+ }
+ if (si.doneExecutingCount != 0) {
+ pw.print(" dxc="); pw.print(si.doneExecutingCount);
+ }
+ pw.print(" ");
+ if (si.intent != null) pw.println(si.intent.toString());
+ else pw.println("null");
+ }
+ }
+
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("intent={");
pw.print(intent.getIntent().toShortString(true, false));
@@ -93,20 +141,39 @@ class ServiceRecord extends Binder {
if (!resDir.equals(baseDir)) pw.print(" resDir="); pw.print(resDir);
pw.print(" dataDir="); pw.println(dataDir);
pw.print(prefix); pw.print("app="); pw.println(app);
- pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
- pw.print(" lastActivity="); pw.println(lastActivity-now);
- pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
- pw.print(" startId="); pw.print(lastStartId);
- pw.print(" executeNesting="); pw.print(executeNesting);
+ if (isForeground || foregroundId != 0) {
+ pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
+ pw.print(" foregroundId="); pw.print(foregroundId);
+ pw.print(" foregroundNoti="); pw.println(foregroundNoti);
+ }
+ pw.print(prefix); pw.print("lastActivity="); pw.print(lastActivity-now);
pw.print(" executingStart="); pw.print(executingStart-now);
- pw.print(" crashCount="); pw.println(crashCount);
- pw.print(prefix); pw.print("totalRestartCount="); pw.print(totalRestartCount);
- pw.print(" restartCount="); pw.print(restartCount);
- pw.print(" restartDelay="); pw.print(restartDelay);
- pw.print(" restartTime="); pw.print(restartTime-now);
- pw.print(" nextRestartTime="); pw.println(nextRestartTime-now);
+ pw.print(" restartTime="); pw.println(restartTime);
+ if (startRequested || lastStartId != 0) {
+ pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
+ pw.print(" stopIfKilled="); pw.print(stopIfKilled);
+ pw.print(" callStart="); pw.print(callStart);
+ pw.print(" lastStartId="); pw.println(lastStartId);
+ }
+ if (executeNesting != 0 || crashCount != 0 || restartCount != 0
+ || restartDelay != 0 || nextRestartTime != 0) {
+ pw.print(prefix); pw.print("executeNesting="); pw.print(executeNesting);
+ pw.print(" restartCount="); pw.print(restartCount);
+ pw.print(" restartDelay="); pw.print(restartDelay-now);
+ pw.print(" nextRestartTime="); pw.print(nextRestartTime-now);
+ pw.print(" crashCount="); pw.println(crashCount);
+ }
+ if (deliveredStarts.size() > 0) {
+ pw.print(prefix); pw.println("Delivered Starts:");
+ dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
+ }
+ if (pendingStarts.size() > 0) {
+ pw.print(prefix); pw.println("Pending Starts:");
+ dumpStartList(pw, prefix, pendingStarts, 0);
+ }
if (bindings.size() > 0) {
Iterator<IntentBindRecord> it = bindings.values().iterator();
+ pw.print(prefix); pw.println("Bindings:");
while (it.hasNext()) {
IntentBindRecord b = it.next();
pw.print(prefix); pw.print("* IntentBindRecord{");
@@ -167,6 +234,45 @@ class ServiceRecord extends Binder {
restartTime = 0;
}
+ public StartItem findDeliveredStart(int id, boolean remove) {
+ final int N = deliveredStarts.size();
+ for (int i=0; i<N; i++) {
+ StartItem si = deliveredStarts.get(i);
+ if (si.id == id) {
+ if (remove) deliveredStarts.remove(i);
+ return si;
+ }
+ }
+
+ return null;
+ }
+
+ public void postNotification() {
+ if (foregroundId != 0 && foregroundNoti != null) {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm != null) {
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotification(packageName, foregroundId,
+ foregroundNoti, outId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
+ public void cancelNotification() {
+ if (foregroundId != 0) {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm != null) {
+ try {
+ inm.cancelNotification(packageName, foregroundId);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }
+
public String toString() {
if (stringName != null) {
return stringName;
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index d458911..66868a3 100755..100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -695,7 +695,14 @@ public final class UsageStatsService extends IUsageStats.Stub {
if (NC > 0) {
for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) {
sb.append("A:");
- sb.append(ent.getKey());
+ String activity = ent.getKey();
+ if (activity.startsWith(pkgName)) {
+ sb.append('*');
+ sb.append(activity.substring(
+ pkgName.length(), activity.length()));
+ } else {
+ sb.append(activity);
+ }
TimeStats times = ent.getValue();
sb.append(',');
sb.append(times.count);
diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java
index a4b47b5..f3127f3 100644
--- a/services/java/com/android/server/status/StatusBarPolicy.java
+++ b/services/java/com/android/server/status/StatusBarPolicy.java
@@ -18,10 +18,10 @@ package com.android.server.status;
import android.app.AlertDialog;
import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothIntent;
+import android.bluetooth.BluetoothPbap;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -76,15 +76,8 @@ public class StatusBarPolicy {
private static StatusBarPolicy sInstance;
// message codes for the handler
- private static final int EVENT_DATA_CONN_STATE_CHANGED = 2;
- private static final int EVENT_DATA_ACTIVITY = 3;
private static final int EVENT_BATTERY_CLOSE = 4;
- // indices into mBatteryThresholds
- private static final int BATTERY_THRESHOLD_CLOSE_WARNING = 0;
- private static final int BATTERY_THRESHOLD_WARNING = 1;
- private static final int BATTERY_THRESHOLD_EMPTY = 2;
-
private final Context mContext;
private final StatusBarService mService;
private final Handler mHandler = new StatusBarHandler();
@@ -99,26 +92,21 @@ public class StatusBarPolicy {
private IBinder mBatteryIcon;
private IconData mBatteryData;
private boolean mBatteryFirst = true;
- private int mBatteryPlugged;
+ private boolean mBatteryPlugged;
private int mBatteryLevel;
- private int mBatteryThreshold = 0; // index into mBatteryThresholds
- private int[] mBatteryThresholds = new int[] { 20, 15, -1 };
private AlertDialog mLowBatteryDialog;
private TextView mBatteryLevelTextView;
private View mBatteryView;
private int mBatteryViewSequence;
private boolean mBatteryShowLowOnEndCall = false;
- private boolean mSentLowBatteryBroadcast = false;
private static final boolean SHOW_LOW_BATTERY_WARNING = true;
// phone
private TelephonyManager mPhone;
private IBinder mPhoneIcon;
- private IBinder mPhoneEvdoIcon;
//***** Signal strength icons
private IconData mPhoneData;
- private IconData mPhoneEvdoData;
//GSM/UMTS
private static final int[] sSignalImages = new int[] {
com.android.internal.R.drawable.stat_sys_signal_0,
@@ -134,14 +122,6 @@ public class StatusBarPolicy {
com.android.internal.R.drawable.stat_sys_r_signal_3,
com.android.internal.R.drawable.stat_sys_r_signal_4
};
- //CDMA
- private static final int[] sSignalImages_cdma = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_cdma_0,
- com.android.internal.R.drawable.stat_sys_signal_cdma_1,
- com.android.internal.R.drawable.stat_sys_signal_cdma_2,
- com.android.internal.R.drawable.stat_sys_signal_cdma_3,
- com.android.internal.R.drawable.stat_sys_signal_cdma_4
- };
private static final int[] sRoamingIndicatorImages_cdma = new int[] {
com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
// 1 is Standard Roaming Indicator OFF
@@ -241,14 +221,6 @@ public class StatusBarPolicy {
// 128-255 Reserved
};
- // EVDO
- private static final int[] sSignalImages_evdo = new int[] {
- com.android.internal.R.drawable.stat_sys_signal_evdo_0,
- com.android.internal.R.drawable.stat_sys_signal_evdo_1,
- com.android.internal.R.drawable.stat_sys_signal_evdo_2,
- com.android.internal.R.drawable.stat_sys_signal_evdo_3,
- com.android.internal.R.drawable.stat_sys_signal_evdo_4
- };
//***** Data connection icons
private int[] mDataIconList = sDataNetType_g;
@@ -271,20 +243,21 @@ public class StatusBarPolicy {
com.android.internal.R.drawable.stat_sys_data_out_e,
com.android.internal.R.drawable.stat_sys_data_inandout_e,
};
- //CDMA
- private static final int[] sDataNetType_evdo = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_evdo,
- com.android.internal.R.drawable.stat_sys_data_in_evdo,
- com.android.internal.R.drawable.stat_sys_data_out_evdo,
- com.android.internal.R.drawable.stat_sys_data_inandout_evdo,
- com.android.internal.R.drawable.stat_sys_data_dormant_evdo,
+ //3.5G
+ private static final int[] sDataNetType_h = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_h,
+ com.android.internal.R.drawable.stat_sys_data_in_h,
+ com.android.internal.R.drawable.stat_sys_data_out_h,
+ com.android.internal.R.drawable.stat_sys_data_inandout_h,
};
- private static final int[] sDataNetType_1xrtt = new int[] {
- com.android.internal.R.drawable.stat_sys_data_connected_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_in_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_out_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_inandout_1xrtt,
- com.android.internal.R.drawable.stat_sys_data_dormant_1xrtt,
+
+ //CDMA
+ // Use 3G icons for EVDO data and 1x icons for 1XRTT data
+ private static final int[] sDataNetType_1x = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_1x,
+ com.android.internal.R.drawable.stat_sys_data_in_1x,
+ com.android.internal.R.drawable.stat_sys_data_out_1x,
+ com.android.internal.R.drawable.stat_sys_data_inandout_1x,
};
// Assume it's all good unless we hear otherwise. We don't always seem
@@ -311,6 +284,7 @@ public class StatusBarPolicy {
private IconData mBluetoothData;
private int mBluetoothHeadsetState;
private int mBluetoothA2dpState;
+ private int mBluetoothPbapState;
private boolean mBluetoothEnabled;
// wifi
@@ -363,6 +337,9 @@ public class StatusBarPolicy {
else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
updateClock();
}
+ else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ updateBattery(intent);
+ }
else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateClock();
}
@@ -377,12 +354,17 @@ public class StatusBarPolicy {
else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
updateSyncState(intent);
}
- else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
- updateBattery(intent);
+ else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+ onBatteryLow(intent);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+ || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+ onBatteryOkay(intent);
}
- else if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION) ||
+ else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION) ||
- action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
+ action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION) ||
+ action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
updateBluetooth(intent);
}
else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
@@ -430,12 +412,6 @@ public class StatusBarPolicy {
null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
mPhoneIcon = service.addIcon(mPhoneData, null);
- // phone_evdo_signal
- mPhoneEvdoData = IconData.makeIcon("phone_evdo_signal",
- null, com.android.internal.R.drawable.stat_sys_signal_evdo_0, 0, 0);
- mPhoneEvdoIcon = service.addIcon(mPhoneEvdoData, null);
- service.setIconVisibility(mPhoneEvdoIcon, false);
-
// register for phone state notifications.
((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
.listen(mPhoneStateListener,
@@ -473,15 +449,16 @@ public class StatusBarPolicy {
mBluetoothData = IconData.makeIcon("bluetooth",
null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
mBluetoothIcon = service.addIcon(mBluetoothData, null);
- BluetoothDevice bluetooth =
- (BluetoothDevice) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
- if (bluetooth != null) {
- mBluetoothEnabled = bluetooth.isEnabled();
+ BluetoothAdapter adapter =
+ (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
+ if (adapter != null) {
+ mBluetoothEnabled = adapter.isEnabled();
} else {
mBluetoothEnabled = false;
}
mBluetoothA2dpState = BluetoothA2dp.STATE_DISCONNECTED;
mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+ mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
// Gps status
@@ -490,7 +467,7 @@ public class StatusBarPolicy {
mGpsFixIconData = IconData.makeIcon("gps",
null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
- service.setIconVisibility(mGpsIcon, false);
+ service.setIconVisibility(mGpsIcon, false);
// Alarm clock
mAlarmClockIconData = IconData.makeIcon(
@@ -513,7 +490,7 @@ public class StatusBarPolicy {
mVolumeIcon = service.addIcon(mVolumeData, null);
service.setIconVisibility(mVolumeIcon, false);
updateVolume();
-
+
IntentFilter filter = new IntentFilter();
// Register for Intent broadcasts for...
@@ -521,14 +498,18 @@ public class StatusBarPolicy {
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
filter.addAction(Intent.ACTION_ALARM_CHANGED);
filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
- filter.addAction(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
filter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
+ filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
@@ -564,38 +545,22 @@ public class StatusBarPolicy {
//mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
}
- private void pickNextBatteryLevel(int level) {
- final int N = mBatteryThresholds.length;
- for (int i=0; i<N; i++) {
- if (level >= mBatteryThresholds[i]) {
- mBatteryThreshold = i;
- break;
- }
- }
- if (mBatteryThreshold >= N) {
- mBatteryThreshold = N-1;
- }
- }
-
private final void updateBattery(Intent intent) {
mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
mBatteryData.iconLevel = intent.getIntExtra("level", 0);
mService.updateIcon(mBatteryIcon, mBatteryData, null);
- int plugged = intent.getIntExtra("plugged", 0);
+ boolean plugged = intent.getIntExtra("plugged", 0) != 0;
int level = intent.getIntExtra("level", -1);
if (false) {
Log.d(TAG, "updateBattery level=" + level
+ " plugged=" + plugged
+ " mBatteryPlugged=" + mBatteryPlugged
+ " mBatteryLevel=" + mBatteryLevel
- + " mBatteryThreshold=" + mBatteryThreshold
+ " mBatteryFirst=" + mBatteryFirst);
}
- int oldPlugged = mBatteryPlugged;
- int oldThreshold = mBatteryThreshold;
- pickNextBatteryLevel(level);
+ boolean oldPlugged = mBatteryPlugged;
mBatteryPlugged = plugged;
mBatteryLevel = level;
@@ -617,49 +582,35 @@ public class StatusBarPolicy {
}
*/
if (false) {
- Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level
- + " mBatteryThreshold=" + mBatteryThreshold + " oldThreshold=" + oldThreshold);
+ Log.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
}
- if (plugged == 0
- && ((oldPlugged != 0 && level < mBatteryThresholds[BATTERY_THRESHOLD_WARNING])
- || (mBatteryThreshold > oldThreshold
- && mBatteryThreshold > BATTERY_THRESHOLD_WARNING))) {
- // Broadcast the low battery warning
- mSentLowBatteryBroadcast = true;
- Intent batIntent = new Intent(Intent.ACTION_BATTERY_LOW);
- batIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(batIntent);
-
- if (SHOW_LOW_BATTERY_WARNING) {
- if (false) {
- Log.d(TAG, "mPhoneState=" + mPhoneState
- + " mLowBatteryDialog=" + mLowBatteryDialog
- + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
- }
+ }
- if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
- showLowBatteryWarning();
- } else {
- mBatteryShowLowOnEndCall = true;
- }
- }
- } else if (mBatteryThreshold < BATTERY_THRESHOLD_WARNING) {
- if (mSentLowBatteryBroadcast == true) {
- mSentLowBatteryBroadcast = false;
- Intent batIntent = new Intent(Intent.ACTION_BATTERY_OKAY);
- batIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcast(batIntent);
+ private void onBatteryLow(Intent intent) {
+ if (SHOW_LOW_BATTERY_WARNING) {
+ if (false) {
+ Log.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
}
- if (SHOW_LOW_BATTERY_WARNING) {
- if (mLowBatteryDialog != null) {
- mLowBatteryDialog.dismiss();
- mBatteryShowLowOnEndCall = false;
- }
+
+ if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ showLowBatteryWarning();
+ } else {
+ mBatteryShowLowOnEndCall = true;
}
}
}
- private void showBatteryView() {
+ private void onBatteryOkay(Intent intent) {
+ if (mLowBatteryDialog != null
+ && SHOW_LOW_BATTERY_WARNING) {
+ mLowBatteryDialog.dismiss();
+ mBatteryShowLowOnEndCall = false;
+ }
+ }
+
+ private void showBatteryView() {
closeLastBatteryView();
if (mLowBatteryDialog != null) {
mLowBatteryDialog.dismiss();
@@ -720,9 +671,11 @@ public class StatusBarPolicy {
private void showLowBatteryWarning() {
closeLastBatteryView();
- int level = mBatteryThresholds[mBatteryThreshold > 1 ? mBatteryThreshold - 1 : 0];
+ /* Show exact battery level.
+ * Add 1 because the text says "less than X%".
+ */
CharSequence levelText = mContext.getString(
- com.android.internal.R.string.battery_low_percent_format, level);
+ com.android.internal.R.string.battery_low_percent_format, mBatteryLevel + 1);
if (mBatteryLevelTextView != null) {
mBatteryLevelTextView.setText(levelText);
@@ -738,7 +691,7 @@ public class StatusBarPolicy {
b.setView(v);
b.setIcon(android.R.drawable.ic_dialog_alert);
b.setPositiveButton(android.R.string.ok, null);
-
+
final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_MULTIPLE_TASK
@@ -773,7 +726,7 @@ public class StatusBarPolicy {
}
if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
if (mBatteryShowLowOnEndCall) {
- if (mBatteryPlugged == 0) {
+ if (!mBatteryPlugged) {
showLowBatteryWarning();
}
mBatteryShowLowOnEndCall = false;
@@ -826,6 +779,10 @@ public class StatusBarPolicy {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
updateCallState(state);
+ // In cdma, if a voice call is made, RSSI should switch to 1x.
+ if (isCdma()) {
+ updateSignalStrength();
+ }
}
@Override
@@ -854,7 +811,7 @@ public class StatusBarPolicy {
final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
mSimState = IccCard.State.PIN_REQUIRED;
- }
+ }
else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
mSimState = IccCard.State.PUK_REQUIRED;
}
@@ -871,6 +828,14 @@ public class StatusBarPolicy {
return ((mPhone != null) && (mPhone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA));
}
+ private boolean isEvdo() {
+ return ( (mServiceState != null)
+ && ((mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+ || (mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_A)));
+ }
+
private boolean hasService() {
if (mServiceState != null) {
switch (mServiceState.getState()) {
@@ -887,9 +852,7 @@ public class StatusBarPolicy {
private final void updateSignalStrength() {
int iconLevel = -1;
- int evdoIconLevel = -1;
int[] iconList;
- int[] evdoIconList;
if (!hasService()) {
//Log.d(TAG, "updateSignalStrength: no service");
@@ -900,7 +863,6 @@ public class StatusBarPolicy {
mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
}
mService.updateIcon(mPhoneIcon, mPhoneData, null);
- mService.setIconVisibility(mPhoneEvdoIcon,false);
return;
}
@@ -923,65 +885,67 @@ public class StatusBarPolicy {
iconList = sSignalImages;
}
} else {
- iconList = this.sSignalImages_cdma;
-
- int cdmaDbm = mSignalStrength.getCdmaDbm();
- int cdmaEcio = mSignalStrength.getCdmaEcio();
- int levelDbm = 0;
- int levelEcio = 0;
-
- if (cdmaDbm >= -75) levelDbm = 4;
- else if (cdmaDbm >= -85) levelDbm = 3;
- else if (cdmaDbm >= -95) levelDbm = 2;
- else if (cdmaDbm >= -100) levelDbm = 1;
- else levelDbm = 0;
-
- // Ec/Io are in dB*10
- if (cdmaEcio >= -90) levelEcio = 4;
- else if (cdmaEcio >= -110) levelEcio = 3;
- else if (cdmaEcio >= -130) levelEcio = 2;
- else if (cdmaEcio >= -150) levelEcio = 1;
- else levelEcio = 0;
-
- iconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio;
- }
+ iconList = this.sSignalImages;
- if ((mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
- || (mServiceState.getRadioTechnology() == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) {
- // Use Evdo icon
- evdoIconList = this.sSignalImages_evdo;
-
- int evdoEcio = mSignalStrength.getEvdoEcio();
- int evdoSnr = mSignalStrength.getEvdoSnr();
- int levelEvdoEcio = 0;
- int levelEvdoSnr = 0;
-
- // Ec/Io are in dB*10
- if (evdoEcio >= -650) levelEvdoEcio = 4;
- else if (evdoEcio >= -750) levelEvdoEcio = 3;
- else if (evdoEcio >= -900) levelEvdoEcio = 2;
- else if (evdoEcio >= -1050) levelEvdoEcio = 1;
- else levelEvdoEcio = 0;
-
- if (evdoSnr > 7) levelEvdoSnr = 4;
- else if (evdoSnr > 5) levelEvdoSnr = 3;
- else if (evdoSnr > 3) levelEvdoSnr = 2;
- else if (evdoSnr > 1) levelEvdoSnr = 1;
- else levelEvdoSnr = 0;
-
- evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr;
-
- mPhoneEvdoData.iconId = evdoIconList[evdoIconLevel];
- mService.updateIcon(mPhoneEvdoIcon, mPhoneEvdoData, null);
- mService.setIconVisibility(mPhoneEvdoIcon,true);
- } else {
- mService.setIconVisibility(mPhoneEvdoIcon,false);
+ // If 3G(EV) and 1x network are available than 3G should be
+ // displayed, displayed RSSI should be from the EV side.
+ // If a voice call is made then RSSI should switch to 1x.
+ if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+ iconLevel = getEvdoLevel();
+ if (false) {
+ Log.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
+ }
+ } else {
+ iconLevel = getCdmaLevel();
+ }
}
-
mPhoneData.iconId = iconList[iconLevel];
mService.updateIcon(mPhoneIcon, mPhoneData, null);
}
+ private int getCdmaLevel() {
+ final int cdmaDbm = mSignalStrength.getCdmaDbm();
+ final int cdmaEcio = mSignalStrength.getCdmaEcio();
+ int levelDbm = 0;
+ int levelEcio = 0;
+
+ if (cdmaDbm >= -75) levelDbm = 4;
+ else if (cdmaDbm >= -85) levelDbm = 3;
+ else if (cdmaDbm >= -95) levelDbm = 2;
+ else if (cdmaDbm >= -100) levelDbm = 1;
+ else levelDbm = 0;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) levelEcio = 4;
+ else if (cdmaEcio >= -110) levelEcio = 3;
+ else if (cdmaEcio >= -130) levelEcio = 2;
+ else if (cdmaEcio >= -150) levelEcio = 1;
+ else levelEcio = 0;
+
+ return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ }
+
+ private int getEvdoLevel() {
+ int evdoDbm = mSignalStrength.getEvdoDbm();
+ int evdoSnr = mSignalStrength.getEvdoSnr();
+ int levelEvdoDbm = 0;
+ int levelEvdoSnr = 0;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 4;
+ else if (evdoDbm >= -75) levelEvdoDbm = 3;
+ else if (evdoDbm >= -90) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 0;
+
+ if (evdoSnr > 7) levelEvdoSnr = 4;
+ else if (evdoSnr > 5) levelEvdoSnr = 3;
+ else if (evdoSnr > 3) levelEvdoSnr = 2;
+ else if (evdoSnr > 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 0;
+
+ return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ }
+
private final void updateDataNetType() {
int net = mPhone.getNetworkType();
@@ -992,16 +956,21 @@ public class StatusBarPolicy {
case TelephonyManager.NETWORK_TYPE_UMTS:
mDataIconList = sDataNetType_3g;
break;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ mDataIconList = sDataNetType_h;
+ break;
case TelephonyManager.NETWORK_TYPE_CDMA:
// display 1xRTT for IS95A/B
- mDataIconList = this.sDataNetType_1xrtt;
+ mDataIconList = this.sDataNetType_1x;
break;
case TelephonyManager.NETWORK_TYPE_1xRTT:
- mDataIconList = this.sDataNetType_1xrtt;
+ mDataIconList = this.sDataNetType_1x;
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
case TelephonyManager.NETWORK_TYPE_EVDO_A:
- mDataIconList = sDataNetType_evdo;
+ mDataIconList = sDataNetType_3g;
break;
default:
mDataIconList = sDataNetType_g;
@@ -1054,8 +1023,6 @@ public class StatusBarPolicy {
iconId = mDataIconList[3];
break;
case TelephonyManager.DATA_ACTIVITY_DORMANT:
- iconId = mDataIconList[4];
- break;
default:
iconId = mDataIconList[0];
break;
@@ -1104,23 +1071,26 @@ public class StatusBarPolicy {
int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
String action = intent.getAction();
- if (action.equals(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION)) {
- int state = intent.getIntExtra(BluetoothIntent.BLUETOOTH_STATE,
- BluetoothError.ERROR);
- mBluetoothEnabled = state == BluetoothDevice.BLUETOOTH_STATE_ON;
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
} else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
mBluetoothHeadsetState = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
BluetoothHeadset.STATE_ERROR);
} else if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
mBluetoothA2dpState = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
BluetoothA2dp.STATE_DISCONNECTED);
+ } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+ mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
+ BluetoothPbap.STATE_DISCONNECTED);
} else {
return;
}
if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED ||
mBluetoothA2dpState == BluetoothA2dp.STATE_CONNECTED ||
- mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING) {
+ mBluetoothA2dpState == BluetoothA2dp.STATE_PLAYING ||
+ mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index b44168a..d680b8a 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -322,6 +322,7 @@ public class StatusBarService extends IStatusBar.Stub
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
context.registerReceiver(mBroadcastReceiver, filter);
}
@@ -1672,7 +1673,8 @@ public class StatusBarService extends IStatusBar.Stub
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
deactivate();
}
else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp
index 1d66fb1..85d63c9 100644
--- a/services/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/jni/com_android_server_AlarmManagerService.cpp
@@ -19,8 +19,8 @@
#include "JNIHelp.h"
#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/Log.h>
+#include <utils/misc.h>
#include <fcntl.h>
#include <stdio.h>
diff --git a/services/jni/com_android_server_BatteryService.cpp b/services/jni/com_android_server_BatteryService.cpp
index 2524966..8e7cadc 100644
--- a/services/jni/com_android_server_BatteryService.cpp
+++ b/services/jni/com_android_server_BatteryService.cpp
@@ -18,8 +18,8 @@
#include "JNIHelp.h"
#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
+#include <utils/Log.h>
+#include <utils/misc.h>
#include <fcntl.h>
#include <stdio.h>
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
+#include <dirent.h>
#if HAVE_ANDROID_OS
#include <linux/ioctl.h>
@@ -38,15 +39,7 @@
namespace android {
-#define AC_ONLINE_PATH "/sys/class/power_supply/ac/online"
-#define USB_ONLINE_PATH "/sys/class/power_supply/usb/online"
-#define BATTERY_STATUS_PATH "/sys/class/power_supply/battery/status"
-#define BATTERY_HEALTH_PATH "/sys/class/power_supply/battery/health"
-#define BATTERY_PRESENT_PATH "/sys/class/power_supply/battery/present"
-#define BATTERY_CAPACITY_PATH "/sys/class/power_supply/battery/capacity"
-#define BATTERY_VOLTAGE_PATH "/sys/class/power_supply/battery/batt_vol"
-#define BATTERY_TEMPERATURE_PATH "/sys/class/power_supply/battery/batt_temp"
-#define BATTERY_TECHNOLOGY_PATH "/sys/class/power_supply/battery/technology"
+#define POWER_SUPPLY_PATH "/sys/class/power_supply"
struct FieldIds {
// members
@@ -77,6 +70,21 @@ struct BatteryManagerConstants {
};
static BatteryManagerConstants gConstants;
+struct PowerSupplyPaths {
+ char* acOnlinePath;
+ char* usbOnlinePath;
+ char* batteryStatusPath;
+ char* batteryHealthPath;
+ char* batteryPresentPath;
+ char* batteryCapacityPath;
+ char* batteryVoltagePath;
+ char* batteryTemperaturePath;
+ char* batteryTechnologyPath;
+};
+static PowerSupplyPaths gPaths;
+
+static int gVoltageDivisor = 1;
+
static jint getBatteryStatus(const char* status)
{
switch (status[0]) {
@@ -126,6 +134,8 @@ static jint getBatteryHealth(const char* status)
static int readFromFile(const char* path, char* buf, size_t size)
{
+ if (!path)
+ return -1;
int fd = open(path, O_RDONLY, 0);
if (fd == -1) {
LOGE("Could not open '%s'", path);
@@ -171,29 +181,43 @@ static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fie
env->SetIntField(obj, fieldID, value);
}
+static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
+{
+ const int SIZE = 128;
+ char buf[SIZE];
+
+ jint value = 0;
+ if (readFromFile(path, buf, SIZE) > 0) {
+ value = atoi(buf);
+ value /= gVoltageDivisor;
+ }
+ env->SetIntField(obj, fieldID, value);
+}
+
+
static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
{
- setBooleanField(env, obj, AC_ONLINE_PATH, gFieldIds.mAcOnline);
- setBooleanField(env, obj, USB_ONLINE_PATH, gFieldIds.mUsbOnline);
- setBooleanField(env, obj, BATTERY_PRESENT_PATH, gFieldIds.mBatteryPresent);
+ setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
+ setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
+ setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);
- setIntField(env, obj, BATTERY_CAPACITY_PATH, gFieldIds.mBatteryLevel);
- setIntField(env, obj, BATTERY_VOLTAGE_PATH, gFieldIds.mBatteryVoltage);
- setIntField(env, obj, BATTERY_TEMPERATURE_PATH, gFieldIds.mBatteryTemperature);
+ setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
+ setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
+ setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);
const int SIZE = 128;
char buf[SIZE];
- if (readFromFile(BATTERY_STATUS_PATH, buf, SIZE) > 0)
+ if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0)
env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
else
env->SetIntField(obj, gFieldIds.mBatteryStatus,
gConstants.statusUnknown);
- if (readFromFile(BATTERY_HEALTH_PATH, buf, SIZE) > 0)
+ if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0)
env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
- if (readFromFile(BATTERY_TECHNOLOGY_PATH, buf, SIZE) > 0)
+ if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0)
env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
}
@@ -204,6 +228,101 @@ static JNINativeMethod sMethods[] = {
int register_android_server_BatteryService(JNIEnv* env)
{
+ char path[PATH_MAX];
+ struct dirent* entry;
+
+ DIR* dir = opendir(POWER_SUPPLY_PATH);
+ if (dir == NULL) {
+ LOGE("Could not open %s\n", POWER_SUPPLY_PATH);
+ return -1;
+ }
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ char buf[20];
+ // Look for "type" file in each subdirectory
+ snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
+ int length = readFromFile(path, buf, sizeof(buf));
+ if (length > 0) {
+ if (buf[length - 1] == '\n')
+ buf[length - 1] = 0;
+
+ if (strcmp(buf, "Mains") == 0) {
+ snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.acOnlinePath = strdup(path);
+ }
+ else if (strcmp(buf, "USB") == 0) {
+ snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.usbOnlinePath = strdup(path);
+ }
+ else if (strcmp(buf, "Battery") == 0) {
+ snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryStatusPath = strdup(path);
+ snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryHealthPath = strdup(path);
+ snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryPresentPath = strdup(path);
+ snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryCapacityPath = strdup(path);
+
+ snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0) {
+ gPaths.batteryVoltagePath = strdup(path);
+ // voltage_now is in microvolts, not millivolts
+ gVoltageDivisor = 1000;
+ } else {
+ snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryVoltagePath = strdup(path);
+ }
+
+ snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0) {
+ gPaths.batteryTemperaturePath = strdup(path);
+ } else {
+ snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryTemperaturePath = strdup(path);
+ }
+
+ snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
+ if (access(path, R_OK) == 0)
+ gPaths.batteryTechnologyPath = strdup(path);
+ }
+ }
+ }
+ closedir(dir);
+
+ if (!gPaths.acOnlinePath)
+ LOGE("acOnlinePath not found");
+ if (!gPaths.usbOnlinePath)
+ LOGE("usbOnlinePath not found");
+ if (!gPaths.batteryStatusPath)
+ LOGE("batteryStatusPath not found");
+ if (!gPaths.batteryHealthPath)
+ LOGE("batteryHealthPath not found");
+ if (!gPaths.batteryPresentPath)
+ LOGE("batteryPresentPath not found");
+ if (!gPaths.batteryCapacityPath)
+ LOGE("batteryCapacityPath not found");
+ if (!gPaths.batteryVoltagePath)
+ LOGE("batteryVoltagePath not found");
+ if (!gPaths.batteryTemperaturePath)
+ LOGE("batteryTemperaturePath not found");
+ if (!gPaths.batteryTechnologyPath)
+ LOGE("batteryTechnologyPath not found");
+
jclass clazz = env->FindClass("com/android/server/BatteryService");
if (clazz == NULL) {
diff --git a/services/jni/com_android_server_HardwareService.cpp b/services/jni/com_android_server_HardwareService.cpp
index b0aab59..22d4bd8 100644
--- a/services/jni/com_android_server_HardwareService.cpp
+++ b/services/jni/com_android_server_HardwareService.cpp
@@ -133,7 +133,7 @@ static void vibratorOff(JNIEnv *env, jobject clazz)
static JNINativeMethod method_table[] = {
{ "init_native", "()I", (void*)init_native },
- { "finalize_native", "(I)V", (void*)init_native },
+ { "finalize_native", "(I)V", (void*)finalize_native },
{ "setLight_native", "(IIIIII)V", (void*)setLight_native },
{ "vibratorOn", "(J)V", (void*)vibratorOn },
{ "vibratorOff", "()V", (void*)vibratorOff }
diff --git a/services/jni/com_android_server_KeyInputQueue.cpp b/services/jni/com_android_server_KeyInputQueue.cpp
index 63830d5..f27596c 100644
--- a/services/jni/com_android_server_KeyInputQueue.cpp
+++ b/services/jni/com_android_server_KeyInputQueue.cpp
@@ -110,6 +110,23 @@ android_server_KeyInputQueue_getDeviceName(JNIEnv* env, jobject clazz,
return NULL;
}
+static void
+android_server_KeyInputQueue_addExcludedDevice(JNIEnv* env, jobject clazz,
+ jstring deviceName)
+{
+ gLock.lock();
+ sp<EventHub> hub = gHub;
+ if (hub == NULL) {
+ hub = new EventHub;
+ gHub = hub;
+ }
+ gLock.unlock();
+
+ const char* nameStr = env->GetStringUTFChars(deviceName, NULL);
+ gHub->addExcludedDevice(nameStr);
+ env->ReleaseStringUTFChars(deviceName, nameStr);
+}
+
static jboolean
android_server_KeyInputQueue_getAbsoluteInfo(JNIEnv* env, jobject clazz,
jint deviceId, jint axis,
@@ -205,6 +222,23 @@ android_server_KeyInputQueue_getKeycodeStateDevice(JNIEnv* env, jobject clazz,
return st;
}
+static jint
+android_server_KeyInputQueue_scancodeToKeycode(JNIEnv* env, jobject clazz,
+ jint deviceId, jint scancode)
+{
+ jint res = 0;
+ gLock.lock();
+ if (gHub != NULL) {
+ int32_t keycode;
+ uint32_t flags;
+ gHub->scancodeToKeycode(deviceId, scancode, &keycode, &flags);
+ res = keycode;
+ }
+ gLock.unlock();
+
+ return res;
+}
+
static jboolean
android_server_KeyInputQueue_hasKeys(JNIEnv* env, jobject clazz,
jintArray keyCodes, jbooleanArray outFlags)
@@ -238,6 +272,8 @@ static JNINativeMethod gInputMethods[] = {
(void*) android_server_KeyInputQueue_getDeviceClasses },
{ "getDeviceName", "(I)Ljava/lang/String;",
(void*) android_server_KeyInputQueue_getDeviceName },
+ { "addExcludedDevice", "(Ljava/lang/String;)V",
+ (void*) android_server_KeyInputQueue_addExcludedDevice },
{ "getAbsoluteInfo", "(IILcom/android/server/InputDevice$AbsoluteInfo;)Z",
(void*) android_server_KeyInputQueue_getAbsoluteInfo },
{ "getSwitchState", "(I)I",
@@ -254,6 +290,8 @@ static JNINativeMethod gInputMethods[] = {
(void*) android_server_KeyInputQueue_getKeycodeStateDevice },
{ "hasKeys", "([I[Z)Z",
(void*) android_server_KeyInputQueue_hasKeys },
+ { "scancodeToKeycode", "(II)I",
+ (void*) android_server_KeyInputQueue_scancodeToKeycode },
};
int register_android_server_KeyInputQueue(JNIEnv* env)
diff --git a/services/jni/com_android_server_SensorService.cpp b/services/jni/com_android_server_SensorService.cpp
index 7390786..3911d1f 100644
--- a/services/jni/com_android_server_SensorService.cpp
+++ b/services/jni/com_android_server_SensorService.cpp
@@ -111,6 +111,15 @@ android_open(JNIEnv *env, jclass clazz)
return bundle;
}
+static jint
+android_close(JNIEnv *env, jclass clazz)
+{
+ if (sSensorDevice->close_data_source)
+ return sSensorDevice->close_data_source(sSensorDevice);
+ else
+ return 0;
+}
+
static jboolean
android_activate(JNIEnv *env, jclass clazz, jint sensor, jboolean activate)
{
@@ -135,6 +144,7 @@ android_data_wake(JNIEnv *env, jclass clazz)
static JNINativeMethod gMethods[] = {
{"_sensors_control_init", "()I", (void*) android_init },
{"_sensors_control_open", "()Landroid/os/Bundle;", (void*) android_open },
+ {"_sensors_control_close", "()I", (void*) android_close },
{"_sensors_control_activate", "(IZ)Z", (void*) android_activate },
{"_sensors_control_wake", "()I", (void*) android_data_wake },
{"_sensors_control_set_delay","(I)I", (void*) android_set_delay },
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index dbe8431..4368464 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -22,11 +22,17 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.SystemProperties;
import android.provider.Contacts;
+import android.provider.ContactsContract;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
+import android.util.Log;
import android.util.SparseIntArray;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_IDP_STRING;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
+
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -56,6 +62,9 @@ public class PhoneNumberUtils
public static final int TOA_International = 0x91;
public static final int TOA_Unknown = 0x81;
+ static final String LOG_TAG = "PhoneNumberUtils";
+ private static final boolean DBG = false;
+
/*
* global-phone-number = ["+"] 1*( DIGIT / written-sep )
* written-sep = ("-"/".")
@@ -129,15 +138,23 @@ public class PhoneNumberUtils
}
String type = intent.resolveType(context);
+ String phoneColumn = null;
+
+ // Correctly read out the phone entry based on requested provider
+ final String authority = uri.getAuthority();
+ if (Contacts.AUTHORITY.equals(authority)) {
+ phoneColumn = Contacts.People.Phones.NUMBER;
+ } else if (ContactsContract.AUTHORITY.equals(authority)) {
+ phoneColumn = ContactsContract.CommonDataKinds.Phone.NUMBER;
+ }
- Cursor c = context.getContentResolver().query(
- uri, new String[]{ Contacts.People.Phones.NUMBER },
- null, null, null);
+ final Cursor c = context.getContentResolver().query(uri, new String[] {
+ phoneColumn
+ }, null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
- number = c.getString(
- c.getColumnIndex(Contacts.People.Phones.NUMBER));
+ number = c.getString(c.getColumnIndex(phoneColumn));
}
} finally {
c.close();
@@ -218,6 +235,9 @@ public class PhoneNumberUtils
}
}
+ private static void log(String msg) {
+ Log.d(LOG_TAG, msg);
+ }
/** index of the last character of the network portion
* (eg anything after is a post-dial string)
*/
@@ -732,6 +752,14 @@ public class PhoneNumberUtils
return true;
}
+ private static boolean isNonSeparator(String address) {
+ for (int i = 0, count = address.length(); i < count; i++) {
+ if (!isNonSeparator(address.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Note: calls extractNetworkPortion(), so do not use for
* SIM EF[ADN] style records
@@ -903,7 +931,7 @@ public class PhoneNumberUtils
"JM", // Jamaica
"PR", // Puerto Rico
"MS", // Montserrat
- "NP", // Northern Mariana Islands
+ "MP", // Northern Mariana Islands
"KN", // Saint Kitts and Nevis
"LC", // Saint Lucia
"VC", // Saint Vincent and the Grenadines
@@ -936,17 +964,7 @@ public class PhoneNumberUtils
public static int getFormatTypeForLocale(Locale locale) {
String country = locale.getCountry();
- // Check for the NANP countries
- int length = NANP_COUNTRIES.length;
- for (int i = 0; i < length; i++) {
- if (NANP_COUNTRIES[i].equals(country)) {
- return FORMAT_NANP;
- }
- }
- if (locale.equals(Locale.JAPAN)) {
- return FORMAT_JAPAN;
- }
- return FORMAT_UNKNOWN;
+ return getFormatTypeFromCountryCode(country);
}
/**
@@ -990,6 +1008,7 @@ public class PhoneNumberUtils
* as:
*
* <p><code>
+ * xxxxx
* xxx-xxxx
* xxx-xxx-xxxx
* 1-xxx-xxx-xxxx
@@ -1003,7 +1022,11 @@ public class PhoneNumberUtils
if (length > "+1-nnn-nnn-nnnn".length()) {
// The string is too long to be formatted
return;
+ } else if (length <= 5) {
+ // The string is either a shortcode or too short to be formatted
+ return;
}
+
CharSequence saved = text.subSequence(0, length);
// Strip the dashes first, as we're going to add them back
@@ -1215,4 +1238,288 @@ public class PhoneNumberUtils
KEYPAD_MAP.put('w', '9'); KEYPAD_MAP.put('x', '9'); KEYPAD_MAP.put('y', '9'); KEYPAD_MAP.put('z', '9');
KEYPAD_MAP.put('W', '9'); KEYPAD_MAP.put('X', '9'); KEYPAD_MAP.put('Y', '9'); KEYPAD_MAP.put('Z', '9');
}
+
+ //================ Plus Code formatting =========================
+ private static final char PLUS_SIGN_CHAR = '+';
+ private static final String PLUS_SIGN_STRING = "+";
+ private static final String NANP_IDP_STRING = "011";
+ private static final int NANP_LENGTH = 10;
+
+ /**
+ * This function checks if there is a plus sign (+) in the passed-in dialing number.
+ * If there is, it processes the plus sign based on the default telephone
+ * numbering plan of the system when the phone is activated and the current
+ * telephone numbering plan of the system that the phone is camped on.
+ * Currently, we only support the case that the default and current telephone
+ * numbering plans are North American Numbering Plan(NANP).
+ *
+ * The passed-in dialStr should only contain the valid format as described below,
+ * 1) the 1st character in the dialStr should be one of the really dialable
+ * characters listed below
+ * ISO-LATIN characters 0-9, *, # , +
+ * 2) the dialStr should already strip out the separator characters,
+ * every character in the dialStr should be one of the non separator characters
+ * listed below
+ * ISO-LATIN characters 0-9, *, # , +, WILD, WAIT, PAUSE
+ *
+ * Otherwise, this function returns the dial string passed in
+ *
+ * @param dialStr the original dial string
+ * @return the converted dial string if the current/default countries belong to NANP,
+ * and if there is the "+" in the original dial string. Otherwise, the original dial
+ * string returns.
+ *
+ * This API is for CDMA only
+ *
+ * @hide TODO: pending API Council approval
+ */
+ public static String cdmaCheckAndProcessPlusCode(String dialStr) {
+ if (!TextUtils.isEmpty(dialStr)) {
+ if (isReallyDialable(dialStr.charAt(0)) &&
+ isNonSeparator(dialStr)) {
+ String currIso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY, "");
+ String defaultIso = SystemProperties.get(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, "");
+ if (!TextUtils.isEmpty(currIso) && !TextUtils.isEmpty(defaultIso)) {
+ return cdmaCheckAndProcessPlusCodeByNumberFormat(dialStr,
+ getFormatTypeFromCountryCode(currIso),
+ getFormatTypeFromCountryCode(defaultIso));
+ }
+ }
+ }
+ return dialStr;
+ }
+
+ /**
+ * This function should be called from checkAndProcessPlusCode only
+ * And it is used for test purpose also.
+ *
+ * It checks the dial string by looping through the network portion,
+ * post dial portion 1, post dial porting 2, etc. If there is any
+ * plus sign, then process the plus sign.
+ * Currently, this function supports the plus sign conversion within NANP only.
+ * Specifically, it handles the plus sign in the following ways:
+ * 1)+1NANP,remove +, e.g.
+ * +18475797000 is converted to 18475797000,
+ * 2)+NANP or +non-NANP Numbers,replace + with the current NANP IDP, e.g,
+ * +8475797000 is converted to 0118475797000,
+ * +11875767800 is converted to 01111875767800
+ * 3)+1NANP in post dial string(s), e.g.
+ * 8475797000;+18475231753 is converted to 8475797000;18475231753
+ *
+ *
+ * @param dialStr the original dial string
+ * @param currFormat the numbering system of the current country that the phone is camped on
+ * @param defaultFormat the numbering system of the country that the phone is activated on
+ * @return the converted dial string if the current/default countries belong to NANP,
+ * and if there is the "+" in the original dial string. Otherwise, the original dial
+ * string returns.
+ *
+ * @hide
+ */
+ public static String
+ cdmaCheckAndProcessPlusCodeByNumberFormat(String dialStr,int currFormat,int defaultFormt) {
+ String retStr = dialStr;
+
+ // Checks if the plus sign character is in the passed-in dial string
+ if (dialStr != null &&
+ dialStr.lastIndexOf(PLUS_SIGN_STRING) != -1) {
+ // Format the string based on the rules for the country the number is from,
+ // and the current country the phone is camped on.
+ if ((currFormat == defaultFormt) && (currFormat == FORMAT_NANP)) {
+ // Handle case where default and current telephone numbering plans are NANP.
+ String postDialStr = null;
+ String tempDialStr = dialStr;
+
+ // Sets the retStr to null since the conversion will be performed below.
+ retStr = null;
+ if (DBG) log("checkAndProcessPlusCode,dialStr=" + dialStr);
+ // This routine is to process the plus sign in the dial string by loop through
+ // the network portion, post dial portion 1, post dial portion 2... etc. if
+ // applied
+ do {
+ String networkDialStr;
+ networkDialStr = extractNetworkPortion(tempDialStr);
+ // Handles the conversion within NANP
+ networkDialStr = processPlusCodeWithinNanp(networkDialStr);
+
+ // Concatenates the string that is converted from network portion
+ if (!TextUtils.isEmpty(networkDialStr)) {
+ if (retStr == null) {
+ retStr = networkDialStr;
+ } else {
+ retStr = retStr.concat(networkDialStr);
+ }
+ } else {
+ // This should never happen since we checked the if dialStr is null
+ // and if it contains the plus sign in the beginning of this function.
+ // The plus sign is part of the network portion.
+ Log.e("checkAndProcessPlusCode: null newDialStr", networkDialStr);
+ return dialStr;
+ }
+ postDialStr = extractPostDialPortion(tempDialStr);
+ if (!TextUtils.isEmpty(postDialStr)) {
+ int dialableIndex = findDialableIndexFromPostDialStr(postDialStr);
+
+ // dialableIndex should always be greater than 0
+ if (dialableIndex >= 1) {
+ retStr = appendPwCharBackToOrigDialStr(dialableIndex,
+ retStr,postDialStr);
+ // Skips the P/W character, extracts the dialable portion
+ tempDialStr = postDialStr.substring(dialableIndex);
+ } else {
+ // Non-dialable character such as P/W should not be at the end of
+ // the dial string after P/W processing in CdmaConnection.java
+ // Set the postDialStr to "" to break out of the loop
+ if (dialableIndex < 0) {
+ postDialStr = "";
+ }
+ Log.e("wrong postDialStr=", postDialStr);
+ }
+ }
+ if (DBG) log("checkAndProcessPlusCode,postDialStr=" + postDialStr);
+ } while (!TextUtils.isEmpty(postDialStr) && !TextUtils.isEmpty(tempDialStr));
+ } else {
+ // TODO: Support NANP international conversion and other telephone numbering plans.
+ // Currently the phone is never used in non-NANP system, so return the original
+ // dial string.
+ Log.e("checkAndProcessPlusCode:non-NANP not supported", dialStr);
+ }
+ }
+ return retStr;
+ }
+
+ // This function gets the default international dialing prefix
+ private static String getDefaultIdp( ) {
+ String ps = null;
+ SystemProperties.get(PROPERTY_IDP_STRING, ps);
+ if (TextUtils.isEmpty(ps)) {
+ ps = NANP_IDP_STRING;
+ }
+ return ps;
+ }
+
+ private static boolean isTwoToNine (char c) {
+ if (c >= '2' && c <= '9') {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static int getFormatTypeFromCountryCode (String country) {
+ // Check for the NANP countries
+ int length = NANP_COUNTRIES.length;
+ for (int i = 0; i < length; i++) {
+ if (NANP_COUNTRIES[i].compareToIgnoreCase(country) == 0) {
+ return FORMAT_NANP;
+ }
+ }
+ if ("jp".compareToIgnoreCase(country) == 0) {
+ return FORMAT_JAPAN;
+ }
+ return FORMAT_UNKNOWN;
+ }
+
+ /**
+ * This function checks if the passed in string conforms to the NANP format
+ * i.e. NXX-NXX-XXXX, N is any digit 2-9 and X is any digit 0-9
+ */
+ private static boolean isNanp (String dialStr) {
+ boolean retVal = false;
+ if (dialStr != null) {
+ if (dialStr.length() == NANP_LENGTH) {
+ if (isTwoToNine(dialStr.charAt(0)) &&
+ isTwoToNine(dialStr.charAt(3))) {
+ retVal = true;
+ for (int i=1; i<NANP_LENGTH; i++ ) {
+ char c=dialStr.charAt(i);
+ if (!PhoneNumberUtils.isISODigit(c)) {
+ retVal = false;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ Log.e("isNanp: null dialStr passed in", dialStr);
+ }
+ return retVal;
+ }
+
+ /**
+ * This function checks if the passed in string conforms to 1-NANP format
+ */
+ private static boolean isOneNanp(String dialStr) {
+ boolean retVal = false;
+ if (dialStr != null) {
+ String newDialStr = dialStr.substring(1);
+ if ((dialStr.charAt(0) == '1') && isNanp(newDialStr)) {
+ retVal = true;
+ }
+ } else {
+ Log.e("isOneNanp: null dialStr passed in", dialStr);
+ }
+ return retVal;
+ }
+
+ /**
+ * This function handles the plus code conversion within NANP CDMA network
+ * If the number format is
+ * 1)+1NANP,remove +,
+ * 2)other than +1NANP, any + numbers,replace + with the current IDP
+ */
+ private static String processPlusCodeWithinNanp(String networkDialStr) {
+ String retStr = networkDialStr;
+
+ if (DBG) log("processPlusCodeWithinNanp,networkDialStr=" + networkDialStr);
+ // If there is a plus sign at the beginning of the dial string,
+ // Convert the plus sign to the default IDP since it's an international number
+ if (networkDialStr != null &
+ networkDialStr.charAt(0) == PLUS_SIGN_CHAR &&
+ networkDialStr.length() > 1) {
+ String newStr = networkDialStr.substring(1);
+ if (isOneNanp(newStr)) {
+ // Remove the leading plus sign
+ retStr = newStr;
+ } else {
+ String idpStr = getDefaultIdp();
+ // Replaces the plus sign with the default IDP
+ retStr = networkDialStr.replaceFirst("[+]", idpStr);
+ }
+ }
+ if (DBG) log("processPlusCodeWithinNanp,retStr=" + retStr);
+ return retStr;
+ }
+
+ // This function finds the index of the dialable character(s)
+ // in the post dial string
+ private static int findDialableIndexFromPostDialStr(String postDialStr) {
+ for (int index = 0;index < postDialStr.length();index++) {
+ char c = postDialStr.charAt(index);
+ if (isReallyDialable(c)) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ // This function appends the non-diablable P/W character to the original
+ // dial string based on the dialable index passed in
+ private static String
+ appendPwCharBackToOrigDialStr(int dialableIndex,String origStr, String dialStr) {
+ String retStr;
+
+ // There is only 1 P/W character before the dialable characters
+ if (dialableIndex == 1) {
+ StringBuilder ret = new StringBuilder(origStr);
+ ret = ret.append(dialStr.charAt(0));
+ retStr = ret.toString();
+ } else {
+ // It means more than 1 P/W characters in the post dial string,
+ // appends to retStr
+ String nonDigitStr = dialStr.substring(0,dialableIndex);
+ retStr = origStr.concat(nonDigitStr);
+ }
+ return retStr;
+ }
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e113680..73e7baa5 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -154,8 +154,9 @@ public class PhoneStateListener {
* @see ServiceState#STATE_IN_SERVICE
* @see ServiceState#STATE_OUT_OF_SERVICE
* @see ServiceState#STATE_POWER_OFF
- * @deprecated, @see #onSignalStrengthsChanged
+ * @deprecated see #onSignalStrengthsChanged
*/
+ @Deprecated
public void onSignalStrengthChanged(int asu) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 50c4d41..06b5c26 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -83,6 +83,12 @@ public class ServiceState implements Parcelable {
public static final int RADIO_TECHNOLOGY_EVDO_0 = 7;
/** @hide */
public static final int RADIO_TECHNOLOGY_EVDO_A = 8;
+ /** @hide */
+ public static final int RADIO_TECHNOLOGY_HSDPA = 9;
+ /** @hide */
+ public static final int RADIO_TECHNOLOGY_HSUPA = 10;
+ /** @hide */
+ public static final int RADIO_TECHNOLOGY_HSPA = 11;
/**
* Available registration states for GSM, UMTS and CDMA.
@@ -366,6 +372,15 @@ public class ServiceState implements Parcelable {
case 8:
radioTechnology = "EvDo rev. A";
break;
+ case 9:
+ radioTechnology = "HSDPA";
+ break;
+ case 10:
+ radioTechnology = "HSUPA";
+ break;
+ case 11:
+ radioTechnology = "HSPA";
+ break;
default:
Log.w(LOG_TAG, "mRadioTechnology variable out of range.");
break;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 82539fb..80de074 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -54,10 +54,13 @@ public final class SmsManager {
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applications,
* which cause smaller number of SMS to be sent in checking period.
@@ -78,9 +81,14 @@ public final class SmsManager {
throw new IllegalArgumentException("Invalid message body");
}
- SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(
- scAddress, destinationAddress, text, (deliveryIntent != null));
- sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
}
/**
@@ -109,10 +117,13 @@ public final class SmsManager {
* <code>PendingIntent</code>s (one for each message part) that is
* broadcast when the corresponding message part has been sent.
* The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applicaitons,
* which cause smaller number of SMS to be sent in checking period.
@@ -169,10 +180,13 @@ public final class SmsManager {
* @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
* The per-application based SMS control checks sentIntent. If sentIntent
* is NULL the caller will be checked against all unknown applicaitons,
* which cause smaller number of SMS to be sent in checking period.
@@ -193,40 +207,11 @@ public final class SmsManager {
throw new IllegalArgumentException("Invalid message data");
}
- SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(
- scAddress, destinationAddress,
- destinationPort, data, (deliveryIntent != null));
- sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent);
- }
-
- /**
- * Send a raw SMS PDU.
- * A PDU is a protocol data unit. It contains the message and the
- * associated meta information.
- *
- * @param smsc the SMSC to send the message through, or NULL for the
- * default SMSC
- * @param pdu the raw PDU to send
- * @param sentIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is successfully sent, or failed.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
- * The per-application based SMS control checks sentIntent. If sentIntent
- * is NULL the caller will be checked against all unknown applications,
- * which cause smaller number of SMS to be sent in checking period.
- * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
- * broadcast when the message is delivered to the recipient. The
- * raw pdu of the status report is in the extended data ("pdu").
- */
- private void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
- iccISms.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent);
+ iccISms.sendData(destinationAddress, scAddress, destinationPort & 0xFFFF,
+ data, sentIntent, deliveryIntent);
}
} catch (RemoteException ex) {
// ignore it
@@ -409,4 +394,6 @@ public final class SmsManager {
static public final int RESULT_ERROR_NULL_PDU = 3;
/** Failed because service is currently unavailable */
static public final int RESULT_ERROR_NO_SERVICE = 4;
+ /** Failed because we reached the sending queue limit. {@hide} */
+ static public final int RESULT_ERROR_LIMIT_EXCEEDED = 5;
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 0617dad..7a10512 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -309,8 +309,13 @@ public class SmsMessage {
while (pos < textLen) {
int nextPos = 0; // Counts code units.
if (ted.codeUnitSize == ENCODING_7BIT) {
- // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
- nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
+ if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
+ // For a singleton CDMA message, the encoding must be ASCII...
+ nextPos = pos + Math.min(limit, textLen - pos);
+ } else {
+ // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
+ nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit);
+ }
} else { // Assume unicode.
nextPos = pos + Math.min(limit / 2, textLen - pos);
}
@@ -345,6 +350,25 @@ public class SmsMessage {
return calculateLength((CharSequence)messageBody, use7bitOnly);
}
+ /*
+ * TODO(cleanup): It looks like there is now no useful reason why
+ * apps should generate pdus themselves using these routines,
+ * instead of handing the raw data to SMSDispatcher (and thereby
+ * have the phone process do the encoding). Moreover, CDMA now
+ * has shared state (in the form of the msgId system property)
+ * which can only be modified by the phone process, and hence
+ * makes the output of these routines incorrect. Since they now
+ * serve no purpose, they should probably just return null
+ * directly, and be deprecated. Going further in that direction,
+ * the above parsers of serialized pdu data should probably also
+ * be gotten rid of, hiding all but the necessarily visible
+ * structured data from client apps. A possible concern with
+ * doing this is that apps may be using these routines to generate
+ * pdus that are then sent elsewhere, some network server, for
+ * example, and that always returning null would thereby break
+ * otherwise useful apps.
+ */
+
/**
* Get an SMS-SUBMIT PDU for a destination address and a message
*
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ed9af66..a744486 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -382,6 +382,15 @@ public class TelephonyManager {
/** Current network is 1xRTT*/
/** @hide */
public static final int NETWORK_TYPE_1xRTT = 7;
+ /** Current network is HSDPA */
+ /** @hide */
+ public static final int NETWORK_TYPE_HSDPA = 8;
+ /** Current network is HSUPA */
+ /** @hide */
+ public static final int NETWORK_TYPE_HSUPA = 9;
+ /** Current network is HSPA */
+ /** @hide */
+ public static final int NETWORK_TYPE_HSPA = 10;
/**
* Returns a constant indicating the radio technology (network type)
@@ -392,35 +401,25 @@ public class TelephonyManager {
* @see #NETWORK_TYPE_GPRS
* @see #NETWORK_TYPE_EDGE
* @see #NETWORK_TYPE_UMTS
+ * @see #NETWORK_TYPE_HSDPA
+ * @see #NETWORK_TYPE_HSUPA
+ * @see #NETWORK_TYPE_HSPA
* @see #NETWORK_TYPE_CDMA
* @see #NETWORK_TYPE_EVDO_0
* @see #NETWORK_TYPE_EVDO_A
* @see #NETWORK_TYPE_1xRTT
*/
public int getNetworkType() {
- String prop = SystemProperties.get(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE);
- if ("GPRS".equals(prop)) {
- return NETWORK_TYPE_GPRS;
- }
- else if ("EDGE".equals(prop)) {
- return NETWORK_TYPE_EDGE;
- }
- else if ("UMTS".equals(prop)) {
- return NETWORK_TYPE_UMTS;
- }
- else if ("CDMA".equals(prop)) {
- return NETWORK_TYPE_CDMA;
- }
- else if ("CDMA - EvDo rev. 0".equals(prop)) {
- return NETWORK_TYPE_EVDO_0;
- }
- else if ("CDMA - EvDo rev. A".equals(prop)) {
- return NETWORK_TYPE_EVDO_A;
- }
- else if ("CDMA - 1xRTT".equals(prop)) {
- return NETWORK_TYPE_1xRTT;
+ try{
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.getNetworkType();
+ } else {
+ // This can happen when the ITelephony interface is not up yet.
+ return NETWORK_TYPE_UNKNOWN;
}
- else {
+ } catch(RemoteException ex){
+ // This shouldn't happen in the normal case
return NETWORK_TYPE_UNKNOWN;
}
}
@@ -440,6 +439,12 @@ public class TelephonyManager {
return "EDGE";
case NETWORK_TYPE_UMTS:
return "UMTS";
+ case NETWORK_TYPE_HSDPA:
+ return "HSDPA";
+ case NETWORK_TYPE_HSUPA:
+ return "HSUPA";
+ case NETWORK_TYPE_HSPA:
+ return "HSPA";
case NETWORK_TYPE_CDMA:
return "CDMA";
case NETWORK_TYPE_EVDO_0:
@@ -476,6 +481,18 @@ public class TelephonyManager {
public static final int SIM_STATE_READY = 5;
/**
+ * @return true if a ICC card is present
+ */
+ public boolean hasIccCard() {
+ try {
+ return getITelephony().hasIccCard();
+ } catch (RemoteException ex) {
+ // Assume no ICC card if remote exception which shouldn't happen
+ return false;
+ }
+ }
+
+ /**
* Returns a constant indicating the state of the
* device SIM card.
*
diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java
index 84dfca0..37ef912 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -345,6 +345,7 @@ public class SmsMessage {
* the number of code units used, and int[2] is the number of code
* units remaining until the next message. int[3] is the encoding
* type that should be used for the message.
+ * @deprecated Use android.telephony.SmsMessage.
*/
@Deprecated
public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.java b/telephony/java/com/android/internal/telephony/AdnRecord.java
index 5f40579..0896ba6 100644
--- a/telephony/java/com/android/internal/telephony/AdnRecord.java
+++ b/telephony/java/com/android/internal/telephony/AdnRecord.java
@@ -23,6 +23,8 @@ import android.util.Log;
import com.android.internal.telephony.GsmAlphabet;
+import java.util.Arrays;
+
/**
*
@@ -38,6 +40,7 @@ public class AdnRecord implements Parcelable {
String alphaTag = "";
String number = "";
+ String[] emails;
int extRecord = 0xff;
int efid; // or 0 if none
int recordNumber; // or 0 if none
@@ -74,13 +77,15 @@ public class AdnRecord implements Parcelable {
int recordNumber;
String alphaTag;
String number;
+ String[] emails;
efid = source.readInt();
recordNumber = source.readInt();
alphaTag = source.readString();
number = source.readString();
+ emails = source.readStringArray();
- return new AdnRecord(efid, recordNumber, alphaTag, number);
+ return new AdnRecord(efid, recordNumber, alphaTag, number, emails);
}
public AdnRecord[] newArray(int size) {
@@ -90,29 +95,38 @@ public class AdnRecord implements Parcelable {
//***** Constructor
- public
- AdnRecord (byte[] record) {
+ public AdnRecord (byte[] record) {
this(0, 0, record);
}
- public
- AdnRecord (int efid, int recordNumber, byte[] record) {
+ public AdnRecord (int efid, int recordNumber, byte[] record) {
this.efid = efid;
this.recordNumber = recordNumber;
parseRecord(record);
}
- public
- AdnRecord (String alphaTag, String number) {
+ public AdnRecord (String alphaTag, String number) {
this(0, 0, alphaTag, number);
}
- public
- AdnRecord (int efid, int recordNumber, String alphaTag, String number) {
+ public AdnRecord (String alphaTag, String number, String[] emails) {
+ this(0, 0, alphaTag, number, emails);
+ }
+
+ public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
+ this.efid = efid;
+ this.recordNumber = recordNumber;
+ this.alphaTag = alphaTag;
+ this.number = number;
+ this.emails = emails;
+ }
+
+ public AdnRecord(int efid, int recordNumber, String alphaTag, String number) {
this.efid = efid;
this.recordNumber = recordNumber;
this.alphaTag = alphaTag;
this.number = number;
+ this.emails = null;
}
//***** Instance Methods
@@ -125,12 +139,20 @@ public class AdnRecord implements Parcelable {
return number;
}
+ public String[] getEmails() {
+ return emails;
+ }
+
+ public void setEmails(String[] emails) {
+ this.emails = emails;
+ }
+
public String toString() {
- return "ADN Record '" + alphaTag + "' '" + number + "'";
+ return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'";
}
public boolean isEmpty() {
- return alphaTag.equals("") && number.equals("");
+ return alphaTag.equals("") && number.equals("") && emails == null;
}
public boolean hasExtendedRecord() {
@@ -139,7 +161,8 @@ public class AdnRecord implements Parcelable {
public boolean isEqual(AdnRecord adn) {
return ( alphaTag.equals(adn.getAlphaTag()) &&
- number.equals(adn.getNumber()) );
+ number.equals(adn.getNumber()) &&
+ Arrays.equals(emails, adn.getEmails()));
}
//***** Parcelable Implementation
@@ -152,6 +175,7 @@ public class AdnRecord implements Parcelable {
dest.writeInt(recordNumber);
dest.writeString(alphaTag);
dest.writeString(number);
+ dest.writeStringArray(emails);
}
/**
@@ -274,10 +298,13 @@ public class AdnRecord implements Parcelable {
extRecord = 0xff & record[record.length - 1];
+ emails = null;
+
} catch (RuntimeException ex) {
Log.w(LOG_TAG, "Error parsing AdnRecord", ex);
number = "";
alphaTag = "";
+ emails = null;
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java
index c270ae5..c8c0658 100644
--- a/telephony/java/com/android/internal/telephony/AdnRecordCache.java
+++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java
@@ -16,14 +16,16 @@
package com.android.internal.telephony;
-import android.util.SparseArray;
-import android.util.Log;
-import android.os.Message;
-import android.os.Handler;
import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.telephony.gsm.UsimPhoneBookManager;
+
import java.util.ArrayList;
import java.util.Iterator;
-import com.android.internal.telephony.IccConstants;
/**
* {@hide}
@@ -32,6 +34,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
//***** Instance Variables
PhoneBase phone;
+ private UsimPhoneBookManager mUsimPhoneBookManager;
// Indexed by EF ID
SparseArray<ArrayList<AdnRecord>> adnLikeFiles
@@ -55,6 +58,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
public AdnRecordCache(PhoneBase phone) {
this.phone = phone;
+ mUsimPhoneBookManager = new UsimPhoneBookManager(phone, this);
}
//***** Called from SIMRecords
@@ -64,6 +68,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
*/
public void reset() {
adnLikeFiles.clear();
+ mUsimPhoneBookManager.reset();
clearWaiters();
clearUserWriters();
@@ -103,14 +108,14 @@ public final class AdnRecordCache extends Handler implements IccConstants {
*
* See 3GPP TS 51.011 for this mapping
*/
- private int
- extensionEfForEf(int efid) {
+ int extensionEfForEf(int efid) {
switch (efid) {
case EF_MBDN: return EF_EXT6;
case EF_ADN: return EF_EXT1;
case EF_SDN: return EF_EXT3;
case EF_FDN: return EF_EXT2;
case EF_MSISDN: return EF_EXT1;
+ case EF_PBR: return 0; // The EF PBR doesn't have an extension record
default: return -1;
}
}
@@ -223,11 +228,15 @@ public final class AdnRecordCache extends Handler implements IccConstants {
* record
*/
public void
- requestLoadAllAdnLike (int efid, Message response) {
+ requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
ArrayList<Message> waiters;
ArrayList<AdnRecord> result;
- result = getRecordsIfLoaded(efid);
+ if (efid == EF_PBR) {
+ result = mUsimPhoneBookManager.loadEfFilesFromUsim();
+ } else {
+ result = getRecordsIfLoaded(efid);
+ }
// Have we already loaded this efid?
if (result != null) {
@@ -258,9 +267,8 @@ public final class AdnRecordCache extends Handler implements IccConstants {
adnLikeWaiters.put(efid, waiters);
- int extensionEF = extensionEfForEf(efid);
- if (extensionEF < 0) {
+ if (extensionEf < 0) {
// respond with error if not known ADN-like record
if (response != null) {
@@ -272,7 +280,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
return;
}
- new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEF,
+ new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEf,
obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
}
@@ -311,7 +319,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
adnLikeWaiters.delete(efid);
if (ar.exception == null) {
- adnLikeFiles.put(efid, (ArrayList<AdnRecord>) (ar.result));
+ adnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
}
notifyWaiters(waiters, ar);
break;
diff --git a/telephony/java/com/android/internal/telephony/Call.java b/telephony/java/com/android/internal/telephony/Call.java
index 7eb9d85..b95dd11 100644
--- a/telephony/java/com/android/internal/telephony/Call.java
+++ b/telephony/java/com/android/internal/telephony/Call.java
@@ -25,10 +25,10 @@ public abstract class Call {
/* Enums */
public enum State {
- IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED;
+ IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
public boolean isAlive() {
- return !(this == IDLE || this == DISCONNECTED);
+ return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
}
public boolean isRinging() {
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index afc8b62..e1bd1db 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -20,9 +20,8 @@ import android.content.Context;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.provider.Contacts;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.text.TextUtils;
import android.telephony.TelephonyManager;
import android.telephony.PhoneNumberUtils;
@@ -134,44 +133,39 @@ public class CallerInfo {
int columnIndex;
// Look for the name
- columnIndex = cursor.getColumnIndex(People.NAME);
+ columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
if (columnIndex != -1) {
info.name = cursor.getString(columnIndex);
}
// Look for the number
- columnIndex = cursor.getColumnIndex(Phones.NUMBER);
+ columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
if (columnIndex != -1) {
info.phoneNumber = cursor.getString(columnIndex);
}
// Look for the label/type combo
- columnIndex = cursor.getColumnIndex(Phones.LABEL);
+ columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
if (columnIndex != -1) {
- int typeColumnIndex = cursor.getColumnIndex(Phones.TYPE);
+ int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
if (typeColumnIndex != -1) {
info.numberType = cursor.getInt(typeColumnIndex);
info.numberLabel = cursor.getString(columnIndex);
- info.phoneLabel = Contacts.Phones.getDisplayLabel(context,
+ info.phoneLabel = Phone.getDisplayLabel(context,
info.numberType, info.numberLabel)
.toString();
}
}
// Look for the person ID
- columnIndex = cursor.getColumnIndex(Phones.PERSON_ID);
+ columnIndex = cursor.getColumnIndex(PhoneLookup._ID);
if (columnIndex != -1) {
info.person_id = cursor.getLong(columnIndex);
- } else {
- columnIndex = cursor.getColumnIndex(People._ID);
- if (columnIndex != -1) {
- info.person_id = cursor.getLong(columnIndex);
- }
}
// look for the custom ringtone, create from the string stored
// in the database.
- columnIndex = cursor.getColumnIndex(People.CUSTOM_RINGTONE);
+ columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
} else {
@@ -180,7 +174,7 @@ public class CallerInfo {
// look for the send to voicemail flag, set it to true only
// under certain circumstances.
- columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL);
+ columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
info.shouldSendToVoicemail = (columnIndex != -1) &&
((cursor.getInt(columnIndex)) == 1);
info.contactExists = true;
@@ -256,8 +250,7 @@ public class CallerInfo {
}
}
- Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL,
- Uri.encode(number));
+ Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
CallerInfo info = getCallerInfo(context, contactUri);
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index f81f42a..3d4f78c 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -24,7 +24,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.provider.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -303,7 +303,7 @@ public class CallerInfoAsyncQuery {
public static CallerInfoAsyncQuery startQuery(int token, Context context, String number,
OnQueryCompleteListener listener, Object cookie) {
//contruct the URI object and start Query.
- Uri contactRef = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, number);
+ Uri contactRef = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
CallerInfoAsyncQuery c = new CallerInfoAsyncQuery();
c.allocate(context, contactRef);
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index e583110..63bdc2c 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -82,15 +82,6 @@ public interface CommandsInterface {
}
}
- enum IccStatus {
- ICC_ABSENT,
- ICC_NOT_READY,
- ICC_READY,
- ICC_PIN,
- ICC_PUK,
- ICC_NETWORK_PERSONALIZATION
- }
-
//***** Constants
// Used as parameter to dial() and setCLIR() below
@@ -153,7 +144,8 @@ public interface CommandsInterface {
static final int GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED = 0xD3;
static final int GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR = 0xFF;
- // CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S00005, 6.5.2.125.
+ // CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S0005, 6.5.2.125.
+ static final int CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID = 4;
static final int CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE = 35;
static final int CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM = 39;
@@ -534,15 +526,6 @@ public interface CommandsInterface {
void unregisterForCdmaOtaProvision(Handler h);
/**
- * Returns current ICC status.
- *
- * AsyncResult.result is IccStatus
- *
- */
-
- void getIccStatus(Message result);
-
- /**
* Supply the ICC PIN to the ICC card
*
* returned message
@@ -625,8 +608,9 @@ public interface CommandsInterface {
* ar.exception carries exception on failure
* ar.userObject contains the orignal value of result.obj
* ar.result contains a List of DataCallState
- * @deprecated
+ * @deprecated Do not use.
*/
+ @Deprecated
void getPDPContextList(Message result);
/**
@@ -797,8 +781,9 @@ public interface CommandsInterface {
* cause code returned as int[0] in Message.obj.response
* returns an integer cause code defined in TS 24.008
* section 6.1.3.1.3 or close approximation
- * @deprecated
+ * @deprecated Do not use.
*/
+ @Deprecated
void getLastPdpFailCause (Message result);
/**
@@ -1243,8 +1228,10 @@ public interface CommandsInterface {
* Request the device MDN / H_SID / H_NID / MIN.
* "response" is const char **
* [0] is MDN if CDMA subscription is available
- * [1] is H_SID (Home SID) if CDMA subscription is available
- * [2] is H_NID (Home NID) if CDMA subscription is available
+ * [1] is a comma separated list of H_SID (Home SID) in decimal format
+ * if CDMA subscription is available
+ * [2] is a comma separated list of H_NID (Home NID) in decimal format
+ * if CDMA subscription is available
* [3] is MIN (10 digits, MIN2+MIN1) if CDMA subscription is available
*/
public void getCDMASubscription(Message response);
@@ -1316,11 +1303,13 @@ public interface CommandsInterface {
* the username for APN, or NULL
* @param password
* the password for APN, or NULL
+ * @param authType
+ * the PAP / CHAP auth type. Values is one of SETUP_DATA_AUTH_*
* @param result
* Callback message
*/
public void setupDataCall(String radioTechnology, String profile, String apn,
- String user, String password, Message result);
+ String user, String password, String authType, Message result);
/**
* Deactivate packet data connection
@@ -1366,4 +1355,12 @@ public interface CommandsInterface {
* @param response callback message
*/
public void exitEmergencyCallbackMode(Message response);
+
+ /**
+ * Request the status of the ICC and UICC cards.
+ *
+ * @param response
+ * Callback message containing {@link IccCardStatus} structure for the card.
+ */
+ public void getIccCardStatus(Message result);
}
diff --git a/telephony/java/com/android/internal/telephony/Connection.java b/telephony/java/com/android/internal/telephony/Connection.java
index 92f6cb8..e6fd0a0 100644
--- a/telephony/java/com/android/internal/telephony/Connection.java
+++ b/telephony/java/com/android/internal/telephony/Connection.java
@@ -56,7 +56,8 @@ public abstract class Connection {
CDMA_RETRY_ORDER, /* requeseted service is rejected, retry delay is set */
CDMA_ACCESS_FAILURE,
CDMA_PREEMPTED,
- CDMA_NOT_EMERGENCY /* not an emergency call */
+ CDMA_NOT_EMERGENCY, /* not an emergency call */
+ ERROR_UNSPECIFIED
}
Object userData;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index c074cb8..ece708a 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -24,14 +24,18 @@ import android.os.Message;
import android.os.RemoteException;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
+import android.text.TextUtils;
import android.util.Log;
+import java.util.ArrayList;
+
/**
* {@hide}
*
*/
public abstract class DataConnectionTracker extends Handler {
- private static final boolean DBG = true;
+ protected static final boolean DBG = true;
+ protected final String LOG_TAG = "DataConnectionTracker";
/**
* IDLE: ready to start data connection setup, default state
@@ -67,7 +71,7 @@ public abstract class DataConnectionTracker extends Handler {
DORMANT
}
- //***** Event Codes
+ /***** Event Codes *****/
protected static final int EVENT_DATA_SETUP_COMPLETE = 1;
protected static final int EVENT_RADIO_AVAILABLE = 3;
protected static final int EVENT_RECORDS_LOADED = 4;
@@ -94,12 +98,33 @@ public abstract class DataConnectionTracker extends Handler {
protected static final int EVENT_PS_RESTRICT_ENABLED = 32;
protected static final int EVENT_PS_RESTRICT_DISABLED = 33;
public static final int EVENT_CLEAN_UP_CONNECTION = 34;
+ protected static final int EVENT_CDMA_OTA_PROVISION = 35;
+ protected static final int EVENT_RESTART_RADIO = 36;
+ private static final int EVENT_ENABLE_APN_REQUEST = 37;
+
+ /***** Constants *****/
+
+ protected static final int APN_INVALID_ID = -1;
+ protected static final int APN_DEFAULT_ID = 0;
+ protected static final int APN_MMS_ID = 1;
+ protected static final int APN_SUPL_ID = 2;
+ protected static final int APN_DUN_ID = 3;
+ protected static final int APN_HIPRI_ID = 4;
+ protected static final int APN_NUM_TYPES = 5;
+
+ protected static final int APN_DISABLED = 0;
+ protected static final int APN_ENABLED = 1;
+
+ protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+ protected int enabledCount = 0;
- //***** Constants
- protected static final int RECONNECT_DELAY_INITIAL_MILLIS = 5 * 1000;
+ /* Currently requested APN type */
+ protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
- /** Cap out with 30 min retry interval. */
- protected static final int RECONNECT_DELAY_MAX_MILLIS = 30 * 60 * 1000;
+ /** Retry configuration: A doubling of retry times from 5secs to 30minutes */
+ protected static final String DEFAULT_DATA_RETRY_CONFIG = "default_randomization=2000,"
+ + "5000,10000,20000,40000,80000:5000,160000:5000,"
+ + "320000:5000,640000:5000,1280000:5000,1800000:5000";
/** Slow poll when attempting connection recovery. */
protected static final int POLL_NETSTAT_SLOW_MILLIS = 5000;
@@ -145,6 +170,9 @@ public abstract class DataConnectionTracker extends Handler {
protected int mNoRecvPollCount = 0;
protected boolean netStatPollEnabled = false;
+ /** Manage the behavior of data retry after failure */
+ protected final RetryManager mRetryMgr = new RetryManager();
+
// wifi connection status will be updated by sticky intent
protected boolean mIsWifiConnected = false;
@@ -162,6 +190,8 @@ public abstract class DataConnectionTracker extends Handler {
this.phone = phone;
}
+ public abstract void dispose();
+
public Activity getActivity() {
return activity;
}
@@ -201,10 +231,13 @@ public abstract class DataConnectionTracker extends Handler {
if (getDataOnRoamingEnabled() != enabled) {
Settings.Secure.putInt(phone.getContext().getContentResolver(),
Settings.Secure.DATA_ROAMING, enabled ? 1 : 0);
+ if (phone.getServiceState().getRoaming()) {
+ if (enabled) {
+ mRetryMgr.resetRetryCount();
+ }
+ sendMessage(obtainMessage(EVENT_ROAMING_ON));
+ }
}
- Message roamingMsg = phone.getServiceState().getRoaming() ?
- obtainMessage(EVENT_ROAMING_ON) : obtainMessage(EVENT_ROAMING_OFF);
- sendMessage(roamingMsg);
}
//Retrieve the data roaming setting from the shared preferences.
@@ -218,7 +251,7 @@ public abstract class DataConnectionTracker extends Handler {
}
// abstract handler methods
- protected abstract void onTrySetupData(String reason);
+ protected abstract boolean onTrySetupData(String reason);
protected abstract void onRoamingOff();
protected abstract void onRoamingOn();
protected abstract void onRadioAvailable();
@@ -229,10 +262,39 @@ public abstract class DataConnectionTracker extends Handler {
protected abstract void onVoiceCallEnded();
protected abstract void onCleanUpConnection(boolean tearDown, String reason);
- //***** Overridden from Handler
+ @Override
public void handleMessage (Message msg) {
switch (msg.what) {
+ case EVENT_ENABLE_APN_REQUEST:
+ int apnId = msg.arg1;
+ synchronized (this) {
+ if (DBG) {
+ Log.d(LOG_TAG, "got EVENT_ENABLE_APN_REQUEST with apnType = " + apnId +
+ " and enable = " + msg.arg2);
+ Log.d(LOG_TAG, "dataEnabled[apnId] = " + dataEnabled[apnId] +
+ ", enabledCount = " + enabledCount);
+ }
+ if (msg.arg2 == APN_ENABLED) {
+ // enable
+ if (!dataEnabled[apnId]) {
+ dataEnabled[apnId] = true;
+ enabledCount++;
+ }
+ onTrySetupData(null);
+ } else {
+ // disable
+ if (dataEnabled[apnId]) {
+ dataEnabled[apnId] = false;
+ enabledCount--;
+ if (enabledCount == 0) {
+ onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+ }
+ }
+ }
+ }
+ break;
+
case EVENT_TRY_SETUP_DATA:
String reason = null;
if (msg.obj instanceof String) {
@@ -242,6 +304,9 @@ public abstract class DataConnectionTracker extends Handler {
break;
case EVENT_ROAMING_OFF:
+ if (getDataOnRoamingEnabled() == false) {
+ mRetryMgr.resetRetryCount();
+ }
onRoamingOff();
break;
@@ -290,30 +355,161 @@ public abstract class DataConnectionTracker extends Handler {
* @return {@code false} if data connectivity has been explicitly disabled,
* {@code true} otherwise.
*/
- public abstract boolean getDataEnabled();
+ public synchronized boolean getDataEnabled() {
+ return dataEnabled[APN_DEFAULT_ID];
+ }
/**
* Report on whether data connectivity is enabled
* @return {@code false} if data connectivity has been explicitly disabled,
* {@code true} otherwise.
*/
- public abstract boolean getAnyDataEnabled();
+ public boolean getAnyDataEnabled() {
+ return (enabledCount != 0);
+ }
+
+ protected abstract void startNetStatPoll();
+
+ protected abstract void stopNetStatPoll();
+
+ protected abstract void restartRadio();
+
+ protected abstract void log(String s);
+
+ protected int apnTypeToId(String type) {
+ if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) {
+ return APN_DEFAULT_ID;
+ } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
+ return APN_MMS_ID;
+ } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
+ return APN_SUPL_ID;
+ } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) {
+ return APN_DUN_ID;
+ } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) {
+ return APN_HIPRI_ID;
+ } else {
+ return APN_INVALID_ID;
+ }
+ }
+
+ protected abstract boolean isApnTypeActive(String type);
+
+ protected abstract boolean isApnTypeAvailable(String type);
+
+ protected abstract String[] getActiveApnTypes();
+
+ protected abstract String getActiveApnString();
+
+ public abstract ArrayList<DataConnection> getAllDataConnections();
+
+ protected abstract String getInterfaceName(String apnType);
+
+ protected abstract String getIpAddress(String apnType);
+
+ protected abstract String getGateway(String apnType);
+
+ protected abstract String[] getDnsServers(String apnType);
+
+ protected abstract void setState(State s);
+
+ protected synchronized boolean isEnabled(int id) {
+ if (id != APN_INVALID_ID) {
+ return dataEnabled[id];
+ }
+ return false;
+ }
+
+ /**
+ * Ensure that we are connected to an APN of the specified type.
+ * @param type the APN type (currently the only valid values
+ * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
+ * @return the result of the operation. Success is indicated by
+ * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or
+ * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast
+ * will be sent by the ConnectivityManager when a connection to
+ * the APN has been established.
+ */
+ public int enableApnType(String type) {
+ int id = apnTypeToId(type);
+ if (id == APN_INVALID_ID) {
+ return Phone.APN_REQUEST_FAILED;
+ }
+
+ // If already active, return
+ if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = "
+ + isApnTypeActive(type) + " and state = " + state);
+
+ if (isApnTypeActive(type)) {
+ if (state == State.INITING) return Phone.APN_REQUEST_STARTED;
+ else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE;
+ }
+
+ if (!isApnTypeAvailable(type)) {
+ return Phone.APN_TYPE_NOT_AVAILABLE;
+ }
+
+ setEnabled(id, true);
+ mRequestedApnType = type;
+ sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN));
+ return Phone.APN_REQUEST_STARTED;
+ }
+
+ /**
+ * The APN of the specified type is no longer needed. Ensure that if
+ * use of the default APN has not been explicitly disabled, we are connected
+ * to the default APN.
+ * @param type the APN type. The only valid values are currently
+ * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
+ * @return
+ */
+ public synchronized int disableApnType(String type) {
+ if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")");
+ int id = apnTypeToId(type);
+ if (id == APN_INVALID_ID) {
+ return Phone.APN_REQUEST_FAILED;
+ }
+ if (isEnabled(id)) {
+ setEnabled(id, false);
+ if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
+ mRequestedApnType = Phone.APN_TYPE_DEFAULT;
+ if (dataEnabled[APN_DEFAULT_ID]) {
+ return Phone.APN_ALREADY_ACTIVE;
+ } else {
+ return Phone.APN_REQUEST_STARTED;
+ }
+ } else {
+ return Phone.APN_REQUEST_STARTED;
+ }
+ } else {
+ return Phone.APN_REQUEST_FAILED;
+ }
+ }
+
+ protected void setEnabled(int id, boolean enable) {
+ if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ") with old state = " +
+ dataEnabled[id] + " and enabledCount = " + enabledCount);
+
+ Message msg = obtainMessage(EVENT_ENABLE_APN_REQUEST);
+ msg.arg1 = id;
+ msg.arg2 = (enable ? APN_ENABLED : APN_DISABLED);
+ sendMessage(msg);
+ }
/**
* Prevent mobile data connections from being established,
* or once again allow mobile data connections. If the state
* toggles, then either tear down or set up data, as
* appropriate to match the new state.
+ * <p>This operation only affects the default APN, and if the same APN is
+ * currently being used for MMS traffic, the teardown will not happen
+ * even when {@code enable} is {@code false}.</p>
* @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data
* @return {@code true} if the operation succeeded
*/
- public abstract boolean setDataEnabled(boolean enable);
-
- protected abstract void startNetStatPoll();
-
- protected abstract void stopNetStatPoll();
-
- protected abstract void restartRadio();
+ public boolean setDataEnabled(boolean enable) {
+ if (DBG) Log.d(LOG_TAG, "setDataEnabled(" + enable + ")");
+ setEnabled(APN_DEFAULT_ID, enable);
+ return true;
+ }
- protected abstract void log(String s);
}
diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index d6151c6..00d7c72 100644
--- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -94,8 +94,11 @@ public class DefaultPhoneNotifier implements PhoneNotifier {
public void notifyDataConnection(Phone sender, String reason) {
try {
- mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()),
- sender.isDataConnectivityPossible(), reason, sender.getActiveApn(),
+ mRegistry.notifyDataConnection(
+ convertDataState(sender.getDataConnectionState()),
+ sender.isDataConnectivityPossible(), reason,
+ sender.getActiveApn(),
+ sender.getActiveApnTypes(),
sender.getInterfaceName(null));
} catch (RemoteException ex) {
// system process is dead
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index e8095e1..461b694 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -183,15 +183,9 @@ public class GsmAlphabet {
}
int headerBits = (header.length + 1) * 8;
- int headerSeptets = headerBits / 7;
- headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
+ int headerSeptets = (headerBits + 6) / 7;
- int sz = data.length();
- int septetCount;
- septetCount = countGsmSeptets(data, true) + headerSeptets;
-
- byte[] ret = stringToGsm7BitPacked(data, 0, septetCount,
- (headerSeptets*7), true);
+ byte[] ret = stringToGsm7BitPacked(data, headerSeptets, true);
// Paste in the header
ret[1] = (byte)header.length;
@@ -215,7 +209,7 @@ public class GsmAlphabet {
*/
public static byte[] stringToGsm7BitPacked(String data)
throws EncodeException {
- return stringToGsm7BitPacked(data, 0, -1, 0, true);
+ return stringToGsm7BitPacked(data, 0, true);
}
/**
@@ -228,58 +222,37 @@ public class GsmAlphabet {
* septets.
*
* @param data the text to convert to septets
- * @param dataOffset the character offset in data to start the encoding from
- * @param maxSeptets the maximum number of septets to convert, or -1 for no
- * enforced maximum.
- * @param startingBitOffset the number of padding bits to put before
- * the start of the first septet at the begining of the array
+ * @param startingSeptetOffset the number of padding septets to put before
+ * the character data at the begining of the array
* @param throwException If true, throws EncodeException on invalid char.
* If false, replaces unencodable char with GSM alphabet space char.
*
* @throws EncodeException if String is too large to encode
*/
- public static byte[] stringToGsm7BitPacked(String data, int dataOffset,
- int maxSeptets, int startingBitOffset, boolean throwException)
- throws EncodeException {
-
- int sz = data.length();
- int septetCount;
- if (maxSeptets == -1) {
- septetCount = countGsmSeptets(data, true);
- } else {
- septetCount = maxSeptets;
+ public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset,
+ boolean throwException) throws EncodeException {
+ int dataLen = data.length();
+ int septetCount = countGsmSeptets(data, throwException) + startingSeptetOffset;
+ if (septetCount > 255) {
+ throw new EncodeException("Payload cannot exceed 255 septets");
}
-
- if(septetCount > 0xff) {
- throw new EncodeException("Payload cannot exceed " + Short.MAX_VALUE
- + " septets");
- }
-
- // Enough for all the septets and the length 2 byte prefix
- byte[] ret = new byte[1 + (((septetCount * 7) + 7) / 8)];
-
- int bitOffset = startingBitOffset;
- int septets = startingBitOffset/7;
- for (int i = dataOffset; i < sz && septets < septetCount; i++, bitOffset += 7) {
+ int byteCount = ((septetCount * 7) + 7) / 8;
+ byte[] ret = new byte[byteCount + 1]; // Include space for one byte length prefix.
+ for (int i = 0, septets = startingSeptetOffset, bitOffset = startingSeptetOffset * 7;
+ i < dataLen && septets < septetCount;
+ i++, bitOffset += 7) {
char c = data.charAt(i);
-
int v = GsmAlphabet.charToGsm(c, throwException);
if (v == GSM_EXTENDED_ESCAPE) {
- // Lookup the extended char
- v = GsmAlphabet.charToGsmExtended(c);
-
+ v = GsmAlphabet.charToGsmExtended(c); // Lookup the extended char.
packSmsChar(ret, bitOffset, GSM_EXTENDED_ESCAPE);
bitOffset += 7;
septets++;
}
-
packSmsChar(ret, bitOffset, v);
septets++;
}
-
- // See check for > 0xff above
- ret[0] = (byte)septets;
-
+ ret[0] = (byte) (septetCount); // Validated by check above.
return ret;
}
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 257f1e6..65bad96 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -67,24 +67,56 @@ interface ISms {
boolean copyMessageToIccEf(int status, in byte[] pdu, in byte[] smsc);
/**
- * Send a SMS
+ * Send a data SMS.
*
* @param smsc the SMSC to send the message through, or NULL for the
* default SMSC
- * @param pdu the raw PDU to send
- * @param sentIntent if not NULL this <code>Intent</code> is
- * broadcast when the message is successfully sent, or failed.
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
- * @param deliveryIntent if not NULL this <code>Intent</code> is
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/
- void sendRawPdu(in byte[] smsc, in byte[] pdu, in PendingIntent sentIntent,
- in PendingIntent deliveryIntent);
+ void sendData(in String destAddr, in String scAddr, in int destPort,
+ in byte[] data, in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+
+ /**
+ * Send an SMS.
+ *
+ * @param smsc the SMSC to send the message through, or NULL for the
+ * default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ void sendText(in String destAddr, in String scAddr, in String text,
+ in PendingIntent sentIntent, in PendingIntent deliveryIntent);
/**
* Send a multi-part text based SMS.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d83b135..2328717 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -239,9 +239,23 @@ interface ITelephony {
String getCdmaEriText();
/**
+ * Returns true if CDMA provisioning needs to run.
+ */
+ boolean getCdmaNeedsProvisioning();
+
+ /**
* Returns the unread count of voicemails
*/
int getVoiceMessageCount();
+ /**
+ * Returns the network type
+ */
+ int getNetworkType();
+
+ /**
+ * Return true if an ICC card is present
+ */
+ boolean hasIccCard();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 865c6ca..6b42e6b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -32,7 +32,7 @@ interface ITelephonyRegistry {
void notifyCallForwardingChanged(boolean cfi);
void notifyDataActivity(int state);
void notifyDataConnection(int state, boolean isDataConnectivityPossible,
- String reason, String apn, String interfaceName);
+ String reason, String apn, in String[] apnTypes, String interfaceName);
void notifyDataConnectionFailed(String reason);
void notifyCellLocation(in Bundle cellLocation);
}
diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index d7ad492..0f76633 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -16,13 +16,40 @@
package com.android.internal.telephony;
-import android.os.Message;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import android.app.ActivityManagerNative;
+import android.content.Intent;
+import android.os.AsyncResult;
import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.util.Log;
+
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.CommandsInterface.RadioState;
/**
* {@hide}
*/
-public interface IccCard {
+public abstract class IccCard {
+ protected String mLogTag;
+ protected boolean mDbg;
+
+ private IccCardStatus mIccCardStatus = null;
+ protected State mState = null;
+ protected PhoneBase mPhone;
+ private RegistrantList mAbsentRegistrants = new RegistrantList();
+ private RegistrantList mPinLockedRegistrants = new RegistrantList();
+ private RegistrantList mNetworkLockedRegistrants = new RegistrantList();
+
+ private boolean mDesiredPinLocked;
+ private boolean mDesiredFdnEnabled;
+ private boolean mIccPinLocked = true; // Default to locked
+ private boolean mIccFdnEnabled = false; // Default to disabled.
+ // Will be updated when SIM_READY.
+
+
/* The extra data for broacasting intent INTENT_ICC_STATE_CHANGE */
static public final String INTENT_KEY_ICC_STATE = "ss";
/* NOT_READY means the ICC interface is not ready (eg, radio is off or powering on) */
@@ -46,6 +73,17 @@ public interface IccCard {
/* NETWORK means ICC is locked on NETWORK PERSONALIZATION */
static public final String INTENT_VALUE_LOCKED_NETWORK = "NETWORK";
+ protected static final int EVENT_ICC_LOCKED_OR_ABSENT = 1;
+ private static final int EVENT_GET_ICC_STATUS_DONE = 2;
+ protected static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
+ private static final int EVENT_PINPUK_DONE = 4;
+ private static final int EVENT_REPOLL_STATUS_DONE = 5;
+ protected static final int EVENT_ICC_READY = 6;
+ private static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
+ private static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
+ private static final int EVENT_CHANGE_ICC_PASSWORD_DONE = 9;
+ private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
+ private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
/*
UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
@@ -58,33 +96,108 @@ public interface IccCard {
PIN_REQUIRED,
PUK_REQUIRED,
NETWORK_LOCKED,
- READY;
+ READY,
+ NOT_READY;
public boolean isPinLocked() {
return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
}
}
- State getState();
+ public State getState() {
+ if (mState == null) {
+ switch(mPhone.mCM.getRadioState()) {
+ /* This switch block must not return anything in
+ * State.isLocked() or State.ABSENT.
+ * If it does, handleSimStatus() may break
+ */
+ case RADIO_OFF:
+ case RADIO_UNAVAILABLE:
+ case SIM_NOT_READY:
+ case RUIM_NOT_READY:
+ return State.UNKNOWN;
+ case SIM_LOCKED_OR_ABSENT:
+ case RUIM_LOCKED_OR_ABSENT:
+ //this should be transient-only
+ return State.UNKNOWN;
+ case SIM_READY:
+ case RUIM_READY:
+ case NV_READY:
+ return State.READY;
+ case NV_NOT_READY:
+ return State.ABSENT;
+ }
+ } else {
+ return mState;
+ }
+ Log.e(mLogTag, "IccCard.getState(): case should never be reached");
+ return State.UNKNOWN;
+ }
+
+ public IccCard(PhoneBase phone, String logTag, Boolean dbg) {
+ mPhone = phone;
+ mLogTag = logTag;
+ mDbg = dbg;
+ }
+
+ abstract public void dispose();
+
+ protected void finalize() {
+ if(mDbg) Log.d(mLogTag, "IccCard finalized");
+ }
/**
* Notifies handler of any transition into State.ABSENT
*/
- void registerForAbsent(Handler h, int what, Object obj);
- void unregisterForAbsent(Handler h);
+ public void registerForAbsent(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mAbsentRegistrants.add(r);
+
+ if (getState() == State.ABSENT) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForAbsent(Handler h) {
+ mAbsentRegistrants.remove(h);
+ }
/**
- * Notifies handler of any transition into State.isPinLocked()
+ * Notifies handler of any transition into State.NETWORK_LOCKED
*/
- void registerForLocked(Handler h, int what, Object obj);
- void unregisterForLocked(Handler h);
+ public void registerForNetworkLocked(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mNetworkLockedRegistrants.add(r);
+
+ if (getState() == State.NETWORK_LOCKED) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForNetworkLocked(Handler h) {
+ mNetworkLockedRegistrants.remove(h);
+ }
/**
- * Notifies handler of any transition into State.NETWORK_LOCKED
+ * Notifies handler of any transition into State.isPinLocked()
*/
- void registerForNetworkLocked(Handler h, int what, Object obj);
- void unregisterForNetworkLocked(Handler h);
+ public void registerForLocked(Handler h, int what, Object obj) {
+ Registrant r = new Registrant (h, what, obj);
+
+ mPinLockedRegistrants.add(r);
+
+ if (getState().isPinLocked()) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForLocked(Handler h) {
+ mPinLockedRegistrants.remove(h);
+ }
+
/**
* Supply the ICC PIN to the ICC
@@ -107,10 +220,30 @@ public interface IccCard {
*
*/
- void supplyPin (String pin, Message onComplete);
- void supplyPuk (String puk, String newPin, Message onComplete);
- void supplyPin2 (String pin2, Message onComplete);
- void supplyPuk2 (String puk2, String newPin2, Message onComplete);
+ public void supplyPin (String pin, Message onComplete) {
+ mPhone.mCM.supplyIccPin(pin, mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPuk (String puk, String newPin, Message onComplete) {
+ mPhone.mCM.supplyIccPuk(puk, newPin,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPin2 (String pin2, Message onComplete) {
+ mPhone.mCM.supplyIccPin2(pin2,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
+ mPhone.mCM.supplyIccPuk2(puk2, newPin2,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
+
+ public void supplyNetworkDepersonalization (String pin, Message onComplete) {
+ if(mDbg) log("Network Despersonalization: " + pin);
+ mPhone.mCM.supplyNetworkDepersonalization(pin,
+ mHandler.obtainMessage(EVENT_PINPUK_DONE, onComplete));
+ }
/**
* Check whether ICC pin lock is enabled
@@ -119,35 +252,9 @@ public interface IccCard {
* @return true for ICC locked enabled
* false for ICC locked disabled
*/
- boolean getIccLockEnabled ();
-
- /**
- * Set the ICC pin lock enabled or disabled
- * When the operation is complete, onComplete will be sent to its handler
- *
- * @param enabled "true" for locked "false" for unlocked.
- * @param password needed to change the ICC pin state, aka. Pin1
- * @param onComplete
- * onComplete.obj will be an AsyncResult
- * ((AsyncResult)onComplete.obj).exception == null on success
- * ((AsyncResult)onComplete.obj).exception != null on fail
- */
- void setIccLockEnabled(boolean enabled, String password, Message onComplete);
-
-
- /**
- * Change the ICC password used in ICC pin lock
- * When the operation is complete, onComplete will be sent to its handler
- *
- * @param oldPassword is the old password
- * @param newPassword is the new password
- * @param onComplete
- * onComplete.obj will be an AsyncResult
- * ((AsyncResult)onComplete.obj).exception == null on success
- * ((AsyncResult)onComplete.obj).exception != null on fail
- */
- void changeIccLockPassword(String oldPassword, String newPassword,
- Message onComplete);
+ public boolean getIccLockEnabled() {
+ return mIccPinLocked;
+ }
/**
* Check whether ICC fdn (fixed dialing number) is enabled
@@ -156,36 +263,99 @@ public interface IccCard {
* @return true for ICC fdn enabled
* false for ICC fdn disabled
*/
- boolean getIccFdnEnabled ();
+ public boolean getIccFdnEnabled() {
+ return mIccFdnEnabled;
+ }
- /**
- * Set the ICC fdn enabled or disabled
- * When the operation is complete, onComplete will be sent to its handler
- *
- * @param enabled "true" for locked "false" for unlocked.
- * @param password needed to change the ICC fdn enable, aka Pin2
- * @param onComplete
- * onComplete.obj will be an AsyncResult
- * ((AsyncResult)onComplete.obj).exception == null on success
- * ((AsyncResult)onComplete.obj).exception != null on fail
- */
- void setIccFdnEnabled(boolean enabled, String password, Message onComplete);
+ /**
+ * Set the ICC pin lock enabled or disabled
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param enabled "true" for locked "false" for unlocked.
+ * @param password needed to change the ICC pin state, aka. Pin1
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void setIccLockEnabled (boolean enabled,
+ String password, Message onComplete) {
+ int serviceClassX;
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX;
- /**
- * Change the ICC password used in ICC fdn enable
- * When the operation is complete, onComplete will be sent to its handler
- *
- * @param oldPassword is the old password
- * @param newPassword is the new password
- * @param onComplete
- * onComplete.obj will be an AsyncResult
- * ((AsyncResult)onComplete.obj).exception == null on success
- * ((AsyncResult)onComplete.obj).exception != null on fail
- */
- void changeIccFdnPassword(String oldPassword, String newPassword,
- Message onComplete);
+ mDesiredPinLocked = enabled;
+
+ mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
+ enabled, password, serviceClassX,
+ mHandler.obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
+ }
+
+ /**
+ * Set the ICC fdn enabled or disabled
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param enabled "true" for locked "false" for unlocked.
+ * @param password needed to change the ICC fdn enable, aka Pin2
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void setIccFdnEnabled (boolean enabled,
+ String password, Message onComplete) {
+ int serviceClassX;
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX +
+ CommandsInterface.SERVICE_CLASS_SMS;
+
+ mDesiredFdnEnabled = enabled;
+
+ mPhone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
+ enabled, password, serviceClassX,
+ mHandler.obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
+ }
+
+ /**
+ * Change the ICC password used in ICC pin lock
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param oldPassword is the old password
+ * @param newPassword is the new password
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void changeIccLockPassword(String oldPassword, String newPassword,
+ Message onComplete) {
+ if(mDbg) log("Change Pin1 old: " + oldPassword + " new: " + newPassword);
+ mPhone.mCM.changeIccPin(oldPassword, newPassword,
+ mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
+
+ }
+
+ /**
+ * Change the ICC password used in ICC fdn enable
+ * When the operation is complete, onComplete will be sent to its handler
+ *
+ * @param oldPassword is the old password
+ * @param newPassword is the new password
+ * @param onComplete
+ * onComplete.obj will be an AsyncResult
+ * ((AsyncResult)onComplete.obj).exception == null on success
+ * ((AsyncResult)onComplete.obj).exception != null on fail
+ */
+ public void changeIccFdnPassword(String oldPassword, String newPassword,
+ Message onComplete) {
+ if(mDbg) log("Change Pin2 old: " + oldPassword + " new: " + newPassword);
+ mPhone.mCM.changeIccPin2(oldPassword, newPassword,
+ mHandler.obtainMessage(EVENT_CHANGE_ICC_PASSWORD_DONE, onComplete));
+
+ }
- void supplyNetworkDepersonalization (String pin, Message onComplete);
/**
* Returns service provider name stored in ICC card.
@@ -203,5 +373,314 @@ public interface IccCard {
* yet available
*
*/
- String getServiceProviderName();
+ public abstract String getServiceProviderName();
+
+ protected void updateStateProperty() {
+ mPhone.setSystemProperty(TelephonyProperties.PROPERTY_SIM_STATE, getState().toString());
+ }
+
+ private void getIccCardStatusDone(AsyncResult ar) {
+ if (ar.exception != null) {
+ Log.e(mLogTag,"Error getting ICC status. "
+ + "RIL_REQUEST_GET_ICC_STATUS should "
+ + "never return an error", ar.exception);
+ return;
+ }
+ handleIccCardStatus((IccCardStatus) ar.result);
+ }
+
+ private void handleIccCardStatus(IccCardStatus newCardStatus) {
+ boolean transitionedIntoPinLocked;
+ boolean transitionedIntoAbsent;
+ boolean transitionedIntoNetworkLocked;
+
+ State oldState, newState;
+
+ oldState = mState;
+ mIccCardStatus = newCardStatus;
+ newState = getIccCardState();
+ mState = newState;
+
+ updateStateProperty();
+
+ transitionedIntoPinLocked = (
+ (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
+ || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
+ transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
+ transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
+ && newState == State.NETWORK_LOCKED);
+
+ if (transitionedIntoPinLocked) {
+ if(mDbg) log("Notify SIM pin or puk locked.");
+ mPinLockedRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
+ (newState == State.PIN_REQUIRED) ?
+ INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
+ } else if (transitionedIntoAbsent) {
+ if(mDbg) log("Notify SIM missing.");
+ mAbsentRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
+ } else if (transitionedIntoNetworkLocked) {
+ if(mDbg) log("Notify SIM network locked.");
+ mNetworkLockedRegistrants.notifyRegistrants();
+ broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
+ INTENT_VALUE_LOCKED_NETWORK);
+ }
+ }
+
+ /**
+ * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
+ * @param ar is asyncResult of Query_Facility_Locked
+ */
+ private void onQueryFdnEnabled(AsyncResult ar) {
+ if(ar.exception != null) {
+ if(mDbg) log("Error in querying facility lock:" + ar.exception);
+ return;
+ }
+
+ int[] ints = (int[])ar.result;
+ if(ints.length != 0) {
+ mIccFdnEnabled = (0!=ints[0]);
+ if(mDbg) log("Query facility lock : " + mIccFdnEnabled);
+ } else {
+ Log.e(mLogTag, "[IccCard] Bogus facility lock response");
+ }
+ }
+
+ /**
+ * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
+ * @param ar is asyncResult of Query_Facility_Locked
+ */
+ private void onQueryFacilityLock(AsyncResult ar) {
+ if(ar.exception != null) {
+ if (mDbg) log("Error in querying facility lock:" + ar.exception);
+ return;
+ }
+
+ int[] ints = (int[])ar.result;
+ if(ints.length != 0) {
+ mIccPinLocked = (0!=ints[0]);
+ if(mDbg) log("Query facility lock : " + mIccPinLocked);
+ } else {
+ Log.e(mLogTag, "[IccCard] Bogus facility lock response");
+ }
+ }
+
+ public void broadcastIccStateChangedIntent(String value, String reason) {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.putExtra(Phone.PHONE_NAME_KEY, mPhone.getPhoneName());
+ intent.putExtra(INTENT_KEY_ICC_STATE, value);
+ intent.putExtra(INTENT_KEY_LOCKED_REASON, reason);
+ if(mDbg) log("Broadcasting intent ACTION_SIM_STATE_CHANGED " + value
+ + " reason " + reason);
+ ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
+ }
+
+ protected Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg){
+ AsyncResult ar;
+ int serviceClassX;
+
+ serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
+ CommandsInterface.SERVICE_CLASS_DATA +
+ CommandsInterface.SERVICE_CLASS_FAX;
+
+ switch (msg.what) {
+ case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
+ mState = null;
+ updateStateProperty();
+ broadcastIccStateChangedIntent(INTENT_VALUE_ICC_NOT_READY, null);
+ break;
+ case EVENT_ICC_READY:
+ //TODO: put facility read in SIM_READY now, maybe in REG_NW
+ mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
+ break;
+ case EVENT_ICC_LOCKED_OR_ABSENT:
+ mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+ mPhone.mCM.queryFacilityLock (
+ CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
+ obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
+ break;
+ case EVENT_GET_ICC_STATUS_DONE:
+ ar = (AsyncResult)msg.obj;
+
+ getIccCardStatusDone(ar);
+ break;
+ case EVENT_PINPUK_DONE:
+ // a PIN/PUK/PIN2/PUK2/Network Personalization
+ // request has completed. ar.userObj is the response Message
+ // Repoll before returning
+ ar = (AsyncResult)msg.obj;
+ // TODO should abstract these exceptions
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ mPhone.mCM.getIccCardStatus(
+ obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
+ break;
+ case EVENT_REPOLL_STATUS_DONE:
+ // Finished repolling status after PIN operation
+ // ar.userObj is the response messaeg
+ // ar.userObj.obj is already an AsyncResult with an
+ // appropriate exception filled in if applicable
+
+ ar = (AsyncResult)msg.obj;
+ getIccCardStatusDone(ar);
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_QUERY_FACILITY_LOCK_DONE:
+ ar = (AsyncResult)msg.obj;
+ onQueryFacilityLock(ar);
+ break;
+ case EVENT_QUERY_FACILITY_FDN_DONE:
+ ar = (AsyncResult)msg.obj;
+ onQueryFdnEnabled(ar);
+ break;
+ case EVENT_CHANGE_FACILITY_LOCK_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ mIccPinLocked = mDesiredPinLocked;
+ if (mDbg) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
+ "mIccPinLocked= " + mIccPinLocked);
+ } else {
+ Log.e(mLogTag, "Error change facility lock with exception "
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_CHANGE_FACILITY_FDN_DONE:
+ ar = (AsyncResult)msg.obj;
+
+ if (ar.exception == null) {
+ mIccFdnEnabled = mDesiredFdnEnabled;
+ if (mDbg) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
+ "mIccFdnEnabled=" + mIccFdnEnabled);
+ } else {
+ Log.e(mLogTag, "Error change facility fdn with exception "
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ case EVENT_CHANGE_ICC_PASSWORD_DONE:
+ ar = (AsyncResult)msg.obj;
+ if(ar.exception != null) {
+ Log.e(mLogTag, "Error in change sim password with exception"
+ + ar.exception);
+ }
+ AsyncResult.forMessage(((Message)ar.userObj)).exception
+ = ar.exception;
+ ((Message)ar.userObj).sendToTarget();
+ break;
+ default:
+ Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
+ }
+ }
+ };
+
+ public State getIccCardState() {
+ if (mIccCardStatus == null) {
+ Log.e(mLogTag, "[IccCard] IccCardStatus is null");
+ return IccCard.State.ABSENT;
+ }
+
+ // this is common for all radio technologies
+ if (!mIccCardStatus.getCardState().isCardPresent()) {
+ return IccCard.State.ABSENT;
+ }
+
+ RadioState currentRadioState = mPhone.mCM.getRadioState();
+ // check radio technology
+ if( currentRadioState == RadioState.RADIO_OFF ||
+ currentRadioState == RadioState.RADIO_UNAVAILABLE ||
+ currentRadioState == RadioState.SIM_NOT_READY ||
+ currentRadioState == RadioState.RUIM_NOT_READY ||
+ currentRadioState == RadioState.NV_NOT_READY ||
+ currentRadioState == RadioState.NV_READY) {
+ return IccCard.State.NOT_READY;
+ }
+
+ if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT ||
+ currentRadioState == RadioState.SIM_READY ||
+ currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
+ currentRadioState == RadioState.RUIM_READY) {
+
+ int index;
+
+ // check for CDMA radio technology
+ if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
+ currentRadioState == RadioState.RUIM_READY) {
+ index = mIccCardStatus.getCdmaSubscriptionAppIndex();
+ }
+ else {
+ index = mIccCardStatus.getGsmUmtsSubscriptionAppIndex();
+ }
+
+ IccCardApplication app = mIccCardStatus.getApplication(index);
+
+ if (app == null) {
+ Log.e(mLogTag, "[IccCard] Subscription Application in not present");
+ return IccCard.State.ABSENT;
+ }
+
+ // check if PIN required
+ if (app.app_state.isPinRequired()) {
+ return IccCard.State.PIN_REQUIRED;
+ }
+ if (app.app_state.isPukRequired()) {
+ return IccCard.State.PUK_REQUIRED;
+ }
+ if (app.app_state.isSubscriptionPersoEnabled()) {
+ return IccCard.State.NETWORK_LOCKED;
+ }
+ if (app.app_state.isAppReady()) {
+ return IccCard.State.READY;
+ }
+ if (app.app_state.isAppNotReady()) {
+ return IccCard.State.NOT_READY;
+ }
+ return IccCard.State.NOT_READY;
+ }
+
+ return IccCard.State.ABSENT;
+ }
+
+
+ public boolean isApplicationOnIcc(IccCardApplication.AppType type) {
+ if (mIccCardStatus == null) return false;
+
+ for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {
+ IccCardApplication app = mIccCardStatus.getApplication(i);
+ if (app != null && app.app_type == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return true if a ICC card is present
+ */
+ public boolean hasIccCard() {
+ boolean isIccPresent;
+ if (mPhone.getPhoneName().equals("GSM")) {
+ return mIccCardStatus.getCardState().isCardPresent();
+ } else {
+ // TODO: Make work with a CDMA device with a RUIM card.
+ return false;
+ }
+ }
+
+ private void log(String msg) {
+ Log.d(mLogTag, "[IccCard] " + msg);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IccCardStatus.java b/telephony/java/com/android/internal/telephony/IccCardStatus.java
index b602b1c..0e7bad7 100644
--- a/telephony/java/com/android/internal/telephony/IccCardStatus.java
+++ b/telephony/java/com/android/internal/telephony/IccCardStatus.java
@@ -45,42 +45,89 @@ public class IccCardStatus {
PINSTATE_ENABLED_PERM_BLOCKED
};
- public CardState card_state;
- public PinState universal_pin_state;
- public int gsm_umts_subscription_app_index;
- public int cdma_subscription_app_index;
- public int num_applications;
+ private CardState mCardState;
+ private PinState mUniversalPinState;
+ private int mGsmUmtsSubscriptionAppIndex;
+ private int mCdmaSubscriptionAppIndex;
+ private int mNumApplications;
- ArrayList<IccCardApplication> application = new ArrayList<IccCardApplication>(CARD_MAX_APPS);
+ private ArrayList<IccCardApplication> mApplications =
+ new ArrayList<IccCardApplication>(CARD_MAX_APPS);
- CardState CardStateFromRILInt(int state) {
- CardState newState;
- /* RIL_CardState ril.h */
+ public CardState getCardState() {
+ return mCardState;
+ }
+
+ public void setCardState(int state) {
switch(state) {
- case 0: newState = CardState.CARDSTATE_ABSENT; break;
- case 1: newState = CardState.CARDSTATE_PRESENT; break;
- case 2: newState = CardState.CARDSTATE_ERROR; break;
- default:
- throw new RuntimeException(
- "Unrecognized RIL_CardState: " +state);
+ case 0:
+ mCardState = CardState.CARDSTATE_ABSENT;
+ break;
+ case 1:
+ mCardState = CardState.CARDSTATE_PRESENT;
+ break;
+ case 2:
+ mCardState = CardState.CARDSTATE_ERROR;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_CardState: " + state);
}
- return newState;
}
- PinState PinStateFromRILInt(int state) {
- PinState newState;
- /* RIL_PinState ril.h */
+ public void setUniversalPinState(int state) {
switch(state) {
- case 0: newState = PinState.PINSTATE_UNKNOWN; break;
- case 1: newState = PinState.PINSTATE_ENABLED_NOT_VERIFIED; break;
- case 2: newState = PinState.PINSTATE_ENABLED_VERIFIED; break;
- case 3: newState = PinState.PINSTATE_DISABLED; break;
- case 4: newState = PinState.PINSTATE_ENABLED_BLOCKED; break;
- case 5: newState = PinState.PINSTATE_ENABLED_PERM_BLOCKED; break;
- default:
- throw new RuntimeException(
- "Unrecognized RIL_PinState: " +state);
+ case 0:
+ mUniversalPinState = PinState.PINSTATE_UNKNOWN;
+ break;
+ case 1:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_NOT_VERIFIED;
+ break;
+ case 2:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_VERIFIED;
+ break;
+ case 3:
+ mUniversalPinState = PinState.PINSTATE_DISABLED;
+ break;
+ case 4:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_BLOCKED;
+ break;
+ case 5:
+ mUniversalPinState = PinState.PINSTATE_ENABLED_PERM_BLOCKED;
+ break;
+ default:
+ throw new RuntimeException("Unrecognized RIL_PinState: " + state);
}
- return newState;
+ }
+
+ public int getGsmUmtsSubscriptionAppIndex() {
+ return mGsmUmtsSubscriptionAppIndex;
+ }
+
+ public void setGsmUmtsSubscriptionAppIndex(int gsmUmtsSubscriptionAppIndex) {
+ mGsmUmtsSubscriptionAppIndex = gsmUmtsSubscriptionAppIndex;
+ }
+
+ public int getCdmaSubscriptionAppIndex() {
+ return mCdmaSubscriptionAppIndex;
+ }
+
+ public void setCdmaSubscriptionAppIndex(int cdmaSubscriptionAppIndex) {
+ mCdmaSubscriptionAppIndex = cdmaSubscriptionAppIndex;
+ }
+
+ public int getNumApplications() {
+ return mNumApplications;
+ }
+
+ public void setNumApplications(int numApplications) {
+ mNumApplications = numApplications;
+ }
+
+ public void addApplication(IccCardApplication application) {
+ mApplications.add(application);
+ }
+
+ public IccCardApplication getApplication(int index) {
+ return mApplications.get(index);
}
}
diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java
index 7eafafd..acc9197 100644
--- a/telephony/java/com/android/internal/telephony/IccConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccConstants.java
@@ -42,6 +42,9 @@ public interface IccConstants {
static final int EF_CFIS = 0x6FCB;
static final int EF_IMG = 0x4f20;
+ // USIM SIM file ids from TS 31.102
+ public static final int EF_PBR = 0x4F30;
+
// GSM SIM file ids from CPHS (phase 2, version 4.2) CPHS4_2.WW6
static final int EF_MAILBOX_CPHS = 0x6F17;
static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11;
@@ -59,6 +62,7 @@ public interface IccConstants {
static final String MF_SIM = "3F00";
static final String DF_TELECOM = "7F10";
+ static final String DF_PHONEBOOK = "5F3A";
static final String DF_GRAPHICS = "5F50";
static final String DF_GSM = "7F20";
static final String DF_CDMA = "7F25";
diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 0bcaaa6..31cf6a7 100644
--- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -115,7 +115,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
* Replace oldAdn with newAdn in ADN-like record in EF
*
* getAdnRecordsInEf must be called at least once before this function,
- * otherwise an error will be returned
+ * otherwise an error will be returned. Currently the email field
+ * if set in the ADN record is ignored.
* throws SecurityException if no WRITE_CONTACTS permission
*
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
@@ -167,7 +168,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
* Update an ADN-like EF record by record index
*
* This is useful for iteration the whole ADN file, such as write the whole
- * phone book or erase/format the whole phonebook
+ * phone book or erase/format the whole phonebook. Currently the email field
+ * if set in the ADN record is ignored.
* throws SecurityException if no WRITE_CONTACTS permission
*
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN
@@ -237,12 +239,13 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
"Requires android.permission.READ_CONTACTS permission");
}
+ efid = updateEfForIccType(efid);
if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
synchronized(mLock) {
checkThread();
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE);
- adnCache.requestLoadAllAdnLike(efid, response);
+ adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
try {
mLock.wait();
} catch (InterruptedException e) {
@@ -262,5 +265,15 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
}
}
}
+
+ private int updateEfForIccType(int efid) {
+ // Check if we are trying to read ADN records
+ if (efid == IccConstants.EF_ADN) {
+ if (phone.getIccCard().isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) {
+ return IccConstants.EF_PBR;
+ }
+ }
+ return efid;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IccProvider.java b/telephony/java/com/android/internal/telephony/IccProvider.java
index 4cbd779..ffcfe6f 100644
--- a/telephony/java/com/android/internal/telephony/IccProvider.java
+++ b/telephony/java/com/android/internal/telephony/IccProvider.java
@@ -46,7 +46,8 @@ public class IccProvider extends ContentProvider {
private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
"name",
- "number"
+ "number",
+ "emails"
};
private static final int ADN = 1;
@@ -55,6 +56,7 @@ public class IccProvider extends ContentProvider {
private static final String STR_TAG = "tag";
private static final String STR_NUMBER = "number";
+ private static final String STR_EMAILS = "emails";
private static final String STR_PIN2 = "pin2";
private static final UriMatcher URL_MATCHER =
@@ -172,7 +174,8 @@ public class IccProvider extends ContentProvider {
String tag = initialValues.getAsString("tag");
String number = initialValues.getAsString("number");
- boolean success = addIccRecordToEf(efType, tag, number, pin2);
+ // TODO(): Read email instead of sending null.
+ boolean success = addIccRecordToEf(efType, tag, number, null, pin2);
if (!success) {
return null;
@@ -238,6 +241,7 @@ public class IccProvider extends ContentProvider {
// parse where clause
String tag = null;
String number = null;
+ String[] emails = null;
String pin2 = null;
String[] tokens = where.split("AND");
@@ -261,6 +265,9 @@ public class IccProvider extends ContentProvider {
tag = normalizeValue(val);
} else if (STR_NUMBER.equals(key)) {
number = normalizeValue(val);
+ } else if (STR_EMAILS.equals(key)) {
+ //TODO(): Email is null.
+ emails = null;
} else if (STR_PIN2.equals(key)) {
pin2 = normalizeValue(val);
}
@@ -274,7 +281,7 @@ public class IccProvider extends ContentProvider {
return 0;
}
- boolean success = deleteIccRecordFromEf(efType, tag, number, pin2);
+ boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2);
if (!success) {
return 0;
}
@@ -307,9 +314,11 @@ public class IccProvider extends ContentProvider {
String tag = values.getAsString("tag");
String number = values.getAsString("number");
+ String[] emails = null;
String newTag = values.getAsString("newTag");
String newNumber = values.getAsString("newNumber");
-
+ String[] newEmails = null;
+ // TODO(): Update for email.
boolean success = updateIccRecordInEf(efType, tag, number,
newTag, newNumber, pin2);
@@ -355,9 +364,9 @@ public class IccProvider extends ContentProvider {
}
private boolean
- addIccRecordToEf(int efType, String name, String number, String pin2) {
+ addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) {
if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +
- ", number=" + number);
+ ", number=" + number + ", emails=" + emails);
boolean success = false;
@@ -384,7 +393,7 @@ public class IccProvider extends ContentProvider {
private boolean
updateIccRecordInEf(int efType, String oldName, String oldNumber,
- String newName, String newNumber,String pin2) {
+ String newName, String newNumber, String pin2) {
if (DBG) log("updateIccRecordInEf: efType=" + efType +
", oldname=" + oldName + ", oldnumber=" + oldNumber +
", newname=" + newName + ", newnumber=" + newNumber);
@@ -407,9 +416,10 @@ public class IccProvider extends ContentProvider {
}
- private boolean deleteIccRecordFromEf(int efType, String name, String number, String pin2) {
+ private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails,
+ String pin2) {
if (DBG) log("deleteIccRecordFromEf: efType=" + efType +
- ", name=" + name + ", number=" + number + ", pin2=" + pin2);
+ ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2);
boolean success = false;
@@ -438,13 +448,26 @@ public class IccProvider extends ContentProvider {
private void loadRecord(AdnRecord record,
ArrayList<ArrayList> results) {
if (!record.isEmpty()) {
- ArrayList<String> contact = new ArrayList<String>(2);
+ ArrayList<String> contact = new ArrayList<String>();
String alphaTag = record.getAlphaTag();
String number = record.getNumber();
+ String[] emails = record.getEmails();
- if (DBG) log("loadRecord: " + alphaTag + ", " + number);
+ if (DBG) log("loadRecord: " + alphaTag + ", " + number + ",");
contact.add(alphaTag);
contact.add(number);
+ StringBuilder emailString = new StringBuilder();
+
+ if (emails != null) {
+ for (String email: emails) {
+ if (DBG) log("Adding email:" + email);
+ emailString.append(email);
+ emailString.append(",");
+ }
+ contact.add(emailString.toString());
+ } else {
+ contact.add(null);
+ }
results.add(contact);
}
}
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index ea24c25..b8d9e3c 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -31,7 +31,7 @@ import java.util.ArrayList;
public abstract class IccRecords extends Handler implements IccConstants {
protected static final boolean DBG = true;
- //***** Instance Variables
+ // ***** Instance Variables
protected PhoneBase phone;
protected RegistrantList recordsLoadedRegistrants = new RegistrantList();
@@ -40,7 +40,7 @@ public abstract class IccRecords extends Handler implements IccConstants {
protected AdnRecordCache adnCache;
- //***** Cached SIM State; cleared on channel close
+ // ***** Cached SIM State; cleared on channel close
protected boolean recordsRequested = false; // true if we've made requests for the sim records
@@ -54,23 +54,26 @@ public abstract class IccRecords extends Handler implements IccConstants {
protected boolean isVoiceMailFixed = false;
protected int countVoiceMessages = 0;
- protected int mncLength = 0; // 0 is used to indicate that the value
- // is not initialized
+ protected int mncLength = UNINITIALIZED;
protected int mailboxIndex = 0; // 0 is no mailbox dailing number associated
protected String spn;
protected int spnDisplayCondition;
- //***** Constants
+ // ***** Constants
+
+ // Markers for mncLength
+ protected static final int UNINITIALIZED = -1;
+ protected static final int UNKNOWN = 0;
// Bitmasks for SPN display rules.
protected static final int SPN_RULE_SHOW_SPN = 0x01;
protected static final int SPN_RULE_SHOW_PLMN = 0x02;
- //***** Event Constants
+ // ***** Event Constants
protected static final int EVENT_SET_MSISDN_DONE = 30;
- //***** Constructor
+ // ***** Constructor
public IccRecords(PhoneBase p) {
this.phone = p;
@@ -234,4 +237,3 @@ public abstract class IccRecords extends Handler implements IccConstants {
protected abstract void log(String s);
}
-
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 620f2de..8a5a6ae 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -20,6 +20,8 @@ import android.app.PendingIntent;
import android.content.Context;
import android.util.Log;
+import com.android.internal.util.HexDump;
+
import java.util.ArrayList;
import java.util.List;
@@ -30,8 +32,6 @@ import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
* access Sms in Icc.
*/
public abstract class IccSmsInterfaceManager extends ISms.Stub {
- static final boolean DBG = true;
-
protected PhoneBase mPhone;
protected Context mContext;
protected SMSDispatcher mDispatcher;
@@ -49,40 +49,85 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
}
/**
- * Send a Raw PDU SMS
+ * Send a data based SMS to a specific application port.
*
- * @param smsc the SMSC to send the message through, or NULL for the
- * defatult SMSC
- * @param pdu the raw PDU to send
- * @param sentIntent if not NULL this <code>Intent</code> is
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is sucessfully sent, or failed.
* The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
- * @param deliveryIntent if not NULL this <code>Intent</code> is
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
* broadcast when the message is delivered to the recipient. The
* raw pdu of the status report is in the extended data ("pdu").
*/
- public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
- Context context = mPhone.getContext();
+ public void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingPermission(
+ "android.permission.SEND_SMS",
+ "Sending SMS message");
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
+ destPort + " data='"+ HexDump.toHexString(data) + "' sentIntent=" +
+ sentIntent + " deliveryIntent=" + deliveryIntent);
+ }
+ mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+ }
- context.enforceCallingPermission(
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ public void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mPhone.getContext().enforceCallingPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
- if (DBG) log("sendRawPdu: smsc=" + smsc +
- " pdu="+ pdu + " sentIntent" + sentIntent +
- " deliveryIntent" + deliveryIntent);
- mDispatcher.sendRawPdu(smsc, pdu, sentIntent, deliveryIntent);
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
+ " text='"+ text + "' sentIntent=" +
+ sentIntent + " deliveryIntent=" + deliveryIntent);
+ }
+ mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
}
/**
* Send a multi-part text based SMS.
*
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
* the current default SMSC
* @param parts an <code>ArrayList</code> of strings that, in order,
* comprise the original message
@@ -94,21 +139,28 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
* <code>RESULT_ERROR_GENERIC_FAILURE</code>
* <code>RESULT_ERROR_RADIO_OFF</code>
* <code>RESULT_ERROR_NULL_PDU</code>.
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
* @param deliveryIntents if not null, an <code>ArrayList</code> of
* <code>PendingIntent</code>s (one for each message part) that is
* broadcast when the corresponding message part has been delivered
* to the recipient. The raw pdu of the status report is in the
* extended data ("pdu").
*/
- public void sendMultipartText(String destinationAddress, String scAddress, List<String> parts,
+ public void sendMultipartText(String destAddr, String scAddr, List<String> parts,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
- Context context = mPhone.getContext();
-
- context.enforceCallingPermission(
+ mPhone.getContext().enforceCallingPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
- if (DBG) log("sendMultipartText");
- mDispatcher.sendMultipartText(destinationAddress, scAddress, (ArrayList<String>) parts,
+ if (Log.isLoggable("SMS", Log.VERBOSE)) {
+ int i = 0;
+ for (String part : parts) {
+ log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
+ ", part[" + (i++) + "]=" + part);
+ }
+ }
+ mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
(ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
}
@@ -163,4 +215,3 @@ public abstract class IccSmsInterfaceManager extends ISms.Stub {
protected abstract void log(String msg);
}
-
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index a51d074..1910a9c 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -50,16 +50,21 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub {
return mIccSmsInterfaceManager.getAllMessagesFromIccEf();
}
- public void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
- PendingIntent deliveryIntent) throws android.os.RemoteException {
- mIccSmsInterfaceManager.sendRawPdu(smsc, pdu, sentIntent,
- deliveryIntent);
+ public void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mIccSmsInterfaceManager.sendData(destAddr, scAddr, destPort, data,
+ sentIntent, deliveryIntent);
}
- public void sendMultipartText(String destinationAddress, String scAddress,
+ public void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
+ }
+
+ public void sendMultipartText(String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
- mIccSmsInterfaceManager.sendMultipartText(destinationAddress, scAddress,
+ mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,
parts, sentIntents, deliveryIntents);
}
diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java
index 881ed2d..3e7094e 100644
--- a/telephony/java/com/android/internal/telephony/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/IccUtils.java
@@ -74,7 +74,7 @@ public class IccUtils {
* exactly as received"
*/
public static int
- bcdByteToInt(byte b) {
+ gsmBcdByteToInt(byte b) {
int ret = 0;
// treat out-of-range BCD values as 0
@@ -89,11 +89,14 @@ public class IccUtils {
return ret;
}
- /** Decodes BCD byte like {@link bcdByteToInt}, but the most significant BCD
- * digit is expected in the most significant nibble.
+ /**
+ * Decodes a CDMA style BCD byte like {@link gsmBcdByteToInt}, but
+ * opposite nibble format. The least significant BCD digit
+ * is in the least significant nibble and the most significant
+ * is in the most significant nibble.
*/
public static int
- beBcdByteToInt(byte b) {
+ cdmaBcdByteToInt(byte b) {
int ret = 0;
// treat out-of-range BCD values as 0
diff --git a/telephony/java/com/android/internal/telephony/MccTable.java b/telephony/java/com/android/internal/telephony/MccTable.java
new file mode 100644
index 0000000..0d11f8c
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/MccTable.java
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2006 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.internal.telephony;
+
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.net.wifi.WifiManager;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * The table below is built from two resources:
+ *
+ * 1) ITU "Mobile Network Code (MNC) for the international
+ * identification plan for mobile terminals and mobile users"
+ * which is available as an annex to the ITU operational bulletin
+ * available here: http://www.itu.int/itu-t/bulletin/annex.html
+ *
+ * 2) The ISO 3166 country codes list, available here:
+ * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
+ *
+ * This table was verified (28 Aug 2009) against
+ * http://en.wikipedia.org/wiki/List_of_mobile_country_codes with the
+ * only unresolved discrepancy being that this list has an extra entry
+ * (461) for China.
+ *
+ * TODO: Complete the mappings for timezones and language/locale codes.
+ *
+ * The actual table data used in the Java code is generated from the
+ * below Python code for efficiency. The information is expected to
+ * be static, but if changes are required, the table in the python
+ * code can be modified and the trailing code run to re-generate the
+ * tables that are to be used by Java.
+
+mcc_table = [
+ (202, 'gr', 2, 'Greece'),
+ (204, 'nl', 2, 'Europe/Amsterdam', 'nl', 13, 'Netherlands (Kingdom of the)'),
+ (206, 'be', 2, 'Belgium'),
+ (208, 'fr', 2, 'Europe/Paris', 'fr', 'France'),
+ (212, 'mc', 2, 'Monaco (Principality of)'),
+ (213, 'ad', 2, 'Andorra (Principality of)'),
+ (214, 'es', 2, 'Europe/Madrid', 'es', 'Spain'),
+ (216, 'hu', 2, 'Hungary (Republic of)'),
+ (218, 'ba', 2, 'Bosnia and Herzegovina'),
+ (219, 'hr', 2, 'Croatia (Republic of)'),
+ (220, 'rs', 2, 'Serbia and Montenegro'),
+ (222, 'it', 2, 'Europe/Rome', 'it', 'Italy'),
+ (225, 'va', 2, 'Europe/Rome', 'it', 'Vatican City State'),
+ (226, 'ro', 2, 'Romania'),
+ (228, 'ch', 2, 'Europe/Zurich', 'de', 'Switzerland (Confederation of)'),
+ (230, 'cz', 2, 'Europe/Prague', 'cs', 13, 'Czech Republic'),
+ (231, 'sk', 2, 'Slovak Republic'),
+ (232, 'at', 2, 'Europe/Vienna', 'de', 13, 'Austria'),
+ (234, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
+ (235, 'gb', 2, 'Europe/London', 'en', 13, 'United Kingdom of Great Britain and Northern Ireland'),
+ (238, 'dk', 2, 'Denmark'),
+ (240, 'se', 2, 'Sweden'),
+ (242, 'no', 2, 'Norway'),
+ (244, 'fi', 2, 'Finland'),
+ (246, 'lt', 2, 'Lithuania (Republic of)'),
+ (247, 'lv', 2, 'Latvia (Republic of)'),
+ (248, 'ee', 2, 'Estonia (Republic of)'),
+ (250, 'ru', 2, 'Russian Federation'),
+ (255, 'ua', 2, 'Ukraine'),
+ (257, 'by', 2, 'Belarus (Republic of)'),
+ (259, 'md', 2, 'Moldova (Republic of)'),
+ (260, 'pl', 2, 'Europe/Warsaw', 'Poland (Republic of)'),
+ (262, 'de', 2, 'Europe/Berlin', 'de', 13, 'Germany (Federal Republic of)'),
+ (266, 'gi', 2, 'Gibraltar'),
+ (268, 'pt', 2, 'Portugal'),
+ (270, 'lu', 2, 'Luxembourg'),
+ (272, 'ie', 2, 'Europe/Dublin', 'en', 'Ireland'),
+ (274, 'is', 2, 'Iceland'),
+ (276, 'al', 2, 'Albania (Republic of)'),
+ (278, 'mt', 2, 'Malta'),
+ (280, 'cy', 2, 'Cyprus (Republic of)'),
+ (282, 'ge', 2, 'Georgia'),
+ (283, 'am', 2, 'Armenia (Republic of)'),
+ (284, 'bg', 2, 'Bulgaria (Republic of)'),
+ (286, 'tr', 2, 'Turkey'),
+ (288, 'fo', 2, 'Faroe Islands'),
+ (289, 'ge', 2, 'Abkhazia (Georgia)'),
+ (290, 'gl', 2, 'Greenland (Denmark)'),
+ (292, 'sm', 2, 'San Marino (Republic of)'),
+ (293, 'sl', 2, 'Slovenia (Republic of)'),
+ (294, 'mk', 2, 'The Former Yugoslav Republic of Macedonia'),
+ (295, 'li', 2, 'Liechtenstein (Principality of)'),
+ (297, 'me', 2, 'Montenegro (Republic of)'),
+ (302, 'ca', 2, '', '', 11, 'Canada'),
+ (308, 'pm', 2, 'Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)'),
+ (310, 'us', 3, '', 'en', 11, 'United States of America'),
+ (311, 'us', 3, '', 'en', 11, 'United States of America'),
+ (312, 'us', 3, '', 'en', 11, 'United States of America'),
+ (313, 'us', 3, '', 'en', 11, 'United States of America'),
+ (314, 'us', 3, '', 'en', 11, 'United States of America'),
+ (315, 'us', 3, '', 'en', 11, 'United States of America'),
+ (316, 'us', 3, '', 'en', 11, 'United States of America'),
+ (330, 'pr', 2, 'Puerto Rico'),
+ (332, 'vi', 2, 'United States Virgin Islands'),
+ (334, 'mx', 3, 'Mexico'),
+ (338, 'jm', 3, 'Jamaica'),
+ (340, 'gp', 2, 'Guadeloupe (French Department of)'),
+ (342, 'bb', 3, 'Barbados'),
+ (344, 'ag', 3, 'Antigua and Barbuda'),
+ (346, 'ky', 3, 'Cayman Islands'),
+ (348, 'vg', 3, 'British Virgin Islands'),
+ (350, 'bm', 2, 'Bermuda'),
+ (352, 'gd', 2, 'Grenada'),
+ (354, 'ms', 2, 'Montserrat'),
+ (356, 'kn', 2, 'Saint Kitts and Nevis'),
+ (358, 'lc', 2, 'Saint Lucia'),
+ (360, 'vc', 2, 'Saint Vincent and the Grenadines'),
+ (362, 'nl', 2, 'Netherlands Antilles'),
+ (363, 'aw', 2, 'Aruba'),
+ (364, 'bs', 2, 'Bahamas (Commonwealth of the)'),
+ (365, 'ai', 3, 'Anguilla'),
+ (366, 'dm', 2, 'Dominica (Commonwealth of)'),
+ (368, 'cu', 2, 'Cuba'),
+ (370, 'do', 2, 'Dominican Republic'),
+ (372, 'ht', 2, 'Haiti (Republic of)'),
+ (374, 'tt', 2, 'Trinidad and Tobago'),
+ (376, 'tc', 2, 'Turks and Caicos Islands'),
+ (400, 'az', 2, 'Azerbaijani Republic'),
+ (401, 'kz', 2, 'Kazakhstan (Republic of)'),
+ (402, 'bt', 2, 'Bhutan (Kingdom of)'),
+ (404, 'in', 2, 'India (Republic of)'),
+ (405, 'in', 2, 'India (Republic of)'),
+ (410, 'pk', 2, 'Pakistan (Islamic Republic of)'),
+ (412, 'af', 2, 'Afghanistan'),
+ (413, 'lk', 2, 'Sri Lanka (Democratic Socialist Republic of)'),
+ (414, 'mm', 2, 'Myanmar (Union of)'),
+ (415, 'lb', 2, 'Lebanon'),
+ (416, 'jo', 2, 'Jordan (Hashemite Kingdom of)'),
+ (417, 'sy', 2, 'Syrian Arab Republic'),
+ (418, 'iq', 2, 'Iraq (Republic of)'),
+ (419, 'kw', 2, 'Kuwait (State of)'),
+ (420, 'sa', 2, 'Saudi Arabia (Kingdom of)'),
+ (421, 'ye', 2, 'Yemen (Republic of)'),
+ (422, 'om', 2, 'Oman (Sultanate of)'),
+ (423, 'ps', 2, 'Palestine'),
+ (424, 'ae', 2, 'United Arab Emirates'),
+ (425, 'il', 2, 'Israel (State of)'),
+ (426, 'bh', 2, 'Bahrain (Kingdom of)'),
+ (427, 'qa', 2, 'Qatar (State of)'),
+ (428, 'mn', 2, 'Mongolia'),
+ (429, 'np', 2, 'Nepal'),
+ (430, 'ae', 2, 'United Arab Emirates'),
+ (431, 'ae', 2, 'United Arab Emirates'),
+ (432, 'ir', 2, 'Iran (Islamic Republic of)'),
+ (434, 'uz', 2, 'Uzbekistan (Republic of)'),
+ (436, 'tj', 2, 'Tajikistan (Republic of)'),
+ (437, 'kg', 2, 'Kyrgyz Republic'),
+ (438, 'tm', 2, 'Turkmenistan'),
+ (440, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
+ (441, 'jp', 2, 'Asia/Tokyo', 'ja', 14, 'Japan'),
+ (450, 'kr', 2, 'Korea (Republic of)'),
+ (452, 'vn', 2, 'Viet Nam (Socialist Republic of)'),
+ (454, 'hk', 2, '"Hong Kong, China"'),
+ (455, 'mo', 2, '"Macao, China"'),
+ (456, 'kh', 2, 'Cambodia (Kingdom of)'),
+ (457, 'la', 2, "Lao People's Democratic Republic"),
+ (460, 'cn', 2, "China (People's Republic of)"),
+ (461, 'cn', 2, "China (People's Republic of)"),
+ (466, 'tw', 2, "Taiwan (Republic of China)"),
+ (467, 'kp', 2, "Democratic People's Republic of Korea"),
+ (470, 'bd', 2, "Bangladesh (People's Republic of)"),
+ (472, 'mv', 2, 'Maldives (Republic of)'),
+ (502, 'my', 2, 'Malaysia'),
+ (505, 'au', 2, 'Australia/Sydney', 'en', 11, 'Australia'),
+ (510, 'id', 2, 'Indonesia (Republic of)'),
+ (514, 'tl', 2, 'Democratic Republic of Timor-Leste'),
+ (515, 'ph', 2, 'Philippines (Republic of the)'),
+ (520, 'th', 2, 'Thailand'),
+ (525, 'sg', 2, 'Singapore', 'en', 11, 'Singapore (Republic of)'),
+ (528, 'bn', 2, 'Brunei Darussalam'),
+ (530, 'nz', 2, 'Pacific/Auckland', 'en', 'New Zealand'),
+ (534, 'mp', 2, 'Northern Mariana Islands (Commonwealth of the)'),
+ (535, 'gu', 2, 'Guam'),
+ (536, 'nr', 2, 'Nauru (Republic of)'),
+ (537, 'pg', 2, 'Papua New Guinea'),
+ (539, 'to', 2, 'Tonga (Kingdom of)'),
+ (540, 'sb', 2, 'Solomon Islands'),
+ (541, 'vu', 2, 'Vanuatu (Republic of)'),
+ (542, 'fj', 2, 'Fiji (Republic of)'),
+ (543, 'wf', 2, "Wallis and Futuna (Territoire franais d'outre-mer)"),
+ (544, 'as', 2, 'American Samoa'),
+ (545, 'ki', 2, 'Kiribati (Republic of)'),
+ (546, 'nc', 2, "New Caledonia (Territoire franais d'outre-mer)"),
+ (547, 'pf', 2, "French Polynesia (Territoire franais d'outre-mer)"),
+ (548, 'ck', 2, 'Cook Islands'),
+ (549, 'ws', 2, 'Samoa (Independent State of)'),
+ (550, 'fm', 2, 'Micronesia (Federated States of)'),
+ (551, 'mh', 2, 'Marshall Islands (Republic of the)'),
+ (552, 'pw', 2, 'Palau (Republic of)'),
+ (602, 'eg', 2, 'Egypt (Arab Republic of)'),
+ (603, 'dz', 2, "Algeria (People's Democratic Republic of)"),
+ (604, 'ma', 2, 'Morocco (Kingdom of)'),
+ (605, 'tn', 2, 'Tunisia'),
+ (606, 'ly', 2, "Libya (Socialist People's Libyan Arab Jamahiriya)"),
+ (607, 'gm', 2, 'Gambia (Republic of the)'),
+ (608, 'sn', 2, 'Senegal (Republic of)'),
+ (609, 'mr', 2, 'Mauritania (Islamic Republic of)'),
+ (610, 'ml', 2, 'Mali (Republic of)'),
+ (611, 'gn', 2, 'Guinea (Republic of)'),
+ (612, 'ci', 2, "Cte d'Ivoire (Republic of)"),
+ (613, 'bf', 2, 'Burkina Faso'),
+ (614, 'ne', 2, 'Niger (Republic of the)'),
+ (615, 'tg', 2, 'Togolese Republic'),
+ (616, 'bj', 2, 'Benin (Republic of)'),
+ (617, 'mu', 2, 'Mauritius (Republic of)'),
+ (618, 'lr', 2, 'Liberia (Republic of)'),
+ (619, 'sl', 2, 'Sierra Leone'),
+ (620, 'gh', 2, 'Ghana'),
+ (621, 'ng', 2, 'Nigeria (Federal Republic of)'),
+ (622, 'td', 2, 'Chad (Republic of)'),
+ (623, 'cf', 2, 'Central African Republic'),
+ (624, 'cm', 2, 'Cameroon (Republic of)'),
+ (625, 'cv', 2, 'Cape Verde (Republic of)'),
+ (626, 'st', 2, 'Sao Tome and Principe (Democratic Republic of)'),
+ (627, 'gq', 2, 'Equatorial Guinea (Republic of)'),
+ (628, 'ga', 2, 'Gabonese Republic'),
+ (629, 'cg', 2, 'Congo (Republic of the)'),
+ (630, 'cg', 2, 'Democratic Republic of the Congo'),
+ (631, 'ao', 2, 'Angola (Republic of)'),
+ (632, 'gw', 2, 'Guinea-Bissau (Republic of)'),
+ (633, 'sc', 2, 'Seychelles (Republic of)'),
+ (634, 'sd', 2, 'Sudan (Republic of the)'),
+ (635, 'rw', 2, 'Rwanda (Republic of)'),
+ (636, 'et', 2, 'Ethiopia (Federal Democratic Republic of)'),
+ (637, 'so', 2, 'Somali Democratic Republic'),
+ (638, 'dj', 2, 'Djibouti (Republic of)'),
+ (639, 'ke', 2, 'Kenya (Republic of)'),
+ (640, 'tz', 2, 'Tanzania (United Republic of)'),
+ (641, 'ug', 2, 'Uganda (Republic of)'),
+ (642, 'bi', 2, 'Burundi (Republic of)'),
+ (643, 'mz', 2, 'Mozambique (Republic of)'),
+ (645, 'zm', 2, 'Zambia (Republic of)'),
+ (646, 'mg', 2, 'Madagascar (Republic of)'),
+ (647, 're', 2, 'Reunion (French Department of)'),
+ (648, 'zw', 2, 'Zimbabwe (Republic of)'),
+ (649, 'na', 2, 'Namibia (Republic of)'),
+ (650, 'mw', 2, 'Malawi'),
+ (651, 'ls', 2, 'Lesotho (Kingdom of)'),
+ (652, 'bw', 2, 'Botswana (Republic of)'),
+ (653, 'sz', 2, 'Swaziland (Kingdom of)'),
+ (654, 'km', 2, 'Comoros (Union of the)'),
+ (655, 'za', 2, 'Africa/Johannesburg', 'en', 'South Africa (Republic of)'),
+ (657, 'er', 2, 'Eritrea'),
+ (702, 'bz', 2, 'Belize'),
+ (704, 'gt', 2, 'Guatemala (Republic of)'),
+ (706, 'sv', 2, 'El Salvador (Republic of)'),
+ (708, 'hn', 3, 'Honduras (Republic of)'),
+ (710, 'ni', 2, 'Nicaragua'),
+ (712, 'cr', 2, 'Costa Rica'),
+ (714, 'pa', 2, 'Panama (Republic of)'),
+ (716, 'pe', 2, 'Peru'),
+ (722, 'ar', 3, 'Argentine Republic'),
+ (724, 'br', 2, 'Brazil (Federative Republic of)'),
+ (730, 'cl', 2, 'Chile'),
+ (732, 'co', 3, 'Colombia (Republic of)'),
+ (734, 've', 2, 'Venezuela (Bolivarian Republic of)'),
+ (736, 'bo', 2, 'Bolivia (Republic of)'),
+ (738, 'gy', 2, 'Guyana'),
+ (740, 'ec', 2, 'Ecuador'),
+ (742, 'gf', 2, 'French Guiana (French Department of)'),
+ (744, 'py', 2, 'Paraguay (Republic of)'),
+ (746, 'sr', 2, 'Suriname (Republic of)'),
+ (748, 'uy', 2, 'Uruguay (Eastern Republic of)'),
+ (750, 'fk', 2, 'Falkland Islands (Malvinas)')]
+
+get_mcc = lambda elt: elt[0]
+get_iso = lambda elt: elt[1]
+get_sd = lambda elt: elt[2]
+get_tz = lambda elt: len(elt) > 4 and elt[3] or ''
+get_lang = lambda elt: len(elt) > 5 and elt[4] or ''
+get_wifi = lambda elt: len(elt) > 6 and elt[5] or 0
+
+mcc_codes = ['0x%04x' % get_mcc(elt) for elt in mcc_table]
+tz_set = sorted(x for x in set(get_tz(elt) for elt in mcc_table))
+lang_set = sorted(x for x in set(get_lang(elt) for elt in mcc_table))
+
+def mk_ind_code(elt):
+ iso = get_iso(elt)
+ iso_code = ((ord(iso[0]) << 8) | ord(iso[1])) & 0xFFFF # 16 bits
+ wifi = get_wifi(elt) & 0x000F # 4 bits
+ sd = get_sd(elt) & 0x0003 # 2 bits
+ tz_ind = tz_set.index(get_tz(elt)) & 0x001F # 5 bits
+ lang_ind = lang_set.index(get_lang(elt)) & 0x000F # 4 bits
+ return (iso_code << 16) | (wifi << 11) | (sd << 9) | (tz_ind << 4) | lang_ind
+
+ind_codes = ['0x%08x' % mk_ind_code(elt) for elt in mcc_table]
+
+def fmt_list(title, l, batch_sz):
+ sl = []
+ for i in range(len(l) / batch_sz + (len(l) % batch_sz and 1 or 0)):
+ j = i * batch_sz
+ sl.append((' ' * 8) + ', '.join(l[j:j + batch_sz]))
+ return ' private static final %s = {\n' % title + ',\n'.join(sl) + '\n };\n'
+
+def do_autogen_comment(extra_desc=[]):
+ print ' /' + '**\n * AUTO GENERATED (by the Python code above)'
+ for line in extra_desc:
+ print ' * %s' % line
+ print ' *' + '/'
+
+do_autogen_comment()
+print fmt_list('String[] TZ_STRINGS', ['"%s"' % x for x in tz_set], 1)
+do_autogen_comment()
+print fmt_list('String[] LANG_STRINGS', ['"%s"' % x for x in lang_set], 10)
+do_autogen_comment(['This table is a list of MCC codes. The index in this table',
+ 'of a given MCC code is the index of extra information about',
+ 'that MCC in the IND_CODES table.'])
+print fmt_list('short[] MCC_CODES', mcc_codes, 10)
+do_autogen_comment(['The values in this table are broken down as follows (msb to lsb):',
+ ' iso country code 16 bits',
+ ' (unused) 1 bit',
+ ' wifi channel 4 bits',
+ ' smalled digit 2 bits',
+ ' default timezone 5 bits',
+ ' default language 4 bits'])
+print fmt_list('int[] IND_CODES', ind_codes, 6)
+
+def parse_ind_code(ind):
+ mcc = eval(mcc_codes[ind])
+ code = eval(ind_codes[ind])
+ iso_lsb = int((code >> 16) & 0x00FF)
+ iso_msb = int((code >> 24) & 0x00FF)
+ iso = '%s%s' % (chr(iso_msb), chr(iso_lsb))
+ wifi = int((code >> 11) & 0x000F)
+ sd = int((code >> 9) & 0x0003)
+ tz_ind = (code >> 4) & 0x001F
+ lang_ind = (code >> 0) & 0x000F
+ return (mcc, iso, sd, tz_set[tz_ind], lang_set[lang_ind], wifi)
+
+fmt_str = 'mcc = %s, iso = %s, sd = %s, tz = %s, lang = %s, wifi = %s'
+orig_table = [fmt_str % (get_mcc(elt), get_iso(elt), get_sd(elt),
+ get_tz(elt), get_lang(elt), get_wifi(elt))
+ for elt in mcc_table]
+derived_table = [fmt_str % parse_ind_code(i) for i in range(len(ind_codes))]
+for i in range(len(orig_table)):
+ if orig_table[i] == derived_table[i]: continue
+ print 'MISMATCH ERROR : ', orig_table[i], " != ", derived_table[i]
+
+*/
+
+/**
+ * Mobile Country Code
+ *
+ * {@hide}
+ */
+public final class MccTable
+{
+ /**
+ * AUTO GENERATED (by the Python code above)
+ */
+ private static final String[] TZ_STRINGS = {
+ "",
+ "Africa/Johannesburg",
+ "Asia/Tokyo",
+ "Australia/Sydney",
+ "Europe/Amsterdam",
+ "Europe/Berlin",
+ "Europe/Dublin",
+ "Europe/London",
+ "Europe/Madrid",
+ "Europe/Paris",
+ "Europe/Prague",
+ "Europe/Rome",
+ "Europe/Vienna",
+ "Europe/Warsaw",
+ "Europe/Zurich",
+ "Pacific/Auckland",
+ "Singapore"
+ };
+
+ /**
+ * AUTO GENERATED (by the Python code above)
+ */
+ private static final String[] LANG_STRINGS = {
+ "", "cs", "de", "en", "es", "fr", "it", "ja", "nl"
+ };
+
+ /**
+ * AUTO GENERATED (by the Python code above)
+ * This table is a list of MCC codes. The index in this table
+ * of a given MCC code is the index of extra information about
+ * that MCC in the IND_CODES table.
+ */
+ private static final short[] MCC_CODES = {
+ 0x00ca, 0x00cc, 0x00ce, 0x00d0, 0x00d4, 0x00d5, 0x00d6, 0x00d8, 0x00da, 0x00db,
+ 0x00dc, 0x00de, 0x00e1, 0x00e2, 0x00e4, 0x00e6, 0x00e7, 0x00e8, 0x00ea, 0x00eb,
+ 0x00ee, 0x00f0, 0x00f2, 0x00f4, 0x00f6, 0x00f7, 0x00f8, 0x00fa, 0x00ff, 0x0101,
+ 0x0103, 0x0104, 0x0106, 0x010a, 0x010c, 0x010e, 0x0110, 0x0112, 0x0114, 0x0116,
+ 0x0118, 0x011a, 0x011b, 0x011c, 0x011e, 0x0120, 0x0121, 0x0122, 0x0124, 0x0125,
+ 0x0126, 0x0127, 0x0129, 0x012e, 0x0134, 0x0136, 0x0137, 0x0138, 0x0139, 0x013a,
+ 0x013b, 0x013c, 0x014a, 0x014c, 0x014e, 0x0152, 0x0154, 0x0156, 0x0158, 0x015a,
+ 0x015c, 0x015e, 0x0160, 0x0162, 0x0164, 0x0166, 0x0168, 0x016a, 0x016b, 0x016c,
+ 0x016d, 0x016e, 0x0170, 0x0172, 0x0174, 0x0176, 0x0178, 0x0190, 0x0191, 0x0192,
+ 0x0194, 0x0195, 0x019a, 0x019c, 0x019d, 0x019e, 0x019f, 0x01a0, 0x01a1, 0x01a2,
+ 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, 0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac,
+ 0x01ad, 0x01ae, 0x01af, 0x01b0, 0x01b2, 0x01b4, 0x01b5, 0x01b6, 0x01b8, 0x01b9,
+ 0x01c2, 0x01c4, 0x01c6, 0x01c7, 0x01c8, 0x01c9, 0x01cc, 0x01cd, 0x01d2, 0x01d3,
+ 0x01d6, 0x01d8, 0x01f6, 0x01f9, 0x01fe, 0x0202, 0x0203, 0x0208, 0x020d, 0x0210,
+ 0x0212, 0x0216, 0x0217, 0x0218, 0x0219, 0x021b, 0x021c, 0x021d, 0x021e, 0x021f,
+ 0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0226, 0x0227, 0x0228, 0x025a,
+ 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 0x0260, 0x0261, 0x0262, 0x0263, 0x0264,
+ 0x0265, 0x0266, 0x0267, 0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e,
+ 0x026f, 0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 0x0278,
+ 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 0x0280, 0x0281, 0x0282,
+ 0x0283, 0x0285, 0x0286, 0x0287, 0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d,
+ 0x028e, 0x028f, 0x0291, 0x02be, 0x02c0, 0x02c2, 0x02c4, 0x02c6, 0x02c8, 0x02ca,
+ 0x02cc, 0x02d2, 0x02d4, 0x02da, 0x02dc, 0x02de, 0x02e0, 0x02e2, 0x02e4, 0x02e6,
+ 0x02e8, 0x02ea, 0x02ec, 0x02ee
+ };
+
+ /**
+ * AUTO GENERATED (by the Python code above)
+ * The values in this table are broken down as follows (msb to lsb):
+ * iso country code 16 bits
+ * (unused) 1 bit
+ * wifi channel 4 bits
+ * smalled digit 2 bits
+ * default timezone 5 bits
+ * default language 4 bits
+ */
+ private static final int[] IND_CODES = {
+ 0x67720400, 0x6e6c6c48, 0x62650400, 0x66720495, 0x6d630400, 0x61640400,
+ 0x65730484, 0x68750400, 0x62610400, 0x68720400, 0x72730400, 0x697404b6,
+ 0x766104b6, 0x726f0400, 0x636804e2, 0x637a6ca1, 0x736b0400, 0x61746cc2,
+ 0x67626c73, 0x67626c73, 0x646b0400, 0x73650400, 0x6e6f0400, 0x66690400,
+ 0x6c740400, 0x6c760400, 0x65650400, 0x72750400, 0x75610400, 0x62790400,
+ 0x6d640400, 0x706c04d0, 0x64656c52, 0x67690400, 0x70740400, 0x6c750400,
+ 0x69650463, 0x69730400, 0x616c0400, 0x6d740400, 0x63790400, 0x67650400,
+ 0x616d0400, 0x62670400, 0x74720400, 0x666f0400, 0x67650400, 0x676c0400,
+ 0x736d0400, 0x736c0400, 0x6d6b0400, 0x6c690400, 0x6d650400, 0x63615c00,
+ 0x706d0400, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03, 0x75735e03,
+ 0x75735e03, 0x75735e03, 0x70720400, 0x76690400, 0x6d780600, 0x6a6d0600,
+ 0x67700400, 0x62620600, 0x61670600, 0x6b790600, 0x76670600, 0x626d0400,
+ 0x67640400, 0x6d730400, 0x6b6e0400, 0x6c630400, 0x76630400, 0x6e6c0400,
+ 0x61770400, 0x62730400, 0x61690600, 0x646d0400, 0x63750400, 0x646f0400,
+ 0x68740400, 0x74740400, 0x74630400, 0x617a0400, 0x6b7a0400, 0x62740400,
+ 0x696e0400, 0x696e0400, 0x706b0400, 0x61660400, 0x6c6b0400, 0x6d6d0400,
+ 0x6c620400, 0x6a6f0400, 0x73790400, 0x69710400, 0x6b770400, 0x73610400,
+ 0x79650400, 0x6f6d0400, 0x70730400, 0x61650400, 0x696c0400, 0x62680400,
+ 0x71610400, 0x6d6e0400, 0x6e700400, 0x61650400, 0x61650400, 0x69720400,
+ 0x757a0400, 0x746a0400, 0x6b670400, 0x746d0400, 0x6a707427, 0x6a707427,
+ 0x6b720400, 0x766e0400, 0x686b0400, 0x6d6f0400, 0x6b680400, 0x6c610400,
+ 0x636e0400, 0x636e0400, 0x74770400, 0x6b700400, 0x62640400, 0x6d760400,
+ 0x6d790400, 0x61755c33, 0x69640400, 0x746c0400, 0x70680400, 0x74680400,
+ 0x73675d03, 0x626e0400, 0x6e7a04f3, 0x6d700400, 0x67750400, 0x6e720400,
+ 0x70670400, 0x746f0400, 0x73620400, 0x76750400, 0x666a0400, 0x77660400,
+ 0x61730400, 0x6b690400, 0x6e630400, 0x70660400, 0x636b0400, 0x77730400,
+ 0x666d0400, 0x6d680400, 0x70770400, 0x65670400, 0x647a0400, 0x6d610400,
+ 0x746e0400, 0x6c790400, 0x676d0400, 0x736e0400, 0x6d720400, 0x6d6c0400,
+ 0x676e0400, 0x63690400, 0x62660400, 0x6e650400, 0x74670400, 0x626a0400,
+ 0x6d750400, 0x6c720400, 0x736c0400, 0x67680400, 0x6e670400, 0x74640400,
+ 0x63660400, 0x636d0400, 0x63760400, 0x73740400, 0x67710400, 0x67610400,
+ 0x63670400, 0x63670400, 0x616f0400, 0x67770400, 0x73630400, 0x73640400,
+ 0x72770400, 0x65740400, 0x736f0400, 0x646a0400, 0x6b650400, 0x747a0400,
+ 0x75670400, 0x62690400, 0x6d7a0400, 0x7a6d0400, 0x6d670400, 0x72650400,
+ 0x7a770400, 0x6e610400, 0x6d770400, 0x6c730400, 0x62770400, 0x737a0400,
+ 0x6b6d0400, 0x7a610413, 0x65720400, 0x627a0400, 0x67740400, 0x73760400,
+ 0x686e0600, 0x6e690400, 0x63720400, 0x70610400, 0x70650400, 0x61720600,
+ 0x62720400, 0x636c0400, 0x636f0600, 0x76650400, 0x626f0400, 0x67790400,
+ 0x65630400, 0x67660400, 0x70790400, 0x73720400, 0x75790400, 0x666b0400
+ };
+
+ static final String LOG_TAG = "MccTable";
+
+ /**
+ * Given a Mobile Country Code, returns a default time zone ID
+ * if available. Returns null if unavailable.
+ */
+ public static String defaultTimeZoneForMcc(int mcc) {
+ int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
+ if (index < 0) {
+ return null;
+ }
+ int indCode = IND_CODES[index];
+ int tzInd = (indCode >>> 4) & 0x001F;
+ String tz = TZ_STRINGS[tzInd];
+ if (tz == "") {
+ return null;
+ }
+ return tz;
+ }
+
+ /**
+ * Given a Mobile Country Code, returns an ISO two-character
+ * country code if available. Returns "" if unavailable.
+ */
+ public static String countryCodeForMcc(int mcc) {
+ int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
+ if (index < 0) {
+ return "";
+ }
+ int indCode = IND_CODES[index];
+ byte[] iso = {(byte)((indCode >>> 24) & 0x00FF), (byte)((indCode >>> 16) & 0x00FF)};
+ return new String(iso);
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns an ISO 2-3 character
+ * language code if available. Returns null if unavailable.
+ */
+ public static String defaultLanguageForMcc(int mcc) {
+ int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
+ if (index < 0) {
+ return null;
+ }
+ int indCode = IND_CODES[index];
+ int langInd = indCode & 0x000F;
+ String lang = LANG_STRINGS[langInd];
+ if (lang == "") {
+ return null;
+ }
+ return lang;
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns the corresponding
+ * smallest number of digits field. Returns 2 if unavailable.
+ */
+ public static int smallestDigitsMccForMnc(int mcc) {
+ int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
+ if (index < 0) {
+ return 2;
+ }
+ int indCode = IND_CODES[index];
+ int smDig = (indCode >>> 9) & 0x0003;
+ return smDig;
+ }
+
+ /**
+ * Given a GSM Mobile Country Code, returns the number of wifi
+ * channels allowed in that country. Returns 0 if unavailable.
+ */
+ public static int wifiChannelsForMcc(int mcc) {
+ int index = Arrays.binarySearch(MCC_CODES, (short)mcc);
+ if (index < 0) {
+ return 0;
+ }
+ int indCode = IND_CODES[index];
+ int wifi = (indCode >>> 11) & 0x000F;
+ return wifi;
+ }
+
+ /**
+ * Updates MCC and MNC device configuration information for application retrieving
+ * correct version of resources. If either MCC or MNC is 0, they will be ignored (not set).
+ * @param phone PhoneBae to act on.
+ * @param mccmnc truncated imsi with just the MCC and MNC - MNC assumed to be from 4th to end
+ */
+ public static void updateMccMncConfiguration(PhoneBase phone, String mccmnc) {
+ Configuration config = new Configuration();
+ int mcc, mnc;
+
+ try {
+ mcc = Integer.parseInt(mccmnc.substring(0,3));
+ mnc = Integer.parseInt(mccmnc.substring(3));
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Error parsing IMSI");
+ return;
+ }
+
+ Log.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);
+
+ if (mcc != 0) {
+ config.mcc = mcc;
+ setTimezoneFromMccIfNeeded(phone, mcc);
+ setLocaleFromMccIfNeeded(phone, mcc);
+ setWifiChannelsFromMccIfNeeded(phone, mcc);
+ }
+ if (mnc != 0) {
+ config.mnc = mnc;
+ }
+ try {
+ ActivityManagerNative.getDefault().updateConfiguration(config);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Can't update configuration", e);
+ }
+ }
+
+ /**
+ * If the timezone is not already set, set it based on the MCC of the SIM.
+ * @param phone PhoneBase to act on (get context from).
+ * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
+ */
+ private static void setTimezoneFromMccIfNeeded(PhoneBase phone, int mcc) {
+ String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
+ if (timezone == null || timezone.length() == 0) {
+ String zoneId = defaultTimeZoneForMcc(mcc);
+ if (zoneId != null && zoneId.length() > 0) {
+ Context context = phone.getContext();
+ // Set time zone based on MCC
+ AlarmManager alarm =
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarm.setTimeZone(zoneId);
+ Log.d(LOG_TAG, "timezone set to "+zoneId);
+ }
+ }
+ }
+
+ /**
+ * If the locale is not already set, set it based on the MCC of the SIM.
+ * @param phone PhoneBase to act on.
+ * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
+ */
+ private static void setLocaleFromMccIfNeeded(PhoneBase phone, int mcc) {
+ String language = MccTable.defaultLanguageForMcc(mcc);
+ String country = MccTable.countryCodeForMcc(mcc);
+
+ Log.d(LOG_TAG, "locale set to "+language+"_"+country);
+ phone.setSystemLocale(language, country);
+ }
+
+ /**
+ * If the number of allowed wifi channels has not been set, set it based on
+ * the MCC of the SIM.
+ * @param phone PhoneBase to act on (get context from).
+ * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
+ */
+ private static void setWifiChannelsFromMccIfNeeded(PhoneBase phone, int mcc) {
+ int wifiChannels = MccTable.wifiChannelsForMcc(mcc);
+ if (wifiChannels != 0) {
+ Context context = phone.getContext();
+ // only set to this default if the user hasn't manually set it
+ try {
+ Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS);
+ } catch (Settings.SettingNotFoundException e) {
+ Log.d(LOG_TAG, "WIFI_NUM_ALLOWED_CHANNESL set to " + wifiChannels);
+ WifiManager wM = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ // don't persist
+ wM.setNumAllowedChannels(wifiChannels, false);
+ }
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index 7f2b849..5203d3f 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -22,6 +22,7 @@ import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
@@ -98,8 +99,9 @@ public interface Phone {
static final String PHONE_NAME_KEY = "phoneName";
static final String FAILURE_REASON_KEY = "reason";
static final String STATE_CHANGE_REASON_KEY = "reason";
- static final String DATA_APN_TYPE_KEY = "apnType";
+ static final String DATA_APN_TYPES_KEY = "apnType";
static final String DATA_APN_KEY = "apn";
+
static final String DATA_IFACE_NAME_KEY = "iface";
static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
static final String PHONE_IN_ECM_STATE = "phoneinECMState";
@@ -119,10 +121,16 @@ public interface Phone {
static final String APN_TYPE_MMS = "mms";
/** APN type for SUPL assisted GPS */
static final String APN_TYPE_SUPL = "supl";
+ /** APN type for DUN traffic */
+ static final String APN_TYPE_DUN = "dun";
+ /** APN type for HiPri traffic */
+ static final String APN_TYPE_HIPRI = "hipri";
// "Features" accessible through the connectivity manager
static final String FEATURE_ENABLE_MMS = "enableMMS";
static final String FEATURE_ENABLE_SUPL = "enableSUPL";
+ static final String FEATURE_ENABLE_DUN = "enableDUN";
+ static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
/**
* Return codes for <code>enableApnType()</code>
@@ -260,8 +268,8 @@ public interface Phone {
/**
* Get current coarse-grained voice call state.
- * Use {@link #registerForPhoneStateChanged(Handler, int, Object)
- * registerForPhoneStateChanged()} for change notification. <p>
+ * Use {@link #registerForPreciseCallStateChanged(Handler, int, Object)
+ * registerForPreciseCallStateChanged()} for change notification. <p>
* If the phone has an active call and call waiting occurs,
* then the phone state is RINGING not OFFHOOK
* <strong>Note:</strong>
@@ -315,18 +323,21 @@ public interface Phone {
void unregisterForUnknownConnection(Handler h);
/**
- * Notifies when any aspect of the voice call state changes.
+ * Register for getting notifications for change in the Call State {@link Call.State}
+ * This is called PreciseCallState because the call state is more precise than the
+ * {@link Phone.State} which can be obtained using the {@link PhoneStateListener}
+ *
* Resulting events will have an AsyncResult in <code>Message.obj</code>.
* AsyncResult.userData will be set to the obj argument here.
* The <em>h</em> parameter is held only by a weak reference.
*/
- void registerForPhoneStateChanged(Handler h, int what, Object obj);
+ void registerForPreciseCallStateChanged(Handler h, int what, Object obj);
/**
* Unregisters for voice call state change notifications.
* Extraneous calls are tolerated silently.
*/
- void unregisterForPhoneStateChanged(Handler h);
+ void unregisterForPreciseCallStateChanged(Handler h);
/**
@@ -423,6 +434,20 @@ public interface Phone {
void unregisterForMmiComplete(Handler h);
/**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what user-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notification for Ecm timer reset
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForEcmTimerReset(Handler h);
+
+ /**
* Returns a list of MMI codes that are pending. (They have initiated
* but have not yet completed).
* Presently there is only ever one.
@@ -538,6 +563,20 @@ public interface Phone {
void unregisterForCdmaOtaStatusChange(Handler h);
/**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj);
+
+ /**
+ * Unregister for notifications for subscription info
+ * @param h Handler to be removed from the registrant list.
+ */
+ public void unregisterForSubscriptionInfoReady(Handler h);
+
+ /**
* Returns SIM record load state. Use
* <code>getSimCard().registerForReady()</code> for change notification.
*
@@ -556,8 +595,8 @@ public interface Phone {
/**
* Answers a ringing or waiting call. Active calls, if any, go on hold.
* Answering occurs asynchronously, and final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when no call is ringing or waiting
*/
@@ -567,8 +606,8 @@ public interface Phone {
* Reject (ignore) a ringing call. In GSM, this means UDUB
* (User Determined User Busy). Reject occurs asynchronously,
* and final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException when no call is ringing or waiting
*/
@@ -578,8 +617,8 @@ public interface Phone {
* Places any active calls on hold, and makes any held calls
* active. Switch occurs asynchronously and may fail.
* Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if a call is ringing, waiting, or
* dialing/alerting. In these cases, this operation may not be performed.
@@ -596,8 +635,8 @@ public interface Phone {
/**
* Conferences holding and active. Conference occurs asynchronously
* and may fail. Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canConference() would return false.
* In these cases, this operation may not be performed.
@@ -631,8 +670,8 @@ public interface Phone {
* Connects the two calls and disconnects the subscriber from both calls
* Explicit Call Transfer occurs asynchronously
* and may fail. Final notification occurs via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*
* @exception CallStateException if canTransfer() would return false.
* In these cases, this operation may not be performed.
@@ -659,8 +698,8 @@ public interface Phone {
* IDLE, ACTIVE, DIALING, ALERTING, or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getForegroundCall();
@@ -676,8 +715,8 @@ public interface Phone {
* IDLE, HOLDING or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getBackgroundCall();
@@ -693,8 +732,8 @@ public interface Phone {
* IDLE, INCOMING, WAITING or DISCONNECTED.
*
* State change notification is available via
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}.
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}.
*/
Call getRingingCall();
@@ -1067,8 +1106,8 @@ public interface Phone {
/**
* Gets current mute status. Use
- * {@link #registerForPhoneStateChanged(android.os.Handler, int,
- * java.lang.Object) registerForPhoneStateChanged()}
+ * {@link #registerForPreciseCallStateChanged(android.os.Handler, int,
+ * java.lang.Object) registerForPreciseCallStateChanged()}
* as a change notifcation, although presently phone state changed is not
* fired when setMute() is called.
*
@@ -1150,17 +1189,9 @@ public interface Phone {
List<DataConnection> getCurrentDataConnectionList ();
/**
- * Udpate LAC and CID in service state for currnet GSM netowrk registration
- *
- * If get different LAC and/or CID, notifyServiceState will be sent
- *
- * @param
- * <strong>On failure</strong>,
- * (((AsyncResult)response.obj).result) == null and
- * (((AsyncResult)response.obj).exception) being an instance of
- * com.android.internal.telephony.gsm.CommandException
+ * Update the ServiceState CellLocation for current network registration.
*/
- void updateServiceLocation(Message response);
+ void updateServiceLocation();
/**
* Enable location update notifications.
@@ -1262,6 +1293,13 @@ public interface Phone {
boolean disableDataConnectivity();
/**
+ * Report the current state of data connectivity (enabled or disabled)
+ * @return {@code false} if data connectivity has been explicitly disabled,
+ * {@code true} otherwise.
+ */
+ boolean isDataConnectivityEnabled();
+
+ /**
* Enables the specified APN type. Only works for "special" APN types,
* i.e., not the default APN.
* @param type The desired APN type. Cannot be {@link #APN_TYPE_DEFAULT}.
@@ -1342,7 +1380,7 @@ public interface Phone {
*/
String getIccSerialNumber();
- //***** CDMA support methods
+ /* CDMA support methods */
/*
* TODO(Moto) TODO(Teleca): can getCdmaMin, getEsn, getMeid use more generic calls
@@ -1355,6 +1393,13 @@ public interface Phone {
String getCdmaMin();
/**
+ * Check if subscription data has been assigned to mMin
+ *
+ * return true if MIN info is ready; false otherwise.
+ */
+ boolean isMinInfoReady();
+
+ /**
* Retrieves PRL Version for CDMA phones
*/
String getCdmaPrlVersion();
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index bcb1ccc..e340f85 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -21,6 +21,7 @@ import android.app.IActivityManager;
import android.content.Context;
import android.content.res.Configuration;
import android.content.SharedPreferences;
+import android.net.wifi.WifiManager;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.Looper;
@@ -28,8 +29,8 @@ import android.os.Message;
import android.os.RegistrantList;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
+import android.provider.Settings;
import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
import android.text.TextUtils;
import android.util.Log;
@@ -53,17 +54,20 @@ import java.util.Locale;
*
*/
-public abstract class PhoneBase implements Phone {
+public abstract class PhoneBase extends Handler implements Phone {
private static final String LOG_TAG = "PHONE";
private static final boolean LOCAL_DEBUG = true;
- // Key used to read and write the saved network selection value
+ // Key used to read and write the saved network selection numeric value
public static final String NETWORK_SELECTION_KEY = "network_selection_key";
+ // Key used to read and write the saved network selection operator name
+ public static final String NETWORK_SELECTION_NAME_KEY = "network_selection_name_key";
- // Key used to read/write "disable data connection on boot" pref (used for testing)
+
+ // Key used to read/write "disable data connection on boot" pref (used for testing)
public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key";
- //***** Event Constants
+ /* Event Constants */
protected static final int EVENT_RADIO_AVAILABLE = 1;
/** Supplementary Service Notification received. */
protected static final int EVENT_SSN = 2;
@@ -79,20 +83,22 @@ public abstract class PhoneBase implements Phone {
protected static final int EVENT_SET_CALL_FORWARD_DONE = 12;
protected static final int EVENT_GET_CALL_FORWARD_DONE = 13;
protected static final int EVENT_CALL_RING = 14;
+ protected static final int EVENT_CALL_RING_CONTINUE = 15;
+
// Used to intercept the carrier selection calls so that
// we can save the values.
- protected static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 15;
- protected static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 16;
- protected static final int EVENT_SET_CLIR_COMPLETE = 17;
- protected static final int EVENT_REGISTERED_TO_NETWORK = 18;
- protected static final int EVENT_SET_VM_NUMBER_DONE = 19;
+ protected static final int EVENT_SET_NETWORK_MANUAL_COMPLETE = 16;
+ protected static final int EVENT_SET_NETWORK_AUTOMATIC_COMPLETE = 17;
+ protected static final int EVENT_SET_CLIR_COMPLETE = 18;
+ protected static final int EVENT_REGISTERED_TO_NETWORK = 19;
+ protected static final int EVENT_SET_VM_NUMBER_DONE = 20;
// Events for CDMA support
- protected static final int EVENT_GET_DEVICE_IDENTITY_DONE = 20;
- protected static final int EVENT_RUIM_RECORDS_LOADED = 21;
- protected static final int EVENT_NV_READY = 22;
- protected static final int EVENT_SET_ENHANCED_VP = 23;
- protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 24;
- protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 25;
+ protected static final int EVENT_GET_DEVICE_IDENTITY_DONE = 21;
+ protected static final int EVENT_RUIM_RECORDS_LOADED = 22;
+ protected static final int EVENT_NV_READY = 23;
+ protected static final int EVENT_SET_ENHANCED_VP = 24;
+ protected static final int EVENT_EMERGENCY_CALLBACK_MODE_ENTER = 25;
+ protected static final int EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE = 26;
// Key used to read/write current CLIR setting
public static final String CLIR_KEY = "clir_key";
@@ -100,10 +106,14 @@ public abstract class PhoneBase implements Phone {
// Key used to read/write "disable DNS server check" pref (used for testing)
public static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
- //***** Instance Variables
+ /* Instance Variables */
public CommandsInterface mCM;
protected IccFileHandler mIccFileHandler;
boolean mDnsCheckDisabled = false;
+ public DataConnectionTracker mDataConnection;
+ boolean mDoesRilSendMultipleCallRing;
+ int mCallRingContinueToken = 0;
+ int mCallRingDelay;
/**
* Set a system property, unless we're in unit test mode
@@ -117,7 +127,7 @@ public abstract class PhoneBase implements Phone {
}
- protected final RegistrantList mPhoneStateRegistrants
+ protected final RegistrantList mPreciseCallStateRegistrants
= new RegistrantList();
protected final RegistrantList mNewRingingConnectionRegistrants
@@ -166,8 +176,8 @@ public abstract class PhoneBase implements Phone {
* @param notifier An instance of DefaultPhoneNotifier,
* unless unit testing.
*/
- protected PhoneBase(PhoneNotifier notifier, Context context) {
- this(notifier, context, false);
+ protected PhoneBase(PhoneNotifier notifier, Context context, CommandsInterface ci) {
+ this(notifier, context, ci, false);
}
/**
@@ -179,18 +189,83 @@ public abstract class PhoneBase implements Phone {
* @param unitTestMode when true, prevents notifications
* of state change events
*/
- protected PhoneBase(PhoneNotifier notifier, Context context,
+ protected PhoneBase(PhoneNotifier notifier, Context context, CommandsInterface ci,
boolean unitTestMode) {
this.mNotifier = notifier;
this.mContext = context;
mLooper = Looper.myLooper();
+ mCM = ci;
- setLocaleByCarrier();
+ setPropertiesByCarrier();
setUnitTestMode(unitTestMode);
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
mDnsCheckDisabled = sp.getBoolean(DNS_SERVER_CHECK_DISABLED_KEY, false);
+ mCM.setOnCallRing(this, EVENT_CALL_RING, null);
+
+ /**
+ * Some RIL's don't always send RIL_UNSOL_CALL_RING so it needs
+ * to be generated locally. Ideally all ring tones should be loops
+ * and this wouldn't be necessary. But to minimize changes to upper
+ * layers it is requested that it be generated by lower layers.
+ *
+ * By default old phones won't have the property set but do generate
+ * the RIL_UNSOL_CALL_RING so the default if there is no property is
+ * true.
+ */
+ mDoesRilSendMultipleCallRing = SystemProperties.getBoolean(
+ TelephonyProperties.PROPERTY_RIL_SENDS_MULTIPLE_CALL_RING, true);
+ Log.d(LOG_TAG, "mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing);
+
+ mCallRingDelay = SystemProperties.getInt(
+ TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
+ Log.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
+ }
+
+ public void dispose() {
+ synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ mCM.unSetOnCallRing(this);
+ }
+ }
+
+ /**
+ * When overridden the derived class needs to call
+ * super.handleMessage(msg) so this method has a
+ * a chance to process the message.
+ *
+ * @param msg
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case EVENT_CALL_RING:
+ Log.d(LOG_TAG, "Event EVENT_CALL_RING Received state=" + getState());
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ Phone.State state = getState();
+ if ((!mDoesRilSendMultipleCallRing)
+ && ((state == Phone.State.RINGING) || (state == Phone.State.IDLE))) {
+ mCallRingContinueToken += 1;
+ sendIncomingCallRingNotification(mCallRingContinueToken);
+ } else {
+ notifyIncomingRing();
+ }
+ }
+ break;
+
+ case EVENT_CALL_RING_CONTINUE:
+ Log.d(LOG_TAG, "Event EVENT_CALL_RING_CONTINUE Received stat=" + getState());
+ if (getState() == Phone.State.RINGING) {
+ sendIncomingCallRingNotification(msg.arg1);
+ }
+ break;
+
+ default:
+ throw new RuntimeException("unexpected event not handled");
+ }
}
// Inherited documentation suffices.
@@ -219,25 +294,24 @@ public abstract class PhoneBase implements Phone {
}
// Inherited documentation suffices.
- public void registerForPhoneStateChanged(Handler h, int what, Object obj) {
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
checkCorrectThread(h);
- mPhoneStateRegistrants.addUnique(h, what, obj);
+ mPreciseCallStateRegistrants.addUnique(h, what, obj);
}
// Inherited documentation suffices.
- public void unregisterForPhoneStateChanged(Handler h) {
- mPhoneStateRegistrants.remove(h);
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mPreciseCallStateRegistrants.remove(h);
}
/**
- * Notify registrants of a PhoneStateChanged.
* Subclasses of Phone probably want to replace this with a
* version scoped to their packages
*/
- protected void notifyCallStateChangedP() {
+ protected void notifyPreciseCallStateChangedP() {
AsyncResult ar = new AsyncResult(null, this, null);
- mPhoneStateRegistrants.notifyRegistrants(ar);
+ mPreciseCallStateRegistrants.notifyRegistrants(ar);
}
// Inherited documentation suffices.
@@ -285,16 +359,6 @@ public abstract class PhoneBase implements Phone {
mCM.unregisterForInCallVoicePrivacyOff(h);
}
- /**
- * Notifiy registrants of a new ringing Connection.
- * Subclasses of Phone probably want to replace this with a
- * version scoped to their packages
- */
- protected void notifyNewRingingConnectionP(Connection cn) {
- AsyncResult ar = new AsyncResult(null, cn, null);
- mNewRingingConnectionRegistrants.notifyRegistrants(ar);
- }
-
// Inherited documentation suffices.
public void registerForIncomingRing(
Handler h, int what, Object obj) {
@@ -450,10 +514,10 @@ public abstract class PhoneBase implements Phone {
}
/**
- * Set the locale by matching the carrier string in
+ * Set the properties by matching the carrier string in
* a string-array resource
*/
- private void setLocaleByCarrier() {
+ private void setPropertiesByCarrier() {
String carrier = SystemProperties.get("ro.carrier");
if (null == carrier || 0 == carrier.length()) {
@@ -461,18 +525,36 @@ public abstract class PhoneBase implements Phone {
}
CharSequence[] carrierLocales = mContext.
- getResources().getTextArray(R.array.carrier_locales);
+ getResources().getTextArray(R.array.carrier_properties);
- for (int i = 0; i < carrierLocales.length-1; i+=2) {
+ for (int i = 0; i < carrierLocales.length; i+=3) {
String c = carrierLocales[i].toString();
- String l = carrierLocales[i+1].toString();
if (carrier.equals(c)) {
+ String l = carrierLocales[i+1].toString();
+ int wifiChannels = 0;
+ try {
+ wifiChannels = Integer.parseInt(
+ carrierLocales[i+2].toString());
+ } catch (NumberFormatException e) { }
+
String language = l.substring(0, 2);
String country = "";
if (l.length() >=5) {
country = l.substring(3, 5);
}
setSystemLocale(language, country);
+
+ if (wifiChannels != 0) {
+ try {
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS);
+ } catch (Settings.SettingNotFoundException e) {
+ // note this is not persisting
+ WifiManager wM = (WifiManager)
+ mContext.getSystemService(Context.WIFI_SERVICE);
+ wM.setNumAllowedChannels(wifiChannels, false);
+ }
+ }
return;
}
}
@@ -530,16 +612,22 @@ public abstract class PhoneBase implements Phone {
}
}
- /*
- * Retrieves the Handler of the Phone instance
+ /**
+ * Get state
*/
- public abstract Handler getHandler();
+ public abstract Phone.State getState();
/**
* Retrieves the IccFileHandler of the Phone instance
*/
public abstract IccFileHandler getIccFileHandler();
+ /*
+ * Retrieves the Handler of the Phone instance
+ */
+ public Handler getHandler() {
+ return this;
+ }
/**
* Query the status of the CDMA roaming preference
@@ -595,7 +683,7 @@ public abstract class PhoneBase implements Phone {
* This should only be called in GSM mode.
* Only here for some backward compatibility
* issues concerning the GSMPhone class.
- * @deprecated
+ * @deprecated Always returns null.
*/
public List<PdpConnection> getCurrentPdpList() {
return null;
@@ -679,6 +767,12 @@ public abstract class PhoneBase implements Phone {
return null;
}
+ public boolean isMinInfoReady() {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ return false;
+ }
+
public String getCdmaPrlVersion(){
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
@@ -705,6 +799,16 @@ public abstract class PhoneBase implements Phone {
Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
}
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ }
+
public boolean isOtaSpNumber(String dialStr) {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
@@ -721,6 +825,16 @@ public abstract class PhoneBase implements Phone {
Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
}
+ public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ }
+
+ public void unregisterForEcmTimerReset(Handler h) {
+ // This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
+ Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
+ }
+
public void registerForSignalInfo(Handler h, int what, Object obj) {
mCM.registerForSignalInfo(h, what, obj);
}
@@ -786,4 +900,106 @@ public abstract class PhoneBase implements Phone {
// This function should be overridden by the class CDMAPhone. Not implemented in GSMPhone.
Log.e(LOG_TAG, "Error! This function should never be executed, inactive CDMAPhone.");
}
+
+ public String getInterfaceName(String apnType) {
+ return mDataConnection.getInterfaceName(apnType);
+ }
+
+ public String getIpAddress(String apnType) {
+ return mDataConnection.getIpAddress(apnType);
+ }
+
+ public boolean isDataConnectivityEnabled() {
+ return mDataConnection.getDataEnabled();
+ }
+
+ public String getGateway(String apnType) {
+ return mDataConnection.getGateway(apnType);
+ }
+
+ public String[] getDnsServers(String apnType) {
+ return mDataConnection.getDnsServers(apnType);
+ }
+
+ public String[] getActiveApnTypes() {
+ return mDataConnection.getActiveApnTypes();
+ }
+
+ public String getActiveApn() {
+ return mDataConnection.getActiveApnString();
+ }
+
+ public int enableApnType(String type) {
+ return mDataConnection.enableApnType(type);
+ }
+
+ public int disableApnType(String type) {
+ return mDataConnection.disableApnType(type);
+ }
+
+ /**
+ * simulateDataConnection
+ *
+ * simulates various data connection states. This messes with
+ * DataConnectionTracker's internal states, but doesn't actually change
+ * the underlying radio connection states.
+ *
+ * @param state Phone.DataState enum.
+ */
+ public void simulateDataConnection(Phone.DataState state) {
+ DataConnectionTracker.State dcState;
+
+ switch (state) {
+ case CONNECTED:
+ dcState = DataConnectionTracker.State.CONNECTED;
+ break;
+ case SUSPENDED:
+ dcState = DataConnectionTracker.State.CONNECTED;
+ break;
+ case DISCONNECTED:
+ dcState = DataConnectionTracker.State.FAILED;
+ break;
+ default:
+ dcState = DataConnectionTracker.State.CONNECTING;
+ break;
+ }
+
+ mDataConnection.setState(dcState);
+ notifyDataConnection(null);
+ }
+
+ /**
+ * Notifiy registrants of a new ringing Connection.
+ * Subclasses of Phone probably want to replace this with a
+ * version scoped to their packages
+ */
+ protected void notifyNewRingingConnectionP(Connection cn) {
+ AsyncResult ar = new AsyncResult(null, cn, null);
+ mNewRingingConnectionRegistrants.notifyRegistrants(ar);
+ }
+
+ /**
+ * Notify registrants of a RING event.
+ */
+ private void notifyIncomingRing() {
+ AsyncResult ar = new AsyncResult(null, this, null);
+ mIncomingRingRegistrants.notifyRegistrants(ar);
+ }
+
+ /**
+ * Send the incoming call Ring notification if conditions are right.
+ */
+ private void sendIncomingCallRingNotification(int token) {
+ if (!mDoesRilSendMultipleCallRing && (token == mCallRingContinueToken)) {
+ Log.d(LOG_TAG, "Sending notifyIncomingRing");
+ notifyIncomingRing();
+ sendMessageDelayed(
+ obtainMessage(EVENT_CALL_RING_CONTINUE, token, 0), mCallRingDelay);
+ } else {
+ Log.d(LOG_TAG, "Ignoring ring notification request,"
+ + " mDoesRilSendMultipleCallRing=" + mDoesRilSendMultipleCallRing
+ + " token=" + token
+ + " mCallRingContinueToken=" + mCallRingContinueToken);
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index da00268..711a48c 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.util.Log;
@@ -210,12 +211,12 @@ public class PhoneProxy extends Handler implements Phone {
mActivePhone.unregisterForUnknownConnection(h);
}
- public void registerForPhoneStateChanged(Handler h, int what, Object obj) {
- mActivePhone.registerForPhoneStateChanged(h, what, obj);
+ public void registerForPreciseCallStateChanged(Handler h, int what, Object obj) {
+ mActivePhone.registerForPreciseCallStateChanged(h, what, obj);
}
- public void unregisterForPhoneStateChanged(Handler h) {
- mActivePhone.unregisterForPhoneStateChanged(h);
+ public void unregisterForPreciseCallStateChanged(Handler h) {
+ mActivePhone.unregisterForPreciseCallStateChanged(h);
}
public void registerForNewRingingConnection(Handler h, int what, Object obj) {
@@ -314,6 +315,22 @@ public class PhoneProxy extends Handler implements Phone {
mActivePhone.unregisterForCdmaOtaStatusChange(h);
}
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ mActivePhone.registerForSubscriptionInfoReady(h, what, obj);
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ mActivePhone.unregisterForSubscriptionInfoReady(h);
+ }
+
+ public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+ mActivePhone.registerForEcmTimerReset(h,what,obj);
+ }
+
+ public void unregisterForEcmTimerReset(Handler h) {
+ mActivePhone.unregisterForEcmTimerReset(h);
+ }
+
public boolean getIccRecordsLoaded() {
return mActivePhone.getIccRecordsLoaded();
}
@@ -418,6 +435,10 @@ public class PhoneProxy extends Handler implements Phone {
return mActivePhone.getCdmaMin();
}
+ public boolean isMinInfoReady() {
+ return mActivePhone.isMinInfoReady();
+ }
+
public String getCdmaPrlVersion() {
return mActivePhone.getCdmaPrlVersion();
}
@@ -545,8 +566,8 @@ public class PhoneProxy extends Handler implements Phone {
return mActivePhone.getCurrentDataConnectionList();
}
- public void updateServiceLocation(Message response) {
- mActivePhone.updateServiceLocation(response);
+ public void updateServiceLocation() {
+ mActivePhone.updateServiceLocation();
}
public void enableLocationUpdates() {
@@ -613,6 +634,10 @@ public class PhoneProxy extends Handler implements Phone {
return mActivePhone.disableApnType(type);
}
+ public boolean isDataConnectivityEnabled() {
+ return mActivePhone.isDataConnectivityEnabled();
+ }
+
public boolean isDataConnectivityPossible() {
return mActivePhone.isDataConnectivityPossible();
}
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 690b38a..cd6340e 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -630,7 +630,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
}
public void
- getIccStatus(Message result) {
+ getIccCardStatus(Message result) {
//Note: This RIL request has not been renamed to ICC,
// but this request is also valid for SIM and RUIM
RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result);
@@ -1237,10 +1237,17 @@ public final class RIL extends BaseCommands implements CommandsInterface {
*/
public void
setupDefaultPDP(String apn, String user, String password, Message result) {
- String radioTechnology = "1"; //0 for CDMA, 1 for GSM/UMTS
+ int radioTechnology;
+ int authType;
String profile = ""; //profile number, NULL for GSM/UMTS
- setupDataCall(radioTechnology, profile, apn, user,
- password, result);
+
+ radioTechnology = RILConstants.SETUP_DATA_TECH_GSM;
+ //TODO(): Add to the APN database, AuthType is set to CHAP/PAP
+ authType = (user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP
+ : RILConstants.SETUP_DATA_AUTH_NONE;
+
+ setupDataCall(Integer.toString(radioTechnology), profile, apn, user,
+ password, Integer.toString(authType), result);
}
@@ -1259,7 +1266,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
*/
public void
setupDataCall(String radioTechnology, String profile, String apn,
- String user, String password, Message result) {
+ String user, String password, String authType, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
@@ -1270,15 +1277,12 @@ public final class RIL extends BaseCommands implements CommandsInterface {
rr.mp.writeString(apn);
rr.mp.writeString(user);
rr.mp.writeString(password);
- //TODO(): Add to the APN database, AuthType is set to CHAP/PAP
- // 0 => Neither PAP nor CHAP will be performed, 3 => PAP / CHAP will be performed.
- if (user != null)
- rr.mp.writeString("3");
- else
- rr.mp.writeString("0");
+ rr.mp.writeString(authType);
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " "
- + apn);
+ if (RILJ_LOGD) riljLog(rr.serialString() + "> "
+ + requestToString(rr.mRequest) + " " + radioTechnology + " "
+ + profile + " " + apn + " " + user + " "
+ + password + " " + authType);
send(rr);
}
@@ -2507,6 +2511,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
mRestrictedStateRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
+ break;
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
if (RILJ_LOGD) unsljLog(response);
@@ -2553,7 +2558,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
break;
case RIL_UNSOL_CDMA_CALL_WAITING:
- if (RILJ_LOGD) unsljLog(response);
+ if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCallWaitingInfoRegistrants != null) {
mCallWaitingInfoRegistrants.notifyRegistrants(
@@ -2735,24 +2740,22 @@ public final class RIL extends BaseCommands implements CommandsInterface {
private Object
responseIccCardStatus(Parcel p) {
- RadioState currentRadioState;
IccCardApplication ca;
- currentRadioState = getRadioState();
-
IccCardStatus status = new IccCardStatus();
- status.card_state = status.CardStateFromRILInt(p.readInt());
- status.universal_pin_state = status.PinStateFromRILInt(p.readInt());
- status.gsm_umts_subscription_app_index = p.readInt();
- status.cdma_subscription_app_index = p.readInt();
- status.num_applications = p.readInt();
+ status.setCardState(p.readInt());
+ status.setUniversalPinState(p.readInt());
+ status.setGsmUmtsSubscriptionAppIndex(p.readInt());
+ status.setCdmaSubscriptionAppIndex(p.readInt());
+ int numApplications = p.readInt();
// limit to maximum allowed applications
- if (status.num_applications > IccCardStatus.CARD_MAX_APPS) {
- status.num_applications = IccCardStatus.CARD_MAX_APPS;
+ if (numApplications > IccCardStatus.CARD_MAX_APPS) {
+ numApplications = IccCardStatus.CARD_MAX_APPS;
}
+ status.setNumApplications(numApplications);
- for (int i = 0 ; i < status.num_applications ; i++) {
+ for (int i = 0 ; i < numApplications ; i++) {
ca = new IccCardApplication();
ca.app_type = ca.AppTypeFromRILInt(p.readInt());
ca.app_state = ca.AppStateFromRILInt(p.readInt());
@@ -2762,62 +2765,9 @@ public final class RIL extends BaseCommands implements CommandsInterface {
ca.pin1_replaced = p.readInt();
ca.pin1 = p.readInt();
ca.pin2 = p.readInt();
- status.application.add(ca);
- }
-
- // this is common for all radio technologies
- if (!status.card_state.isCardPresent()) {
- return IccStatus.ICC_ABSENT;
- }
-
- // check radio technology
- if( currentRadioState == RadioState.RADIO_OFF ||
- currentRadioState == RadioState.RADIO_UNAVAILABLE ||
- currentRadioState == RadioState.SIM_NOT_READY ||
- currentRadioState == RadioState.RUIM_NOT_READY ||
- currentRadioState == RadioState.NV_NOT_READY ||
- currentRadioState == RadioState.NV_READY ) {
- return IccStatus.ICC_NOT_READY;
+ status.addApplication(ca);
}
-
- if( currentRadioState == RadioState.SIM_LOCKED_OR_ABSENT ||
- currentRadioState == RadioState.SIM_READY ||
- currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
- currentRadioState == RadioState.RUIM_READY) {
-
- int index;
-
- // check for CDMA radio technology
- if (currentRadioState == RadioState.RUIM_LOCKED_OR_ABSENT ||
- currentRadioState == RadioState.RUIM_READY) {
- index = status.cdma_subscription_app_index;
- }
- else {
- index = status.gsm_umts_subscription_app_index;
- }
-
- // check if PIN required
- if (status.application.get(index).app_state.isPinRequired()) {
- return IccStatus.ICC_PIN;
- }
- if (status.application.get(index).app_state.isPukRequired()) {
- return IccStatus.ICC_PUK;
- }
- if (status.application.get(index).app_state.isSubscriptionPersoEnabled()) {
- return IccStatus.ICC_NETWORK_PERSONALIZATION;
- }
- if (status.application.get(index).app_state.isAppReady()) {
- return IccStatus.ICC_READY;
- }
- if (status.application.get(index).app_state.isAppNotReady()) {
- return IccStatus.ICC_NOT_READY;
- }
- return IccStatus.ICC_NOT_READY;
- }
-
- // Unrecognized ICC status. Treat it like a missing ICC.
- Log.e(LOG_TAG, "Unrecognized RIL_REQUEST_GET_SIM_STATUS result: " + status);
- return IccStatus.ICC_ABSENT;
+ return status;
}
private Object
@@ -3035,7 +2985,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification();
notification.number = p.readString();
- notification.numberPresentation = p.readInt();
+ notification.numberPresentation = notification.presentationFromCLIP(p.readInt());
notification.name = p.readString();
notification.namePresentation = notification.numberPresentation;
notification.isPresent = p.readInt();
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 0763e63..90a82f9 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -79,6 +79,14 @@ public interface RILConstants {
int CDM_TTY_HCO_MODE = 2;
int CDM_TTY_VCO_MODE = 3;
+ /* Setup a packet data connection. See ril.h RIL_REQUEST_SETUP_DATA_CALL */
+ int SETUP_DATA_TECH_CDMA = 0;
+ int SETUP_DATA_TECH_GSM = 1;
+ int SETUP_DATA_AUTH_NONE = 0;
+ int SETUP_DATA_AUTH_PAP = 1;
+ int SETUP_DATA_AUTH_CHAP = 2;
+ int SETUP_DATA_AUTH_PAP_CHAP = 3;
+
/*
cat include/telephony/ril.h | \
egrep '^#define' | \
diff --git a/telephony/java/com/android/internal/telephony/RetryManager.java b/telephony/java/com/android/internal/telephony/RetryManager.java
new file mode 100644
index 0000000..385b191
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/RetryManager.java
@@ -0,0 +1,392 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.util.Log;
+import android.util.Pair;
+import android.text.TextUtils;
+
+import java.util.Random;
+import java.util.ArrayList;
+
+/**
+ * Retry manager allows a simple way to declare a series of
+ * retires timeouts. After creating a RetryManager the configure
+ * method is used to define the sequence. A simple linear series
+ * may be initialized using configure with three integer parameters
+ * The other configure method allows a series to be declared using
+ * a string.
+ *<p>
+ * The format of the configuration string is a series of parameters
+ * separated by a comma. There are two name value pair parameters plus a series
+ * of delay times. The units of of these delay times is unspecified.
+ * The name value pairs which may be specified are:
+ *<ul>
+ *<li>max_retries=<value>
+ *<li>default_randomizationTime=<value>
+ *</ul>
+ *<p>
+ * max_retries is the number of times that incrementRetryCount
+ * maybe called before isRetryNeeded will return false. if value
+ * is infinite then isRetryNeeded will always return true.
+ *
+ * default_randomizationTime will be used as the randomizationTime
+ * for delay times which have no supplied randomizationTime. If
+ * default_randomizationTime is not defined it defaults to 0.
+ *<p>
+ * The other parameters define The series of delay times and each
+ * may have an optional randomization value separated from the
+ * delay time by a colon.
+ *<p>
+ * Examples:
+ * <ul>
+ * <li>3 retires with no randomization value which means its 0:
+ * <ul><li><code>"1000, 2000, 3000"</code></ul>
+ *
+ * <li>10 retires with a 500 default randomization value for each and
+ * the 4..10 retries all using 3000 as the delay:
+ * <ul><li><code>"max_retries=10, default_randomization=500, 1000, 2000, 3000"</code></ul>
+ *
+ * <li>4 retires with a 100 as the default randomization value for the first 2 values and
+ * the other two having specified values of 500:
+ * <ul><li><code>"default_randomization=100, 1000, 2000, 4000:500, 5000:500"</code></ul>
+ *
+ * <li>Infinite number of retires with the first one at 1000, the second at 2000 all
+ * others will be at 3000.
+ * <ul><li><code>"max_retries=infinite,1000,2000,3000</code></ul>
+ * </ul>
+ *
+ * {@hide}
+ */
+public class RetryManager {
+ static public final String LOG_TAG = "RetryManager";
+ static public final boolean DBG = false;
+ static public final int RETRYIES_NOT_STARTED = 0;
+ static public final int RETRYIES_ON_GOING = 1;
+ static public final int RETRYIES_COMPLETED = 2;
+
+ /**
+ * Retry record with times in milli-seconds
+ */
+ private static class RetryRec {
+ RetryRec(int delayTime, int randomizationTime) {
+ mDelayTime = delayTime;
+ mRandomizationTime = randomizationTime;
+ }
+
+ int mDelayTime;
+ int mRandomizationTime;
+ }
+
+ /** The array of retry records */
+ private ArrayList<RetryRec> mRetryArray = new ArrayList<RetryRec>();
+
+ /** When true isRetryNeeded() will always return true */
+ private boolean mRetryForever;
+
+ /**
+ * The maximum number of retries to attempt before
+ * isRetryNeeded returns false
+ */
+ private int mMaxRetryCount;
+
+ /** The current number of retires */
+ private int mRetryCount;
+
+ /** Random number generator */
+ private Random rng = new Random();
+
+ /** Constructor */
+ public RetryManager() {
+ if (DBG) log("constructor");
+ }
+
+ /**
+ * Configure for a simple linear sequence of times plus
+ * a random value.
+ *
+ * @param maxRetryCount is the maximum number of retries
+ * before isRetryNeeded returns false.
+ * @param retryTime is a time that will be returned by getRetryTime.
+ * @param randomizationTime a random value between 0 and
+ * randomizationTime will be added to retryTime. this
+ * parameter may be 0.
+ * @return true if successfull
+ */
+ public boolean configure(int maxRetryCount, int retryTime, int randomizationTime) {
+ Pair<Boolean, Integer> value;
+
+ if (DBG) log("configure: " + maxRetryCount + ", " + retryTime + "," + randomizationTime);
+
+ if (!validateNonNegativeInt("maxRetryCount", maxRetryCount)) {
+ return false;
+ }
+
+ if (!validateNonNegativeInt("retryTime", retryTime)) {
+ return false;
+ }
+
+ if (!validateNonNegativeInt("randomizationTime", randomizationTime)) {
+ return false;
+ }
+
+ mMaxRetryCount = maxRetryCount;
+ resetRetryCount();
+ mRetryArray.clear();
+ mRetryArray.add(new RetryRec(retryTime, randomizationTime));
+
+ return true;
+ }
+
+ /**
+ * Configure for using string which allow arbitary
+ * sequences of times. See class comments for the
+ * string format.
+ *
+ * @return true if successfull
+ */
+ public boolean configure(String configStr) {
+ if (DBG) log("configure: '" + configStr + "'");
+
+ if (!TextUtils.isEmpty(configStr)) {
+ int defaultRandomization = 0;
+
+ if (DBG) log("configure: not empty");
+
+ mMaxRetryCount = 0;
+ resetRetryCount();
+ mRetryArray.clear();
+
+ String strArray[] = configStr.split(",");
+ for (int i = 0; i < strArray.length; i++) {
+ if (DBG) log("configure: strArray[" + i + "]='" + strArray[i] + "'");
+ Pair<Boolean, Integer> value;
+ String splitStr[] = strArray[i].split("=", 2);
+ splitStr[0] = splitStr[0].trim();
+ if (DBG) log("configure: splitStr[0]='" + splitStr[0] + "'");
+ if (splitStr.length > 1) {
+ splitStr[1] = splitStr[1].trim();
+ if (DBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+ if (TextUtils.equals(splitStr[0], "default_randomization")) {
+ value = parseNonNegativeInt(splitStr[0], splitStr[1]);
+ if (!value.first) return false;
+ defaultRandomization = value.second;
+ } else if (TextUtils.equals(splitStr[0], "max_retries")) {
+ if (TextUtils.equals("infinite",splitStr[1])) {
+ mRetryForever = true;
+ } else {
+ value = parseNonNegativeInt(splitStr[0], splitStr[1]);
+ if (!value.first) return false;
+ mMaxRetryCount = value.second;
+ }
+ } else {
+ Log.e(LOG_TAG, "Unrecognized configuration name value pair: "
+ + strArray[i]);
+ return false;
+ }
+ } else {
+ /**
+ * Assume a retry time with an optional randomization value
+ * following a ":"
+ */
+ splitStr = strArray[i].split(":", 2);
+ splitStr[0] = splitStr[0].trim();
+ RetryRec rr = new RetryRec(0, 0);
+ value = parseNonNegativeInt("delayTime", splitStr[0]);
+ if (!value.first) return false;
+ rr.mDelayTime = value.second;
+
+ // Check if optional randomization value present
+ if (splitStr.length > 1) {
+ splitStr[1] = splitStr[1].trim();
+ if (DBG) log("configure: splitStr[1]='" + splitStr[1] + "'");
+ value = parseNonNegativeInt("randomizationTime", splitStr[1]);
+ if (!value.first) return false;
+ rr.mRandomizationTime = value.second;
+ } else {
+ rr.mRandomizationTime = defaultRandomization;
+ }
+ mRetryArray.add(rr);
+ }
+ }
+ if (mRetryArray.size() > mMaxRetryCount) {
+ mMaxRetryCount = mRetryArray.size();
+ if (DBG) log("configure: setting mMaxRetryCount=" + mMaxRetryCount);
+ }
+ if (DBG) log("configure: true");
+ return true;
+ } else {
+ if (DBG) log("configure: false it's empty");
+ return false;
+ }
+ }
+
+ /**
+ * Report whether data reconnection should be retried
+ *
+ * @return {@code true} if the max retires has not been reached. {@code
+ * false} otherwise.
+ */
+ public boolean isRetryNeeded() {
+ boolean retVal = mRetryForever || (mRetryCount < mMaxRetryCount);
+ if (DBG) log("isRetryNeeded: " + retVal);
+ return retVal;
+ }
+
+ /**
+ * Return the timer that should be used to trigger the data reconnection
+ */
+ public int getRetryTimer() {
+ int index;
+ if (mRetryCount < mRetryArray.size()) {
+ index = mRetryCount;
+ } else {
+ index = mRetryArray.size() - 1;
+ }
+
+ int retVal;
+ if ((index >= 0) && (index < mRetryArray.size())) {
+ retVal = mRetryArray.get(index).mDelayTime + nextRandomizationTime(index);
+ } else {
+ retVal = 0;
+ }
+
+ if (DBG) log("getRetryTimer: " + retVal);
+ return retVal;
+ }
+
+ /**
+ * @return retry count
+ */
+ public int getRetryCount() {
+ if (DBG) log("getRetryCount: " + mRetryCount);
+ return mRetryCount;
+ }
+
+ /**
+ * Increase the retry counter, does not change retry forever.
+ */
+ public void increaseRetryCount() {
+ mRetryCount++;
+ if (mRetryCount > mMaxRetryCount) {
+ mRetryCount = mMaxRetryCount;
+ }
+ if (DBG) log("increseRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Set retry count to the specified value
+ * and turns off retrying forever.
+ */
+ public void setRetryCount(int count) {
+ mRetryCount = count;
+ if (mRetryCount > mMaxRetryCount) {
+ mRetryCount = mMaxRetryCount;
+ }
+
+ if (mRetryCount < 0) {
+ mRetryCount = 0;
+ }
+
+ mRetryForever = false;
+ if (DBG) log("setRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Reset network re-registration indicator and clear the data-retry counter
+ * and turns off retrying forever.
+ */
+ public void resetRetryCount() {
+ mRetryCount = 0;
+ mRetryForever = false;
+ if (DBG) log("resetRetryCount: " + mRetryCount);
+ }
+
+ /**
+ * Retry forever using last timeout time.
+ */
+ public void retryForeverUsingLastTimeout() {
+ mRetryCount = mMaxRetryCount;
+ mRetryForever = true;
+ if (DBG) log("retryForeverUsingLastTimeout: " + mRetryForever + ", " + mRetryCount);
+ }
+
+ /**
+ * @return true if retrying forever
+ */
+ public boolean isRetryForever() {
+ if (DBG) log("isRetryForever: " + mRetryForever);
+ return mRetryForever;
+ }
+
+ /**
+ * Parse an integer validating the value is not negative.
+ *
+ * @param name
+ * @param stringValue
+ * @return Pair.first == true if stringValue an integer >= 0
+ */
+ private Pair<Boolean, Integer> parseNonNegativeInt(String name, String stringValue) {
+ int value;
+ Pair<Boolean, Integer> retVal;
+ try {
+ value = Integer.parseInt(stringValue);
+ retVal = new Pair<Boolean, Integer>(validateNonNegativeInt(name, value), value);
+ } catch (NumberFormatException e) {
+ Log.e(LOG_TAG, name + " bad value: " + stringValue, e);
+ retVal = new Pair<Boolean, Integer>(false, 0);
+ }
+ if (DBG) log("parseNonNetativeInt: " + name + ", " + stringValue + ", "
+ + retVal.first + ", " + retVal.second);
+ return retVal;
+ }
+
+ /**
+ * Validate an integer is >= 0 and logs an error if not
+ *
+ * @param name
+ * @param value
+ * @return Pair.first
+ */
+ private boolean validateNonNegativeInt(String name, int value) {
+ boolean retVal;
+ if (value < 0) {
+ Log.e(LOG_TAG, name + " bad value: is < 0");
+ retVal = false;
+ } else {
+ retVal = true;
+ }
+ if (DBG) log("validateNonNegative: " + name + ", " + value + ", " + retVal);
+ return retVal;
+ }
+
+ /**
+ * Return next random number for the index
+ */
+ private int nextRandomizationTime(int index) {
+ int randomTime = mRetryArray.get(index).mRandomizationTime;
+ if (randomTime == 0) {
+ return 0;
+ } else {
+ return rng.nextInt(randomTime);
+ }
+ }
+
+ private void log(String s) {
+ Log.d(LOG_TAG, s);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index 890ea63..d26a092 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -61,6 +61,7 @@ import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
+import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
public abstract class SMSDispatcher extends Handler {
@@ -105,6 +106,9 @@ public abstract class SMSDispatcher extends Handler {
/** Alert is timeout */
static final protected int EVENT_ALERT_TIMEOUT = 9;
+ /** Stop the sending */
+ static final protected int EVENT_STOP_SENDING = 10;
+
protected Phone mPhone;
protected Context mContext;
protected ContentResolver mResolver;
@@ -120,6 +124,8 @@ public abstract class SMSDispatcher extends Handler {
private static final int SEND_RETRY_DELAY = 2000;
/** single part SMS */
private static final int SINGLE_PART_SMS = 1;
+ /** Message sending queue limit */
+ private static final int MO_MSG_QUEUE_LIMIT = 5;
/**
* Message reference for a CONCATENATED_8_BIT_REFERENCE or
@@ -130,7 +136,7 @@ public abstract class SMSDispatcher extends Handler {
private SmsCounter mCounter;
- private SmsTracker mSTracker;
+ private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT);
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
@@ -144,7 +150,8 @@ public abstract class SMSDispatcher extends Handler {
private static SmsMessage mSmsMessage;
private static SmsMessageBase mSmsMessageBase;
private SmsMessageBase.SubmitPduBase mSubmitPduBase;
- private boolean mStorageAvailable = true;
+
+ protected boolean mStorageAvailable = true;
protected static int getNextConcatenatedRef() {
sConcatenatedRef += 1;
@@ -214,7 +221,6 @@ public abstract class SMSDispatcher extends Handler {
mContext = phone.getContext();
mResolver = mContext.getContentResolver();
mCm = phone.mCM;
- mSTracker = null;
createWakelock();
@@ -289,19 +295,15 @@ public abstract class SMSDispatcher extends Handler {
sms = (SmsMessage) ar.result;
try {
- if (mStorageAvailable) {
- int result = dispatchMessage(sms.mWrappedSmsMessage);
- if (result != Activity.RESULT_OK) {
- // RESULT_OK means that message was broadcast for app(s) to handle.
- // Any other result, we should ack here.
- boolean handled = (result == Intents.RESULT_SMS_HANDLED);
- acknowledgeLastIncomingSms(handled, result, null);
- }
- } else {
- acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_OUT_OF_MEMORY, null);
+ int result = dispatchMessage(sms.mWrappedSmsMessage);
+ if (result != Activity.RESULT_OK) {
+ // RESULT_OK means that message was broadcast for app(s) to handle.
+ // Any other result, we should ack here.
+ boolean handled = (result == Intents.RESULT_SMS_HANDLED);
+ notifyAndAcknowledgeLastIncomingSms(handled, result, null);
}
} catch (RuntimeException ex) {
- acknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
+ notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null);
}
break;
@@ -330,17 +332,41 @@ public abstract class SMSDispatcher extends Handler {
case EVENT_ALERT_TIMEOUT:
((AlertDialog)(msg.obj)).dismiss();
msg.obj = null;
- mSTracker = null;
+ if (mSTrackers.isEmpty() == false) {
+ try {
+ SmsTracker sTracker = (SmsTracker)mSTrackers.remove(0);
+ sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
+ } catch (CanceledException ex) {
+ Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
+ }
+ }
+ if (Config.LOGD) {
+ Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending");
+ }
break;
case EVENT_SEND_CONFIRMED_SMS:
- if (mSTracker!=null) {
- if (isMultipartTracker(mSTracker)) {
- sendMultipartSms(mSTracker);
+ if (mSTrackers.isEmpty() == false) {
+ SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+ if (isMultipartTracker(sTracker)) {
+ sendMultipartSms(sTracker);
} else {
- sendSms(mSTracker);
+ sendSms(sTracker);
+ }
+ removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
+ }
+ break;
+
+ case EVENT_STOP_SENDING:
+ if (mSTrackers.isEmpty() == false) {
+ // Remove the latest one.
+ try {
+ SmsTracker sTracker = (SmsTracker)mSTrackers.remove(mSTrackers.size() - 1);
+ sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
+ } catch (CanceledException ex) {
+ Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
}
- mSTracker = null;
+ removeMessages(EVENT_ALERT_TIMEOUT, msg.obj);
}
break;
}
@@ -445,7 +471,11 @@ public abstract class SMSDispatcher extends Handler {
} else if (tracker.mSentIntent != null) {
// Done retrying; return an error to the app.
try {
- tracker.mSentIntent.send(RESULT_ERROR_GENERIC_FAILURE);
+ Intent fillIn = new Intent();
+ if (ar.result != null) {
+ fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
+ }
+ tracker.mSentIntent.send(mContext, RESULT_ERROR_GENERIC_FAILURE, fillIn);
} catch (CanceledException ex) {}
}
}
@@ -602,12 +632,66 @@ public abstract class SMSDispatcher extends Handler {
dispatch(intent, "android.permission.RECEIVE_SMS");
}
+ /**
+ * Send a data based SMS to a specific application port.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param destPort the port to deliver the message to
+ * @param data the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applicaitons,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ protected abstract void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
+
+ /**
+ * Send a text based SMS.
+ *
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
+ * the current default SMSC
+ * @param text the body of the message to send
+ * @param sentIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is sucessfully sent, or failed.
+ * The result code will be <code>Activity.RESULT_OK<code> for success,
+ * or one of these errors:<br>
+ * <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+ * <code>RESULT_ERROR_RADIO_OFF</code><br>
+ * <code>RESULT_ERROR_NULL_PDU</code><br>
+ * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+ * the extra "errorCode" containing a radio technology specific value,
+ * generally only useful for troubleshooting.<br>
+ * The per-application based SMS control checks sentIntent. If sentIntent
+ * is NULL the caller will be checked against all unknown applications,
+ * which cause smaller number of SMS to be sent in checking period.
+ * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
+ * broadcast when the message is delivered to the recipient. The
+ * raw pdu of the status report is in the extended data ("pdu").
+ */
+ protected abstract void sendText(String destAddr, String scAddr,
+ String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
/**
* Send a multi-part text based SMS.
*
- * @param destinationAddress the address to send the message to
- * @param scAddress is the service center address or null to use
+ * @param destAddr the address to send the message to
+ * @param scAddr is the service center address or null to use
* the current default SMSC
* @param parts an <code>ArrayList</code> of strings that, in order,
* comprise the original message
@@ -628,7 +712,7 @@ public abstract class SMSDispatcher extends Handler {
* to the recipient. The raw pdu of the status report is in the
* extended data ("pdu").
*/
- protected abstract void sendMultipartText(String destinationAddress, String scAddress,
+ protected abstract void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents);
@@ -689,6 +773,15 @@ public abstract class SMSDispatcher extends Handler {
* An SmsTracker for the current message.
*/
protected void handleReachSentLimit(SmsTracker tracker) {
+ if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) {
+ // Deny the sending when the queue limit is reached.
+ try {
+ tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED);
+ } catch (CanceledException ex) {
+ Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED");
+ }
+ return;
+ }
Resources r = Resources.getSystem();
@@ -698,13 +791,13 @@ public abstract class SMSDispatcher extends Handler {
.setTitle(r.getString(R.string.sms_control_title))
.setMessage(appName + " " + r.getString(R.string.sms_control_message))
.setPositiveButton(r.getString(R.string.sms_control_yes), mListener)
- .setNegativeButton(r.getString(R.string.sms_control_no), null)
+ .setNegativeButton(r.getString(R.string.sms_control_no), mListener)
.create();
d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
d.show();
- mSTracker = tracker;
+ mSTrackers.add(tracker);
sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d),
DEFAULT_SMS_TIMOUEOUT);
}
@@ -770,6 +863,25 @@ public abstract class SMSDispatcher extends Handler {
int result, Message response);
/**
+ * Notify interested apps if the framework has rejected an incoming SMS,
+ * and send an acknowledge message to the network.
+ * @param success indicates that last message was successfully received.
+ * @param result result code indicating any error
+ * @param response callback message sent when operation completes.
+ */
+ private void notifyAndAcknowledgeLastIncomingSms(boolean success,
+ int result, Message response) {
+ if (!success) {
+ // broadcast SMS_REJECTED_ACTION intent
+ Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
+ intent.putExtra("result", result);
+ mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
+ mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS");
+ }
+ acknowledgeLastIncomingSms(success, result, response);
+ }
+
+ /**
* Check if a SmsTracker holds multi-part Sms
*
* @param tracker a SmsTracker could hold a multi-part Sms
@@ -815,6 +927,9 @@ public abstract class SMSDispatcher extends Handler {
if (which == DialogInterface.BUTTON_POSITIVE) {
Log.d(TAG, "click YES to send out sms");
sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS));
+ } else if (which == DialogInterface.BUTTON_NEGATIVE) {
+ Log.d(TAG, "click NO to stop sending");
+ sendMessage(obtainMessage(EVENT_STOP_SENDING));
}
}
};
diff --git a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
index bdcf3f7..6892998 100644
--- a/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -28,12 +28,9 @@ import android.telephony.SignalStrength;
* {@hide}
*/
public abstract class ServiceStateTracker extends Handler {
+
/**
- * The access technology currently in use:
- * 0 = unknown
- * 1 = GPRS only
- * 2 = EDGE
- * 3 = UMTS
+ * Access technology currently in use.
*/
protected static final int DATA_ACCESS_UNKNOWN = 0;
protected static final int DATA_ACCESS_GPRS = 1;
@@ -44,7 +41,9 @@ public abstract class ServiceStateTracker extends Handler {
protected static final int DATA_ACCESS_CDMA_1xRTT = 6;
protected static final int DATA_ACCESS_CDMA_EvDo_0 = 7;
protected static final int DATA_ACCESS_CDMA_EvDo_A = 8;
- //***** Instance Variables
+ protected static final int DATA_ACCESS_HSDPA = 9;
+ protected static final int DATA_ACCESS_HSUPA = 10;
+ protected static final int DATA_ACCESS_HSPA = 11;
protected CommandsInterface cm;
@@ -53,34 +52,36 @@ public abstract class ServiceStateTracker extends Handler {
public SignalStrength mSignalStrength;
- // Used as a unique identifier to track requests associated with a poll
- // and ignore stale responses.The value is a count-down of expected responses
- // in this pollingContext
+ /**
+ * A unique identifier to track requests associated with a poll
+ * and ignore stale responses. The value is a count-down of
+ * expected responses in this pollingContext.
+ */
protected int[] pollingContext;
protected boolean mDesiredPowerState;
- protected boolean dontPollSignalStrength = false; // Default is to poll strength
- // If we're getting unsolicited signal strength updates from the radio,
- // set value to true and don't bother polling any more
+ /**
+ * By default, strength polling is enabled. However, if we're
+ * getting unsolicited signal strength updates from the radio, set
+ * value to true and don't bother polling any more.
+ */
+ protected boolean dontPollSignalStrength = false;
protected RegistrantList networkAttachedRegistrants = new RegistrantList();
protected RegistrantList roamingOnRegistrants = new RegistrantList();
protected RegistrantList roamingOffRegistrants = new RegistrantList();
- //***** Constants
-
protected static final boolean DBG = true;
- // signal strength poll rate
+ /** Signal strength poll rate. */
protected static final int POLL_PERIOD_MILLIS = 20 * 1000;
- // waiting period before recheck gprs and voice registration
+ /** Waiting period before recheck gprs and voice registration. */
public static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
- public static final int MAX_NUM_DATA_STATE_READS = 15;
public static final int DATA_STATE_POLL_SLEEP_MS = 100;
- //*****GSM events
+ /** GSM events */
protected static final int EVENT_RADIO_STATE_CHANGED = 1;
protected static final int EVENT_NETWORK_STATE_CHANGED = 2;
protected static final int EVENT_GET_SIGNAL_STRENGTH = 3;
@@ -102,7 +103,7 @@ public abstract class ServiceStateTracker extends Handler {
protected static final int EVENT_CHECK_REPORT_GPRS = 22;
protected static final int EVENT_RESTRICTED_STATE_CHANGED = 23;
- //*****CDMA events:
+ /** CDMA events */
protected static final int EVENT_POLL_STATE_REGISTRATION_CDMA = 24;
protected static final int EVENT_POLL_STATE_OPERATOR_CDMA = 25;
protected static final int EVENT_RUIM_READY = 26;
@@ -116,14 +117,17 @@ public abstract class ServiceStateTracker extends Handler {
protected static final int EVENT_POLL_STATE_CDMA_SUBSCRIPTION = 34;
protected static final int EVENT_NV_READY = 35;
protected static final int EVENT_ERI_FILE_LOADED = 36;
+ protected static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 37;
+ protected static final int EVENT_SET_RADIO_POWER_OFF = 38;
- //***** Time Zones
protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
- // List of ISO codes for countries that can have an offset of GMT+0
- // when not in daylight savings time. This ignores some small places
- // such as the Canary Islands (Spain) and Danmarkshavn (Denmark).
- // The list must be sorted by code.
+ /**
+ * List of ISO codes for countries that can have an offset of
+ * GMT+0 when not in daylight savings time. This ignores some
+ * small places such as the Canary Islands (Spain) and
+ * Danmarkshavn (Denmark). The list must be sorted by code.
+ */
protected static final String[] GMT_COUNTRY_CODES = {
"bf", // Burkina Faso
"ci", // Cote d'Ivoire
@@ -147,11 +151,10 @@ public abstract class ServiceStateTracker extends Handler {
"uk", // U.K
};
- //***** Registration denied reason
+ /** Reason for registration denial. */
protected static final String REGISTRATION_DENIED_GEN = "General";
protected static final String REGISTRATION_DENIED_AUTH = "Authentication Failure";
- //***** Constructors
public ServiceStateTracker() {
}
@@ -216,8 +219,6 @@ public abstract class ServiceStateTracker extends Handler {
obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE, onComplete));
}
-
- //***** Called from Phone
public void
setRadioPower(boolean power) {
mDesiredPowerState = power;
@@ -225,26 +226,54 @@ public abstract class ServiceStateTracker extends Handler {
setPowerStateToDesired();
}
+ /**
+ * These two flags manage the behavior of the cell lock -- the
+ * lock should be held if either flag is true. The intention is
+ * to allow temporary aquisition of the lock to get a single
+ * update. Such a lock grab and release can thus be made to not
+ * interfere with more permanent lock holds -- in other words, the
+ * lock will only be released if both flags are false, and so
+ * releases by temporary users will only affect the lock state if
+ * there is no continuous user.
+ */
+ private boolean mWantContinuousLocationUpdates;
+ private boolean mWantSingleLocationUpdate;
+
+ public void enableSingleLocationUpdate() {
+ if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
+ mWantSingleLocationUpdate = true;
+ cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
+ }
public void enableLocationUpdates() {
+ if (mWantSingleLocationUpdate || mWantContinuousLocationUpdates) return;
+ mWantContinuousLocationUpdates = true;
cm.setLocationUpdates(true, obtainMessage(EVENT_LOCATION_UPDATES_ENABLED));
}
+ protected void disableSingleLocationUpdate() {
+ mWantSingleLocationUpdate = false;
+ if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
+ cm.setLocationUpdates(false, null);
+ }
+ }
+
public void disableLocationUpdates() {
- cm.setLocationUpdates(false, null);
+ mWantContinuousLocationUpdates = false;
+ if (!mWantSingleLocationUpdate && !mWantContinuousLocationUpdates) {
+ cm.setLocationUpdates(false, null);
+ }
}
- //***** Overridden from Handler
public abstract void handleMessage(Message msg);
- //***** Protected abstract Methods
protected abstract void handlePollStateResult(int what, AsyncResult ar);
protected abstract void updateSpnDisplay();
protected abstract void setPowerStateToDesired();
/** Cancel a pending (if any) pollState() operation */
protected void cancelPollState() {
- // This will effectively cancel the rest of the poll requests
+ // This will effectively cancel the rest of the poll requests.
pollingContext = new int[1];
}
}
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 8b9ccb4..3f0213b 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -116,6 +116,16 @@ public abstract class SmsMessageBase {
* android.telephony.SmsMessage ENCODING_*).
*/
public int codeUnitSize;
+
+ @Override
+ public String toString() {
+ return "TextEncodingDetails " +
+ "{ msgCount=" + msgCount +
+ ", codeUnitCount=" + codeUnitCount +
+ ", codeUnitsRemaining=" + codeUnitsRemaining +
+ ", codeUnitSize=" + codeUnitSize +
+ " }";
+ }
}
// TODO(): This class is duplicated in SmsMessage.java. Refactor accordingly.
@@ -353,60 +363,30 @@ public abstract class SmsMessageBase {
}
/**
- * Try to parse this message as an email gateway message -> Neither
- * of the standard ways are currently supported: There are two ways
- * specified in TS 23.040 Section 3.8 (not supported via this mechanism) -
- * SMS message "may have its TP-PID set for internet electronic mail - MT
+ * Try to parse this message as an email gateway message
+ * There are two ways specified in TS 23.040 Section 3.8 :
+ * - SMS message "may have its TP-PID set for internet electronic mail - MT
* SMS format: [<from-address><space>]<message> - "Depending on the
* nature of the gateway, the destination/origination address is either
* derived from the content of the SMS TP-OA or TP-DA field, or the
* TP-OA/TP-DA field contains a generic gateway address and the to/from
- * address is added at the beginning as shown above." - multiple addreses
- * separated by commas, no spaces - subject field delimited by '()' or '##'
- * and '#' Section 9.2.3.24.11
+ * address is added at the beginning as shown above." (which is supported here)
+ * - Multiple addreses separated by commas, no spaces, Subject field delimited
+ * by '()' or '##' and '#' Section 9.2.3.24.11 (which are NOT supported here)
*/
protected void extractEmailAddressFromMessageBody() {
- /*
- * a little guesswork here. I haven't found doc for this.
- * the format could be either
+ /* Some carriers may use " /" delimiter as below
*
* 1. [x@y][ ]/[subject][ ]/[body]
* -or-
* 2. [x@y][ ]/[body]
*/
- int slash = 0, slash2 = 0, atSymbol = 0;
-
- try {
- slash = messageBody.indexOf(" /");
- if (slash == -1) {
- return;
- }
-
- atSymbol = messageBody.indexOf('@');
- if (atSymbol == -1 || atSymbol > slash) {
- return;
- }
-
- emailFrom = messageBody.substring(0, slash);
-
- slash2 = messageBody.indexOf(" /", slash + 2);
-
- if (slash2 == -1) {
- pseudoSubject = null;
- emailBody = messageBody.substring(slash + 2);
- } else {
- pseudoSubject = messageBody.substring(slash + 2, slash2);
- emailBody = messageBody.substring(slash2 + 2);
- }
-
- isEmail = true;
- } catch (Exception ex) {
- Log.w(LOG_TAG,
- "extractEmailAddressFromMessageBody: exception slash="
- + slash + ", atSymbol=" + atSymbol + ", slash2="
- + slash2, ex);
- }
+ String[] parts = messageBody.split("( /)|( )", 2);
+ if (parts.length < 1) return;
+ emailFrom = parts[0];
+ emailBody = parts[1];
+ isEmail = true;
}
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 02e9800..2216ec4 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -220,28 +220,4 @@ public class TelephonyIntents {
*/
public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
= "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
-
- /**
- * Broadcast Action: The MDN changed during the CDMA OTA Process
- * The intent will have the following extra values:</p>
- * <ul>
- * <li><em>mdn</em> - An Integer of the updated MDN number.</li>
- * </ul>
- *
- * <p class="note">This is a protected intent that can only be sent
- * by the system.
- *
- * <p class="note">
- */
- // TODO(Moto): Generally broadcast intents are for use to allow entities which
- // may not know about each other to "communicate". This seems quite specific
- // and maybe using the registrant style would be better.
-
- // Moto: Since this is used for apps not in the same process of phone, can the
- // registrant style be used? (Ling Li says: Maybe the "app" can request rather
- // than save the MDN each time and this intent would not be necessary?)
- // Moto response: Moto internal discussion is on-going.
- public static final String ACTION_CDMA_OTA_MDN_CHANGED
- = "android.intent.action.ACTION_MDN_STATE_CHANGED";
-
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 5ec4020..55ba149 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -109,4 +109,31 @@ public interface TelephonyProperties
/** The international dialing prefix conversion string */
static final String PROPERTY_IDP_STRING = "ro.cdma.idpstring";
+
+ /**
+ * Defines the schema for the carrier specified OTASP number
+ */
+ static final String PROPERTY_OTASP_NUM_SCHEMA = "ro.cdma.otaspnumschema";
+
+ /**
+ * Disable all calls including Emergency call when it set to true.
+ */
+ static final String PROPERTY_DISABLE_CALL = "ro.telephony.disable-call";
+
+ /**
+ * Set to true for vendor RIL's that send multiple UNSOL_CALL_RING notifications.
+ */
+ static final String PROPERTY_RIL_SENDS_MULTIPLE_CALL_RING =
+ "ro.telephony.call_ring.multiple";
+
+ /**
+ * The number of milli-seconds between CALL_RING notifications.
+ */
+ static final String PROPERTY_CALL_RING_DELAY = "ro.telephony.call_ring.delay";
+
+ /**
+ * Track CDMA SMS message id numbers to ensure they increment
+ * monotonically, regardless of reboots.
+ */
+ static final String PROPERTY_CDMA_MSG_ID = "persist.radio.cdma.msgid";
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 23eedfe..dfc4889 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -26,14 +26,14 @@ import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
-import android.provider.Settings;
import android.provider.Telephony;
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
@@ -42,15 +42,13 @@ import android.telephony.SignalStrength;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DataConnection;
-// TODO(Moto): need to move MccTable from telephony.gsm to telephony
-// since there is no difference between CDMA and GSM for MccTable and
-// CDMA uses gsm's MccTable is not good.
-import com.android.internal.telephony.gsm.MccTable;
+import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.IccException;
import com.android.internal.telephony.IccFileHandler;
@@ -71,34 +69,41 @@ import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OP
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
+
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
/**
* {@hide}
*/
public class CDMAPhone extends PhoneBase {
static final String LOG_TAG = "CDMA";
- private static final boolean LOCAL_DEBUG = true;
+ private static final boolean DBG = true;
// Default Emergency Callback Mode exit timer
- private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 30000;
+ private static final int DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
+
static final String VM_COUNT_CDMA = "vm_count_key_cdma";
private static final String VM_NUMBER_CDMA = "vm_number_key_cdma";
private String mVmNumber = null;
- //***** Instance Variables
+ static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
+ static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
+
+ // Instance Variables
CdmaCallTracker mCT;
CdmaSMSDispatcher mSMS;
CdmaServiceStateTracker mSST;
- CdmaDataConnectionTracker mDataConnection;
RuimFileHandler mRuimFileHandler;
RuimRecords mRuimRecords;
RuimCard mRuimCard;
- MyHandler h;
RuimPhoneBookInterfaceManager mRuimPhoneBookInterfaceManager;
RuimSmsInterfaceManager mRuimSmsInterfaceManager;
PhoneSubInfo mSubInfo;
EriManager mEriManager;
+ WakeLock mWakeLock;
+
// mNvLoadedRegistrants are informed after the EVENT_NV_READY
private RegistrantList mNvLoadedRegistrants = new RegistrantList();
@@ -106,15 +111,20 @@ public class CDMAPhone extends PhoneBase {
// mEriFileLoadedRegistrants are informed after the ERI text has been loaded
private RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
- // mECMExitRespRegistrant is informed after the phone has been exited
+ // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started
+ private RegistrantList mEcmTimerResetRegistrants = new RegistrantList();
+
+ // mEcmExitRespRegistrant is informed after the phone has been exited
//the emergency callback mode
//keep track of if phone is in emergency callback mode
- private boolean mIsPhoneInECMState;
- private Registrant mECMExitRespRegistrant;
+ private boolean mIsPhoneInEcmState;
+ private Registrant mEcmExitRespRegistrant;
private String mEsn;
private String mMeid;
+ // string to define how the carrier specifies its own ota sp number
+ private String mCarrierOtaSpNumSchema;
- // A runnable which is used to automatically exit from ECM after a period of time.
+ // A runnable which is used to automatically exit from Ecm after a period of time.
private Runnable mExitEcmRunnable = new Runnable() {
public void run() {
exitEmergencyCallbackMode();
@@ -124,17 +134,14 @@ public class CDMAPhone extends PhoneBase {
Registrant mPostDialHandler;
- //***** Constructors
+ // Constructors
public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) {
this(context,ci,notifier, false);
}
public CDMAPhone(Context context, CommandsInterface ci, PhoneNotifier notifier,
boolean unitTestMode) {
- super(notifier, context, unitTestMode);
-
- h = new MyHandler();
- mCM = ci;
+ super(notifier, context, ci, unitTestMode);
mCM.setPhoneType(RILConstants.CDMA_PHONE);
mCT = new CdmaCallTracker(this);
@@ -149,16 +156,18 @@ public class CDMAPhone extends PhoneBase {
mSubInfo = new PhoneSubInfo(this);
mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML);
- mCM.registerForAvailable(h, EVENT_RADIO_AVAILABLE, null);
- mRuimRecords.registerForRecordsLoaded(h, EVENT_RUIM_RECORDS_LOADED, null);
- mCM.registerForOffOrNotAvailable(h, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
- mCM.registerForOn(h, EVENT_RADIO_ON, null);
- mCM.setOnSuppServiceNotification(h, EVENT_SSN, null);
- mCM.setOnCallRing(h, EVENT_CALL_RING, null);
- mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null);
- mCM.registerForNVReady(h, EVENT_NV_READY, null);
- mCM.setEmergencyCallbackMode(h, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
+ mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
+ mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null);
+ mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mCM.registerForOn(this, EVENT_RADIO_ON, null);
+ mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
+ mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);
+ mCM.registerForNVReady(this, EVENT_NV_READY, null);
+ mCM.setEmergencyCallbackMode(this, EVENT_EMERGENCY_CALLBACK_MODE_ENTER, null);
+ PowerManager pm
+ = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,LOG_TAG);
//Change the system setting
SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE,
@@ -166,7 +175,15 @@ public class CDMAPhone extends PhoneBase {
// This is needed to handle phone process crashes
String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
- mIsPhoneInECMState = inEcm.equals("true");
+ mIsPhoneInEcmState = inEcm.equals("true");
+ if (mIsPhoneInEcmState) {
+ // Send a message which will invoke handleExitEmergencyCallbackMode
+ mCM.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
+ }
+
+ // get the string that specifies the carrier OTA Sp number
+ mCarrierOtaSpNumSchema = SystemProperties.get(
+ TelephonyProperties.PROPERTY_OTASP_NUM_SCHEMA,"");
// Sets operator alpha property by retrieving from build-time system property
String operatorAlpha = SystemProperties.get("ro.cdma.home.operator.alpha");
@@ -183,7 +200,8 @@ public class CDMAPhone extends PhoneBase {
updateCurrentCarrierInProvider(operatorNumeric);
// Updates MCC MNC device configuration information
- updateMccMncConfiguration(operatorNumeric);
+ MccTable.updateMccMncConfiguration(this, operatorNumeric);
+
// Notify voicemails.
notifier.notifyMessageWaitingChanged(this);
@@ -191,17 +209,16 @@ public class CDMAPhone extends PhoneBase {
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ super.dispose();
//Unregister from all former registered events
- mRuimRecords.unregisterForRecordsLoaded(h); //EVENT_RUIM_RECORDS_LOADED
- mCM.unregisterForAvailable(h); //EVENT_RADIO_AVAILABLE
- mCM.unregisterForOffOrNotAvailable(h); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
- mCM.unregisterForOn(h); //EVENT_RADIO_ON
- mCM.unregisterForNVReady(h); //EVENT_NV_READY
- mSST.unregisterForNetworkAttach(h); //EVENT_REGISTERED_TO_NETWORK
- mCM.unSetOnSuppServiceNotification(h);
- mCM.unSetOnCallRing(h);
-
+ mRuimRecords.unregisterForRecordsLoaded(this); //EVENT_RUIM_RECORDS_LOADED
+ mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
+ mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
+ mCM.unregisterForOn(this); //EVENT_RADIO_ON
+ mCM.unregisterForNVReady(this); //EVENT_NV_READY
+ mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
+ mCM.unSetOnSuppServiceNotification(this);
//Force all referenced classes to unregister their former registered events
mCT.dispose();
@@ -233,11 +250,13 @@ public class CDMAPhone extends PhoneBase {
}
protected void finalize() {
- if(LOCAL_DEBUG) Log.d(LOG_TAG, "CDMAPhone finalized");
+ if(DBG) Log.d(LOG_TAG, "CDMAPhone finalized");
+ if (mWakeLock.isHeld()) {
+ Log.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing.");
+ mWakeLock.release();
+ }
}
-
- //***** Overridden from Phone
public ServiceState getServiceState() {
return mSST.ss;
}
@@ -322,15 +341,6 @@ public class CDMAPhone extends PhoneBase {
dial (String dialString) throws CallStateException {
// Need to make sure dialString gets parsed properly
String newDialString = PhoneNumberUtils.stripSeparators(dialString);
-
- if (!mCT.foregroundCall.isIdle()) {
- FeatureCode fc = FeatureCode.newFromDialString(newDialString, this);
- if (fc != null) {
- //mMmiRegistrants.notifyRegistrants(new AsyncResult(null, fc, null));
- fc.processCode();
- return null;
- }
- }
return mCT.dial(newDialString);
}
@@ -358,42 +368,11 @@ public class CDMAPhone extends PhoneBase {
return mCT.backgroundCall;
}
- public String getGateway(String apnType) {
- return mDataConnection.getGateway();
- }
-
public boolean handleInCallMmiCommands(String dialString) {
Log.e(LOG_TAG, "method handleInCallMmiCommands is NOT supported in CDMA!");
return false;
}
- public int enableApnType(String type) {
- // This request is mainly used to enable MMS APN
- // In CDMA there is no need to enable/disable a different APN for MMS
- Log.d(LOG_TAG, "Request to enableApnType("+type+")");
- if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
- return Phone.APN_ALREADY_ACTIVE;
- } else {
- return Phone.APN_REQUEST_FAILED;
- }
- }
-
- public int disableApnType(String type) {
- // This request is mainly used to disable MMS APN
- // In CDMA there is no need to enable/disable a different APN for MMS
- Log.d(LOG_TAG, "Request to disableApnType("+type+")");
- if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
- return Phone.APN_REQUEST_STARTED;
- } else {
- return Phone.APN_REQUEST_FAILED;
- }
- }
-
- public String getActiveApn() {
- Log.d(LOG_TAG, "Request to getActiveApn()");
- return null;
- }
-
public void
setNetworkSelectionModeAutomatic(Message response) {
Log.e(LOG_TAG, "method setNetworkSelectionModeAutomatic is NOT supported in CDMA!");
@@ -423,13 +402,17 @@ public class CDMAPhone extends PhoneBase {
}
public String getCdmaPrlVersion(){
- return mRuimRecords.getPrlVersion();
+ return mSST.getPrlVersion();
}
- public String getCdmaMIN() {
+ public String getCdmaMin() {
return mSST.getCdmaMin();
}
+ public boolean isMinInfoReady() {
+ return mSST.isMinInfoReady();
+ }
+
public void getCallWaiting(Message onComplete) {
mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
}
@@ -466,10 +449,6 @@ public class CDMAPhone extends PhoneBase {
return false;
}
- public String getInterfaceName(String apnType) {
- return mDataConnection.getInterfaceName();
- }
-
public CellLocation getCellLocation() {
return mSST.cellLoc;
}
@@ -509,10 +488,6 @@ public class CDMAPhone extends PhoneBase {
Log.e(LOG_TAG, "setLine1Number: not possible in CDMA");
}
- public String[] getDnsServers(String apnType) {
- return mDataConnection.getDnsServers();
- }
-
public IccCard getIccCard() {
return mRuimCard;
}
@@ -525,8 +500,8 @@ public class CDMAPhone extends PhoneBase {
Log.e(LOG_TAG, "method setCallWaiting is NOT supported in CDMA!");
}
- public void updateServiceLocation(Message response) {
- mSST.getLacAndCid(response);
+ public void updateServiceLocation() {
+ mSST.enableSingleLocationUpdate();
}
public void setDataRoamingEnabled(boolean enable) {
@@ -541,12 +516,20 @@ public class CDMAPhone extends PhoneBase {
mCM.unregisterForCdmaOtaProvision(h);
}
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ mSST.registerForSubscriptionInfoReady(h, what, obj);
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ mSST.unregisterForSubscriptionInfoReady(h);
+ }
+
public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
- mECMExitRespRegistrant = new Registrant (h, what, obj);
+ mEcmExitRespRegistrant = new Registrant (h, what, obj);
}
public void unsetOnEcbModeExitResponse(Handler h) {
- mECMExitRespRegistrant.clear();
+ mEcmExitRespRegistrant.clear();
}
public void registerForCallWaiting(Handler h, int what, Object obj) {
@@ -557,10 +540,6 @@ public class CDMAPhone extends PhoneBase {
mCT.unregisterForCallWaiting(h);
}
- public String getIpAddress(String apnType) {
- return mDataConnection.getIpAddress();
- }
-
public void
getNeighboringCids(Message response) {
/*
@@ -674,14 +653,6 @@ public class CDMAPhone extends PhoneBase {
Log.e(LOG_TAG, "getAvailableNetworks: not possible in CDMA");
}
- public String[] getActiveApnTypes() {
- String[] result;
- Log.d(LOG_TAG, "Request to getActiveApn()");
- result = new String[1];
- result[0] = Phone.APN_TYPE_DEFAULT;
- return result;
- }
-
public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode, Message onComplete) {
Log.e(LOG_TAG, "setOutgoingCallerIdDisplay: not possible in CDMA");
}
@@ -690,6 +661,10 @@ public class CDMAPhone extends PhoneBase {
mSST.enableLocationUpdates();
}
+ public void disableLocationUpdates() {
+ mSST.disableLocationUpdates();
+ }
+
/**
* @deprecated
*/
@@ -714,7 +689,7 @@ public class CDMAPhone extends PhoneBase {
Message onComplete) {
Message resp;
mVmNumber = voiceMailNumber;
- resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+ resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
mRuimRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
@@ -758,19 +733,18 @@ public class CDMAPhone extends PhoneBase {
public boolean enableDataConnectivity() {
// block data activities when phone is in emergency callback mode
- if (mIsPhoneInECMState) {
+ if (mIsPhoneInEcmState) {
Intent intent = new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS);
ActivityManagerNative.broadcastStickyIntent(intent, null);
return false;
+ } else if ((mCT.state == Phone.State.OFFHOOK) && mCT.isInEmergencyCall()) {
+ // Do not allow data call to be enabled when emergency call is going on
+ return false;
} else {
return mDataConnection.setDataEnabled(true);
}
}
- public void disableLocationUpdates() {
- mSST.disableLocationUpdates();
- }
-
public boolean getIccRecordsLoaded() {
return mRuimRecords.getRecordsLoaded();
}
@@ -808,19 +782,19 @@ public class CDMAPhone extends PhoneBase {
}
/**
- * Notify any interested party of a Phone state change.
+ * Notify any interested party of a Phone state change {@link Phone.State}
*/
/*package*/ void notifyPhoneStateChanged() {
mNotifier.notifyPhoneState(this);
}
/**
- * Notifies registrants (ie, activities in the Phone app) about
- * changes to call state (including Phone and Connection changes).
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
*/
- /*package*/ void notifyCallStateChanged() {
+ /*package*/ void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyCallStateChangedP();
+ super.notifyPreciseCallStateChangedP();
}
void notifyServiceStateChanged(ServiceState ss) {
@@ -836,14 +810,6 @@ public class CDMAPhone extends PhoneBase {
super.notifyNewRingingConnectionP(c);
}
- /**
- * Notifiy registrants of a RING event.
- */
- void notifyIncomingRing() {
- AsyncResult ar = new AsyncResult(null, this, null);
- mIncomingRingRegistrants.notifyRegistrants(ar);
- }
-
/*package*/ void notifyDisconnect(Connection cn) {
mDisconnectRegistrants.notifyResult(cn);
}
@@ -855,8 +821,9 @@ public class CDMAPhone extends PhoneBase {
void sendEmergencyCallbackModeChange(){
//Send an Intent
Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
- intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInECMState);
+ intent.putExtra(PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
ActivityManagerNative.broadcastStickyIntent(intent,null);
+ if (DBG) Log.d(LOG_TAG, "sendEmergencyCallbackModeChange");
}
/*package*/ void
@@ -871,32 +838,23 @@ public class CDMAPhone extends PhoneBase {
mRuimRecords.setVoiceMessageWaiting(1, mwi);
}
- /**
- * Removes the given FC from the pending list and notifies
- * registrants that it is complete.
- * @param fc FC that is done
- */
- /*package*/ void onFeatureCodeDone(FeatureCode fc) {
- /* Only notify complete if it's on the pending list.
- * Otherwise, it's already been handled (eg, previously canceled).
- * The exception is cancellation of an incoming USSD-REQUEST, which is
- * not on the list.
- */
- mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, fc, null));
- }
-
-
@Override
public void exitEmergencyCallbackMode() {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
// Send a message which will invoke handleExitEmergencyCallbackMode
- mCM.exitEmergencyCallbackMode(h.obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
+ mCM.exitEmergencyCallbackMode(obtainMessage(EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE));
}
private void handleEnterEmergencyCallbackMode(Message msg) {
- Log.d(LOG_TAG, "Event EVENT_EMERGENCY_CALLBACK_MODE Received");
- // if phone is not in ECM mode, and it's changed to ECM mode
- if (mIsPhoneInECMState == false) {
- mIsPhoneInECMState = true;
+ if (DBG) {
+ Log.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
+ + mIsPhoneInEcmState);
+ }
+ // if phone is not in Ecm mode, and it's changed to Ecm mode
+ if (mIsPhoneInEcmState == false) {
+ mIsPhoneInEcmState = true;
// notify change
sendEmergencyCallbackModeChange();
setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
@@ -905,147 +863,174 @@ public class CDMAPhone extends PhoneBase {
// if no one invokes exitEmergencyCallbackMode() directly.
long delayInMillis = SystemProperties.getLong(
TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
- h.postDelayed(mExitEcmRunnable, delayInMillis);
+ postDelayed(mExitEcmRunnable, delayInMillis);
+ // We don't want to go to sleep while in Ecm
+ mWakeLock.acquire();
}
}
private void handleExitEmergencyCallbackMode(Message msg) {
- Log.d(LOG_TAG, "Event EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE Received");
AsyncResult ar = (AsyncResult)msg.obj;
+ if (DBG) {
+ Log.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState "
+ + ar.exception + mIsPhoneInEcmState);
+ }
+ // Remove pending exit Ecm runnable, if any
+ removeCallbacks(mExitEcmRunnable);
- // Remove pending exit ECM runnable, if any
- h.removeCallbacks(mExitEcmRunnable);
-
- if (mECMExitRespRegistrant != null) {
- mECMExitRespRegistrant.notifyRegistrant(ar);
+ if (mEcmExitRespRegistrant != null) {
+ mEcmExitRespRegistrant.notifyRegistrant(ar);
}
// if exiting ecm success
if (ar.exception == null) {
- if (mIsPhoneInECMState) {
- mIsPhoneInECMState = false;
+ if (mIsPhoneInEcmState) {
+ mIsPhoneInEcmState = false;
setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
}
// send an Intent
sendEmergencyCallbackModeChange();
+ // Re-initiate data connection
+ mDataConnection.setDataEnabled(true);
}
}
- //***** Inner Classes
- class MyHandler extends Handler {
- MyHandler() {
+ /**
+ * Handle to cancel or restart Ecm timer in emergency call back mode
+ * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled;
+ * otherwise, restart Ecm timer and notify apps the timer is restarted.
+ */
+ void handleTimerInEmergencyCallbackMode(int action) {
+ switch(action) {
+ case CANCEL_ECM_TIMER:
+ removeCallbacks(mExitEcmRunnable);
+ mEcmTimerResetRegistrants.notifyResult(new Boolean(true));
+ break;
+ case RESTART_ECM_TIMER:
+ long delayInMillis = SystemProperties.getLong(
+ TelephonyProperties.PROPERTY_ECM_EXIT_TIMER, DEFAULT_ECM_EXIT_TIMER_VALUE);
+ postDelayed(mExitEcmRunnable, delayInMillis);
+ mEcmTimerResetRegistrants.notifyResult(new Boolean(false));
+ break;
+ default:
+ Log.e(LOG_TAG, "handleTimerInEmergencyCallbackMode, unsupported action " + action);
}
+ }
- MyHandler(Looper l) {
- super(l);
- }
+ /**
+ * Registration point for Ecm timer reset
+ * @param h handler to notify
+ * @param what User-defined message code
+ * @param obj placed in Message.obj
+ */
+ public void registerForEcmTimerReset(Handler h, int what, Object obj) {
+ mEcmTimerResetRegistrants.addUnique(h, what, obj);
+ }
- @Override
- public void handleMessage(Message msg) {
- AsyncResult ar;
- Message onComplete;
+ public void unregisterForEcmTimerReset(Handler h) {
+ mEcmTimerResetRegistrants.remove(h);
+ }
- switch(msg.what) {
- case EVENT_RADIO_AVAILABLE: {
- mCM.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+ Message onComplete;
- mCM.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
- }
- break;
+ switch(msg.what) {
+ case EVENT_RADIO_AVAILABLE: {
+ mCM.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
- case EVENT_GET_BASEBAND_VERSION_DONE:{
- ar = (AsyncResult)msg.obj;
+ mCM.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
+ }
+ break;
- if (ar.exception != null) {
- break;
- }
+ case EVENT_GET_BASEBAND_VERSION_DONE:{
+ ar = (AsyncResult)msg.obj;
- if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
- setSystemProperty(TelephonyProperties.PROPERTY_BASEBAND_VERSION, (String)ar.result);
+ if (ar.exception != null) {
+ break;
}
- break;
- case EVENT_GET_DEVICE_IDENTITY_DONE:{
- ar = (AsyncResult)msg.obj;
+ if (DBG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
+ setSystemProperty(TelephonyProperties.PROPERTY_BASEBAND_VERSION, (String)ar.result);
+ }
+ break;
- if (ar.exception != null) {
- break;
- }
- String[] respId = (String[])ar.result;
- mEsn = respId[2];
- mMeid = respId[3];
- }
- break;
+ case EVENT_GET_DEVICE_IDENTITY_DONE:{
+ ar = (AsyncResult)msg.obj;
- case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{
- handleEnterEmergencyCallbackMode(msg);
+ if (ar.exception != null) {
+ break;
}
- break;
+ String[] respId = (String[])ar.result;
+ mEsn = respId[2];
+ mMeid = respId[3];
+ }
+ break;
- case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{
- handleExitEmergencyCallbackMode(msg);
- }
- break;
+ case EVENT_EMERGENCY_CALLBACK_MODE_ENTER:{
+ handleEnterEmergencyCallbackMode(msg);
+ }
+ break;
- case EVENT_RUIM_RECORDS_LOADED:{
- Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received");
- }
- break;
+ case EVENT_EXIT_EMERGENCY_CALLBACK_RESPONSE:{
+ handleExitEmergencyCallbackMode(msg);
+ }
+ break;
- case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{
- Log.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received");
- }
- break;
+ case EVENT_RUIM_RECORDS_LOADED:{
+ Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received");
+ }
+ break;
- case EVENT_RADIO_ON:{
- Log.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
- }
- break;
+ case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:{
+ Log.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received");
+ }
+ break;
- case EVENT_SSN:{
- Log.d(LOG_TAG, "Event EVENT_SSN Received");
- }
- break;
+ case EVENT_RADIO_ON:{
+ Log.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
+ }
+ break;
- case EVENT_CALL_RING:{
- Log.d(LOG_TAG, "Event EVENT_CALL_RING Received");
- }
- break;
+ case EVENT_SSN:{
+ Log.d(LOG_TAG, "Event EVENT_SSN Received");
+ }
+ break;
- case EVENT_REGISTERED_TO_NETWORK:{
- Log.d(LOG_TAG, "Event EVENT_REGISTERED_TO_NETWORK Received");
+ case EVENT_REGISTERED_TO_NETWORK:{
+ Log.d(LOG_TAG, "Event EVENT_REGISTERED_TO_NETWORK Received");
+ }
+ break;
+
+ case EVENT_NV_READY:{
+ Log.d(LOG_TAG, "Event EVENT_NV_READY Received");
+ //Inform the Service State Tracker
+ mEriManager.loadEriFile();
+ mNvLoadedRegistrants.notifyRegistrants();
+ if(mEriManager.isEriFileLoaded()) {
+ // when the ERI file is loaded
+ Log.d(LOG_TAG, "ERI read, notify registrants");
+ mEriFileLoadedRegistrants.notifyRegistrants();
}
- break;
+ }
+ break;
- case EVENT_NV_READY:{
- Log.d(LOG_TAG, "Event EVENT_NV_READY Received");
- //Inform the Service State Tracker
- mEriManager.loadEriFile();
- mNvLoadedRegistrants.notifyRegistrants();
- if(mEriManager.isEriFileLoaded()) {
- // when the ERI file is loaded
- Log.d(LOG_TAG, "ERI read, notify registrants");
- mEriFileLoadedRegistrants.notifyRegistrants();
- }
- setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE,"false");
+ case EVENT_SET_VM_NUMBER_DONE:{
+ ar = (AsyncResult)msg.obj;
+ if (IccException.class.isInstance(ar.exception)) {
+ storeVoiceMailNumber(mVmNumber);
+ ar.exception = null;
}
- break;
-
- case EVENT_SET_VM_NUMBER_DONE:{
- ar = (AsyncResult)msg.obj;
- if (IccException.class.isInstance(ar.exception)) {
- storeVoiceMailNumber(mVmNumber);
- ar.exception = null;
- }
- onComplete = (Message) ar.userObj;
- if (onComplete != null) {
- AsyncResult.forMessage(onComplete, ar.result, ar.exception);
- onComplete.sendToTarget();
- }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
}
+ }
+ break;
- default:{
- throw new RuntimeException("unexpected event not handled");
- }
+ default:{
+ super.handleMessage(msg);
}
}
}
@@ -1100,13 +1085,6 @@ public class CDMAPhone extends PhoneBase {
/**
* {@inheritDoc}
*/
- public Handler getHandler() {
- return h;
- }
-
- /**
- * {@inheritDoc}
- */
public IccFileHandler getIccFileHandler() {
return this.mIccFileHandler;
}
@@ -1153,10 +1131,10 @@ public class CDMAPhone extends PhoneBase {
mSMS.setCellBroadcastConfig(configValuesArray, response);
}
- public static final String IS683A_FEATURE_CODE = "*228" ;
- public static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4 ;
- public static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2 ;
- public static final int IS683A_SYS_SEL_CODE_OFFSET = 4;
+ private static final String IS683A_FEATURE_CODE = "*228";
+ private static final int IS683A_FEATURE_CODE_NUM_DIGITS = 4;
+ private static final int IS683A_SYS_SEL_CODE_NUM_DIGITS = 2;
+ private static final int IS683A_SYS_SEL_CODE_OFFSET = 4;
private static final int IS683_CONST_800MHZ_A_BAND = 0;
private static final int IS683_CONST_800MHZ_B_BAND = 1;
@@ -1166,6 +1144,7 @@ public class CDMAPhone extends PhoneBase {
private static final int IS683_CONST_1900MHZ_D_BLOCK = 5;
private static final int IS683_CONST_1900MHZ_E_BLOCK = 6;
private static final int IS683_CONST_1900MHZ_F_BLOCK = 7;
+ private static final int INVALID_SYSTEM_SELECTION_CODE = -1;
private boolean isIs683OtaSpDialStr(String dialStr) {
int sysSelCodeInt;
@@ -1176,58 +1155,168 @@ public class CDMAPhone extends PhoneBase {
if (dialStr.equals(IS683A_FEATURE_CODE)) {
isOtaspDialString = true;
}
- } else if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE, 0,
- IS683A_FEATURE_CODE_NUM_DIGITS) == true)
- && (dialStrLen >=
- (IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS))) {
- StringBuilder sb = new StringBuilder(dialStr);
- // Separate the System Selection Code into its own string
- char[] sysSel = new char[2];
- sb.delete(0, IS683A_SYS_SEL_CODE_OFFSET);
- sb.getChars(0, IS683A_SYS_SEL_CODE_NUM_DIGITS, sysSel, 0);
-
- if ((PhoneNumberUtils.isISODigit(sysSel[0]))
- && (PhoneNumberUtils.isISODigit(sysSel[1]))) {
- String sysSelCode = new String(sysSel);
- sysSelCodeInt = Integer.parseInt((String)sysSelCode);
- switch (sysSelCodeInt) {
- case IS683_CONST_800MHZ_A_BAND:
- case IS683_CONST_800MHZ_B_BAND:
- case IS683_CONST_1900MHZ_A_BLOCK:
- case IS683_CONST_1900MHZ_B_BLOCK:
- case IS683_CONST_1900MHZ_C_BLOCK:
- case IS683_CONST_1900MHZ_D_BLOCK:
- case IS683_CONST_1900MHZ_E_BLOCK:
- case IS683_CONST_1900MHZ_F_BLOCK:
- isOtaspDialString = true;
- break;
+ } else {
+ sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
+ switch (sysSelCodeInt) {
+ case IS683_CONST_800MHZ_A_BAND:
+ case IS683_CONST_800MHZ_B_BAND:
+ case IS683_CONST_1900MHZ_A_BLOCK:
+ case IS683_CONST_1900MHZ_B_BLOCK:
+ case IS683_CONST_1900MHZ_C_BLOCK:
+ case IS683_CONST_1900MHZ_D_BLOCK:
+ case IS683_CONST_1900MHZ_E_BLOCK:
+ case IS683_CONST_1900MHZ_F_BLOCK:
+ isOtaspDialString = true;
+ break;
+ default:
+ break;
+ }
+ }
+ return isOtaspDialString;
+ }
+ /**
+ * This function extracts the system selection code from the dial string.
+ */
+ private int extractSelCodeFromOtaSpNum(String dialStr) {
+ int dialStrLen = dialStr.length();
+ int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE;
+
+ if ((dialStr.regionMatches(0, IS683A_FEATURE_CODE,
+ 0, IS683A_FEATURE_CODE_NUM_DIGITS)) &&
+ (dialStrLen >= (IS683A_FEATURE_CODE_NUM_DIGITS +
+ IS683A_SYS_SEL_CODE_NUM_DIGITS))) {
+ // Since we checked the condition above, the system selection code
+ // extracted from dialStr will not cause any exception
+ sysSelCodeInt = Integer.parseInt (
+ dialStr.substring (IS683A_FEATURE_CODE_NUM_DIGITS,
+ IS683A_FEATURE_CODE_NUM_DIGITS + IS683A_SYS_SEL_CODE_NUM_DIGITS));
+ }
+ if (DBG) Log.d(LOG_TAG, "extractSelCodeFromOtaSpNum " + sysSelCodeInt);
+ return sysSelCodeInt;
+ }
- default:
+ /**
+ * This function checks if the system selection code extracted from
+ * the dial string "sysSelCodeInt' is the system selection code specified
+ * in the carrier ota sp number schema "sch".
+ */
+ private boolean
+ checkOtaSpNumBasedOnSysSelCode (int sysSelCodeInt, String sch[]) {
+ boolean isOtaSpNum = false;
+ try {
+ // Get how many number of system selection code ranges
+ int selRc = Integer.parseInt((String)sch[1]);
+ for (int i = 0; i < selRc; i++) {
+ if (!TextUtils.isEmpty(sch[i+2]) && !TextUtils.isEmpty(sch[i+3])) {
+ int selMin = Integer.parseInt((String)sch[i+2]);
+ int selMax = Integer.parseInt((String)sch[i+3]);
+ // Check if the selection code extracted from the dial string falls
+ // within any of the range pairs specified in the schema.
+ if ((sysSelCodeInt >= selMin) && (sysSelCodeInt <= selMax)) {
+ isOtaSpNum = true;
break;
+ }
}
}
+ } catch (NumberFormatException ex) {
+ // If the carrier ota sp number schema is not correct, we still allow dial
+ // and only log the error:
+ Log.e(LOG_TAG, "checkOtaSpNumBasedOnSysSelCode, error", ex);
}
- return isOtaspDialString;
+ return isOtaSpNum;
}
- /**
- * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier
- * OTASP dial string.
- *
- * @param dialStr the number to look up.
- * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string
- */
+ // Define the pattern/format for carrier specified OTASP number schema.
+ // It separates by comma and/or whitespace.
+ private static Pattern pOtaSpNumSchema = Pattern.compile("[,\\s]+");
+
+ /**
+ * The following function checks if a dial string is a carrier specified
+ * OTASP number or not by checking against the OTASP number schema stored
+ * in PROPERTY_OTASP_NUM_SCHEMA.
+ *
+ * Currently, there are 2 schemas for carriers to specify the OTASP number:
+ * 1) Use system selection code:
+ * The schema is:
+ * SELC,the # of code pairs,min1,max1,min2,max2,...
+ * e.g "SELC,3,10,20,30,40,60,70" indicates that there are 3 pairs of
+ * selection codes, and they are {10,20}, {30,40} and {60,70} respectively.
+ *
+ * 2) Use feature code:
+ * The schema is:
+ * "FC,length of feature code,feature code".
+ * e.g "FC,2,*2" indicates that the length of the feature code is 2,
+ * and the code itself is "*2".
+ */
+ private boolean isCarrierOtaSpNum(String dialStr) {
+ boolean isOtaSpNum = false;
+ int sysSelCodeInt = extractSelCodeFromOtaSpNum(dialStr);
+ if (sysSelCodeInt == INVALID_SYSTEM_SELECTION_CODE) {
+ return isOtaSpNum;
+ }
+ // mCarrierOtaSpNumSchema is retrieved from PROPERTY_OTASP_NUM_SCHEMA:
+ if (!TextUtils.isEmpty(mCarrierOtaSpNumSchema)) {
+ Matcher m = pOtaSpNumSchema.matcher(mCarrierOtaSpNumSchema);
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,schema" + mCarrierOtaSpNumSchema);
+ }
+
+ if (m.find()) {
+ String sch[] = pOtaSpNumSchema.split(mCarrierOtaSpNumSchema);
+ // If carrier uses system selection code mechanism
+ if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("SELC")) {
+ if (sysSelCodeInt!=INVALID_SYSTEM_SELECTION_CODE) {
+ isOtaSpNum=checkOtaSpNumBasedOnSysSelCode(sysSelCodeInt,sch);
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,sysSelCodeInt is invalid");
+ }
+ }
+ } else if (!TextUtils.isEmpty(sch[0]) && sch[0].equals("FC")) {
+ int fcLen = Integer.parseInt((String)sch[1]);
+ String fc = (String)sch[2];
+ if (dialStr.regionMatches(0,fc,0,fcLen)) {
+ isOtaSpNum = true;
+ } else {
+ if (DBG) Log.d(LOG_TAG, "isCarrierOtaSpNum,not otasp number");
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema not supported" + sch[0]);
+ }
+ }
+ } else {
+ if (DBG) {
+ Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern not right" +
+ mCarrierOtaSpNumSchema);
+ }
+ }
+ } else {
+ if (DBG) Log.d(LOG_TAG, "isCarrierOtaSpNum,ota schema pattern empty");
+ }
+ return isOtaSpNum;
+ }
+
+ /**
+ * isOTASPNumber: checks a given number against the IS-683A OTASP dial string and carrier
+ * OTASP dial string.
+ *
+ * @param dialStr the number to look up.
+ * @return true if the number is in IS-683A OTASP dial string or carrier OTASP dial string
+ */
@Override
- public boolean isOtaSpNumber(String dialStr){
- boolean isOtaSpNum = false;
- if(dialStr != null){
- isOtaSpNum=isIs683OtaSpDialStr(dialStr);
- if(isOtaSpNum == false){
- //TO DO:Add carrier specific OTASP number detection here.
- }
- }
- return isOtaSpNum;
- }
+ public boolean isOtaSpNumber(String dialStr){
+ boolean isOtaSpNum = false;
+ String dialableStr = PhoneNumberUtils.extractNetworkPortion(dialStr);
+ if (dialableStr != null) {
+ isOtaSpNum = isIs683OtaSpDialStr(dialableStr);
+ if (isOtaSpNum == false) {
+ isOtaSpNum = isCarrierOtaSpNum(dialableStr);
+ }
+ }
+ if (DBG) Log.d(LOG_TAG, "isOtaSpNumber " + isOtaSpNum);
+ return isOtaSpNum;
+ }
@Override
public int getCdmaEriIconIndex() {
@@ -1314,21 +1403,4 @@ public class CDMAPhone extends PhoneBase {
return false;
}
- /**
- * Updates MCC and MNC device configuration information for application retrieving
- * correct version of resources
- *
- */
- private void updateMccMncConfiguration(String operatorNumeric) {
- if (operatorNumeric.length() >= 5) {
- Configuration config = new Configuration();
- config.mcc = Integer.parseInt(operatorNumeric.substring(0,3));
- config.mnc = Integer.parseInt(operatorNumeric.substring(3));
- try {
- ActivityManagerNative.getDefault().updateConfiguration(config);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Can't update configuration", e);
- }
- }
- }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
index e8724c2..c3bb01f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCall.java
@@ -24,6 +24,7 @@ import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.DriverCall;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.Call.State;
/**
* {@hide}
@@ -186,6 +187,7 @@ public final class CdmaCall extends Call {
cn.onHangupLocal();
}
+ state = State.DISCONNECTING;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index ed2ea90..806c31d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -72,7 +72,8 @@ public final class CdmaCallTracker extends CallTracker {
CdmaConnection pendingMO;
boolean hangupPendingMO;
- boolean pendingCallInECM=false;
+ boolean pendingCallInEcm=false;
+ boolean mIsInEmergencyCall = false;
CDMAPhone phone;
boolean desiredMute = false; // false = mute off
@@ -80,6 +81,7 @@ public final class CdmaCallTracker extends CallTracker {
int pendingCallClirMode;
Phone.State state = Phone.State.IDLE;
+ private boolean mIsEcmTimerCanceled = false;
// boolean needsPoll;
@@ -182,6 +184,14 @@ public final class CdmaCallTracker extends CallTracker {
throw new CallStateException("cannot dial in current state");
}
+ String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+ boolean isPhoneInEcmMode = inEcm.equals("true");
+ boolean isEmergencyCall = PhoneNumberUtils.isEmergencyNumber(dialString);
+
+ // Cancel Ecm timer if a second emergency call is originating in Ecm mode
+ if (isPhoneInEcmMode && isEmergencyCall) {
+ handleEcmTimer(phone.CANCEL_ECM_TIMER);
+ }
// We are initiating a call therefore even if we previously
// didn't know the state (i.e. Generic was true) we now know
@@ -210,19 +220,22 @@ public final class CdmaCallTracker extends CallTracker {
// Always unmute when initiating a new call
setMute(false);
- String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
- if(inEcm.equals("false")) {
+ // Check data call
+ disableDataCallInEmergencyCall(dialString);
+
+ // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.
+ if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {
cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());
} else {
phone.exitEmergencyCallbackMode();
phone.setOnEcbModeExitResponse(this,EVENT_EXIT_ECM_RESPONSE_CDMA, null);
pendingCallClirMode=clirMode;
- pendingCallInECM=true;
+ pendingCallInEcm=true;
}
}
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
return pendingMO;
}
@@ -236,6 +249,9 @@ public final class CdmaCallTracker extends CallTracker {
private Connection
dialThreeWay (String dialString) {
if (!foregroundCall.isIdle()) {
+ // Check data call
+ disableDataCallInEmergencyCall(dialString);
+
// Attach the new connection to foregroundCall
pendingMO = new CdmaConnection(phone.getContext(),
dialString, this, foregroundCall);
@@ -262,6 +278,7 @@ public final class CdmaCallTracker extends CallTracker {
// triggered by updateParent.
cwConn.updateParent(ringingCall, foregroundCall);
cwConn.onConnectedInOrOut();
+ updatePhoneState();
switchWaitingOrHoldingAndActive();
} else {
throw new CallStateException("phone not ringing");
@@ -284,8 +301,14 @@ public final class CdmaCallTracker extends CallTracker {
// Should we bother with this check?
if (ringingCall.getState() == CdmaCall.State.INCOMING) {
throw new CallStateException("cannot be in the incoming state");
- } else {
+ } else if (foregroundCall.getConnections().size() > 1) {
flashAndSetGenericTrue();
+ } else {
+ // Send a flash command to CDMA network for putting the other party on hold.
+ // For CDMA networks which do not support this the user would just hear a beep
+ // from the network. For CDMA networks which do support it will put the other
+ // party on hold.
+ cm.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
}
}
@@ -305,7 +328,7 @@ public final class CdmaCallTracker extends CallTracker {
internalClearDisconnected();
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
boolean
@@ -320,13 +343,16 @@ public final class CdmaCallTracker extends CallTracker {
canDial() {
boolean ret;
int serviceState = phone.getServiceState().getState();
+ String disableCall = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
- ret = (serviceState != ServiceState.STATE_POWER_OFF) &&
- pendingMO == null
+ ret = (serviceState != ServiceState.STATE_POWER_OFF)
+ && pendingMO == null
&& !ringingCall.isRinging()
+ && !disableCall.equals("true")
&& (!foregroundCall.getState().isAlive()
- || (foregroundCall.getState() == CdmaCall.State.ACTIVE)
- || !backgroundCall.getState().isAlive());
+ || (foregroundCall.getState() == CdmaCall.State.ACTIVE)
+ || !backgroundCall.getState().isAlive());
return ret;
}
@@ -475,6 +501,11 @@ public final class CdmaCallTracker extends CallTracker {
// Someone has already asked to hangup this call
if (hangupPendingMO) {
hangupPendingMO = false;
+ // Re-start Ecm timer when an uncompleted emergency call ends
+ if (mIsEcmTimerCanceled) {
+ handleEcmTimer(phone.RESTART_ECM_TIMER);
+ }
+
try {
if (Phone.DEBUG_PHONE) log(
"poll: hangupPendingMO, hangup conn " + i);
@@ -528,20 +559,17 @@ public final class CdmaCallTracker extends CallTracker {
}
}
foregroundCall.setGeneric(false);
+
+ // Re-start Ecm timer when the connected emergency call ends
+ if (mIsEcmTimerCanceled) {
+ handleEcmTimer(phone.RESTART_ECM_TIMER);
+ } else {
+ mIsInEmergencyCall = false;
+ }
+
// Dropped connections are removed from the CallTracker
// list but kept in the Call list
connections[i] = null;
- } else if (conn != null && dc != null && !conn.compareTo(dc)) {
- // Connection in CLCC response does not match what
- // we were tracking. Assume dropped call and new call
-
- droppedDuringPoll.add(conn);
- connections[i] = new CdmaConnection (phone.getContext(), dc, this, i);
-
- if (connections[i].getCall() == ringingCall) {
- newRinging = connections[i];
- } // else something strange happened
- hasNonHangupStateChanged = true;
} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
boolean changed;
changed = conn.update(dc);
@@ -578,8 +606,8 @@ public final class CdmaCallTracker extends CallTracker {
droppedDuringPoll.add(pendingMO);
pendingMO = null;
hangupPendingMO = false;
- if( pendingCallInECM) {
- pendingCallInECM = false;
+ if( pendingCallInEcm) {
+ pendingCallInEcm = false;
}
}
@@ -644,7 +672,7 @@ public final class CdmaCallTracker extends CallTracker {
}
if (hasNonHangupStateChanged || newRinging != null) {
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
//dumpState();
@@ -678,7 +706,8 @@ public final class CdmaCallTracker extends CallTracker {
// the hangup reason is user ignoring or timing out. So conn.onDisconnect()
// is not called here. Instead, conn.onLocalDisconnect() is called.
conn.onLocalDisconnect();
- phone.notifyCallStateChanged();
+ updatePhoneState();
+ phone.notifyPreciseCallStateChanged();
return;
} else {
try {
@@ -760,6 +789,7 @@ public final class CdmaCallTracker extends CallTracker {
}
call.onHangupLocal();
+ phone.notifyPreciseCallStateChanged();
}
/* package */
@@ -821,7 +851,7 @@ public final class CdmaCallTracker extends CallTracker {
// the status of the call is after a call waiting is answered,
// 3 way call merged or a switch between calls.
foregroundCall.setGeneric(true);
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
private Phone.SuppService getFailedService(int what) {
@@ -865,6 +895,7 @@ public final class CdmaCallTracker extends CallTracker {
// Create a new CdmaConnection which attaches itself to ringingCall.
ringingCall.setGeneric(false);
new CdmaConnection(phone.getContext(), cw, this, ringingCall);
+ updatePhoneState();
// Finally notify application
notifyCallWaitingInfo(cw);
@@ -926,7 +957,7 @@ public final class CdmaCallTracker extends CallTracker {
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
droppedDuringPoll.clear();
break;
@@ -945,9 +976,9 @@ public final class CdmaCallTracker extends CallTracker {
case EVENT_EXIT_ECM_RESPONSE_CDMA:
//no matter the result, we still do the same here
- if (pendingCallInECM) {
+ if (pendingCallInEcm) {
cm.dial(pendingMO.address, pendingCallClirMode, obtainCompleteMessage());
- pendingCallInECM = false;
+ pendingCallInEcm = false;
}
phone.unsetOnEcbModeExitResponse(this);
break;
@@ -965,6 +996,7 @@ public final class CdmaCallTracker extends CallTracker {
if (ar.exception == null) {
// Assume 3 way call is connected
pendingMO.onConnectedInOrOut();
+ pendingMO = null;
}
break;
@@ -974,6 +1006,39 @@ public final class CdmaCallTracker extends CallTracker {
}
}
+ /**
+ * Handle Ecm timer to be canceled or re-started
+ */
+ private void handleEcmTimer(int action) {
+ phone.handleTimerInEmergencyCallbackMode(action);
+ switch(action) {
+ case CDMAPhone.CANCEL_ECM_TIMER: mIsEcmTimerCanceled = true; break;
+ case CDMAPhone.RESTART_ECM_TIMER: mIsEcmTimerCanceled = false; break;
+ default:
+ Log.e(LOG_TAG, "handleEcmTimer, unsupported action " + action);
+ }
+ }
+
+ /**
+ * Disable data call when emergency call is connected
+ */
+ private void disableDataCallInEmergencyCall(String dialString) {
+ if (PhoneNumberUtils.isEmergencyNumber(dialString)) {
+ phone.disableDataConnectivity();
+ mIsInEmergencyCall = true;
+ }
+ }
+
+ /**
+ * Check if current call is in emergency call
+ *
+ * @return true if it is in emergency call
+ * false if it is not in emergency call
+ */
+ boolean isInEmergencyCall() {
+ return mIsInEmergencyCall;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[CdmaCallTracker] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
index 54dec48..f4119ad 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaCallWaitingNotification.java
@@ -16,12 +16,16 @@
package com.android.internal.telephony.cdma;
+import android.util.Log;
+import com.android.internal.telephony.Connection;
+
/**
* Represents a Supplementary Service Notification received from the network.
*
* {@hide}
*/
public class CdmaCallWaitingNotification {
+ static final String LOG_TAG = "CDMA";
public String number =null;
public int numberPresentation = 0;
public String name = null;
@@ -31,7 +35,6 @@ public class CdmaCallWaitingNotification {
public int alertPitch = 0;
public int signal = 0;
-
public String toString()
{
return super.toString() + "Call Waiting Notification "
@@ -45,4 +48,17 @@ public class CdmaCallWaitingNotification {
+ " signal: " + signal ;
}
+ public static int
+ presentationFromCLIP(int cli)
+ {
+ switch(cli) {
+ case 0: return Connection.PRESENTATION_ALLOWED;
+ case 1: return Connection.PRESENTATION_RESTRICTED;
+ case 2: return Connection.PRESENTATION_UNKNOWN;
+ default:
+ // This shouldn't happen, just log an error and treat as Unknown
+ Log.d(LOG_TAG, "Unexpected presentation " + cli);
+ return Connection.PRESENTATION_UNKNOWN;
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 025382d..0c94e6a 100644..100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -161,8 +161,8 @@ public class CdmaConnection extends Connection {
isIncoming = false;
cnapName = null;
- cnapNamePresentation = 0;
- numberPresentation = 0;
+ cnapNamePresentation = Connection.PRESENTATION_ALLOWED;
+ numberPresentation = Connection.PRESENTATION_ALLOWED;
createTime = System.currentTimeMillis();
if (parent != null) {
@@ -435,8 +435,10 @@ public class CdmaConnection extends Connection {
} else if (phone.mCM.getRadioState() != CommandsInterface.RadioState.NV_READY
&& phone.getIccCard().getState() != RuimCard.State.READY) {
return DisconnectCause.ICC_ERROR;
- } else {
+ } else if (causeCode==CallFailCause.NORMAL_CLEARING) {
return DisconnectCause.NORMAL;
+ } else {
+ return DisconnectCause.ERROR_UNSPECIFIED;
}
}
}
@@ -816,15 +818,12 @@ public class CdmaConnection extends Connection {
return c == PhoneNumberUtils.WAIT;
}
-
-
-
// This function is to find the next PAUSE character index if
// multiple pauses in a row. Otherwise it finds the next non PAUSE or
// non WAIT character index.
private static int
findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
- boolean wMatched = false;
+ boolean wMatched = isWait(phoneNumber.charAt(currIndex));
int index = currIndex + 1;
int length = phoneNumber.length();
while (index < length) {
@@ -861,17 +860,17 @@ public class CdmaConnection extends Connection {
// Append the PW char
ret = (isPause(c)) ? PhoneNumberUtils.PAUSE : PhoneNumberUtils.WAIT;
- // if there is a PAUSE in at the begining of PW character sequences, and this
- // PW character sequences has more than 2 PAUSE and WAIT Characters,skip P, append W
- if (isPause(c) && (nextNonPwCharIndex > (currPwIndex + 1))) {
+ // if there is a PAUSE in at the beginning of PW character sequences, and this
+ // PW character sequences has more than 2 PAUSE and WAIT Characters,skip PAUSE,
+ // append WAIT.
+ if (isPause(c) && (nextNonPwCharIndex > (currPwIndex + 2))) {
ret = PhoneNumberUtils.WAIT;
}
return ret;
}
-
/**
- * format orignal dial string
+ * format original dial string
* 1) convert international dialing prefix "+" to
* string specified per region
*
@@ -890,20 +889,10 @@ public class CdmaConnection extends Connection {
StringBuilder ret = new StringBuilder();
char c;
int currIndex = 0;
+
while (currIndex < length) {
c = phoneNumber.charAt(currIndex);
- if (PhoneNumberUtils.isDialable(c)) {
- if (c == '+') {
- String ps = null;
- SystemProperties.get(TelephonyProperties.PROPERTY_IDP_STRING, ps);
- if (TextUtils.isEmpty(ps)) {
- ps = "011";
- }
- ret.append(ps);
- } else {
- ret.append(c);
- }
- } else if (isPause(c) || isWait(c)) {
+ if (isPause(c) || isWait(c)) {
if (currIndex < length - 1) {
// if PW not at the end
int nextIndex = findNextPCharOrNonPOrNonWCharIndex(phoneNumber, currIndex);
@@ -911,8 +900,10 @@ public class CdmaConnection extends Connection {
if (nextIndex < length) {
char pC = findPOrWCharToAppend(phoneNumber, currIndex, nextIndex);
ret.append(pC);
- // If PW char is immediately followed by non-PW char
- if (nextIndex > (currIndex + 1)) {
+ // If PW char sequence has more than 2 PW characters,
+ // skip to the last character since the sequence already be
+ // converted to WAIT character
+ if (nextIndex > (currIndex + 2)) {
currIndex = nextIndex - 1;
}
} else if (nextIndex == length) {
@@ -925,7 +916,7 @@ public class CdmaConnection extends Connection {
}
currIndex++;
}
- return ret.toString();
+ return PhoneNumberUtils.cdmaCheckAndProcessPlusCode(ret.toString());
}
private void log(String msg) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
index fef6d3c..4588f36 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnection.java
@@ -143,9 +143,10 @@ public class CdmaDataConnection extends DataConnection {
lastFailTime = -1;
lastFailCause = FailCause.NONE;
receivedDisconnectReq = false;
- phone.mCM.setupDataCall(Integer.toString(RILConstants.CDMA_PHONE),
+ phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_CDMA),
Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), null, null,
- null, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE));
+ null, Integer.toString(RILConstants.SETUP_DATA_AUTH_PAP_CHAP),
+ obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE));
}
private void tearDownData(Message msg) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index c3818f5..ffaa1cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -31,6 +31,7 @@ import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.preference.PreferenceManager;
import android.provider.Checkin;
import android.telephony.ServiceState;
@@ -45,7 +46,9 @@ import com.android.internal.telephony.DataCallState;
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.DataConnection.FailCause;
import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyEventLog;
import java.util.ArrayList;
@@ -54,15 +57,12 @@ import java.util.ArrayList;
* {@hide}
*/
public final class CdmaDataConnectionTracker extends DataConnectionTracker {
- private static final String LOG_TAG = "CDMA";
- private static final boolean DBG = true;
+ protected final String LOG_TAG = "CDMA";
private CDMAPhone mCdmaPhone;
// Indicates baseband will not auto-attach
private boolean noAutoAttach = false;
- long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
- private boolean mReregisterOnReconnectFailure = false;
private boolean mIsScreenOn = true;
//useful for debugging
@@ -76,11 +76,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
/** Currently active CdmaDataConnection */
private CdmaDataConnection mActiveDataConnection;
- /** Defined cdma connection profiles */
- private static final int EXTERNAL_NETWORK_DEFAULT_ID = 0;
- private static final int EXTERNAL_NETWORK_NUM_TYPES = 1;
-
- private boolean[] dataEnabled = new boolean[EXTERNAL_NETWORK_NUM_TYPES];
+ private boolean mPendingRestartRadio = false;
+ private static final int TIME_DELAYED_TO_RESTART_RADIO =
+ SystemProperties.getInt("ro.cdma.timetoradiorestart", 20000);
/**
* Pool size of CdmaDataConnection objects.
@@ -100,6 +98,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DOWN = 1;
private static final int DATA_CONNECTION_ACTIVE_PH_LINK_UP = 2;
+ private static final String[] mSupportedApnTypes = {
+ Phone.APN_TYPE_DEFAULT,
+ Phone.APN_TYPE_MMS,
+ Phone.APN_TYPE_HIPRI };
+
// Possibly promoate to base class, the only difference is
// the INTENT_RECONNECT_ALARM action is a different string.
// Do consider technology changes if it is promoted.
@@ -143,7 +146,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
};
- //***** Constructor
+ /* Constructor */
CdmaDataConnectionTracker(CDMAPhone p) {
super(p);
@@ -160,6 +163,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
p.mSST.registerForCdmaDataConnectionDetached(this, EVENT_CDMA_DATA_DETACHED, null);
p.mSST.registerForRoamingOn(this, EVENT_ROAMING_ON, null);
p.mSST.registerForRoamingOff(this, EVENT_ROAMING_OFF, null);
+ p.mCM.registerForCdmaOtaProvision(this, EVENT_CDMA_OTA_PROVISION, null);
this.netstat = INetStatService.Stub.asInterface(ServiceManager.getService("netstat"));
@@ -170,7 +174,9 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- p.getContext().registerReceiver(mIntentReceiver, filter, null, p.h);
+ // TODO: Why is this registering the phone as the receiver of the intent
+ // and not its own handler?
+ p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
mDataConnectionTracker = this;
@@ -180,13 +186,25 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
// and 2) whether the RIL will setup the baseband to auto-PS attach.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
- dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID] =
+ dataEnabled[APN_DEFAULT_ID] =
!sp.getBoolean(CDMAPhone.DATA_DISABLED_ON_BOOT_KEY, false);
- noAutoAttach = !dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID];
+ if (dataEnabled[APN_DEFAULT_ID]) {
+ enabledCount++;
+ }
+ noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
+
+ if (!mRetryMgr.configure(SystemProperties.get("ro.cdma.data_retry_config"))) {
+ if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+ // Should never happen, log an error and default to a simple linear sequence.
+ Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
+ + DEFAULT_DATA_RETRY_CONFIG);
+ mRetryMgr.configure(20, 2000, 1000);
+ }
+ }
}
public void dispose() {
- //Unregister from all events
+ // Unregister from all events
phone.mCM.unregisterForAvailable(this);
phone.mCM.unregisterForOffOrNotAvailable(this);
mCdmaPhone.mRuimRecords.unregisterForRecordsLoaded(this);
@@ -198,6 +216,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
mCdmaPhone.mSST.unregisterForCdmaDataConnectionDetached(this);
mCdmaPhone.mSST.unregisterForRoamingOn(this);
mCdmaPhone.mSST.unregisterForRoamingOff(this);
+ phone.mCM.unregisterForCdmaOtaProvision(this);
phone.getContext().unregisterReceiver(this.mIntentReceiver);
destroyAllDataConnectionList();
@@ -207,7 +226,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
if(DBG) Log.d(LOG_TAG, "CdmaDataConnectionTracker finalized");
}
- void setState(State s) {
+ protected void setState(State s) {
if (DBG) log ("setState: " + s);
if (state != s) {
if (s == State.INITING) { // request Data connection context
@@ -224,39 +243,33 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
state = s;
}
- public int enableApnType(String type) {
- // This request is mainly used to enable MMS APN
- // In CDMA there is no need to enable/disable a different APN for MMS
- Log.d(LOG_TAG, "Request to enableApnType("+type+")");
- if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
- return Phone.APN_ALREADY_ACTIVE;
- } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
- Log.w(LOG_TAG, "Phone.APN_TYPE_SUPL not enabled for CDMA");
- return Phone.APN_REQUEST_FAILED;
- } else {
- return Phone.APN_REQUEST_FAILED;
- }
+ @Override
+ protected boolean isApnTypeActive(String type) {
+ return (isApnTypeAvailable(type) &&
+ mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() ==
+ ServiceState.STATE_IN_SERVICE);
}
- public int disableApnType(String type) {
- // This request is mainly used to disable MMS APN
- // In CDMA there is no need to enable/disable a different APN for MMS
- Log.d(LOG_TAG, "Request to disableApnType("+type+")");
- if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) {
- return Phone.APN_REQUEST_STARTED;
- } else {
- return Phone.APN_REQUEST_FAILED;
+ @Override
+ protected boolean isApnTypeAvailable(String type) {
+ for (String s : mSupportedApnTypes) {
+ if (TextUtils.equals(type, s)) {
+ return true;
+ }
}
+ return false;
}
- private boolean isEnabled(int cdmaDataProfile) {
- return dataEnabled[cdmaDataProfile];
+ protected String[] getActiveApnTypes() {
+ if (mCdmaPhone.mSST.getCurrentCdmaDataConnectionState() ==
+ ServiceState.STATE_IN_SERVICE) {
+ return mSupportedApnTypes.clone();
+ }
+ return new String[0];
}
- private void setEnabled(int cdmaDataProfile, boolean enable) {
- Log.d(LOG_TAG, "setEnabled(" + cdmaDataProfile + ", " + enable + ')');
- dataEnabled[cdmaDataProfile] = enable;
- Log.d(LOG_TAG, "dataEnabled[DEFAULT_PROFILE]=" + dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID]);
+ protected String getActiveApnString() {
+ return null;
}
/**
@@ -282,54 +295,6 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
return true;
}
- /**
- * Prevent mobile data connections from being established,
- * or once again allow mobile data connections. If the state
- * toggles, then either tear down or set up data, as
- * appropriate to match the new state.
- * <p>This operation only affects the default connection
- * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data
- * @return {@code true} if the operation succeeded
- */
- public boolean setDataEnabled(boolean enable) {
-
- boolean isEnabled = isEnabled(EXTERNAL_NETWORK_DEFAULT_ID);
-
- Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled);
- if (!isEnabled && enable) {
- setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, true);
- sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
- } else if (!enable) {
- setEnabled(EXTERNAL_NETWORK_DEFAULT_ID, false);
- Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
- msg.arg1 = 1; // tearDown is true
- msg.obj = Phone.REASON_DATA_DISABLED;
- sendMessage(msg);
- }
- return true;
- }
-
- /**
- * Report the current state of data connectivity (enabled or disabled)
- * @return {@code false} if data connectivity has been explicitly disabled,
- * {@code true} otherwise.
- */
- public boolean getDataEnabled() {
- return dataEnabled[EXTERNAL_NETWORK_DEFAULT_ID];
- }
-
- /**
- * Report on whether data connectivity is enabled
- * @return {@code false} if data connectivity has been explicitly disabled,
- * {@code true} otherwise.
- */
- public boolean getAnyDataEnabled() {
- for (int i=0; i < EXTERNAL_NETWORK_NUM_TYPES; i++) {
- if (isEnabled(i)) return true;
- }
- return false;
- }
-
private boolean isDataAllowed() {
boolean roaming = phone.getServiceState().getRoaming();
return getAnyDataEnabled() && (!roaming || getDataOnRoamingEnabled());
@@ -359,7 +324,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
&& (mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
phone.getState() == Phone.State.IDLE )
&& isDataAllowed()
- && desiredPowerState) {
+ && desiredPowerState
+ && !mPendingRestartRadio) {
return setupData(reason);
@@ -375,7 +341,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
" dataEnabled=" + getAnyDataEnabled() +
" roaming=" + roaming +
" dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
- " desiredPowerState=" + desiredPowerState);
+ " desiredPowerState=" + desiredPowerState +
+ " PendingRestartRadio=" + mPendingRestartRadio);
}
return false;
}
@@ -459,9 +426,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
setState(State.CONNECTED);
phone.notifyDataConnection(reason);
startNetStatPoll();
- // reset reconnect timer
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
- mReregisterOnReconnectFailure = false;
+ mRetryMgr.resetRetryCount();
}
private void resetPollStats() {
@@ -488,16 +453,11 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
}
protected void restartRadio() {
- Log.d(LOG_TAG, "************TURN OFF RADIO**************");
+ if (DBG) log("Cleanup connection and wait " +
+ (TIME_DELAYED_TO_RESTART_RADIO / 1000) + "s to restart radio");
cleanUpConnection(true, Phone.REASON_RADIO_TURNED_OFF);
- phone.mCM.setRadioPower(false, null);
- /* Note: no need to call setRadioPower(true). Assuming the desired
- * radio power state is still ON (as tracked by ServiceStateTracker),
- * ServiceStateTracker will call setRadioPower when it receives the
- * RADIO_STATE_CHANGED notification for the power off. And if the
- * desired power state has changed in the interim, we don't want to
- * override it with an unconditional power on.
- */
+ sendEmptyMessageDelayed(EVENT_RESTART_RADIO, TIME_DELAYED_TO_RESTART_RADIO);
+ mPendingRestartRadio = true;
}
private Runnable mPollNetStat = new Runnable() {
@@ -541,10 +501,10 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
sentSinceLastRecv = 0;
newActivity = Activity.DATAIN;
} else if (sent == 0 && received == 0) {
- newActivity = Activity.NONE;
+ newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE;
} else {
sentSinceLastRecv = 0;
- newActivity = Activity.NONE;
+ newActivity = (activity == Activity.DORMANT) ? activity : Activity.NONE;
}
if (activity != newActivity) {
@@ -608,8 +568,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
private boolean retryAfterDisconnected(String reason) {
boolean retry = true;
- if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
- Phone.REASON_DATA_DISABLED.equals(reason) ) {
+ if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
retry = false;
}
return retry;
@@ -617,21 +576,13 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
if (state == State.FAILED) {
- if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
- if (mReregisterOnReconnectFailure) {
- // We have already tried to re-register to the network.
- // This might be a problem with the data network.
- nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
- } else {
- // Try to Re-register to the network.
- Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
- mReregisterOnReconnectFailure = true;
- mCdmaPhone.mSST.reRegisterNetwork(null);
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
- return;
- }
- }
-
+ /**
+ * For now With CDMA we never try to reconnect on
+ * error and instead just continue to retry
+ * at the last time until the state is changed.
+ * TODO: Make this configurable?
+ */
+ int nextReconnectDelay = mRetryMgr.getRetryTimer();
Log.d(LOG_TAG, "Data Connection activate failed. Scheduling next attempt for "
+ (nextReconnectDelay / 1000) + "s");
@@ -645,8 +596,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
SystemClock.elapsedRealtime() + nextReconnectDelay,
mReconnectIntent);
- // double it for next time
- nextReconnectDelay *= 2;
+ mRetryMgr.increaseRetryCount();
if (!shouldPostNotification(lastFailCauseCode)) {
Log.d(LOG_TAG,"NOT Posting Data Connection Unavailable notification "
@@ -678,8 +628,8 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
/**
* @override com.android.internal.telephony.DataConnectionTracker
*/
- protected void onTrySetupData(String reason) {
- trySetupData(reason);
+ protected boolean onTrySetupData(String reason) {
+ return trySetupData(reason);
}
/**
@@ -723,10 +673,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
* @override com.android.internal.telephony.DataConnectionTracker
*/
protected void onRadioOffOrNotAvailable() {
- // Make sure our reconnect delay starts at the initial value
- // next time the radio comes on
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
- mReregisterOnReconnectFailure = false;
+ mRetryMgr.resetRetryCount();
if (phone.getSimulatedRadioControl() != null) {
// Assume data is connected on the simulator
@@ -773,6 +720,20 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
reason = (String) ar.userObj;
}
setState(State.IDLE);
+
+ // Since the pending request to turn off or restart radio will be processed here,
+ // remove the pending event to restart radio from the message queue.
+ if (mPendingRestartRadio) removeMessages(EVENT_RESTART_RADIO);
+
+ // Process the pending request to turn off radio in ServiceStateTracker first.
+ // If radio is turned off in ServiceStateTracker, ignore the pending event to restart radio.
+ CdmaServiceStateTracker ssTracker = mCdmaPhone.mSST;
+ if (ssTracker.processPendingRadioPowerOffAfterDataOff()) {
+ mPendingRestartRadio = false;
+ } else {
+ onRestartRadio();
+ }
+
phone.notifyDataConnection(reason);
if (retryAfterDisconnected(reason)) {
trySetupData(reason);
@@ -802,9 +763,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
resetPollStats();
}
} else {
- // reset reconnect timer
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
- mReregisterOnReconnectFailure = false;
+ mRetryMgr.resetRetryCount();
// in case data setup was attempted when we were on a voice call
trySetupData(Phone.REASON_VOICE_CALL_ENDED);
}
@@ -840,7 +799,7 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
} else {
if (state == State.FAILED) {
cleanUpConnection(false, Phone.REASON_CDMA_DATA_DETACHED);
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
int bsid = (loc != null) ? loc.getBaseStationId() : -1;
@@ -853,6 +812,37 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
}
}
+ private void onCdmaOtaProvision(AsyncResult ar) {
+ if (ar.exception != null) {
+ int [] otaPrivision = (int [])ar.result;
+ if ((otaPrivision != null) && (otaPrivision.length > 1)) {
+ switch (otaPrivision[0]) {
+ case Phone.CDMA_OTA_PROVISION_STATUS_COMMITTED:
+ case Phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED:
+ mRetryMgr.resetRetryCount();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ private void onRestartRadio() {
+ if (mPendingRestartRadio) {
+ Log.d(LOG_TAG, "************TURN OFF RADIO**************");
+ phone.mCM.setRadioPower(false, null);
+ /* Note: no need to call setRadioPower(true). Assuming the desired
+ * radio power state is still ON (as tracked by ServiceStateTracker),
+ * ServiceStateTracker will call setRadioPower when it receives the
+ * RADIO_STATE_CHANGED notification for the power off. And if the
+ * desired power state has changed in the interim, we don't want to
+ * override it with an unconditional power on.
+ */
+ mPendingRestartRadio = false;
+ }
+ }
+
private void writeEventLogCdmaDataDrop() {
CdmaCellLocation loc = (CdmaCellLocation)(phone.getCellLocation());
int bsid = (loc != null) ? loc.getBaseStationId() : -1;
@@ -905,28 +895,28 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
}
}
- String getInterfaceName() {
+ protected String getInterfaceName(String apnType) {
if (mActiveDataConnection != null) {
return mActiveDataConnection.getInterface();
}
return null;
}
- protected String getIpAddress() {
+ protected String getIpAddress(String apnType) {
if (mActiveDataConnection != null) {
return mActiveDataConnection.getIpAddress();
}
return null;
}
- String getGateway() {
+ protected String getGateway(String apnType) {
if (mActiveDataConnection != null) {
return mActiveDataConnection.getGatewayAddress();
}
return null;
}
- protected String[] getDnsServers() {
+ protected String[] getDnsServers(String apnType) {
if (mActiveDataConnection != null) {
return mActiveDataConnection.getDnsServers();
}
@@ -961,6 +951,15 @@ public final class CdmaDataConnectionTracker extends DataConnectionTracker {
onDataStateChanged((AsyncResult) msg.obj);
break;
+ case EVENT_CDMA_OTA_PROVISION:
+ onCdmaOtaProvision((AsyncResult) msg.obj);
+ break;
+
+ case EVENT_RESTART_RADIO:
+ if (DBG) log("EVENT_RESTART_RADIO");
+ onRestartRadio();
+ break;
+
default:
// handle the message in the super class DataConnectionTracker
super.handleMessage(msg);
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index ecdc8f6..bf42257 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -19,18 +19,23 @@ package com.android.internal.telephony.cdma;
import android.app.Activity;
import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
import android.content.ContentValues;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.SystemProperties;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
import android.preference.PreferenceManager;
import android.util.Config;
import android.util.Log;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage.MessageClass;
+import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
@@ -43,6 +48,7 @@ import com.android.internal.util.HexDump;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
+import java.lang.Boolean;
final class CdmaSMSDispatcher extends SMSDispatcher {
@@ -75,13 +81,31 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
return Intents.RESULT_SMS_GENERIC_ERROR;
}
+ String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+ if (inEcm.equals("true")) {
+ return Intents.RESULT_SMS_GENERIC_ERROR;
+ }
+
// Decode BD stream and set sms variables.
SmsMessage sms = (SmsMessage) smsb;
sms.parseSms();
int teleService = sms.getTeleService();
boolean handled = false;
- if (sms.getUserData() == null) {
+ if ((SmsEnvelope.TELESERVICE_VMN == teleService) ||
+ (SmsEnvelope.TELESERVICE_MWI == teleService)) {
+ // handling Voicemail
+ int voicemailCount = sms.getNumOfVoicemails();
+ Log.d(TAG, "Voicemail count=" + voicemailCount);
+ // Store the voicemail count in preferences.
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
+ ((CDMAPhone) mPhone).getContext());
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
+ editor.commit();
+ ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
+ handled = true;
+ } else if ((sms.getUserData() == null)) {
if (Config.LOGD) {
Log.d(TAG, "Received SMS without user data");
}
@@ -92,24 +116,27 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
return Intents.RESULT_SMS_HANDLED;
}
- if (SmsEnvelope.TELESERVICE_WAP == teleService){
+ if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) {
+ // It's a storable message and there's no storage available. Bail.
+ // (See C.S0015-B v2.0 for a description of "Immediate Display"
+ // messages, which we represent as CLASS_0.)
+ return Intents.RESULT_SMS_OUT_OF_MEMORY;
+ }
+
+ if (SmsEnvelope.TELESERVICE_WAP == teleService) {
return processCdmaWapPdu(sms.getUserData(), sms.messageRef,
sms.getOriginatingAddress());
- } else if (SmsEnvelope.TELESERVICE_VMN == teleService) {
- // handling Voicemail
- int voicemailCount = sms.getNumOfVoicemails();
- Log.d(TAG, "Voicemail count=" + voicemailCount);
- // Store the voicemail count in preferences.
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
- ((CDMAPhone) mPhone).getContext());
- SharedPreferences.Editor editor = sp.edit();
- editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount);
- editor.commit();
- ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount);
- return Intents.RESULT_SMS_HANDLED;
}
- /**
+ // Reject (NAK) any messages with teleservice ids that have
+ // not yet been handled and also do not correspond to the two
+ // kinds that are processed below.
+ if ((SmsEnvelope.TELESERVICE_WMT != teleService) &&
+ (SmsEnvelope.TELESERVICE_WEMT != teleService)) {
+ return Intents.RESULT_SMS_UNSUPPORTED;
+ }
+
+ /*
* TODO(cleanup): Why are we using a getter method for this
* (and for so many other sms fields)? Trivial getters and
* setters like this are direct violations of the style guide.
@@ -122,11 +149,12 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
*/
SmsHeader smsHeader = sms.getUserDataHeader();
- /**
+ /*
* TODO(cleanup): Since both CDMA and GSM use the same header
* format, this dispatch processing is naturally identical,
* and code should probably not be replicated explicitly.
*/
+
// See if message is partial or port addressed.
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
// Message is not partial (not part of concatenated sequence).
@@ -274,6 +302,22 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
}
/** {@inheritDoc} */
+ protected void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
+ scAddr, destAddr, destPort, data, (deliveryIntent != null));
+ sendSubmitPdu(pdu, sentIntent, deliveryIntent);
+ }
+
+ /** {@inheritDoc} */
+ protected void sendText(String destAddr, String scAddr, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
+ scAddr, destAddr, text, (deliveryIntent != null), null);
+ sendSubmitPdu(pdu, sentIntent, deliveryIntent);
+ }
+
+ /** {@inheritDoc} */
protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) {
@@ -317,10 +361,20 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
}
}
- protected void sendSubmitPdu(SmsMessage.SubmitPdu submitPdu, PendingIntent sentIntent,
- PendingIntent deliveryIntent) {
- sendRawPdu(submitPdu.encodedScAddress, submitPdu.encodedMessage,
- sentIntent, deliveryIntent);
+ protected void sendSubmitPdu(SmsMessage.SubmitPdu pdu,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {
+ if (sentIntent != null) {
+ try {
+ sentIntent.send(SmsManager.RESULT_ERROR_NO_SERVICE);
+ } catch (CanceledException ex) {}
+ }
+ if (Config.LOGD) {
+ Log.d(TAG, "Block SMS in Emergency Callback mode");
+ }
+ return;
+ }
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
}
/** {@inheritDoc} */
@@ -343,6 +397,12 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
/** {@inheritDoc} */
protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){
// FIXME unit test leaves cm == null. this should change
+
+ String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+ if (inEcm.equals("true")) {
+ return;
+ }
+
if (mCm != null) {
mCm.acknowledgeLastIncomingCdmaSms(success, resultToCause(result), response);
}
@@ -365,15 +425,17 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
private int resultToCause(int rc) {
switch (rc) {
- case Activity.RESULT_OK:
- case Intents.RESULT_SMS_HANDLED:
- // Cause code is ignored on success.
- return 0;
- case Intents.RESULT_SMS_OUT_OF_MEMORY:
- return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE;
- case Intents.RESULT_SMS_GENERIC_ERROR:
- default:
- return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM;
+ case Activity.RESULT_OK:
+ case Intents.RESULT_SMS_HANDLED:
+ // Cause code is ignored on success.
+ return 0;
+ case Intents.RESULT_SMS_OUT_OF_MEMORY:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_RESOURCE_SHORTAGE;
+ case Intents.RESULT_SMS_UNSUPPORTED:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_INVALID_TELESERVICE_ID;
+ case Intents.RESULT_SMS_GENERIC_ERROR:
+ default:
+ return CommandsInterface.CDMA_SMS_FAIL_CAUSE_OTHER_TERMINAL_PROBLEM;
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 23a0520..9ac78eb 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -41,28 +41,19 @@ import android.util.EventLog;
import android.util.Log;
import android.util.Config;
import android.util.TimeUtils;
-import java.util.Calendar;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.DataConnectionTracker;
-// pretty sure importing stuff from GSM is bad:
-import com.android.internal.telephony.gsm.MccTable;
-import com.android.internal.telephony.PhoneProxy;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyEventLog;
import com.android.internal.telephony.TelephonyIntents;
-
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISMANUAL;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
@@ -70,14 +61,14 @@ import java.util.TimeZone;
* {@hide}
*/
final class CdmaServiceStateTracker extends ServiceStateTracker {
+ static final String LOG_TAG = "CDMA";
- //***** Instance Variables
CDMAPhone phone;
CdmaCellLocation cellLoc;
CdmaCellLocation newCellLoc;
/**
- * The access technology currently in use: DATA_ACCESS_
+ * Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
*/
private int networkType = 0;
private int newNetworkType = 0;
@@ -87,16 +78,21 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
private boolean mIsInPrl;
private int mDefaultRoamingIndicator;
- // Initially we assume no data connection
+ /**
+ * Initially assume no data connection.
+ */
private int cdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
private int newCdmaDataConnectionState = ServiceState.STATE_OUT_OF_SERVICE;
private int mRegistrationState = -1;
private RegistrantList cdmaDataConnectionAttachedRegistrants = new RegistrantList();
private RegistrantList cdmaDataConnectionDetachedRegistrants = new RegistrantList();
+ private RegistrantList cdmaForSubscriptionInfoReadyRegistrants = new RegistrantList();
- // Sometimes we get the NITZ time before we know what country we are in.
- // Keep the time zone information from the NITZ string so we can fix
- // the time zone once know the country.
+ /**
+ * Sometimes we get the NITZ time before we know what country we
+ * are in. Keep the time zone information from the NITZ string so
+ * we can fix the time zone once know the country.
+ */
private boolean mNeedFixZone = false;
private int mZoneOffset;
private boolean mZoneDst;
@@ -106,33 +102,37 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
long mSavedTime;
long mSavedAtTime;
- // We can't register for SIM_RECORDS_LOADED immediately because the
- // SIMRecords object may not be instantiated yet.
+ /**
+ * We can't register for SIM_RECORDS_LOADED immediately because the
+ * SIMRecords object may not be instantiated yet.
+ */
private boolean mNeedToRegForRuimLoaded = false;
- // Wake lock used while setting time of day.
+ /** Wake lock used while setting time of day. */
private PowerManager.WakeLock mWakeLock;
private static final String WAKELOCK_TAG = "ServiceStateTracker";
- // Keep track of SPN display rules, so we only broadcast intent if something changes.
+ /** Track of SPN display rules, so we only broadcast intent if something changes. */
private String curSpn = null;
- private String curPlmn = null; // it contains the name of the registered network in CDMA can
- // be the ONS or ERI text
private int curSpnRule = 0;
+ /** Contains the name of the registered network in CDMA (either ONS or ERI text). */
+ private String curPlmn = null;
+
private String mMdn;
- private int mHomeSystemId;
- private int mHomeNetworkId;
+ private int mHomeSystemId[] = null;
+ private int mHomeNetworkId[] = null;
private String mMin;
+ private String mPrlVersion;
+ private boolean mIsMinInfoReady = false;
private boolean isEriTextLoaded = false;
private boolean isSubscriptionFromRuim = false;
- // Registration Denied Reason, General/Authentication Failure, used only for debugging purposes
- private String mRegistrationDeniedReason;
+ private boolean mPendingRadioPowerOffAfterDataOff = false;
- //***** Constants
- static final String LOG_TAG = "CDMA";
+ /* Used only for debugging purposes. */
+ private String mRegistrationDeniedReason;
private ContentResolver cr;
private String currentCarrier = null;
@@ -146,9 +146,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
};
-
- //***** Constructors
-
public CdmaServiceStateTracker(CDMAPhone phone) {
super();
@@ -175,8 +172,9 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
cm.registerForNVReady(this, EVENT_NV_READY, null);
phone.registerForEriFileLoaded(this, EVENT_ERI_FILE_LOADED, null);
+ cm.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
- // system setting property AIRPLANE_MODE_ON is set in Settings.
+ // System setting property AIRPLANE_MODE_ON is set in Settings.
int airplaneMode = Settings.System.getInt(
phone.getContext().getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0);
@@ -192,12 +190,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
public void dispose() {
- //Unregister for all events
+ // Unregister for all events.
cm.unregisterForAvailable(this);
cm.unregisterForRadioStateChanged(this);
cm.unregisterForNetworkStateChanged(this);
cm.unregisterForRUIMReady(this);
cm.unregisterForNVReady(this);
+ cm.unregisterForCdmaOtaProvision(this);
phone.unregisterForEriFileLoaded(this);
phone.mRuimRecords.unregisterForRecordsLoaded(this);
cm.unSetOnSignalStrengthUpdate(this);
@@ -229,8 +228,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void
- registerForCdmaDataConnectionAttached(Handler h, int what, Object obj) {
+ void registerForCdmaDataConnectionAttached(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
cdmaDataConnectionAttachedRegistrants.add(r);
@@ -238,6 +236,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
r.notifyRegistrant();
}
}
+
void unregisterForCdmaDataConnectionAttached(Handler h) {
cdmaDataConnectionAttachedRegistrants.remove(h);
}
@@ -248,8 +247,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void
- registerForCdmaDataConnectionDetached(Handler h, int what, Object obj) {
+ void registerForCdmaDataConnectionDetached(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
cdmaDataConnectionDetachedRegistrants.add(r);
@@ -257,15 +255,28 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
r.notifyRegistrant();
}
}
+
void unregisterForCdmaDataConnectionDetached(Handler h) {
cdmaDataConnectionDetachedRegistrants.remove(h);
}
- //***** Called from CDMAPhone
- public void
- getLacAndCid(Message onComplete) {
- cm.getRegistrationState(obtainMessage(
- EVENT_GET_LOC_DONE_CDMA, onComplete));
+ /**
+ * Registration point for subscription info ready
+ * @param h handler to notify
+ * @param what what code of message when delivered
+ * @param obj placed in Message.obj
+ */
+ public void registerForSubscriptionInfoReady(Handler h, int what, Object obj) {
+ Registrant r = new Registrant(h, what, obj);
+ cdmaForSubscriptionInfoReadyRegistrants.add(r);
+
+ if (isMinInfoReady()) {
+ r.notifyRegistrant();
+ }
+ }
+
+ public void unregisterForSubscriptionInfoReady(Handler h) {
+ cdmaForSubscriptionInfoReadyRegistrants.remove(h);
}
@Override
@@ -276,37 +287,41 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
switch (msg.what) {
case EVENT_RADIO_AVAILABLE:
- //this is unnecessary
- //setPowerStateToDesired();
break;
case EVENT_RUIM_READY:
- // The RUIM is now ready i.e if it was locked
- // it has been unlocked. At this stage, the radio is already
- // powered on.
+ // The RUIM is now ready i.e if it was locked it has been
+ // unlocked. At this stage, the radio is already powered on.
isSubscriptionFromRuim = true;
if (mNeedToRegForRuimLoaded) {
phone.mRuimRecords.registerForRecordsLoaded(this,
EVENT_RUIM_RECORDS_LOADED, null);
mNeedToRegForRuimLoaded = false;
}
- // restore the previous network selection.
+
+ cm.getCDMASubscription(obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
+ if (DBG) log("Receive EVENT_RUIM_READY and Send Request getCDMASubscription.");
+
+ // Restore the previous network selection.
pollState();
- // Signal strength polling stops when radio is off
+ // Signal strength polling stops when radio is off.
queueNextSignalStrengthPoll();
break;
case EVENT_NV_READY:
isSubscriptionFromRuim = false;
+ // For Non-RUIM phones, the subscription information is stored in
+ // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
+ // subscription info.
+ cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
pollState();
- // Signal strength polling stops when radio is off
+ // Signal strength polling stops when radio is off.
queueNextSignalStrengthPoll();
break;
case EVENT_RADIO_STATE_CHANGED:
- // This will do nothing in the radio not
- // available case
+ // This will do nothing in the 'radio not available' case.
setPowerStateToDesired();
pollState();
break;
@@ -317,10 +332,10 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
case EVENT_GET_SIGNAL_STRENGTH:
// This callback is called when signal strength is polled
- // all by itself
+ // all by itself.
if (!(cm.getRadioState().isOn()) || (cm.getRadioState().isGsm())) {
- // Polling will continue when radio turns back on
+ // Polling will continue when radio turns back on.
return;
}
ar = (AsyncResult) msg.obj;
@@ -356,31 +371,73 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
}
- // only update if cell location really changed
- if (cellLoc.getBaseStationId() != baseStationData[0]
- || cellLoc.getBaseStationLatitude() != baseStationData[1]
- || cellLoc.getBaseStationLongitude() != baseStationData[2]) {
- cellLoc.setCellLocationData(baseStationData[0],
- baseStationData[1],
- baseStationData[2]);
- phone.notifyLocationChanged();
- }
+ cellLoc.setCellLocationData(baseStationData[0],
+ baseStationData[1], baseStationData[2]);
+ phone.notifyLocationChanged();
}
- if (ar.userObj != null) {
- AsyncResult.forMessage(((Message) ar.userObj)).exception
- = ar.exception;
- ((Message) ar.userObj).sendToTarget();
- }
+ // Release any temporary cell lock, which could have been
+ // aquired to allow a single-shot location update.
+ disableSingleLocationUpdate();
break;
case EVENT_POLL_STATE_REGISTRATION_CDMA:
case EVENT_POLL_STATE_OPERATOR_CDMA:
- case EVENT_POLL_STATE_CDMA_SUBSCRIPTION:
ar = (AsyncResult) msg.obj;
handlePollStateResult(msg.what, ar);
break;
+ case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
+ ar = (AsyncResult) msg.obj;
+
+ if (ar.exception == null) {
+ String cdmaSubscription[] = (String[])ar.result;
+ if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
+ mMdn = cdmaSubscription[0];
+ if (cdmaSubscription[1] != null) {
+ String[] sid = cdmaSubscription[1].split(",");
+ mHomeSystemId = new int[sid.length];
+ for (int i = 0; i < sid.length; i++) {
+ try {
+ mHomeSystemId[i] = Integer.parseInt(sid[i]);
+ } catch (NumberFormatException ex) {
+ Log.e(LOG_TAG, "error parsing system id: ", ex);
+ }
+ }
+ }
+ Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION SID=" + cdmaSubscription[1] );
+
+ if (cdmaSubscription[2] != null) {
+ String[] nid = cdmaSubscription[2].split(",");
+ mHomeNetworkId = new int[nid.length];
+ for (int i = 0; i < nid.length; i++) {
+ try {
+ mHomeNetworkId[i] = Integer.parseInt(nid[i]);
+ } catch (NumberFormatException ex) {
+ Log.e(LOG_TAG, "error parsing network id: ", ex);
+ }
+ }
+ }
+ Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION NID=" + cdmaSubscription[2] );
+ mMin = cdmaSubscription[3];
+ mPrlVersion = cdmaSubscription[4];
+ Log.d(LOG_TAG,"GET_CDMA_SUBSCRIPTION MDN=" + mMdn);
+ //Notify apps subscription info is ready
+ if (cdmaForSubscriptionInfoReadyRegistrants != null) {
+ cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
+ }
+ if (!mIsMinInfoReady) {
+ mIsMinInfoReady = true;
+ }
+ phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
+ null);
+ } else {
+ Log.w(LOG_TAG,"error parsing cdmaSubscription params num="
+ + cdmaSubscription.length);
+ }
+ }
+ break;
+
case EVENT_POLL_SIGNAL_STRENGTH:
// Just poll signal strength...not part of pollState()
@@ -397,13 +454,12 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
break;
case EVENT_SIGNAL_STRENGTH_UPDATE:
- // This is a notification from
- // CommandsInterface.setOnSignalStrengthUpdate
+ // This is a notification from CommandsInterface.setOnSignalStrengthUpdate.
ar = (AsyncResult) msg.obj;
- // The radio is telling us about signal strength changes
- // we don't have to ask it
+ // The radio is telling us about signal strength changes,
+ // so we don't have to ask it.
dontPollSignalStrength = true;
onSignalStrengthResult(ar);
@@ -417,16 +473,39 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- getLacAndCid(null);
+ cm.getRegistrationState(obtainMessage(EVENT_GET_LOC_DONE_CDMA, null));
}
break;
case EVENT_ERI_FILE_LOADED:
- // Repoll the state once the ERI file has been loaded
+ // Repoll the state once the ERI file has been loaded.
if (DBG) log("[CdmaServiceStateTracker] ERI file has been loaded, repolling.");
pollState();
break;
+ case EVENT_OTA_PROVISION_STATUS_CHANGE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ ints = (int[]) ar.result;
+ int otaStatus = ints[0];
+ if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_COMMITTED
+ || otaStatus == phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
+ Log.d(LOG_TAG, "Received OTA_PROGRAMMING Complete,Reload MDN ");
+ cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
+ }
+ }
+ break;
+
+ case EVENT_SET_RADIO_POWER_OFF:
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff) {
+ if (DBG) log("EVENT_SET_RADIO_OFF, turn radio off now.");
+ cm.setRadioPower(false, null);
+ mPendingRadioPowerOffAfterDataOff = false;
+ }
+ }
+ break;
+
default:
Log.e(LOG_TAG, "Unhandled message with number: " + msg.what);
break;
@@ -455,19 +534,23 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF;
dcTracker.sendMessage(msg);
- // Poll data state up to 15 times, with a 100ms delay
- // totaling 1.5 sec. Normal data disable action will finish in 100ms.
- for (int i = 0; i < MAX_NUM_DATA_STATE_READS; i++) {
- DataConnectionTracker.State currentState = dcTracker.getState();
- if (currentState != DataConnectionTracker.State.CONNECTED
- && currentState != DataConnectionTracker.State.DISCONNECTING) {
- if (DBG) log("Data shutdown complete.");
- break;
+ synchronized(this) {
+ if (!mPendingRadioPowerOffAfterDataOff) {
+ DataConnectionTracker.State currentState = dcTracker.getState();
+ if (currentState != DataConnectionTracker.State.CONNECTED
+ && currentState != DataConnectionTracker.State.DISCONNECTING) {
+ if (DBG) log("Data disconnected, turn off radio right away.");
+ cm.setRadioPower(false, null);
+ }
+ else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 5000)) {
+ if (DBG) log("Wait 5 sec for data to be disconnected, then turn off radio.");
+ mPendingRadioPowerOffAfterDataOff = true;
+ } else {
+ Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away.");
+ cm.setRadioPower(false, null);
+ }
}
- SystemClock.sleep(DATA_STATE_POLL_SLEEP_MS);
}
- // If it's on and available and we want it off..
- cm.setRadioPower(false, null);
} // Otherwise, we're in the desired state
}
@@ -518,7 +601,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
int ints[];
String states[];
- // Ignore stale requests from last poll
+ // Ignore stale requests from last poll.
if (ar.userObj != pollingContext) return;
if (ar.exception != null) {
@@ -529,13 +612,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
if (err == CommandException.Error.RADIO_NOT_AVAILABLE) {
- // Radio has crashed or turned off
+ // Radio has crashed or turned off.
cancelPollState();
return;
}
if (!cm.getRadioState().isOn()) {
- // Radio has crashed or turned off
+ // Radio has crashed or turned off.
cancelPollState();
return;
}
@@ -589,10 +672,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
mRegistrationState = registrationState;
- mCdmaRoaming = regCodeIsRoaming(registrationState);
+ // When registration state is roaming and TSB58
+ // roaming indicator is not in the carrier-specified
+ // list of ERIs for home system, mCdmaRoaming is true.
+ mCdmaRoaming =
+ regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
newSS.setState (regCodeToServiceState(registrationState));
- this.newCdmaDataConnectionState = radioTechnologyToDataServiceState(radioTechnology);
+ this.newCdmaDataConnectionState =
+ radioTechnologyToDataServiceState(radioTechnology);
newSS.setRadioTechnology(radioTechnology);
newNetworkType = radioTechnology;
@@ -603,7 +691,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
mDefaultRoamingIndicator = defaultRoamingIndicator;
- // values are -1 if not available
+ // Values are -1 if not available.
newCellLoc.setCellLocationData(baseStationId, baseStationLatitude,
baseStationLongitude, systemId, networkId);
@@ -625,8 +713,8 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
if (opNames != null && opNames.length >= 3) {
if (cm.getRadioState().isNVReady()) {
- // In CDMA in case on NV the ss.mOperatorAlphaLong is set later with the
- // ERI text, so here it is ignored what is coming from the modem
+ // In CDMA in case on NV, the ss.mOperatorAlphaLong is set later with the
+ // ERI text, so here it is ignored what is coming from the modem.
newSS.setOperatorName(null, opNames[1], opNames[2]);
} else {
newSS.setOperatorName(opNames[0], opNames[1], opNames[2]);
@@ -636,27 +724,6 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
break;
- case EVENT_POLL_STATE_CDMA_SUBSCRIPTION: // Handle RIL_CDMA_SUBSCRIPTION
- String cdmaSubscription[] = (String[])ar.result;
-
- if (cdmaSubscription != null && cdmaSubscription.length >= 4) {
- mMdn = cdmaSubscription[0];
- // TODO: Only grabbing the first SID/NID for now.
- if (cdmaSubscription[1] != null) {
- String[] sid = cdmaSubscription[1].split(",");
- mHomeSystemId = sid.length > 0 ? Integer.parseInt(sid[0]) : 0;
- }
- if (cdmaSubscription[2] != null) {
- String[] nid = cdmaSubscription[2].split(",");
- mHomeNetworkId = nid.length > 0 ? Integer.parseInt(nid[0]) : 0;
- }
- mMin = cdmaSubscription[3];
-
- } else {
- Log.w(LOG_TAG, "error parsing cdmaSubscription");
- }
- break;
-
default:
Log.e(LOG_TAG, "RIL response handle in wrong phone!"
+ " Expected CDMA RIL request and get GSM RIL request.");
@@ -672,7 +739,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
if (pollingContext[0] == 0) {
boolean namMatch = false;
- if ((mHomeSystemId != 0) && (mHomeSystemId == newSS.getSystemId()) ) {
+ if (!isSidsAllZeros() && isHomeSid(newSS.getSystemId())) {
namMatch = true;
}
@@ -684,33 +751,42 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
// Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
- // TODO(Teleca): Validate this is correct.
- if (mIsInPrl) {
- if (namMatch && (mRoamingIndicator <= 2)) {
- // System is acquired, prl match, nam match and mRoamingIndicator <= 2
- newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
- } else {
- // System is acquired, prl match, no nam match or mRoamingIndicator > 2
- newSS.setCdmaRoamingIndicator(mRoamingIndicator);
- }
- } else {
- if (mRegistrationState == 5) {
- // System is acquired but prl not loaded or no prl match
+ newSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
+ newSS.setCdmaRoamingIndicator(mRoamingIndicator);
+ boolean isPrlLoaded = true;
+ if (TextUtils.isEmpty(mPrlVersion)) {
+ isPrlLoaded = false;
+ }
+ if (!isPrlLoaded) {
+ newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
+ } else if (!isSidsAllZeros()) {
+ if (!namMatch && !mIsInPrl) {
+ // Use default
+ newSS.setCdmaRoamingIndicator(mDefaultRoamingIndicator);
+ } else if (namMatch && !mIsInPrl) {
newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_FLASH);
+ } else if (!namMatch && mIsInPrl) {
+ // Use the one from PRL/ERI
+ newSS.setCdmaRoamingIndicator(mRoamingIndicator);
} else {
- // Use the default indicator
+ // It means namMatch && mIsInPrl
+ if ((mRoamingIndicator <= 2)) {
+ newSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
+ } else {
+ // Use the one from PRL/ERI
+ newSS.setCdmaRoamingIndicator(mRoamingIndicator);
+ }
}
}
- newSS.setCdmaDefaultRoamingIndicator(mDefaultRoamingIndicator);
-
- // NOTE: Some operator may require to override the mCdmaRoaming (set by the modem)
- // depending on the mRoamingIndicator.
+ // NOTE: Some operator may require overriding mCdmaRoaming
+ // (set by the modem), depending on the mRoamingIndicator.
if (DBG) {
log("Set CDMA Roaming Indicator to: " + newSS.getCdmaRoamingIndicator()
- + ". mCdmaRoaming = " + mCdmaRoaming + ", namMatch = " + namMatch
- + ", mIsInPrl = " + mIsInPrl + ", mRoamingIndicator = " + mRoamingIndicator
+ + ". mCdmaRoaming = " + mCdmaRoaming + ", isPrlLoaded = " + isPrlLoaded
+ + ". namMatch = " + namMatch + " , mIsInPrl = " + mIsInPrl
+ + ", mRoamingIndicator = " + mRoamingIndicator
+ ", mDefaultRoamingIndicator= " + mDefaultRoamingIndicator);
}
pollStateDone();
@@ -764,18 +840,13 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
setSignalStrengthDefaultValues();
mGotCountryCode = false;
- //NOTE: pollStateDone() is not needed in this case
+ // NOTE: pollStateDone() is not needed in this case
break;
default:
- // Issue all poll-related commands at once
- // then count down the responses, which
- // are allowed to arrive out-of-order
-
- pollingContext[0]++;
- // RIL_REQUEST_CDMA_SUBSCRIPTION is necessary for CDMA
- cm.getCDMASubscription(
- obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION, pollingContext));
+ // Issue all poll-related commands at once, then count
+ // down the responses which are allowed to arrive
+ // out-of-order.
pollingContext[0]++;
// RIL_REQUEST_OPERATOR is necessary for CDMA
@@ -904,7 +975,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
newSS.setStateOutOfService(); // clean slate for next time
if (hasNetworkTypeChanged) {
- phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
networkTypeToString(networkType));
}
@@ -931,14 +1002,14 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
String operatorNumeric;
- phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
ss.getOperatorAlphaLong());
operatorNumeric = ss.getOperatorNumeric();
- phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
if (operatorNumeric == null) {
- phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, "");
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
} else {
String isoCountryCode = "";
try{
@@ -950,14 +1021,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
}
- phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, isoCountryCode);
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY,
+ isoCountryCode);
mGotCountryCode = true;
if (mNeedFixZone) {
fixTimeZone(isoCountryCode);
}
}
- phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
ss.getRoaming() ? "true" : "false");
updateSpnDisplay();
@@ -972,7 +1044,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
cdmaDataConnectionDetachedRegistrants.notifyRegistrants();
}
- if (hasCdmaDataConnectionChanged) {
+ if (hasCdmaDataConnectionChanged || hasNetworkTypeChanged) {
phone.notifyDataConnection(null);
}
@@ -1047,7 +1119,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
/**
* send signal-strength-changed notification if changed
- * Called both for solicited and unsolicited signal stength updates
+ * Called both for solicited and unsolicited signal strength updates
*/
private void
onSignalStrengthResult(AsyncResult ar) {
@@ -1060,15 +1132,15 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
int[] ints = (int[])ar.result;
int offset = 2;
- int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -1;
- int cdmaEcio = (ints[offset+1] > 0) ? -ints[offset+1] : -1;
+ int cdmaDbm = (ints[offset] > 0) ? -ints[offset] : -120;
+ int cdmaEcio = (ints[offset+1] > 0) ? -ints[offset+1] : -160;
int evdoRssi = -1;
int evdoEcio = -1;
int evdoSnr = -1;
if ((networkType == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
|| (networkType == ServiceState.RADIO_TECHNOLOGY_EVDO_A)) {
- evdoRssi = (ints[offset+2] > 0) ? -ints[offset+2] : -1;
+ evdoRssi = (ints[offset+2] > 0) ? -ints[offset+2] : -120;
evdoEcio = (ints[offset+3] > 0) ? -ints[offset+3] : -1;
evdoSnr = ((ints[offset+4] > 0) && (ints[offset+4] <= 8)) ? ints[offset+4] : -1;
}
@@ -1152,6 +1224,33 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
/**
+ * Determine whether a roaming indicator is in the carrier-specified list of ERIs for
+ * home system
+ *
+ * @param roamInd roaming indicator in String
+ * @return true if the roamInd is in the carrier-specified list of ERIs for home network
+ */
+ private boolean isRoamIndForHomeSystem(String roamInd) {
+ // retrieve the carrier-specified list of ERIs for home system
+ String homeRoamIndcators = SystemProperties.get("ro.cdma.homesystem");
+
+ if (!TextUtils.isEmpty(homeRoamIndcators)) {
+ // searches through the comma-separated list for a match,
+ // return true if one is found.
+ for (String homeRoamInd : homeRoamIndcators.split(",")) {
+ if (homeRoamInd.equals(roamInd)) {
+ return true;
+ }
+ }
+ // no matches found against the list!
+ return false;
+ }
+
+ // no system property found for the roaming indicators for home system
+ return false;
+ }
+
+ /**
* Set roaming state when cdmaRoaming is true and ons is different from spn
* @param cdmaRoaming TS 27.007 7.2 CREG registered roaming
* @param s ServiceState hold current ons
@@ -1159,7 +1258,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
*/
private
boolean isRoamingBetweenOperators(boolean cdmaRoaming, ServiceState s) {
- String spn = SystemProperties.get(PROPERTY_ICC_OPERATOR_ALPHA, "empty");
+ String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
// NOTE: in case of RUIM we should completely ignore the ERI data file and
// mOperatorAlphaLong is set from RIL_REQUEST_OPERATOR response 0 (alpha ONS)
@@ -1243,7 +1342,7 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
zone = TimeZone.getTimeZone( tzname );
}
- String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY);
+ String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
if (zone == null) {
@@ -1396,12 +1495,36 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
}
}
+ private boolean isSidsAllZeros() {
+ if (mHomeSystemId != null) {
+ for (int i=0; i < mHomeSystemId.length; i++) {
+ if (mHomeSystemId[i] != 0) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Check whether a specified system ID that matches one of the home system IDs.
+ */
+ private boolean isHomeSid(int sid) {
+ if (mHomeSystemId != null) {
+ for (int i=0; i < mHomeSystemId.length; i++) {
+ if (sid == mHomeSystemId[i]) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
/**
* @return true if phone is camping on a technology
* that could support voice and data simultaneously.
*/
boolean isConcurrentVoiceAndData() {
-
// Note: it needs to be confirmed which CDMA network types
// can support voice and data calls concurrently.
// For the time-being, the return value will be false.
@@ -1420,14 +1543,20 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
return mMin;
}
+ /** Returns null if NV is not yet ready */
+ public String getPrlVersion() {
+ return mPrlVersion;
+ }
+
/**
* Returns IMSI as MCC + MNC + MIN
*/
- /*package*/ String getImsi() {
+ String getImsi() {
// TODO(Moto): When RUIM is enabled, IMSI will come from RUIM
// not build-time props. Moto will provide implementation
// for RUIM-ready case later.
- String operatorNumeric = SystemProperties.get(PROPERTY_ICC_OPERATOR_NUMERIC, "");
+ String operatorNumeric = SystemProperties.get(
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
if (!TextUtils.isEmpty(operatorNumeric) && getCdmaMin() != null) {
return (operatorNumeric + getCdmaMin());
@@ -1435,4 +1564,31 @@ final class CdmaServiceStateTracker extends ServiceStateTracker {
return null;
}
}
+
+ /**
+ * Check if subscription data has been assigned to mMin
+ *
+ * return true if MIN info is ready; false otherwise.
+ */
+ public boolean isMinInfoReady() {
+ return mIsMinInfoReady;
+ }
+
+ /**
+ * process the pending request to turn radio off after data is disconnected
+ *
+ * return true if there is pending request to process; false otherwise.
+ */
+ public boolean processPendingRadioPowerOffAfterDataOff() {
+ synchronized(this) {
+ if (mPendingRadioPowerOffAfterDataOff) {
+ if (DBG) log("Process pending request to turn radio off.");
+ removeMessages(EVENT_SET_RADIO_POWER_OFF);
+ cm.setRadioPower(false, null);
+ mPendingRadioPowerOffAfterDataOff = false;
+ return true;
+ }
+ return false;
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/EriManager.java b/telephony/java/com/android/internal/telephony/cdma/EriManager.java
index 6c1384c..44c6173 100644
--- a/telephony/java/com/android/internal/telephony/cdma/EriManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/EriManager.java
@@ -19,12 +19,19 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.os.Message;
import android.util.Log;
+import android.util.Xml;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneBase;
import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.util.HashMap;
/**
@@ -76,7 +83,8 @@ public final class EriManager {
}
}
- static final String LOG_TAG = "CDMA";
+ private static final String LOG_TAG = "CDMA";
+ private static final boolean DBG = true;
public static final int ERI_FROM_XML = 0;
public static final int ERI_FROM_FILE_SYSTEM = 1;
@@ -143,8 +151,30 @@ public final class EriManager {
*
*/
private void loadEriFileFromXml() {
+ XmlPullParser parser = null;
+ FileInputStream stream = null;
Resources r = mContext.getResources();
- XmlResourceParser parser = r.getXml(com.android.internal.R.xml.eri);
+
+ try {
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: check for alternate file");
+ stream = new FileInputStream(
+ r.getString(com.android.internal.R.string.alternate_eri_file));
+ parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: opened alternate file");
+ } catch (FileNotFoundException e) {
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: no alternate file");
+ parser = null;
+ } catch (XmlPullParserException e) {
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: no parser for alternate file");
+ parser = null;
+ }
+
+ if (parser == null) {
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: open normal file");
+ parser = r.getXml(com.android.internal.R.xml.eri);
+ }
+
try {
XmlUtils.beginDocument(parser, "EriFile");
mEriFile.mVersionNumber = Integer.parseInt(
@@ -187,12 +217,22 @@ public final class EriManager {
}
}
+ if (DBG) Log.d(LOG_TAG, "loadEriFileFromXml: eri parsing successful, file loaded");
isEriFileLoaded = true;
} catch (Exception e) {
Log.e(LOG_TAG, "Got exception while loading ERI file.", e);
} finally {
- parser.close();
+ if (parser instanceof XmlResourceParser) {
+ ((XmlResourceParser)parser).close();
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
}
}
@@ -345,16 +385,16 @@ public final class EriManager {
default:
if (!isEriFileLoaded) {
// ERI file NOT loaded
- Log.d(LOG_TAG, "ERI File not loaded");
+ if (DBG) Log.d(LOG_TAG, "ERI File not loaded");
if(defRoamInd > 2) {
- Log.d(LOG_TAG, "ERI defRoamInd > 2 ...flashing");
+ if (DBG) Log.d(LOG_TAG, "ERI defRoamInd > 2 ...flashing");
ret = new EriDisplayInformation(
EriInfo.ROAMING_INDICATOR_FLASH,
EriInfo.ROAMING_ICON_MODE_FLASH,
mContext.getText(com.android.internal
.R.string.roamingText2).toString());
} else {
- Log.d(LOG_TAG, "ERI defRoamInd <= 2");
+ if (DBG) Log.d(LOG_TAG, "ERI defRoamInd <= 2");
switch (defRoamInd) {
case EriInfo.ROAMING_INDICATOR_ON:
ret = new EriDisplayInformation(
@@ -386,12 +426,14 @@ public final class EriManager {
}
} else {
// ERI file loaded
- Log.d(LOG_TAG, "ERI File loaded");
+ if (DBG) Log.d(LOG_TAG, "ERI File loaded");
EriInfo eriInfo = getEriInfo(roamInd);
EriInfo defEriInfo = getEriInfo(defRoamInd);
if (eriInfo == null) {
- Log.d(LOG_TAG, "ERI roamInd " + roamInd
+ if (DBG) {
+ Log.d(LOG_TAG, "ERI roamInd " + roamInd
+ " not found in ERI file ...using defRoamInd " + defRoamInd);
+ }
if(defEriInfo == null) {
Log.e(LOG_TAG, "ERI defRoamInd " + defRoamInd
+ " not found in ERI file ...on");
@@ -402,14 +444,16 @@ public final class EriManager {
.R.string.roamingText0).toString());
} else {
- Log.d(LOG_TAG, "ERI defRoamInd " + defRoamInd + " found in ERI file");
+ if (DBG) {
+ Log.d(LOG_TAG, "ERI defRoamInd " + defRoamInd + " found in ERI file");
+ }
ret = new EriDisplayInformation(
defEriInfo.mIconIndex,
defEriInfo.mIconMode,
defEriInfo.mEriText);
}
} else {
- Log.d(LOG_TAG, "ERI roamInd " + roamInd + " found in ERI file");
+ if (DBG) Log.d(LOG_TAG, "ERI roamInd " + roamInd + " found in ERI file");
ret = new EriDisplayInformation(
eriInfo.mIconIndex,
eriInfo.mIconMode,
@@ -418,7 +462,7 @@ public final class EriManager {
}
break;
}
- Log.d(LOG_TAG, "Displaying ERI " + ret.toString());
+ if (DBG) Log.d(LOG_TAG, "Displaying ERI " + ret.toString());
return ret;
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java b/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java
deleted file mode 100644
index 23a4ac7..0000000
--- a/telephony/java/com/android/internal/telephony/cdma/FeatureCode.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2006 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.internal.telephony.cdma;
-
-import android.content.Context;
-import android.os.*;
-import android.util.Log;
-
-import com.android.internal.telephony.*;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- *
- * {@hide}
- *
- */
-public final class FeatureCode extends Handler implements MmiCode {
- static final String LOG_TAG = "CDMA";
-
- //***** Constants
-
- // Call Forwarding
- static final String FC_CF_ACTIVATE = "72";
- static final String FC_CF_DEACTIVATE = "73";
- static final String FC_CF_FORWARD_TO_NUMBER = "56";
-
- // Call Forwarding Busy Line
- static final String FC_CFBL_ACTIVATE = "90";
- static final String FC_CFBL_DEACTIVATE = "91";
- static final String FC_CFBL_FORWARD_TO_NUMBER = "40";
-
- // Call Forwarding Don't Answer
- static final String FC_CFDA_ACTIVATE = "92";
- static final String FC_CFDA_DEACTIVATE = "93";
- static final String FC_CFDA_FORWARD_TO_NUMBER = "42";
-
- // Cancel Call Waiting
- static final String FC_CCW = "70";
-
- // Usage Sensitive Three-way Calling
- static final String FC_3WC = "71";
-
- // Do Not Disturb
- static final String FC_DND_ACTIVATE = "78";
- static final String FC_DND_DEACTIVATE = "79";
-
- // Who Called Me?
- static final String FC_WHO = "51";
-
- // Rejection of Undesired Annoying Calls
- static final String FC_RUAC_ACTIVATE = "60";
- static final String FC_RUAC_DEACTIVATE = "80";
-
- // Calling Number Delivery
- // Calling Number Identification Presentation
- static final String FC_CNIP = "65";
- // Calling Number Identification Restriction
- static final String FC_CNIR = "85";
-
-
- //***** Event Constants
-
- static final int EVENT_SET_COMPLETE = 1;
- static final int EVENT_CDMA_FLASH_COMPLETED = 2;
-
-
- //***** Instance Variables
-
- CDMAPhone phone;
- Context context;
- String action; // '*' in CDMA
- String sc; // Service Code
- String poundString; // Entire Flash string
- String dialingNumber;
-
- /** Set to true in processCode, not at newFromDialString time */
-
- State state = State.PENDING;
- CharSequence message;
-
- //***** Class Variables
-
-
- // Flash Code Pattern
-
- static Pattern sPatternSuppService = Pattern.compile(
- "((\\*)(\\d{2,3})(#?)([^*#]*)?)(.*)");
-/* 1 2 3 4 5 6
-
- 1 = Full string up to and including #
- 2 = action
- 3 = service code
- 4 = separator
- 5 = dialing number
-*/
-
- static final int MATCH_GROUP_POUND_STRING = 1;
- static final int MATCH_GROUP_ACTION_STRING = 2;
- static final int MATCH_GROUP_SERVICE_CODE = 3;
- static final int MATCH_GROUP_DIALING_NUMBER = 5;
-
-
- //***** Public Class methods
-
- /**
- * Some dial strings in CDMA are defined to do non-call setup
- * things, such as set supplementary service settings (eg, call
- * forwarding). These are generally referred to as "Feature Codes".
- * We look to see if the dial string contains a valid Feature code (potentially
- * with a dial string at the end as well) and return info here.
- *
- * If the dial string contains no Feature code, we return an instance with
- * only "dialingNumber" set
- *
- * Please see also S.R0006-000-A v2.0 "Wireless Features Description"
- */
-
- static FeatureCode newFromDialString(String dialString, CDMAPhone phone) {
- Matcher m;
- FeatureCode ret = null;
-
- m = sPatternSuppService.matcher(dialString);
-
- // Is this formatted like a standard supplementary service code?
- if (m.matches()) {
- ret = new FeatureCode(phone);
- ret.poundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
- ret.action = makeEmptyNull(m.group(MATCH_GROUP_ACTION_STRING));
- ret.sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
- ret.dialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
- }
-
- return ret;
- }
-
- //***** Private Class methods
-
- /** make empty strings be null.
- * Java regexp returns empty strings for empty groups
- */
- private static String makeEmptyNull (String s) {
- if (s != null && s.length() == 0) return null;
-
- return s;
- }
-
- /** returns true of the string is empty or null */
- private static boolean isEmptyOrNull(CharSequence s) {
- return s == null || (s.length() == 0);
- }
-
- static boolean isServiceCodeCallForwarding(String sc) {
- return sc != null &&
- (sc.equals(FC_CF_ACTIVATE)
- || sc.equals(FC_CF_DEACTIVATE) || sc.equals(FC_CF_FORWARD_TO_NUMBER)
- || sc.equals(FC_CFBL_ACTIVATE) || sc.equals(FC_CFBL_DEACTIVATE)
- || sc.equals(FC_CFBL_FORWARD_TO_NUMBER) || sc.equals(FC_CFDA_ACTIVATE)
- || sc.equals(FC_CFDA_DEACTIVATE) || sc.equals(FC_CFDA_FORWARD_TO_NUMBER));
- }
-
- static boolean isServiceCodeCallWaiting(String sc) {
- return sc != null && sc.equals(FC_CCW);
- }
-
- static boolean isServiceCodeThreeWayCalling(String sc) {
- return sc != null && sc.equals(FC_3WC);
- }
-
- static boolean isServiceCodeAnnoyingCalls(String sc) {
- return sc != null &&
- (sc.equals(FC_RUAC_ACTIVATE)
- || sc.equals(FC_RUAC_DEACTIVATE));
- }
-
- static boolean isServiceCodeCallingNumberDelivery(String sc) {
- return sc != null &&
- (sc.equals(FC_CNIP)
- || sc.equals(FC_CNIR));
- }
-
- static boolean isServiceCodeDoNotDisturb(String sc) {
- return sc != null &&
- (sc.equals(FC_DND_ACTIVATE)
- || sc.equals(FC_DND_DEACTIVATE));
- }
-
-
- //***** Constructor
-
- FeatureCode (CDMAPhone phone) {
- super(phone.getHandler().getLooper());
- this.phone = phone;
- this.context = phone.getContext();
- }
-
-
- //***** MmiCode implementation
-
- public State getState() {
- return state;
- }
-
- public CharSequence getMessage() {
- return message;
- }
-
- // inherited javadoc suffices
- public void cancel() {
- //Not used here
- }
-
- public boolean isCancelable() {
- Log.e(LOG_TAG, "isCancelable: not used in CDMA");
- return false;
- }
-
- public boolean isUssdRequest() {
- Log.e(LOG_TAG, "isUssdRequest: not used in CDMA");
- return false;
- }
-
- /** Process a Flash Code...anything that isn't a dialing number */
- void processCode() {
- Log.d(LOG_TAG, "send feature code...");
- phone.mCM.sendCDMAFeatureCode(this.poundString, obtainMessage(EVENT_CDMA_FLASH_COMPLETED));
- }
-
- /** Called from CDMAPhone.handleMessage; not a Handler subclass */
- public void handleMessage (Message msg) {
- AsyncResult ar;
-
- switch (msg.what) {
- case EVENT_SET_COMPLETE:
- ar = (AsyncResult) (msg.obj);
- onSetComplete(ar);
- break;
- case EVENT_CDMA_FLASH_COMPLETED:
- ar = (AsyncResult) (msg.obj);
-
- if (ar.exception != null) {
- state = State.FAILED;
- message = context.getText(com.android.internal.R.string.fcError);
- } else {
- state = State.COMPLETE;
- message = context.getText(com.android.internal.R.string.fcComplete);
- }
- phone.onFeatureCodeDone(this);
- break;
- }
- }
-
-
- //***** Private instance methods
-
- private CharSequence getScString() {
- if (sc != null) {
- if (isServiceCodeCallForwarding(sc)) {
- return context.getText(com.android.internal.R.string.CfMmi);
- } else if (isServiceCodeCallWaiting(sc)) {
- return context.getText(com.android.internal.R.string.CwMmi);
- } else if (sc.equals(FC_CNIP)) {
- return context.getText(com.android.internal.R.string.CnipMmi);
- } else if (sc.equals(FC_CNIR)) {
- return context.getText(com.android.internal.R.string.CnirMmi);
- } else if (isServiceCodeThreeWayCalling(sc)) {
- return context.getText(com.android.internal.R.string.ThreeWCMmi);
- } else if (isServiceCodeAnnoyingCalls(sc)) {
- return context.getText(com.android.internal.R.string.RuacMmi);
- } else if (isServiceCodeCallingNumberDelivery(sc)) {
- return context.getText(com.android.internal.R.string.CndMmi);
- } else if (isServiceCodeDoNotDisturb(sc)) {
- return context.getText(com.android.internal.R.string.DndMmi);
- }
- }
-
- return "";
- }
-
- private void onSetComplete(AsyncResult ar){
- StringBuilder sb = new StringBuilder(getScString());
- sb.append("\n");
-
- if (ar.exception != null) {
- state = State.FAILED;
- sb.append(context.getText(com.android.internal.R.string.mmiError));
- } else {
- state = State.FAILED;
- sb.append(context.getText(com.android.internal.R.string.mmiError));
- }
-
- message = sb;
- phone.onFeatureCodeDone(this);
- }
-}
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimCard.java b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
index 9d9f479..734badd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimCard.java
@@ -16,507 +16,34 @@
package com.android.internal.telephony.cdma;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneProxy;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
-import android.app.ActivityManagerNative;
-import android.content.Intent;
-import android.content.res.Configuration;
-
-import static android.Manifest.permission.READ_PHONE_STATE;
/**
* Note: this class shares common code with SimCard, consider a base class to minimize code
* duplication.
* {@hide}
*/
-public final class RuimCard extends Handler implements IccCard {
- static final String LOG_TAG="CDMA";
-
- //***** Instance Variables
- private static final boolean DBG = true;
-
- private CDMAPhone phone;
-
- private CommandsInterface.IccStatus status = null;
- private boolean mDesiredPinLocked;
- private boolean mDesiredFdnEnabled;
- private boolean mRuimPinLocked = true; // default to locked
- private boolean mRuimFdnEnabled = false; // Default to disabled.
- // Will be updated when RUIM_READY.
-// //***** Constants
-
-// // FIXME I hope this doesn't conflict with the Dialer's notifications
-// Nobody is using this at the moment
-// static final int NOTIFICATION_ID_ICC_STATUS = 33456;
-
- //***** Event Constants
-
- static final int EVENT_RUIM_LOCKED_OR_ABSENT = 1;
- static final int EVENT_GET_RUIM_STATUS_DONE = 2;
- static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
- static final int EVENT_PINPUK_DONE = 4;
- static final int EVENT_REPOLL_STATUS_DONE = 5;
- static final int EVENT_RUIM_READY = 6;
- static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
- static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
- static final int EVENT_CHANGE_RUIM_PASSWORD_DONE = 9;
- static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
- static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
-
-
- //***** Constructor
+public final class RuimCard extends IccCard {
RuimCard(CDMAPhone phone) {
- this.phone = phone;
-
- phone.mCM.registerForRUIMLockedOrAbsent(
- this, EVENT_RUIM_LOCKED_OR_ABSENT, null);
-
- phone.mCM.registerForOffOrNotAvailable(
- this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-
- phone.mCM.registerForRUIMReady(
- this, EVENT_RUIM_READY, null);
-
+ super(phone, "CDMA", true);
+ mPhone.mCM.registerForRUIMLockedOrAbsent(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
+ mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mPhone.mCM.registerForRUIMReady(mHandler, EVENT_ICC_READY, null);
updateStateProperty();
}
- //***** RuimCard implementation
-
- public State
- getState() {
- if (status == null) {
- switch(phone.mCM.getRadioState()) {
- /* This switch block must not return anything in
- * State.isLocked() or State.ABSENT.
- * If it does, handleSimStatus() may break
- */
- case RADIO_OFF:
- case RADIO_UNAVAILABLE:
- case RUIM_NOT_READY:
- return State.UNKNOWN;
- case RUIM_LOCKED_OR_ABSENT:
- //this should be transient-only
- return State.UNKNOWN;
- case RUIM_READY:
- return State.READY;
- case NV_READY:
- case NV_NOT_READY:
- return State.ABSENT;
- }
- } else {
- switch (status) {
- case ICC_ABSENT: return State.ABSENT;
- case ICC_NOT_READY: return State.UNKNOWN;
- case ICC_READY: return State.READY;
- case ICC_PIN: return State.PIN_REQUIRED;
- case ICC_PUK: return State.PUK_REQUIRED;
- case ICC_NETWORK_PERSONALIZATION: return State.NETWORK_LOCKED;
- }
- }
-
- Log.e(LOG_TAG, "RuimCard.getState(): case should never be reached");
- return State.UNKNOWN;
- }
-
+ @Override
public void dispose() {
//Unregister for all events
- phone.mCM.unregisterForRUIMLockedOrAbsent(this);
- phone.mCM.unregisterForOffOrNotAvailable(this);
- phone.mCM.unregisterForRUIMReady(this);
- }
-
- protected void finalize() {
- if(DBG) Log.d(LOG_TAG, "RuimCard finalized");
- }
-
- private RegistrantList absentRegistrants = new RegistrantList();
- private RegistrantList pinLockedRegistrants = new RegistrantList();
- private RegistrantList networkLockedRegistrants = new RegistrantList();
-
-
- public void registerForAbsent(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- absentRegistrants.add(r);
-
- if (getState() == State.ABSENT) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForAbsent(Handler h) {
- absentRegistrants.remove(h);
- }
-
- public void registerForNetworkLocked(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- networkLockedRegistrants.add(r);
-
- if (getState() == State.NETWORK_LOCKED) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForNetworkLocked(Handler h) {
- networkLockedRegistrants.remove(h);
- }
-
- public void registerForLocked(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- pinLockedRegistrants.add(r);
-
- if (getState().isPinLocked()) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForLocked(Handler h) {
- pinLockedRegistrants.remove(h);
- }
-
- public void supplyPin (String pin, Message onComplete) {
- phone.mCM.supplyIccPin(pin, obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyPuk (String puk, String newPin, Message onComplete) {
- phone.mCM.supplyIccPuk(puk, newPin, obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyPin2 (String pin2, Message onComplete) {
- phone.mCM.supplyIccPin2(pin2, obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
- phone.mCM.supplyIccPuk2(puk2, newPin2, obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyNetworkDepersonalization (String pin, Message onComplete) {
- if(DBG) log("Network Despersonalization: " + pin);
- phone.mCM.supplyNetworkDepersonalization(pin,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public boolean getIccLockEnabled() {
- return mRuimPinLocked;
- }
-
- public boolean getIccFdnEnabled() {
- return mRuimFdnEnabled;
- }
-
- public void setIccLockEnabled (boolean enabled,
- String password, Message onComplete) {
- int serviceClassX;
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX;
-
- mDesiredPinLocked = enabled;
-
- phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
- enabled, password, serviceClassX,
- obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
- }
-
- public void setIccFdnEnabled (boolean enabled,
- String password, Message onComplete) {
- int serviceClassX;
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX +
- CommandsInterface.SERVICE_CLASS_SMS;
-
- mDesiredFdnEnabled = enabled;
-
- phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
- enabled, password, serviceClassX,
- obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
+ mPhone.mCM.unregisterForRUIMLockedOrAbsent(mHandler);
+ mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
+ mPhone.mCM.unregisterForRUIMReady(mHandler);
}
- public void changeIccLockPassword(String oldPassword, String newPassword,
- Message onComplete) {
- if(DBG) log("Change Pin1 old: " + oldPassword + " new: " + newPassword);
- phone.mCM.changeIccPin(oldPassword, newPassword,
- obtainMessage(EVENT_CHANGE_RUIM_PASSWORD_DONE, onComplete));
- }
-
- public void changeIccFdnPassword(String oldPassword, String newPassword,
- Message onComplete) {
- if(DBG) log("Change Pin2 old: " + oldPassword + " new: " + newPassword);
- phone.mCM.changeIccPin2(oldPassword, newPassword,
- obtainMessage(EVENT_CHANGE_RUIM_PASSWORD_DONE, onComplete));
- }
-
- public String getServiceProviderName() {
- return phone.mRuimRecords.getServiceProviderName();
- }
-
- //***** Handler implementation
@Override
- public void handleMessage(Message msg){
- AsyncResult ar;
- int serviceClassX;
-
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX;
-
- switch (msg.what) {
- case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
- Log.d(LOG_TAG, "Event EVENT_RADIO_OFF_OR_NOT_AVAILABLE Received");
- status = null;
- updateStateProperty();
- broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_NOT_READY, null);
- break;
- case EVENT_RUIM_READY:
- Log.d(LOG_TAG, "Event EVENT_RUIM_READY Received");
- //TODO: put facility read in SIM_READY now, maybe in REG_NW
- phone.mCM.getIccStatus(obtainMessage(EVENT_GET_RUIM_STATUS_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
- break;
- case EVENT_RUIM_LOCKED_OR_ABSENT:
- Log.d(LOG_TAG, "Event EVENT_RUIM_LOCKED_OR_ABSENT Received");
- phone.mCM.getIccStatus(obtainMessage(EVENT_GET_RUIM_STATUS_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
- break;
- case EVENT_GET_RUIM_STATUS_DONE:
- Log.d(LOG_TAG, "Event EVENT_GET_RUIM_STATUS_DONE Received");
- ar = (AsyncResult)msg.obj;
-
- getRuimStatusDone(ar);
- break;
- case EVENT_PINPUK_DONE:
- Log.d(LOG_TAG, "Event EVENT_PINPUK_DONE Received");
- // a PIN/PUK/PIN2/PUK2/Network Personalization
- // request has completed. ar.userObj is the response Message
- // Repoll before returning
- ar = (AsyncResult)msg.obj;
- // TODO should abstract these exceptions
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- phone.mCM.getIccStatus(
- obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
- break;
- case EVENT_REPOLL_STATUS_DONE:
- Log.d(LOG_TAG, "Event EVENT_REPOLL_STATUS_DONE Received");
- // Finished repolling status after PIN operation
- // ar.userObj is the response messaeg
- // ar.userObj.obj is already an AsyncResult with an
- // appropriate exception filled in if applicable
-
- ar = (AsyncResult)msg.obj;
- getRuimStatusDone(ar);
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_QUERY_FACILITY_LOCK_DONE:
- Log.d(LOG_TAG, "Event EVENT_QUERY_FACILITY_LOCK_DONE Received");
- ar = (AsyncResult)msg.obj;
- onQueryFacilityLock(ar);
- break;
- case EVENT_QUERY_FACILITY_FDN_DONE:
- Log.d(LOG_TAG, "Event EVENT_QUERY_FACILITY_FDN_DONE Received");
- ar = (AsyncResult)msg.obj;
- onQueryFdnEnabled(ar);
- break;
- case EVENT_CHANGE_FACILITY_LOCK_DONE:
- Log.d(LOG_TAG, "Event EVENT_CHANGE_FACILITY_LOCK_DONE Received");
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- mRuimPinLocked = mDesiredPinLocked;
- if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
- "mRuimPinLocked= " + mRuimPinLocked);
- } else {
- Log.e(LOG_TAG, "Error change facility lock with exception "
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_CHANGE_FACILITY_FDN_DONE:
- Log.d(LOG_TAG, "Event EVENT_CHANGE_FACILITY_FDN_DONE Received");
- ar = (AsyncResult)msg.obj;
-
- if (ar.exception == null) {
- mRuimFdnEnabled = mDesiredFdnEnabled;
- if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
- "mRuimFdnEnabled=" + mRuimFdnEnabled);
- } else {
- Log.e(LOG_TAG, "Error change facility fdn with exception "
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_CHANGE_RUIM_PASSWORD_DONE:
- Log.d(LOG_TAG, "Event EVENT_CHANGE_RUIM_PASSWORD_DONE Received");
- ar = (AsyncResult)msg.obj;
- if(ar.exception != null) {
- Log.e(LOG_TAG, "Error in change sim password with exception"
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- default:
- Log.e(LOG_TAG, "[CdmaRuimCard] Unknown Event " + msg.what);
- }
- }
-
- //***** Private methods
-
- /**
- * Interpret EVENT_QUERY_FACILITY_LOCK_DONE
- * @param ar is asyncResult of Query_Facility_Locked
- */
- private void onQueryFacilityLock(AsyncResult ar) {
- if(ar.exception != null) {
- if (DBG) log("Error in querying facility lock:" + ar.exception);
- return;
- }
-
- int[] ints = (int[])ar.result;
- if(ints.length != 0) {
- mRuimPinLocked = (0!=ints[0]);
- if(DBG) log("Query facility lock : " + mRuimPinLocked);
- } else {
- Log.e(LOG_TAG, "[CdmaRuimCard] Bogus facility lock response");
- }
- }
-
- /**
- * Interpret EVENT_QUERY_FACILITY_LOCK_DONE
- * @param ar is asyncResult of Query_Facility_Locked
- */
- private void onQueryFdnEnabled(AsyncResult ar) {
- if(ar.exception != null) {
- if(DBG) log("Error in querying facility lock:" + ar.exception);
- return;
- }
-
- int[] ints = (int[])ar.result;
- if(ints.length != 0) {
- mRuimFdnEnabled = (0!=ints[0]);
- if(DBG) log("Query facility lock : " + mRuimFdnEnabled);
- } else {
- Log.e(LOG_TAG, "[CdmaRuimCard] Bogus facility lock response");
- }
- }
-
- private void
- getRuimStatusDone(AsyncResult ar) {
- if (ar.exception != null) {
- Log.e(LOG_TAG,"Error getting SIM status. "
- + "RIL_REQUEST_GET_SIM_STATUS should "
- + "never return an error", ar.exception);
- return;
- }
-
- CommandsInterface.IccStatus newStatus
- = (CommandsInterface.IccStatus) ar.result;
-
- handleRuimStatus(newStatus);
- }
-
- private void
- handleRuimStatus(CommandsInterface.IccStatus newStatus) {
- boolean transitionedIntoPinLocked;
- boolean transitionedIntoAbsent;
- boolean transitionedIntoNetworkLocked;
-
- RuimCard.State oldState, newState;
-
- oldState = getState();
- status = newStatus;
- newState = getState();
-
- updateStateProperty();
-
- transitionedIntoPinLocked = (
- (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
- || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
- transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
- transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
- && newState == State.NETWORK_LOCKED);
-
- if (transitionedIntoPinLocked) {
- if(DBG) log("Notify RUIM pin or puk locked.");
- pinLockedRegistrants.notifyRegistrants();
- broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_LOCKED,
- (newState == State.PIN_REQUIRED) ?
- INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
- } else if (transitionedIntoAbsent) {
- if(DBG) log("Notify RUIM missing.");
- absentRegistrants.notifyRegistrants();
- broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_ABSENT, null);
- } else if (transitionedIntoNetworkLocked) {
- if(DBG) log("Notify RUIM network locked.");
- networkLockedRegistrants.notifyRegistrants();
- broadcastRuimStateChangedIntent(RuimCard.INTENT_VALUE_ICC_LOCKED,
- INTENT_VALUE_LOCKED_NETWORK);
- }
- }
-
- public void broadcastRuimStateChangedIntent(String value, String reason) {
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- intent.putExtra(Phone.PHONE_NAME_KEY, phone.getPhoneName());
- intent.putExtra(RuimCard.INTENT_KEY_ICC_STATE, value);
- intent.putExtra(RuimCard.INTENT_KEY_LOCKED_REASON, reason);
- if(DBG) log("Broadcasting intent SIM_STATE_CHANGED_ACTION " + value
- + " reason " + reason);
- ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
- }
-
- public void updateImsiConfiguration(String imsi) {
- if (imsi.length() >= 6) {
- Configuration config = new Configuration();
- config.mcc = ((imsi.charAt(0)-'0')*100)
- + ((imsi.charAt(1)-'0')*10)
- + (imsi.charAt(2)-'0');
- config.mnc = ((imsi.charAt(3)-'0')*100)
- + ((imsi.charAt(4)-'0')*10)
- + (imsi.charAt(5)-'0');
- try {
- ActivityManagerNative.getDefault().updateConfiguration(config);
- } catch (RemoteException e) {
- }
- }
- }
-
- private void
- updateStateProperty() {
- phone.setSystemProperty(
- TelephonyProperties.PROPERTY_SIM_STATE,
- getState().toString());
- }
-
- private void log(String msg) {
- Log.d(LOG_TAG, "[RuimCard] " + msg);
+ public String getServiceProviderName () {
+ return ((CDMAPhone)mPhone).mRuimRecords.getServiceProviderName();
}
-}
+ }
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
index 55f48b1..b9ece8b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimRecords.java
@@ -28,7 +28,7 @@ import com.android.internal.telephony.AdnRecordLoader;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.cdma.RuimCard;
-import com.android.internal.telephony.gsm.MccTable;
+import com.android.internal.telephony.MccTable;
// can't be used since VoiceMailConstants is not public
//import com.android.internal.telephony.gsm.VoiceMailConstants;
@@ -37,10 +37,6 @@ import com.android.internal.telephony.IccRecords;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.PhoneProxy;
-import com.android.internal.telephony.TelephonyIntents;
-import android.app.ActivityManagerNative;
-import android.content.Intent;
-
/**
* {@hide}
@@ -51,7 +47,7 @@ public final class RuimRecords extends IccRecords {
private static final boolean DBG = true;
private boolean m_ota_commited=false;
- //***** Instance Variables
+ // ***** Instance Variables
private String mImsi;
private String mMyMobileNumber;
@@ -59,13 +55,12 @@ public final class RuimRecords extends IccRecords {
private String mPrlVersion;
- //***** Event Constants
+ // ***** Event Constants
private static final int EVENT_RUIM_READY = 1;
private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
private static final int EVENT_GET_DEVICE_IDENTITY_DONE = 4;
private static final int EVENT_GET_ICCID_DONE = 5;
- private static final int EVENT_NV_READY = 9;
private static final int EVENT_GET_CDMA_SUBSCRIPTION_DONE = 10;
private static final int EVENT_UPDATE_DONE = 14;
private static final int EVENT_GET_SST_DONE = 17;
@@ -76,7 +71,7 @@ public final class RuimRecords extends IccRecords {
private static final int EVENT_GET_SMS_DONE = 22;
private static final int EVENT_RUIM_REFRESH = 31;
- private static final int EVENT_OTA_PROVISION_STATUS_CHANGE = 32;
+
RuimRecords(CDMAPhone p) {
super(p);
@@ -90,14 +85,12 @@ public final class RuimRecords extends IccRecords {
p.mCM.registerForRUIMReady(this, EVENT_RUIM_READY, null);
- p.mCM.registerForNVReady(this, EVENT_NV_READY, null);
p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
// NOTE the EVENT_SMS_ON_RUIM is not registered
p.mCM.setOnIccRefresh(this, EVENT_RUIM_REFRESH, null);
// Start off by setting empty state
onRadioOffOrNotAvailable();
- p.mCM.registerForCdmaOtaProvision(this,EVENT_OTA_PROVISION_STATUS_CHANGE, null);
}
@@ -106,8 +99,6 @@ public final class RuimRecords extends IccRecords {
phone.mCM.unregisterForRUIMReady(this);
phone.mCM.unregisterForOffOrNotAvailable( this);
phone.mCM.unSetOnIccRefresh(this);
- phone.mCM.unregisterForNVReady(this);
- phone.mCM.unregisterForCdmaOtaProvision(this);
}
@Override
@@ -118,7 +109,7 @@ public final class RuimRecords extends IccRecords {
@Override
protected void onRadioOffOrNotAvailable() {
countVoiceMessages = 0;
- mncLength = 0;
+ mncLength = UNINITIALIZED;
iccid = null;
adnCache.reset();
@@ -176,7 +167,7 @@ public final class RuimRecords extends IccRecords {
}
// TODO(Moto): mncLength is not set anywhere.
- if (mncLength != 0) {
+ if (mncLength != UNINITIALIZED && mncLength != UNKNOWN) {
// Length = length of MCC + length of MNC
// TODO: change spec name
// length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
@@ -202,9 +193,7 @@ public final class RuimRecords extends IccRecords {
case EVENT_RUIM_READY:
onRuimReady();
break;
- case EVENT_NV_READY:
- onNvReady();
- break;
+
case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
onRadioOffOrNotAvailable();
break;
@@ -221,15 +210,7 @@ public final class RuimRecords extends IccRecords {
if (ar.exception != null) {
break;
}
- if (m_ota_commited) {
- if (mMyMobileNumber != localTemp[0]) {
- Intent intent = new Intent(TelephonyIntents.ACTION_CDMA_OTA_MDN_CHANGED);
- intent.putExtra("mdn", localTemp[0]);
- Log.d(LOG_TAG,"Broadcasting intent MDN Change in OTA ");
- ActivityManagerNative.broadcastStickyIntent(intent, null);
- }
- m_ota_commited = false;
- }
+
mMyMobileNumber = localTemp[0];
mMin2Min1 = localTemp[3];
mPrlVersion = localTemp[4];
@@ -281,21 +262,6 @@ public final class RuimRecords extends IccRecords {
}
break;
- case EVENT_OTA_PROVISION_STATUS_CHANGE:
- m_ota_commited=false;
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- int[] ints = (int[]) ar.result;
- int otaStatus = ints[0];
- if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_COMMITTED) {
- m_ota_commited=true;
- phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
- } else if (otaStatus == phone.CDMA_OTA_PROVISION_STATUS_OTAPA_STOPPED) {
- phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
- }
- }
- break;
-
}}catch (RuntimeException exc) {
// I don't want these exceptions to be fatal
Log.w(LOG_TAG, "Exception parsing RUIM record", exc);
@@ -329,7 +295,7 @@ public final class RuimRecords extends IccRecords {
recordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
- ((CDMAPhone) phone).mRuimCard.broadcastRuimStateChangedIntent(
+ ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
RuimCard.INTENT_VALUE_ICC_LOADED, null);
}
@@ -338,7 +304,7 @@ public final class RuimRecords extends IccRecords {
READY is sent before IMSI ready
*/
- ((CDMAPhone) phone).mRuimCard.broadcastRuimStateChangedIntent(
+ ((CDMAPhone) phone).mRuimCard.broadcastIccStateChangedIntent(
RuimCard.INTENT_VALUE_ICC_READY, null);
fetchRuimRecords();
@@ -347,10 +313,6 @@ public final class RuimRecords extends IccRecords {
}
- private void onNvReady() {
- phone.mCM.getCDMASubscription(obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_DONE));
-
- }
private void fetchRuimRecords() {
recordsRequested = true;
diff --git a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java
index 44958e9..4b88057 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SignalToneUtil.java
@@ -35,7 +35,12 @@ public class SignalToneUtil {
static public final int IS95_CONST_IR_ALERT_MED = 0;
static public final int IS95_CONST_IR_ALERT_HIGH = 1;
static public final int IS95_CONST_IR_ALERT_LOW = 2;
- static public final int TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN = 255;
+
+ // Based on 3GPP2 C.S0005-E, seciton 3.7.5.5 Signal,
+ // set TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN to 0 to avoid
+ // the alert pitch to be involved in hash calculation for
+ // signal type other than IS54B.
+ static public final int TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN = 0;
// public final int int IS95_CONST_IR_SIGNAL_TYPE;
static public final int IS95_CONST_IR_SIG_ISDN_NORMAL = 0;
@@ -81,6 +86,15 @@ public class SignalToneUtil {
(alertPitch < 0) || (signal > 256) || (signal < 0)) {
return new Integer(CDMA_INVALID_TONE);
}
+ // Based on 3GPP2 C.S0005-E, seciton 3.7.5.5 Signal,
+ // the alert pitch field is ignored by the mobile station unless
+ // SIGNAL_TYPE is '10',IS-54B Alerting.
+ // Set alert pitch to TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN
+ // so the alert pitch is not involved in hash calculation
+ // when signal type is not IS-54B.
+ if (signalType != IS95_CONST_IR_SIGNAL_IS54B) {
+ alertPitch = TAPIAMSSCDMA_SIGNAL_PITCH_UNKNOWN;
+ }
return new Integer(signalType * 256 * 256 + alertPitch * 256 + signal);
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 3a92064..d17468c 100644..100755
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.cdma;
import android.os.Parcel;
+import android.os.SystemProperties;
import android.text.format.Time;
import android.util.Config;
import android.util.Log;
@@ -25,10 +26,12 @@ import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
import com.android.internal.telephony.cdma.sms.SmsEnvelope;
import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.util.HexDump;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -37,7 +40,6 @@ import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
-import java.util.Random;
/**
* TODO(cleanup): these constants are disturbing... are they not just
@@ -67,6 +69,7 @@ import static android.telephony.SmsMessage.MessageClass;
*/
public class SmsMessage extends SmsMessageBase {
static final String LOG_TAG = "CDMA";
+ private final static Boolean DBG_SMS = false;
/**
* Status of a previously submitted SMS.
@@ -76,14 +79,6 @@ public class SmsMessage extends SmsMessageBase {
*/
private int status;
- /** The next message ID for the BearerData. Shall be a random value on first use.
- * (See C.S0015-B, v2.0, 4.3.1.5)
- */
- private static int nextMessageId = 0;
-
- /** Specifies if this is the first SMS message submit */
- private static boolean firstSMS = true;
-
/** Specifies if a return of an acknowledgment is requested for send SMS */
private static final int RETURN_NO_ACK = 0;
private static final int RETURN_ACK = 1;
@@ -329,7 +324,7 @@ public class SmsMessage extends SmsMessageBase {
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
- public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, short destPort,
+ public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
byte[] data, boolean statusReportRequested) {
/**
@@ -518,6 +513,7 @@ public class SmsMessage extends SmsMessageBase {
originatingAddress = addr;
env.origAddress = addr;
mEnvelope = env;
+ mPdu = pdu;
parseSms();
}
@@ -526,7 +522,25 @@ public class SmsMessage extends SmsMessageBase {
* Parses a SMS message from its BearerData stream. (mobile-terminated only)
*/
protected void parseSms() {
+ // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
+ // It contains only an 8-bit number with the number of messages waiting
+ if (mEnvelope.teleService == SmsEnvelope.TELESERVICE_MWI) {
+ mBearerData = new BearerData();
+ if (mEnvelope.bearerData != null) {
+ mBearerData.numberOfMessages = 0x000000FF & mEnvelope.bearerData[0];
+ }
+ if (Config.DEBUG) {
+ Log.d(LOG_TAG, "parseSms: get MWI " +
+ Integer.toString(mBearerData.numberOfMessages));
+ }
+ return;
+ }
mBearerData = BearerData.decode(mEnvelope.bearerData);
+ if (DBG_SMS) {
+ Log.d(LOG_TAG, "MT raw BearerData = '" +
+ HexDump.toHexString(mEnvelope.bearerData) + "'");
+ Log.d(LOG_TAG, "MT (decoded) BearerData = " + mBearerData);
+ }
messageRef = mBearerData.messageId;
if (mBearerData.userData != null) {
userData = mBearerData.userData.payload;
@@ -583,36 +597,28 @@ public class SmsMessage extends SmsMessageBase {
}
}
- private static CdmaSmsAddress parseCdmaSmsAddr(String addrStr) {
- // see C.S0015-B, v2.0, 3.4.3.3
- CdmaSmsAddress addr = new CdmaSmsAddress();
- addr.digitMode = CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR;
- try {
- addr.origBytes = addrStr.getBytes("UTF-8");
- } catch (java.io.UnsupportedEncodingException ex) {
- Log.e(LOG_TAG, "CDMA address parsing failed: " + ex);
- return null;
- }
- addr.numberOfDigits = (byte)addr.origBytes.length;
- addr.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
- addr.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
- addr.ton = CdmaSmsAddress.TON_INTERNATIONAL_OR_IP;
- return addr;
- }
-
/**
- * Set the nextMessageId to a random value between 0 and 65536
- * See C.S0015-B, v2.0, 4.3.1.5
+ * Calculate the next message id, starting at 1 and iteratively
+ * incrementing within the range 1..65535 remembering the state
+ * via a persistent system property. (See C.S0015-B, v2.0,
+ * 4.3.1.5) Since this routine is expected to be accessed via via
+ * binder-call, and hence should be threadsafe, it has been
+ * synchronized.
*/
- private static void setNextMessageId() {
- // Message ID, modulo 65536
- if(firstSMS) {
- Random generator = new Random();
- nextMessageId = generator.nextInt(65536);
- firstSMS = false;
- } else {
- nextMessageId = ++nextMessageId & 0xFFFF;
+ private synchronized static int getNextMessageId() {
+ // Testing and dialog with partners has indicated that
+ // msgId==0 is (sometimes?) treated specially by lower levels.
+ // Specifically, the ID is not preserved for delivery ACKs.
+ // Hence, avoid 0 -- constraining the range to 1..65535.
+ int msgId = SystemProperties.getInt(TelephonyProperties.PROPERTY_CDMA_MSG_ID, 1);
+ String nextMsgId = Integer.toString((msgId % 0xFFFF) + 1);
+ SystemProperties.set(TelephonyProperties.PROPERTY_CDMA_MSG_ID, nextMsgId);
+ if (DBG_SMS) {
+ Log.d(LOG_TAG, "next " + TelephonyProperties.PROPERTY_CDMA_MSG_ID + " = " + nextMsgId);
+ Log.d(LOG_TAG, "readback gets " +
+ SystemProperties.get(TelephonyProperties.PROPERTY_CDMA_MSG_ID));
}
+ return msgId;
}
/**
@@ -626,14 +632,19 @@ public class SmsMessage extends SmsMessageBase {
* TODO(cleanup): give this function a more meaningful name.
*/
- CdmaSmsAddress destAddr = parseCdmaSmsAddr(destAddrStr);
+ /**
+ * TODO(cleanup): Make returning null from the getSubmitPdu
+ * variations meaningful -- clean up the error feedback
+ * mechanism, and avoid null pointer exceptions.
+ */
+
+ CdmaSmsAddress destAddr = CdmaSmsAddress.parse(destAddrStr);
if (destAddr == null) return null;
BearerData bearerData = new BearerData();
bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
- if (userData != null) setNextMessageId();
- bearerData.messageId = nextMessageId;
+ bearerData.messageId = getNextMessageId();
bearerData.deliveryAckReq = statusReportRequested;
bearerData.userAckReq = false;
@@ -641,14 +652,17 @@ public class SmsMessage extends SmsMessageBase {
bearerData.reportReq = false;
bearerData.userData = userData;
- bearerData.hasUserDataHeader = (userData.userDataHeader != null);
-
- int teleservice = bearerData.hasUserDataHeader ?
- SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (DBG_SMS) {
+ Log.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
+ Log.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
+ }
if (encodedBearerData == null) return null;
+ int teleservice = bearerData.hasUserDataHeader ?
+ SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+
SmsEnvelope envelope = new SmsEnvelope();
envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
envelope.teleService = teleservice;
@@ -728,9 +742,8 @@ public class SmsMessage extends SmsMessageBase {
dos.close();
/**
- * TODO(cleanup) -- This is the only place where mPdu is
- * defined, and this is not obviously the only place where
- * it needs to be defined. It would be much nicer if
+ * TODO(cleanup) -- The mPdu field is managed in
+ * a fragile manner, and it would be much nicer if
* accessing the serialized representation used a less
* fragile mechanism. Maybe the getPdu method could
* generate a representation if there was not yet one?
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index ef3afff..721729d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -233,22 +233,22 @@ public final class BearerData {
public static TimeStamp fromByteArray(byte[] data) {
TimeStamp ts = new TimeStamp();
// C.S0015-B v2.0, 4.5.4: range is 1996-2095
- int year = IccUtils.beBcdByteToInt(data[0]);
+ int year = IccUtils.cdmaBcdByteToInt(data[0]);
if (year > 99 || year < 0) return null;
ts.year = year >= 96 ? year + 1900 : year + 2000;
- int month = IccUtils.beBcdByteToInt(data[1]);
+ int month = IccUtils.cdmaBcdByteToInt(data[1]);
if (month < 1 || month > 12) return null;
ts.month = month - 1;
- int day = IccUtils.beBcdByteToInt(data[2]);
+ int day = IccUtils.cdmaBcdByteToInt(data[2]);
if (day < 1 || day > 31) return null;
ts.monthDay = day;
- int hour = IccUtils.beBcdByteToInt(data[3]);
+ int hour = IccUtils.cdmaBcdByteToInt(data[3]);
if (hour < 0 || hour > 23) return null;
ts.hour = hour;
- int minute = IccUtils.beBcdByteToInt(data[4]);
+ int minute = IccUtils.cdmaBcdByteToInt(data[4]);
if (minute < 0 || minute > 59) return null;
ts.minute = minute;
- int second = IccUtils.beBcdByteToInt(data[5]);
+ int second = IccUtils.cdmaBcdByteToInt(data[5]);
if (second < 0 || second > 59) return null;
ts.second = second;
return ts;
@@ -455,53 +455,117 @@ public final class BearerData {
}
}
- private static int calcUdhSeptetPadding(int userDataHeaderLen) {
- int udhBits = userDataHeaderLen * 8;
- int udhSeptets = (udhBits + 6) / 7;
- int paddingBits = (udhSeptets * 7) - udhBits;
- return paddingBits;
+ private static class Gsm7bitCodingResult {
+ int septets;
+ byte[] data;
}
- private static byte[] encode7bitGsm(String msg, int paddingBits)
+ private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force)
throws CodingException
{
try {
/*
* TODO(cleanup): It would be nice if GsmAlphabet provided
* an option to produce just the data without prepending
- * the length.
+ * the septet count, as this function is really just a
+ * wrapper to strip that off. Not to mention that the
+ * septet count is generally known prior to invocation of
+ * the encoder. Note that it cannot be derived from the
+ * resulting array length, since that cannot distinguish
+ * if the last contains either 1 or 8 valid bits.
+ *
+ * TODO(cleanup): The BitwiseXStreams could also be
+ * extended with byte-wise reversed endianness read/write
+ * routines to allow a corresponding implementation of
+ * stringToGsm7BitPacked, and potentially directly support
+ * access to the main bitwise stream from encode/decode.
*/
- byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg, 0, -1, paddingBits, true);
- byte []data = new byte[fullData.length - 1];
- System.arraycopy(fullData, 1, data, 0, fullData.length - 1);
- return data;
+ byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force);
+ Gsm7bitCodingResult result = new Gsm7bitCodingResult();
+ result.data = new byte[fullData.length - 1];
+ System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
+ result.septets = fullData[0] & 0x00FF;
+ return result;
} catch (com.android.internal.telephony.EncodeException ex) {
throw new CodingException("7bit GSM encode failed: " + ex);
}
}
+ private static void encode7bitEms(UserData uData, byte[] udhData, boolean force)
+ throws CodingException
+ {
+ int udhBytes = udhData.length + 1; // Add length octet.
+ int udhSeptets = ((udhBytes * 8) + 6) / 7;
+ Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
+ uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+ uData.msgEncodingSet = true;
+ uData.numFields = gcr.septets;
+ uData.payload = gcr.data;
+ uData.payload[0] = (byte)udhData.length;
+ System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
+ }
+
+ private static void encode16bitEms(UserData uData, byte[] udhData)
+ throws CodingException
+ {
+ byte[] payload = encodeUtf16(uData.payloadStr);
+ int udhBytes = udhData.length + 1; // Add length octet.
+ int udhCodeUnits = (udhBytes + 1) / 2;
+ int udhPadding = udhBytes % 2;
+ int payloadCodeUnits = payload.length / 2;
+ uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+ uData.msgEncodingSet = true;
+ uData.numFields = udhCodeUnits + payloadCodeUnits;
+ uData.payload = new byte[uData.numFields * 2];
+ uData.payload[0] = (byte)udhData.length;
+ System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
+ System.arraycopy(payload, 0, uData.payload, udhBytes + udhPadding, payload.length);
+ }
+
+ private static void encodeEmsUserDataPayload(UserData uData)
+ throws CodingException
+ {
+ byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
+ if (uData.msgEncodingSet) {
+ if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
+ encode7bitEms(uData, headerData, true);
+ } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
+ encode16bitEms(uData, headerData);
+ } else {
+ throw new CodingException("unsupported EMS user data encoding (" +
+ uData.msgEncoding + ")");
+ }
+ } else {
+ try {
+ encode7bitEms(uData, headerData, false);
+ } catch (CodingException ex) {
+ encode16bitEms(uData, headerData);
+ }
+ }
+ }
+
private static void encodeUserDataPayload(UserData uData)
throws CodingException
{
- // TODO(cleanup): UDH can only occur in EMS mode, meaning
- // encapsulation of GSM encoding, and so the logic here should
- // be refactored to more cleanly reflect this constraint.
+ if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
+ Log.e(LOG_TAG, "user data with null payloadStr");
+ uData.payloadStr = "";
+ }
- byte[] headerData = null;
- if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader);
- int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet
+ if (uData.userDataHeader != null) {
+ encodeEmsUserDataPayload(uData);
+ return;
+ }
- byte[] payloadData;
- int codeUnitCount;
if (uData.msgEncodingSet) {
if (uData.msgEncoding == UserData.ENCODING_OCTET) {
if (uData.payload == null) {
Log.e(LOG_TAG, "user data with octet encoding but null payload");
- payloadData = new byte[0];
- codeUnitCount = 0;
+ uData.payload = new byte[0];
+ uData.numFields = 0;
} else {
- payloadData = uData.payload;
- codeUnitCount = uData.payload.length;
+ uData.payload = uData.payload;
+ uData.numFields = uData.payload.length;
}
} else {
if (uData.payloadStr == null) {
@@ -509,67 +573,52 @@ public final class BearerData {
uData.payloadStr = "";
}
if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
- int paddingBits = calcUdhSeptetPadding(headerDataLen);
- payloadData = encode7bitGsm(uData.payloadStr, paddingBits);
- codeUnitCount = ((payloadData.length + headerDataLen) * 8) / 7;
+ Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
+ uData.payload = gcr.data;
+ uData.numFields = gcr.septets;
} else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
- payloadData = encode7bitAscii(uData.payloadStr, true);
- codeUnitCount = uData.payloadStr.length();
+ uData.payload = encode7bitAscii(uData.payloadStr, true);
+ uData.numFields = uData.payloadStr.length();
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
- payloadData = encodeUtf16(uData.payloadStr);
- codeUnitCount = uData.payloadStr.length();
+ uData.payload = encodeUtf16(uData.payloadStr);
+ uData.numFields = uData.payloadStr.length();
} else {
throw new CodingException("unsupported user data encoding (" +
uData.msgEncoding + ")");
}
}
} else {
- if (uData.payloadStr == null) {
- Log.e(LOG_TAG, "user data with null payloadStr");
- uData.payloadStr = "";
- }
try {
- if (headerData == null) {
- payloadData = encode7bitAscii(uData.payloadStr, false);
- codeUnitCount = uData.payloadStr.length();
- uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
- } else {
- // If there is a header, we are in EMS mode, in
- // which case we use GSM encodings.
- int paddingBits = calcUdhSeptetPadding(headerDataLen);
- payloadData = encode7bitGsm(uData.payloadStr, paddingBits);
- codeUnitCount = ((payloadData.length + headerDataLen) * 8) / 7;
- uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
- }
+ uData.payload = encode7bitAscii(uData.payloadStr, false);
+ uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
} catch (CodingException ex) {
- payloadData = encodeUtf16(uData.payloadStr);
- codeUnitCount = uData.payloadStr.length();
+ uData.payload = encodeUtf16(uData.payloadStr);
uData.msgEncoding = UserData.ENCODING_UNICODE_16;
}
+ uData.numFields = uData.payloadStr.length();
uData.msgEncodingSet = true;
}
-
- int totalLength = payloadData.length + headerDataLen;
- if (totalLength > SmsMessage.MAX_USER_DATA_BYTES) {
- throw new CodingException("encoded user data too large (" + totalLength +
- " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
- }
-
- uData.numFields = codeUnitCount;
- uData.payload = new byte[totalLength];
- if (headerData != null) {
- uData.payload[0] = (byte)headerData.length;
- System.arraycopy(headerData, 0, uData.payload, 1, headerData.length);
- }
- System.arraycopy(payloadData, 0, uData.payload, headerDataLen, payloadData.length);
}
private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
throws BitwiseOutputStream.AccessException, CodingException
{
+ /*
+ * TODO(cleanup): Do we really need to set userData.payload as
+ * a side effect of encoding? If not, we could avoid data
+ * copies by passing outStream directly.
+ */
encodeUserDataPayload(bData.userData);
- /**
- * XXX/TODO: figure out what the right answer is WRT padding bits
+ bData.hasUserDataHeader = bData.userData.userDataHeader != null;
+
+ if (bData.userData.payload.length > SmsMessage.MAX_USER_DATA_BYTES) {
+ throw new CodingException("encoded user data too large (" +
+ bData.userData.payload.length +
+ " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)");
+ }
+
+ /*
+ * TODO(cleanup): figure out what the right answer is WRT paddingBits field
*
* userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
* userData.paddingBits = 0; // XXX this seems better, but why?
@@ -624,6 +673,12 @@ public final class BearerData {
return rawData;
}
+ /*
+ * TODO(cleanup): CdmaSmsAddress encoding should make use of
+ * CdmaSmsAddress.parse provided that DTMF encoding is unified,
+ * and the difference in 4bit vs 8bit is resolved.
+ */
+
private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
try {
@@ -792,23 +847,34 @@ public final class BearerData {
return null;
}
- private static void decodeMessageId(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 3) {
- throw new CodingException("MESSAGE_IDENTIFIER subparam size incorrect");
- }
- bData.messageType = inStream.read(4);
- bData.messageId = inStream.read(8) << 8;
- bData.messageId |= inStream.read(8);
- bData.hasUserDataHeader = (inStream.read(1) == 1);
- inStream.skip(3);
+ final int EXPECTED_PARAM_SIZE = 3 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.messageType = inStream.read(4);
+ bData.messageId = inStream.read(8) << 8;
+ bData.messageId |= inStream.read(8);
+ bData.hasUserDataHeader = (inStream.read(1) == 1);
+ inStream.skip(3);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeUserData(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException
{
- int paramBytes = inStream.read(8);
+ int paramBits = inStream.read(8) * 8;
bData.userData = new UserData();
bData.userData.msgEncoding = inStream.read(5);
bData.userData.msgEncodingSet = true;
@@ -821,13 +887,17 @@ public final class BearerData {
}
bData.userData.numFields = inStream.read(8);
consumedBits += 8;
- int dataBits = (paramBytes * 8) - consumedBits;
+ int dataBits = paramBits - consumedBits;
bData.userData.payload = inStream.readByteArray(dataBits);
+ return true;
}
private static String decodeUtf16(byte[] data, int offset, int numFields)
throws CodingException
{
+ // Start reading from the next 16-bit aligned boundry after offset.
+ int padding = offset % 2;
+ numFields -= (offset + padding) / 2;
try {
return new String(data, offset, numFields * 2, "utf-16be");
} catch (java.io.UnsupportedEncodingException ex) {
@@ -835,7 +905,7 @@ public final class BearerData {
}
}
- private static String decodeIa5(byte[] data, int offset, int numFields)
+ private static String decode7bitAscii(byte[] data, int offset, int numFields)
throws CodingException
{
try {
@@ -850,38 +920,20 @@ public final class BearerData {
inStream.skip(offset);
for (int i = 0; i < numFields; i++) {
int charCode = inStream.read(7);
- if ((charCode < UserData.IA5_MAP_BASE_INDEX) ||
- (charCode > UserData.IA5_MAP_MAX_INDEX)) {
- throw new CodingException("unsupported AI5 character code (" + charCode + ")");
+ if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
+ (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
+ strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
+ } else if (charCode == UserData.ASCII_NL_INDEX) {
+ strBuf.append('\n');
+ } else if (charCode == UserData.ASCII_CR_INDEX) {
+ strBuf.append('\r');
+ } else {
+ /* For other charCodes, they are unprintable, and so simply use SPACE. */
+ strBuf.append(' ');
}
- strBuf.append(UserData.IA5_MAP[charCode - UserData.IA5_MAP_BASE_INDEX]);
}
return strBuf.toString();
} catch (BitwiseInputStream.AccessException ex) {
- throw new CodingException("AI5 decode failed: " + ex);
- }
- }
-
- private static String decode7bitAscii(byte[] data, int offset, int numFields)
- throws CodingException
- {
- try {
- offset *= 8;
- BitwiseInputStream inStream = new BitwiseInputStream(data);
- int wantedBits = offset + (numFields * 7);
- if (inStream.available() < wantedBits) {
- throw new CodingException("insufficient data (wanted " + wantedBits +
- " bits, but only have " + inStream.available() + ")");
- }
- inStream.skip(offset);
- byte[] expandedData = new byte[numFields];
- for (int i = 0; i < numFields; i++) {
- expandedData[i] = (byte)inStream.read(7);
- }
- return new String(expandedData, 0, numFields, "US-ASCII");
- } catch (java.io.UnsupportedEncodingException ex) {
- throw new CodingException("7bit ASCII decode failed: " + ex);
- } catch (BitwiseInputStream.AccessException ex) {
throw new CodingException("7bit ASCII decode failed: " + ex);
}
}
@@ -889,11 +941,11 @@ public final class BearerData {
private static String decode7bitGsm(byte[] data, int offset, int numFields)
throws CodingException
{
- int paddingBits = calcUdhSeptetPadding(offset);
- numFields -= (((offset * 8) + paddingBits) / 7);
- // TODO: It seems wrong that only Gsm7 bit encodings would
- // take into account the header in numFields calculations.
- // This should be verified.
+ // Start reading from the next 7-bit aligned boundry after offset.
+ int offsetBits = offset * 8;
+ int offsetSeptets = (offsetBits + 6) / 7;
+ numFields -= offsetSeptets;
+ int paddingBits = (offsetSeptets * 7) - offsetBits;
String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits);
if (result == null) {
throw new CodingException("7bit GSM decoding failed");
@@ -901,6 +953,16 @@ public final class BearerData {
return result;
}
+ private static String decodeLatin(byte[] data, int offset, int numFields)
+ throws CodingException
+ {
+ try {
+ return new String(data, offset, numFields - offset, "ISO-8859-1");
+ } catch (java.io.UnsupportedEncodingException ex) {
+ throw new CodingException("ISO-8859-1 decode failed: " + ex);
+ }
+ }
+
private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
throws CodingException
{
@@ -914,19 +976,33 @@ public final class BearerData {
}
switch (userData.msgEncoding) {
case UserData.ENCODING_OCTET:
+ // Strip off any padding bytes, meaning any differences between the length of the
+ // array and the target length specified by numFields. This is to avoid any confusion
+ // by code elsewhere that only considers the payload array length.
+ byte[] payload = new byte[userData.numFields];
+ int copyLen = userData.numFields < userData.payload.length
+ ? userData.numFields : userData.payload.length;
+
+ System.arraycopy(userData.payload, 0, payload, 0, copyLen);
+ userData.payload = payload;
+
+ // There are many devices in the market that send 8bit text sms (latin encoded) as
+ // octet encoded.
+ userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
break;
+ case UserData.ENCODING_IA5:
case UserData.ENCODING_7BIT_ASCII:
userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
break;
- case UserData.ENCODING_IA5:
- userData.payloadStr = decodeIa5(userData.payload, offset, userData.numFields);
- break;
case UserData.ENCODING_UNICODE_16:
userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
break;
case UserData.ENCODING_GSM_7BIT_ALPHABET:
userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
break;
+ case UserData.ENCODING_LATIN:
+ userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
+ break;
default:
throw new CodingException("unsupported user data encoding ("
+ userData.msgEncoding + ")");
@@ -959,7 +1035,7 @@ public final class BearerData {
try {
StringBuffer strbuf = new StringBuffer(dataLen);
while (inStream.available() >= 6) {
- strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+ strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
}
String data = strbuf.toString();
bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
@@ -1001,7 +1077,7 @@ public final class BearerData {
}
StringBuffer strbuf = new StringBuffer(dataLen);
for (int i = 0; i < numFields; i++) {
- strbuf.append(UserData.IA5_MAP[inStream.read(6)]);
+ strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
}
bData.userData.payloadStr = strbuf.toString();
}
@@ -1049,36 +1125,68 @@ public final class BearerData {
}
}
- private static void decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- int paramBytes = inStream.read(8);
- if (paramBytes != 1) {
- throw new CodingException("REPLY_OPTION subparam size incorrect");
- }
- bData.userAckReq = (inStream.read(1) == 1);
- bData.deliveryAckReq = (inStream.read(1) == 1);
- bData.readAckReq = (inStream.read(1) == 1);
- bData.reportReq = (inStream.read(1) == 1);
- inStream.skip(4);
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.userAckReq = (inStream.read(1) == 1);
+ bData.deliveryAckReq = (inStream.read(1) == 1);
+ bData.readAckReq = (inStream.read(1) == 1);
+ bData.reportReq = (inStream.read(1) == 1);
+ inStream.skip(4);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "REPLY_OPTION decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("NUMBER_OF_MESSAGES subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.numberOfMessages = inStream.read(8);
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 2) {
- throw new CodingException("MESSAGE_DEPOSIT_INDEX subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 2 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
@@ -1112,10 +1220,10 @@ public final class BearerData {
}
}
- private static void decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- int paramBytes = inStream.read(8);
+ int paramBits = inStream.read(8) * 8;
CdmaSmsAddress addr = new CdmaSmsAddress();
addr.digitMode = inStream.read(1);
byte fieldBits = 4;
@@ -1128,140 +1236,274 @@ public final class BearerData {
}
addr.numberOfDigits = inStream.read(8);
consumedBits += 8;
- int remainingBits = (paramBytes * 8) - consumedBits;
+ int remainingBits = paramBits - consumedBits;
int dataBits = addr.numberOfDigits * fieldBits;
int paddingBits = remainingBits - dataBits;
if (remainingBits < dataBits) {
throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
- "remainingBits " + remainingBits + ", dataBits " +
- dataBits + ", paddingBits " + paddingBits + ")");
+ "remainingBits + " + remainingBits + ", dataBits + " +
+ dataBits + ", paddingBits + " + paddingBits + ")");
}
addr.origBytes = inStream.readByteArray(dataBits);
inStream.skip(paddingBits);
decodeSmsAddress(addr);
bData.callbackNumber = addr;
+ return true;
}
- private static void decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("MESSAGE_STATUS subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.errorClass = inStream.read(2);
+ bData.messageStatus = inStream.read(6);
}
- bData.errorClass = inStream.read(2);
- bData.messageStatus = inStream.read(6);
- bData.messageStatusSet = true;
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "MESSAGE_STATUS decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ bData.messageStatusSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 6) {
- throw new CodingException("MESSAGE_CENTER_TIME_STAMP subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 6 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 6) {
- throw new CodingException("VALIDITY_PERIOD_ABSOLUTE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 6 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
}
- bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 6) {
- throw new CodingException("DEFERRED_DELIVERY_TIME_ABSOLUTE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 6 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
+ inStream.readByteArray(6 * 8));
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
+ inStream.skip(paramBits);
+ return decodeSuccess;
}
- private static void decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("VALIDITY_PERIOD_RELATIVE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.deferredDeliveryTimeRelative = inStream.read(8);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.deferredDeliveryTimeRelative = inStream.read(8);
- bData.deferredDeliveryTimeRelativeSet = true;
+ inStream.skip(paramBits);
+ bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("DEFERRED_DELIVERY_TIME_RELATIVE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.validityPeriodRelative = inStream.read(8);
}
- bData.validityPeriodRelative = inStream.read(8);
- bData.validityPeriodRelativeSet = true;
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ bData.validityPeriodRelativeSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("PRIVACY_INDICATOR subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.privacy = inStream.read(2);
+ inStream.skip(6);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.privacy = inStream.read(2);
- inStream.skip(6);
- bData.privacyIndicatorSet = true;
+ inStream.skip(paramBits);
+ bData.privacyIndicatorSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("LANGUAGE_INDICATOR subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.language = inStream.read(8);
}
- bData.language = inStream.read(8);
- bData.languageIndicatorSet = true;
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ bData.languageIndicatorSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("DISPLAY_MODE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.displayMode = inStream.read(2);
+ inStream.skip(6);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "DISPLAY_MODE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.displayMode = inStream.read(2);
- inStream.skip(6);
- bData.displayModeSet = true;
+ inStream.skip(paramBits);
+ bData.displayModeSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("PRIORITY_INDICATOR subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.priority = inStream.read(2);
+ inStream.skip(6);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.priority = inStream.read(2);
- inStream.skip(6);
- bData.priorityIndicatorSet = true;
+ inStream.skip(paramBits);
+ bData.priorityIndicatorSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("ALERT_ON_MESSAGE_DELIVERY subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.alert = inStream.read(2);
+ inStream.skip(6);
}
- bData.alert = inStream.read(2);
- inStream.skip(6);
- bData.alertIndicatorSet = true;
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
+ }
+ inStream.skip(paramBits);
+ bData.alertIndicatorSet = decodeSuccess;
+ return decodeSuccess;
}
- private static void decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
+ private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
throws BitwiseInputStream.AccessException, CodingException
{
- if (inStream.read(8) != 1) {
- throw new CodingException("USER_REPONSE_CODE subparam size incorrect");
+ final int EXPECTED_PARAM_SIZE = 1 * 8;
+ boolean decodeSuccess = false;
+ int paramBits = inStream.read(8) * 8;
+ if (paramBits >= EXPECTED_PARAM_SIZE) {
+ paramBits -= EXPECTED_PARAM_SIZE;
+ decodeSuccess = true;
+ bData.userResponseCode = inStream.read(8);
+ }
+ if ((! decodeSuccess) || (paramBits > 0)) {
+ Log.d(LOG_TAG, "USER_REPONSE_CODE decode " +
+ (decodeSuccess ? "succeeded" : "failed") +
+ " (extra bits = " + paramBits + ")");
}
- bData.userResponseCode = inStream.read(8);
- bData.userResponseCodeSet = true;
+ inStream.skip(paramBits);
+ bData.userResponseCodeSet = decodeSuccess;
+ return decodeSuccess;
}
/**
@@ -1278,72 +1520,73 @@ public final class BearerData {
BearerData bData = new BearerData();
int foundSubparamMask = 0;
while (inStream.available() > 0) {
+ boolean decodeSuccess = false;
int subparamId = inStream.read(8);
int subparamIdBit = 1 << subparamId;
if ((foundSubparamMask & subparamIdBit) != 0) {
throw new CodingException("illegal duplicate subparameter (" +
subparamId + ")");
}
- foundSubparamMask |= subparamIdBit;
switch (subparamId) {
case SUBPARAM_MESSAGE_IDENTIFIER:
- decodeMessageId(bData, inStream);
+ decodeSuccess = decodeMessageId(bData, inStream);
break;
case SUBPARAM_USER_DATA:
- decodeUserData(bData, inStream);
+ decodeSuccess = decodeUserData(bData, inStream);
break;
case SUBPARAM_USER_REPONSE_CODE:
- decodeUserResponseCode(bData, inStream);
+ decodeSuccess = decodeUserResponseCode(bData, inStream);
break;
case SUBPARAM_REPLY_OPTION:
- decodeReplyOption(bData, inStream);
+ decodeSuccess = decodeReplyOption(bData, inStream);
break;
case SUBPARAM_NUMBER_OF_MESSAGES:
- decodeMsgCount(bData, inStream);
+ decodeSuccess = decodeMsgCount(bData, inStream);
break;
case SUBPARAM_CALLBACK_NUMBER:
- decodeCallbackNumber(bData, inStream);
+ decodeSuccess = decodeCallbackNumber(bData, inStream);
break;
case SUBPARAM_MESSAGE_STATUS:
- decodeMsgStatus(bData, inStream);
+ decodeSuccess = decodeMsgStatus(bData, inStream);
break;
case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
- decodeMsgCenterTimeStamp(bData, inStream);
+ decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
break;
case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
- decodeValidityAbs(bData, inStream);
+ decodeSuccess = decodeValidityAbs(bData, inStream);
break;
case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
- decodeValidityRel(bData, inStream);
+ decodeSuccess = decodeValidityRel(bData, inStream);
break;
case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
- decodeDeferredDeliveryAbs(bData, inStream);
+ decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
break;
case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
- decodeDeferredDeliveryRel(bData, inStream);
+ decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
break;
case SUBPARAM_PRIVACY_INDICATOR:
- decodePrivacyIndicator(bData, inStream);
+ decodeSuccess = decodePrivacyIndicator(bData, inStream);
break;
case SUBPARAM_LANGUAGE_INDICATOR:
- decodeLanguageIndicator(bData, inStream);
+ decodeSuccess = decodeLanguageIndicator(bData, inStream);
break;
case SUBPARAM_MESSAGE_DISPLAY_MODE:
- decodeDisplayMode(bData, inStream);
+ decodeSuccess = decodeDisplayMode(bData, inStream);
break;
case SUBPARAM_PRIORITY_INDICATOR:
- decodePriorityIndicator(bData, inStream);
+ decodeSuccess = decodePriorityIndicator(bData, inStream);
break;
case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
- decodeMsgDeliveryAlert(bData, inStream);
+ decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
break;
case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
- decodeDepositIndex(bData, inStream);
+ decodeSuccess = decodeDepositIndex(bData, inStream);
break;
default:
throw new CodingException("unsupported bearer data subparameter ("
+ subparamId + ")");
}
+ if (decodeSuccess) foundSubparamMask |= subparamIdBit;
}
if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index 4d79966..d9cc2c6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -16,7 +16,10 @@
package com.android.internal.telephony.cdma.sms;
+import android.util.SparseBooleanArray;
+
import com.android.internal.telephony.SmsAddress;
+import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
public class CdmaSmsAddress extends SmsAddress {
@@ -43,7 +46,8 @@ public class CdmaSmsAddress extends SmsAddress {
/**
* Number Types for data networks.
- * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
+ * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table)
+ * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset)
* NOTE: value is stored in the parent class ton field.
*/
static public final int TON_UNKNOWN = 0x00;
@@ -98,10 +102,127 @@ public class CdmaSmsAddress extends SmsAddress {
builder.append(", numberPlan=" + numberPlan);
builder.append(", numberOfDigits=" + numberOfDigits);
builder.append(", ton=" + ton);
- builder.append(", address=" + address);
+ builder.append(", address=\"" + address + "\"");
builder.append(", origBytes=" + HexDump.toHexString(origBytes));
builder.append(" }");
return builder.toString();
}
+ /*
+ * TODO(cleanup): Refactor the parsing for addresses to better
+ * share code and logic with GSM. Also, gather all DTMF/BCD
+ * processing code in one place.
+ */
+
+ private static byte[] parseToDtmf(String address) {
+ int digits = address.length();
+ byte[] result = new byte[digits];
+ for (int i = 0; i < digits; i++) {
+ char c = address.charAt(i);
+ int val = 0;
+ if ((c >= '1') && (c <= '9')) val = c - '0';
+ else if (c == '0') val = 10;
+ else if (c == '*') val = 11;
+ else if (c == '#') val = 12;
+ else return null;
+ result[i] = (byte)val;
+ }
+ return result;
+ }
+
+ private static final char[] numericCharsDialable = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#'
+ };
+
+ private static final char[] numericCharsSugar = {
+ '(', ')', ' ', '-', '+'
+ };
+
+ private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray (
+ numericCharsDialable.length + numericCharsSugar.length);
+ static {
+ for (int i = 0; i < numericCharsDialable.length; i++) {
+ numericCharDialableMap.put(numericCharsDialable[i], true);
+ }
+ for (int i = 0; i < numericCharsSugar.length; i++) {
+ numericCharDialableMap.put(numericCharsSugar[i], false);
+ }
+ }
+
+ /**
+ * Given a numeric address string, return the string without
+ * syntactic sugar, meaning parens, spaces, hyphens/minuses, or
+ * plus signs. If the input string contains non-numeric
+ * non-punctuation characters, return null.
+ */
+ private static String filterNumericSugar(String address) {
+ StringBuilder builder = new StringBuilder();
+ int len = address.length();
+ for (int i = 0; i < len; i++) {
+ char c = address.charAt(i);
+ int mapIndex = numericCharDialableMap.indexOfKey(c);
+ if (mapIndex < 0) return null;
+ if (! numericCharDialableMap.valueAt(mapIndex)) continue;
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Given a string, return the string without whitespace,
+ * including CR/LF.
+ */
+ private static String filterWhitespace(String address) {
+ StringBuilder builder = new StringBuilder();
+ int len = address.length();
+ for (int i = 0; i < len; i++) {
+ char c = address.charAt(i);
+ if ((c == ' ') || (c == '\r') || (c == '\n') || (c == '\t')) continue;
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Given a string, create a corresponding CdmaSmsAddress object.
+ *
+ * The result will be null if the input string is not
+ * representable using printable ASCII.
+ *
+ * For numeric addresses, the string is cleaned up by removing
+ * common punctuation. For alpha addresses, the string is cleaned
+ * up by removing whitespace.
+ */
+ public static CdmaSmsAddress parse(String address) {
+ CdmaSmsAddress addr = new CdmaSmsAddress();
+ addr.address = address;
+ addr.ton = CdmaSmsAddress.TON_UNKNOWN;
+ byte[] origBytes = null;
+ String filteredAddr = filterNumericSugar(address);
+ if (filteredAddr != null) {
+ origBytes = parseToDtmf(filteredAddr);
+ }
+ if (origBytes != null) {
+ addr.digitMode = DIGIT_MODE_4BIT_DTMF;
+ addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
+ if (address.indexOf('+') != -1) {
+ addr.ton = TON_INTERNATIONAL_OR_IP;
+ }
+ } else {
+ filteredAddr = filterWhitespace(address);
+ origBytes = UserData.stringToAscii(filteredAddr);
+ if (origBytes == null) {
+ return null;
+ }
+ addr.digitMode = DIGIT_MODE_8BIT_CHAR;
+ addr.numberMode = NUMBER_MODE_DATA_NETWORK;
+ if (address.indexOf('@') != -1) {
+ addr.ton = TON_NATIONAL_OR_EMAIL;
+ }
+ }
+ addr.origBytes = origBytes;
+ addr.numberOfDigits = origBytes.length;
+ return addr;
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index f80e8c0..0dcacc1 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -36,6 +36,14 @@ public final class SmsEnvelope{
static public final int TELESERVICE_WAP = 0x1004;
static public final int TELESERVICE_WEMT = 0x1005;
+ /**
+ * The following are defined as extensions to the standard teleservices
+ */
+ // Voice mail notification through Message Waiting Indication in CDMA mode or Analog mode.
+ // Defined in 3GPP2 C.S-0005, 3.7.5.6, an Info Record containing an 8-bit number with the
+ // number of messages waiting, it's used by some CDMA carriers for a voice mail count.
+ static public final int TELESERVICE_MWI = 0x40000;
+
// ServiceCategories for Cell Broadcast, see 3GPP2 C.R1001 table 9.3.1-1
//static public final int SERVICECATEGORY_EMERGENCY = 0x0010;
//...
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
index 34cbbfa..d93852c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java
@@ -35,7 +35,7 @@ public class UserData {
//public static final int ENCODING_SHIFT_JIS = 0x05;
//public static final int ENCODING_KOREAN = 0x06;
//public static final int ENCODING_LATIN_HEBREW = 0x07;
- //public static final int ENCODING_LATIN = 0x08;
+ public static final int ENCODING_LATIN = 0x08;
public static final int ENCODING_GSM_7BIT_ALPHABET = 0x09;
public static final int ENCODING_GSM_DCS = 0x0A;
@@ -49,19 +49,20 @@ public class UserData {
public static final int IS91_MSG_TYPE_SHORT_MESSAGE = 0x85;
/**
- * IA5 data encoding character mappings.
- * (See CCITT Rec. T.50 Tables 1 and 3)
+ * US ASCII character mapping table.
*
- * Note this mapping is the the same as for printable ASCII
- * characters, with a 0x20 offset, meaning that the ASCII SPACE
- * character occurs with code 0x20.
+ * This table contains only the printable ASCII characters, with a
+ * 0x20 offset, meaning that the ASCII SPACE character is at index
+ * 0, with the resulting code of 0x20.
*
- * Note this mapping is also equivalent to that used by the IS-91
- * protocol, except for the latter using only 6 bits, and hence
- * mapping only entries up to the '_' character.
+ * Note this mapping is also equivalent to that used by both the
+ * IS5 and the IS-91 encodings. For the former this is defined
+ * using CCITT Rec. T.50 Tables 1 and 3. For the latter IS 637 B,
+ * Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
+ * and hence only maps entries up to the '_' character.
*
*/
- public static final char[] IA5_MAP = {
+ public static final char[] ASCII_MAP = {
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
@@ -81,23 +82,43 @@ public class UserData {
* Only elements between these indices in the ASCII table are printable.
*/
public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
- public static final int ASCII_LF_INDEX = 0x0A;
+ public static final int ASCII_NL_INDEX = 0x0A;
public static final int ASCII_CR_INDEX = 0x0D;
public static final SparseIntArray charToAscii = new SparseIntArray();
static {
- for (int i = 0; i < IA5_MAP.length; i++) {
- charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
+ for (int i = 0; i < ASCII_MAP.length; i++) {
+ charToAscii.put(ASCII_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
}
- charToAscii.put('\r', ASCII_LF_INDEX);
- charToAscii.put('\n', ASCII_CR_INDEX);
+ charToAscii.put('\n', ASCII_NL_INDEX);
+ charToAscii.put('\r', ASCII_CR_INDEX);
+ }
+
+ /*
+ * TODO(cleanup): Move this very generic functionality somewhere
+ * more general.
+ */
+ /**
+ * Given a string generate a corresponding ASCII-encoded byte
+ * array, but limited to printable characters. If the input
+ * contains unprintable characters, return null.
+ */
+ public static byte[] stringToAscii(String str) {
+ int len = str.length();
+ byte[] result = new byte[len];
+ for (int i = 0; i < len; i++) {
+ int charCode = charToAscii.get(str.charAt(i), -1);
+ if (charCode == -1) return null;
+ result[i] = (byte)charCode;
+ }
+ return result;
}
/**
- * Mapping for IA5 values less than 32 are flow control signals
+ * Mapping for ASCII values less than 32 are flow control signals
* and not used here.
*/
- public static final int IA5_MAP_BASE_INDEX = 0x20;
- public static final int IA5_MAP_MAX_INDEX = IA5_MAP_BASE_INDEX + IA5_MAP.length - 1;
+ public static final int ASCII_MAP_BASE_INDEX = 0x20;
+ public static final int ASCII_MAP_MAX_INDEX = ASCII_MAP_BASE_INDEX + ASCII_MAP.length - 1;
/**
* Contains the data header of the user data
@@ -133,7 +154,7 @@ public class UserData {
builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset"));
builder.append(", msgType=" + msgType);
builder.append(", paddingBits=" + paddingBits);
- builder.append(", numFields=" + (int)numFields);
+ builder.append(", numFields=" + numFields);
builder.append(", userDataHeader=" + userDataHeader);
builder.append(", payload='" + HexDump.toHexString(payload) + "'");
builder.append(", payloadStr='" + payloadStr + "'");
diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 3ca39de..dc6f92d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -72,7 +72,10 @@ public class ApnSetting {
boolean canHandleType(String type) {
for (String t : types) {
- if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL)) {
+ // DEFAULT handles all, and HIPRI is handled by DEFAULT
+ if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL) ||
+ (t.equals(Phone.APN_TYPE_DEFAULT) &&
+ type.equals(Phone.APN_TYPE_HIPRI))) {
return true;
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index d1e4b4f..2fc2e13 100755
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -23,13 +23,11 @@ import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Handler;
-import android.os.Looper;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemProperties;
import android.preference.PreferenceManager;
-import android.provider.Settings;
import android.provider.Telephony;
import android.telephony.CellLocation;
import android.telephony.PhoneNumberUtils;
@@ -51,6 +49,7 @@ import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDI
import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
+import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallForwardInfo;
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.CommandsInterface;
@@ -97,15 +96,13 @@ public class GSMPhone extends PhoneBase {
// Key used to read/write the SIM IMSI used for storing the voice mail
public static final String VM_SIM_IMSI = "vm_sim_imsi_key";
- //***** Instance Variables
+ // Instance Variables
GsmCallTracker mCT;
GsmServiceStateTracker mSST;
GsmSMSDispatcher mSMS;
- GsmDataConnectionTracker mDataConnection;
SIMRecords mSIMRecords;
SimCard mSimCard;
StkService mStkService;
- MyHandler h;
ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>();
SimPhoneBookInterfaceManager mSimPhoneBookIntManager;
SimSmsInterfaceManager mSimSmsIntManager;
@@ -129,7 +126,7 @@ public class GSMPhone extends PhoneBase {
private String mVmNumber;
- //***** Constructors
+ // Constructors
public
GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier) {
@@ -138,9 +135,7 @@ public class GSMPhone extends PhoneBase {
public
GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
- super(notifier, context, unitTestMode);
- h = new MyHandler();
- mCM = ci;
+ super(notifier, context, ci, unitTestMode);
if (ci instanceof SimulatedRadioControl) {
mSimulatedRadioControl = (SimulatedRadioControl) ci;
@@ -162,14 +157,13 @@ public class GSMPhone extends PhoneBase {
mStkService = StkService.getInstance(mCM, mSIMRecords, mContext,
(SIMFileHandler)mIccFileHandler, mSimCard);
- mCM.registerForAvailable(h, EVENT_RADIO_AVAILABLE, null);
- mSIMRecords.registerForRecordsLoaded(h, EVENT_SIM_RECORDS_LOADED, null);
- mCM.registerForOffOrNotAvailable(h, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
- mCM.registerForOn(h, EVENT_RADIO_ON, null);
- mCM.setOnUSSD(h, EVENT_USSD, null);
- mCM.setOnSuppServiceNotification(h, EVENT_SSN, null);
- mCM.setOnCallRing(h, EVENT_CALL_RING, null);
- mSST.registerForNetworkAttach(h, EVENT_REGISTERED_TO_NETWORK, null);
+ mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
+ mSIMRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
+ mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mCM.registerForOn(this, EVENT_RADIO_ON, null);
+ mCM.setOnUSSD(this, EVENT_USSD, null);
+ mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);
+ mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);
if (false) {
try {
@@ -212,15 +206,16 @@ public class GSMPhone extends PhoneBase {
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+ super.dispose();
+
//Unregister from all former registered events
- mCM.unregisterForAvailable(h); //EVENT_RADIO_AVAILABLE
- mSIMRecords.unregisterForRecordsLoaded(h); //EVENT_SIM_RECORDS_LOADED
- mCM.unregisterForOffOrNotAvailable(h); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
- mCM.unregisterForOn(h); //EVENT_RADIO_ON
- mSST.unregisterForNetworkAttach(h); //EVENT_REGISTERED_TO_NETWORK
- mCM.unSetOnUSSD(h);
- mCM.unSetOnSuppServiceNotification(h);
- mCM.unSetOnCallRing(h);
+ mCM.unregisterForAvailable(this); //EVENT_RADIO_AVAILABLE
+ mSIMRecords.unregisterForRecordsLoaded(this); //EVENT_SIM_RECORDS_LOADED
+ mCM.unregisterForOffOrNotAvailable(this); //EVENT_RADIO_OFF_OR_NOT_AVAILABLE
+ mCM.unregisterForOn(this); //EVENT_RADIO_ON
+ mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK
+ mCM.unSetOnUSSD(this);
+ mCM.unSetOnSuppServiceNotification(this);
mPendingMMIs.clear();
@@ -258,8 +253,6 @@ public class GSMPhone extends PhoneBase {
}
- //***** Overridden from Phone
-
public ServiceState
getServiceState() {
return mSST.ss;
@@ -279,14 +272,6 @@ public class GSMPhone extends PhoneBase {
return "GSM";
}
- public String[] getActiveApnTypes() {
- return mDataConnection.getActiveApnTypes();
- }
-
- public String getActiveApn() {
- return mDataConnection.getActiveApnString();
- }
-
public SignalStrength getSignalStrength() {
return mSST.mSignalStrength;
}
@@ -378,20 +363,19 @@ public class GSMPhone extends PhoneBase {
}
/**
- * Notify any interested party of a Phone state change.
+ * Notify any interested party of a Phone state change {@link Phone.State}
*/
/*package*/ void notifyPhoneStateChanged() {
mNotifier.notifyPhoneState(this);
}
/**
- * Notifies registrants (ie, activities in the Phone app) about
- * changes to call state (including Phone and Connection changes).
+ * Notify registrants of a change in the call state. This notifies changes in {@link Call.State}
+ * Use this when changes in the precise call state are needed, else use notifyPhoneStateChanged.
*/
- /*package*/ void
- notifyCallStateChanged() {
+ /*package*/ void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
- super.notifyCallStateChangedP();
+ super.notifyPreciseCallStateChangedP();
}
/*package*/ void
@@ -400,14 +384,6 @@ public class GSMPhone extends PhoneBase {
super.notifyNewRingingConnectionP(c);
}
- /**
- * Notifiy registrants of a RING event.
- */
- void notifyIncomingRing() {
- AsyncResult ar = new AsyncResult(null, this, null);
- mIncomingRingRegistrants.notifyRegistrants(ar);
- }
-
/*package*/ void
notifyDisconnect(Connection cn) {
mDisconnectRegistrants.notifyResult(cn);
@@ -926,7 +902,7 @@ public class GSMPhone extends PhoneBase {
Message resp;
mVmNumber = voiceMailNumber;
- resp = h.obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
+ resp = obtainMessage(EVENT_SET_VM_NUMBER_DONE, 0, 0, onComplete);
mSIMRecords.setVoiceMailNumber(alphaTag, mVmNumber, resp);
}
@@ -965,7 +941,7 @@ public class GSMPhone extends PhoneBase {
if (LOCAL_DEBUG) Log.d(LOG_TAG, "requesting call forwarding query.");
Message resp;
if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) {
- resp = h.obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
+ resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, onComplete);
} else {
resp = onComplete;
}
@@ -983,7 +959,7 @@ public class GSMPhone extends PhoneBase {
Message resp;
if (commandInterfaceCFReason == CF_REASON_UNCONDITIONAL) {
- resp = h.obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
+ resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE,
isCfEnable(commandInterfaceCFAction) ? 1 : 0, 0, onComplete);
} else {
resp = onComplete;
@@ -1004,7 +980,7 @@ public class GSMPhone extends PhoneBase {
public void setOutgoingCallerIdDisplay(int commandInterfaceCLIRMode,
Message onComplete) {
mCM.setCLIR(commandInterfaceCLIRMode,
- h.obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete));
+ obtainMessage(EVENT_SET_CLIR_COMPLETE, commandInterfaceCLIRMode, 0, onComplete));
}
public void getCallWaiting(Message onComplete) {
@@ -1032,11 +1008,13 @@ public class GSMPhone extends PhoneBase {
/**
* Small container class used to hold information relevant to
* the carrier selection process. operatorNumeric can be ""
- * if we are looking for automatic selection.
+ * if we are looking for automatic selection. operatorAlphaLong is the
+ * corresponding operator name.
*/
private static class NetworkSelectMessage {
public Message message;
public String operatorNumeric;
+ public String operatorAlphaLong;
}
public void
@@ -1047,9 +1025,10 @@ public class GSMPhone extends PhoneBase {
NetworkSelectMessage nsm = new NetworkSelectMessage();
nsm.message = response;
nsm.operatorNumeric = "";
+ nsm.operatorAlphaLong = "";
// get the message
- Message msg = h.obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
+ Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
if (LOCAL_DEBUG)
Log.d(LOG_TAG, "wrapping and sending message to connect automatically");
@@ -1064,9 +1043,10 @@ public class GSMPhone extends PhoneBase {
NetworkSelectMessage nsm = new NetworkSelectMessage();
nsm.message = response;
nsm.operatorNumeric = network.operatorNumeric;
+ nsm.operatorAlphaLong = network.operatorAlphaLong;
// get the message
- Message msg = h.obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
+ Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
mCM.setNetworkSelectionModeManual(network.operatorNumeric, msg);
}
@@ -1089,8 +1069,9 @@ public class GSMPhone extends PhoneBase {
}
/**
- * @deprecated
+ * @deprecated Do not use.
*/
+ @Deprecated
public void getPdpContextList(Message response) {
getDataCallList(response);
}
@@ -1100,8 +1081,9 @@ public class GSMPhone extends PhoneBase {
}
/**
- * @deprecated
+ * @deprecated Do not use.
*/
+ @Deprecated
public List<PdpConnection> getCurrentPdpList() {
ArrayList<DataConnection> connections = new ArrayList<DataConnection>();
ArrayList<PdpConnection> pdp_list = new ArrayList<PdpConnection>();
@@ -1117,8 +1099,8 @@ public class GSMPhone extends PhoneBase {
return mDataConnection.getAllDataConnections();
}
- public void updateServiceLocation(Message response) {
- mSST.getLacAndCid(response);
+ public void updateServiceLocation() {
+ mSST.enableSingleLocationUpdate();
}
public void enableLocationUpdates() {
@@ -1141,34 +1123,10 @@ public class GSMPhone extends PhoneBase {
return mDataConnection.setDataEnabled(true);
}
- public int enableApnType(String type) {
- return mDataConnection.enableApnType(type);
- }
-
- public int disableApnType(String type) {
- return mDataConnection.disableApnType(type);
- }
-
public boolean disableDataConnectivity() {
return mDataConnection.setDataEnabled(false);
}
- public String getInterfaceName(String apnType) {
- return mDataConnection.getInterfaceName(apnType);
- }
-
- public String getIpAddress(String apnType) {
- return mDataConnection.getIpAddress(apnType);
- }
-
- public String getGateway(String apnType) {
- return mDataConnection.getGateway(apnType);
- }
-
- public String[] getDnsServers(String apnType) {
- return mDataConnection.getDnsServers(apnType);
- }
-
/**
* The only circumstances under which we report that data connectivity is not
* possible are
@@ -1274,178 +1232,163 @@ public class GSMPhone extends PhoneBase {
}
}
- //***** Inner Classes
+ @Override
+ public void handleMessage (Message msg) {
+ AsyncResult ar;
+ Message onComplete;
- class MyHandler extends Handler {
- MyHandler() {
- }
+ switch (msg.what) {
+ case EVENT_RADIO_AVAILABLE: {
+ mCM.getBasebandVersion(
+ obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
- MyHandler(Looper l) {
- super(l);
- }
-
- public void
- handleMessage (Message msg) {
- AsyncResult ar;
- Message onComplete;
-
- switch (msg.what) {
- case EVENT_RADIO_AVAILABLE: {
- mCM.getBasebandVersion(
- obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
+ mCM.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
+ mCM.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
+ }
+ break;
- mCM.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
- mCM.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
- }
- break;
+ case EVENT_RADIO_ON:
+ break;
- case EVENT_RADIO_ON:
+ case EVENT_REGISTERED_TO_NETWORK:
+ syncClirSetting();
break;
- case EVENT_REGISTERED_TO_NETWORK:
- syncClirSetting();
- break;
-
- case EVENT_SIM_RECORDS_LOADED:
- updateCurrentCarrierInProvider();
+ case EVENT_SIM_RECORDS_LOADED:
+ updateCurrentCarrierInProvider();
- // Check if this is a different SIM than the previous one. If so unset the
- // voice mail number.
- String imsi = getVmSimImsi();
- if (imsi != null && !getSubscriberId().equals(imsi)) {
- storeVoiceMailNumber(null);
- setVmSimImsi(null);
- }
+ // Check if this is a different SIM than the previous one. If so unset the
+ // voice mail number.
+ String imsi = getVmSimImsi();
+ if (imsi != null && !getSubscriberId().equals(imsi)) {
+ storeVoiceMailNumber(null);
+ setVmSimImsi(null);
+ }
- break;
+ break;
- case EVENT_GET_BASEBAND_VERSION_DONE:
- ar = (AsyncResult)msg.obj;
+ case EVENT_GET_BASEBAND_VERSION_DONE:
+ ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- break;
- }
+ if (ar.exception != null) {
+ break;
+ }
- if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
- setSystemProperty(PROPERTY_BASEBAND_VERSION, (String)ar.result);
- break;
+ if (LOCAL_DEBUG) Log.d(LOG_TAG, "Baseband version: " + ar.result);
+ setSystemProperty(PROPERTY_BASEBAND_VERSION, (String)ar.result);
+ break;
- case EVENT_GET_IMEI_DONE:
- ar = (AsyncResult)msg.obj;
+ case EVENT_GET_IMEI_DONE:
+ ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- break;
- }
+ if (ar.exception != null) {
+ break;
+ }
- mImei = (String)ar.result;
- break;
+ mImei = (String)ar.result;
+ break;
- case EVENT_GET_IMEISV_DONE:
- ar = (AsyncResult)msg.obj;
+ case EVENT_GET_IMEISV_DONE:
+ ar = (AsyncResult)msg.obj;
- if (ar.exception != null) {
- break;
- }
+ if (ar.exception != null) {
+ break;
+ }
- mImeiSv = (String)ar.result;
- break;
+ mImeiSv = (String)ar.result;
+ break;
- case EVENT_USSD:
- ar = (AsyncResult)msg.obj;
+ case EVENT_USSD:
+ ar = (AsyncResult)msg.obj;
- String[] ussdResult = (String[]) ar.result;
+ String[] ussdResult = (String[]) ar.result;
- if (ussdResult.length > 1) {
- try {
- onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]);
- } catch (NumberFormatException e) {
- Log.w(LOG_TAG, "error parsing USSD");
- }
+ if (ussdResult.length > 1) {
+ try {
+ onIncomingUSSD(Integer.parseInt(ussdResult[0]), ussdResult[1]);
+ } catch (NumberFormatException e) {
+ Log.w(LOG_TAG, "error parsing USSD");
}
- break;
+ }
+ break;
- case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
- // Some MMI requests (eg USSD) are not completed
- // within the course of a CommandsInterface request
- // If the radio shuts off or resets while one of these
- // is pending, we need to clean up.
+ case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
+ // Some MMI requests (eg USSD) are not completed
+ // within the course of a CommandsInterface request
+ // If the radio shuts off or resets while one of these
+ // is pending, we need to clean up.
- for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
- if (mPendingMMIs.get(i).isPendingUSSD()) {
- mPendingMMIs.get(i).onUssdFinishedError();
- }
+ for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
+ if (mPendingMMIs.get(i).isPendingUSSD()) {
+ mPendingMMIs.get(i).onUssdFinishedError();
}
+ }
+ break;
+
+ case EVENT_SSN:
+ ar = (AsyncResult)msg.obj;
+ SuppServiceNotification not = (SuppServiceNotification) ar.result;
+ mSsnRegistrants.notifyRegistrants(ar);
+ break;
+
+ case EVENT_SET_CALL_FORWARD_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ mSIMRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
break;
- case EVENT_SSN:
- ar = (AsyncResult)msg.obj;
- SuppServiceNotification not = (SuppServiceNotification) ar.result;
- mSsnRegistrants.notifyRegistrants(ar);
+ case EVENT_SET_VM_NUMBER_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (IccVmNotSupportedException.class.isInstance(ar.exception)) {
+ storeVoiceMailNumber(mVmNumber);
+ ar.exception = null;
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
break;
- case EVENT_SET_CALL_FORWARD_DONE:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- mSIMRecords.setVoiceCallForwardingFlag(1, msg.arg1 == 1);
- }
- onComplete = (Message) ar.userObj;
- if (onComplete != null) {
- AsyncResult.forMessage(onComplete, ar.result, ar.exception);
- onComplete.sendToTarget();
- }
- break;
-
- case EVENT_SET_VM_NUMBER_DONE:
- ar = (AsyncResult)msg.obj;
- if (IccVmNotSupportedException.class.isInstance(ar.exception)) {
- storeVoiceMailNumber(mVmNumber);
- ar.exception = null;
- }
- onComplete = (Message) ar.userObj;
- if (onComplete != null) {
- AsyncResult.forMessage(onComplete, ar.result, ar.exception);
- onComplete.sendToTarget();
- }
- break;
-
- case EVENT_GET_CALL_FORWARD_DONE:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- handleCfuQueryResult((CallForwardInfo[])ar.result);
- }
- onComplete = (Message) ar.userObj;
- if (onComplete != null) {
- AsyncResult.forMessage(onComplete, ar.result, ar.exception);
- onComplete.sendToTarget();
- }
- break;
+ case EVENT_GET_CALL_FORWARD_DONE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ handleCfuQueryResult((CallForwardInfo[])ar.result);
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
+ break;
- case EVENT_CALL_RING:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- notifyIncomingRing();
- }
- break;
+ // handle the select network completion callbacks.
+ case EVENT_SET_NETWORK_MANUAL_COMPLETE:
+ case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
+ handleSetSelectNetwork((AsyncResult) msg.obj);
+ break;
- // handle the select network completion callbacks.
- case EVENT_SET_NETWORK_MANUAL_COMPLETE:
- case EVENT_SET_NETWORK_AUTOMATIC_COMPLETE:
- handleSetSelectNetwork((AsyncResult) msg.obj);
- break;
+ case EVENT_SET_CLIR_COMPLETE:
+ ar = (AsyncResult)msg.obj;
+ if (ar.exception == null) {
+ saveClirSetting(msg.arg1);
+ }
+ onComplete = (Message) ar.userObj;
+ if (onComplete != null) {
+ AsyncResult.forMessage(onComplete, ar.result, ar.exception);
+ onComplete.sendToTarget();
+ }
+ break;
- case EVENT_SET_CLIR_COMPLETE:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- saveClirSetting(msg.arg1);
- }
- onComplete = (Message) ar.userObj;
- if (onComplete != null) {
- AsyncResult.forMessage(onComplete, ar.result, ar.exception);
- onComplete.sendToTarget();
- }
- break;
- }
+ default:
+ super.handleMessage(msg);
}
}
@@ -1495,6 +1438,7 @@ public class GSMPhone extends PhoneBase {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = sp.edit();
editor.putString(NETWORK_SELECTION_KEY, nsm.operatorNumeric);
+ editor.putString(NETWORK_SELECTION_NAME_KEY, nsm.operatorAlphaLong);
// commit and log the result.
if (! editor.commit()) {
@@ -1534,36 +1478,6 @@ public class GSMPhone extends PhoneBase {
}
}
}
- /**
- * simulateDataConnection
- *
- * simulates various data connection states. This messes with
- * DataConnectionTracker's internal states, but doesn't actually change
- * the underlying radio connection states.
- *
- * @param state Phone.DataState enum.
- */
- public void simulateDataConnection(Phone.DataState state) {
- DataConnectionTracker.State dcState;
-
- switch (state) {
- case CONNECTED:
- dcState = DataConnectionTracker.State.CONNECTED;
- break;
- case SUSPENDED:
- dcState = DataConnectionTracker.State.CONNECTED;
- break;
- case DISCONNECTED:
- dcState = DataConnectionTracker.State.FAILED;
- break;
- default:
- dcState = DataConnectionTracker.State.CONNECTING;
- break;
- }
-
- mDataConnection.setState(dcState);
- notifyDataConnection(null);
- }
/**
* Retrieves the PhoneSubInfo of the GSMPhone
@@ -1589,13 +1503,6 @@ public class GSMPhone extends PhoneBase {
/**
* {@inheritDoc}
*/
- public Handler getHandler(){
- return h;
- }
-
- /**
- * {@inheritDoc}
- */
public IccFileHandler getIccFileHandler(){
return this.mIccFileHandler;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
index a92e52d..9542d20 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCall.java
@@ -185,6 +185,7 @@ class GsmCall extends Call {
cn.onHangupLocal();
}
+ state = State.DISCONNECTING;
}
/**
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 5c5090f..91c089e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -212,7 +212,7 @@ public final class GsmCallTracker extends CallTracker {
}
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
return pendingMO;
}
@@ -279,7 +279,7 @@ public final class GsmCallTracker extends CallTracker {
internalClearDisconnected();
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
boolean
@@ -294,12 +294,15 @@ public final class GsmCallTracker extends CallTracker {
canDial() {
boolean ret;
int serviceState = phone.getServiceState().getState();
+ String disableCall = SystemProperties.get(
+ TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
- ret = (serviceState != ServiceState.STATE_POWER_OFF) &&
- pendingMO == null
+ ret = (serviceState != ServiceState.STATE_POWER_OFF)
+ && pendingMO == null
&& !ringingCall.isRinging()
+ && !disableCall.equals("true")
&& (!foregroundCall.getState().isAlive()
- || !backgroundCall.getState().isAlive());
+ || !backgroundCall.getState().isAlive());
return ret;
}
@@ -600,7 +603,7 @@ public final class GsmCallTracker extends CallTracker {
}
if (hasNonHangupStateChanged || newRinging != null) {
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
}
//dumpState();
@@ -738,6 +741,7 @@ public final class GsmCallTracker extends CallTracker {
}
call.onHangupLocal();
+ phone.notifyPreciseCallStateChanged();
}
/* package */
@@ -883,7 +887,7 @@ public final class GsmCallTracker extends CallTracker {
updatePhoneState();
- phone.notifyCallStateChanged();
+ phone.notifyPreciseCallStateChanged();
droppedDuringPoll.clear();
break;
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
index d93ca1d..2091fb6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmConnection.java
@@ -381,10 +381,14 @@ public class GsmConnection extends Connection {
} else if (phone.mSST.rs.isCsNormalRestricted()) {
return DisconnectCause.CS_RESTRICTED_NORMAL;
} else {
- return DisconnectCause.NORMAL;
+ return DisconnectCause.ERROR_UNSPECIFIED;
}
- } else {
+ } else if (causeCode == CallFailCause.NORMAL_CLEARING) {
return DisconnectCause.NORMAL;
+ } else {
+ // If nothing else matches, report unknown call drop reason
+ // to app, not NORMAL call end.
+ return DisconnectCause.ERROR_UNSPECIFIED;
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index d48c941..0215ab2 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -52,6 +52,7 @@ import com.android.internal.telephony.DataCallState;
import com.android.internal.telephony.DataConnection;
import com.android.internal.telephony.DataConnectionTracker;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.RetryManager;
import com.android.internal.telephony.TelephonyEventLog;
import com.android.internal.telephony.DataConnection.FailCause;
@@ -62,8 +63,7 @@ import java.util.ArrayList;
* {@hide}
*/
public final class GsmDataConnectionTracker extends DataConnectionTracker {
- private static final String LOG_TAG = "GSM";
- private static final boolean DBG = true;
+ protected final String LOG_TAG = "GSM";
private GSMPhone mGsmPhone;
/**
@@ -85,7 +85,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
INetStatService netstat;
// Indicates baseband will not auto-attach
private boolean noAutoAttach = false;
- long nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+
private boolean mReregisterOnReconnectFailure = false;
private ContentResolver mResolver;
@@ -95,6 +95,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private int mPdpResetCount = 0;
private boolean mIsScreenOn = true;
+ /** Delay between APN attempts */
+ protected static final int APN_DELAY_MILLIS = 5000;
+
//useful for debugging
boolean failNextConnect = false;
@@ -115,27 +118,17 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private ApnSetting preferredApn = null;
+ /* Currently active APN */
+ protected ApnSetting mActiveApn;
+
/**
* pdpList holds all the PDP connection, i.e. IP Link in GPRS
*/
private ArrayList<DataConnection> pdpList;
- /** Currently requested APN type */
- private String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
-
- /** Currently active APN */
- private ApnSetting mActiveApn;
-
/** Currently active PdpConnection */
private PdpConnection mActivePdp;
- private static int APN_DEFAULT_ID = 0;
- private static int APN_MMS_ID = 1;
- private static int APN_SUPL_ID = 2;
- private static int APN_NUM_TYPES = 3;
-
- private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
-
/** Is packet service restricted by network */
private boolean mIsPsRestricted = false;
@@ -207,7 +200,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
GsmDataConnectionTracker(GSMPhone p) {
super(p);
mGsmPhone = p;
-
p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null);
p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null);
@@ -230,7 +222,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- p.getContext().registerReceiver(mIntentReceiver, filter, null, p.h);
+ // TODO: Why is this registering the phone as the receiver of the intent
+ // and not its own handler?
+ p.getContext().registerReceiver(mIntentReceiver, filter, null, p);
mDataConnectionTracker = this;
@@ -246,7 +240,19 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
// and 2) whether the RIL will setup the baseband to auto-PS attach.
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext());
dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false);
+ if (dataEnabled[APN_DEFAULT_ID]) {
+ enabledCount++;
+ }
noAutoAttach = !dataEnabled[APN_DEFAULT_ID];
+
+ if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) {
+ if (!mRetryMgr.configure(DEFAULT_DATA_RETRY_CONFIG)) {
+ // Should never happen, log an error and default to a simple linear sequence.
+ Log.e(LOG_TAG, "Could not configure using DEFAULT_DATA_RETRY_CONFIG="
+ + DEFAULT_DATA_RETRY_CONFIG);
+ mRetryMgr.configure(20, 2000, 1000);
+ }
+ }
}
public void dispose() {
@@ -274,7 +280,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if(DBG) Log.d(LOG_TAG, "GsmDataConnectionTracker finalized");
}
- void setState(State s) {
+ protected void setState(State s) {
if (DBG) log ("setState: " + s);
if (state != s) {
if (s == State.INITING) { // request PDP context
@@ -298,7 +304,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
}
- String[] getActiveApnTypes() {
+ public String[] getActiveApnTypes() {
String[] result;
if (mActiveApn != null) {
result = mActiveApn.types;
@@ -318,91 +324,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
/**
- * Ensure that we are connected to an APN of the specified type.
- * @param type the APN type (currently the only valid values
- * are {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL})
- * @return the result of the operation. Success is indicated by
- * a return value of either {@code Phone.APN_ALREADY_ACTIVE} or
- * {@code Phone.APN_REQUEST_STARTED}. In the latter case, a broadcast
- * will be sent by the ConnectivityManager when a connection to
- * the APN has been established.
- */
- protected int enableApnType(String type) {
- if (!TextUtils.equals(type, Phone.APN_TYPE_MMS) &&
- !TextUtils.equals(type, Phone.APN_TYPE_SUPL)) {
- return Phone.APN_REQUEST_FAILED;
- }
-
- // If already active, return
- Log.d(LOG_TAG, "enableApnType("+type+")");
- if (isApnTypeActive(type)) {
- setEnabled(type, true);
- removeMessages(EVENT_RESTORE_DEFAULT_APN);
- /**
- * We're being asked to enable a non-default APN that's already in use.
- * This means we should restart the timer that will automatically
- * switch back to the default APN and disable the non-default APN
- * when it expires.
- */
- sendMessageDelayed(
- obtainMessage(EVENT_RESTORE_DEFAULT_APN),
- getRestoreDefaultApnDelay());
- if (state == State.INITING) return Phone.APN_REQUEST_STARTED;
- else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE;
- }
-
- if (!isApnTypeAvailable(type)) {
- return Phone.APN_TYPE_NOT_AVAILABLE;
- }
-
- setEnabled(type, true);
- mRequestedApnType = type;
- sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN));
- return Phone.APN_REQUEST_STARTED;
- }
-
- /**
- * The APN of the specified type is no longer needed. Ensure that if
- * use of the default APN has not been explicitly disabled, we are connected
- * to the default APN.
- * @param type the APN type. The only valid values are currently
- * {@link Phone#APN_TYPE_MMS} and {@link Phone#APN_TYPE_SUPL}.
- * @return
- */
- protected int disableApnType(String type) {
- Log.d(LOG_TAG, "disableApnType("+type+")");
- if ((TextUtils.equals(type, Phone.APN_TYPE_MMS) ||
- TextUtils.equals(type, Phone.APN_TYPE_SUPL))
- && isEnabled(type)) {
- removeMessages(EVENT_RESTORE_DEFAULT_APN);
- setEnabled(type, false);
- if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
- mRequestedApnType = Phone.APN_TYPE_DEFAULT;
- if (dataEnabled[APN_DEFAULT_ID]) {
- return Phone.APN_ALREADY_ACTIVE;
- } else {
- Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
- msg.arg1 = 1; // tearDown is true;
- msg.obj = Phone.REASON_DATA_DISABLED;
- sendMessage(msg);
- return Phone.APN_REQUEST_STARTED;
- }
- } else {
- /*
- * Note that if default data is disabled, the following
- * has the effect of disabling the MMS APN, and then
- * ignoring the request to enable the default APN.
- * The net result is that data is completely disabled.
- */
- sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN));
- return Phone.APN_REQUEST_STARTED;
- }
- } else {
- return Phone.APN_REQUEST_FAILED;
- }
- }
-
- /**
* The data connection is expected to be setup while device
* 1. has sim card
* 2. registered to gprs service
@@ -428,12 +349,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return mGsmPhone.mSST.getDataRoaming();
}
- private boolean isApnTypeActive(String type) {
- // TODO: to support simultaneous, mActiveApn can be a List instead.
+ @Override
+ protected boolean isApnTypeActive(String type) {
+ // TODO: support simultaneous with List instead
return mActiveApn != null && mActiveApn.canHandleType(type);
}
- private boolean isApnTypeAvailable(String type) {
+ @Override
+ protected boolean isApnTypeAvailable(String type) {
if (allApns != null) {
for (ApnSetting apn : allApns) {
if (apn.canHandleType(type)) {
@@ -444,90 +367,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return false;
}
- private boolean isEnabled(String apnType) {
- if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) {
- return dataEnabled[APN_DEFAULT_ID];
- } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) {
- return dataEnabled[APN_MMS_ID];
- } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
- return dataEnabled[APN_SUPL_ID];
- } else {
- return false;
- }
- }
-
- private void setEnabled(String apnType, boolean enable) {
- Log.d(LOG_TAG, "setEnabled(" + apnType + ", " + enable + ')');
- if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) {
- dataEnabled[APN_DEFAULT_ID] = enable;
- } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) {
- dataEnabled[APN_MMS_ID] = enable;
- } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) {
- dataEnabled[APN_SUPL_ID] = enable;
- }
- Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] +
- " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID] +
- " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID]);
- }
-
- /**
- * Prevent mobile data connections from being established,
- * or once again allow mobile data connections. If the state
- * toggles, then either tear down or set up data, as
- * appropriate to match the new state.
- * <p>This operation only affects the default APN, and if the same APN is
- * currently being used for MMS traffic, the teardown will not happen
- * even when {@code enable} is {@code false}.</p>
- * @param enable indicates whether to enable ({@code true}) or disable ({@code false}) data
- * @return {@code true} if the operation succeeded
- */
- public boolean setDataEnabled(boolean enable) {
- boolean isEnabled = isEnabled(Phone.APN_TYPE_DEFAULT);
- Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled);
- if (!isEnabled && enable) {
- setEnabled(Phone.APN_TYPE_DEFAULT, true);
- // trySetupData() will be a no-op if we are currently
- // connected to the MMS APN
- sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA));
- return true;
- } else if (!enable) {
- setEnabled(Phone.APN_TYPE_DEFAULT, false);
- // Don't tear down if there is an active APN and it handles MMS or SUPL.
- // TODO: This isn't very general.
- if ((isApnTypeActive(Phone.APN_TYPE_MMS) && isEnabled(Phone.APN_TYPE_MMS)) ||
- (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) {
- return false;
- }
- Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
- msg.arg1 = 1; // tearDown is true
- msg.obj = Phone.REASON_DATA_DISABLED;
- sendMessage(msg);
- return true;
- } else {
- // isEnabled && enable
- return true;
- }
- }
-
- /**
- * Report the current state of data connectivity (enabled or disabled) for
- * the default APN.
- * @return {@code false} if data connectivity has been explicitly disabled,
- * {@code true} otherwise.
- */
- public boolean getDataEnabled() {
- return dataEnabled[APN_DEFAULT_ID];
- }
-
- /**
- * Report on whether data connectivity is enabled for any APN.
- * @return {@code false} if data connectivity has been explicitly disabled,
- * {@code true} otherwise.
- */
- public boolean getAnyDataEnabled() {
- return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID];
- }
-
/**
* Formerly this method was ArrayList<PdpConnection> getAllPdps()
*/
@@ -562,7 +401,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
} else {
if (state == State.FAILED) {
cleanUpConnection(false, Phone.REASON_GPRS_ATTACHED);
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
}
trySetupData(Phone.REASON_GPRS_ATTACHED);
}
@@ -607,7 +446,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
if (DBG) {
- log ("Setup watingApns : " + apnListToString(waitingApns));
+ log ("Setup waitngApns : " + apnListToString(waitingApns));
}
return setupData(reason);
} else {
@@ -618,6 +457,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
" sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() +
" UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() +
" phoneState=" + phone.getState() +
+ " isDataAllowed=" + isDataAllowed() +
" dataEnabled=" + getAnyDataEnabled() +
" roaming=" + roaming +
" dataOnRoamingEnable=" + getDataOnRoamingEnabled() +
@@ -742,7 +582,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return true;
}
- String getInterfaceName(String apnType) {
+ protected String getInterfaceName(String apnType) {
if (mActivePdp != null
&& (apnType == null || mActiveApn.canHandleType(apnType))) {
return mActivePdp.getInterface();
@@ -758,7 +598,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
return null;
}
- String getGateway(String apnType) {
+ public String getGateway(String apnType) {
if (mActivePdp != null
&& (apnType == null || mActiveApn.canHandleType(apnType))) {
return mActivePdp.getGatewayAddress();
@@ -812,7 +652,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
cleanUpConnection(isConnected, Phone.REASON_APN_CHANGED);
if (!isConnected) {
// reset reconnect timer
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
mReregisterOnReconnectFailure = false;
trySetupData(Phone.REASON_APN_CHANGED);
}
@@ -893,7 +733,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
phone.notifyDataConnection(reason);
startNetStatPoll();
// reset reconnect timer
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
mReregisterOnReconnectFailure = false;
}
@@ -1170,8 +1010,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private boolean retryAfterDisconnected(String reason) {
boolean retry = true;
- if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ||
- Phone.REASON_DATA_DISABLED.equals(reason) ) {
+ if ( Phone.REASON_RADIO_TURNED_OFF.equals(reason) ) {
retry = false;
}
return retry;
@@ -1179,20 +1018,21 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private void reconnectAfterFail(FailCause lastFailCauseCode, String reason) {
if (state == State.FAILED) {
- if (nextReconnectDelay > RECONNECT_DELAY_MAX_MILLIS) {
+ if (!mRetryMgr.isRetryNeeded()) {
if (mReregisterOnReconnectFailure) {
- // We have already tried to re-register to the network.
- // This might be a problem with the data network.
- nextReconnectDelay = RECONNECT_DELAY_MAX_MILLIS;
+ // We've re-registerd once now just retry forever.
+ mRetryMgr.retryForeverUsingLastTimeout();
} else {
- // Try to Re-register to the network.
+ // Try to re-register to the network.
Log.d(LOG_TAG, "PDP activate failed, Reregistering to the network");
mReregisterOnReconnectFailure = true;
mGsmPhone.mSST.reRegisterNetwork(null);
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
return;
}
}
+
+ int nextReconnectDelay = mRetryMgr.getRetryTimer();
Log.d(LOG_TAG, "PDP activate failed. Scheduling next attempt for "
+ (nextReconnectDelay / 1000) + "s");
@@ -1206,8 +1046,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
SystemClock.elapsedRealtime() + nextReconnectDelay,
mReconnectIntent);
- // double it for next time
- nextReconnectDelay *= 2;
+ mRetryMgr.increaseRetryCount();
if (!shouldPostNotification(lastFailCauseCode)) {
Log.d(LOG_TAG,"NOT Posting GPRS Unavailable notification "
@@ -1236,17 +1075,8 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
cleanUpConnection(true, Phone.REASON_APN_SWITCHED);
}
- protected void onTrySetupData(String reason) {
- trySetupData(reason);
- }
-
- protected void onRestoreDefaultApn() {
- if (DBG) Log.d(LOG_TAG, "Restore default APN");
- setEnabled(Phone.APN_TYPE_MMS, false);
- mRequestedApnType = Phone.APN_TYPE_DEFAULT;
- if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
- cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN);
- }
+ protected boolean onTrySetupData(String reason) {
+ return trySetupData(reason);
}
/**
@@ -1302,7 +1132,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
protected void onRadioOffOrNotAvailable() {
// Make sure our reconnect delay starts at the initial value
// next time the radio comes on
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
mReregisterOnReconnectFailure = false;
if (phone.getSimulatedRadioControl() != null) {
@@ -1324,22 +1154,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
if (ar.exception == null) {
// everything is setup
-
- /*
- * We may have switched away from the default PDP context
- * in order to enable a "special" APN (e.g., for MMS
- * traffic). Set a timer to switch back and/or disable the
- * special APN, so that a negligient application doesn't
- * permanently prevent data connectivity. What we are
- * protecting against here is not malicious apps, but
- * rather an app that inadvertantly fails to reset to the
- * default APN, or that dies before doing so.
- */
- if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]) {
- removeMessages(EVENT_RESTORE_DEFAULT_APN);
- sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN),
- getRestoreDefaultApnDelay());
- }
if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) {
SystemProperties.set("gsm.defaultpdpcontext.active", "true");
if (canSetPreferApn && preferredApn == null) {
@@ -1387,8 +1201,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
setState(State.SCANNING);
// Wait a bit before trying the next APN, so that
// we're not tying up the RIL command channel
- sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason),
- RECONNECT_DELAY_INITIAL_MILLIS);
+ sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
}
}
}
@@ -1433,7 +1246,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
} else {
// reset reconnect timer
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
mReregisterOnReconnectFailure = false;
// in case data setup was attempted when we were on a voice call
trySetupData(Phone.REASON_VOICE_CALL_ENDED);
@@ -1444,18 +1257,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
cleanUpConnection(tearDown, reason);
}
- private int getRestoreDefaultApnDelay() {
- String restoreApnDelayStr = SystemProperties.get(APN_RESTORE_DELAY_PROP_NAME);
-
- if (restoreApnDelayStr != null && restoreApnDelayStr.length() != 0) {
- try {
- return Integer.valueOf(restoreApnDelayStr);
- } catch (NumberFormatException e) {
- }
- }
- return RESTORE_DEFAULT_APN_DELAY;
- }
-
/**
* Based on the sim operator numeric, create a list for all possible pdps
* with all apns associated with that pdp
@@ -1580,10 +1381,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
private void startDelayedRetry(PdpConnection.FailCause cause, String reason) {
notifyNoData(cause);
- if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) {
- sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN));
- }
- else {
+ if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) {
reconnectAfterFail(cause, reason);
}
}
@@ -1638,7 +1436,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
}
public void handleMessage (Message msg) {
-
+ if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg);
switch (msg.what) {
case EVENT_RECORDS_LOADED:
onRecordsLoaded();
@@ -1648,10 +1446,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
onEnableNewApn();
break;
- case EVENT_RESTORE_DEFAULT_APN:
- onRestoreDefaultApn();
- break;
-
case EVENT_GPRS_DETACHED:
onGprsDetached();
break;
@@ -1710,7 +1504,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
} else {
if (state == State.FAILED) {
cleanUpConnection(false, Phone.REASON_PS_RESTRICT_ENABLED);
- nextReconnectDelay = RECONNECT_DELAY_INITIAL_MILLIS;
+ mRetryMgr.resetRetryCount();
mReregisterOnReconnectFailure = false;
}
trySetupData(Phone.REASON_PS_RESTRICT_ENABLED);
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 2770ddc..0ca3148 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -37,6 +37,7 @@ import com.android.internal.telephony.SmsMessageBase;
import java.util.ArrayList;
import java.util.HashMap;
+import static android.telephony.SmsMessage.MessageClass;
final class GsmSMSDispatcher extends SMSDispatcher {
private static final String TAG = "GSM";
@@ -111,6 +112,12 @@ final class GsmSMSDispatcher extends SMSDispatcher {
return Intents.RESULT_SMS_HANDLED;
}
+ if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) {
+ // It's a storable message and there's no storage available. Bail.
+ // (See TS 23.038 for a description of class 0 messages.)
+ return Intents.RESULT_SMS_OUT_OF_MEMORY;
+ }
+
SmsHeader smsHeader = sms.getUserDataHeader();
// See if message is partial or port addressed.
if ((smsHeader == null) || (smsHeader.concatRef == null)) {
@@ -137,6 +144,22 @@ final class GsmSMSDispatcher extends SMSDispatcher {
}
/** {@inheritDoc} */
+ protected void sendData(String destAddr, String scAddr, int destPort,
+ byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
+ scAddr, destAddr, destPort, data, (deliveryIntent != null));
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ }
+
+ /** {@inheritDoc} */
+ protected void sendText(String destAddr, String scAddr, String text,
+ PendingIntent sentIntent, PendingIntent deliveryIntent) {
+ SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
+ scAddr, destAddr, text, (deliveryIntent != null));
+ sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
+ }
+
+ /** {@inheritDoc} */
protected void sendMultipartText(String destinationAddress, String scAddress,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 60d4e8f..003899b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -16,13 +16,6 @@
package com.android.internal.telephony.gsm;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ALPHA;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISROAMING;
-import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_NUMERIC;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -56,10 +49,12 @@ import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.DataConnectionTracker;
import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.ServiceStateTracker;
import com.android.internal.telephony.TelephonyEventLog;
import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.Arrays;
import java.util.Calendar;
@@ -70,8 +65,9 @@ import java.util.TimeZone;
* {@hide}
*/
final class GsmServiceStateTracker extends ServiceStateTracker {
+ static final String LOG_TAG = "GSM";
+ static final boolean DBG = true;
- //***** Instance Variables
GSMPhone phone;
GsmCellLocation cellLoc;
GsmCellLocation newCellLoc;
@@ -82,13 +78,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
private int newGPRSState = ServiceState.STATE_OUT_OF_SERVICE;
/**
- * The access technology currently in use: DATA_ACCESS_
+ * Values correspond to ServiceStateTracker.DATA_ACCESS_ definitions.
*/
private int networkType = 0;
private int newNetworkType = 0;
- /* gsm roaming status solely based on TS 27.007 7.2 CREG */
+
+ /** GSM roaming status solely based on TS 27.007 7.2 CREG. */
private boolean mGsmRoaming = false;
- /* data roaming status solely based on TS 27.007 10.1.19 CGREG */
+
+ /** Data roaming status solely based on TS 27.007 10.1.19 CGREG. */
private boolean mDataRoaming = false;
private boolean newDataRoaming = false;
@@ -97,9 +95,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
private RegistrantList psRestrictEnabledRegistrants = new RegistrantList();
private RegistrantList psRestrictDisabledRegistrants = new RegistrantList();
- // Sometimes we get the NITZ time before we know what country we are in.
- // Keep the time zone information from the NITZ string so we can fix
- // the time zone once know the country.
+ /**
+ * Sometimes we get the NITZ time before we know what country we
+ * are in. Keep the time zone information from the NITZ string so
+ * we can fix the time zone once know the country.
+ */
private boolean mNeedFixZone = false;
private int mZoneOffset;
private boolean mZoneDst;
@@ -111,13 +111,16 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
long mSavedTime;
long mSavedAtTime;
- // We can't register for SIM_RECORDS_LOADED immediately because the
- // SIMRecords object may not be instantiated yet.
+ /**
+ * We can't register for SIM_RECORDS_LOADED immediately because the
+ * SIMRecords object may not be instantiated yet.
+ */
private boolean mNeedToRegForSimLoaded;
- // Started the recheck process after finding gprs should registerd but not
+ /** Started the recheck process after finding gprs should registerd but not. */
private boolean mStartedGprsRegCheck = false;
- // Already sent the event-log for no gprs register
+
+ /** Already sent the event-log for no gprs register. */
private boolean mReportedGprsNoReg = false;
/**
@@ -125,34 +128,31 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
*/
private Notification mNotification;
- // Wake lock used while setting time of day.
+ /** Wake lock used while setting time of day. */
private PowerManager.WakeLock mWakeLock;
private static final String WAKELOCK_TAG = "ServiceStateTracker";
- // Keep track of SPN display rules, so we only broadcast intent if something changes.
+ /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
private String curSpn = null;
private String curPlmn = null;
private int curSpnRule = 0;
- //***** Constants
-
- static final boolean DBG = true;
- static final String LOG_TAG = "GSM";
-
- // waiting period before recheck gprs and voice registration
+ /** waiting period before recheck gprs and voice registration. */
static final int DEFAULT_GPRS_CHECK_PERIOD_MILLIS = 60 * 1000;
- // notification type
- static final int PS_ENABLED = 1001; // Access Control blocks data service
- static final int PS_DISABLED = 1002; // Access Control enables data service
- static final int CS_ENABLED = 1003; // Access Control blocks all voice/sms service
- static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service
- static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service
- static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service
+ /** Notification type. */
+ static final int PS_ENABLED = 1001; // Access Control blocks data service
+ static final int PS_DISABLED = 1002; // Access Control enables data service
+ static final int CS_ENABLED = 1003; // Access Control blocks all voice/sms service
+ static final int CS_DISABLED = 1004; // Access Control enables all voice/sms service
+ static final int CS_NORMAL_ENABLED = 1005; // Access Control blocks normal voice/sms service
+ static final int CS_EMERGENCY_ENABLED = 1006; // Access Control blocks emergency call service
- // notification id
- static final int PS_NOTIFICATION = 888; //id to update and cancel PS restricted
- static final int CS_NOTIFICATION = 999; //id to update and cancel CS restricted
+ /** Notification id. */
+ static final int PS_NOTIFICATION = 888; // Id to update and cancel PS restricted
+ static final int CS_NOTIFICATION = 999; // Id to update and cancel CS restricted
+
+ static final int MAX_NUM_DATA_STATE_READS = 15;
private ContentObserver mAutoTimeObserver = new ContentObserver(new Handler()) {
@Override
@@ -162,9 +162,6 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
};
-
- //***** Constructors
-
public GsmServiceStateTracker(GSMPhone phone) {
super();
@@ -205,7 +202,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
public void dispose() {
- //Unregister for all events
+ // Unregister for all events.
cm.unregisterForAvailable(this);
cm.unregisterForRadioStateChanged(this);
cm.unregisterForNetworkStateChanged(this);
@@ -228,7 +225,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void registerForGprsAttached(Handler h, int what, Object obj) {
+ void registerForGprsAttached(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
gprsAttachedRegistrants.add(r);
@@ -237,11 +234,11 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- /*protected*/ void unregisterForGprsAttached(Handler h) {
+ void unregisterForGprsAttached(Handler h) {
gprsAttachedRegistrants.remove(h);
}
- /*protected*/ void registerForNetworkAttach(Handler h, int what, Object obj) {
+ void registerForNetworkAttach(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
networkAttachedRegistrants.add(r);
@@ -250,16 +247,17 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- /*protected*/ void unregisterForNetworkAttach(Handler h) {
+ void unregisterForNetworkAttach(Handler h) {
networkAttachedRegistrants.remove(h);
}
+
/**
* Registration point for transition into GPRS detached.
* @param h handler to notify
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void registerForGprsDetached(Handler h, int what, Object obj) {
+ void registerForGprsDetached(Handler h, int what, Object obj) {
Registrant r = new Registrant(h, what, obj);
gprsDetachedRegistrants.add(r);
@@ -268,7 +266,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- /*protected*/ void unregisterForGprsDetached(Handler h) {
+ void unregisterForGprsDetached(Handler h) {
gprsDetachedRegistrants.remove(h);
}
@@ -278,7 +276,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
+ void registerForPsRestrictedEnabled(Handler h, int what, Object obj) {
Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedEnabled ");
Registrant r = new Registrant(h, what, obj);
psRestrictEnabledRegistrants.add(r);
@@ -288,7 +286,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- /*protected*/ void unregisterForPsRestrictedEnabled(Handler h) {
+ void unregisterForPsRestrictedEnabled(Handler h) {
psRestrictEnabledRegistrants.remove(h);
}
@@ -298,7 +296,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* @param what what code of message when delivered
* @param obj placed in Message.obj
*/
- /*protected*/ void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
+ void registerForPsRestrictedDisabled(Handler h, int what, Object obj) {
Log.d(LOG_TAG, "[DSAC DEB] " + "registerForPsRestrictedDisabled ");
Registrant r = new Registrant(h, what, obj);
psRestrictDisabledRegistrants.add(r);
@@ -308,25 +306,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- /*protected*/ void unregisterForPsRestrictedDisabled(Handler h) {
+ void unregisterForPsRestrictedDisabled(Handler h) {
psRestrictDisabledRegistrants.remove(h);
}
- /*protected*/ boolean getDataRoaming() {
+ boolean getDataRoaming() {
return mDataRoaming;
}
- //***** Called from GSMPhone
- public void
- getLacAndCid(Message onComplete) {
- cm.getRegistrationState(obtainMessage(
- EVENT_GET_LOC_DONE, onComplete));
- }
-
-
- //***** Overridden from ServiceStateTracker
- public void
- handleMessage (Message msg) {
+ public void handleMessage (Message msg) {
AsyncResult ar;
int[] ints;
String[] strings;
@@ -398,19 +386,13 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
Log.w(LOG_TAG, "error parsing location: " + ex);
}
}
-
- // only update if lac or cid changed
- if (cellLoc.getCid() != cid || cellLoc.getLac() != lac) {
- cellLoc.setLacAndCid(lac, cid);
- phone.notifyLocationChanged();
- }
+ cellLoc.setLacAndCid(lac, cid);
+ phone.notifyLocationChanged();
}
- if (ar.userObj != null) {
- AsyncResult.forMessage(((Message) ar.userObj)).exception
- = ar.exception;
- ((Message) ar.userObj).sendToTarget();
- }
+ // Release any temporary cell lock, which could have been
+ // aquired to allow a single-shot location update.
+ disableSingleLocationUpdate();
break;
case EVENT_POLL_STATE_REGISTRATION:
@@ -458,7 +440,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
ar = (AsyncResult) msg.obj;
if (ar.exception == null) {
- getLacAndCid(null);
+ cm.getRegistrationState(obtainMessage(EVENT_GET_LOC_DONE, null));
}
break;
@@ -527,10 +509,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- //***** Private Instance Methods
-
- protected void setPowerStateToDesired()
- {
+ protected void setPowerStateToDesired() {
// If we want it on and it's off, turn it on
if (mDesiredPowerState
&& cm.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
@@ -591,9 +570,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
/**
* Handle the result of one of the pollState()-related requests
*/
-
- protected void
- handlePollStateResult (int what, AsyncResult ar) {
+ protected void handlePollStateResult (int what, AsyncResult ar) {
int ints[];
String states[];
@@ -675,6 +652,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
newGPRSState = regCodeToServiceState(regState);
newDataRoaming = regCodeIsRoaming(regState);
newNetworkType = type;
+ newSS.setRadioTechnology(type);
break;
case EVENT_POLL_STATE_OPERATOR:
@@ -723,9 +701,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* and start over again if the radio notifies us that some
* event has changed
*/
-
- private void
- pollState() {
+ private void pollState() {
pollingContext = new int[1];
pollingContext[0] = 0;
@@ -808,6 +784,15 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
case DATA_ACCESS_UMTS:
ret = "UMTS";
break;
+ case DATA_ACCESS_HSDPA:
+ ret = "HSDPA";
+ break;
+ case DATA_ACCESS_HSUPA:
+ ret = "HSUPA";
+ break;
+ case DATA_ACCESS_HSPA:
+ ret = "HSPA";
+ break;
default:
Log.e(LOG_TAG, "Wrong network type: " + Integer.toString(type));
break;
@@ -816,8 +801,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
return ret;
}
- private void
- pollStateDone() {
+ private void pollStateDone() {
if (DBG) {
Log.d(LOG_TAG, "Poll ServiceState done: " +
" oldSS=[" + ss + "] newSS=[" + newSS +
@@ -870,7 +854,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
newSS.setStateOutOfService(); // clean slate for next time
if (hasNetworkTypeChanged) {
- phone.setSystemProperty(PROPERTY_DATA_NETWORK_TYPE,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
networkTypeToString(networkType));
}
@@ -883,14 +867,14 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
if (hasChanged) {
String operatorNumeric;
- phone.setSystemProperty(PROPERTY_OPERATOR_ALPHA,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ALPHA,
ss.getOperatorAlphaLong());
operatorNumeric = ss.getOperatorNumeric();
- phone.setSystemProperty(PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, operatorNumeric);
if (operatorNumeric == null) {
- phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, "");
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
} else {
String iso = "";
try{
@@ -902,7 +886,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
Log.w(LOG_TAG, "countryCodeForMcc error" + ex);
}
- phone.setSystemProperty(PROPERTY_OPERATOR_ISO_COUNTRY, iso);
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, iso);
mGotCountryCode = true;
if (mNeedFixZone) {
@@ -945,7 +929,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
}
- phone.setSystemProperty(PROPERTY_OPERATOR_ISROAMING,
+ phone.setSystemProperty(TelephonyProperties.PROPERTY_OPERATOR_ISROAMING,
ss.getRoaming() ? "true" : "false");
updateSpnDisplay();
@@ -1040,8 +1024,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
return guess;
}
- private void
- queueNextSignalStrengthPoll() {
+ private void queueNextSignalStrengthPoll() {
if (dontPollSignalStrength || (cm.getRadioState().isCdma())) {
// The radio is telling us about signal strength changes
// we don't have to ask it
@@ -1063,8 +1046,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* send signal-strength-changed notification if changed
* Called both for solicited and unsolicited signal stength updates
*/
- private void
- onSignalStrengthResult(AsyncResult ar) {
+ private void onSignalStrengthResult(AsyncResult ar) {
SignalStrength oldSignalStrength = mSignalStrength;
int rssi = 99;
@@ -1105,8 +1087,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
*
* @param ar an int value of RIL_RESTRICTED_STATE_*
*/
- private void onRestrictedStateChanged(AsyncResult ar)
- {
+ private void onRestrictedStateChanged(AsyncResult ar) {
Log.d(LOG_TAG, "[DSAC DEB] " + "onRestrictedStateChanged");
RestrictedState newRs = new RestrictedState();
@@ -1195,8 +1176,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
}
/** code is registration state 0-5 from TS 27.007 7.2 */
- private int
- regCodeToServiceState(int code) {
+ private int regCodeToServiceState(int code) {
switch (code) {
case 0:
case 2: // 2 is "searching"
@@ -1222,8 +1202,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* code is registration state 0-5 from TS 27.007 7.2
* returns true if registered roam, false otherwise
*/
- private boolean
- regCodeIsRoaming (int code) {
+ private boolean regCodeIsRoaming (int code) {
// 5 is "in service -- roam"
return 5 == code;
}
@@ -1235,9 +1214,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* @param s ServiceState hold current ons
* @return true for roaming state set
*/
- private
- boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) {
- String spn = SystemProperties.get(PROPERTY_ICC_OPERATOR_ALPHA, "empty");
+ private boolean isRoamingBetweenOperators(boolean gsmRoaming, ServiceState s) {
+ String spn = SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA, "empty");
String onsl = s.getOperatorAlphaLong();
String onss = s.getOperatorAlphaShort();
@@ -1245,7 +1223,8 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
boolean equalsOnsl = onsl != null && spn.equals(onsl);
boolean equalsOnss = onss != null && spn.equals(onss);
- String simNumeric = SystemProperties.get(PROPERTY_ICC_OPERATOR_NUMERIC, "");
+ String simNumeric = SystemProperties.get(
+ TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC, "");
String operatorNumeric = s.getOperatorNumeric();
boolean equalsMcc = true;
@@ -1258,8 +1237,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
return gsmRoaming && !(equalsMcc && (equalsOnsl || equalsOnss));
}
- private static
- int twoDigitsAt(String s, int offset) {
+ private static int twoDigitsAt(String s, int offset) {
int a, b;
a = Character.digit(s.charAt(offset), 10);
@@ -1277,7 +1255,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* @return The current GPRS state. IN_SERVICE is the same as "attached"
* and OUT_OF_SERVICE is the same as detached.
*/
- /*package*/ int getCurrentGprsState() {
+ int getCurrentGprsState() {
return gprsState;
}
@@ -1286,7 +1264,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
* that could support voice and data simultaniously.
*/
boolean isConcurrentVoiceAndData() {
- return (networkType == DATA_ACCESS_UMTS);
+ return (networkType >= DATA_ACCESS_UMTS);
}
/**
@@ -1325,10 +1303,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
/**
* nitzReceiveTime is time_t that the NITZ time was posted
*/
-
- private
- void setTimeFromNITZString (String nitz, long nitzReceiveTime)
- {
+ private void setTimeFromNITZString (String nitz, long nitzReceiveTime) {
// "yy/mm/dd,hh:mm:ss(+/-)tz"
// tz is in number of quarter-hours
@@ -1392,7 +1367,7 @@ final class GsmServiceStateTracker extends ServiceStateTracker {
zone = TimeZone.getTimeZone( tzname );
}
- String iso = SystemProperties.get(PROPERTY_OPERATOR_ISO_COUNTRY);
+ String iso = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY);
if (zone == null) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/MccTable.java b/telephony/java/com/android/internal/telephony/gsm/MccTable.java
deleted file mode 100644
index e18da56..0000000
--- a/telephony/java/com/android/internal/telephony/gsm/MccTable.java
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * Copyright (C) 2006 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.internal.telephony.gsm;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Mobile Country Code
- *
- * {@hide}
- */
-public final class MccTable
-{
- static ArrayList<MccEntry> table;
-
- static class MccEntry implements Comparable<MccEntry>
- {
- int mcc;
- String iso;
- int smallestDigitsMnc;
- String timezone;
- String language;
-
- MccEntry(int mnc, String iso, int smallestDigitsMCC) {
- this(mnc, iso, smallestDigitsMCC, null);
- }
-
- MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone) {
- this(mnc, iso, smallestDigitsMCC, timezone, null);
- }
-
- MccEntry(int mnc, String iso, int smallestDigitsMCC, String timezone, String language) {
- this.mcc = mnc;
- this.iso = iso;
- this.smallestDigitsMnc = smallestDigitsMCC;
- this.timezone = timezone;
- this.language = language;
- }
-
- public int compareTo(MccEntry o)
- {
- return mcc - o.mcc;
- }
- }
-
- private static MccEntry
- entryForMcc(int mcc)
- {
- int index;
-
- MccEntry m;
-
- m = new MccEntry(mcc, null, 0);
-
- index = Collections.binarySearch(table, m);
-
- if (index < 0) {
- return null;
- } else {
- return table.get(index);
- }
- }
-
- /**
- * Returns a default time zone ID for the given MCC.
- * @param mcc Mobile Country Code
- * @return default TimeZone ID, or null if not specified
- */
- /* package */ static String defaultTimeZoneForMcc(int mcc) {
- MccEntry entry;
-
- entry = entryForMcc(mcc);
-
- if (entry == null) {
- return null;
- } else {
- return entry.timezone;
- }
- }
-
- /**
- * Given a GSM Mobile Country Code, returns
- * an ISO two-character country code if available.
- * Returns "" if unavailable.
- */
- public static String
- countryCodeForMcc(int mcc)
- {
- MccEntry entry;
-
- entry = entryForMcc(mcc);
-
- if (entry == null) {
- return "";
- } else {
- return entry.iso;
- }
- }
-
- /**
- * Given a GSM Mobile Country Code, returns
- * an ISO 2-3 character language code if available.
- * Returns null if unavailable.
- */
- /* package */ static String defaultLanguageForMcc(int mcc) {
- MccEntry entry;
-
- entry = entryForMcc(mcc);
-
- if (entry == null) {
- return null;
- } else {
- return entry.language;
- }
- }
-
- /**
- * Given a GSM Mobile Country Code, returns
- * the smallest number of digits that M if available.
- * Returns "" if unavailable.
- */
- public static int
- smallestDigitsMccForMnc(int mcc)
- {
- MccEntry entry;
-
- entry = entryForMcc(mcc);
-
- if (entry == null) {
- return 2;
- } else {
- return entry.smallestDigitsMnc;
- }
- }
-
- static {
- table = new ArrayList<MccEntry>(240);
-
-
- /*
- * The table below is built from two resources:
- *
- * 1) ITU "Mobile Network Code (MNC) for the international
- * identification plan for mobile terminals and mobile users"
- * which is available as an annex to the ITU operational bulletin
- * available here: http://www.itu.int/itu-t/bulletin/annex.html
- *
- * 2) The ISO 3166 country codes list, available here:
- * http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/index.html
- *
- * This table has not been verified.
- *
- * FIXME(mkf) this should be stored in a more efficient representation
- */
-
- table.add(new MccEntry(202,"gr",2)); //Greece
- table.add(new MccEntry(204,"nl",2,"Europe/Amsterdam","nl")); //Netherlands (Kingdom of the)
- table.add(new MccEntry(206,"be",2)); //Belgium
- table.add(new MccEntry(208,"fr",2,"Europe/Paris","fr")); //France
- table.add(new MccEntry(212,"mc",2)); //Monaco (Principality of)
- table.add(new MccEntry(213,"ad",2)); //Andorra (Principality of)
- table.add(new MccEntry(214,"es",2,"Europe/Madrid","es")); //Spain
- table.add(new MccEntry(216,"hu",2)); //Hungary (Republic of)
- table.add(new MccEntry(218,"ba",2)); //Bosnia and Herzegovina
- table.add(new MccEntry(219,"hr",2)); //Croatia (Republic of)
- table.add(new MccEntry(220,"rs",2)); //Serbia and Montenegro
- table.add(new MccEntry(222,"it",2,"Europe/Rome","it")); //Italy
- table.add(new MccEntry(225,"va",2,"Europe/Rome","it")); //Vatican City State
- table.add(new MccEntry(226,"ro",2)); //Romania
- table.add(new MccEntry(228,"ch",2,"Europe/Zurich","de")); //Switzerland (Confederation of)
- table.add(new MccEntry(230,"cz",2,"Europe/Prague","cs")); //Czech Republic
- table.add(new MccEntry(231,"sk",2)); //Slovak Republic
- table.add(new MccEntry(232,"at",2,"Europe/Vienna","de")); //Austria
- table.add(new MccEntry(234,"gb",2,"Europe/London","en")); //United Kingdom of Great Britain and Northern Ireland
- table.add(new MccEntry(235,"gb",2,"Europe/London","en")); //United Kingdom of Great Britain and Northern Ireland
- table.add(new MccEntry(238,"dk",2)); //Denmark
- table.add(new MccEntry(240,"se",2)); //Sweden
- table.add(new MccEntry(242,"no",2)); //Norway
- table.add(new MccEntry(244,"fi",2)); //Finland
- table.add(new MccEntry(246,"lt",2)); //Lithuania (Republic of)
- table.add(new MccEntry(247,"lv",2)); //Latvia (Republic of)
- table.add(new MccEntry(248,"ee",2)); //Estonia (Republic of)
- table.add(new MccEntry(250,"ru",2)); //Russian Federation
- table.add(new MccEntry(255,"ua",2)); //Ukraine
- table.add(new MccEntry(257,"by",2)); //Belarus (Republic of)
- table.add(new MccEntry(259,"md",2)); //Moldova (Republic of)
- table.add(new MccEntry(260,"pl",2,"Europe/Warsaw")); //Poland (Republic of)
- table.add(new MccEntry(262,"de",2,"Europe/Berlin","de")); //Germany (Federal Republic of)
- table.add(new MccEntry(266,"gi",2)); //Gibraltar
- table.add(new MccEntry(268,"pt",2)); //Portugal
- table.add(new MccEntry(270,"lu",2)); //Luxembourg
- table.add(new MccEntry(272,"ie",2,"Europe/Dublin","en")); //Ireland
- table.add(new MccEntry(274,"is",2)); //Iceland
- table.add(new MccEntry(276,"al",2)); //Albania (Republic of)
- table.add(new MccEntry(278,"mt",2)); //Malta
- table.add(new MccEntry(280,"cy",2)); //Cyprus (Republic of)
- table.add(new MccEntry(282,"ge",2)); //Georgia
- table.add(new MccEntry(283,"am",2)); //Armenia (Republic of)
- table.add(new MccEntry(284,"bg",2)); //Bulgaria (Republic of)
- table.add(new MccEntry(286,"tr",2)); //Turkey
- table.add(new MccEntry(288,"fo",2)); //Faroe Islands
- table.add(new MccEntry(290,"gl",2)); //Greenland (Denmark)
- table.add(new MccEntry(292,"sm",2)); //San Marino (Republic of)
- table.add(new MccEntry(293,"sl",2)); //Slovenia (Republic of)
- table.add(new MccEntry(294,"mk",2)); //The Former Yugoslav Republic of Macedonia
- table.add(new MccEntry(295,"li",2)); //Liechtenstein (Principality of)
- table.add(new MccEntry(302,"ca",2)); //Canada
- table.add(new MccEntry(308,"pm",2)); //Saint Pierre and Miquelon (Collectivit territoriale de la Rpublique franaise)
- table.add(new MccEntry(310,"us",3,"","en")); //United States of America
- table.add(new MccEntry(311,"us",3,"","en")); //United States of America
- table.add(new MccEntry(312,"us",3,"","en")); //United States of America
- table.add(new MccEntry(313,"us",3,"","en")); //United States of America
- table.add(new MccEntry(314,"us",3,"","en")); //United States of America
- table.add(new MccEntry(315,"us",3,"","en")); //United States of America
- table.add(new MccEntry(316,"us",3,"","en")); //United States of America
- table.add(new MccEntry(330,"pr",2)); //Puerto Rico
- table.add(new MccEntry(332,"vi",2)); //United States Virgin Islands
- table.add(new MccEntry(334,"mx",3)); //Mexico
- table.add(new MccEntry(338,"jm",3)); //Jamaica
- table.add(new MccEntry(340,"gp",2)); //Guadeloupe (French Department of)
- table.add(new MccEntry(342,"bb",3)); //Barbados
- table.add(new MccEntry(344,"ag",3)); //Antigua and Barbuda
- table.add(new MccEntry(346,"ky",3)); //Cayman Islands
- table.add(new MccEntry(348,"vg",3)); //British Virgin Islands
- table.add(new MccEntry(350,"bm",2)); //Bermuda
- table.add(new MccEntry(352,"gd",2)); //Grenada
- table.add(new MccEntry(354,"ms",2)); //Montserrat
- table.add(new MccEntry(356,"kn",2)); //Saint Kitts and Nevis
- table.add(new MccEntry(358,"lc",2)); //Saint Lucia
- table.add(new MccEntry(360,"vc",2)); //Saint Vincent and the Grenadines
- table.add(new MccEntry(362,"nl",2)); //Netherlands Antilles
- table.add(new MccEntry(363,"aw",2)); //Aruba
- table.add(new MccEntry(364,"bs",2)); //Bahamas (Commonwealth of the)
- table.add(new MccEntry(365,"ai",3)); //Anguilla
- table.add(new MccEntry(366,"dm",2)); //Dominica (Commonwealth of)
- table.add(new MccEntry(368,"cu",2)); //Cuba
- table.add(new MccEntry(370,"do",2)); //Dominican Republic
- table.add(new MccEntry(372,"ht",2)); //Haiti (Republic of)
- table.add(new MccEntry(374,"tt",2)); //Trinidad and Tobago
- table.add(new MccEntry(376,"tc",2)); //Turks and Caicos Islands
- table.add(new MccEntry(400,"az",2)); //Azerbaijani Republic
- table.add(new MccEntry(401,"kz",2)); //Kazakhstan (Republic of)
- table.add(new MccEntry(402,"bt",2)); //Bhutan (Kingdom of)
- table.add(new MccEntry(404,"in",2)); //India (Republic of)
- table.add(new MccEntry(405,"in",2)); //India (Republic of)
- table.add(new MccEntry(410,"pk",2)); //Pakistan (Islamic Republic of)
- table.add(new MccEntry(412,"af",2)); //Afghanistan
- table.add(new MccEntry(413,"lk",2)); //Sri Lanka (Democratic Socialist Republic of)
- table.add(new MccEntry(414,"mm",2)); //Myanmar (Union of)
- table.add(new MccEntry(415,"lb",2)); //Lebanon
- table.add(new MccEntry(416,"jo",2)); //Jordan (Hashemite Kingdom of)
- table.add(new MccEntry(417,"sy",2)); //Syrian Arab Republic
- table.add(new MccEntry(418,"iq",2)); //Iraq (Republic of)
- table.add(new MccEntry(419,"kw",2)); //Kuwait (State of)
- table.add(new MccEntry(420,"sa",2)); //Saudi Arabia (Kingdom of)
- table.add(new MccEntry(421,"ye",2)); //Yemen (Republic of)
- table.add(new MccEntry(422,"om",2)); //Oman (Sultanate of)
- table.add(new MccEntry(424,"ae",2)); //United Arab Emirates
- table.add(new MccEntry(425,"il",2)); //Israel (State of)
- table.add(new MccEntry(426,"bh",2)); //Bahrain (Kingdom of)
- table.add(new MccEntry(427,"qa",2)); //Qatar (State of)
- table.add(new MccEntry(428,"mn",2)); //Mongolia
- table.add(new MccEntry(429,"np",2)); //Nepal
- table.add(new MccEntry(430,"ae",2)); //United Arab Emirates
- table.add(new MccEntry(431,"ae",2)); //United Arab Emirates
- table.add(new MccEntry(432,"ir",2)); //Iran (Islamic Republic of)
- table.add(new MccEntry(434,"uz",2)); //Uzbekistan (Republic of)
- table.add(new MccEntry(436,"tj",2)); //Tajikistan (Republic of)
- table.add(new MccEntry(437,"kg",2)); //Kyrgyz Republic
- table.add(new MccEntry(438,"tm",2)); //Turkmenistan
- table.add(new MccEntry(440,"jp",2,"Asia/Tokyo","ja")); //Japan
- table.add(new MccEntry(441,"jp",2,"Asia/Tokyo","ja")); //Japan
- table.add(new MccEntry(450,"kr",2)); //Korea (Republic of)
- table.add(new MccEntry(452,"vn",2)); //Viet Nam (Socialist Republic of)
- table.add(new MccEntry(454,"hk",2)); //"Hong Kong, China"
- table.add(new MccEntry(455,"mo",2)); //"Macao, China"
- table.add(new MccEntry(456,"kh",2)); //Cambodia (Kingdom of)
- table.add(new MccEntry(457,"la",2)); //Lao People's Democratic Republic
- table.add(new MccEntry(460,"cn",2)); //China (People's Republic of)
- table.add(new MccEntry(461,"cn",2)); //China (People's Republic of)
- table.add(new MccEntry(466,"tw",2)); //"Taiwan, China"
- table.add(new MccEntry(467,"kp",2)); //Democratic People's Republic of Korea
- table.add(new MccEntry(470,"bd",2)); //Bangladesh (People's Republic of)
- table.add(new MccEntry(472,"mv",2)); //Maldives (Republic of)
- table.add(new MccEntry(502,"my",2)); //Malaysia
- table.add(new MccEntry(505,"au",2,"Australia/Sydney","en")); //Australia
- table.add(new MccEntry(510,"id",2)); //Indonesia (Republic of)
- table.add(new MccEntry(514,"tl",2)); //Democratic Republic of Timor-Leste
- table.add(new MccEntry(515,"ph",2)); //Philippines (Republic of the)
- table.add(new MccEntry(520,"th",2)); //Thailand
- table.add(new MccEntry(525,"sg",2,"Singapore","en")); //Singapore (Republic of)
- table.add(new MccEntry(528,"bn",2)); //Brunei Darussalam
- table.add(new MccEntry(530,"nz",2,"Pacific/Auckland", "en")); //New Zealand
- table.add(new MccEntry(534,"mp",2)); //Northern Mariana Islands (Commonwealth of the)
- table.add(new MccEntry(535,"gu",2)); //Guam
- table.add(new MccEntry(536,"nr",2)); //Nauru (Republic of)
- table.add(new MccEntry(537,"pg",2)); //Papua New Guinea
- table.add(new MccEntry(539,"to",2)); //Tonga (Kingdom of)
- table.add(new MccEntry(540,"sb",2)); //Solomon Islands
- table.add(new MccEntry(541,"vu",2)); //Vanuatu (Republic of)
- table.add(new MccEntry(542,"fj",2)); //Fiji (Republic of)
- table.add(new MccEntry(543,"wf",2)); //Wallis and Futuna (Territoire franais d'outre-mer)
- table.add(new MccEntry(544,"as",2)); //American Samoa
- table.add(new MccEntry(545,"ki",2)); //Kiribati (Republic of)
- table.add(new MccEntry(546,"nc",2)); //New Caledonia (Territoire franais d'outre-mer)
- table.add(new MccEntry(547,"pf",2)); //French Polynesia (Territoire franais d'outre-mer)
- table.add(new MccEntry(548,"ck",2)); //Cook Islands
- table.add(new MccEntry(549,"ws",2)); //Samoa (Independent State of)
- table.add(new MccEntry(550,"fm",2)); //Micronesia (Federated States of)
- table.add(new MccEntry(551,"mh",2)); //Marshall Islands (Republic of the)
- table.add(new MccEntry(552,"pw",2)); //Palau (Republic of)
- table.add(new MccEntry(602,"eg",2)); //Egypt (Arab Republic of)
- table.add(new MccEntry(603,"dz",2)); //Algeria (People's Democratic Republic of)
- table.add(new MccEntry(604,"ma",2)); //Morocco (Kingdom of)
- table.add(new MccEntry(605,"tn",2)); //Tunisia
- table.add(new MccEntry(606,"ly",2)); //Libya (Socialist People's Libyan Arab Jamahiriya)
- table.add(new MccEntry(607,"gm",2)); //Gambia (Republic of the)
- table.add(new MccEntry(608,"sn",2)); //Senegal (Republic of)
- table.add(new MccEntry(609,"mr",2)); //Mauritania (Islamic Republic of)
- table.add(new MccEntry(610,"ml",2)); //Mali (Republic of)
- table.add(new MccEntry(611,"gn",2)); //Guinea (Republic of)
- table.add(new MccEntry(612,"ci",2)); //Cte d'Ivoire (Republic of)
- table.add(new MccEntry(613,"bf",2)); //Burkina Faso
- table.add(new MccEntry(614,"ne",2)); //Niger (Republic of the)
- table.add(new MccEntry(615,"tg",2)); //Togolese Republic
- table.add(new MccEntry(616,"bj",2)); //Benin (Republic of)
- table.add(new MccEntry(617,"mu",2)); //Mauritius (Republic of)
- table.add(new MccEntry(618,"lr",2)); //Liberia (Republic of)
- table.add(new MccEntry(619,"sl",2)); //Sierra Leone
- table.add(new MccEntry(620,"gh",2)); //Ghana
- table.add(new MccEntry(621,"ng",2)); //Nigeria (Federal Republic of)
- table.add(new MccEntry(622,"td",2)); //Chad (Republic of)
- table.add(new MccEntry(623,"cf",2)); //Central African Republic
- table.add(new MccEntry(624,"cm",2)); //Cameroon (Republic of)
- table.add(new MccEntry(625,"cv",2)); //Cape Verde (Republic of)
- table.add(new MccEntry(626,"st",2)); //Sao Tome and Principe (Democratic Republic of)
- table.add(new MccEntry(627,"gq",2)); //Equatorial Guinea (Republic of)
- table.add(new MccEntry(628,"ga",2)); //Gabonese Republic
- table.add(new MccEntry(629,"cg",2)); //Congo (Republic of the)
- table.add(new MccEntry(630,"cg",2)); //Democratic Republic of the Congo
- table.add(new MccEntry(631,"ao",2)); //Angola (Republic of)
- table.add(new MccEntry(632,"gw",2)); //Guinea-Bissau (Republic of)
- table.add(new MccEntry(633,"sc",2)); //Seychelles (Republic of)
- table.add(new MccEntry(634,"sd",2)); //Sudan (Republic of the)
- table.add(new MccEntry(635,"rw",2)); //Rwanda (Republic of)
- table.add(new MccEntry(636,"et",2)); //Ethiopia (Federal Democratic Republic of)
- table.add(new MccEntry(637,"so",2)); //Somali Democratic Republic
- table.add(new MccEntry(638,"dj",2)); //Djibouti (Republic of)
- table.add(new MccEntry(639,"ke",2)); //Kenya (Republic of)
- table.add(new MccEntry(640,"tz",2)); //Tanzania (United Republic of)
- table.add(new MccEntry(641,"ug",2)); //Uganda (Republic of)
- table.add(new MccEntry(642,"bi",2)); //Burundi (Republic of)
- table.add(new MccEntry(643,"mz",2)); //Mozambique (Republic of)
- table.add(new MccEntry(645,"zm",2)); //Zambia (Republic of)
- table.add(new MccEntry(646,"mg",2)); //Madagascar (Republic of)
- table.add(new MccEntry(647,"re",2)); //Reunion (French Department of)
- table.add(new MccEntry(648,"zw",2)); //Zimbabwe (Republic of)
- table.add(new MccEntry(649,"na",2)); //Namibia (Republic of)
- table.add(new MccEntry(650,"mw",2)); //Malawi
- table.add(new MccEntry(651,"ls",2)); //Lesotho (Kingdom of)
- table.add(new MccEntry(652,"bw",2)); //Botswana (Republic of)
- table.add(new MccEntry(653,"sz",2)); //Swaziland (Kingdom of)
- table.add(new MccEntry(654,"km",2)); //Comoros (Union of the)
- table.add(new MccEntry(655,"za",2,"Africa/Johannesburg","en")); //South Africa (Republic of)
- table.add(new MccEntry(657,"er",2)); //Eritrea
- table.add(new MccEntry(702,"bz",2)); //Belize
- table.add(new MccEntry(704,"gt",2)); //Guatemala (Republic of)
- table.add(new MccEntry(706,"sv",2)); //El Salvador (Republic of)
- table.add(new MccEntry(708,"hn",3)); //Honduras (Republic of)
- table.add(new MccEntry(710,"ni",2)); //Nicaragua
- table.add(new MccEntry(712,"cr",2)); //Costa Rica
- table.add(new MccEntry(714,"pa",2)); //Panama (Republic of)
- table.add(new MccEntry(716,"pe",2)); //Peru
- table.add(new MccEntry(722,"ar",3)); //Argentine Republic
- table.add(new MccEntry(724,"br",2)); //Brazil (Federative Republic of)
- table.add(new MccEntry(730,"cl",2)); //Chile
- table.add(new MccEntry(732,"co",3)); //Colombia (Republic of)
- table.add(new MccEntry(734,"ve",2)); //Venezuela (Bolivarian Republic of)
- table.add(new MccEntry(736,"bo",2)); //Bolivia (Republic of)
- table.add(new MccEntry(738,"gy",2)); //Guyana
- table.add(new MccEntry(740,"ec",2)); //Ecuador
- table.add(new MccEntry(742,"gf",2)); //French Guiana (French Department of)
- table.add(new MccEntry(744,"py",2)); //Paraguay (Republic of)
- table.add(new MccEntry(746,"sr",2)); //Suriname (Republic of)
- table.add(new MccEntry(748,"uy",2)); //Uruguay (Eastern Republic of)
- table.add(new MccEntry(750,"fk",2)); //Falkland Islands (Malvinas)
- //table.add(new MccEntry(901,"",2)); //"International Mobile, shared code"
-
- Collections.sort(table);
- }
-}
diff --git a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java
index 89de867..224419e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java
+++ b/telephony/java/com/android/internal/telephony/gsm/PdpConnection.java
@@ -84,9 +84,13 @@ public class PdpConnection extends DataConnection {
lastFailCause = FailCause.NONE;
receivedDisconnectReq = false;
- phone.mCM.setupDataCall(Integer.toString(RILConstants.GSM_PHONE),
+ int authType = (apn.user != null) ? RILConstants.SETUP_DATA_AUTH_PAP_CHAP :
+ RILConstants.SETUP_DATA_AUTH_NONE;
+
+ phone.mCM.setupDataCall(Integer.toString(RILConstants.SETUP_DATA_TECH_GSM),
Integer.toString(RILConstants.DATA_PROFILE_DEFAULT), apn.apn, apn.user,
- apn.password, obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE));
+ apn.password, Integer.toString(authType),
+ obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE));
}
private void tearDownData(Message msg) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
index a08cdde..206e62f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java
@@ -19,15 +19,18 @@ package com.android.internal.telephony.gsm;
import android.os.Message;
import android.util.Log;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.IccCardApplication;
import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.IccFileHandler;
+import com.android.internal.telephony.Phone;
/**
* {@hide}
*/
public final class SIMFileHandler extends IccFileHandler implements IccConstants {
static final String LOG_TAG = "GSM";
-
+ private Phone mPhone;
//***** Instance Variables
@@ -35,6 +38,7 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
SIMFileHandler(GSMPhone phone) {
super(phone);
+ mPhone = phone;
}
public void dispose() {
@@ -53,7 +57,6 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
}
protected String getEFPath(int efid) {
- // TODO(): Make changes when USIM is supported
// TODO(): DF_GSM can be 7F20 or 7F21 to handle backward compatibility.
// Implement this after discussion with OEMs.
switch(efid) {
@@ -79,8 +82,23 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
case EF_SPN_SHORT_CPHS:
case EF_INFO_CPHS:
return MF_SIM + DF_GSM;
+
+ case EF_PBR:
+ // we only support global phonebook.
+ return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
+ }
+ String path = getCommonIccEFPath(efid);
+ if (path == null) {
+ // The EFids in USIM phone book entries are decided by the card manufacturer.
+ // So if we don't match any of the cases above and if its a USIM return
+ // the phone book path.
+ IccCard card = phone.getIccCard();
+ if (card != null && card.isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) {
+ return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
+ }
+ Log.e(LOG_TAG, "Error: EF Path being returned in null");
}
- return getCommonIccEFPath(efid);
+ return path;
}
protected void logd(String msg) {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index e25de81..d711a80 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -16,39 +16,27 @@
package com.android.internal.telephony.gsm;
-import android.app.ActivityManagerNative;
-import android.app.AlarmManager;
-import android.app.IActivityManager;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
import android.content.Context;
-import android.content.res.Configuration;
import android.os.AsyncResult;
-import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
-import android.os.Registrant;
import android.util.Log;
-import java.util.ArrayList;
-
-
-import static com.android.internal.telephony.TelephonyProperties.*;
import com.android.internal.telephony.AdnRecord;
import com.android.internal.telephony.AdnRecordCache;
import com.android.internal.telephony.AdnRecordLoader;
import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.gsm.SimCard;
-import com.android.internal.telephony.gsm.SmsMessage;
import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccRecords;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.IccVmFixedException;
import com.android.internal.telephony.IccVmNotSupportedException;
-import com.android.internal.telephony.PhoneProxy;
-
-
-
-
+import com.android.internal.telephony.MccTable;
+import java.util.ArrayList;
/**
@@ -61,14 +49,14 @@ public final class SIMRecords extends IccRecords {
private static final boolean DBG = true;
- //***** Instance Variables
+ // ***** Instance Variables
VoiceMailConstants mVmConfig;
SpnOverride mSpnOverride;
- //***** Cached SIM State; cleared on channel close
+ // ***** Cached SIM State; cleared on channel close
String imsi;
boolean callForwardingEnabled;
@@ -98,7 +86,7 @@ public final class SIMRecords extends IccRecords {
String pnnHomeName = null;
- //***** Constants
+ // ***** Constants
// Bitmasks for SPN display rules.
static final int SPN_RULE_SHOW_SPN = 0x01;
@@ -123,7 +111,7 @@ public final class SIMRecords extends IccRecords {
private static final int CPHS_SST_MBN_MASK = 0x30;
private static final int CPHS_SST_MBN_ENABLED = 0x30;
- //***** Event Constants
+ // ***** Event Constants
private static final int EVENT_SIM_READY = 1;
private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
@@ -153,9 +141,7 @@ public final class SIMRecords extends IccRecords {
private static final int EVENT_SIM_REFRESH = 31;
private static final int EVENT_GET_CFIS_DONE = 32;
- private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
-
- //***** Constructor
+ // ***** Constructor
SIMRecords(GSMPhone p) {
super(p);
@@ -198,7 +184,7 @@ public final class SIMRecords extends IccRecords {
msisdn = null;
voiceMailNum = null;
countVoiceMessages = 0;
- mncLength = 0;
+ mncLength = UNINITIALIZED;
iccid = null;
// -1 means no EF_SPN found; treat accordingly.
spnDisplayCondition = -1;
@@ -463,56 +449,16 @@ public final class SIMRecords extends IccRecords {
* provided the SIM card. Returns null of SIM is not yet ready
*/
String getSIMOperatorNumeric() {
- if (imsi == null) {
+ if (imsi == null || mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
return null;
}
- if (mncLength != 0) {
- // Length = length of MCC + length of MNC
- // length of mcc = 3 (TS 23.003 Section 2.2)
- return imsi.substring(0, 3 + mncLength);
- }
-
- // Guess the MNC length based on the MCC if we don't
- // have a valid value in ef[ad]
-
- int mcc;
-
- mcc = Integer.parseInt(imsi.substring(0,3));
-
- return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
+ // Length = length of MCC + length of MNC
+ // length of mcc = 3 (TS 23.003 Section 2.2)
+ return imsi.substring(0, 3 + mncLength);
}
- /**
- * If the timezone is not already set, set it based on the MCC of the SIM.
- * @param mcc Mobile Country Code of the SIM
- */
- private void setTimezoneFromMccIfNeeded(int mcc) {
- String timezone = SystemProperties.get(TIMEZONE_PROPERTY);
- if (timezone == null || timezone.length() == 0) {
- String zoneId = MccTable.defaultTimeZoneForMcc(mcc);
-
- if (zoneId != null && zoneId.length() > 0) {
- // Set time zone based on MCC
- AlarmManager alarm =
- (AlarmManager) phone.getContext().getSystemService(Context.ALARM_SERVICE);
- alarm.setTimeZone(zoneId);
- }
- }
- }
-
- /**
- * If the locale is not already set, set it based on the MCC of the SIM.
- * @param mcc Mobile Country Code of the SIM
- */
- private void setLocaleFromMccIfNeeded(int mcc) {
- String language = MccTable.defaultLanguageForMcc(mcc);
- String country = MccTable.countryCodeForMcc(mcc);
-
- phone.setSystemLocale(language, country);
- }
-
- //***** Overridden from Handler
+ // ***** Overridden from Handler
public void handleMessage(Message msg) {
AsyncResult ar;
AdnRecord adn;
@@ -551,13 +497,25 @@ public final class SIMRecords extends IccRecords {
}
Log.d(LOG_TAG, "IMSI: " + imsi.substring(0, 6) + "xxxxxxxxx");
- ((GSMPhone) phone).mSimCard.updateImsiConfiguration(imsi);
- ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent(
- SimCard.INTENT_VALUE_ICC_IMSI, null);
- int mcc = Integer.parseInt(imsi.substring(0, 3));
- setTimezoneFromMccIfNeeded(mcc);
- setLocaleFromMccIfNeeded(mcc);
+ if (mncLength == UNKNOWN) {
+ // the SIM has told us all it knows, but it didn't know the mnc length.
+ // guess using the mcc
+ try {
+ int mcc = Integer.parseInt(imsi.substring(0,3));
+ mncLength = MccTable.smallestDigitsMccForMnc(mcc);
+ } catch (NumberFormatException e) {
+ mncLength = UNKNOWN;
+ Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
+ }
+ }
+
+ if (mncLength != UNKNOWN && mncLength != UNINITIALIZED) {
+ // finally have both the imsi and the mncLength and can parse the imsi properly
+ MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
+ }
+ ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
+ SimCard.INTENT_VALUE_ICC_IMSI, null);
break;
case EVENT_GET_MBI_DONE:
@@ -755,39 +713,58 @@ public final class SIMRecords extends IccRecords {
case EVENT_GET_AD_DONE:
- isRecordLoadResponse = true;
+ try {
+ isRecordLoadResponse = true;
- ar = (AsyncResult)msg.obj;
- data = (byte[])ar.result;
+ ar = (AsyncResult)msg.obj;
+ data = (byte[])ar.result;
- if (ar.exception != null) {
- break;
- }
-
- Log.d(LOG_TAG, "EF_AD: " +
- IccUtils.bytesToHexString(data));
+ if (ar.exception != null) {
+ break;
+ }
- if (data.length < 3) {
- Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
- break;
- }
+ Log.d(LOG_TAG, "EF_AD: " +
+ IccUtils.bytesToHexString(data));
- if (data.length == 3) {
- Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
- break;
- }
+ if (data.length < 3) {
+ Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
+ break;
+ }
- mncLength = (int)data[3] & 0xf;
+ if (data.length == 3) {
+ Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
+ break;
+ }
- if (mncLength == 0xf) {
- // Resetting mncLength to 0 to indicate that it is not
- // initialised
- mncLength = 0;
+ mncLength = (int)data[3] & 0xf;
- Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
- break;
+ if (mncLength == 0xf) {
+ mncLength = UNKNOWN;
+ }
+ } finally {
+ if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
+ if (imsi != null) {
+ try {
+ int mcc = Integer.parseInt(imsi.substring(0,3));
+
+ mncLength = MccTable.smallestDigitsMccForMnc(mcc);
+ } catch (NumberFormatException e) {
+ mncLength = UNKNOWN;
+ Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
+ }
+ } else {
+ // Indicate we got this info, but it didn't contain the length.
+ mncLength = UNKNOWN;
+
+ Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
+ }
+ }
+ if (imsi != null && mncLength != UNKNOWN) {
+ // finally have both imsi and the length of the mnc and can parse
+ // the imsi properly
+ MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
+ }
}
-
break;
case EVENT_GET_SPN_DONE:
@@ -1178,7 +1155,7 @@ public final class SIMRecords extends IccRecords {
recordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
- ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent(
+ ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
SimCard.INTENT_VALUE_ICC_LOADED, null);
}
@@ -1203,7 +1180,7 @@ public final class SIMRecords extends IccRecords {
/* broadcast intent SIM_READY here so that we can make sure
READY is sent before IMSI ready
*/
- ((GSMPhone) phone).mSimCard.broadcastSimStateChangedIntent(
+ ((GSMPhone) phone).mSimCard.broadcastIccStateChangedIntent(
SimCard.INTENT_VALUE_ICC_READY, null);
fetchSimRecords();
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
index 9af3aa6..835cb29 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
@@ -16,497 +16,35 @@
package com.android.internal.telephony.gsm;
-import android.app.ActivityManagerNative;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.AsyncResult;
-import android.os.RemoteException;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
import android.util.Log;
-import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.IccCard;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneProxy;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-
-import static android.Manifest.permission.READ_PHONE_STATE;
/**
- * Note: this class shares common code with RuimCard, consider a base class to minimize code
- * duplication.
* {@hide}
*/
-public final class SimCard extends Handler implements IccCard {
- static final String LOG_TAG="GSM";
-
- //***** Instance Variables
- private static final boolean DBG = true;
-
- private GSMPhone phone;
- private CommandsInterface.IccStatus status = null;
- private boolean mDesiredPinLocked;
- private boolean mDesiredFdnEnabled;
- private boolean mSimPinLocked = true; // Default to locked
- private boolean mSimFdnEnabled = false; // Default to disabled.
- // Will be updated when SIM_READY.
-
- //***** Constants
-
- // FIXME I hope this doesn't conflict with the Dialer's notifications
- static final int NOTIFICATION_ID_SIM_STATUS = 33456;
-
- //***** Event Constants
-
- static final int EVENT_SIM_LOCKED_OR_ABSENT = 1;
- static final int EVENT_GET_SIM_STATUS_DONE = 2;
- static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 3;
- static final int EVENT_PINPUK_DONE = 4;
- static final int EVENT_REPOLL_STATUS_DONE = 5;
- static final int EVENT_SIM_READY = 6;
- static final int EVENT_QUERY_FACILITY_LOCK_DONE = 7;
- static final int EVENT_CHANGE_FACILITY_LOCK_DONE = 8;
- static final int EVENT_CHANGE_SIM_PASSWORD_DONE = 9;
- static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
- static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
-
-
- //***** Constructor
+public final class SimCard extends IccCard {
SimCard(GSMPhone phone) {
- this.phone = phone;
-
- phone.mCM.registerForSIMLockedOrAbsent(
- this, EVENT_SIM_LOCKED_OR_ABSENT, null);
-
- phone.mCM.registerForOffOrNotAvailable(
- this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-
- phone.mCM.registerForSIMReady(
- this, EVENT_SIM_READY, null);
+ super(phone, "GSM", true);
+ mPhone.mCM.registerForSIMLockedOrAbsent(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
+ mPhone.mCM.registerForOffOrNotAvailable(mHandler, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
+ mPhone.mCM.registerForSIMReady(mHandler, EVENT_ICC_READY, null);
updateStateProperty();
}
+ @Override
public void dispose() {
//Unregister for all events
- phone.mCM.unregisterForSIMLockedOrAbsent(this);
- phone.mCM.unregisterForOffOrNotAvailable(this);
- phone.mCM.unregisterForSIMReady(this);
- }
-
- protected void finalize() {
- if(DBG) Log.d(LOG_TAG, "SimCard finalized");
- }
-
- //***** SimCard implementation
-
- public State
- getState() {
- if (status == null) {
- switch(phone.mCM.getRadioState()) {
- /* This switch block must not return anything in
- * State.isLocked() or State.ABSENT.
- * If it does, handleSimStatus() may break
- */
- case RADIO_OFF:
- case RADIO_UNAVAILABLE:
- case SIM_NOT_READY:
- return State.UNKNOWN;
- case SIM_LOCKED_OR_ABSENT:
- //this should be transient-only
- return State.UNKNOWN;
- case SIM_READY:
- return State.READY;
- }
- } else {
- switch (status) {
- case ICC_ABSENT: return State.ABSENT;
- case ICC_NOT_READY: return State.UNKNOWN;
- case ICC_READY: return State.READY;
- case ICC_PIN: return State.PIN_REQUIRED;
- case ICC_PUK: return State.PUK_REQUIRED;
- case ICC_NETWORK_PERSONALIZATION: return State.NETWORK_LOCKED;
- }
- }
-
- Log.e(LOG_TAG, "GsmSimCard.getState(): case should never be reached");
- return State.UNKNOWN;
- }
-
- private RegistrantList absentRegistrants = new RegistrantList();
- private RegistrantList pinLockedRegistrants = new RegistrantList();
- private RegistrantList networkLockedRegistrants = new RegistrantList();
-
-
- public void registerForAbsent(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- absentRegistrants.add(r);
-
- if (getState() == State.ABSENT) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForAbsent(Handler h) {
- absentRegistrants.remove(h);
- }
-
- public void registerForNetworkLocked(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- networkLockedRegistrants.add(r);
-
- if (getState() == State.NETWORK_LOCKED) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForNetworkLocked(Handler h) {
- networkLockedRegistrants.remove(h);
- }
-
- public void registerForLocked(Handler h, int what, Object obj) {
- Registrant r = new Registrant (h, what, obj);
-
- pinLockedRegistrants.add(r);
-
- if (getState().isPinLocked()) {
- r.notifyRegistrant();
- }
- }
-
- public void unregisterForLocked(Handler h) {
- pinLockedRegistrants.remove(h);
- }
-
-
- public void supplyPin (String pin, Message onComplete) {
- phone.mCM.supplyIccPin(pin,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyPuk (String puk, String newPin, Message onComplete) {
- phone.mCM.supplyIccPuk(puk, newPin,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
- public void supplyPin2 (String pin2, Message onComplete) {
- phone.mCM.supplyIccPin2(pin2,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
- public void supplyPuk2 (String puk2, String newPin2, Message onComplete) {
- phone.mCM.supplyIccPuk2(puk2, newPin2,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public void supplyNetworkDepersonalization (String pin, Message onComplete) {
- if(DBG) log("Network Despersonalization: " + pin);
- phone.mCM.supplyNetworkDepersonalization(pin,
- obtainMessage(EVENT_PINPUK_DONE, onComplete));
- }
-
- public boolean getIccLockEnabled() {
- return mSimPinLocked;
- }
-
- public boolean getIccFdnEnabled() {
- return mSimFdnEnabled;
+ mPhone.mCM.unregisterForSIMLockedOrAbsent(mHandler);
+ mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
+ mPhone.mCM.unregisterForSIMReady(mHandler);
}
- public void setIccLockEnabled (boolean enabled,
- String password, Message onComplete) {
- int serviceClassX;
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX;
-
- mDesiredPinLocked = enabled;
-
- phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_SIM,
- enabled, password, serviceClassX,
- obtainMessage(EVENT_CHANGE_FACILITY_LOCK_DONE, onComplete));
- }
-
- public void setIccFdnEnabled (boolean enabled,
- String password, Message onComplete) {
- int serviceClassX;
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX +
- CommandsInterface.SERVICE_CLASS_SMS;
-
- mDesiredFdnEnabled = enabled;
-
- phone.mCM.setFacilityLock(CommandsInterface.CB_FACILITY_BA_FD,
- enabled, password, serviceClassX,
- obtainMessage(EVENT_CHANGE_FACILITY_FDN_DONE, onComplete));
- }
-
- public void changeIccLockPassword(String oldPassword, String newPassword,
- Message onComplete) {
- if(DBG) log("Change Pin1 old: " + oldPassword + " new: " + newPassword);
- phone.mCM.changeIccPin(oldPassword, newPassword,
- obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete));
-
- }
-
- public void changeIccFdnPassword(String oldPassword, String newPassword,
- Message onComplete) {
- if(DBG) log("Change Pin2 old: " + oldPassword + " new: " + newPassword);
- phone.mCM.changeIccPin2(oldPassword, newPassword,
- obtainMessage(EVENT_CHANGE_SIM_PASSWORD_DONE, onComplete));
-
- }
-
- public String getServiceProviderName () {
- return phone.mSIMRecords.getServiceProviderName();
- }
-
- //***** Handler implementation
@Override
- public void handleMessage(Message msg){
- AsyncResult ar;
- int serviceClassX;
-
- serviceClassX = CommandsInterface.SERVICE_CLASS_VOICE +
- CommandsInterface.SERVICE_CLASS_DATA +
- CommandsInterface.SERVICE_CLASS_FAX;
-
- switch (msg.what) {
- case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
- status = null;
- updateStateProperty();
- broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_NOT_READY, null);
- break;
- case EVENT_SIM_READY:
- //TODO: put facility read in SIM_READY now, maybe in REG_NW
- phone.mCM.getIccStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_FD, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_FDN_DONE));
- break;
- case EVENT_SIM_LOCKED_OR_ABSENT:
- phone.mCM.getIccStatus(obtainMessage(EVENT_GET_SIM_STATUS_DONE));
- phone.mCM.queryFacilityLock (
- CommandsInterface.CB_FACILITY_BA_SIM, "", serviceClassX,
- obtainMessage(EVENT_QUERY_FACILITY_LOCK_DONE));
- break;
- case EVENT_GET_SIM_STATUS_DONE:
- ar = (AsyncResult)msg.obj;
-
- getSimStatusDone(ar);
- break;
- case EVENT_PINPUK_DONE:
- // a PIN/PUK/PIN2/PUK2/Network Personalization
- // request has completed. ar.userObj is the response Message
- // Repoll before returning
- ar = (AsyncResult)msg.obj;
- // TODO should abstract these exceptions
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- phone.mCM.getIccStatus(
- obtainMessage(EVENT_REPOLL_STATUS_DONE, ar.userObj));
- break;
- case EVENT_REPOLL_STATUS_DONE:
- // Finished repolling status after PIN operation
- // ar.userObj is the response messaeg
- // ar.userObj.obj is already an AsyncResult with an
- // appropriate exception filled in if applicable
-
- ar = (AsyncResult)msg.obj;
- getSimStatusDone(ar);
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_QUERY_FACILITY_LOCK_DONE:
- ar = (AsyncResult)msg.obj;
- onQueryFacilityLock(ar);
- break;
- case EVENT_QUERY_FACILITY_FDN_DONE:
- ar = (AsyncResult)msg.obj;
- onQueryFdnEnabled(ar);
- break;
- case EVENT_CHANGE_FACILITY_LOCK_DONE:
- ar = (AsyncResult)msg.obj;
- if (ar.exception == null) {
- mSimPinLocked = mDesiredPinLocked;
- if (DBG) log( "EVENT_CHANGE_FACILITY_LOCK_DONE: " +
- "mSimPinLocked= " + mSimPinLocked);
- } else {
- Log.e(LOG_TAG, "Error change facility lock with exception "
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_CHANGE_FACILITY_FDN_DONE:
- ar = (AsyncResult)msg.obj;
-
- if (ar.exception == null) {
- mSimFdnEnabled = mDesiredFdnEnabled;
- if (DBG) log("EVENT_CHANGE_FACILITY_FDN_DONE: " +
- "mSimFdnEnabled=" + mSimFdnEnabled);
- } else {
- Log.e(LOG_TAG, "Error change facility fdn with exception "
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- case EVENT_CHANGE_SIM_PASSWORD_DONE:
- ar = (AsyncResult)msg.obj;
- if(ar.exception != null) {
- Log.e(LOG_TAG, "Error in change sim password with exception"
- + ar.exception);
- }
- AsyncResult.forMessage(((Message)ar.userObj)).exception
- = ar.exception;
- ((Message)ar.userObj).sendToTarget();
- break;
- default:
- Log.e(LOG_TAG, "[GsmSimCard] Unknown Event " + msg.what);
- }
- }
-
-
- //***** Private methods
-
- /**
- * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
- * @param ar is asyncResult of Query_Facility_Locked
- */
- private void onQueryFacilityLock(AsyncResult ar) {
- if(ar.exception != null) {
- if (DBG) log("Error in querying facility lock:" + ar.exception);
- return;
- }
-
- int[] ints = (int[])ar.result;
- if(ints.length != 0) {
- mSimPinLocked = (0!=ints[0]);
- if(DBG) log("Query facility lock : " + mSimPinLocked);
- } else {
- Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response");
- }
- }
-
- /**
- * Interperate EVENT_QUERY_FACILITY_LOCK_DONE
- * @param ar is asyncResult of Query_Facility_Locked
- */
- private void onQueryFdnEnabled(AsyncResult ar) {
- if(ar.exception != null) {
- if(DBG) log("Error in querying facility lock:" + ar.exception);
- return;
- }
-
- int[] ints = (int[])ar.result;
- if(ints.length != 0) {
- mSimFdnEnabled = (0!=ints[0]);
- if(DBG) log("Query facility lock : " + mSimFdnEnabled);
- } else {
- Log.e(LOG_TAG, "[GsmSimCard] Bogus facility lock response");
- }
- }
-
- private void
- getSimStatusDone(AsyncResult ar) {
- if (ar.exception != null) {
- Log.e(LOG_TAG,"Error getting ICC status. "
- + "RIL_REQUEST_GET_ICC_STATUS should "
- + "never return an error", ar.exception);
- return;
- }
-
- CommandsInterface.IccStatus newStatus
- = (CommandsInterface.IccStatus) ar.result;
-
- handleSimStatus(newStatus);
- }
-
- private void
- handleSimStatus(CommandsInterface.IccStatus newStatus) {
- boolean transitionedIntoPinLocked;
- boolean transitionedIntoAbsent;
- boolean transitionedIntoNetworkLocked;
-
- SimCard.State oldState, newState;
-
- oldState = getState();
- status = newStatus;
- newState = getState();
-
- updateStateProperty();
-
- transitionedIntoPinLocked = (
- (oldState != State.PIN_REQUIRED && newState == State.PIN_REQUIRED)
- || (oldState != State.PUK_REQUIRED && newState == State.PUK_REQUIRED));
- transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
- transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
- && newState == State.NETWORK_LOCKED);
-
- if (transitionedIntoPinLocked) {
- if(DBG) log("Notify SIM pin or puk locked.");
- pinLockedRegistrants.notifyRegistrants();
- broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_LOCKED,
- (newState == State.PIN_REQUIRED) ?
- INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
- } else if (transitionedIntoAbsent) {
- if(DBG) log("Notify SIM missing.");
- absentRegistrants.notifyRegistrants();
- broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_ABSENT, null);
- } else if (transitionedIntoNetworkLocked) {
- if(DBG) log("Notify SIM network locked.");
- networkLockedRegistrants.notifyRegistrants();
- broadcastSimStateChangedIntent(SimCard.INTENT_VALUE_ICC_LOCKED,
- INTENT_VALUE_LOCKED_NETWORK);
- }
- }
-
- public void broadcastSimStateChangedIntent(String value, String reason) {
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- intent.putExtra(Phone.PHONE_NAME_KEY, phone.getPhoneName());
- intent.putExtra(SimCard.INTENT_KEY_ICC_STATE, value);
- intent.putExtra(SimCard.INTENT_KEY_LOCKED_REASON, reason);
- if(DBG) log("Broadcasting intent SIM_STATE_CHANGED_ACTION " + value
- + " reason " + reason);
- ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE);
- }
-
- public void updateImsiConfiguration(String imsi) {
- if (imsi.length() >= 6) {
- Configuration config = new Configuration();
- config.mcc = ((imsi.charAt(0)-'0')*100)
- + ((imsi.charAt(1)-'0')*10)
- + (imsi.charAt(2)-'0');
- config.mnc = ((imsi.charAt(3)-'0')*100)
- + ((imsi.charAt(4)-'0')*10)
- + (imsi.charAt(5)-'0');
- try {
- ActivityManagerNative.getDefault().updateConfiguration(config);
- } catch (RemoteException e) {
- }
- }
- }
-
- private void
- updateStateProperty() {
- phone.setSystemProperty(
- TelephonyProperties.PROPERTY_SIM_STATE,
- getState().toString());
+ public String getServiceProviderName () {
+ return ((GSMPhone)mPhone).mSIMRecords.getServiceProviderName();
}
- private void log(String msg) {
- Log.d(LOG_TAG, "[GsmSimCard] " + msg);
- }
}
-
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java
index 30543c7..497cf5f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java
@@ -47,7 +47,6 @@ public class SimTlv
public boolean nextObject() {
if (!hasValidTlvObject) return false;
-
curOffset = curDataOffset + curDataLength;
hasValidTlvObject = parseCurrentTlvObject();
return hasValidTlvObject;
@@ -88,11 +87,12 @@ public class SimTlv
private boolean parseCurrentTlvObject() {
// 0x00 and 0xff are invalid tag values
- if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) {
- return false;
- }
try {
+ if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) {
+ return false;
+ }
+
if ((record[curOffset + 1] & 0xff) < 0x80) {
// one byte length 0 - 0x7f
curDataLength = record[curOffset + 1] & 0xff;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index af59126..569cf25 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -337,7 +337,7 @@ public class SmsMessage extends SmsMessageBase{
* Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
- String destinationAddress, short destinationPort, byte[] data,
+ String destinationAddress, int destinationPort, byte[] data,
boolean statusReportRequested) {
SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
@@ -519,12 +519,12 @@ public class SmsMessage extends SmsMessageBase{
long getSCTimestampMillis() {
// TP-Service-Centre-Time-Stamp
- int year = IccUtils.bcdByteToInt(pdu[cur++]);
- int month = IccUtils.bcdByteToInt(pdu[cur++]);
- int day = IccUtils.bcdByteToInt(pdu[cur++]);
- int hour = IccUtils.bcdByteToInt(pdu[cur++]);
- int minute = IccUtils.bcdByteToInt(pdu[cur++]);
- int second = IccUtils.bcdByteToInt(pdu[cur++]);
+ int year = IccUtils.gsmBcdByteToInt(pdu[cur++]);
+ int month = IccUtils.gsmBcdByteToInt(pdu[cur++]);
+ int day = IccUtils.gsmBcdByteToInt(pdu[cur++]);
+ int hour = IccUtils.gsmBcdByteToInt(pdu[cur++]);
+ int minute = IccUtils.gsmBcdByteToInt(pdu[cur++]);
+ int second = IccUtils.gsmBcdByteToInt(pdu[cur++]);
// For the timezone, the most significant bit of the
// least signficant nibble is the sign byte
@@ -534,11 +534,9 @@ public class SmsMessage extends SmsMessageBase{
byte tzByte = pdu[cur++];
// Mask out sign bit.
- int timezoneOffset = IccUtils
- .bcdByteToInt((byte) (tzByte & (~0x08)));
+ int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
- timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset
- : -timezoneOffset;
+ timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
Time time = new Time(Time.TIMEZONE_UTC);
diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
new file mode 100644
index 0000000..d27f240
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.gsm;
+
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.internal.telephony.AdnRecord;
+import com.android.internal.telephony.AdnRecordCache;
+import com.android.internal.telephony.IccConstants;
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.PhoneBase;
+
+import org.apache.harmony.luni.lang.reflect.ListOfTypes;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class implements reading and parsing USIM records.
+ * Refer to Spec 3GPP TS 31.102 for more details.
+ *
+ * {@hide}
+ */
+public class UsimPhoneBookManager extends Handler implements IccConstants {
+ private static final String LOG_TAG = "GSM";
+ private static final boolean DBG = true;
+ private PbrFile mPbrFile;
+ private Boolean mIsPbrPresent;
+ private PhoneBase mPhone;
+ private AdnRecordCache mAdnCache;
+ private Object mLock = new Object();
+ private ArrayList<AdnRecord> mPhoneBookRecords;
+ private boolean mEmailPresentInIap = false;
+ private int mEmailTagNumberInIap = 0;
+ private ArrayList<byte[]> mIapFileRecord;
+ private ArrayList<byte[]> mEmailFileRecord;
+ private Map<Integer, ArrayList<String>> mEmailsForAdnRec;
+
+ private static final int EVENT_PBR_LOAD_DONE = 1;
+ private static final int EVENT_USIM_ADN_LOAD_DONE = 2;
+ private static final int EVENT_IAP_LOAD_DONE = 3;
+ private static final int EVENT_EMAIL_LOAD_DONE = 4;
+
+ private static final int USIM_TYPE1_TAG = 0xA8;
+ private static final int USIM_TYPE2_TAG = 0xA9;
+ private static final int USIM_TYPE3_TAG = 0xAA;
+ private static final int USIM_EFADN_TAG = 0xC0;
+ private static final int USIM_EFIAP_TAG = 0xC1;
+ private static final int USIM_EFEXT1_TAG = 0xC2;
+ private static final int USIM_EFSNE_TAG = 0xC3;
+ private static final int USIM_EFANR_TAG = 0xC4;
+ private static final int USIM_EFPBC_TAG = 0xC5;
+ private static final int USIM_EFGRP_TAG = 0xC6;
+ private static final int USIM_EFAAS_TAG = 0xC7;
+ private static final int USIM_EFGSD_TAG = 0xC8;
+ private static final int USIM_EFUID_TAG = 0xC9;
+ private static final int USIM_EFEMAIL_TAG = 0xCA;
+ private static final int USIM_EFCCP1_TAG = 0xCB;
+
+ public UsimPhoneBookManager(PhoneBase phone, AdnRecordCache cache) {
+ mPhone = phone;
+ mPhoneBookRecords = new ArrayList<AdnRecord>();
+ mPbrFile = null;
+ // We assume its present, after the first read this is updated.
+ // So we don't have to read from UICC if its not present on subsequent reads.
+ mIsPbrPresent = true;
+ mAdnCache = cache;
+ }
+
+ public void reset() {
+ mPhoneBookRecords.clear();
+ mIapFileRecord = null;
+ mEmailFileRecord = null;
+ mPbrFile = null;
+ mIsPbrPresent = true;
+ }
+
+ public ArrayList<AdnRecord> loadEfFilesFromUsim() {
+ synchronized (mLock) {
+ if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords;
+ if (!mIsPbrPresent) return null;
+
+ // Check if the PBR file is present in the cache, if not read it
+ // from the USIM.
+ if (mPbrFile == null) {
+ readPbrFileAndWait();
+ }
+
+ if (mPbrFile == null) return null;
+
+ int numRecs = mPbrFile.mFileIds.size();
+ for (int i = 0; i < numRecs; i++) {
+ readAdnFileAndWait(i);
+ readEmailFileAndWait(i);
+ }
+ // All EF files are loaded, post the response.
+ }
+ return mPhoneBookRecords;
+ }
+
+ private void readPbrFileAndWait() {
+ mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
+ }
+ }
+
+ private void readEmailFileAndWait(int recNum) {
+ Map <Integer,Integer> fileIds;
+ fileIds = mPbrFile.mFileIds.get(recNum);
+ if (fileIds == null) return;
+
+ if (fileIds.containsKey(USIM_EFEMAIL_TAG)) {
+ int efid = fileIds.get(USIM_EFEMAIL_TAG);
+ // Check if the EFEmail is a Type 1 file or a type 2 file.
+ // If mEmailPresentInIap is true, its a type 2 file.
+ // So we read the IAP file and then read the email records.
+ // instead of reading directly.
+ if (mEmailPresentInIap) {
+ readIapFileAndWait(fileIds.get(USIM_EFIAP_TAG));
+ if (mIapFileRecord == null) {
+ Log.e(LOG_TAG, "Error: IAP file is empty");
+ return;
+ }
+ }
+ // Read the EFEmail file.
+ mPhone.getIccFileHandler().loadEFLinearFixedAll(fileIds.get(USIM_EFEMAIL_TAG),
+ obtainMessage(EVENT_EMAIL_LOAD_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait");
+ }
+
+ if (mEmailFileRecord == null) {
+ Log.e(LOG_TAG, "Error: Email file is empty");
+ return;
+ }
+ updatePhoneAdnRecord();
+ }
+
+ }
+
+ private void readIapFileAndWait(int efid) {
+ mPhone.getIccFileHandler().loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait");
+ }
+ }
+
+ private void updatePhoneAdnRecord() {
+ if (mEmailFileRecord == null) return;
+ int numAdnRecs = mPhoneBookRecords.size();
+ if (mIapFileRecord != null) {
+ // The number of records in the IAP file is same as the number of records in ADN file.
+ // The order of the pointers in an EFIAP shall be the same as the order of file IDs
+ // that appear in the TLV object indicated by Tag 'A9' in the reference file record.
+ // i.e value of mEmailTagNumberInIap
+
+ for (int i = 0; i < numAdnRecs; i++) {
+ byte[] record = null;
+ try {
+ record = mIapFileRecord.get(i);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(LOG_TAG, "Error: Improper ICC card: No IAP record for ADN, continuing");
+ break;
+ }
+ int recNum = record[mEmailTagNumberInIap];
+
+ if (recNum != -1) {
+ String[] emails = new String[1];
+ // SIM record numbers are 1 based
+ emails[0] = readEmailRecord(recNum - 1);
+ AdnRecord rec = mPhoneBookRecords.get(i);
+ if (rec != null) {
+ rec.setEmails(emails);
+ } else {
+ // might be a record with only email
+ rec = new AdnRecord("", "", emails);
+ }
+ mPhoneBookRecords.set(i, rec);
+ }
+ }
+ }
+
+ // ICC cards can be made such that they have an IAP file but all
+ // records are empty. So we read both type 1 and type 2 file
+ // email records, just to be sure.
+
+ int len = mPhoneBookRecords.size();
+ // Type 1 file, the number of records is the same as the number of
+ // records in the ADN file.
+ if (mEmailsForAdnRec == null) {
+ parseType1EmailFile(len);
+ }
+ for (int i = 0; i < numAdnRecs; i++) {
+ ArrayList<String> emailList = null;
+ try {
+ emailList = mEmailsForAdnRec.get(i);
+ } catch (IndexOutOfBoundsException e) {
+ break;
+ }
+ if (emailList == null) continue;
+
+ AdnRecord rec = mPhoneBookRecords.get(i);
+
+ String[] emails = new String[emailList.size()];
+ System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size());
+ rec.setEmails(emails);
+ mPhoneBookRecords.set(i, rec);
+ }
+ }
+
+ void parseType1EmailFile(int numRecs) {
+ mEmailsForAdnRec = new HashMap<Integer, ArrayList<String>>();
+ byte[] emailRec = null;
+ for (int i = 0; i < numRecs; i++) {
+ try {
+ emailRec = mEmailFileRecord.get(i);
+ } catch (IndexOutOfBoundsException e) {
+ Log.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing");
+ break;
+ }
+ int adnRecNum = emailRec[emailRec.length - 1];
+
+ if (adnRecNum == -1) {
+ continue;
+ }
+
+ String email = readEmailRecord(i);
+
+ if (email == null || email.equals("")) {
+ continue;
+ }
+
+ // SIM record numbers are 1 based.
+ ArrayList<String> val = mEmailsForAdnRec.get(adnRecNum - 1);
+ if (val == null) {
+ val = new ArrayList<String>();
+ }
+ val.add(email);
+ // SIM record numbers are 1 based.
+ mEmailsForAdnRec.put(adnRecNum - 1, val);
+ }
+ }
+
+ private String readEmailRecord(int recNum) {
+ byte[] emailRec = null;
+ try {
+ emailRec = mEmailFileRecord.get(recNum);
+ } catch (IndexOutOfBoundsException e) {
+ return null;
+ }
+
+ // The length of the record is X+2 byte, where X bytes is the email address
+ String email = IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2);
+ return email;
+ }
+
+ private void readAdnFileAndWait(int recNum) {
+ Map <Integer,Integer> fileIds;
+ fileIds = mPbrFile.mFileIds.get(recNum);
+ if (fileIds == null) return;
+
+ mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG),
+ fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
+ }
+ }
+
+ private void createPbrFile(ArrayList<byte[]> records) {
+ if (records == null) {
+ mPbrFile = null;
+ mIsPbrPresent = false;
+ return;
+ }
+ mPbrFile = new PbrFile(records);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AsyncResult ar;
+
+ switch(msg.what) {
+ case EVENT_PBR_LOAD_DONE:
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ createPbrFile((ArrayList<byte[]>)ar.result);
+ }
+ synchronized (mLock) {
+ mLock.notify();
+ }
+ break;
+ case EVENT_USIM_ADN_LOAD_DONE:
+ log("Loading USIM ADN records done");
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result);
+ }
+ synchronized (mLock) {
+ mLock.notify();
+ }
+ break;
+ case EVENT_IAP_LOAD_DONE:
+ log("Loading USIM IAP records done");
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ mIapFileRecord = ((ArrayList<byte[]>)ar.result);
+ }
+ synchronized (mLock) {
+ mLock.notify();
+ }
+ break;
+ case EVENT_EMAIL_LOAD_DONE:
+ log("Loading USIM Email records done");
+ ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ mEmailFileRecord = ((ArrayList<byte[]>)ar.result);
+ }
+
+ synchronized (mLock) {
+ mLock.notify();
+ }
+ break;
+ }
+ }
+
+ private class PbrFile {
+ // RecNum <EF Tag, efid>
+ HashMap<Integer,Map<Integer,Integer>> mFileIds;
+
+ PbrFile(ArrayList<byte[]> records) {
+ mFileIds = new HashMap<Integer, Map<Integer, Integer>>();
+ SimTlv recTlv;
+ int recNum = 0;
+ for (byte[] record: records) {
+ recTlv = new SimTlv(record, 0, record.length);
+ parseTag(recTlv, recNum);
+ recNum ++;
+ }
+ }
+
+ void parseTag(SimTlv tlv, int recNum) {
+ SimTlv tlvEf;
+ int tag;
+ byte[] data;
+ Map<Integer, Integer> val = new HashMap<Integer, Integer>();
+ do {
+ tag = tlv.getTag();
+ switch(tag) {
+ case USIM_TYPE1_TAG: // A8
+ case USIM_TYPE3_TAG: // AA
+ case USIM_TYPE2_TAG: // A9
+ data = tlv.getData();
+ tlvEf = new SimTlv(data, 0, data.length);
+ parseEf(tlvEf, val, tag);
+ break;
+ }
+ } while (tlv.nextObject());
+ mFileIds.put(recNum, val);
+ }
+
+ void parseEf(SimTlv tlv, Map<Integer, Integer> val, int parentTag) {
+ int tag;
+ byte[] data;
+ int tagNumberWithinParentTag = 0;
+ do {
+ tag = tlv.getTag();
+ if (parentTag == USIM_TYPE2_TAG && tag == USIM_EFEMAIL_TAG) {
+ mEmailPresentInIap = true;
+ mEmailTagNumberInIap = tagNumberWithinParentTag;
+ }
+ switch(tag) {
+ case USIM_EFEMAIL_TAG:
+ case USIM_EFADN_TAG:
+ case USIM_EFEXT1_TAG:
+ case USIM_EFANR_TAG:
+ case USIM_EFPBC_TAG:
+ case USIM_EFGRP_TAG:
+ case USIM_EFAAS_TAG:
+ case USIM_EFGSD_TAG:
+ case USIM_EFUID_TAG:
+ case USIM_EFCCP1_TAG:
+ case USIM_EFIAP_TAG:
+ case USIM_EFSNE_TAG:
+ data = tlv.getData();
+ int efid = data[0] << 8 | data[1];
+ val.put(tag, efid);
+ break;
+ }
+ tagNumberWithinParentTag ++;
+ } while(tlv.nextObject());
+ }
+ }
+
+ private void log(String msg) {
+ if(DBG) Log.d(LOG_TAG, msg);
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
index f71ea48..11b3fd6 100644
--- a/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/telephony/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -27,10 +27,11 @@ import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandException;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.DataCallState;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.Phone;
import com.android.internal.telephony.gsm.CallFailCause;
import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
import com.android.internal.telephony.gsm.SuppServiceNotification;
-import com.android.internal.telephony.Phone;
import java.util.ArrayList;
@@ -103,39 +104,8 @@ public final class SimulatedCommands extends BaseCommands
//***** CommandsInterface implementation
- public void getIccStatus(Message result) {
- switch (mState) {
- case SIM_READY:
- resultSuccess(result, IccStatus.ICC_READY);
- break;
-
- case SIM_LOCKED_OR_ABSENT:
- returnSimLockedStatus(result);
- break;
-
- default:
- resultSuccess(result, IccStatus.ICC_NOT_READY);
- break;
- }
- }
-
- private void returnSimLockedStatus(Message result) {
- switch (mSimLockedState) {
- case REQUIRE_PIN:
- Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: ICC_PIN");
- resultSuccess(result, IccStatus.ICC_PIN);
- break;
-
- case REQUIRE_PUK:
- Log.i(LOG_TAG, "[SimCmd] returnSimLockedStatus: ICC_PUK");
- resultSuccess(result, IccStatus.ICC_PUK);
- break;
-
- default:
- Log.i(LOG_TAG,
- "[SimCmd] returnSimLockedStatus: mSimLockedState==NONE !?");
- break;
- }
+ public void getIccCardStatus(Message result) {
+ unimplemented(result);
}
public void supplyIccPin(String pin, Message result) {
@@ -974,7 +944,7 @@ public final class SimulatedCommands extends BaseCommands
}
public void setupDataCall(String radioTechnology, String profile, String apn, String user,
- String password, Message result) {
+ String password, String authType, Message result) {
unimplemented(result);
}
diff --git a/test-runner/android/test/AndroidTestRunner.java b/test-runner/android/test/AndroidTestRunner.java
index 79cedb0..358b7e9 100644
--- a/test-runner/android/test/AndroidTestRunner.java
+++ b/test-runner/android/test/AndroidTestRunner.java
@@ -158,16 +158,18 @@ public class AndroidTestRunner extends BaseTestRunner {
mTestResult.addListener(testListener);
}
+ Context testContext = mInstrumentation == null ? mContext : mInstrumentation.getContext();
for (TestCase testCase : mTestCases) {
- setContextIfAndroidTestCase(testCase, mContext);
+ setContextIfAndroidTestCase(testCase, mContext, testContext);
setInstrumentationIfInstrumentationTestCase(testCase, mInstrumentation);
testCase.run(mTestResult);
}
}
- private void setContextIfAndroidTestCase(Test test, Context context) {
+ private void setContextIfAndroidTestCase(Test test, Context context, Context testContext) {
if (AndroidTestCase.class.isAssignableFrom(test.getClass())) {
((AndroidTestCase) test).setContext(context);
+ ((AndroidTestCase) test).setTestContext(testContext);
}
}
@@ -178,14 +180,23 @@ public class AndroidTestRunner extends BaseTestRunner {
private void setInstrumentationIfInstrumentationTestCase(
Test test, Instrumentation instrumentation) {
if (InstrumentationTestCase.class.isAssignableFrom(test.getClass())) {
- ((InstrumentationTestCase) test).injectInsrumentation(instrumentation);
+ ((InstrumentationTestCase) test).injectInstrumentation(instrumentation);
}
}
- public void setInstrumentaiton(Instrumentation instrumentation) {
+ public void setInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
}
+ /**
+ * @deprecated Incorrect spelling,
+ * use {@link #setInstrumentation(android.app.Instrumentation)} instead.
+ */
+ @Deprecated
+ public void setInstrumentaiton(Instrumentation instrumentation) {
+ setInstrumentation(instrumentation);
+ }
+
@Override
protected Class loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
return mContext.getClassLoader().loadClass(suiteClassName);
diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java
index 6658fb0..23f0ed4 100644
--- a/test-runner/android/test/InstrumentationTestRunner.java
+++ b/test-runner/android/test/InstrumentationTestRunner.java
@@ -329,7 +329,7 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu
mTestRunner = getAndroidTestRunner();
mTestRunner.setContext(getTargetContext());
- mTestRunner.setInstrumentaiton(this);
+ mTestRunner.setInstrumentation(this);
mTestRunner.setSkipExecution(logOnly);
mTestRunner.setTest(testSuiteBuilder.build());
mTestCount = mTestRunner.getTestCases().size();
diff --git a/test-runner/android/test/IsolatedContext.java b/test-runner/android/test/IsolatedContext.java
index 859b2e5..5c66169 100644
--- a/test-runner/android/test/IsolatedContext.java
+++ b/test-runner/android/test/IsolatedContext.java
@@ -2,6 +2,9 @@ package android.test;
import com.google.android.collect.Lists;
+import android.accounts.AccountManager;
+import android.accounts.OnAccountsUpdatedListener;
+import android.accounts.Account;
import android.content.ContextWrapper;
import android.content.ContentResolver;
import android.content.Intent;
@@ -11,6 +14,8 @@ import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import java.util.List;
import java.io.File;
@@ -22,6 +27,7 @@ import java.io.File;
public class IsolatedContext extends ContextWrapper {
private ContentResolver mResolver;
+ private final MockAccountManager mMockAccountManager;
private List<Intent> mBroadcastIntents = Lists.newArrayList();
@@ -29,6 +35,7 @@ public class IsolatedContext extends ContextWrapper {
ContentResolver resolver, Context targetContext) {
super(targetContext);
mResolver = resolver;
+ mMockAccountManager = new MockAccountManager();
}
/** Returns the list of intents that were broadcast since the last call to this method. */
@@ -79,10 +86,27 @@ public class IsolatedContext extends ContextWrapper {
@Override
public Object getSystemService(String name) {
- // No services exist in this context.
+ if (Context.ACCOUNT_SERVICE.equals(name)) {
+ return mMockAccountManager;
+ }
+ // No other services exist in this context.
return null;
}
+ private class MockAccountManager extends AccountManager {
+ public MockAccountManager() {
+ super(IsolatedContext.this, null /* IAccountManager */, null /* handler */);
+ }
+
+ public void addOnAccountsUpdatedListener(OnAccountsUpdatedListener listener,
+ Handler handler, boolean updateImmediately) {
+ // do nothing
+ }
+
+ public Account[] getAccounts() {
+ return new Account[]{};
+ }
+ }
@Override
public File getFilesDir() {
return new File("/dev/null");
diff --git a/test-runner/android/test/ProviderTestCase.java b/test-runner/android/test/ProviderTestCase.java
index 445b4eb..668e9f7 100644
--- a/test-runner/android/test/ProviderTestCase.java
+++ b/test-runner/android/test/ProviderTestCase.java
@@ -15,6 +15,7 @@ import android.database.DatabaseUtils;
* @deprecated this class extends InstrumentationTestCase but should extend AndroidTestCase. Use
* ProviderTestCase2, which corrects this problem, instead.
*/
+@Deprecated
public abstract class ProviderTestCase<T extends ContentProvider>
extends InstrumentationTestCase {
diff --git a/test-runner/android/test/RenamingDelegatingContext.java b/test-runner/android/test/RenamingDelegatingContext.java
index 3f64340..0ea43ab 100644
--- a/test-runner/android/test/RenamingDelegatingContext.java
+++ b/test-runner/android/test/RenamingDelegatingContext.java
@@ -6,6 +6,8 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.ContentProvider;
import android.database.sqlite.SQLiteDatabase;
+import android.os.FileUtils;
+import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
@@ -22,6 +24,8 @@ public class RenamingDelegatingContext extends ContextWrapper {
private Context mFileContext;
private String mFilePrefix = null;
+ private File mCacheDir;
+ private final Object mSync = new Object();
private Set<String> mDatabaseNames = Sets.newHashSet();
private Set<String> mFileNames = Sets.newHashSet();
@@ -136,6 +140,11 @@ public class RenamingDelegatingContext extends ContextWrapper {
return false;
}
}
+
+ @Override
+ public File getDatabasePath(String name) {
+ return mFileContext.getDatabasePath(renamedFileName(name));
+ }
@Override
public String[] databaseList() {
@@ -179,6 +188,32 @@ public class RenamingDelegatingContext extends ContextWrapper {
public String[] fileList() {
return mFileNames.toArray(new String[]{});
}
+
+ /**
+ * In order to support calls to getCacheDir(), we create a temp cache dir (inside the real
+ * one) and return it instead. This code is basically getCacheDir(), except it uses the real
+ * cache dir as the parent directory and creates a test cache dir inside that.
+ */
+ @Override
+ public File getCacheDir() {
+ synchronized (mSync) {
+ if (mCacheDir == null) {
+ mCacheDir = new File(mFileContext.getCacheDir(), renamedFileName("cache"));
+ }
+ if (!mCacheDir.exists()) {
+ if(!mCacheDir.mkdirs()) {
+ Log.w("RenamingDelegatingContext", "Unable to create cache directory");
+ return null;
+ }
+ FileUtils.setPermissions(
+ mCacheDir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ }
+ return mCacheDir;
+ }
+
// /**
// * Given an array of files returns only those whose names indicate that they belong to this
diff --git a/test-runner/android/test/SyncBaseInstrumentation.java b/test-runner/android/test/SyncBaseInstrumentation.java
index 772d75c..a860bb3 100644
--- a/test-runner/android/test/SyncBaseInstrumentation.java
+++ b/test-runner/android/test/SyncBaseInstrumentation.java
@@ -19,9 +19,9 @@ package android.test;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.SystemClock;
import android.net.Uri;
+import android.accounts.Account;
/**
* If you would like to test sync a single provider with an
@@ -44,12 +44,12 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
* Syncs the specified provider.
* @throws Exception
*/
- protected void syncProvider(Uri uri, String account, String authority) throws Exception {
+ protected void syncProvider(Uri uri, String accountName, String authority) throws Exception {
Bundle extras = new Bundle();
- extras.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
- extras.putString(ContentResolver.SYNC_EXTRAS_ACCOUNT, account);
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ Account account = new Account(accountName, "com.google.GAIA");
- mContentResolver.startSync(uri, extras);
+ ContentResolver.requestSync(account, authority, extras);
long startTimeInMillis = SystemClock.elapsedRealtime();
long endTimeInMillis = startTimeInMillis + MAX_TIME_FOR_SYNC_IN_MINS * 60000;
@@ -64,7 +64,7 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
break;
}
- if (isSyncActive(account, authority)) {
+ if (ContentResolver.isSyncActive(account, authority)) {
counter = 0;
continue;
}
@@ -73,24 +73,7 @@ public class SyncBaseInstrumentation extends InstrumentationTestCase {
}
protected void cancelSyncsandDisableAutoSync() {
- try {
- ContentResolver.getContentService().setListenForNetworkTickles(false);
- } catch (RemoteException e) {
- }
- mContentResolver.cancelSync(null);
- }
-
- /**
- * This method tests if any sync is active or not. Sync is considered to be active if the
- * entry is in either the Pending or Active tables.
- * @return
- */
- private boolean isSyncActive(String account, String authority) {
- try {
- return ContentResolver.getContentService().isSyncActive(account,
- authority);
- } catch (RemoteException e) {
- return false;
- }
+ ContentResolver.setMasterSyncAutomatically(false);
+ ContentResolver.cancelSync(null /* all accounts */, null /* all authorities */);
}
}
diff --git a/test-runner/android/test/TestRunner.java b/test-runner/android/test/TestRunner.java
index efa2480..012df35 100644
--- a/test-runner/android/test/TestRunner.java
+++ b/test-runner/android/test/TestRunner.java
@@ -39,7 +39,7 @@ import com.google.android.collect.Lists;
* and you probably will not need to instantiate, extend, or call this
* class yourself. See the full {@link android.test} package description
* to learn more about testing Android applications.
- *
+ *
* {@hide} Not needed for 1.0 SDK.
*/
public class TestRunner implements PerformanceTestCase.Intermediates {
@@ -84,6 +84,7 @@ public class TestRunner implements PerformanceTestCase.Intermediates {
super();
}
+ @Override
public void run(TestResult result) {
result.addListener(this);
super.run(result);
@@ -301,7 +302,7 @@ public class TestRunner implements PerformanceTestCase.Intermediates {
if (mMode == PERFORMANCE) {
runInPerformanceMode(test, className, false, className);
} else if (mMode == PROFILING) {
- //Need a way to mark a test to be run in profiling mode or not.
+ //Need a way to mark a test to be run in profiling mode or not.
startProfiling();
test.run();
finishProfiling();
@@ -337,6 +338,7 @@ public class TestRunner implements PerformanceTestCase.Intermediates {
AndroidTestCase testcase = (AndroidTestCase) test;
try {
testcase.setContext(mContext);
+ testcase.setTestContext(mContext);
} catch (Exception ex) {
Log.i("TestHarness", ex.toString());
}
@@ -700,7 +702,7 @@ public class TestRunner implements PerformanceTestCase.Intermediates {
}
} catch (ClassNotFoundException e) {
return 1; // this gets the count right, because either this test
- // is missing, and it will fail when run or it is a single Junit test to be run.
+ // is missing, and it will fail when run or it is a single Junit test to be run.
}
return 0;
}
diff --git a/test-runner/android/test/TouchUtils.java b/test-runner/android/test/TouchUtils.java
index 52d2ee8..962b2f9 100644
--- a/test-runner/android/test/TouchUtils.java
+++ b/test-runner/android/test/TouchUtils.java
@@ -565,6 +565,7 @@ public class TouchUtils {
* {@link android.test.ActivityInstrumentationTestCase2}, which provides more options for
* configuring the Activity under test
*/
+ @Deprecated
public static int dragViewBy(InstrumentationTestCase test, View v, int gravity, int deltaX,
int deltaY) {
int[] xy = new int[2];
diff --git a/test-runner/android/test/mock/MockContentProvider.java b/test-runner/android/test/mock/MockContentProvider.java
index d04fc44..74f86d8 100644
--- a/test-runner/android/test/mock/MockContentProvider.java
+++ b/test-runner/android/test/mock/MockContentProvider.java
@@ -18,7 +18,11 @@ package android.test.mock;
import android.content.ContentValues;
import android.content.IContentProvider;
-import android.content.ISyncAdapter;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.content.ContentProviderResult;
+import android.content.ContentProviderOperation;
+import android.content.OperationApplicationException;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.CursorWindow;
@@ -30,6 +34,7 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
/**
* Mock implementation of IContentProvider that does nothing. All methods are non-functional and
@@ -48,6 +53,10 @@ public class MockContentProvider implements IContentProvider {
return 0;
}
+ public Uri insertEntity(Uri uri, Entity entities) throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
@SuppressWarnings("unused")
public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder, IContentObserver observer,
@@ -62,11 +71,6 @@ public class MockContentProvider implements IContentProvider {
}
@SuppressWarnings("unused")
- public ISyncAdapter getSyncAdapter() throws RemoteException {
- throw new UnsupportedOperationException("unimplemented mock method");
- }
-
- @SuppressWarnings("unused")
public String getType(Uri url) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -87,19 +91,33 @@ public class MockContentProvider implements IContentProvider {
throws FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
-
+
+ public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
+ throws RemoteException, OperationApplicationException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
@SuppressWarnings("unused")
public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
String sortOrder) throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
+ public EntityIterator queryEntities(Uri url, String selection, String[] selectionArgs,
+ String sortOrder) throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
@SuppressWarnings("unused")
public int update(Uri url, ContentValues values, String selection, String[] selectionArgs)
throws RemoteException {
throw new UnsupportedOperationException("unimplemented mock method");
}
+ public int updateEntity(Uri uri, Entity entity) throws RemoteException {
+ throw new UnsupportedOperationException("unimplemented mock method");
+ }
+
public IBinder asBinder() {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index d5cd6ef..beb9044 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -22,6 +22,7 @@ import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageInstallObserver;
@@ -139,6 +140,11 @@ public class MockPackageManager extends PackageManager {
}
@Override
+ public int checkSignatures(int uid1, int uid2) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String[] getPackagesForUid(int uid) {
throw new UnsupportedOperationException();
}
@@ -292,9 +298,6 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
- /**
- * @hide - to match hiding in superclass
- */
@Override
public String getInstallerPackageName(String packageName) {
throw new UnsupportedOperationException();
@@ -422,6 +425,11 @@ public class MockPackageManager extends PackageManager {
}
@Override
+ public FeatureInfo[] getSystemAvailableFeatures() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public boolean isSafeMode() {
throw new UnsupportedOperationException();
}
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 845f547..d94327a 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -52,6 +52,7 @@
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
+ <uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
diff --git a/tests/AndroidTests/res/raw/v21_simple_1.vcf b/tests/AndroidTests/res/raw/v21_simple_1.vcf
new file mode 100644
index 0000000..6aabb4c
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_simple_1.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+N:Ando;Roid;
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple_2.vcf b/tests/AndroidTests/res/raw/v21_simple_2.vcf
new file mode 100644
index 0000000..f0d5ab5
--- /dev/null
+++ b/tests/AndroidTests/res/raw/v21_simple_2.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD
+FN:Ando Roid
+END:VCARD
diff --git a/tests/AndroidTests/res/raw/v21_simple.vcf b/tests/AndroidTests/res/raw/v21_simple_3.vcf
index beddabb..beddabb 100644
--- a/tests/AndroidTests/res/raw/v21_simple.vcf
+++ b/tests/AndroidTests/res/raw/v21_simple_3.vcf
diff --git a/tests/AndroidTests/run_test.sh b/tests/AndroidTests/run_test.sh
index 7ada698..bc06b7e 100755
--- a/tests/AndroidTests/run_test.sh
+++ b/tests/AndroidTests/run_test.sh
@@ -1,4 +1,4 @@
framework=/system/framework
bpath=$framework/core.jar:$framework/ext.jar:$framework/framework.jar:$framework/android.test.runner.jar
-adb shell exec dalvikvm -Xbootclasspath:$bpath -cp /system/app/AndroidTests.apk:/data/app/com.android.unit_tests.apk \
+adb shell exec dalvikvm -Xbootclasspath:$bpath -cp /system/app/AndroidTests.apk:/data/app/com.android.unit_tests.apk:/data/app/AndroidTests.apk \
com.android.internal.util.WithFramework junit.textui.TestRunner $*
diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
index f0ba573..98d4c25 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java
@@ -18,9 +18,11 @@ package com.android.unit_tests;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.cdma.SmsMessage;
import com.android.internal.telephony.cdma.sms.BearerData;
import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
+import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
import com.android.internal.util.HexDump;
@@ -28,14 +30,73 @@ import com.android.internal.util.HexDump;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import java.util.Iterator;
-
-import java.lang.Integer;
-
import android.util.Log;
+import java.util.ArrayList;
+
public class CdmaSmsTest extends AndroidTestCase {
- private final static String LOG_TAG = "CDMA";
+ private final static String LOG_TAG = "XXX CdmaSmsTest XXX";
+
+ @SmallTest
+ public void testCdmaSmsAddrParsing() throws Exception {
+ CdmaSmsAddress addr = CdmaSmsAddress.parse("6502531000");
+ assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+ assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
+ assertEquals(addr.numberOfDigits, 10);
+ assertEquals(addr.origBytes.length, 10);
+ byte[] data = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10};
+ for (int i = 0; i < data.length; i++) {
+ assertEquals(addr.origBytes[i], data[i]);
+ }
+ addr = CdmaSmsAddress.parse("(650) 253-1000");
+ assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+ assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
+ assertEquals(addr.numberOfDigits, 10);
+ assertEquals(addr.origBytes.length, 10);
+ byte[] data2 = {6, 5, 10, 2, 5, 3, 1, 10, 10, 10};
+ for (int i = 0; i < data2.length; i++) {
+ assertEquals(addr.origBytes[i], data2[i]);
+ }
+ addr = CdmaSmsAddress.parse("(+886) 917 222 555");
+ assertEquals(addr.ton, CdmaSmsAddress.TON_INTERNATIONAL_OR_IP);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);
+ assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);
+ assertEquals(addr.numberOfDigits, 12);
+ assertEquals(addr.origBytes.length, 12);
+ byte[] data3 = {8, 8, 6, 9, 1, 7, 2, 2, 2, 5, 5, 5};
+ for (int i = 0; i < data3.length; i++) {
+ assertEquals(addr.origBytes[i], data3[i]);
+ }
+ addr = CdmaSmsAddress.parse("(650) *253-1000 #600");
+ byte[] data4 = {6, 5, 10, 11, 2, 5, 3, 1, 10, 10, 10, 12, 6, 10, 10};
+ for (int i = 0; i < data4.length; i++) {
+ assertEquals(addr.origBytes[i], data4[i]);
+ }
+ String input = "x@y.com,a@b.com";
+ addr = CdmaSmsAddress.parse(input);
+ assertEquals(addr.ton, CdmaSmsAddress.TON_NATIONAL_OR_EMAIL);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR);
+ assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK);
+ assertEquals(addr.numberOfDigits, 15);
+ assertEquals(addr.origBytes.length, 15);
+ assertEquals(new String(addr.origBytes), input);
+ addr = CdmaSmsAddress.parse("foo bar");
+ assertEquals(addr.ton, CdmaSmsAddress.TON_UNKNOWN);
+ assertEquals(addr.digitMode, CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR);
+ assertEquals(addr.numberMode, CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK);
+ assertEquals(addr.numberOfDigits, 6);
+ assertEquals(addr.origBytes.length, 6);
+ assertEquals(new String(addr.origBytes), "foobar");
+ addr = CdmaSmsAddress.parse("f\noo\tb a\rr");
+ assertEquals(new String(addr.origBytes), "foobar");
+ assertEquals(CdmaSmsAddress.parse("f\u0000oo bar"), null);
+ assertEquals(CdmaSmsAddress.parse("f\u0007oo bar"), null);
+ assertEquals(CdmaSmsAddress.parse("f\u0080oo bar"), null);
+ assertEquals(CdmaSmsAddress.parse("f\u1ECFboo\u001fbar"), null);
+ assertEquals(CdmaSmsAddress.parse("f\u0080oo bar"), null);
+ }
@SmallTest
public void testUserData7bitGsm() throws Exception {
@@ -84,6 +145,15 @@ public class CdmaSmsTest extends AndroidTestCase {
assertEquals(userData.msgEncoding, revBearerData.userData.msgEncoding);
assertEquals(userData.payloadStr.length(), revBearerData.userData.numFields);
assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "Test \u007f standard \u0000 SMS";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals("Test standard SMS", revBearerData.userData.payloadStr);
+ userData.payloadStr = "Test \n standard \r SMS";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
}
@SmallTest
@@ -105,6 +175,21 @@ public class CdmaSmsTest extends AndroidTestCase {
assertEquals(userData.msgEncoding, revBearerData.userData.msgEncoding);
assertEquals(userData.payloadStr.length(), revBearerData.userData.numFields);
assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "1234567";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "12345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789012345678901234567890" +
+ "1234567890";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "Test \u007f illegal \u0000 SMS chars";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals("Test illegal SMS chars", revBearerData.userData.payloadStr);
userData.payloadStr = "More @ testing\nis great^|^~woohoo";
revBearerData = BearerData.decode(BearerData.encode(bearerData));
assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
@@ -153,6 +238,12 @@ public class CdmaSmsTest extends AndroidTestCase {
assertEquals(userData.msgEncoding, revBearerData.userData.msgEncoding);
assertEquals(userData.payloadStr.length(), revBearerData.userData.numFields);
assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "1234567";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ userData.payloadStr = "";
+ revBearerData = BearerData.decode(BearerData.encode(bearerData));
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
}
@SmallTest
@@ -437,29 +528,19 @@ public class CdmaSmsTest extends AndroidTestCase {
@SmallTest
public void testNumberOfMessages() throws Exception {
+ // Note that the message text below does not properly reflect
+ // the message count. The author of these messages was
+ // apparently unaware that the values are bcd encoded, and the
+ // values being tested against (not the ones in the message
+ // text) are actually correct.
String pdu1 = "000310409001124896a794e07595f69f199540ea759a0dc8e00b0163";
BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
assertEquals("Test Voice mail 99", bd1.userData.payloadStr);
- assertEquals(99, bd1.numberOfMessages);
+ assertEquals(63, bd1.numberOfMessages);
String pdu2 = "00031040900113489ea794e07595f69f199540ea759a0988c0600b0164";
BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
assertEquals("Test Voice mail 100", bd2.userData.payloadStr);
- assertEquals(100, bd2.numberOfMessages);
- }
-
- @SmallTest
- public void testNumberOfMessagesFeedback() throws Exception {
- BearerData bearerData = new BearerData();
- bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
- bearerData.messageId = 0;
- bearerData.hasUserDataHeader = false;
- UserData userData = new UserData();
- userData.payloadStr = "test message count";
- bearerData.userData = userData;
- bearerData.numberOfMessages = 27;
- byte []encodedSms = BearerData.encode(bearerData);
- BearerData revBearerData = BearerData.decode(encodedSms);
- assertEquals(bearerData.numberOfMessages, revBearerData.numberOfMessages);
+ assertEquals(64, bd2.numberOfMessages);
}
@SmallTest
@@ -576,6 +657,16 @@ public class CdmaSmsTest extends AndroidTestCase {
BearerData bd4 = BearerData.decode(HexDump.hexStringToByteArray(pdu4));
assertEquals(bd4.alert, 3);
assertEquals(bd4.userData.payloadStr, "Test Alert 3");
+ String pdu5 = "00031000000126114F4CBCFA20DB979F3C39F2A0C9976" +
+ "69ED979794187665E5D1028EFA7A6840E1062D3D39A900C028000";
+ BearerData bd5 = BearerData.decode(HexDump.hexStringToByteArray(pdu5));
+ assertEquals(bd5.alert, BearerData.ALERT_MEDIUM_PRIO);
+ assertEquals(bd5.userData.payloadStr, "test message delivery alert (with 8 bits)");
+ String pdu6 = "00031000000126114F4CBCFA20DB979F3C39F2A0C9976" +
+ "69ED979794187665E5D1028EFA7A6840C1062D3D39A900C00";
+ BearerData bd6 = BearerData.decode(HexDump.hexStringToByteArray(pdu6));
+ assertEquals(bd6.userData.payloadStr, "test message delivery alert (with 0 bits)");
+ assertEquals(bd6.alertIndicatorSet, false);
}
@SmallTest
@@ -663,7 +754,6 @@ public class CdmaSmsTest extends AndroidTestCase {
public void testDisplayMode() throws Exception {
String pdu1 = "0003104090010c485f4194dfea34becf61b8400f0100";
BearerData bd1 = BearerData.decode(HexDump.hexStringToByteArray(pdu1));
- //Log.d(LOG_TAG, "bd1 = " + bd1);
assertEquals(bd1.displayMode, BearerData.DISPLAY_MODE_IMMEDIATE);
String pdu2 = "0003104090010c485f4194dfea34becf61b8400f0140";
BearerData bd2 = BearerData.decode(HexDump.hexStringToByteArray(pdu2));
@@ -707,4 +797,50 @@ public class CdmaSmsTest extends AndroidTestCase {
assertEquals(bd4.userData.payloadStr, "ABCDEFG");
}
+ @SmallTest
+ public void testUserDataHeaderWithEightCharMsg() throws Exception {
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+ bearerData.messageId = 55;
+ SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
+ concatRef.refNumber = 0xEE;
+ concatRef.msgCount = 2;
+ concatRef.seqNumber = 2;
+ concatRef.isEightBits = true;
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.concatRef = concatRef;
+ UserData userData = new UserData();
+ userData.payloadStr = "01234567";
+ userData.userDataHeader = smsHeader;
+ bearerData.userData = userData;
+ byte[] encodedSms = BearerData.encode(bearerData);
+ BearerData revBearerData = BearerData.decode(encodedSms);
+ assertEquals(userData.payloadStr, revBearerData.userData.payloadStr);
+ }
+
+ @SmallTest
+ public void testFragmentText() throws Exception {
+ // Valid 160 character ASCII text.
+ String text1 = "123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789[";
+ TextEncodingDetails ted = SmsMessage.calculateLength(text1, false);
+ assertEquals(ted.msgCount, 1);
+ assertEquals(ted.codeUnitCount, 160);
+ assertEquals(ted.codeUnitSize, 1);
+ ArrayList<String> fragments = android.telephony.SmsMessage.fragmentText(text1);
+ assertEquals(fragments.size(), 1);
+ // Valid 160 character GSM text -- the last character is
+ // non-ASCII, and so this will currently generate a singleton
+ // EMS message, which is not necessarily supported by Verizon.
+ String text2 = "123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "12345678901234567890123456789\u00a3"; // Trailing pound-currency sign.
+ ted = SmsMessage.calculateLength(text2, false);
+ assertEquals(ted.msgCount, 1);
+ assertEquals(ted.codeUnitCount, 160);
+ assertEquals(ted.codeUnitSize, 1);
+ fragments = android.telephony.SmsMessage.fragmentText(text2);
+ assertEquals(fragments.size(), 1);
+ }
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/GsmSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/GsmSmsTest.java
new file mode 100644
index 0000000..8987d6b
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/GsmSmsTest.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2006 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.unit_tests;
+
+import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.gsm.SmsMessage;
+import com.android.internal.util.HexDump;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+public class GsmSmsTest extends AndroidTestCase {
+
+ @SmallTest
+ public void testAddressing() throws Exception {
+ String pdu = "07914151551512f2040B916105551511f100006060605130308A04D4F29C0E";
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ assertEquals("+14155551212", sms.getServiceCenterAddress());
+ assertEquals("+16505551111", sms.getOriginatingAddress());
+ assertEquals("Test", sms.getMessageBody());
+
+ pdu = "07914151551512f2040B916105551511f100036060924180008A0DA"
+ + "8695DAC2E8FE9296A794E07";
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ assertEquals("+14155551212", sms.getServiceCenterAddress());
+ assertEquals("+16505551111", sms.getOriginatingAddress());
+ assertEquals("(Subject)Test", sms.getMessageBody());
+ }
+
+ @SmallTest
+ public void testUdh() throws Exception {
+ String pdu = "07914140279510F6440A8111110301003BF56080207130138A8C0B05040B8423F"
+ + "000032A02010106276170706C69636174696F6E2F766E642E7761702E6D6D732D"
+ + "6D65737361676500AF848D0185B4848C8298524E453955304A6D7135514141426"
+ + "66C414141414D7741414236514141414141008D908918802B3135313232393737"
+ + "3638332F545950453D504C4D4E008A808E022B918805810306977F83687474703"
+ + "A2F2F36";
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ SmsHeader header = sms.getUserDataHeader();
+ assertNotNull(header);
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 42);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 1);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
+
+ pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F"
+ + "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141"
+ + "42666C414141414D774141423651414141414100";
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ header = sms.getUserDataHeader();
+ assertNotNull(header);
+ assertNotNull(header.concatRef);
+ assertEquals(header.concatRef.refNumber, 42);
+ assertEquals(header.concatRef.msgCount, 2);
+ assertEquals(header.concatRef.seqNumber, 2);
+ assertEquals(header.concatRef.isEightBits, true);
+ assertNotNull(header.portAddrs);
+ assertEquals(header.portAddrs.destPort, 2948);
+ assertEquals(header.portAddrs.origPort, 9200);
+ assertEquals(header.portAddrs.areEightBits, false);
+ }
+
+ @SmallTest
+ public void testUcs2() throws Exception {
+ String pdu = "07912160130300F4040B914151245584F600087010807121352B1021220"
+ + "0A900AE00680065006C006C006F";
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ assertEquals("\u2122\u00a9\u00aehello", sms.getMessageBody());
+ }
+
+ @SmallTest
+ public void testMultipart() throws Exception {
+ /*
+ * Multi-part text SMS with septet data.
+ */
+ String pdu = "07916163838408F6440B816105224431F700007060217175830AA0050003"
+ + "00020162B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
+ + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
+ + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
+ + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
+ + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562";
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ assertEquals(sms.getMessageBody(),
+ "1111111111111111111111111111111111111111"
+ + "1111111111111111111111111111111111111111"
+ + "1111111111111111111111111111111111111111"
+ + "111111111111111111111111111111111");
+
+ pdu = "07916163838408F6440B816105224431F700007060217185000A23050003"
+ + "00020262B1582C168BC96432994C2693C96432994C2693C96432990C";
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+ assertEquals("1111111222222222222222222222", sms.getMessageBody());
+ }
+
+ @SmallTest
+ public void testCPHSVoiceMail() throws Exception {
+ // "set MWI flag"
+
+ String pdu = "07912160130310F20404D0110041006060627171118A0120";
+
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertTrue(sms.isReplace());
+ assertEquals("_@", sms.getOriginatingAddress());
+ assertEquals(" ", sms.getMessageBody());
+ assertTrue(sms.isMWISetMessage());
+
+ // "clear mwi flag"
+
+ pdu = "07912160130310F20404D0100041006021924193352B0120";
+
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertTrue(sms.isMWIClearMessage());
+
+ // "clear MWI flag"
+
+ pdu = "07912160130310F20404D0100041006060627161058A0120";
+
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertTrue(sms.isReplace());
+ assertEquals("\u0394@", sms.getOriginatingAddress());
+ assertEquals(" ", sms.getMessageBody());
+ assertTrue(sms.isMWIClearMessage());
+ }
+
+ @SmallTest
+ public void testCingularVoiceMail() throws Exception {
+ // "set MWI flag"
+
+ String pdu = "07912180958750F84401800500C87020026195702B06040102000200";
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertTrue(sms.isMWISetMessage());
+ assertTrue(sms.isMwiDontStore());
+
+ // "clear mwi flag"
+
+ pdu = "07912180958750F84401800500C07020027160112B06040102000000";
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertTrue(sms.isMWIClearMessage());
+ assertTrue(sms.isMwiDontStore());
+ }
+
+ @SmallTest
+ public void testEmailGateway() throws Exception {
+ String pdu = "07914151551512f204038105f300007011103164638a28e6f71b50c687db" +
+ "7076d9357eb7412f7a794e07cdeb6275794c07bde8e5391d247e93f3";
+
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertEquals("+14155551212", sms.getServiceCenterAddress());
+ assertTrue(sms.isEmail());
+ assertEquals("foo@example.com", sms.getEmailFrom());
+ assertEquals("foo@example.com", sms.getDisplayOriginatingAddress());
+ // As of https://android-git.corp.google.com/g/#change,9324
+ // getPseudoSubject will always be empty, and any subject is not extracted.
+ assertEquals("", sms.getPseudoSubject());
+ assertEquals("test subject /test body", sms.getDisplayMessageBody());
+ assertEquals("test subject /test body", sms.getEmailBody());
+
+ // email gateway sms test, including gsm extended character set.
+ pdu = "07914151551512f204038105f400007011103105458a29e6f71b50c687db" +
+ "7076d9357eb741af0d0a442fcfe9c23739bfe16d289bdee6b5f1813629";
+
+ sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertEquals("+14155551212", sms.getServiceCenterAddress());
+ assertTrue(sms.isEmail());
+ assertEquals("foo@example.com", sms.getDisplayOriginatingAddress());
+ assertEquals("foo@example.com", sms.getEmailFrom());
+ assertEquals("{ testBody[^~\\] }", sms.getDisplayMessageBody());
+ assertEquals("{ testBody[^~\\] }", sms.getEmailBody());
+ }
+
+ @SmallTest
+ public void testExtendedCharacterTable() throws Exception {
+ String pdu = "07914151551512f2040B916105551511f100006080615131728A44D4F29C0E2" +
+ "AE3E96537B94C068DD16179784C2FCB41F4B0985D06B958ADD00FB0E94536AF9749" +
+ "74DA6D281BA00E95E26D509B946FC3DBF87A25D56A04";
+
+ SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+
+ assertEquals("+14155551212", sms.getServiceCenterAddress());
+ assertEquals("+16505551111", sms.getOriginatingAddress());
+ assertEquals("Test extended character table .,-!?@~_\\/&\"';^|:()<{}>[]=%*+#",
+ sms.getMessageBody());
+ }
+
+ @SmallTest
+ public void testDecode() throws Exception {
+ byte[] septets = new byte[(7 * 128 + 7) / 8];
+
+ int bitOffset = 0;
+
+ for (int i = 0; i < 128; i++) {
+ int v;
+ if (i == 0x1b) {
+ // extended escape char
+ v = 0;
+ } else {
+ v = i;
+ }
+
+ int byteOffset = bitOffset / 8;
+ int shift = bitOffset % 8;
+
+ septets[byteOffset] |= v << shift;
+
+ if (shift > 1) {
+ septets[byteOffset + 1] = (byte) (v >> (8 - shift));
+ }
+
+ bitOffset += 7;
+ }
+
+ String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128);
+ byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded);
+
+ // reEncoded has the count septets byte at the front
+ assertEquals(reEncoded.length, septets.length + 1);
+
+ for (int i = 0; i < septets.length; i++) {
+ assertEquals(reEncoded[i + 1], septets[i]);
+ }
+ }
+
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/MccTableTest.java b/tests/AndroidTests/src/com/android/unit_tests/MccTableTest.java
new file mode 100644
index 0000000..b2f1ded
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/MccTableTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2006 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.unit_tests;
+
+import com.android.internal.telephony.MccTable;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+public class MccTableTest extends AndroidTestCase {
+ private final static String LOG_TAG = "GSM";
+
+ @SmallTest
+ public void testTimeZone() throws Exception {
+ assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris");
+ assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna");
+ assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg");
+ assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo");
+ assertEquals(MccTable.defaultTimeZoneForMcc(441), "Asia/Tokyo");
+ assertEquals(MccTable.defaultTimeZoneForMcc(525), "Singapore");
+ assertEquals(MccTable.defaultTimeZoneForMcc(240), null); // tz not defined, hence default
+ assertEquals(MccTable.defaultTimeZoneForMcc(0), null); // mcc not defined, hence default
+ assertEquals(MccTable.defaultTimeZoneForMcc(2000), null); // mcc not defined, hence default
+ }
+
+ @SmallTest
+ public void testCountryCode() throws Exception {
+ assertEquals(MccTable.countryCodeForMcc(270), "lu");
+ assertEquals(MccTable.countryCodeForMcc(202), "gr");
+ assertEquals(MccTable.countryCodeForMcc(750), "fk");
+ assertEquals(MccTable.countryCodeForMcc(646), "mg");
+ assertEquals(MccTable.countryCodeForMcc(314), "us");
+ assertEquals(MccTable.countryCodeForMcc(300), ""); // mcc not defined, hence default
+ assertEquals(MccTable.countryCodeForMcc(0), ""); // mcc not defined, hence default
+ assertEquals(MccTable.countryCodeForMcc(2000), ""); // mcc not defined, hence default
+ }
+
+ @SmallTest
+ public void testLang() throws Exception {
+ assertEquals(MccTable.defaultLanguageForMcc(311), "en");
+ assertEquals(MccTable.defaultLanguageForMcc(232), "de");
+ assertEquals(MccTable.defaultLanguageForMcc(230), "cs");
+ assertEquals(MccTable.defaultLanguageForMcc(204), "nl");
+ assertEquals(MccTable.defaultLanguageForMcc(274), null); // lang not defined, hence default
+ assertEquals(MccTable.defaultLanguageForMcc(0), null); // mcc not defined, hence default
+ assertEquals(MccTable.defaultLanguageForMcc(2000), null); // mcc not defined, hence default
+ }
+
+ @SmallTest
+ public void testSmDigits() throws Exception {
+ assertEquals(MccTable.smallestDigitsMccForMnc(312), 3);
+ assertEquals(MccTable.smallestDigitsMccForMnc(430), 2);
+ assertEquals(MccTable.smallestDigitsMccForMnc(365), 3);
+ assertEquals(MccTable.smallestDigitsMccForMnc(536), 2);
+ assertEquals(MccTable.smallestDigitsMccForMnc(352), 2); // sd not defined, hence default
+ assertEquals(MccTable.smallestDigitsMccForMnc(0), 2); // mcc not defined, hence default
+ assertEquals(MccTable.smallestDigitsMccForMnc(2000), 2); // mcc not defined, hence default
+ }
+
+ @SmallTest
+ public void testWifi() throws Exception {
+ assertEquals(MccTable.wifiChannelsForMcc(262), 13);
+ assertEquals(MccTable.wifiChannelsForMcc(234), 13);
+ assertEquals(MccTable.wifiChannelsForMcc(505), 11);
+ assertEquals(MccTable.wifiChannelsForMcc(313), 11);
+ assertEquals(MccTable.wifiChannelsForMcc(330), 0); // wifi not defined, hence default
+ assertEquals(MccTable.wifiChannelsForMcc(0), 0); // mcc not defined, hence default
+ assertEquals(MccTable.wifiChannelsForMcc(2000), 0); // mcc not defined, hence default
+
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java b/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
deleted file mode 100644
index 9d44fd9..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/SMSTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2006 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.unit_tests;
-
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.gsm.SmsMessage;
-import com.android.internal.util.HexDump;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.Iterator;
-
-public class SMSTest extends AndroidTestCase {
-
- @SmallTest
- public void testOne() throws Exception {
- String pdu = "07914151551512f2040B916105551511f100006060605130308A04D4F29C0E";
-
- SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertEquals("+14155551212", sms.getServiceCenterAddress());
- assertEquals("+16505551111", sms.getOriginatingAddress());
- assertEquals("Test", sms.getMessageBody());
- //assertTrue(sms.scTimeMillis == 1152223383000L);
-
- pdu = "07914151551512f2040B916105551511f100036060924180008A0DA"
- + "8695DAC2E8FE9296A794E07";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertEquals("+14155551212", sms.getServiceCenterAddress());
- assertEquals("+16505551111", sms.getOriginatingAddress());
- assertEquals("(Subject)Test", sms.getMessageBody());
-
- /* lines[0] = "+CMT: ,45";
- lines[1] = "07914140279510F6440A8111110301003BF56070624111958A8C0B05040B8423F"
- + "000033702010106276170706C69636174696F6E2F766E642E7761702E6D6D732D"
- + "6D65737361676500AF848D018BB4848C8298524D66616E304A6D7135514141416"
- + "57341414141546741414E4E304141414141008D908918802B3136353032343836"
- + "3137392F545950453D504C4D4E009646573A20008A808E0222C788058103093A7"
- + "F836874";
-
- sms = SMSMessage.createFromPdu(mContext, lines);
- */
-
- pdu = "07914140279510F6440A8111110301003BF56080207130138A8C0B05040B8423F"
- + "000032A02010106276170706C69636174696F6E2F766E642E7761702E6D6D732D"
- + "6D65737361676500AF848D0185B4848C8298524E453955304A6D7135514141426"
- + "66C414141414D7741414236514141414141008D908918802B3135313232393737"
- + "3638332F545950453D504C4D4E008A808E022B918805810306977F83687474703"
- + "A2F2F36";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- SmsHeader header = sms.getUserDataHeader();
- assertNotNull(header);
- assertNotNull(header.concatRef);
- assertEquals(header.concatRef.refNumber, 42);
- assertEquals(header.concatRef.msgCount, 2);
- assertEquals(header.concatRef.seqNumber, 1);
- assertEquals(header.concatRef.isEightBits, true);
- assertNotNull(header.portAddrs);
- assertEquals(header.portAddrs.destPort, 2948);
- assertEquals(header.portAddrs.origPort, 9200);
- assertEquals(header.portAddrs.areEightBits, false);
-
- pdu = "07914140279510F6440A8111110301003BF56080207130238A3B0B05040B8423F"
- + "000032A0202362E3130322E3137312E3135302F524E453955304A6D7135514141"
- + "42666C414141414D774141423651414141414100";
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- header = sms.getUserDataHeader();
- assertNotNull(header);
- assertNotNull(header.concatRef);
- assertEquals(header.concatRef.refNumber, 42);
- assertEquals(header.concatRef.msgCount, 2);
- assertEquals(header.concatRef.seqNumber, 2);
- assertEquals(header.concatRef.isEightBits, true);
- assertNotNull(header.portAddrs);
- assertEquals(header.portAddrs.destPort, 2948);
- assertEquals(header.portAddrs.origPort, 9200);
- assertEquals(header.portAddrs.areEightBits, false);
-
- /*
- * UCS-2 encoded SMS
- */
-
- pdu = "07912160130300F4040B914151245584F600087010807121352B10212200A900AE00680065006C006C006F";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
- assertEquals("\u2122\u00a9\u00aehello", sms.getMessageBody());
-
- // Entire alphabet (minus the escape character)
-
- /****
- lines[0] = "+CMT: ";
- lines[1] = "0001000A8114455245680000808080604028180E888462C168381E90886442A9582E988C06C0E9783EA09068442A994EA8946AC56AB95EB0986C46ABD96EB89C6EC7EBF97EC0A070482C1A8FC8A472C96C3A9FD0A8744AAD5AAFD8AC76CBED7ABFE0B0784C2E9BCFE8B47ACD6EBBDFF0B87C4EAFDBEFF8BC7ECFEFFBFF";
- sms = SMSMessage.createFromPdu(mContext, lines);
-
- System.out.println("full alphabet message body len: "
- + sms.getMessageBody().length());
-
- System.out.println("'" + sms.getMessageBody() +"'");
-
- assertTrue(sms.getMessageBody().length() == 128);
- ****/
-
- /*
- * Multi-part text SMS with data in septets
- */
- pdu = "07916163838408F6440B816105224431F700007060217175830AA0050003"
- + "00020162B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
- + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
- + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
- + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562B1582C"
- + "168BC562B1582C168BC562B1582C168BC562B1582C168BC562";
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
- assertEquals(sms.getMessageBody(),
- "1111111111111111111111111111111111111111"
- + "1111111111111111111111111111111111111111"
- + "1111111111111111111111111111111111111111"
- + "111111111111111111111111111111111");
-
- pdu = "07916163838408F6440B816105224431F700007060217185000A23050003"
- + "00020262B1582C168BC96432994C2693C96432994C2693C96432990C";
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
- assertEquals("1111111222222222222222222222", sms.getMessageBody());
- }
-
- @SmallTest
- public void testCPHSVoiceMail() throws Exception {
- // "set MWI flag"
-
- String pdu = "07912160130310F20404D0110041006060627171118A0120";
-
- SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertTrue(sms.isReplace());
- assertEquals("_@", sms.getOriginatingAddress());
- assertEquals(" ", sms.getMessageBody());
- assertTrue(sms.isMWISetMessage());
-
- // "clear mwi flag"
-
- pdu = "07912160130310F20404D0100041006021924193352B0120";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertTrue(sms.isMWIClearMessage());
-
- // "clear MWI flag"
-
- pdu = "07912160130310F20404D0100041006060627161058A0120";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
-// System.out.println("originating address: "
-// + (int) (sms.getOriginatingAddress().charAt(0)) + " "
-// + (int) (sms.getOriginatingAddress().charAt(1)));
-
- assertTrue(sms.isReplace());
- assertEquals("\u0394@", sms.getOriginatingAddress());
- assertEquals(" ", sms.getMessageBody());
- assertTrue(sms.isMWIClearMessage());
- }
-
- @SmallTest
- public void testCingularVoiceMail() throws Exception {
- // "set MWI flag"
-
- String pdu = "07912180958750F84401800500C87020026195702B06040102000200";
- SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertTrue(sms.isMWISetMessage());
- assertTrue(sms.isMwiDontStore());
-
- // "clear mwi flag"
-
- pdu = "07912180958750F84401800500C07020027160112B06040102000000";
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertTrue(sms.isMWIClearMessage());
- assertTrue(sms.isMwiDontStore());
- }
-
-// public void testTwo() throws Exception {
-// // FIXME need an SMS-SUBMIT test
-//
-// System.out.println(
-// "SMS SUBMIT: " + SmsMessage.getSubmitPdu(null, "+14155551212", "test", false));
-// }
-
- @SmallTest
- public void testEmailGateway() throws Exception {
- // email gateway sms test
- String pdu = "07914151551512f204038105f300007011103164638a28e6f71b50c687db" +
- "7076d9357eb7412f7a794e07cdeb6275794c07bde8e5391d247e93f3";
-
- SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertEquals("+14155551212", sms.getServiceCenterAddress());
- assertTrue(sms.isEmail());
- assertEquals("foo@example.com", sms.getEmailFrom());
- assertEquals("foo@example.com", sms.getDisplayOriginatingAddress());
- assertEquals("test subject", sms.getPseudoSubject());
- assertEquals("test body", sms.getDisplayMessageBody());
- assertEquals("test body", sms.getEmailBody());
-
- // email gateway sms test, including gsm extended character set.
- pdu = "07914151551512f204038105f400007011103105458a29e6f71b50c687db" +
- "7076d9357eb741af0d0a442fcfe9c23739bfe16d289bdee6b5f1813629";
-
- sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertEquals("+14155551212", sms.getServiceCenterAddress());
- assertTrue(sms.isEmail());
- assertEquals("foo@example.com", sms.getDisplayOriginatingAddress());
- assertEquals("foo@example.com", sms.getEmailFrom());
- assertEquals("{ testBody[^~\\] }", sms.getDisplayMessageBody());
- assertEquals("{ testBody[^~\\] }", sms.getEmailBody());
- }
-
- @SmallTest
- public void testExtendedCharacterTable() throws Exception {
- String pdu = "07914151551512f2040B916105551511f100006080615131728A44D4F29C0E2" +
- "AE3E96537B94C068DD16179784C2FCB41F4B0985D06B958ADD00FB0E94536AF9749" +
- "74DA6D281BA00E95E26D509B946FC3DBF87A25D56A04";
-
- SmsMessage sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
-
- assertEquals("+14155551212", sms.getServiceCenterAddress());
- assertEquals("+16505551111", sms.getOriginatingAddress());
- assertEquals("Test extended character table .,-!?@~_\\/&\"';^|:()<{}>[]=%*+#", sms.getMessageBody());
- }
-
- @SmallTest
- public void testDecode() throws Exception {
- byte[] septets = new byte[(7 * 128 + 7) / 8];
-
- int bitOffset = 0;
-
- for (int i = 0; i < 128; i++) {
- int v;
- if (i == 0x1b) {
- // extended escape char
- v = 0;
- } else {
- v = i;
- }
-
- int byteOffset = bitOffset / 8;
- int shift = bitOffset % 8;
-
- septets[byteOffset] |= v << shift;
-
- if (shift > 1) {
- septets[byteOffset + 1] = (byte) (v >> (8 - shift));
- }
-
- bitOffset += 7;
- }
-
- String decoded = GsmAlphabet.gsm7BitPackedToString(septets, 0, 128);
- byte[] reEncoded = GsmAlphabet.stringToGsm7BitPacked(decoded);
-
- // reEncoded has the count septets byte at the front
- assertEquals(reEncoded.length, septets.length + 1);
-
- for (int i = 0; i < septets.length; i++) {
- assertEquals(reEncoded[i + 1], septets[i]);
- }
- }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java
deleted file mode 100644
index b7f562d..0000000
--- a/tests/AndroidTests/src/com/android/unit_tests/VCardTests.java
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.unit_tests;
-
-import android.content.ContentValues;
-import android.syncml.pim.PropertyNode;
-import android.syncml.pim.VDataBuilder;
-import android.syncml.pim.VNode;
-import android.syncml.pim.vcard.VCardException;
-import android.syncml.pim.vcard.VCardParser_V21;
-import android.syncml.pim.vcard.VCardParser_V30;
-import android.test.AndroidTestCase;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Vector;
-
-public class VCardTests extends AndroidTestCase {
-
- private class PropertyNodesVerifier {
- private HashMap<String, Vector<PropertyNode>> mPropertyNodeMap;
- public PropertyNodesVerifier(PropertyNode... nodes) {
- mPropertyNodeMap = new HashMap<String, Vector<PropertyNode>>();
- for (PropertyNode propertyNode : nodes) {
- String propName = propertyNode.propName;
- Vector<PropertyNode> expectedNodes =
- mPropertyNodeMap.get(propName);
- if (expectedNodes == null) {
- expectedNodes = new Vector<PropertyNode>();
- mPropertyNodeMap.put(propName, expectedNodes);
- }
- expectedNodes.add(propertyNode);
- }
- }
-
- public void verify(VNode vnode) {
- for (PropertyNode propertyNode : vnode.propList) {
- String propName = propertyNode.propName;
- Vector<PropertyNode> nodes = mPropertyNodeMap.get(propName);
- if (nodes == null) {
- fail("Unexpected propName \"" + propName + "\" exists.");
- }
- boolean successful = false;
- int size = nodes.size();
- for (int i = 0; i < size; i++) {
- PropertyNode expectedNode = nodes.get(i);
- if (expectedNode.propName.equals(propName)) {
- if (expectedNode.equals(propertyNode)) {
- successful = true;
- nodes.remove(i);
- if (nodes.size() == 0) {
- mPropertyNodeMap.remove(propName);
- }
- break;
- } else {
- fail("Property \"" + propName + "\" has wrong value.\n"
- + "expected: " + expectedNode.toString()
- + "\n actual: " + propertyNode.toString());
- }
- }
- }
- if (!successful) {
- fail("Unexpected property \"" + propName + "\" exists.");
- }
- }
- if (mPropertyNodeMap.size() != 0) {
- Vector<String> expectedProps = new Vector<String>();
- for (Vector<PropertyNode> nodes : mPropertyNodeMap.values()) {
- for (PropertyNode node : nodes) {
- expectedProps.add(node.propName);
- }
- }
- fail("expected props " + Arrays.toString(expectedProps.toArray()) +
- " was not found");
- }
- }
- }
-
- public void testV21SimpleCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("N", "Ando;Roid;",
- Arrays.asList("Ando", "Roid", ""),
- null, null, null, null),
- new PropertyNode("FN", "Ando Roid",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21BackslashCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", ";A;B\\;C\\;;D;:E;\\\\;",
- Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""),
- null, null, null, null),
- new PropertyNode("FN", "A;B\\C\\;D:E\\\\",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21ComplicatedCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- ContentValues contentValuesForPhoto = new ContentValues();
- contentValuesForPhoto.put("ENCODING", "BASE64");
- // Push data into int array at first since values like 0x80 are
- // interpreted as int by the compiler and casting all of them is
- // cumbersome...
- int[] photoIntArray = {
- 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
- 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
- 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
- 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
- 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
- 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
- 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
- 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
- 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
- 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
- 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
- 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
- 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
- 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
- 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
- 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
- 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
- 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
- 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
- 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
- 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
- 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
- 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
- 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
- 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
- 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
- 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
- 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
- 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
- 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
- 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
- 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
- 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
- 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
- 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
- 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
- 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
- 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
- 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
- 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
- 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
- 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
- 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
- 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
- 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
- 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
- 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
- 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
- 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
- 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
- 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
- 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
- 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
- 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
- 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
- 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
- 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
- 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
- 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
- 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
- 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
- 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
- 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
- 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
- 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
- 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
- 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
- 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
- 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
- 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
- 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
- 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
- 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
- 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
- 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
- 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
- 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
- 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
- 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
- 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
- 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
- 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
- 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
- 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
- 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
- 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
- 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
- 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
- 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
- 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
- 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
- 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
- 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
- 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
- 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
- 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
- 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
- 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
- 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
- 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
- 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
- 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
- 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
- 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
- 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
- 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
- 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
- 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
- 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
- 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
- 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
- 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
- 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
- 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
- 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
- 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
- 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
- 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
- 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
- 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
- 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
- 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
- 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
- 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
- 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
- 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
- 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
- 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
- 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
- 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
- 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
- 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
- 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
- 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
- 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
- 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
- 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
- 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
- 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
- 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
- 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
- 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
- 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
- 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
- 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
- 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
- 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
- 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
- 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
- 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
- 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
- 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
- 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
- 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
- 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
- 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
- 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
- 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
- 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
- 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
- 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
- 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
- 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
- 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
- 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
- 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
- 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
- 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
- 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
- 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
- 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
- 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
- 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
- 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
- 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
- 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
- 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
- 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
- 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
- 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
- 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
- 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
- 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
- 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
- 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
- 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
- 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
- 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
- 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
- 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
- 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
- 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
- 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
- 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
- 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
- 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
- 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
- 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
- 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
- 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
- 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
- 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
- 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
- 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
- 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
- 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
- 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
- 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
- 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
- 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
- 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
- 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
- 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
- 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
- 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
- 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
- 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
- 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
- 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
- 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
- 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
- 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
- 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
- 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
- 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
- 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
- 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
- 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
- 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
- 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
- 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
- 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
- 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
- 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
- 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
- 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
- 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
- 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
- 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
- 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
- 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
- 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
- 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
- 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
- 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
- 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
- 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
- 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
- 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
- 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
- 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
- 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
- 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
- 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
- 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
- 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
- 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
- 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
- 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
- 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
- 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
- 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
- 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
- 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
- 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
- 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
- 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
- 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
- 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
- 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
- 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
- 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
- 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
- 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
- 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
- 0x0c, 0xd1, 0x00, 0xff, 0xd9};
- int length = photoIntArray.length;
- byte[] photoByteArray = new byte[length];
- for (int i = 0; i < length; i++) {
- photoByteArray[i] = (byte)photoIntArray[i];
- }
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "Gump;Forrest;Hoge;Pos;Tao",
- Arrays.asList("Gump", "Forrest",
- "Hoge", "Pos", "Tao"),
- null, null, null, null),
- new PropertyNode("FN", "Joe Due",
- null, null, null, null, null),
- new PropertyNode("ORG",
- "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
- Arrays.asList("Gump Shrimp Co.",
- "Sales Dept.;Manager",
- "Fish keeper"),
- null, null, null, null),
- new PropertyNode("ROLE", "Fish Cake Keeper!",
- null, null, null, null, null),
- new PropertyNode("TITLE", "Shrimp Man",
- null, null, null, null, null),
- new PropertyNode("X-CLASS", "PUBLIC",
- null, null, null, null, null),
- new PropertyNode("TEL", "(111) 555-1212",
- null, null, null,
- new HashSet<String>(Arrays.asList("WORK", "VOICE")), null),
- new PropertyNode("TEL", "(404) 555-1212",
- null, null, null,
- new HashSet<String>(Arrays.asList("HOME", "VOICE")), null),
- new PropertyNode("TEL", "0311111111",
- null, null, null,
- new HashSet<String>(Arrays.asList("CELL")), null),
- new PropertyNode("TEL", "0322222222",
- null, null, null,
- new HashSet<String>(Arrays.asList("VIDEO")), null),
- new PropertyNode("TEL", "0333333333",
- null, null, null,
- new HashSet<String>(Arrays.asList("VOICE")), null),
- new PropertyNode("ADR",
- ";;100 Waters Edge;Baytown;LA;30314;United States of America",
- Arrays.asList("", "", "100 Waters Edge", "Baytown",
- "LA", "30314", "United States of America"),
- null, null,
- new HashSet<String>(Arrays.asList("WORK")), null),
- new PropertyNode("LABEL",
- "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("WORK")), null),
- new PropertyNode("ADR",
- ";;42 Plantation St.;Baytown;LA;30314;United States of America",
- Arrays.asList("", "", "42 Plantation St.", "Baytown",
- "LA", "30314", "United States of America"), null, null,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("LABEL",
- "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
- null, null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("EMAIL", "forrestgump@walladalla.com",
- null, null, null,
- new HashSet<String>(Arrays.asList("PREF", "INTERNET")), null),
- new PropertyNode("EMAIL", "cell@example.com",
- null, null, null,
- new HashSet<String>(Arrays.asList("CELL")), null),
- new PropertyNode("NOTE", "The following note is the example from RFC 2045.",
- null, null, null, null, null),
- new PropertyNode("NOTE",
- "Now's the time for all folk to come to the aid of their country.",
- null, null, contentValuesForQP, null, null),
- new PropertyNode("PHOTO", null,
- null, photoByteArray, contentValuesForPhoto,
- new HashSet<String>(Arrays.asList("JPEG")), null),
- new PropertyNode("X-ATTRIBUTE", "Some String",
- null, null, null, null, null),
- new PropertyNode("BDAY", "19800101",
- null, null, null, null, null),
- new PropertyNode("GEO", "35.6563854,139.6994233",
- null, null, null, null, null),
- new PropertyNode("URL", "http://www.example.com/",
- null, null, null, null, null),
- new PropertyNode("REV", "20080424T195243Z",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21Japanese1() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_1);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- contentValuesForQP.put("CHARSET", "SHIFT_JIS");
- // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
- // vCard 2.1/3.0 specification does not allow multiple values.
- // Do not need to handle it as multiple values.
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "0300000000",
- null, null, null,
- new HashSet<String>(Arrays.asList("VOICE", "PREF")), null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21Japanese2() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_2);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- ContentValues contentValuesForQP = new ContentValues();
- contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
- contentValuesForQP.put("CHARSET", "SHIFT_JIS");
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
- Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
- "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("FN",
- "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
- null, null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- ("\uFF71\uFF9D\uFF84\uFF9E\uFF73" +
- ";\uFF9B\uFF72\uFF84\uFF9E\u0031;;;"),
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("ADR",
- (";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
- "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
- "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
- "\u968E;;;;150-8512;"),
- Arrays.asList("",
- "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
- "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
- "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
- "\u0036\u968E", "", "", "", "150-8512", ""),
- null, contentValuesForQP,
- new HashSet<String>(Arrays.asList("HOME")), null),
- new PropertyNode("NOTE", "\u30E1\u30E2",
- null, null, contentValuesForQP, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-
- public void testV21MultipleEntryCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V21();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v21_multiple_entry);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(3, builder.vNodeList.size());
- ContentValues contentValuesForShiftJis = new ContentValues();
- contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "9",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-SECRET")), null),
- new PropertyNode("TEL", "10",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-HOTEL")), null),
- new PropertyNode("TEL", "11",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-SCHOOL")), null),
- new PropertyNode("TEL", "12",
- null, null, null,
- new HashSet<String>(Arrays.asList("FAX", "HOME")), null));
- verifier.verify(builder.vNodeList.get(0));
-
- verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "13",
- null, null, null,
- new HashSet<String>(Arrays.asList("MODEM")), null),
- new PropertyNode("TEL", "14",
- null, null, null,
- new HashSet<String>(Arrays.asList("PAGER")), null),
- new PropertyNode("TEL", "15",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-FAMILY")), null),
- new PropertyNode("TEL", "16",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-GIRL")), null));
- verifier.verify(builder.vNodeList.get(1));
- verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "2.1",
- null, null, null, null, null),
- new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
- Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
- null, contentValuesForShiftJis, null, null),
- new PropertyNode("SOUND",
- "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
- null, null, contentValuesForShiftJis,
- new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
- new PropertyNode("TEL", "17",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-BOY")), null),
- new PropertyNode("TEL", "18",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-FRIEND")), null),
- new PropertyNode("TEL", "19",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-PHS")), null),
- new PropertyNode("TEL", "20",
- null, null, null,
- new HashSet<String>(Arrays.asList("X-NEC-RESTAURANT")), null));
- verifier.verify(builder.vNodeList.get(2));
- }
-
- public void testV30SimpleCase() throws IOException, VCardException {
- VCardParser_V21 parser = new VCardParser_V30();
- VDataBuilder builder = new VDataBuilder();
- InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple);
- assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
- is.close();
- assertEquals(1, builder.vNodeList.size());
- PropertyNodesVerifier verifier = new PropertyNodesVerifier(
- new PropertyNode("VERSION", "3.0",
- null, null, null, null, null),
- new PropertyNode("FN", "And Roid",
- null, null, null, null, null),
- new PropertyNode("N", "And;Roid;;;",
- Arrays.asList("And", "Roid", "", "", ""),
- null, null, null, null),
- new PropertyNode("ORG", "Open;Handset; Alliance",
- Arrays.asList("Open", "Handset", " Alliance"),
- null, null, null, null),
- new PropertyNode("SORT-STRING", "android", null, null, null, null, null),
- new PropertyNode("TEL", "0300000000",
- null, null, null,
- new HashSet<String>(Arrays.asList("PREF", "VOICE")), null),
- new PropertyNode("CLASS", "PUBLIC", null, null, null, null, null),
- new PropertyNode("X-GNO", "0", null, null, null, null, null),
- new PropertyNode("X-GN", "group0", null, null, null, null, null),
- new PropertyNode("X-REDUCTION", "0",
- null, null, null, null, null),
- new PropertyNode("REV", "20081031T065854Z",
- null, null, null, null, null));
- verifier.verify(builder.vNodeList.get(0));
- }
-}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java
new file mode 100644
index 0000000..1e4f161
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/accounts/AccountManagerServiceTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests.accounts;
+
+import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.test.IsolatedContext;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.content.*;
+import android.accounts.Account;
+import android.accounts.AccountManagerService;
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class AccountManagerServiceTest extends AndroidTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ final String filenamePrefix = "test.";
+ MockContentResolver resolver = new MockContentResolver();
+ RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+ new MockContext(), // The context that most methods are delegated to
+ getContext(), // The context that file methods are delegated to
+ filenamePrefix);
+ Context context = new IsolatedContext(resolver, targetContextWrapper);
+ setContext(context);
+ }
+
+ public class AccountSorter implements Comparator<Account> {
+ public int compare(Account object1, Account object2) {
+ if (object1 == object2) return 0;
+ if (object1 == null) return 1;
+ if (object2 == null) return -1;
+ int result = object1.type.compareTo(object2.type);
+ if (result != 0) return result;
+ return object1.name.compareTo(object2.name);
+ }
+ }
+
+ public void testCheckAddAccount() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a21 = new Account("account2", "type1");
+ Account a31 = new Account("account3", "type1");
+ Account a12 = new Account("account1", "type2");
+ Account a22 = new Account("account2", "type2");
+ Account a32 = new Account("account3", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+ ams.addAccount(a21, "p21", null);
+ ams.addAccount(a22, "p22", null);
+ ams.addAccount(a31, "p31", null);
+ ams.addAccount(a32, "p32", null);
+
+ Account[] accounts = ams.getAccounts(null);
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(6, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a21, accounts[1]);
+ assertEquals(a31, accounts[2]);
+ assertEquals(a12, accounts[3]);
+ assertEquals(a22, accounts[4]);
+ assertEquals(a32, accounts[5]);
+
+ accounts = ams.getAccountsByType("type1" );
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(3, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a21, accounts[1]);
+ assertEquals(a31, accounts[2]);
+
+ ams.removeAccount(null, a21);
+
+ accounts = ams.getAccountsByType("type1" );
+ Arrays.sort(accounts, new AccountSorter());
+ assertEquals(2, accounts.length);
+ assertEquals(a11, accounts[0]);
+ assertEquals(a31, accounts[1]);
+ }
+
+ public void testPasswords() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a12 = new Account("account1", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+
+ assertEquals("p11", ams.getPassword(a11));
+ assertEquals("p12", ams.getPassword(a12));
+
+ ams.setPassword(a11, "p11b");
+
+ assertEquals("p11b", ams.getPassword(a11));
+ assertEquals("p12", ams.getPassword(a12));
+ }
+
+ public void testUserdata() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Bundle u11 = new Bundle();
+ u11.putString("a", "a_a11");
+ u11.putString("b", "b_a11");
+ u11.putString("c", "c_a11");
+ Account a12 = new Account("account1", "type2");
+ Bundle u12 = new Bundle();
+ u12.putString("a", "a_a12");
+ u12.putString("b", "b_a12");
+ u12.putString("c", "c_a12");
+ ams.addAccount(a11, "p11", u11);
+ ams.addAccount(a12, "p12", u12);
+
+ assertEquals("a_a11", ams.getUserData(a11, "a"));
+ assertEquals("b_a11", ams.getUserData(a11, "b"));
+ assertEquals("c_a11", ams.getUserData(a11, "c"));
+ assertEquals("a_a12", ams.getUserData(a12, "a"));
+ assertEquals("b_a12", ams.getUserData(a12, "b"));
+ assertEquals("c_a12", ams.getUserData(a12, "c"));
+
+ ams.setUserData(a11, "b", "b_a11b");
+
+ assertEquals("a_a11", ams.getUserData(a11, "a"));
+ assertEquals("b_a11b", ams.getUserData(a11, "b"));
+ assertEquals("c_a11", ams.getUserData(a11, "c"));
+ assertEquals("a_a12", ams.getUserData(a12, "a"));
+ assertEquals("b_a12", ams.getUserData(a12, "b"));
+ assertEquals("c_a12", ams.getUserData(a12, "c"));
+ }
+
+ public void testAuthtokens() throws Exception {
+ AccountManagerService ams = new AccountManagerService(getContext());
+ Account a11 = new Account("account1", "type1");
+ Account a12 = new Account("account1", "type2");
+ ams.addAccount(a11, "p11", null);
+ ams.addAccount(a12, "p12", null);
+
+ ams.setAuthToken(a11, "att1", "a11_att1");
+ ams.setAuthToken(a11, "att2", "a11_att2");
+ ams.setAuthToken(a11, "att3", "a11_att3");
+ ams.setAuthToken(a12, "att1", "a12_att1");
+ ams.setAuthToken(a12, "att2", "a12_att2");
+ ams.setAuthToken(a12, "att3", "a12_att3");
+
+ assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+ assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+ assertEquals("a11_att3", ams.peekAuthToken(a11, "att3"));
+ assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+ assertEquals("a12_att2", ams.peekAuthToken(a12, "att2"));
+ assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+ ams.setAuthToken(a11, "att3", "a11_att3b");
+ ams.invalidateAuthToken(a12.type, "a12_att2");
+
+ assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+ assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+ assertEquals("a11_att3b", ams.peekAuthToken(a11, "att3"));
+ assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+ assertNull(ams.peekAuthToken(a12, "att2"));
+ assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+ assertNull(ams.readAuthTokenFromDatabase(a12, "att2"));
+ }
+} \ No newline at end of file
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
new file mode 100644
index 0000000..0ee74df
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/PropertyNode.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.unit_tests.vcard;
+
+import android.content.ContentValues;
+
+import org.apache.commons.codec.binary.Base64;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.regex.Pattern;
+
+/**
+ * @hide old class just for test
+ */
+public class PropertyNode {
+ public String propName;
+ public String propValue;
+ public List<String> propValue_vector;
+
+ /** Store value as byte[],after decode.
+ * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
+ */
+ public byte[] propValue_bytes;
+
+ /** param store: key=paramType, value=paramValue
+ * Note that currently PropertyNode class does not support multiple param-values
+ * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
+ * one String value like "A,B", not ["A", "B"]...
+ * TODO: fix this.
+ */
+ public ContentValues paramMap;
+
+ /** Only for TYPE=??? param store. */
+ public Set<String> paramMap_TYPE;
+
+ /** Store group values. Used only in VCard. */
+ public Set<String> propGroupSet;
+
+ public PropertyNode() {
+ propName = "";
+ propValue = "";
+ propValue_vector = new ArrayList<String>();
+ paramMap = new ContentValues();
+ paramMap_TYPE = new HashSet<String>();
+ propGroupSet = new HashSet<String>();
+ }
+
+ public PropertyNode(
+ String propName, String propValue, List<String> propValue_vector,
+ byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
+ Set<String> propGroupSet) {
+ if (propName != null) {
+ this.propName = propName;
+ } else {
+ this.propName = "";
+ }
+ if (propValue != null) {
+ this.propValue = propValue;
+ } else {
+ this.propValue = "";
+ }
+ if (propValue_vector != null) {
+ this.propValue_vector = propValue_vector;
+ } else {
+ this.propValue_vector = new ArrayList<String>();
+ }
+ this.propValue_bytes = propValue_bytes;
+ if (paramMap != null) {
+ this.paramMap = paramMap;
+ } else {
+ this.paramMap = new ContentValues();
+ }
+ if (paramMap_TYPE != null) {
+ this.paramMap_TYPE = paramMap_TYPE;
+ } else {
+ this.paramMap_TYPE = new HashSet<String>();
+ }
+ if (propGroupSet != null) {
+ this.propGroupSet = propGroupSet;
+ } else {
+ this.propGroupSet = new HashSet<String>();
+ }
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PropertyNode)) {
+ return false;
+ }
+
+ PropertyNode node = (PropertyNode)obj;
+
+ if (propName == null || !propName.equals(node.propName)) {
+ return false;
+ } else if (!paramMap.equals(node.paramMap)) {
+ return false;
+ } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+ return false;
+ } else if (!propGroupSet.equals(node.propGroupSet)) {
+ return false;
+ }
+
+ if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
+ return true;
+ } else {
+ // Log.d("@@@", propValue + ", " + node.propValue);
+ if (!propValue.equals(node.propValue)) {
+ return false;
+ }
+
+ // The value in propValue_vector is not decoded even if it should be
+ // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
+ // is 1, the encoded value is stored in propValue, so we do not have to
+ // check it.
+ return (propValue_vector.equals(node.propValue_vector) ||
+ propValue_vector.size() == 1 ||
+ node.propValue_vector.size() == 1);
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("propName: ");
+ builder.append(propName);
+ builder.append(", paramMap: ");
+ builder.append(paramMap.toString());
+ builder.append(", propmMap_TYPE: ");
+ builder.append(paramMap_TYPE.toString());
+ builder.append(", propGroupSet: ");
+ builder.append(propGroupSet.toString());
+ if (propValue_vector != null && propValue_vector.size() > 1) {
+ builder.append(", propValue_vector size: ");
+ builder.append(propValue_vector.size());
+ }
+ if (propValue_bytes != null) {
+ builder.append(", propValue_bytes size: ");
+ builder.append(propValue_bytes.length);
+ }
+ builder.append(", propValue: ");
+ builder.append(propValue);
+ return builder.toString();
+ }
+
+ /**
+ * Encode this object into a string which can be decoded.
+ */
+ public String encode() {
+ // PropertyNode#toString() is for reading, not for parsing in the future.
+ // We construct appropriate String here.
+ StringBuilder builder = new StringBuilder();
+ if (propName.length() > 0) {
+ builder.append("propName:[");
+ builder.append(propName);
+ builder.append("],");
+ }
+ int size = propGroupSet.size();
+ if (size > 0) {
+ Set<String> set = propGroupSet;
+ builder.append("propGroup:[");
+ int i = 0;
+ for (String group : set) {
+ // We do not need to double quote groups.
+ // group = 1*(ALPHA / DIGIT / "-")
+ builder.append(group);
+ if (i < size - 1) {
+ builder.append(",");
+ }
+ i++;
+ }
+ builder.append("],");
+ }
+
+ if (paramMap.size() > 0 || paramMap_TYPE.size() > 0) {
+ ContentValues values = paramMap;
+ builder.append("paramMap:[");
+ size = paramMap.size();
+ int i = 0;
+ for (Entry<String, Object> entry : values.valueSet()) {
+ // Assuming param-key does not contain NON-ASCII nor symbols.
+ //
+ // According to vCard 3.0:
+ // param-name = iana-token / x-name
+ builder.append(entry.getKey());
+
+ // param-value may contain any value including NON-ASCIIs.
+ // We use the following replacing rule.
+ // \ -> \\
+ // , -> \,
+ // In String#replaceAll(), "\\\\" means a single backslash.
+ builder.append("=");
+ builder.append(entry.getValue().toString()
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ i++;
+ }
+
+ Set<String> set = paramMap_TYPE;
+ size = paramMap_TYPE.size();
+ if (i > 0 && size > 0) {
+ builder.append(",");
+ }
+ i = 0;
+ for (String type : set) {
+ builder.append("TYPE=");
+ builder.append(type
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size - 1) {
+ builder.append(",");
+ }
+ i++;
+ }
+ builder.append("],");
+ }
+
+ size = propValue_vector.size();
+ if (size > 0) {
+ builder.append("propValue:[");
+ List<String> list = propValue_vector;
+ for (int i = 0; i < size; i++) {
+ builder.append(list.get(i)
+ .replaceAll("\\\\", "\\\\\\\\")
+ .replaceAll(",", "\\\\,"));
+ if (i < size -1) {
+ builder.append(",");
+ }
+ }
+ builder.append("],");
+ }
+
+ return builder.toString();
+ }
+
+ public static PropertyNode decode(String encodedString) {
+ PropertyNode propertyNode = new PropertyNode();
+ String trimed = encodedString.trim();
+ if (trimed.length() == 0) {
+ return propertyNode;
+ }
+ String[] elems = trimed.split("],");
+
+ for (String elem : elems) {
+ int index = elem.indexOf('[');
+ String name = elem.substring(0, index - 1);
+ Pattern pattern = Pattern.compile("(?<!\\\\),");
+ String[] values = pattern.split(elem.substring(index + 1), -1);
+ if (name.equals("propName")) {
+ propertyNode.propName = values[0];
+ } else if (name.equals("propGroupSet")) {
+ for (String value : values) {
+ propertyNode.propGroupSet.add(value);
+ }
+ } else if (name.equals("paramMap")) {
+ ContentValues paramMap = propertyNode.paramMap;
+ Set<String> paramMap_TYPE = propertyNode.paramMap_TYPE;
+ for (String value : values) {
+ String[] tmp = value.split("=", 2);
+ String mapKey = tmp[0];
+ // \, -> ,
+ // \\ -> \
+ // In String#replaceAll(), "\\\\" means a single backslash.
+ String mapValue =
+ tmp[1].replaceAll("\\\\,", ",").replaceAll("\\\\\\\\", "\\\\");
+ if (mapKey.equalsIgnoreCase("TYPE")) {
+ paramMap_TYPE.add(mapValue);
+ } else {
+ paramMap.put(mapKey, mapValue);
+ }
+ }
+ } else if (name.equals("propValue")) {
+ StringBuilder builder = new StringBuilder();
+ List<String> list = propertyNode.propValue_vector;
+ int length = values.length;
+ for (int i = 0; i < length; i++) {
+ String normValue = values[i]
+ .replaceAll("\\\\,", ",")
+ .replaceAll("\\\\\\\\", "\\\\");
+ list.add(normValue);
+ builder.append(normValue);
+ if (i < length - 1) {
+ builder.append(";");
+ }
+ }
+ propertyNode.propValue = builder.toString();
+ }
+ }
+
+ // At this time, QUOTED-PRINTABLE is already decoded to Java String.
+ // We just need to decode BASE64 String to binary.
+ String encoding = propertyNode.paramMap.getAsString("ENCODING");
+ if (encoding != null &&
+ (encoding.equalsIgnoreCase("BASE64") ||
+ encoding.equalsIgnoreCase("B"))) {
+ propertyNode.propValue_bytes =
+ Base64.decodeBase64(propertyNode.propValue_vector.get(0).getBytes());
+ }
+
+ return propertyNode;
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
new file mode 100644
index 0000000..7589ba8
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VCardTests.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.ContactStruct;
+import android.pim.vcard.EntryHandler;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.test.AndroidTestCase;
+
+import com.android.unit_tests.R;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+public class VCardTests extends AndroidTestCase {
+
+ // TODO: Use EntityIterator, which is added in Eclair.
+ private static class EntryHolder implements EntryHandler {
+ public List<ContactStruct> contacts = new ArrayList<ContactStruct>();
+ public void onParsingStart() {
+ }
+ public void onEntryCreated(ContactStruct contactStruct) {
+ contacts.add(contactStruct);
+ }
+ public void onParsingEnd() {
+ }
+ }
+ /*
+ static void verify(ContactStruct expected, ContactStruct actual) {
+ if (!equalsString(expected.getName(), actual.getName())) {
+ fail(String.format("Names do not equal: \"%s\" != \"%s\"",
+ expected.getName(), actual.getName()));
+ }
+ if (!equalsString(
+ expected.getPhoneticName(), actual.getPhoneticName())) {
+ fail(String.format("Phonetic names do not equal: \"%s\" != \"%s\"",
+ expected.getPhoneticName(), actual.getPhoneticName()));
+ }
+ {
+ final byte[] expectedPhotoBytes = expected.getPhotoBytes();
+ final byte[] actualPhotoBytes = actual.getPhotoBytes();
+ if (!((expectedPhotoBytes == null && actualPhotoBytes == null) ||
+ Arrays.equals(expectedPhotoBytes, actualPhotoBytes))) {
+ fail("photoBytes is not equal.");
+ }
+ }
+ verifyInternal(expected.getNotes(), actual.getNotes(), "notes");
+ verifyInternal(expected.getPhoneList(), actual.getPhoneList(), "phones");
+ verifyInternal(expected.getContactMethodList(), actual.getContactMethodList(),
+ "contact lists");
+ verifyInternal(expected.getOrganizationList(), actual.getOrganizationList(),
+ "organizations");
+ {
+ final Map<String, List<String>> expectedMap =
+ expected.getExtensionMap();
+ final Map<String, List<String>> actualMap =
+ actual.getExtensionMap();
+ if (verifySize((expectedMap == null ? 0 : expectedMap.size()),
+ (actualMap == null ? 0 : actualMap.size()), "extensions") > 0) {
+ for (String key : expectedMap.keySet()) {
+ if (!actualMap.containsKey(key)) {
+ fail(String.format(
+ "Actual does not have %s extension while expected has",
+ key));
+ }
+ final List<String> expectedList = expectedMap.get(key);
+ final List<String> actualList = actualMap.get(key);
+ verifyInternal(expectedList, actualList,
+ String.format("extension \"%s\"", key));
+ }
+ }
+ }
+ }
+
+ private static boolean equalsString(String a, String b) {
+ if (a == null || a.length() == 0) {
+ return b == null || b.length() == 0;
+ } else {
+ return a.equals(b);
+ }
+ }
+
+ private static int verifySize(int expectedSize, int actualSize, String name) {
+ if (expectedSize != actualSize) {
+ fail(String.format("Size of %s is different: %d != %d",
+ name, expectedSize, actualSize));
+ }
+ return expectedSize;
+ }
+
+ private static <T> void verifyInternal(final List<T> expected, final List<T> actual,
+ String name) {
+ if(verifySize((expected == null ? 0 : expected.size()),
+ (actual == null ? 0 : actual.size()), name) > 0) {
+ int size = expected.size();
+ for (int i = 0; i < size; i++) {
+ final T expectedObj = expected.get(i);
+ final T actualObj = actual.get(i);
+ if (!expected.equals(actual)) {
+ fail(String.format("The %i %s are different: %s != %s",
+ i, name, expectedObj, actualObj));
+ }
+ }
+ }
+ }*/
+
+ private class PropertyNodesVerifier {
+ private HashMap<String, ArrayList<PropertyNode>> mPropertyNodeMap;
+ public PropertyNodesVerifier(PropertyNode... nodes) {
+ mPropertyNodeMap = new HashMap<String, ArrayList<PropertyNode>>();
+ for (PropertyNode propertyNode : nodes) {
+ String propName = propertyNode.propName;
+ ArrayList<PropertyNode> expectedNodes =
+ mPropertyNodeMap.get(propName);
+ if (expectedNodes == null) {
+ expectedNodes = new ArrayList<PropertyNode>();
+ mPropertyNodeMap.put(propName, expectedNodes);
+ }
+ expectedNodes.add(propertyNode);
+ }
+ }
+
+ public void verify(VNode vnode) {
+ for (PropertyNode propertyNode : vnode.propList) {
+ String propName = propertyNode.propName;
+ ArrayList<PropertyNode> nodes = mPropertyNodeMap.get(propName);
+ if (nodes == null) {
+ fail("Unexpected propName \"" + propName + "\" exists.");
+ }
+ boolean successful = false;
+ int size = nodes.size();
+ for (int i = 0; i < size; i++) {
+ PropertyNode expectedNode = nodes.get(i);
+ if (expectedNode.propName.equals(propName)) {
+ if (expectedNode.equals(propertyNode)) {
+ successful = true;
+ nodes.remove(i);
+ if (nodes.size() == 0) {
+ mPropertyNodeMap.remove(propName);
+ }
+ break;
+ } else {
+ fail("Property \"" + propName + "\" has wrong value.\n"
+ + "expected: " + expectedNode.toString()
+ + "\n actual: " + propertyNode.toString());
+ }
+ }
+ }
+ if (!successful) {
+ fail("Unexpected property \"" + propName + "\" exists.");
+ }
+ }
+ if (mPropertyNodeMap.size() != 0) {
+ ArrayList<String> expectedProps = new ArrayList<String>();
+ for (ArrayList<PropertyNode> nodes : mPropertyNodeMap.values()) {
+ for (PropertyNode node : nodes) {
+ expectedProps.add(node.propName);
+ }
+ }
+ fail("expected props " + Arrays.toString(expectedProps.toArray()) +
+ " was not found");
+ }
+ }
+ }
+
+ /*
+ public void testV21SimpleCase1_1() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Roid Ando", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase1_2() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_JAPANESE);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_1);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase2() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder.addEntryHandler(holder);
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_2);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, holder.contacts.size());
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ holder.contacts.get(0));
+ }
+
+ public void testV21SimpleCase3() throws IOException, VCardException {
+ VCardParser parser = new VCardParser_V21();
+ VCardDataBuilder builder1 = new VCardDataBuilder(VCardConfig.NAME_ORDER_TYPE_ENGLISH);
+ EntryHolder holder = new EntryHolder();
+ builder1.addEntryHandler(holder);
+ VNodeBuilder builder2 = new VNodeBuilder();
+ VCardBuilderCollection collection =
+ new VCardBuilderCollection(
+ new ArrayList<VCardBuilder>(Arrays.asList(builder1, builder2)));
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_simple_3);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", collection));
+ is.close();
+
+ assertEquals(1, builder2.vNodeList.size());
+ VNode vnode = builder2.vNodeList.get(0);
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("N", "Ando;Roid;",
+ Arrays.asList("Ando", "Roid", ""),
+ null, null, null, null),
+ new PropertyNode("FN", "Ando Roid",
+ null, null, null, null, null));
+ verifier.verify(vnode);
+
+ // FN is prefered.
+ assertEquals(1, holder.contacts.size());
+ ContactStruct actual = holder.contacts.get(0);
+ verify(new ContactStruct("Ando Roid", null,
+ null, null, null, null, null, null),
+ actual);
+ }*/
+
+ public void testV21BackslashCase() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_backslash);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", ";A;B\\;C\\;;D;:E;\\\\;",
+ Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""),
+ null, null, null, null),
+ new PropertyNode("FN", "A;B\\C\\;D:E\\\\",
+ null, null, null, null, null));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21ComplicatedCase() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_complicated);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ ContentValues contentValuesForPhoto = new ContentValues();
+ contentValuesForPhoto.put("ENCODING", "BASE64");
+ // Push data into int array at first since values like 0x80 are
+ // interpreted as int by the compiler and casting all of them is
+ // cumbersome...
+ int[] photoIntArray = {
+ 0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
+ 0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
+ 0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
+ 0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
+ 0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+ 0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+ 0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
+ 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
+ 0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
+ 0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
+ 0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
+ 0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
+ 0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
+ 0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
+ 0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
+ 0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
+ 0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
+ 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
+ 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
+ 0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
+ 0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
+ 0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
+ 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
+ 0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+ 0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
+ 0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
+ 0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
+ 0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
+ 0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
+ 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
+ 0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
+ 0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
+ 0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+ 0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
+ 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
+ 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
+ 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
+ 0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
+ 0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
+ 0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
+ 0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
+ 0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
+ 0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
+ 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+ 0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
+ 0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
+ 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
+ 0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
+ 0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
+ 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
+ 0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
+ 0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
+ 0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+ 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
+ 0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
+ 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
+ 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
+ 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+ 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+ 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+ 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
+ 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
+ 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+ 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+ 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
+ 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+ 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
+ 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
+ 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
+ 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
+ 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+ 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+ 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+ 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+ 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+ 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+ 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
+ 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
+ 0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
+ 0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
+ 0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
+ 0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
+ 0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
+ 0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
+ 0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
+ 0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
+ 0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
+ 0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
+ 0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
+ 0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
+ 0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
+ 0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
+ 0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
+ 0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
+ 0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
+ 0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
+ 0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
+ 0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
+ 0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
+ 0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
+ 0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
+ 0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
+ 0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
+ 0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
+ 0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
+ 0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
+ 0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
+ 0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
+ 0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
+ 0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
+ 0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
+ 0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
+ 0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
+ 0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
+ 0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
+ 0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
+ 0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
+ 0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
+ 0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
+ 0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
+ 0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
+ 0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
+ 0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
+ 0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
+ 0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
+ 0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
+ 0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
+ 0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
+ 0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
+ 0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
+ 0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
+ 0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
+ 0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
+ 0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
+ 0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
+ 0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
+ 0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
+ 0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
+ 0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
+ 0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
+ 0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
+ 0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
+ 0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
+ 0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
+ 0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
+ 0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
+ 0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
+ 0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
+ 0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
+ 0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
+ 0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
+ 0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
+ 0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
+ 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
+ 0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
+ 0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
+ 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
+ 0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
+ 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
+ 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
+ 0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+ 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
+ 0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
+ 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
+ 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+ 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
+ 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+ 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
+ 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
+ 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+ 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+ 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+ 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+ 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+ 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+ 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+ 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
+ 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
+ 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+ 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+ 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+ 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+ 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
+ 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
+ 0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
+ 0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
+ 0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
+ 0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
+ 0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
+ 0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
+ 0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
+ 0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
+ 0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
+ 0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
+ 0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
+ 0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
+ 0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
+ 0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
+ 0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
+ 0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
+ 0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
+ 0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
+ 0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
+ 0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
+ 0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
+ 0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
+ 0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
+ 0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
+ 0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
+ 0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
+ 0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
+ 0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
+ 0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
+ 0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
+ 0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
+ 0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
+ 0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
+ 0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
+ 0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
+ 0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
+ 0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
+ 0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
+ 0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
+ 0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
+ 0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
+ 0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
+ 0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
+ 0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
+ 0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
+ 0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
+ 0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
+ 0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
+ 0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
+ 0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
+ 0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
+ 0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
+ 0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
+ 0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
+ 0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
+ 0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
+ 0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
+ 0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
+ 0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
+ 0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
+ 0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
+ 0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
+ 0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
+ 0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
+ 0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
+ 0x0c, 0xd1, 0x00, 0xff, 0xd9};
+ int length = photoIntArray.length;
+ byte[] photoByteArray = new byte[length];
+ for (int i = 0; i < length; i++) {
+ photoByteArray[i] = (byte)photoIntArray[i];
+ }
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "Gump;Forrest;Hoge;Pos;Tao",
+ Arrays.asList("Gump", "Forrest",
+ "Hoge", "Pos", "Tao"),
+ null, null, null, null),
+ new PropertyNode("FN", "Joe Due",
+ null, null, null, null, null),
+ new PropertyNode("ORG",
+ "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
+ Arrays.asList("Gump Shrimp Co.",
+ "Sales Dept.;Manager",
+ "Fish keeper"),
+ null, null, null, null),
+ new PropertyNode("ROLE", "Fish Cake Keeper!",
+ null, null, null, null, null),
+ new PropertyNode("TITLE", "Shrimp Man",
+ null, null, null, null, null),
+ new PropertyNode("X-CLASS", "PUBLIC",
+ null, null, null, null, null),
+ new PropertyNode("TEL", "(111) 555-1212",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("WORK", "VOICE")), null),
+ new PropertyNode("TEL", "(404) 555-1212",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("HOME", "VOICE")), null),
+ new PropertyNode("TEL", "0311111111",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("CELL")), null),
+ new PropertyNode("TEL", "0322222222",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("VIDEO")), null),
+ new PropertyNode("TEL", "0333333333",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("VOICE")), null),
+ new PropertyNode("ADR",
+ ";;100 Waters Edge;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "100 Waters Edge", "Baytown",
+ "LA", "30314", "United States of America"),
+ null, null,
+ new HashSet<String>(Arrays.asList("WORK")), null),
+ new PropertyNode("LABEL",
+ "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, contentValuesForQP,
+ new HashSet<String>(Arrays.asList("WORK")), null),
+ new PropertyNode("ADR",
+ ";;42 Plantation St.;Baytown;LA;30314;United States of America",
+ Arrays.asList("", "", "42 Plantation St.", "Baytown",
+ "LA", "30314", "United States of America"), null, null,
+ new HashSet<String>(Arrays.asList("HOME")), null),
+ new PropertyNode("LABEL",
+ "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited States of America",
+ null, null, contentValuesForQP,
+ new HashSet<String>(Arrays.asList("HOME")), null),
+ new PropertyNode("EMAIL", "forrestgump@walladalla.com",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("PREF", "INTERNET")), null),
+ new PropertyNode("EMAIL", "cell@example.com",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("CELL")), null),
+ new PropertyNode("NOTE", "The following note is the example from RFC 2045.",
+ null, null, null, null, null),
+ new PropertyNode("NOTE",
+ "Now's the time for all folk to come to the aid of their country.",
+ null, null, contentValuesForQP, null, null),
+ new PropertyNode("PHOTO", null,
+ null, photoByteArray, contentValuesForPhoto,
+ new HashSet<String>(Arrays.asList("JPEG")), null),
+ new PropertyNode("X-ATTRIBUTE", "Some String",
+ null, null, null, null, null),
+ new PropertyNode("BDAY", "19800101",
+ null, null, null, null, null),
+ new PropertyNode("GEO", "35.6563854,139.6994233",
+ null, null, null, null, null),
+ new PropertyNode("URL", "http://www.example.com/",
+ null, null, null, null, null),
+ new PropertyNode("REV", "20080424T195243Z",
+ null, null, null, null, null));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21Japanese1() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_1);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ contentValuesForQP.put("CHARSET", "SHIFT_JIS");
+ // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
+ // vCard 2.1/3.0 specification does not allow multiple values.
+ // Do not need to handle it as multiple values.
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null),
+ new PropertyNode("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
+ null, null, contentValuesForShiftJis,
+ new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
+ new PropertyNode("TEL", "0300000000",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("VOICE", "PREF")), null));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21Japanese2() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_japanese_2);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ ContentValues contentValuesForQP = new ContentValues();
+ contentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+ contentValuesForQP.put("CHARSET", "SHIFT_JIS");
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
+ Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
+ "", "", ""),
+ null, contentValuesForShiftJis, null, null),
+ new PropertyNode("FN",
+ "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
+ null, null, contentValuesForShiftJis, null, null),
+ new PropertyNode("SOUND",
+ ("\uFF71\uFF9D\uFF84\uFF9E\uFF73" +
+ ";\uFF9B\uFF72\uFF84\uFF9E\u0031;;;"),
+ null, null, contentValuesForShiftJis,
+ new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
+ new PropertyNode("ADR",
+ (";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
+ "\u968E;;;;150-8512;"),
+ Arrays.asList("",
+ "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+ "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+ "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+ "\u0036\u968E", "", "", "", "150-8512", ""),
+ null, contentValuesForQP,
+ new HashSet<String>(Arrays.asList("HOME")), null),
+ new PropertyNode("NOTE", "\u30E1\u30E2",
+ null, null, contentValuesForQP, null, null));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+
+ public void testV21MultipleEntryCase() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V21();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v21_multiple_entry);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(3, builder.vNodeList.size());
+ ContentValues contentValuesForShiftJis = new ContentValues();
+ contentValuesForShiftJis.put("CHARSET", "SHIFT_JIS");
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null),
+ new PropertyNode("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
+ null, null, contentValuesForShiftJis,
+ new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
+ new PropertyNode("TEL", "9",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-SECRET")), null),
+ new PropertyNode("TEL", "10",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-HOTEL")), null),
+ new PropertyNode("TEL", "11",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-SCHOOL")), null),
+ new PropertyNode("TEL", "12",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("FAX", "HOME")), null));
+ verifier.verify(builder.vNodeList.get(0));
+
+ verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null),
+ new PropertyNode("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
+ null, null, contentValuesForShiftJis,
+ new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
+ new PropertyNode("TEL", "13",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("MODEM")), null),
+ new PropertyNode("TEL", "14",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("PAGER")), null),
+ new PropertyNode("TEL", "15",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-FAMILY")), null),
+ new PropertyNode("TEL", "16",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-GIRL")), null));
+ verifier.verify(builder.vNodeList.get(1));
+ verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "2.1",
+ null, null, null, null, null),
+ new PropertyNode("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
+ Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
+ null, contentValuesForShiftJis, null, null),
+ new PropertyNode("SOUND",
+ "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
+ null, null, contentValuesForShiftJis,
+ new HashSet<String>(Arrays.asList("X-IRMC-N")), null),
+ new PropertyNode("TEL", "17",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-BOY")), null),
+ new PropertyNode("TEL", "18",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-FRIEND")), null),
+ new PropertyNode("TEL", "19",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-PHS")), null),
+ new PropertyNode("TEL", "20",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("X-NEC-RESTAURANT")), null));
+ verifier.verify(builder.vNodeList.get(2));
+ }
+
+ public void testV30SimpleCase() throws IOException, VCardException {
+ VCardParser_V21 parser = new VCardParser_V30();
+ VNodeBuilder builder = new VNodeBuilder();
+ InputStream is = getContext().getResources().openRawResource(R.raw.v30_simple);
+ assertEquals(true, parser.parse(is,"ISO-8859-1", builder));
+ is.close();
+ assertEquals(1, builder.vNodeList.size());
+ PropertyNodesVerifier verifier = new PropertyNodesVerifier(
+ new PropertyNode("VERSION", "3.0",
+ null, null, null, null, null),
+ new PropertyNode("FN", "And Roid",
+ null, null, null, null, null),
+ new PropertyNode("N", "And;Roid;;;",
+ Arrays.asList("And", "Roid", "", "", ""),
+ null, null, null, null),
+ new PropertyNode("ORG", "Open;Handset; Alliance",
+ Arrays.asList("Open", "Handset", " Alliance"),
+ null, null, null, null),
+ new PropertyNode("SORT-STRING", "android", null, null, null, null, null),
+ new PropertyNode("TEL", "0300000000",
+ null, null, null,
+ new HashSet<String>(Arrays.asList("PREF", "VOICE")), null),
+ new PropertyNode("CLASS", "PUBLIC", null, null, null, null, null),
+ new PropertyNode("X-GNO", "0", null, null, null, null, null),
+ new PropertyNode("X-GN", "group0", null, null, null, null, null),
+ new PropertyNode("X-REDUCTION", "0",
+ null, null, null, null, null),
+ new PropertyNode("REV", "20081031T065854Z",
+ null, null, null, null, null));
+ verifier.verify(builder.vNodeList.get(0));
+ }
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
new file mode 100644
index 0000000..3eb827b
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNode.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.unit_tests.vcard;
+
+import java.util.ArrayList;
+
+/**
+ * @hide old class. Just for testing
+ */
+public class VNode {
+ public String VName;
+
+ public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
+
+ /** 0:parse over. 1:parsing. */
+ public int parseStatus = 1;
+}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
new file mode 100644
index 0000000..6d69223
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/vcard/VNodeBuilder.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.unit_tests.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardBuilder;
+import android.pim.vcard.VCardConfig;
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Store the parse result to custom datastruct: VNode, PropertyNode
+ * Maybe several vcard instance, so use vNodeList to store.
+ * VNode: standy by a vcard instance.
+ * PropertyNode: standy by a property line of a card.
+ * @hide old class, just for testing use
+ */
+public class VNodeBuilder implements VCardBuilder {
+ static private String LOG_TAG = "VDATABuilder";
+
+ /**
+ * If there's no other information available, this class uses this charset for encoding
+ * byte arrays.
+ */
+ static public String TARGET_CHARSET = "UTF-8";
+
+ /** type=VNode */
+ public List<VNode> vNodeList = new ArrayList<VNode>();
+ private int mNodeListPos = 0;
+ private VNode mCurrentVNode;
+ private PropertyNode mCurrentPropNode;
+ private String mCurrentParamType;
+
+ /**
+ * The charset using which VParser parses the text.
+ */
+ private String mSourceCharset;
+
+ /**
+ * The charset with which byte array is encoded to String.
+ */
+ private String mTargetCharset;
+
+ private boolean mStrictLineBreakParsing;
+
+ public VNodeBuilder() {
+ this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
+ }
+
+ public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
+ this(null, charset, strictLineBreakParsing);
+ }
+
+ /**
+ * @hide sourceCharset is temporal.
+ */
+ public VNodeBuilder(String sourceCharset, String targetCharset,
+ boolean strictLineBreakParsing) {
+ if (sourceCharset != null) {
+ mSourceCharset = sourceCharset;
+ } else {
+ mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+ }
+ if (targetCharset != null) {
+ mTargetCharset = targetCharset;
+ } else {
+ mTargetCharset = TARGET_CHARSET;
+ }
+ mStrictLineBreakParsing = strictLineBreakParsing;
+ }
+
+ public void start() {
+ }
+
+ public void end() {
+ }
+
+ // Note: I guess that this code assumes the Record may nest like this:
+ // START:VPOS
+ // ...
+ // START:VPOS2
+ // ...
+ // END:VPOS2
+ // ...
+ // END:VPOS
+ //
+ // However the following code has a bug.
+ // When error occurs after calling startRecord(), the entry which is probably
+ // the cause of the error remains to be in vNodeList, while endRecord() is not called.
+ //
+ // I leave this code as is since I'm not familiar with vcalendar specification.
+ // But I believe we should refactor this code in the future.
+ // Until this, the last entry has to be removed when some error occurs.
+ public void startRecord(String type) {
+
+ VNode vnode = new VNode();
+ vnode.parseStatus = 1;
+ vnode.VName = type;
+ // I feel this should be done in endRecord(), but it cannot be done because of
+ // the reason above.
+ vNodeList.add(vnode);
+ mNodeListPos = vNodeList.size() - 1;
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void endRecord() {
+ VNode endNode = vNodeList.get(mNodeListPos);
+ endNode.parseStatus = 0;
+ while(mNodeListPos > 0){
+ mNodeListPos--;
+ if((vNodeList.get(mNodeListPos)).parseStatus == 1)
+ break;
+ }
+ mCurrentVNode = vNodeList.get(mNodeListPos);
+ }
+
+ public void startProperty() {
+ mCurrentPropNode = new PropertyNode();
+ }
+
+ public void endProperty() {
+ mCurrentVNode.propList.add(mCurrentPropNode);
+ }
+
+ public void propertyName(String name) {
+ mCurrentPropNode.propName = name;
+ }
+
+ // Used only in VCard.
+ public void propertyGroup(String group) {
+ mCurrentPropNode.propGroupSet.add(group);
+ }
+
+ public void propertyParamType(String type) {
+ mCurrentParamType = type;
+ }
+
+ public void propertyParamValue(String value) {
+ if (mCurrentParamType == null ||
+ mCurrentParamType.equalsIgnoreCase("TYPE")) {
+ mCurrentPropNode.paramMap_TYPE.add(value);
+ } else {
+ mCurrentPropNode.paramMap.put(mCurrentParamType, value);
+ }
+
+ mCurrentParamType = null;
+ }
+
+ private String encodeString(String originalString, String targetCharset) {
+ if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+ return originalString;
+ }
+ Charset charset = Charset.forName(mSourceCharset);
+ ByteBuffer byteBuffer = charset.encode(originalString);
+ // byteBuffer.array() "may" return byte array which is larger than
+ // byteBuffer.remaining(). Here, we keep on the safe side.
+ byte[] bytes = new byte[byteBuffer.remaining()];
+ byteBuffer.get(bytes);
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return null;
+ }
+ }
+
+ private String handleOneValue(String value, String targetCharset, String encoding) {
+ if (encoding != null) {
+ if (encoding.equals("BASE64") || encoding.equals("B")) {
+ // Assume BASE64 is used only when the number of values is 1.
+ mCurrentPropNode.propValue_bytes =
+ Base64.decodeBase64(value.getBytes());
+ return value;
+ } else if (encoding.equals("QUOTED-PRINTABLE")) {
+ String quotedPrintable = value
+ .replaceAll("= ", " ").replaceAll("=\t", "\t");
+ String[] lines;
+ if (mStrictLineBreakParsing) {
+ lines = quotedPrintable.split("\r\n");
+ } else {
+ StringBuilder builder = new StringBuilder();
+ int length = quotedPrintable.length();
+ ArrayList<String> list = new ArrayList<String>();
+ for (int i = 0; i < length; i++) {
+ char ch = quotedPrintable.charAt(i);
+ if (ch == '\n') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ } else if (ch == '\r') {
+ list.add(builder.toString());
+ builder = new StringBuilder();
+ if (i < length - 1) {
+ char nextCh = quotedPrintable.charAt(i + 1);
+ if (nextCh == '\n') {
+ i++;
+ }
+ }
+ } else {
+ builder.append(ch);
+ }
+ }
+ String finalLine = builder.toString();
+ if (finalLine.length() > 0) {
+ list.add(finalLine);
+ }
+ lines = list.toArray(new String[0]);
+ }
+ StringBuilder builder = new StringBuilder();
+ for (String line : lines) {
+ if (line.endsWith("=")) {
+ line = line.substring(0, line.length() - 1);
+ }
+ builder.append(line);
+ }
+ byte[] bytes;
+ try {
+ bytes = builder.toString().getBytes(mSourceCharset);
+ } catch (UnsupportedEncodingException e1) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
+ bytes = builder.toString().getBytes();
+ }
+
+ try {
+ bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+ } catch (DecoderException e) {
+ Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+ return "";
+ }
+
+ try {
+ return new String(bytes, targetCharset);
+ } catch (UnsupportedEncodingException e) {
+ Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+ return new String(bytes);
+ }
+ }
+ // Unknown encoding. Fall back to default.
+ }
+ return encodeString(value, targetCharset);
+ }
+
+ public void propertyValues(List<String> values) {
+ if (values == null || values.size() == 0) {
+ mCurrentPropNode.propValue_bytes = null;
+ mCurrentPropNode.propValue_vector.clear();
+ mCurrentPropNode.propValue_vector.add("");
+ mCurrentPropNode.propValue = "";
+ return;
+ }
+
+ ContentValues paramMap = mCurrentPropNode.paramMap;
+
+ String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET"));
+ String encoding = paramMap.getAsString("ENCODING");
+
+ if (targetCharset == null || targetCharset.length() == 0) {
+ targetCharset = mTargetCharset;
+ }
+
+ for (String value : values) {
+ mCurrentPropNode.propValue_vector.add(
+ handleOneValue(value, targetCharset, encoding));
+ }
+
+ mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
+ }
+
+ private String listToString(List<String> list){
+ int size = list.size();
+ if (size > 1) {
+ StringBuilder typeListB = new StringBuilder();
+ for (String type : list) {
+ typeListB.append(type).append(";");
+ }
+ int len = typeListB.length();
+ if (len > 0 && typeListB.charAt(len - 1) == ';') {
+ return typeListB.substring(0, len - 1);
+ }
+ return typeListB.toString();
+ } else if (size == 1) {
+ return list.get(0);
+ } else {
+ return "";
+ }
+ }
+
+ public String getResult(){
+ return null;
+ }
+}
diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk
new file mode 100644
index 0000000..f2c07b3
--- /dev/null
+++ b/tests/BrowserPowerTest/Android.mk
@@ -0,0 +1,30 @@
+# Copyright 2008, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := BrowserPowerTests
+
+#LOCAL_INSTRUMENTATION_FOR := browserpowertest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/BrowserPowerTest/AndroidManifest.xml b/tests/BrowserPowerTest/AndroidManifest.xml
new file mode 100644
index 0000000..43eeaad
--- /dev/null
+++ b/tests/BrowserPowerTest/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.browserpowertest">
+
+ <!-- We add an application tag here just so that we can indicate that
+ this package needs to link against the android.test library,
+ which is needed when building test cases. -->
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name="PowerTestActivity" android:label="Power">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.TEST" />
+ </intent-filter>
+ </activity>
+ </application>
+ <!--
+ This declares that this app uses the instrumentation test runner targeting
+ the package of browserpowertest. To run the tests use the command:
+ "adb shell am instrument -w com.android.browserpowertest/.PowerTestRunner"
+ -->
+ <instrumentation android:name=".PowerTestRunner"
+ android:targetPackage="com.android.browserpowertest"
+ android:label="Test runner for Browser Power Tests."
+ />
+
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_SDCARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java
new file mode 100644
index 0000000..74ac865
--- /dev/null
+++ b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerMeasurement.java
@@ -0,0 +1,51 @@
+package com.android.browserpowertest;
+
+import android.content.Intent;
+import android.app.Instrumentation;
+import android.os.Handler;
+import android.os.Message;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import junit.framework.*;
+
+public class PowerMeasurement extends ActivityInstrumentationTestCase2<PowerTestActivity> {
+
+ private static final String LOGTAG = "PowerMeasurement";
+ private static final String PKG_NAME = "com.android.browserpowertest";
+ private static final String TESTING_URL = "http://www.espn.com";
+ private static final int TIME_OUT = 2 * 60 * 1000;
+ private static final int DELAY = 0;
+
+ public PowerMeasurement() {
+ super(PKG_NAME, PowerTestActivity.class);
+ }
+
+ public void testPageLoad() throws Throwable {
+ Instrumentation mInst = getInstrumentation();
+ PowerTestActivity act = getActivity();
+
+ Intent intent = new Intent(mInst.getContext(), PowerTestActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ long start = System.currentTimeMillis();
+ PowerTestActivity activity = (PowerTestActivity)mInst.startActivitySync(
+ intent);
+ activity.reset();
+ //send a message with the new URL
+ Handler handler = activity.getHandler();
+ Message msg = handler.obtainMessage(
+ PowerTestActivity.MSG_NAVIGATE, TIME_OUT, DELAY);
+ msg.getData().putString(PowerTestActivity.MSG_NAV_URL, TESTING_URL);
+ msg.getData().putBoolean(PowerTestActivity.MSG_NAV_LOGTIME, true);
+
+ handler.sendMessage(msg);
+ boolean timeoutFlag = activity.waitUntilDone();
+ long end = System.currentTimeMillis();
+ assertFalse(TESTING_URL + " failed to load", timeoutFlag);
+ boolean pageErrorFlag = activity.getPageError();
+ assertFalse(TESTING_URL + " is not available, either network is down or the server is down",
+ pageErrorFlag);
+ Log.v(LOGTAG, "Page is loaded in " + activity.getPageLoadTime() + " ms.");
+
+ activity.finish();
+ }
+}
diff --git a/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestActivity.java b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestActivity.java
new file mode 100644
index 0000000..77e390b
--- /dev/null
+++ b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestActivity.java
@@ -0,0 +1,253 @@
+package com.android.browserpowertest;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.WebSettings.LayoutAlgorithm;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+public class PowerTestActivity extends Activity {
+
+ public static final String LOGTAG = "PowerTestActivity";
+ public static final String PARAM_URL = "URL";
+ public static final String PARAM_TIMEOUT = "Timeout";
+ public static final int RESULT_TIMEOUT = 0xDEAD;
+ public static final int MSG_TIMEOUT = 0xC001;
+ public static final int MSG_NAVIGATE = 0xC002;
+ public static final String MSG_NAV_URL = "url";
+ public static final String MSG_NAV_LOGTIME = "logtime";
+
+ private WebView webView;
+ private SimpleWebViewClient webViewClient;
+ private SimpleChromeClient chromeClient;
+ private Handler handler;
+ private boolean timeoutFlag;
+ private boolean logTime;
+ private boolean pageDone;
+ private Object pageDoneLock;
+ private int pageStartCount;
+ private int manualDelay;
+ private long startTime;
+ private long pageLoadTime;
+ private PageDoneRunner pageDoneRunner = new PageDoneRunner();
+
+ public PowerTestActivity() {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Log.v(LOGTAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
+
+ LinearLayout contentView = new LinearLayout(this);
+ contentView.setOrientation(LinearLayout.VERTICAL);
+ setContentView(contentView);
+ setTitle("Idle");
+
+ webView = new WebView(this);
+ webView.getSettings().setJavaScriptEnabled(true);
+ webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
+ webView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
+
+ webViewClient = new SimpleWebViewClient();
+ chromeClient = new SimpleChromeClient();
+ webView.setWebViewClient(webViewClient);
+ webView.setWebChromeClient(chromeClient);
+
+ contentView.addView(webView, new LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
+
+ handler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TIMEOUT:
+ handleTimeout();
+ return;
+ case MSG_NAVIGATE:
+ manualDelay = msg.arg2;
+ navigate(msg.getData().getString(MSG_NAV_URL), msg.arg1);
+ logTime = msg.getData().getBoolean(MSG_NAV_LOGTIME);
+ return;
+ }
+ }
+ };
+
+ pageDoneLock = new Object();
+ }
+
+ public void reset() {
+ synchronized (pageDoneLock) {
+ pageDone = false;
+ }
+ timeoutFlag = false;
+ pageStartCount = 0;
+ chromeClient.resetJsTimeout();
+ }
+
+ private void navigate(String url, int timeout) {
+ if(url == null) {
+ Log.v(LOGTAG, "URL is null, cancelling...");
+ finish();
+ }
+ webView.stopLoading();
+ if(logTime) {
+ webView.clearCache(true);
+ }
+ startTime = System.currentTimeMillis();
+ Log.v(LOGTAG, "Navigating to URL: " + url);
+ webView.loadUrl(url);
+
+ if(timeout != 0) {
+ //set a timer with specified timeout (in ms)
+ handler.sendMessageDelayed(handler.obtainMessage(MSG_TIMEOUT),
+ timeout);
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.v(LOGTAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+ webView.clearCache(true);
+ webView.destroy();
+ }
+
+ private boolean isPageDone() {
+ synchronized (pageDoneLock) {
+ return pageDone;
+ }
+ }
+
+ private void setPageDone(boolean pageDone) {
+ synchronized (pageDoneLock) {
+ this.pageDone = pageDone;
+ pageDoneLock.notifyAll();
+ }
+ }
+
+ private void handleTimeout() {
+ int progress = webView.getProgress();
+ webView.stopLoading();
+ Log.v(LOGTAG, "Page timeout triggered, progress = " + progress);
+ timeoutFlag = true;
+ handler.postDelayed(pageDoneRunner, manualDelay);
+ }
+
+ public boolean waitUntilDone() {
+ validateNotAppThread();
+ synchronized (pageDoneLock) {
+ while(!isPageDone()) {
+ try {
+ pageDoneLock.wait();
+ } catch (InterruptedException ie) {
+ //no-op
+ }
+ }
+ }
+ return timeoutFlag;
+ }
+
+ public Handler getHandler() {
+ return handler;
+ }
+
+ private final void validateNotAppThread() {
+ if (ActivityThread.currentActivityThread() != null) {
+ throw new RuntimeException(
+ "This method can not be called from the main application thread");
+ }
+ }
+
+ public long getPageLoadTime() {
+ return pageLoadTime;
+ }
+
+ public boolean getPageError() {
+ return webViewClient.getPageErrorFlag();
+ }
+
+ class SimpleWebViewClient extends WebViewClient {
+
+ private boolean pageErrorFlag = false;
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description,
+ String failingUrl) {
+ pageErrorFlag = true;
+ Log.v(LOGTAG, "WebCore error: code=" + errorCode
+ + ", description=" + description
+ + ", url=" + failingUrl);
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ pageStartCount++;
+ Log.v(LOGTAG, "onPageStarted: " + url);
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ Log.v(LOGTAG, "onPageFinished: " + url);
+ // let handleTimeout take care of finishing the page
+ if(!timeoutFlag)
+ handler.postDelayed(new WebViewStatusChecker(), 500);
+ }
+
+ // return true if the URL is not available or the page is down
+ public boolean getPageErrorFlag() {
+ return pageErrorFlag;
+ }
+ }
+
+ class SimpleChromeClient extends WebChromeClient {
+
+ private int timeoutCounter = 0;
+
+ public void resetJsTimeout() {
+ timeoutCounter = 0;
+ }
+
+ @Override
+ public void onReceivedTitle(WebView view, String title) {
+ PowerTestActivity.this.setTitle(title);
+ }
+ }
+
+ class WebViewStatusChecker implements Runnable {
+
+ private int initialStartCount;
+
+ public WebViewStatusChecker() {
+ initialStartCount = pageStartCount;
+ }
+
+ public void run() {
+ if (initialStartCount == pageStartCount && !isPageDone()) {
+ handler.removeMessages(MSG_TIMEOUT);
+ webView.stopLoading();
+ handler.postDelayed(pageDoneRunner, manualDelay);
+ }
+ }
+ }
+
+ class PageDoneRunner implements Runnable {
+
+ public void run() {
+ Log.v(LOGTAG, "Finishing URL: " + webView.getUrl());
+ pageLoadTime = System.currentTimeMillis() - startTime;
+ setPageDone(true);
+ }
+ }
+}
diff --git a/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestRunner.java b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestRunner.java
new file mode 100644
index 0000000..4857209
--- /dev/null
+++ b/tests/BrowserPowerTest/src/com/android/browserpowertest/PowerTestRunner.java
@@ -0,0 +1,31 @@
+package com.android.browserpowertest;
+
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import junit.framework.TestSuite;
+
+
+/**
+ * Instrumentation Test Runner for all browser power tests.
+ *
+ * Running power tests:
+ *
+ * adb shell am instrument \
+ * -w com.android.browserpowertest/.PowerTestRunner
+ */
+
+public class PowerTestRunner extends InstrumentationTestRunner {
+ @Override
+ public TestSuite getAllTests() {
+ TestSuite suite = new InstrumentationTestSuite(this);
+ suite.addTestSuite(PowerMeasurement.class);
+ return suite;
+ }
+
+ @Override
+ public ClassLoader getLoader() {
+ return PowerTestRunner.class.getClassLoader();
+ }
+
+}
diff --git a/tests/BrowserTestPlugin/Android.mk b/tests/BrowserTestPlugin/Android.mk
new file mode 100644
index 0000000..968d9e6
--- /dev/null
+++ b/tests/BrowserTestPlugin/Android.mk
@@ -0,0 +1,36 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build application
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := BrowserTestPlugin
+
+LOCAL_JNI_SHARED_LIBRARIES := libtestplugin
+
+include $(BUILD_PACKAGE)
+
+# ============================================================
+
+# Also build all of the sub-targets under this one: the shared library.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/BrowserTestPlugin/AndroidManifest.xml b/tests/BrowserTestPlugin/AndroidManifest.xml
new file mode 100644
index 0000000..f071ab6
--- /dev/null
+++ b/tests/BrowserTestPlugin/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.testplugin"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.webkit.permission.PLUGIN"/>
+
+ <uses-sdk android:minSdkVersion="3" />
+
+ <application android:icon="@drawable/browser_test_plugin"
+ android:label="Browser Test Plugin">
+ <service android:name="TestPlugin">
+ <intent-filter>
+ <action android:name="android.webkit.PLUGIN" />
+ </intent-filter>
+ </service>
+ </application>
+
+</manifest>
diff --git a/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2 b/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/BrowserTestPlugin/MODULE_LICENSE_APACHE2
diff --git a/tests/BrowserTestPlugin/NOTICE b/tests/BrowserTestPlugin/NOTICE
new file mode 100644
index 0000000..9df2554
--- /dev/null
+++ b/tests/BrowserTestPlugin/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2009, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/tests/BrowserTestPlugin/jni/Android.mk b/tests/BrowserTestPlugin/jni/Android.mk
new file mode 100644
index 0000000..95a21e9
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/Android.mk
@@ -0,0 +1,49 @@
+##
+##
+## Copyright 2009, The Android Open Source Project
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted provided that the following conditions
+## are met:
+## * Redistributions of source code must retain the above copyright
+## notice, this list of conditions and the following disclaimer.
+## * Redistributions in binary form must reproduce the above copyright
+## notice, this list of conditions and the following disclaimer in the
+## documentation and/or other materials provided with the distribution.
+##
+## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+## EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+## PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+## EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+## PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+## PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+## OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+##
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.cpp \
+ PluginObject.cpp \
+ event/EventPlugin.cpp \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/event \
+ external/webkit/WebCore/bridge \
+ external/webkit/WebCore/plugins \
+ external/webkit/WebCore/platform/android/JavaVM \
+ external/webkit/WebKit/android/plugins
+
+LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_MODULE := libtestplugin
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.cpp b/tests/BrowserTestPlugin/jni/PluginObject.cpp
new file mode 100644
index 0000000..68fca60
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/PluginObject.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include "main.h"
+#include "PluginObject.h"
+
+static void pluginInvalidate(NPObject *obj);
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name);
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name);
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant);
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant);
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass);
+static void pluginDeallocate(NPObject *obj);
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name);
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count);
+
+
+
+static NPClass pluginClass = {
+ NP_CLASS_STRUCT_VERSION,
+ pluginAllocate,
+ pluginDeallocate,
+ pluginInvalidate,
+ pluginHasMethod,
+ pluginInvoke,
+ pluginInvokeDefault,
+ pluginHasProperty,
+ pluginGetProperty,
+ pluginSetProperty,
+ pluginRemoveProperty,
+ pluginEnumerate
+};
+
+NPClass *getPluginClass(void)
+{
+ return &pluginClass;
+}
+
+static bool identifiersInitialized = false;
+
+#define ID_TESTFILE_PROPERTY 0
+#define NUM_PROPERTY_IDENTIFIERS 1
+
+static NPIdentifier pluginPropertyIdentifiers[NUM_PROPERTY_IDENTIFIERS];
+static const NPUTF8 *pluginPropertyIdentifierNames[NUM_PROPERTY_IDENTIFIERS] = {
+ "testfile"
+};
+
+#define ID_GETTESTFILE_METHOD 0
+#define NUM_METHOD_IDENTIFIERS 1
+
+static NPIdentifier pluginMethodIdentifiers[NUM_METHOD_IDENTIFIERS];
+static const NPUTF8 *pluginMethodIdentifierNames[NUM_METHOD_IDENTIFIERS] = {
+ "getTestFile"
+};
+
+static void initializeIdentifiers(void)
+{
+ browser->getstringidentifiers(pluginPropertyIdentifierNames, NUM_PROPERTY_IDENTIFIERS, pluginPropertyIdentifiers);
+ browser->getstringidentifiers(pluginMethodIdentifierNames, NUM_METHOD_IDENTIFIERS, pluginMethodIdentifiers);
+}
+
+static bool pluginHasProperty(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_PROPERTY_IDENTIFIERS; i++)
+ if (name == pluginPropertyIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginHasMethod(NPObject *obj, NPIdentifier name)
+{
+ int i;
+ for (i = 0; i < NUM_METHOD_IDENTIFIERS; i++)
+ if (name == pluginMethodIdentifiers[i])
+ return true;
+ return false;
+}
+
+static bool pluginGetProperty(NPObject *obj, NPIdentifier name, NPVariant *variant)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginPropertyIdentifiers[ID_TESTFILE_PROPERTY]) {
+ BOOLEAN_TO_NPVARIANT(true, *variant);
+ return true;
+ }
+ return false;
+}
+
+static bool pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)
+{
+ return false;
+}
+
+static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ PluginObject *plugin = (PluginObject *)obj;
+ if (name == pluginMethodIdentifiers[ID_GETTESTFILE_METHOD]) {
+ return true;
+ }
+ return false;
+}
+
+static bool pluginInvokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result)
+{
+ return false;
+}
+
+static void pluginInvalidate(NPObject *obj)
+{
+ // Release any remaining references to JavaScript objects.
+}
+
+static NPObject *pluginAllocate(NPP npp, NPClass *theClass)
+{
+ PluginObject *newInstance = (PluginObject*) malloc(sizeof(PluginObject));
+ newInstance->header._class = theClass;
+ newInstance->header.referenceCount = 1;
+
+ if (!identifiersInitialized) {
+ identifiersInitialized = true;
+ initializeIdentifiers();
+ }
+
+ newInstance->npp = npp;
+
+ return &newInstance->header;
+}
+
+static void pluginDeallocate(NPObject *obj)
+{
+ free(obj);
+}
+
+static bool pluginRemoveProperty(NPObject *npobj, NPIdentifier name)
+{
+ return false;
+}
+
+static bool pluginEnumerate(NPObject *npobj, NPIdentifier **value, uint32_t *count)
+{
+ return false;
+}
diff --git a/tests/BrowserTestPlugin/jni/PluginObject.h b/tests/BrowserTestPlugin/jni/PluginObject.h
new file mode 100644
index 0000000..a058d4a
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/PluginObject.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in
+ consideration of your agreement to the following terms, and your use, installation,
+ modification or redistribution of this Apple software constitutes acceptance of these
+ terms. If you do not agree with these terms, please do not use, install, modify or
+ redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject to these
+ terms, Apple grants you a personal, non-exclusive license, under AppleÕs copyrights in
+ this original Apple software (the "Apple Software"), to use, reproduce, modify and
+ redistribute the Apple Software, with or without modifications, in source and/or binary
+ forms; provided that if you redistribute the Apple Software in its entirety and without
+ modifications, you must retain this notice and the following text and disclaimers in all
+ such redistributions of the Apple Software. Neither the name, trademarks, service marks
+ or logos of Apple Computer, Inc. may be used to endorse or promote products derived from
+ the Apple Software without specific prior written permission from Apple. Except as expressly
+ stated in this notice, no other rights or licenses, express or implied, are granted by Apple
+ herein, including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES,
+ EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS
+ USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE,
+ REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND
+ WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
+ OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef PluginObject__DEFINED
+#define PluginObject__DEFINED
+
+#include "main.h"
+
+class SubPlugin {
+public:
+ SubPlugin(NPP inst) : m_inst(inst) {}
+ virtual ~SubPlugin() {}
+ virtual int16 handleEvent(const ANPEvent* evt) = 0;
+
+ NPP inst() const { return m_inst; }
+
+private:
+ NPP m_inst;
+};
+
+typedef struct PluginObject {
+ NPObject header;
+ NPP npp;
+ NPWindow* window;
+
+ SubPlugin* subPlugin;
+
+} PluginObject;
+
+NPClass *getPluginClass(void);
+
+#endif // PluginObject__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
new file mode 100644
index 0000000..1263204
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "EventPlugin.h"
+#include "android_npapi.h"
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+
+extern NPNetscapeFuncs* browser;
+extern ANPCanvasInterfaceV0 gCanvasI;
+extern ANPLogInterfaceV0 gLogI;
+extern ANPPaintInterfaceV0 gPaintI;
+extern ANPSurfaceInterfaceV0 gSurfaceI;
+extern ANPTypefaceInterfaceV0 gTypefaceI;
+
+///////////////////////////////////////////////////////////////////////////////
+
+EventPlugin::EventPlugin(NPP inst) : SubPlugin(inst) {
+
+ // initialize the drawing surface
+ m_surfaceReady = false;
+ m_surface = gSurfaceI.newRasterSurface(inst, kRGB_565_ANPBitmapFormat, false);
+ if(!m_surface)
+ gLogI.log(inst, kError_ANPLogType, "----%p Unable to create Raster surface", inst);
+}
+
+EventPlugin::~EventPlugin() {
+ gSurfaceI.deleteSurface(m_surface);
+}
+
+void EventPlugin::drawPlugin(int surfaceWidth, int surfaceHeight) {
+
+ gLogI.log(inst(), kDebug_ANPLogType, " ------ %p drawing the plugin (%d,%d)", inst(), surfaceWidth, surfaceHeight);
+
+ // get the plugin's dimensions according to the DOM
+ PluginObject *obj = (PluginObject*) inst()->pdata;
+ const int W = obj->window->width;
+ const int H = obj->window->height;
+
+ // compute the current zoom level
+ const float zoomFactorW = static_cast<float>(surfaceWidth) / W;
+ const float zoomFactorH = static_cast<float>(surfaceHeight) / H;
+
+ // check to make sure the zoom level is uniform
+ if (zoomFactorW + .01 < zoomFactorH && zoomFactorW - .01 > zoomFactorH)
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p zoom is out of sync (%f,%f)",
+ inst(), zoomFactorW, zoomFactorH);
+
+ // scale the variables based on the zoom level
+ const int fontSize = (int)(zoomFactorW * 16);
+ const int leftMargin = (int)(zoomFactorW * 10);
+
+ // lock the surface
+ ANPBitmap bitmap;
+ if (!m_surfaceReady || !gSurfaceI.lock(m_surface, &bitmap, NULL)) {
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p unable to lock the plugin", inst());
+ return;
+ }
+
+ // create a canvas
+ ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
+ gCanvasI.drawColor(canvas, 0xFFFFFFFF);
+
+ // configure the paint
+ ANPPaint* paint = gPaintI.newPaint();
+ gPaintI.setFlags(paint, gPaintI.getFlags(paint) | kAntiAlias_ANPPaintFlag);
+ gPaintI.setColor(paint, 0xFF0000FF);
+ gPaintI.setTextSize(paint, fontSize);
+
+ // configure the font
+ ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
+ gPaintI.setTypeface(paint, tf);
+ gTypefaceI.unref(tf);
+
+ // retrieve the font metrics
+ ANPFontMetrics fm;
+ gPaintI.getFontMetrics(paint, &fm);
+
+ // write text on the canvas
+ const char c[] = "Browser Test Plugin";
+ gCanvasI.drawText(canvas, c, sizeof(c)-1, leftMargin, -fm.fTop, paint);
+
+ // clean up variables and unlock the surface
+ gPaintI.deletePaint(paint);
+ gCanvasI.deleteCanvas(canvas);
+ gSurfaceI.unlock(m_surface);
+}
+
+void EventPlugin::printToDiv(const char* text, int length) {
+ // Get the plugin's DOM object
+ NPObject* windowObject = NULL;
+ browser->getvalue(inst(), NPNVWindowNPObject, &windowObject);
+
+ if (!windowObject)
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p Unable to retrieve DOM Window", inst());
+
+ // create a string (JS code) that is stored in memory allocated by the browser
+ const char* jsBegin = "var outputDiv = document.getElementById('eventOutput'); outputDiv.innerHTML += ' ";
+ const char* jsEnd = "';";
+
+ // allocate memory and configure pointers
+ int totalLength = strlen(jsBegin) + length + strlen(jsEnd);
+ char* beginMem = (char*)browser->memalloc(totalLength);
+ char* middleMem = beginMem + strlen(jsBegin);
+ char* endMem = middleMem + length;
+
+ // copy into the allocated memory
+ memcpy(beginMem, jsBegin, strlen(jsBegin));
+ memcpy(middleMem, text, length);
+ memcpy(endMem, jsEnd, strlen(jsEnd));
+
+ gLogI.log(inst(), kError_ANPLogType, "text: %.*s\n", totalLength, (char*)beginMem);
+
+ // execute the javascript in the plugin's DOM object
+ NPString script = { (char*)beginMem, totalLength };
+ NPVariant scriptVariant;
+ if (!browser->evaluate(inst(), windowObject, &script, &scriptVariant))
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p Unable to eval the JS.", inst());
+
+ // free the memory allocated within the browser
+ browser->memfree(beginMem);
+}
+
+int16 EventPlugin::handleEvent(const ANPEvent* evt) {
+ switch (evt->eventType) {
+ case kDraw_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request draw events", inst());
+ break;
+ case kSurface_ANPEventType:
+ switch (evt->data.surface.action) {
+ case kCreated_ANPSurfaceAction:
+ m_surfaceReady = true;
+ return 1;
+ case kDestroyed_ANPSurfaceAction:
+ m_surfaceReady = false;
+ return 1;
+ case kChanged_ANPSurfaceAction:
+ drawPlugin(evt->data.surface.data.changed.width,
+ evt->data.surface.data.changed.height);
+ return 1;
+ }
+ break;
+ case kLifecycle_ANPEventType:
+ switch (evt->data.lifecycle.action) {
+ case kOnLoad_ANPLifecycleAction: {
+ char msg[] = "lifecycle-onLoad";
+ printToDiv(msg, strlen(msg));
+ break;
+ }
+ case kGainFocus_ANPLifecycleAction: {
+ char msg[] = "lifecycle-gainFocus";
+ printToDiv(msg, strlen(msg));
+ break;
+ }
+ case kLoseFocus_ANPLifecycleAction: {
+ char msg[] = "lifecycle-loseFocus";
+ printToDiv(msg, strlen(msg));
+ break;
+ }
+ }
+ return 1;
+ case kTouch_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request touch events", inst());
+ break;
+ case kKey_ANPEventType:
+ gLogI.log(inst(), kError_ANPLogType, " ------ %p the plugin did not request key events", inst());
+ break;
+ default:
+ break;
+ }
+ return 0; // unknown or unhandled event
+}
diff --git a/tests/BrowserTestPlugin/jni/event/EventPlugin.h b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
new file mode 100644
index 0000000..73dd6ea
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/event/EventPlugin.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "PluginObject.h"
+
+#ifndef eventPlugin__DEFINED
+#define eventPlugin__DEFINED
+
+class EventPlugin : public SubPlugin {
+public:
+ EventPlugin(NPP inst);
+ virtual ~EventPlugin();
+ virtual int16 handleEvent(const ANPEvent* evt);
+
+private:
+ void drawPlugin(int surfaceWidth, int surfaceHeight);
+ void printToDiv(const char* text, int length);
+
+ bool m_surfaceReady;
+ ANPSurface* m_surface;
+};
+
+#endif // eventPlugin__DEFINED
diff --git a/tests/BrowserTestPlugin/jni/main.cpp b/tests/BrowserTestPlugin/jni/main.cpp
new file mode 100644
index 0000000..056ec4d
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/main.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "android_npapi.h"
+#include "main.h"
+#include "PluginObject.h"
+#include "EventPlugin.h"
+
+NPNetscapeFuncs* browser;
+#define EXPORT __attribute__((visibility("default")))
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved);
+NPError NPP_Destroy(NPP instance, NPSavedData** save);
+NPError NPP_SetWindow(NPP instance, NPWindow* window);
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+int32 NPP_WriteReady(NPP instance, NPStream* stream);
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,
+ void* buffer);
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname);
+void NPP_Print(NPP instance, NPPrint* platformPrint);
+int16 NPP_HandleEvent(NPP instance, void* event);
+void NPP_URLNotify(NPP instance, const char* URL, NPReason reason,
+ void* notifyData);
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value);
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value);
+
+extern "C" {
+EXPORT NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context);
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
+EXPORT const char* NP_GetMIMEDescription(void);
+EXPORT void NP_Shutdown(void);
+};
+
+ANPAudioTrackInterfaceV0 gSoundI;
+ANPBitmapInterfaceV0 gBitmapI;
+ANPCanvasInterfaceV0 gCanvasI;
+ANPLogInterfaceV0 gLogI;
+ANPPaintInterfaceV0 gPaintI;
+ANPPathInterfaceV0 gPathI;
+ANPSurfaceInterfaceV0 gSurfaceI;
+ANPSystemInterfaceV0 gSystemI;
+ANPTypefaceInterfaceV0 gTypefaceI;
+ANPWindowInterfaceV0 gWindowI;
+
+#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0]))
+
+NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env, void *application_context)
+{
+ // Make sure we have a function table equal or larger than we are built against.
+ if (browserFuncs->size < sizeof(NPNetscapeFuncs)) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // Copy the function table (structure)
+ browser = (NPNetscapeFuncs*) malloc(sizeof(NPNetscapeFuncs));
+ memcpy(browser, browserFuncs, sizeof(NPNetscapeFuncs));
+
+ // Build the plugin function table
+ pluginFuncs->version = 11;
+ pluginFuncs->size = sizeof(pluginFuncs);
+ pluginFuncs->newp = NPP_New;
+ pluginFuncs->destroy = NPP_Destroy;
+ pluginFuncs->setwindow = NPP_SetWindow;
+ pluginFuncs->newstream = NPP_NewStream;
+ pluginFuncs->destroystream = NPP_DestroyStream;
+ pluginFuncs->asfile = NPP_StreamAsFile;
+ pluginFuncs->writeready = NPP_WriteReady;
+ pluginFuncs->write = (NPP_WriteProcPtr)NPP_Write;
+ pluginFuncs->print = NPP_Print;
+ pluginFuncs->event = NPP_HandleEvent;
+ pluginFuncs->urlnotify = NPP_URLNotify;
+ pluginFuncs->getvalue = NPP_GetValue;
+ pluginFuncs->setvalue = NPP_SetValue;
+
+ static const struct {
+ NPNVariable v;
+ uint32_t size;
+ ANPInterface* i;
+ } gPairs[] = {
+ { kAudioTrackInterfaceV0_ANPGetValue, sizeof(gSoundI), &gSoundI },
+ { kBitmapInterfaceV0_ANPGetValue, sizeof(gBitmapI), &gBitmapI },
+ { kCanvasInterfaceV0_ANPGetValue, sizeof(gCanvasI), &gCanvasI },
+ { kLogInterfaceV0_ANPGetValue, sizeof(gLogI), &gLogI },
+ { kPaintInterfaceV0_ANPGetValue, sizeof(gPaintI), &gPaintI },
+ { kPathInterfaceV0_ANPGetValue, sizeof(gPathI), &gPathI },
+ { kSurfaceInterfaceV0_ANPGetValue, sizeof(gSurfaceI), &gSurfaceI },
+ { kSystemInterfaceV0_ANPGetValue, sizeof(gSystemI), &gSystemI },
+ { kTypefaceInterfaceV0_ANPGetValue, sizeof(gTypefaceI), &gTypefaceI },
+ { kWindowInterfaceV0_ANPGetValue, sizeof(gWindowI), &gWindowI },
+ };
+ for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {
+ gPairs[i].i->inSize = gPairs[i].size;
+ NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+void NP_Shutdown(void)
+{
+
+}
+
+const char *NP_GetMIMEDescription(void)
+{
+ return "application/x-browsertestplugin:btp:Android Browser Test Plugin";
+}
+
+NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc,
+ char* argn[], char* argv[], NPSavedData* saved)
+{
+
+
+ gLogI.log(instance, kDebug_ANPLogType, "creating plugin");
+
+ PluginObject *obj = NULL;
+
+ // Scripting functions appeared in NPAPI version 14
+ if (browser->version >= 14) {
+ instance->pdata = browser->createobject (instance, getPluginClass());
+ obj = static_cast<PluginObject*>(instance->pdata);
+ bzero(obj, sizeof(*obj));
+ } else {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ // select the drawing model
+ ANPDrawingModel model = kSurface_ANPDrawingModel;
+
+ // notify the plugin API of the drawing model we wish to use. This must be
+ // done prior to creating certain subPlugin objects (e.g. surfaceViews)
+ NPError err = browser->setvalue(instance, kRequestDrawingModel_ANPSetValue,
+ reinterpret_cast<void*>(model));
+ if (err) {
+ gLogI.log(instance, kError_ANPLogType, "request model %d err %d", model, err);
+ return err;
+ }
+
+ // create the sub-plugin
+ obj->subPlugin = new EventPlugin(instance);
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_Destroy(NPP instance, NPSavedData** save)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+ delete obj->subPlugin;
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_SetWindow(NPP instance, NPWindow* window)
+{
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ // Do nothing if browser didn't support NPN_CreateObject which would have created the PluginObject.
+ if (obj != NULL) {
+ obj->window = window;
+ }
+
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype)
+{
+ *stype = NP_ASFILEONLY;
+ return NPERR_NO_ERROR;
+}
+
+NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason)
+{
+ return NPERR_NO_ERROR;
+}
+
+int32 NPP_WriteReady(NPP instance, NPStream* stream)
+{
+ return 0;
+}
+
+int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len, void* buffer)
+{
+ return 0;
+}
+
+void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname)
+{
+}
+
+void NPP_Print(NPP instance, NPPrint* platformPrint)
+{
+}
+
+int16 NPP_HandleEvent(NPP instance, void* event)
+{
+ PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
+ const ANPEvent* evt = reinterpret_cast<const ANPEvent*>(event);
+
+ if(!obj->subPlugin) {
+ gLogI.log(instance, kError_ANPLogType, "the sub-plugin is null.");
+ return 0; // unknown or unhandled event
+ }
+ else {
+ return obj->subPlugin->handleEvent(evt);
+ }
+}
+
+void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData)
+{
+}
+
+EXPORT NPError NP_GetValue(NPP instance, NPPVariable variable, void *value) {
+
+ if (variable == NPPVpluginNameString) {
+ const char **str = (const char **)value;
+ *str = "Browser Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ if (variable == NPPVpluginDescriptionString) {
+ const char **str = (const char **)value;
+ *str = "Description of Browser Test Plugin";
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
+{
+ if (variable == NPPVpluginScriptableNPObject) {
+ void **v = (void **)value;
+ PluginObject *obj = (PluginObject*) instance->pdata;
+
+ if (obj)
+ browser->retainobject((NPObject*)obj);
+
+ *v = obj;
+ return NPERR_NO_ERROR;
+ }
+
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
+{
+ return NPERR_GENERIC_ERROR;
+}
+
diff --git a/tests/BrowserTestPlugin/jni/main.h b/tests/BrowserTestPlugin/jni/main.h
new file mode 100644
index 0000000..e6e8c73
--- /dev/null
+++ b/tests/BrowserTestPlugin/jni/main.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2009, The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <npapi.h>
+#include <npfunctions.h>
+#include <npruntime.h>
+#include "android_npapi.h"
+
+extern NPNetscapeFuncs* browser;
diff --git a/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png b/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
new file mode 100755
index 0000000..47c79d1
--- /dev/null
+++ b/tests/BrowserTestPlugin/res/drawable/browser_test_plugin.png
Binary files differ
diff --git a/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java b/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
new file mode 100644
index 0000000..94a18fd
--- /dev/null
+++ b/tests/BrowserTestPlugin/src/com/android/testplugin/TestPlugin.java
@@ -0,0 +1,15 @@
+package com.android.testplugin;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class TestPlugin extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
diff --git a/tests/CoreTests/android/content/SyncStorageEngineTest.java b/tests/CoreTests/android/content/SyncStorageEngineTest.java
index dee6e38..533338e 100644
--- a/tests/CoreTests/android/content/SyncStorageEngineTest.java
+++ b/tests/CoreTests/android/content/SyncStorageEngineTest.java
@@ -20,6 +20,7 @@ import android.test.AndroidTestCase;
import android.test.RenamingDelegatingContext;
import android.test.mock.MockContext;
import android.test.mock.MockContentResolver;
+import android.accounts.Account;
public class SyncStorageEngineTest extends AndroidTestCase {
@@ -28,7 +29,7 @@ public class SyncStorageEngineTest extends AndroidTestCase {
* correcponding sync is finished. This can happen if the clock changes while we are syncing.
*/
public void testPurgeActiveSync() throws Exception {
- final String account = "a@example.com";
+ final Account account = new Account("a@example.com", "example.type");
final String authority = "testprovider";
MockContentResolver mockResolver = new MockContentResolver();
diff --git a/tests/CoreTests/android/core/RecurrenceSetTest.java b/tests/CoreTests/android/core/RecurrenceSetTest.java
new file mode 100644
index 0000000..cee324c
--- /dev/null
+++ b/tests/CoreTests/android/core/RecurrenceSetTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.core;
+
+import android.content.ContentValues;
+import android.pim.ICalendar;
+import android.pim.RecurrenceSet;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.provider.Calendar;
+import junit.framework.TestCase;
+
+/**
+ * Test some pim.RecurrenceSet functionality.
+ */
+public class RecurrenceSetTest extends TestCase {
+
+ // Test a recurrence
+ @SmallTest
+ public void testRecurrenceSet0() throws Exception {
+ String recurrence = "DTSTART;TZID=America/New_York:20080221T070000\n"
+ + "DTEND;TZID=America/New_York:20080221T190000\n"
+ + "RRULE:FREQ=DAILY;UNTIL=20080222T000000Z\n"
+ + "EXDATE:20080222T120000Z";
+ verifyPopulateContentValues(recurrence, "FREQ=DAILY;UNTIL=20080222T000000Z", null,
+ null, "20080222T120000Z", 1203595200000L, "America/New_York", "P43200S", 0);
+ }
+
+ // Test 1 day all-day event
+ @SmallTest
+ public void testRecurrenceSet1() throws Exception {
+ String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090822\n"
+ + "RRULE:FREQ=YEARLY;WKST=SU";
+ verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
+ null, null, 1250812800000L, null, "P1D", 1);
+ }
+
+ // Test 2 day all-day event
+ @SmallTest
+ public void testRecurrenceSet2() throws Exception {
+ String recurrence = "DTSTART;VALUE=DATE:20090821\nDTEND;VALUE=DATE:20090823\n"
+ + "RRULE:FREQ=YEARLY;WKST=SU";
+ verifyPopulateContentValues(recurrence, "FREQ=YEARLY;WKST=SU", null,
+ null, null, 1250812800000L, null, "P2D", 1);
+ }
+
+ // run populateContentValues and verify the results
+ private void verifyPopulateContentValues(String recurrence, String rrule, String rdate,
+ String exrule, String exdate, long dtstart, String tzid, String duration, int allDay)
+ throws ICalendar.FormatException {
+ ICalendar.Component recurrenceComponent =
+ new ICalendar.Component("DUMMY", null /* parent */);
+ ICalendar.parseComponent(recurrenceComponent, recurrence);
+ ContentValues values = new ContentValues();
+ RecurrenceSet.populateContentValues(recurrenceComponent, values);
+ Log.d("KS", "values " + values);
+
+ assertEquals(rrule, values.get(android.provider.Calendar.Events.RRULE));
+ assertEquals(rdate, values.get(android.provider.Calendar.Events.RDATE));
+ assertEquals(exrule, values.get(android.provider.Calendar.Events.EXRULE));
+ assertEquals(exdate, values.get(android.provider.Calendar.Events.EXDATE));
+ assertEquals(dtstart, (long) values.getAsLong(Calendar.Events.DTSTART));
+ assertEquals(tzid, values.get(android.provider.Calendar.Events.EVENT_TIMEZONE));
+ assertEquals(duration, values.get(android.provider.Calendar.Events.DURATION));
+ assertEquals(allDay,
+ (int) values.getAsInteger(android.provider.Calendar.Events.ALL_DAY));
+ }
+}
diff --git a/tests/CoreTests/android/core/RequestAPITest.java b/tests/CoreTests/android/core/RequestAPITest.java
index d89f5ae..94eb23e 100644
--- a/tests/CoreTests/android/core/RequestAPITest.java
+++ b/tests/CoreTests/android/core/RequestAPITest.java
@@ -72,7 +72,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle =
mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", headers, null,
- null, 0, false);
+ null, 0);
handle.waitUntilComplete();
fail("expected exception not thrown");
@@ -121,7 +121,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
mTestWebServer.setKeepAlive(false);
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", headers, null,
- null, 0, false);
+ null, 0);
handle.waitUntilComplete();
}
@@ -197,7 +197,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testGet - sent request. Waiting");
handle.waitUntilComplete();
@@ -231,11 +231,11 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle0 = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
handle0.waitUntilComplete();
RequestHandle handle1 = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler2,
- null, 0, false);
+ null, 0);
handle1.waitUntilComplete();
/* It's not correct to use same listener for multiple
@@ -270,7 +270,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "HEAD", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testHead - sent request waiting");
handle.waitUntilComplete();
@@ -297,7 +297,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
RequestHandle handle = mRequestQueue.queueRequest(
"http://localhost:8080/test1", "GET", null, testEventHandler,
- null, 0, false);
+ null, 0);
Log.d(LOGTAG, "testChunked - sent request waiting");
handle.waitUntilComplete();
@@ -330,7 +330,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
Log.d(LOGTAG, testName + " start - rq = " + mRequestQueue);
RequestHandle requestHandle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0);
Log.d(LOGTAG, testName + " - sent request waiting");
requestHandle.waitUntilComplete();
@@ -398,10 +398,10 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
leh2.expectHeaders();
RequestHandle handle0 = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "GET", null, testEventHandler, null, 0);
handle0.waitUntilComplete();
RequestHandle handle1 = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "HEAD", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "HEAD", null, testEventHandler, null, 0);
Log.d(LOGTAG, "testGetAndHead - sent request. Waiting");
handle1.waitUntilComplete();
@@ -432,7 +432,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
Log.d(LOGTAG, "testPost start - rq = " + mRequestQueue);
RequestHandle handle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "POST", null, testEventHandler, null, 0, false);
+ "http://localhost:8080/test1", "POST", null, testEventHandler, null, 0);
Log.d(LOGTAG, "testPost - sent request waiting");
handle.waitUntilComplete();
@@ -470,7 +470,7 @@ public class RequestAPITest extends AndroidTestCase implements HttpConstants {
InputStream bodyProvider = new ByteArrayInputStream(mBody.getBytes());
RequestHandle handle = mRequestQueue.queueRequest(
- "http://localhost:8080/test1", "POST", null, testEventHandler, bodyProvider, bodyLength, false);
+ "http://localhost:8080/test1", "POST", null, testEventHandler, bodyProvider, bodyLength);
Log.d(LOGTAG, "testPostWithData - sent request waiting");
handle.waitUntilComplete();
diff --git a/tests/CoreTests/android/webkit/CookieTest.java b/tests/CoreTests/android/webkit/CookieTest.java
index ea4422f..5c5a6a8 100644
--- a/tests/CoreTests/android/webkit/CookieTest.java
+++ b/tests/CoreTests/android/webkit/CookieTest.java
@@ -188,6 +188,6 @@ public class CookieTest extends AndroidTestCase {
mCookieManager.setCookie(url, "a=d");
cookie = mCookieManager.getCookie(url + "/wee");
- assertTrue(cookie.equals("a=b; a=c; a=d"));
+ assertTrue(cookie.equals("a=b; a=d"));
}
}
diff --git a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 577d384..7426d33 100644
--- a/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -309,6 +309,11 @@ public class PhoneNumberUtilsTest extends TestCase {
number.append("800-55512");
PhoneNumberUtils.formatNanpNumber(number);
assertEquals("800-555-12", number.toString());
+
+ number.clear();
+ number.append("46645");
+ PhoneNumberUtils.formatNanpNumber(number);
+ assertEquals("46645", number.toString());
}
@SmallTest
@@ -330,4 +335,76 @@ public class PhoneNumberUtilsTest extends TestCase {
assertEquals("(800) 222-3334",
PhoneNumberUtils.convertKeypadLettersToDigits("(800) ABC-DEFG"));
}
+
+ @SmallTest
+ public void testCheckAndProcessPlusCode() {
+ assertEquals("0118475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
+ assertEquals("18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+18475797000"));
+ assertEquals("0111234567",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+1234567"));
+ assertEquals("01123456700000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+23456700000"));
+ assertEquals("01111875767800",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+11875767800"));
+ assertEquals("8475797000,18475231753",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+18475231753"));
+ assertEquals("0118475797000,18475231753",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000,+18475231753"));
+ assertEquals("8475797000;0118469312345",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;+8469312345"));
+ assertEquals("8475797000,0111234567",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,+1234567"));
+ assertEquals("847597000;01111875767000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847597000;+11875767000"));
+ assertEquals("8475797000,,0118469312345",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,+8469312345"));
+ assertEquals("8475797000;,0118469312345",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+8469312345"));
+ assertEquals("8475797000,;18475231753",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+18475231753"));
+ assertEquals("8475797000;,01111875767000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,+11875767000"));
+ assertEquals("8475797000,;01111875767000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,;+11875767000"));
+ assertEquals("8475797000,,,01111875767000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,,,+11875767000"));
+ assertEquals("8475797000;,,01111875767000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000;,,+11875767000"));
+ assertEquals("+;,8475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+;,8475797000"));
+ assertEquals("8475797000,",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("8475797000,"));
+ assertEquals("847+579-7000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("847+579-7000"));
+ assertEquals(",8475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode(",8475797000"));
+ assertEquals(";;8475797000,,",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode(";;8475797000,,"));
+ assertEquals("+this+is$weird;,+",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+this+is$weird;,+"));
+ assertEquals("",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCode(""));
+ assertNull(PhoneNumberUtils.cdmaCheckAndProcessPlusCode(null));
+ }
+
+ @SmallTest
+ public void testCheckAndProcessPlusCodeByNumberFormat() {
+ assertEquals("18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+ PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_NANP));
+ assertEquals("+18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+ PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_JAPAN));
+ assertEquals("+18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+ PhoneNumberUtils.FORMAT_NANP,PhoneNumberUtils.FORMAT_UNKNOWN));
+ assertEquals("+18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+ PhoneNumberUtils.FORMAT_JAPAN,PhoneNumberUtils.FORMAT_JAPAN));
+ assertEquals("+18475797000",
+ PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
+ PhoneNumberUtils.FORMAT_UNKNOWN,PhoneNumberUtils.FORMAT_UNKNOWN));
+ }
}
diff --git a/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java
index e733af9..db38ede 100644
--- a/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/SimUtilsTest.java
@@ -44,13 +44,22 @@ public class SimUtilsTest extends TestCase {
assertEquals("890", IccUtils.bcdToString(data, 0, data.length));
/*
- * bcdByteToInt()
- */
+ * gsmBcdByteToInt()
+ */
+
+ assertEquals(98, IccUtils.gsmBcdByteToInt((byte) 0x89));
+
+ // Out of range is treated as 0
+ assertEquals(8, IccUtils.gsmBcdByteToInt((byte) 0x8c));
- assertEquals(98, IccUtils.bcdByteToInt((byte) 0x89));
+ /*
+ * cdmaBcdByteToInt()
+ */
+
+ assertEquals(89, IccUtils.cdmaBcdByteToInt((byte) 0x89));
// Out of range is treated as 0
- assertEquals(8, IccUtils.bcdByteToInt((byte) 0x8c));
+ assertEquals(80, IccUtils.cdmaBcdByteToInt((byte) 0x8c));
/*
* adnStringFieldToString()
@@ -76,4 +85,3 @@ public class SimUtilsTest extends TestCase {
}
}
-
diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java b/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
index b4fb324..fdfafe1 100644
--- a/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
+++ b/tests/CoreTests/com/android/internal/telephony/TelephonyTests.java
@@ -47,6 +47,7 @@ public class TelephonyTests {
suite.addTestSuite(SimUtilsTest.class);
suite.addTestSuite(SimPhoneBookTest.class);
suite.addTestSuite(SimSmsTest.class);
+ suite.addTestSuite(TelephonyUtilsTest.class);
return suite;
}
diff --git a/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java b/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java
new file mode 100644
index 0000000..bf0c88b
--- /dev/null
+++ b/tests/CoreTests/com/android/internal/telephony/TelephonyUtilsTest.java
@@ -0,0 +1,219 @@
+/**
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import com.android.internal.telephony.RetryManager;
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class TelephonyUtilsTest extends TestCase {
+
+ /**
+ * After first creating the RetryManager
+ * isRetryNeeded should be false and the time 0
+ */
+ @SmallTest
+ public void testRetryManagerEmpty() throws Exception {
+ RetryManager rm = new RetryManager();
+
+ assertEquals(0, rm.getRetryCount());
+ assertFalse(rm.isRetryForever());
+ assertFalse(rm.isRetryNeeded());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(0, rm.getRetryTimer());
+
+ rm.increaseRetryCount();
+ assertFalse(rm.isRetryForever());
+ assertFalse(rm.isRetryNeeded());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(0, rm.getRetryTimer());
+
+ rm.setRetryCount(123);
+ assertFalse(rm.isRetryForever());
+ assertFalse(rm.isRetryNeeded());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(0, rm.getRetryTimer());
+
+ rm.retryForeverUsingLastTimeout();
+ assertTrue(rm.isRetryForever());
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(0, rm.getRetryTimer());
+
+ rm.setRetryCount(2);
+ assertFalse(rm.isRetryForever());
+ assertFalse(rm.isRetryNeeded());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(0, rm.getRetryTimer());
+ }
+
+ /**
+ * A simple test and that randomization is doing something.
+ */
+ @SmallTest
+ public void testRetryManagerSimplest() throws Exception {
+ RetryManager rm = new RetryManager();
+
+ assertTrue(rm.configure(1, 500, 10));
+ int loops = 10;
+ int count = 0;
+ for (int i = 0; i < loops; i++) {
+ assertTrue(rm.isRetryNeeded());
+ int time = rm.getRetryTimer();
+ assertTrue((time >= 500) && (time < 600));
+ if (time == 500) {
+ count++;
+ }
+ }
+ assertFalse(count == loops);
+ rm.increaseRetryCount();
+ assertFalse(rm.isRetryNeeded());
+ rm.setRetryCount(0);
+ assertTrue(rm.isRetryNeeded());
+ }
+
+ /**
+ * Test multiple values using simple configuration.
+ */
+ @SmallTest
+ public void testRetryManagerSimple() throws Exception {
+ RetryManager rm = new RetryManager();
+
+ assertTrue(rm.configure(3, 1000, 0));
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(1000, rm.getRetryTimer());
+ assertEquals(rm.getRetryTimer(), 1000);
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(1000, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(1000, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertFalse(rm.isRetryNeeded());
+ assertEquals(1000, rm.getRetryTimer());
+ }
+
+ /**
+ * Test string configuration, simplest
+ */
+ @SmallTest
+ public void testRetryManageSimpleString() throws Exception {
+ RetryManager rm = new RetryManager();
+
+ assertTrue(rm.configure("101"));
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(101, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertFalse(rm.isRetryNeeded());
+ }
+
+ /**
+ * Test infinite retires
+ */
+ @SmallTest
+ public void testRetryManageInfinite() throws Exception {
+ RetryManager rm = new RetryManager();
+
+ assertTrue(rm.configure("1000,2000,3000,max_retries=infinite"));
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(1000, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ assertEquals(2000, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ // All others are 3000 and isRetryNeeded is always true
+ for (int i=0; i < 100; i++) {
+ assertEquals(3000, rm.getRetryTimer());
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ }
+ }
+
+ /**
+ * Test string configuration using all options.
+ */
+ @SmallTest
+ public void testRetryManageString() throws Exception {
+ RetryManager rm = new RetryManager();
+ int time;
+
+ assertTrue(rm.configure("max_retries=4,"
+ + "default_randomization=100,1000, 2000 :200 , 3000"));
+ assertTrue(rm.isRetryNeeded());
+ time = rm.getRetryTimer();
+ assertTrue((time >= 1000) && (time < 1100));
+
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ time = rm.getRetryTimer();
+ assertTrue((time >= 2000) && (time < 2200));
+
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ time = rm.getRetryTimer();
+ assertTrue((time >= 3000) && (time < 3100));
+
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ time = rm.getRetryTimer();
+ assertTrue((time >= 3000) && (time < 3100));
+
+ rm.increaseRetryCount();
+ assertFalse(rm.isRetryNeeded());
+ }
+
+ /**
+ * Test string configuration using all options.
+ */
+ @SmallTest
+ public void testRetryManageForever() throws Exception {
+ RetryManager rm = new RetryManager();
+ int time;
+
+ assertTrue(rm.configure("1000, 2000, 3000"));
+ assertTrue(rm.isRetryNeeded());
+ assertFalse(rm.isRetryForever());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(1000, rm.getRetryTimer());
+
+ rm.retryForeverUsingLastTimeout();
+ rm.increaseRetryCount();
+ rm.increaseRetryCount();
+ rm.increaseRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ assertTrue(rm.isRetryForever());
+ assertEquals(3, rm.getRetryCount());
+ assertEquals(3000, rm.getRetryTimer());
+
+ rm.setRetryCount(1);
+ assertTrue(rm.isRetryNeeded());
+ assertFalse(rm.isRetryForever());
+ assertEquals(1, rm.getRetryCount());
+ assertEquals(2000, rm.getRetryTimer());
+
+ rm.retryForeverUsingLastTimeout();
+ assertTrue(rm.isRetryNeeded());
+ assertTrue(rm.isRetryForever());
+ rm.resetRetryCount();
+ assertTrue(rm.isRetryNeeded());
+ assertFalse(rm.isRetryForever());
+ assertEquals(0, rm.getRetryCount());
+ assertEquals(1000, rm.getRetryTimer());
+ }
+}
diff --git a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java b/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
index 7107412..b96743a 100644
--- a/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
+++ b/tests/CoreTests/com/android/internal/telephony/gsm/GSMPhoneTest.java
@@ -81,7 +81,7 @@ public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase
mRadioControl = mGSMTestHandler.getSimulatedCommands();
mHandler = mGSMTestHandler.getHandler();
- mGSMPhone.registerForPhoneStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
+ mGSMPhone.registerForPreciseCallStateChanged(mHandler, EVENT_PHONE_STATE_CHANGED, null);
mGSMPhone.registerForNewRingingConnection(mHandler, EVENT_RINGING, null);
mGSMPhone.registerForDisconnect(mHandler, EVENT_DISCONNECT, null);
@@ -109,7 +109,7 @@ public class GSMPhoneTest extends AndroidTestCase implements PerformanceTestCase
protected void tearDown() throws Exception {
mRadioControl.shutdown();
- mGSMPhone.unregisterForPhoneStateChanged(mHandler);
+ mGSMPhone.unregisterForPreciseCallStateChanged(mHandler);
mGSMPhone.unregisterForNewRingingConnection(mHandler);
mGSMPhone.unregisterForDisconnect(mHandler);
mGSMPhone.setOnPostDialCharacter(mHandler, 0, null);
diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py
index 49165d0..c3e6b5b 100755
--- a/tests/DumpRenderTree/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree/assets/run_layout_tests.py
@@ -22,7 +22,7 @@
use --refresh-test-list option *once* to re-generate test list on the card.
Some other options are:
- --rebaseline generates expected layout tests results under /sdcard/android/expected_result/
+ --rebaseline generates expected layout tests results under /sdcard/android/expected_result/
--time-out-ms (default is 8000 millis) for each test
--adb-options="-e" passes option string to adb
--results-directory=..., (default is ./layout-test-results) directory name under which results are stored.
@@ -51,11 +51,11 @@ def CountLineNumber(filename):
def DumpRenderTreeFinished(adb_cmd):
""" Check if DumpRenderTree finished running tests
-
+
Args:
output: adb_cmd string
"""
-
+
# pull /sdcard/android/running_test.txt, if the content is "#DONE", it's done
shell_cmd_str = adb_cmd + " shell cat /sdcard/android/running_test.txt"
adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
@@ -69,7 +69,7 @@ def DiffResults(marker, new_results, old_results, diff_results, strip_reason,
"""
old_file = open(old_results, "r")
new_file = open(new_results, "r")
- diff_file = open(diff_results, "a")
+ diff_file = open(diff_results, "a")
# Read lines from each file
ndict = new_file.readlines()
@@ -122,7 +122,7 @@ def CompareResults(ref_dir, results_dir):
"""
logging.info("Comparing results to " + ref_dir)
- diff_result = os.path.join(results_dir, "layout_tests_diff.txt")
+ diff_result = os.path.join(results_dir, "layout_tests_diff.txt")
if os.path.exists(diff_result):
os.remove(diff_result)
@@ -136,7 +136,7 @@ def CompareResults(ref_dir, results_dir):
def main(options, args):
"""Run the tests. Will call sys.exit when complete.
-
+
Args:
options: a dictionary of command line options
args: a list of sub directories or files to test
@@ -175,7 +175,7 @@ def main(options, args):
# Count crashed tests.
crashed_tests = []
- timeout_ms = '5000'
+ timeout_ms = '30000'
if options.time_out_ms:
timeout_ms = options.time_out_ms
@@ -198,7 +198,7 @@ def main(options, args):
shell_cmd_str = adb_cmd + " shell cat /sdcard/android/running_test.txt"
crashed_test = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE).communicate()[0]
-
+
logging.info(crashed_test + " CRASHED");
crashed_tests.append(crashed_test);
@@ -226,14 +226,15 @@ def main(options, args):
result_files = ["/sdcard/layout_tests_passed.txt",
"/sdcard/layout_tests_failed.txt",
"/sdcard/layout_tests_nontext.txt"]
- for file in result_files:
+ for file in result_files:
shell_cmd_str = adb_cmd + " pull " + file + " " + results_dir
adb_output = subprocess.Popen(shell_cmd_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
logging.debug(adb_output)
-
+
# Create the crash list.
fp = open(results_dir + "/layout_tests_crashed.txt", "w");
- fp.writelines('\n'.join(crashed_tests))
+ for crashed_test in crashed_tests:
+ fp.writelines(crashed_test + '\n')
fp.close()
# Count the number of tests in each category.
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
index a389461..f33b01d 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/CallbackProxy.java
@@ -18,6 +18,8 @@ package com.android.dumprendertree;
import android.os.Handler;
import android.os.Message;
+import android.webkit.MockGeolocation;
+import android.webkit.WebStorage;
import java.util.HashMap;
@@ -25,7 +27,7 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
private EventSender mEventSender;
private LayoutTestController mLayoutTestController;
-
+
private static final int EVENT_DOM_LOG = 1;
private static final int EVENT_FIRE_KBD = 2;
private static final int EVENT_KEY_DOWN_1 = 3;
@@ -57,6 +59,9 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
private static final int LAYOUT_SET_WINDOW_KEY = 38;
private static final int LAYOUT_TEST_REPAINT = 39;
private static final int LAYOUT_WAIT_UNTIL_DONE = 40;
+ private static final int LAYOUT_DUMP_DATABASE_CALLBACKS = 41;
+ private static final int LAYOUT_SET_CAN_OPEN_WINDOWS = 42;
+ private static final int SET_GEOLOCATION_PERMISSION = 43;
CallbackProxy(EventSender eventSender,
LayoutTestController layoutTestController) {
@@ -190,6 +195,19 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
case LAYOUT_WAIT_UNTIL_DONE:
mLayoutTestController.waitUntilDone();
break;
+
+ case LAYOUT_DUMP_DATABASE_CALLBACKS:
+ mLayoutTestController.dumpDatabaseCallbacks();
+ break;
+
+ case LAYOUT_SET_CAN_OPEN_WINDOWS:
+ mLayoutTestController.setCanOpenWindows();
+ break;
+
+ case SET_GEOLOCATION_PERMISSION:
+ mLayoutTestController.setGeolocationPermission(
+ msg.arg1 == 1 ? true : false);
+ break;
}
}
@@ -314,7 +332,7 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
}
public void setWindowIsKey(boolean b) {
- obtainMessage(LAYOUT_SET_WINDOW_KEY,b ? 1 : 0, 0).sendToTarget();
+ obtainMessage(LAYOUT_SET_WINDOW_KEY, b ? 1 : 0, 0).sendToTarget();
}
public void testRepaint() {
@@ -325,4 +343,35 @@ public class CallbackProxy extends Handler implements EventSender, LayoutTestCon
obtainMessage(LAYOUT_WAIT_UNTIL_DONE).sendToTarget();
}
+ public void dumpDatabaseCallbacks() {
+ obtainMessage(LAYOUT_DUMP_DATABASE_CALLBACKS).sendToTarget();
+ }
+
+ public void clearAllDatabases() {
+ WebStorage.getInstance().deleteAllData();
+ }
+
+ public void setDatabaseQuota(long quota) {
+ WebStorage.getInstance().setQuotaForOrigin("file://", quota);
+ }
+
+ public void setCanOpenWindows() {
+ obtainMessage(LAYOUT_SET_CAN_OPEN_WINDOWS).sendToTarget();
+ }
+
+ public void setMockGeolocationPosition(double latitude,
+ double longitude,
+ double accuracy) {
+ MockGeolocation.getInstance().setPosition(latitude,
+ longitude,
+ accuracy);
+ }
+
+ public void setMockGeolocationError(int code, String message) {
+ MockGeolocation.getInstance().setError(code, message);
+ }
+
+ public void setGeolocationPermission(boolean allow) {
+ obtainMessage(SET_GEOLOCATION_PERMISSION, allow ? 1 : 0, 0).sendToTarget();
+ }
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
index 4f162b3..8fea967 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/FileFilter.java
@@ -79,11 +79,25 @@ public class FileFilter {
"profiler", // profiler is not supported
"svg", // svg is not supported
"platform", // platform specific
- "http" // requires local http(s) server
+ "http", // requires local http(s) server
+ "fast/workers",
};
static final String [] ignoreTestList = {
- };
+ // RegExp is exponatal
+ "fast/regex/test1.html",
+ "fast/regex/slow.html",
+ // RegExp is too large, causing OOM
+ "fast/js/regexp-charclass-crash.html",
+ // The Android browser has no notion of private browsing.
+ "storage/private-browsing-readonly.html",
+ "storage/domstorage/localstorage/private-browsing-affects-storage.html",
+ "storage/domstorage/sessionstorage/private-browsing-affects-storage.html",
+ // Android layout tests are stored in "layout_tests". The following two
+ // tests expect "LayoutTests" in their output.
+ "storage/domstorage/localstorage/iframe-events.html",
+ "storage/domstorage/sessionstorage/iframe-events.html"
+ };
static void fillIgnoreResultSet() {
// need test plugin
@@ -195,11 +209,7 @@ public class FileFilter {
ignoreResultList.add("fast/loader/local-iFrame-source-from-local.html");
// extra spacing because iFrames rendered next to each other on Apple
ignoreResultList.add("fast/loader/opaque-base-url.html");
- // RegExp is too large, causing OOM
- ignoreResultList.add("fast/js/regexp-charclass-crash.html");
ignoreResultList.add("fast/text/plain-text-line-breaks.html");
-
-
}
static void fillBugTable() {
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
index 6166dd0..f535ed7 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestController.java
@@ -58,5 +58,11 @@ public interface LayoutTestController {
public void queueLoad(String Url, String frameTarget);
public void setAcceptsEditing(boolean b);
-
+
+ // For storage tests
+ public void dumpDatabaseCallbacks();
+ public void setCanOpenWindows();
+
+ // For Geolocation tests
+ public void setGeolocationPermission(boolean allow);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index a03490d..e4c8716 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -16,6 +16,10 @@
package com.android.dumprendertree;
+import com.android.dumprendertree.TestShellActivity.DumpDataType;
+import com.android.dumprendertree.forwarder.AdbUtils;
+import com.android.dumprendertree.forwarder.ForwardServer;
+
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
@@ -42,7 +46,7 @@ class MyTestRecorder {
private BufferedOutputStream mBufferedOutputFailedStream;
private BufferedOutputStream mBufferedOutputNoresultStream;
private BufferedOutputStream mBufferedOutputTimedoutStream;
-
+
public void passed(String layout_file) {
try {
mBufferedOutputPassedStream.write(layout_file.getBytes());
@@ -52,7 +56,7 @@ class MyTestRecorder {
e.printStackTrace();
}
}
-
+
public void failed(String layout_file) {
try {
mBufferedOutputFailedStream.write(layout_file.getBytes());
@@ -62,7 +66,7 @@ class MyTestRecorder {
e.printStackTrace();
}
}
-
+
public void noresult(String layout_file) {
try {
mBufferedOutputNoresultStream.write(layout_file.getBytes());
@@ -72,7 +76,7 @@ class MyTestRecorder {
e.printStackTrace();
}
}
-
+
public void timedout(String url) {
try {
mBufferedOutputTimedoutStream.write(url.getBytes());
@@ -82,14 +86,14 @@ class MyTestRecorder {
e.printStackTrace();
}
}
-
+
public MyTestRecorder(boolean resume) {
try {
File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt");
File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt");
File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt");
File resultTimedoutFile = new File("/sdcard/layout_tests_timedout.txt");
-
+
mBufferedOutputPassedStream =
new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume));
mBufferedOutputFailedStream =
@@ -102,7 +106,7 @@ class MyTestRecorder {
e.printStackTrace();
}
}
-
+
public void close() {
try {
mBufferedOutputPassedStream.close();
@@ -120,7 +124,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
private static final String LOGTAG = "LayoutTests";
static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000;
-
+
static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/";
static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/";
static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/";
@@ -139,14 +143,64 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt";
static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py";
+ static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/";
+ static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/";
+ static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/";
+ static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/";
+ static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/";
+
+
+ static final String DEFAULT_TEST_HOST = "android-browser-test.mtv.corp.google.com";
+ static final String FORWARD_HOST_CONF = "/sdcard/drt_forward_host.txt";
+ private ForwardServer fs8000, fs8080, fs8443;
+
private MyTestRecorder mResultRecorder;
private Vector<String> mTestList;
private boolean mRebaselineResults;
private String mTestPathPrefix;
private boolean mFinished;
-
+
public LayoutTestsAutoTest() {
super("com.android.dumprendertree", TestShellActivity.class);
+
+ int addr = getForwardHostAddr();
+ if(addr != -1) {
+ fs8000 = new ForwardServer(8000, addr, 8000);
+ fs8080 = new ForwardServer(8080, addr, 8080);
+ fs8443 = new ForwardServer(8443, addr, 8443);
+ }
+ }
+
+ private int getForwardHostAddr() {
+ int addr = -1;
+ String host = null;
+ File forwardHostConf = new File(FORWARD_HOST_CONF);
+ if (forwardHostConf.isFile()) {
+ BufferedReader hostReader = null;
+ try {
+ hostReader = new BufferedReader(new FileReader(forwardHostConf));
+ host = hostReader.readLine();
+ Log.v(LOGTAG, "read forward host from file: " + host);
+ } catch (IOException ioe) {
+ Log.v(LOGTAG, "cannot read forward host from file", ioe);
+ } finally {
+ if (hostReader != null) {
+ try {
+ hostReader.close();
+ } catch (IOException ioe) {
+ // burn!!!
+ }
+ }
+ }
+ }
+ if (host == null || host.length() == 0)
+ host = DEFAULT_TEST_HOST;
+ try {
+ addr = AdbUtils.resolve(host);
+ } catch (IOException ioe) {
+ Log.e(LOGTAG, "failed to resolve server address", ioe);
+ }
+ return addr;
}
// This function writes the result of the layout test to
@@ -157,7 +211,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
bundle.putBoolean(file, result);
inst.sendStatus(0, bundle);
}
-
+
private void getTestList() {
// Read test list.
try {
@@ -174,7 +228,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
Log.e(LOGTAG, "Error while reading test list : " + e.getMessage());
}
}
-
+
private void resumeTestList() {
// read out the test name it stoped last time.
try {
@@ -189,7 +243,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
}
}
-
+
private void clearTestStatus() {
// Delete TEST_STATUS_FILE
try {
@@ -208,13 +262,13 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
// Write actual results to result directory.
return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt";
}
-
+
private String getExpectedResultFile(String test) {
int pos = test.lastIndexOf('.');
if(pos == -1)
return null;
String shortName = test.substring(0, pos);
- return shortName + "-expected.txt";
+ return shortName + "-expected.txt";
}
private String getAndroidExpectedResultFile(String expectedResultFile) {
@@ -224,7 +278,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
// Wrap up
private void failedCase(String file) {
Log.w("Layout test: ", file + " failed");
- mResultRecorder.failed(file);
+ mResultRecorder.failed(file);
}
private void passedCase(String file) {
@@ -236,7 +290,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
Log.v("Layout test:", file + " no expected result");
mResultRecorder.noresult(file);
}
-
+
private void processResult(String testFile, String actualResultFile, String expectedResultFile) {
Log.v(LOGTAG, " Processing result: " + testFile);
@@ -257,13 +311,13 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
break;
}
}
-
+
if (passing) {
passedCase(testFile);
} else {
failedCase(testFile);
}
-
+
fe.close();
fr.close();
} catch (FileNotFoundException ex) {
@@ -278,7 +332,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
noresultCase(testFile);
}
}
-
+
private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout) {
activity.setCallback(new TestShellCallback() {
public void finished() {
@@ -287,8 +341,9 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
LayoutTestsAutoTest.this.notifyAll();
}
}
-
+
public void timedOut(String url) {
+ Log.v(LOGTAG, "layout timeout: " + url);
}
});
@@ -306,16 +361,16 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
resultFile = getAndroidExpectedResultFile(expectedResultFile);
}
-
+
mFinished = false;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setClass(activity, TestShellActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra(TestShellActivity.TEST_URL, "file://" + test);
+ intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test));
intent.putExtra(TestShellActivity.RESULT_FILE, resultFile);
intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
activity.startActivity(intent);
-
+
// Wait until done.
synchronized (this) {
while(!mFinished){
@@ -324,18 +379,18 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
} catch (InterruptedException e) { }
}
}
-
+
if (!mRebaselineResults) {
String expectedResultFile = getExpectedResultFile(test);
File f = new File(expectedResultFile);
if (!f.exists()) {
expectedResultFile = getAndroidExpectedResultFile(expectedResultFile);
}
-
+
processResult(test, resultFile, expectedResultFile);
}
- }
-
+ }
+
// Invokes running of layout tests
// and waits till it has finished running.
public void executeLayoutTests(boolean resume) {
@@ -348,35 +403,45 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
}
this.mTestList = new Vector<String>();
-
+
// Read settings
try {
this.mTestPathPrefix =
(new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath();
- } catch (IOException e) {
+ } catch (IOException e) {
Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage());
return;
}
-
+
this.mRebaselineResults = runner.mRebaseline;
-
+
int timeout = runner.mTimeoutInMillis;
if (timeout <= 0) {
timeout = DEFAULT_TIMEOUT_IN_MILLIS;
}
-
+
this.mResultRecorder = new MyTestRecorder(resume);
-
+
if (!resume)
clearTestStatus();
-
+
getTestList();
if (resume)
resumeTestList();
- TestShellActivity activity = (TestShellActivity) getActivity();
+ TestShellActivity activity = getActivity();
+ activity.setDefaultDumpDataType(DumpDataType.DUMP_AS_TEXT);
// Run tests.
+ int addr = -1;
+ try{
+ addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com");
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "error while resolving test host name", ioe);
+ }
+ if(addr == -1) {
+ Log.w(LOGTAG, "failed to resolve test host. http tests will fail.");
+ }
for (int i = 0; i < mTestList.size(); i++) {
String s = mTestList.elementAt(i);
FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
@@ -385,10 +450,48 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
}
FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
-
+ if(fs8000 != null)
+ fs8000.stop();
+ if(fs8080 != null)
+ fs8080.stop();
+ if(fs8443 != null)
+ fs8443.stop();
+
activity.finish();
}
+ private void startForwardServerIfNeeded() {
+ try {
+ if(fs8000 != null)
+ fs8000.start();
+ if(fs8080 != null)
+ fs8080.start();
+ if(fs8443 != null)
+ fs8443.start();
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe);
+ }
+ }
+
+ private String getTestUrl(String path) {
+ String url = null;
+ if (!path.startsWith(HTTP_TESTS_PREFIX)) {
+ url = "file://" + path;
+ } else {
+ startForwardServerIfNeeded();
+ if (path.startsWith(HTTPS_TESTS_PREFIX)) {
+ // still cut the URL after "http/tests/"
+ url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length());
+ } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX)
+ && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX)
+ && !path.startsWith(HTTP_WML_TESTS_PREFIX)) {
+ url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length());
+ } else {
+ url = "file://" + path;
+ }
+ }
+ return url;
+ }
private String getTestPath() {
LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation();
@@ -403,10 +506,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage());
}
Log.v("LayoutTestsAutoTest", " Test path : " + test_path);
-
+
return test_path;
}
-
+
public void generateTestList() {
try {
File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
@@ -431,7 +534,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh
} catch (Exception e) {
e.printStackTrace();
}
-
+
executeLayoutTests(false);
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 09f7cbc..663df83 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -18,6 +18,7 @@ package com.android.dumprendertree;
import android.app.Activity;
import android.app.AlertDialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
@@ -28,12 +29,14 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.ViewGroup;
+import android.webkit.GeolocationPermissions;
import android.webkit.HttpAuthHandler;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
+import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
@@ -43,6 +46,10 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Vector;
public class TestShellActivity extends Activity implements LayoutTestController {
@@ -88,7 +95,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
}
public void clearCache() {
- mWebView.clearCache(true);
+ mWebView.freeMemory();
}
@Override
@@ -100,52 +107,22 @@ public class TestShellActivity extends Activity implements LayoutTestController
setContentView(contentView);
mWebView = new WebView(this);
- mWebView.getSettings().setJavaScriptEnabled(true);
- mWebView.setWebChromeClient(mChromeClient);
- mWebView.setWebViewClient(new WebViewClient(){
-
- @Override
- public void onPageFinished(WebView view, String url) {
- Log.v(LOGTAG, "onPageFinished, url=" + url);
- super.onPageFinished(view, url);
- }
-
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- Log.v(LOGTAG, "onPageStarted, url=" + url);
- super.onPageStarted(view, url, favicon);
- }
-
- @Override
- public void onReceivedError(WebView view, int errorCode, String description,
- String failingUrl) {
- Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
- + ", desc=" + description + ", url=" + failingUrl);
- super.onReceivedError(view, errorCode, description, failingUrl);
- }
-
- @Override
- public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
- String host, String realm) {
- handler.cancel();
- }
-
- @Override
- public void onReceivedSslError(WebView view, SslErrorHandler handler,
- SslError error) {
- handler.proceed();
- }
-
- });
mEventSender = new WebViewEventSender(mWebView);
mCallbackProxy = new CallbackProxy(mEventSender, this);
mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
+ setupWebViewForLayoutTests(mWebView, mCallbackProxy);
+
contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+ // Expose window.gc function to JavaScript. JSC build exposes
+ // this function by default, but V8 requires the flag to turn it on.
+ // WebView::setJsFlags is noop in JSC build.
+ mWebView.setJsFlags("--expose_gc");
+
mHandler = new AsyncHandler();
Intent intent = getIntent();
@@ -262,8 +239,8 @@ public class TestShellActivity extends Activity implements LayoutTestController
@Override
public void onLowMemory() {
super.onLowMemory();
- Log.e(LOGTAG, "Low memory, kill self");
- System.exit(1);
+ Log.e(LOGTAG, "Low memory, clearing caches");
+ mWebView.freeMemory();
}
// Dump the page
@@ -290,6 +267,12 @@ public class TestShellActivity extends Activity implements LayoutTestController
if (mDialogStrings != null)
os.write(mDialogStrings.toString().getBytes());
mDialogStrings = null;
+ if (mDatabaseCallbackStrings != null)
+ os.write(mDatabaseCallbackStrings.toString().getBytes());
+ mDatabaseCallbackStrings = null;
+ if (mConsoleMessages != null)
+ os.write(mConsoleMessages.toString().getBytes());
+ mConsoleMessages = null;
if (webkitData != null)
os.write(webkitData.getBytes());
os.flush();
@@ -438,6 +421,59 @@ public class TestShellActivity extends Activity implements LayoutTestController
mWebView.invalidate();
}
+ public void dumpDatabaseCallbacks() {
+ Log.v(LOGTAG, "dumpDatabaseCallbacks called.");
+ mDumpDatabaseCallbacks = true;
+ }
+
+ public void setCanOpenWindows() {
+ Log.v(LOGTAG, "setCanOpenWindows called.");
+ mCanOpenWindows = true;
+ }
+
+ /**
+ * Sets the Geolocation permission state to be used for all future requests.
+ */
+ public void setGeolocationPermission(boolean allow) {
+ mGeolocationPermissionSet = true;
+ mGeolocationPermission = allow;
+ }
+
+ private final WebViewClient mViewClient = new WebViewClient(){
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ Log.v(LOGTAG, "onPageFinished, url=" + url);
+ super.onPageFinished(view, url);
+ }
+
+ @Override
+ public void onPageStarted(WebView view, String url, Bitmap favicon) {
+ Log.v(LOGTAG, "onPageStarted, url=" + url);
+ super.onPageStarted(view, url, favicon);
+ }
+
+ @Override
+ public void onReceivedError(WebView view, int errorCode, String description,
+ String failingUrl) {
+ Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
+ + ", desc=" + description + ", url=" + failingUrl);
+ super.onReceivedError(view, errorCode, description, failingUrl);
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
+ String host, String realm) {
+ handler.cancel();
+ }
+
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error) {
+ handler.proceed();
+ }
+ };
+
+
private final WebChromeClient mChromeClient = new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
@@ -512,17 +548,135 @@ public class TestShellActivity extends Activity implements LayoutTestController
result.confirm();
return true;
}
+
+ @Override
+ public boolean onJsTimeout() {
+ Log.v(LOGTAG, "JavaScript timeout");
+ return false;
+ }
+
+ @Override
+ public void onExceededDatabaseQuota(String url_str,
+ String databaseIdentifier, long currentQuota,
+ long estimatedSize, long totalUsedQuota,
+ WebStorage.QuotaUpdater callback) {
+ if (mDumpDatabaseCallbacks) {
+ if (mDatabaseCallbackStrings == null) {
+ mDatabaseCallbackStrings = new StringBuffer();
+ }
+
+ String protocol = "";
+ String host = "";
+ int port = 0;
+
+ try {
+ URL url = new URL(url_str);
+ protocol = url.getProtocol();
+ host = url.getHost();
+ if (url.getPort() > -1) {
+ port = url.getPort();
+ }
+ } catch (MalformedURLException e) {}
+
+ String databaseCallbackString =
+ "UI DELEGATE DATABASE CALLBACK: " +
+ "exceededDatabaseQuotaForSecurityOrigin:{" + protocol +
+ ", " + host + ", " + port + "} database:" +
+ databaseIdentifier + "\n";
+ Log.v(LOGTAG, "LOG: "+databaseCallbackString);
+ mDatabaseCallbackStrings.append(databaseCallbackString);
+ }
+ // Give 5MB more quota.
+ callback.updateQuota(currentQuota + 1024 * 1024 * 5);
+ }
+
+ /**
+ * Instructs the client to show a prompt to ask the user to set the
+ * Geolocation permission state for the specified origin.
+ */
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin,
+ GeolocationPermissions.Callback callback) {
+ if (mGeolocationPermissionSet) {
+ callback.invoke(origin, mGeolocationPermission, false);
+ }
+ }
+
+ @Override
+ public void addMessageToConsole(String message, int lineNumber,
+ String sourceID) {
+ if (mConsoleMessages == null) {
+ mConsoleMessages = new StringBuffer();
+ }
+ String consoleMessage = "CONSOLE MESSAGE: line "
+ + lineNumber +": "+ message +"\n";
+ mConsoleMessages.append(consoleMessage);
+ Log.v(LOGTAG, "LOG: "+consoleMessage);
+ }
+
+ @Override
+ public boolean onCreateWindow(WebView view, boolean dialog,
+ boolean userGesture, Message resultMsg) {
+ if (!mCanOpenWindows) {
+ return false;
+ }
+
+ // We never display the new window, just create the view and
+ // allow it's content to execute and be recorded by the test
+ // runner.
+
+ HashMap<String, Object> jsIfaces = new HashMap<String, Object>();
+ jsIfaces.put("layoutTestController", mCallbackProxy);
+ jsIfaces.put("eventSender", mCallbackProxy);
+ WebView newWindowView = new NewWindowWebView(TestShellActivity.this, jsIfaces);
+ setupWebViewForLayoutTests(newWindowView, mCallbackProxy);
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) resultMsg.obj;
+ transport.setWebView(newWindowView);
+ resultMsg.sendToTarget();
+ return true;
+ }
};
+ private static class NewWindowWebView extends WebView {
+ public NewWindowWebView(Context context, Map<String, Object> jsIfaces) {
+ super(context, null, 0, jsIfaces);
+ }
+ }
+
private void resetTestStatus() {
mWaitUntilDone = false;
mDumpDataType = mDefaultDumpDataType;
mTimedOut = false;
mDumpTitleChanges = false;
mRequestedWebKitData = false;
+ mDumpDatabaseCallbacks = false;
+ mCanOpenWindows = false;
mEventSender.resetMouse();
}
+ private void setupWebViewForLayoutTests(WebView webview, CallbackProxy callbackProxy) {
+ if (webview == null) {
+ return;
+ }
+
+ WebSettings settings = webview.getSettings();
+ settings.setAppCacheEnabled(true);
+ settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
+ settings.setAppCacheMaxSize(Long.MAX_VALUE);
+ settings.setJavaScriptEnabled(true);
+ settings.setJavaScriptCanOpenWindowsAutomatically(true);
+ settings.setSupportMultipleWindows(true);
+ settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
+ settings.setDatabaseEnabled(true);
+ settings.setDatabasePath(getDir("databases",0).getAbsolutePath());
+ settings.setDomStorageEnabled(true);
+ settings.setWorkersEnabled(false);
+
+ webview.setWebChromeClient(mChromeClient);
+ webview.setWebViewClient(mViewClient);
+ }
+
private WebView mWebView;
private WebViewEventSender mEventSender;
private AsyncHandler mHandler;
@@ -550,6 +704,10 @@ public class TestShellActivity extends Activity implements LayoutTestController
private StringBuffer mDialogStrings;
private boolean mKeepWebHistory;
private Vector mWebHistory;
+ private boolean mDumpDatabaseCallbacks;
+ private StringBuffer mDatabaseCallbackStrings;
+ private StringBuffer mConsoleMessages;
+ private boolean mCanOpenWindows;
static final String TIMEOUT_STR = "**Test timeout";
@@ -562,4 +720,7 @@ public class TestShellActivity extends Activity implements LayoutTestController
static final String RESULT_FILE = "ResultFile";
static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
static final String UI_AUTO_TEST = "UiAutoTest";
+
+ private boolean mGeolocationPermissionSet;
+ private boolean mGeolocationPermission;
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
new file mode 100644
index 0000000..9a3e9c2
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
@@ -0,0 +1,112 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class AdbUtils {
+
+ private static final String ADB_OK = "OKAY";
+ private static final int ADB_PORT = 5037;
+ private static final String ADB_HOST = "127.0.0.1";
+ private static final int ADB_RESPONSE_SIZE = 4;
+
+ private static final String LOGTAG = "AdbUtils";
+
+ /**
+ *
+ * Convert integer format IP into xxx.xxx.xxx.xxx format
+ *
+ * @param host IP address in integer format
+ * @return human readable format
+ */
+ public static String convert(int host) {
+ return ((host >> 24) & 0xFF) + "."
+ + ((host >> 16) & 0xFF) + "."
+ + ((host >> 8) & 0xFF) + "."
+ + (host & 0xFF);
+ }
+
+ /**
+ *
+ * Resolve DNS name into IP address
+ *
+ * @param host DNS name
+ * @return IP address in integer format
+ * @throws IOException
+ */
+ public static int resolve(String host) throws IOException {
+ Socket localSocket = new Socket(ADB_HOST, ADB_PORT);
+ DataInputStream dis = new DataInputStream(localSocket.getInputStream());
+ OutputStream os = localSocket.getOutputStream();
+ int count_read = 0;
+ byte[] buf = new byte[128];
+
+ if (localSocket == null || dis == null || os == null)
+ return -1;
+ String cmd = "dns:" + host;
+
+ if(!sendAdbCmd(dis, os, cmd))
+ return -1;
+
+ count_read = dis.readInt();
+ localSocket.close();
+ return count_read;
+ }
+
+ /**
+ *
+ * Send an ADB command using existing socket connection
+ *
+ * the streams provided must be from a socket connected to adbd already
+ *
+ * @param is input stream of the socket connection
+ * @param os output stream of the socket
+ * @param cmd the adb command to send
+ * @return if adb gave a success response
+ * @throws IOException
+ */
+ public static boolean sendAdbCmd(InputStream is, OutputStream os,
+ String cmd) throws IOException {
+ byte[] buf = new byte[ADB_RESPONSE_SIZE];
+
+ cmd = String.format("%04X", cmd.length()) + cmd;
+ os.write(cmd.getBytes());
+ int read = is.read(buf);
+ if(read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
+ Log.w(LOGTAG, "adb cmd faild.");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ *
+ * Get a tcp socket connection to specified IP address and port proxied by adb
+ *
+ * The proxying is transparent, e.g. if a socket is returned, then it can be written to and
+ * read from as if it is directly connected to the target
+ *
+ * @param remoteAddress IP address of the host to connect to
+ * @param remotePort port of the host to connect to
+ * @return a valid Socket instance if successful, null otherwise
+ */
+ public static Socket getForwardedSocket(int remoteAddress, int remotePort) {
+ try {
+ Socket socket = new Socket(ADB_HOST, ADB_PORT);
+ String cmd = "tcp:" + remotePort + ":" + convert(remoteAddress);
+ if(!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) {
+ socket.close();
+ return null;
+ }
+ return socket;
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "error creating adb socket", ioe);
+ return null;
+ }
+ }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
new file mode 100644
index 0000000..74e018e
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
@@ -0,0 +1,117 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * A port forwarding server. Listens at specified local port and forward the tcp communications to
+ * external host/port via adb networking proxy.
+ *
+ */
+public class ForwardServer {
+
+ private static final String LOGTAG = "ForwardServer";
+
+ private int remotePort;
+ private int remoteAddress;
+ private int localPort;
+ private ServerSocket serverSocket;
+ private boolean started;
+
+ private Set<Forwarder> forwarders;
+
+ public ForwardServer(int localPort, int remoteAddress, int remotePort) {
+ this.localPort = localPort;
+ this.remoteAddress = remoteAddress;
+ this.remotePort = remotePort;
+ started = false;
+ forwarders = new HashSet<Forwarder>();
+ }
+
+ public synchronized void start() throws IOException {
+ if(!started) {
+ serverSocket = new ServerSocket(localPort);
+ Thread serverThread = new Thread(new ServerRunner(serverSocket));
+ serverThread.setName(LOGTAG);
+ serverThread.start();
+ started = true;
+ }
+ }
+
+ public synchronized void stop() {
+ if(started) {
+ synchronized (forwarders) {
+ for(Forwarder forwarder : forwarders)
+ forwarder.stop();
+ forwarders.clear();
+ }
+ try {
+ serverSocket.close();
+ } catch (IOException ioe) {
+ Log.v(LOGTAG, "exception while closing", ioe);
+ } finally {
+ started = false;
+ }
+ }
+ }
+
+ public synchronized boolean isRunning() {
+ return started;
+ }
+
+ private class ServerRunner implements Runnable {
+
+ private ServerSocket socket;
+
+ public ServerRunner(ServerSocket socket) {
+ this.socket = socket;
+ }
+
+ public void run() {
+ try {
+ while (true) {
+ Socket localSocket = socket.accept();
+ Socket remoteSocket = AdbUtils.getForwardedSocket(remoteAddress, remotePort);
+ if(remoteSocket == null) {
+ try {
+ localSocket.close();
+ } catch (IOException ioe) {
+ Log.w(LOGTAG, "error while closing socket", ioe);
+ } finally {
+ Log.w(LOGTAG, "failed to start forwarding from " + localSocket);
+ }
+ } else {
+ Forwarder forwarder = new Forwarder(localSocket, remoteSocket,
+ ForwardServer.this);
+ forwarder.start();
+ }
+ }
+ } catch (IOException ioe) {
+ return;
+ }
+ }
+ }
+
+ public void register(Forwarder forwarder) {
+ synchronized (forwarders) {
+ if(!forwarders.contains(forwarder)) {
+ forwarders.add(forwarder);
+ }
+ }
+ }
+
+ public void unregister(Forwarder recyclable) {
+ synchronized (forwarders) {
+ if(forwarders.contains(recyclable)) {
+ recyclable.stop();
+ forwarders.remove(recyclable);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
new file mode 100644
index 0000000..e1e04a7
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
@@ -0,0 +1,92 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ *
+ * Worker class for {@link ForwardServer}. A Forwarder will be created once the ForwardServer
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ *
+ */
+public class Forwarder {
+
+ private ForwardServer server;
+ private Socket from, to;
+
+ private static final String LOGTAG = "Forwarder";
+
+ public Forwarder (Socket from, Socket to, ForwardServer server) {
+ this.server = server;
+ this.from = from;
+ this.to = to;
+ server.register(this);
+ }
+
+ public void start() {
+ Thread outgoing = new Thread(new SocketPipe(from, to));
+ Thread incoming = new Thread(new SocketPipe(to, from));
+ outgoing.setName(LOGTAG);
+ incoming.setName(LOGTAG);
+ outgoing.start();
+ incoming.start();
+ }
+
+ public void stop() {
+ shutdown(from);
+ shutdown(to);
+ }
+
+ private void shutdown(Socket socket) {
+ try {
+ socket.shutdownInput();
+ } catch (IOException e) {
+ Log.v(LOGTAG, "Socket#shutdownInput", e);
+ }
+ try {
+ socket.shutdownOutput();
+ } catch (IOException e) {
+ Log.v(LOGTAG, "Socket#shutdownOutput", e);
+ }
+ try {
+ socket.close();
+ } catch (IOException e) {
+ Log.v(LOGTAG, "Socket#close", e);
+ }
+ }
+
+ private class SocketPipe implements Runnable {
+
+ private Socket in, out;
+
+ public SocketPipe(Socket in, Socket out) {
+ this.in = in;
+ this.out = out;
+ }
+
+ public void run() {
+ try {
+ int length;
+ InputStream is = in.getInputStream();
+ OutputStream os = out.getOutputStream();
+ byte[] buffer = new byte[4096];
+ while ((length = is.read(buffer)) > 0) {
+ os.write(buffer, 0, length);
+ }
+ } catch (IOException ioe) {
+ } finally {
+ server.unregister(Forwarder.this);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SocketPipe{" + in + "=>" + out + "}";
+ }
+ }
+}
diff --git a/tests/FrameworkTest/res/drawable-hdpi/big_drawable_background.9.png b/tests/FrameworkTest/res/drawable-hdpi/big_drawable_background.9.png
new file mode 100644
index 0000000..53470b8
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/big_drawable_background.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/black_square.png b/tests/FrameworkTest/res/drawable-hdpi/black_square.png
new file mode 100644
index 0000000..7752103
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/black_square.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/black_square_stretchable.9.png b/tests/FrameworkTest/res/drawable-hdpi/black_square_stretchable.9.png
new file mode 100644
index 0000000..4988163
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/black_square_stretchable.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/drawable_background.9.png b/tests/FrameworkTest/res/drawable-hdpi/drawable_background.9.png
new file mode 100644
index 0000000..f692d38
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/drawable_background.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_pause_1.png b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_pause_1.png
new file mode 100644
index 0000000..9edb064
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_pause_1.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_backward_1.png b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_backward_1.png
new file mode 100644
index 0000000..c4b6b92
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_backward_1.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_forward_1.png b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_forward_1.png
new file mode 100644
index 0000000..03140f5
--- /dev/null
+++ b/tests/FrameworkTest/res/drawable-hdpi/sym_now_playing_skip_forward_1.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/big_drawable_background.9.png b/tests/FrameworkTest/res/drawable-mdpi/big_drawable_background.9.png
index aded635..aded635 100644
--- a/tests/FrameworkTest/res/drawable/big_drawable_background.9.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/big_drawable_background.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/black_square.png b/tests/FrameworkTest/res/drawable-mdpi/black_square.png
index 1bfe0a2..1bfe0a2 100644
--- a/tests/FrameworkTest/res/drawable/black_square.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/black_square.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/black_square_stretchable.9.png b/tests/FrameworkTest/res/drawable-mdpi/black_square_stretchable.9.png
index 1fcbeb1..1fcbeb1 100644
--- a/tests/FrameworkTest/res/drawable/black_square_stretchable.9.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/black_square_stretchable.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/drawable_background.9.png b/tests/FrameworkTest/res/drawable-mdpi/drawable_background.9.png
index 1337ba9..1337ba9 100644
--- a/tests/FrameworkTest/res/drawable/drawable_background.9.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/drawable_background.9.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/sym_now_playing_pause_1.png b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_pause_1.png
index 2f19d08..2f19d08 100644
--- a/tests/FrameworkTest/res/drawable/sym_now_playing_pause_1.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_pause_1.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/sym_now_playing_skip_backward_1.png b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_backward_1.png
index f945a81..f945a81 100644
--- a/tests/FrameworkTest/res/drawable/sym_now_playing_skip_backward_1.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_backward_1.png
Binary files differ
diff --git a/tests/FrameworkTest/res/drawable/sym_now_playing_skip_forward_1.png b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_forward_1.png
index da9361a..da9361a 100644
--- a/tests/FrameworkTest/res/drawable/sym_now_playing_skip_forward_1.png
+++ b/tests/FrameworkTest/res/drawable-mdpi/sym_now_playing_skip_forward_1.png
Binary files differ
diff --git a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
index aa3d186..a8af7f8 100644
--- a/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
+++ b/tests/FrameworkTest/tests/src/android/content/AbstractTableMergerTest.java
@@ -8,6 +8,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.test.AndroidTestCase;
import android.text.TextUtils;
+import android.accounts.Account;
import java.util.ArrayList;
import java.util.Map;
@@ -26,7 +27,7 @@ public class AbstractTableMergerTest extends AndroidTestCase {
static final Uri TABLE_URI = Uri.withAppendedPath(CONTENT_URI, TABLE_NAME);
static final Uri DELETED_TABLE_URI = Uri.withAppendedPath(CONTENT_URI, DELETED_TABLE_NAME);
- private final String ACCOUNT = "account@goo.com";
+ private final Account ACCOUNT = new Account("account@goo.com", "example.type");
private final ArrayList<Expectation> mExpectations = Lists.newArrayList();
@@ -65,25 +66,31 @@ public class AbstractTableMergerTest extends AndroidTestCase {
mExpectations.clear();
}
- ContentValues newValues(String data, String syncId, String syncAccount,
+ ContentValues newValues(String data, String syncId, Account syncAccount,
String syncTime, String syncVersion, Long syncLocalId) {
ContentValues values = new ContentValues();
if (data != null) values.put("data", data);
if (syncTime != null) values.put("_sync_time", syncTime);
if (syncVersion != null) values.put("_sync_version", syncVersion);
if (syncId != null) values.put("_sync_id", syncId);
- if (syncAccount != null) values.put("_sync_account", syncAccount);
+ if (syncAccount != null) {
+ values.put("_sync_account", syncAccount.name);
+ values.put("_sync_account_type", syncAccount.type);
+ }
values.put("_sync_local_id", syncLocalId);
values.put("_sync_dirty", 0);
return values;
}
- ContentValues newDeletedValues(String syncId, String syncAccount, String syncVersion,
+ ContentValues newDeletedValues(String syncId, Account syncAccount, String syncVersion,
Long syncLocalId) {
ContentValues values = new ContentValues();
if (syncVersion != null) values.put("_sync_version", syncVersion);
if (syncId != null) values.put("_sync_id", syncId);
- if (syncAccount != null) values.put("_sync_account", syncAccount);
+ if (syncAccount != null) {
+ values.put("_sync_account", syncAccount.name);
+ values.put("_sync_account_type", syncAccount.type);
+ }
if (syncLocalId != null) values.put("_sync_local_id", syncLocalId);
return values;
}
@@ -380,6 +387,7 @@ public class AbstractTableMergerTest extends AndroidTestCase {
+ "_sync_local_id INTEGER, "
+ "_sync_dirty INTEGER NOT NULL DEFAULT 0, "
+ "_sync_account TEXT, "
+ + "_sync_account_type TEXT, "
+ "_sync_mark INTEGER)");
mDb.execSQL("CREATE TABLE deleted_items ("
@@ -388,6 +396,7 @@ public class AbstractTableMergerTest extends AndroidTestCase {
+ "_sync_id TEXT, "
+ "_sync_local_id INTEGER, "
+ "_sync_account TEXT, "
+ + "_sync_account_type TEXT, "
+ "_sync_mark INTEGER)");
}
@@ -501,7 +510,7 @@ public class AbstractTableMergerTest extends AndroidTestCase {
throw new UnsupportedOperationException();
}
- public void onSyncStart(SyncContext context, String account) {
+ public void onSyncStart(SyncContext context, Account account) {
throw new UnsupportedOperationException();
}
@@ -509,7 +518,7 @@ public class AbstractTableMergerTest extends AndroidTestCase {
throw new UnsupportedOperationException();
}
- public String getSyncingAccount() {
+ public Account getSyncingAccount() {
throw new UnsupportedOperationException();
}
@@ -544,24 +553,24 @@ public class AbstractTableMergerTest extends AndroidTestCase {
throw new UnsupportedOperationException();
}
- protected void onAccountsChanged(String[] accountsArray) {
+ protected void onAccountsChanged(Account[] accountsArray) {
throw new UnsupportedOperationException();
}
- protected void deleteRowsForRemovedAccounts(Map<String, Boolean> accounts, String table,
- String accountColumnName) {
+ protected void deleteRowsForRemovedAccounts(Map<Account, Boolean> accounts, String table
+ ) {
throw new UnsupportedOperationException();
}
- public void wipeAccount(String account) {
+ public void wipeAccount(Account account) {
throw new UnsupportedOperationException();
}
- public byte[] readSyncDataBytes(String account) {
+ public byte[] readSyncDataBytes(Account account) {
throw new UnsupportedOperationException();
}
- public void writeSyncDataBytes(String account, byte[] data) {
+ public void writeSyncDataBytes(Account account, byte[] data) {
throw new UnsupportedOperationException();
}
}
diff --git a/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
new file mode 100644
index 0000000..1ba9d66
--- /dev/null
+++ b/tests/FrameworkTest/tests/src/android/content/ContentProviderOperationTest.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.Map;
+import java.util.Map.Entry;
+
+@SmallTest
+public class ContentProviderOperationTest extends TestCase {
+ private final static Uri sTestUri1 = Uri.parse("content://authority/blah");
+ private final static ContentValues sTestValues1;
+
+ private final static Class<ContentProviderOperation.Builder> CLASS_BUILDER =
+ ContentProviderOperation.Builder.class;
+ private final static Class<ContentProviderOperation> CLASS_OPERATION =
+ ContentProviderOperation.class;
+
+ static {
+ sTestValues1 = new ContentValues();
+ sTestValues1.put("a", 1);
+ sTestValues1.put("b", "two");
+ }
+
+ public void testInsert() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, null, 0);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testInsertNoValues() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertNull(values);
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, null, 0);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testInsertFailed() {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ try {
+ op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return null;
+ }
+ }, null, 0);
+ fail("the apply should have thrown an OperationApplicationException");
+ } catch (OperationApplicationException e) {
+ // this is the expected case
+ }
+ }
+
+ public void testInsertWithBackRefs() throws OperationApplicationException {
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .withValueBackReference("a1", 3)
+ .withValueBackReference("a2", 1)
+ .build();
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ ContentValues expected = new ContentValues(sTestValues1);
+ expected.put("a1", 103);
+ expected.put("a2", 101);
+ assertEquals(expected.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, previousResults, previousResults.length);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testUpdate() throws OperationApplicationException {
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(sTestValues1)
+ .build();
+ ContentProviderResult[] backRefs = new ContentProviderResult[2];
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Uri insert(Uri uri, ContentValues values) {
+ assertEquals(sTestUri1.toString(), uri.toString());
+ assertEquals(sTestValues1.toString(), values.toString());
+ return uri.buildUpon().appendPath("19").build();
+ }
+ }, backRefs, 1);
+ assertEquals(sTestUri1.buildUpon().appendPath("19").toString(), result.uri.toString());
+ }
+
+ public void testAssert() {
+ // Build an operation to assert values match provider
+ ContentProviderOperation op1 = ContentProviderOperation.newAssertQuery(sTestUri1)
+ .withValues(sTestValues1).build();
+
+ try {
+ // Assert that values match from cursor
+ ContentProviderResult result = op1.apply(new TestContentProvider() {
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ // Return cursor over specific set of values
+ return getCursor(sTestValues1);
+ }
+ }, null, 0);
+ } catch (OperationApplicationException e) {
+ fail("newAssert() failed");
+ }
+ }
+
+ /**
+ * Build a {@link Cursor} with a single row that contains all values
+ * provided through the given {@link ContentValues}.
+ */
+ private Cursor getCursor(ContentValues contentValues) {
+ final Set<Entry<String, Object>> valueSet = contentValues.valueSet();
+ final String[] keys = new String[valueSet.size()];
+ final Object[] values = new Object[valueSet.size()];
+
+ int i = 0;
+ for (Entry<String, Object> entry : valueSet) {
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ i++;
+ }
+
+ final MatrixCursor cursor = new MatrixCursor(keys);
+ cursor.addRow(values);
+ return cursor;
+ }
+
+ public void testValueBackRefs() {
+ ContentValues values = new ContentValues();
+ values.put("a", "in1");
+ values.put("a2", "in2");
+ values.put("b", "in3");
+ values.put("c", "in4");
+
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+
+ ContentValues expectedValues = new ContentValues(values);
+ expectedValues.put("a1", "103");
+ expectedValues.put("a2", "101");
+ expectedValues.put("a3", "102");
+
+ ContentProviderOperation op1 = ContentProviderOperation.newInsert(sTestUri1)
+ .withValues(values)
+ .withValueBackReference("a1", 3)
+ .withValueBackReference("a2", 1)
+ .withValueBackReference("a3", 2)
+ .build();
+ ContentValues v2 = op1.resolveValueBackReferences(previousResults, previousResults.length);
+ assertEquals(expectedValues, v2);
+ }
+
+ public void testSelectionBackRefs() {
+ ContentProviderResult[] previousResults = new ContentProviderResult[4];
+ previousResults[0] = new ContentProviderResult(100);
+ previousResults[1] = new ContentProviderResult(101);
+ previousResults[2] = new ContentProviderResult(102);
+ previousResults[3] = new ContentProviderResult(103);
+
+ String[] selectionArgs = new String[]{"a", null, null, "b", null};
+
+ final ContentValues values = new ContentValues();
+ values.put("unused", "unused");
+
+ ContentProviderOperation op1 = ContentProviderOperation.newUpdate(sTestUri1)
+ .withSelectionBackReference(1, 3)
+ .withSelectionBackReference(2, 1)
+ .withSelectionBackReference(4, 2)
+ .withSelection("unused", selectionArgs)
+ .withValues(values)
+ .build();
+ String[] s2 = op1.resolveSelectionArgsBackReferences(
+ previousResults, previousResults.length);
+ assertEquals("a,103,101,b,102", TextUtils.join(",", s2));
+ }
+
+ public void testParcelingOperation() throws NoSuchFieldException, IllegalAccessException,
+ NoSuchMethodException, InvocationTargetException, InstantiationException {
+ Parcel parcel = Parcel.obtain();
+ ContentProviderOperation op1;
+ ContentProviderOperation op2;
+
+ HashMap<Integer, Integer> selArgsBackRef = new HashMap<Integer, Integer>();
+ selArgsBackRef.put(1, 2);
+ selArgsBackRef.put(3, 4);
+
+ ContentValues values = new ContentValues();
+ values.put("v1", "val1");
+ values.put("v2", "43");
+
+ ContentValues valuesBackRef = new ContentValues();
+ values.put("v3", "val3");
+ values.put("v4", "44");
+
+ try {
+ ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(
+ Uri.parse("content://goo/bar"));
+
+ builderSetExpectedCount(builder, 42);
+ builderSetSelection(builder, "selection");
+ builderSetSelectionArgs(builder, new String[]{"a", "b"});
+ builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
+ builderSetValues(builder, values);
+ builderSetValuesBackReferences(builder, valuesBackRef);
+
+ op1 = newOperationFromBuilder(builder);
+ op1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+
+ assertEquals(ContentProviderOperation.TYPE_INSERT, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertEquals(Integer.valueOf(42), operationGetExpectedCount(op2));
+ assertEquals("selection", operationGetSelection(op2));
+ assertEquals(2, operationGetSelectionArgs(op2).length);
+ assertEquals("a", operationGetSelectionArgs(op2)[0]);
+ assertEquals("b", operationGetSelectionArgs(op2)[1]);
+ assertEquals(values, operationGetValues(op2));
+ assertEquals(valuesBackRef, operationGetValuesBackReferences(op2));
+ assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
+ assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
+ assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
+ } finally {
+ parcel.recycle();
+ }
+
+ try {
+ ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(
+ Uri.parse("content://goo/bar"));
+
+ builderSetSelectionArgsBackReferences(builder, selArgsBackRef);
+
+ op1 = newOperationFromBuilder(builder);
+ op1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+ assertEquals(ContentProviderOperation.TYPE_UPDATE, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertNull(operationGetExpectedCount(op2));
+ assertNull(operationGetSelection(op2));
+ assertNull(operationGetSelectionArgs(op2));
+ assertNull(operationGetValues(op2));
+ assertNull(operationGetValuesBackReferences(op2));
+ assertEquals(2, operationGetSelectionArgsBackReferences(op2).size());
+ assertEquals(Integer.valueOf(2), operationGetSelectionArgsBackReferences(op2).get(1));
+ assertEquals(Integer.valueOf(4), operationGetSelectionArgsBackReferences(op2).get(3));
+ } finally {
+ parcel.recycle();
+ }
+
+ try {
+ ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(
+ Uri.parse("content://goo/bar"));
+
+ op1 = newOperationFromBuilder(builder);
+ op1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ op2 = ContentProviderOperation.CREATOR.createFromParcel(parcel);
+ assertEquals(ContentProviderOperation.TYPE_DELETE, operationGetType(op2));
+ assertEquals("content://goo/bar", operationGetUri(op2).toString());
+ assertNull(operationGetExpectedCount(op2));
+ assertNull(operationGetSelection(op2));
+ assertNull(operationGetSelectionArgs(op2));
+ assertNull(operationGetValues(op2));
+ assertNull(operationGetValuesBackReferences(op2));
+ assertNull(operationGetSelectionArgsBackReferences(op2));
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ private static ContentProviderOperation newOperationFromBuilder(
+ ContentProviderOperation.Builder builder)
+ throws NoSuchMethodException, InstantiationException, IllegalAccessException,
+ InvocationTargetException {
+ final Constructor constructor = CLASS_OPERATION.getDeclaredConstructor(CLASS_BUILDER);
+ constructor.setAccessible(true);
+ return (ContentProviderOperation) constructor.newInstance(builder);
+ }
+
+ private void builderSetSelectionArgsBackReferences(
+ ContentProviderOperation.Builder builder, HashMap<Integer, Integer> selArgsBackRef)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mSelectionArgsBackReferences");
+ field.setAccessible(true);
+ field.set(builder, selArgsBackRef);
+ }
+
+ private void builderSetValuesBackReferences(
+ ContentProviderOperation.Builder builder, ContentValues valuesBackReferences)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mValuesBackReferences");
+ field.setAccessible(true);
+ field.set(builder, valuesBackReferences);
+ }
+
+ private void builderSetSelection(
+ ContentProviderOperation.Builder builder, String selection)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mSelection");
+ field.setAccessible(true);
+ field.set(builder, selection);
+ }
+
+ private void builderSetSelectionArgs(
+ ContentProviderOperation.Builder builder, String[] selArgs)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mSelectionArgs");
+ field.setAccessible(true);
+ field.set(builder, selArgs);
+ }
+
+ private void builderSetValues(
+ ContentProviderOperation.Builder builder, ContentValues values)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mValues");
+ field.setAccessible(true);
+ field.set(builder, values);
+ }
+
+ private void builderSetExpectedCount(
+ ContentProviderOperation.Builder builder, Integer expectedCount)
+ throws NoSuchFieldException, IllegalAccessException {
+ Field field;
+ field = CLASS_BUILDER.getDeclaredField("mExpectedCount");
+ field.setAccessible(true);
+ field.set(builder, expectedCount);
+ }
+
+ private int operationGetType(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mType");
+ field.setAccessible(true);
+ return field.getInt(operation);
+ }
+
+ private Uri operationGetUri(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mUri");
+ field.setAccessible(true);
+ return (Uri) field.get(operation);
+ }
+
+ private String operationGetSelection(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mSelection");
+ field.setAccessible(true);
+ return (String) field.get(operation);
+ }
+
+ private String[] operationGetSelectionArgs(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgs");
+ field.setAccessible(true);
+ return (String[]) field.get(operation);
+ }
+
+ private ContentValues operationGetValues(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mValues");
+ field.setAccessible(true);
+ return (ContentValues) field.get(operation);
+ }
+
+ private Integer operationGetExpectedCount(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mExpectedCount");
+ field.setAccessible(true);
+ return (Integer) field.get(operation);
+ }
+
+ private ContentValues operationGetValuesBackReferences(ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mValuesBackReferences");
+ field.setAccessible(true);
+ return (ContentValues) field.get(operation);
+ }
+
+ private Map<Integer, Integer> operationGetSelectionArgsBackReferences(
+ ContentProviderOperation operation)
+ throws NoSuchFieldException, IllegalAccessException {
+ final Field field = CLASS_OPERATION.getDeclaredField("mSelectionArgsBackReferences");
+ field.setAccessible(true);
+ return (Map<Integer, Integer>) field.get(operation);
+ }
+
+ public void testParcelingResult() {
+ Parcel parcel = Parcel.obtain();
+ ContentProviderResult result1;
+ ContentProviderResult result2;
+ try {
+ result1 = new ContentProviderResult(Uri.parse("content://goo/bar"));
+ result1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+ assertEquals("content://goo/bar", result2.uri.toString());
+ assertNull(result2.count);
+ } finally {
+ parcel.recycle();
+ }
+
+ parcel = Parcel.obtain();
+ try {
+ result1 = new ContentProviderResult(42);
+ result1.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ result2 = ContentProviderResult.CREATOR.createFromParcel(parcel);
+ assertEquals(Integer.valueOf(42), result2.count);
+ assertNull(result2.uri);
+ } finally {
+ parcel.recycle();
+ }
+ }
+
+ static class TestContentProvider extends ContentProvider {
+ public boolean onCreate() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/LowStorageTest/Android.mk b/tests/LowStorageTest/Android.mk
new file mode 100644
index 0000000..ab5b9e9
--- /dev/null
+++ b/tests/LowStorageTest/Android.mk
@@ -0,0 +1,25 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := lowstoragetest
+
+include $(BUILD_PACKAGE)
diff --git a/tests/LowStorageTest/AndroidManifest.xml b/tests/LowStorageTest/AndroidManifest.xml
new file mode 100644
index 0000000..9d4a63a
--- /dev/null
+++ b/tests/LowStorageTest/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.lowstoragetest">
+ <application android:label="LowStorageTest">
+ <activity android:name="LowStorageTest"
+ android:theme="@android:style/Theme.Black.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest> \ No newline at end of file
diff --git a/tests/LowStorageTest/res/layout/main.xml b/tests/LowStorageTest/res/layout/main.xml
new file mode 100644
index 0000000..cc99102
--- /dev/null
+++ b/tests/LowStorageTest/res/layout/main.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:stretchColumns="1">
+
+ <TextView
+ android:text="@string/total_storage"
+ android:gravity="left"
+ android:padding="3dip" />
+
+ <TextView
+ android:id="@+id/totalsize"
+ android:gravity="left"
+ android:padding="3dip" />
+
+ <View
+ android:layout_height="2dip"
+ android:background="#FF909090" />
+
+ <TextView
+ android:layout_column="1"
+ android:gravity="left"
+ android:text="@string/available_storage"
+ android:padding="3dip" />
+
+ <TextView
+ android:id="@+id/freesize"
+ android:gravity="left"
+ android:padding="3dip" />
+
+ <TextView
+ android:layout_column="1"
+ android:gravity="left"
+ android:textSize="18sp"
+ android:text="@string/status"
+ android:padding="3dip" />
+
+ <TextView
+ android:layout_column="1"
+ android:gravity="left"
+ android:textSize="18sp"
+ android:id="@+id/status"
+ android:text="@string/eat_up_storage"
+ android:padding="3dip" />
+
+ <View
+ android:layout_height="2dip"
+ android:background="#FF909090" />
+
+ <Button
+ android:id="@+id/button_run"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/run_text"
+ android:layout_alignParentRight="true" />
+
+</TableLayout> \ No newline at end of file
diff --git a/tests/LowStorageTest/res/values/strings.xml b/tests/LowStorageTest/res/values/strings.xml
new file mode 100644
index 0000000..93d9876
--- /dev/null
+++ b/tests/LowStorageTest/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="total_storage">"Total Storage Size ..."</string>
+ <string name="available_storage">"Available Storage Size ..."</string>
+ <string name="eat_up_storage">"Please Wait..."</string>
+ <string name="status">"Status"</string>
+ <string name="run_text">"Start"</string>
+</resources>
diff --git a/tests/LowStorageTest/src/com/android/lowstoragetest/LowStorageTest.java b/tests/LowStorageTest/src/com/android/lowstoragetest/LowStorageTest.java
new file mode 100644
index 0000000..9f297aa
--- /dev/null
+++ b/tests/LowStorageTest/src/com/android/lowstoragetest/LowStorageTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.lowstoragetest;
+
+import android.app.Activity;
+import android.content.Context;
+
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.StatFs;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import java.io.File;
+import java.io.FileOutputStream;
+import android.widget.TextView;
+import android.widget.Button;
+
+public class LowStorageTest extends Activity {
+ static final String TAG = "DiskFullTest";
+ static final long WAIT_FOR_FINISH = 5 * 60 * 60;
+ static final int NO_OF_BLOCKS_TO_FILL = 1000;
+ static final int BYTE_SIZE = 1024;
+ static final int WAIT_FOR_SYSTEM_UPDATE = 10000;
+
+ private int mBlockSize = 0;
+ private final Object fillUpDone = new Object();
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.main);
+
+ // Update the current data info
+ File path = Environment.getDataDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ int totalBlocks = stat.getBlockCount();
+ mBlockSize = (int) (stat.getBlockSize());
+ TextView startSizeTextView = (TextView) findViewById(R.id.totalsize);
+ startSizeTextView.setText(Long.toString((totalBlocks * mBlockSize) / BYTE_SIZE));
+ Button button = (Button) findViewById(R.id.button_run);
+ button.setOnClickListener(mStartListener);
+ }
+
+ View.OnClickListener mStartListener = new OnClickListener() {
+ public void onClick(View v) {
+ fillDataAndUpdateInfo();
+ }
+ };
+
+ public void fillDataAndUpdateInfo() {
+ updateInfo(this);
+ }
+
+ // Fill up 100% of the data partition
+ public void fillupdisk(Context context) {
+ final Context contextfill = context;
+ new Thread() {
+ @Override
+ public void run() {
+ try {
+ // Fill up all the memory
+ File path = Environment.getDataDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ int totalBlocks = stat.getBlockCount();
+ int noOfBlockToFill = stat.getAvailableBlocks();
+ FileOutputStream fs =
+ contextfill.openFileOutput("testdata", Context.MODE_APPEND);
+ for (int i = 0; i < (noOfBlockToFill / NO_OF_BLOCKS_TO_FILL); i++) {
+ byte buf[] = new byte[mBlockSize * NO_OF_BLOCKS_TO_FILL];
+ fs.write(buf);
+ fs.flush();
+ }
+
+ // Fill up the last few block
+ byte buf[] = new byte[(noOfBlockToFill % NO_OF_BLOCKS_TO_FILL) * mBlockSize];
+ fs.write(buf);
+ fs.flush();
+ fs.close();
+
+ // Finished, update the info
+ synchronized (fillUpDone) {
+ fillUpDone.notify();
+ }
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+ }.start();
+ }
+
+ public void updateInfo(Context context) {
+ fillupdisk(this);
+ synchronized (fillUpDone) {
+ try {
+ fillUpDone.wait(WAIT_FOR_FINISH);
+ } catch (Exception e) {
+ Log.v(TAG, "wait was interrupted.");
+ }
+ }
+ try {
+ // The stat didn't relect the correct data right away
+ // put some extra time to make sure if get the right size.
+ Thread.sleep(WAIT_FOR_SYSTEM_UPDATE);
+ File path = Environment.getDataDirectory();
+ StatFs stat = new StatFs(path.getPath());
+ long availableBlocks = stat.getAvailableBlocks();
+ TextView freeSizeTextView = (TextView) findViewById(R.id.freesize);
+ freeSizeTextView.setText(Long.toString((availableBlocks * mBlockSize) / BYTE_SIZE));
+ TextView statusTextView = (TextView) findViewById(R.id.status);
+ statusTextView.setText("Finished. You can start the test now.");
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ }
+ }
+}
diff --git a/tests/appwidgets/AppWidgetHostTest/res/drawable-hdpi/oh_hai_icon.png b/tests/appwidgets/AppWidgetHostTest/res/drawable-hdpi/oh_hai_icon.png
new file mode 100644
index 0000000..2ddde94
--- /dev/null
+++ b/tests/appwidgets/AppWidgetHostTest/res/drawable-hdpi/oh_hai_icon.png
Binary files differ
diff --git a/tests/appwidgets/AppWidgetHostTest/res/drawable/oh_hai_icon.png b/tests/appwidgets/AppWidgetHostTest/res/drawable-mdpi/oh_hai_icon.png
index 30ff267..30ff267 100644
--- a/tests/appwidgets/AppWidgetHostTest/res/drawable/oh_hai_icon.png
+++ b/tests/appwidgets/AppWidgetHostTest/res/drawable-mdpi/oh_hai_icon.png
Binary files differ
diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml
index 3778742..d992627 100644
--- a/tests/backup/AndroidManifest.xml
+++ b/tests/backup/AndroidManifest.xml
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.backuptest">
+ <uses-permission android:name="android.permission.BACKUP_DATA" />
<application android:backupAgent="BackupTestAgent">
<activity android:name="BackupTestActivity" android:label="_BackupTest">
<intent-filter>
diff --git a/tests/backup/backup_stress_test.sh b/tests/backup/backup_stress_test.sh
new file mode 100755
index 0000000..8155507
--- /dev/null
+++ b/tests/backup/backup_stress_test.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+iterations=150
+failures=0
+i=0
+LOGDIR="$HOME/backup_tests"
+LOGFILE="$LOGDIR/backup_stress.`date +%s`.log"
+export BUGREPORT_DIR="$LOGDIR/bugreports"
+
+# make sure that we have a place to put logs and bugreports
+mkdir -p $LOGDIR $BUGREPORT_DIR
+
+echo "logfile is $LOGFILE"
+
+(while (sleep 10); do
+ failed=0
+
+ echo
+ echo "Iteration $i at `date`"
+ echo
+
+ ./test_backup.sh "$@" 2>&1
+
+ sleep 10
+ echo "Restore at `date`"
+ echo
+
+ ./test_restore.sh "$@" 2>&1 || failed=1
+
+ if [ "$failed" -ne 0 ]; then
+ failures=$(($failures+1))
+ # Long and verbose so it sticks out
+ echo "FAILED iteration $i of $iterations; $failures failures so far"
+ echo "FAILED iteration $i of $iterations; $failures failures so far" > /dev/stderr
+ else
+ printf "Iteration %d:\tPASS; remaining: %d\n" $i $(($iterations - $i - 1))
+ printf "Iteration %d:\tPASS; remaining: %d\n" $i $(($iterations - $i - 1)) > /dev/stderr
+ fi
+
+ echo "End $i at `date`"
+
+ i=$(($i+1))
+ if [ $i -eq $iterations ]; then
+ echo "DONE: $iterations iterations with $failures failures."
+ echo "DONE: $iterations iterations with $failures failures." > /dev/stderr
+ [ "$failures" -eq 0 ] && exit 0
+ exit 1
+ fi
+done) > "$LOGFILE"
+
diff --git a/tests/backup/test_backup.sh b/tests/backup/test_backup.sh
index dbf9ed2..10b809d 100755
--- a/tests/backup/test_backup.sh
+++ b/tests/backup/test_backup.sh
@@ -1,27 +1,63 @@
#!/bin/bash
-#adb kill-server
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# uncomment for debugging
+#export DRY_RUN="echo"
+source test_backup_common.sh
+
+# wipe prior backup data for packages
+b_pkgs=$(a shell dumpsys backup | \
+ ruby -ne 'print($1+" ") if $_ =~ /^\s*ApplicationInfo\S+ (.+?)\}/')
+
+for pkg in $b_pkgs; do
+ a shell bmgr wipe "$pkg"
+done
+
+echo 'Waiting 5 seconds for things to settle...'
+sleep 5
+
+# run adb as root so we can poke at com.android.backuptest's data
+adb_root
+
+# show commands as we go
+set -x
# set the transport
-adb shell bmgr transport 1
+a shell bmgr transport com.google.android.backup/.BackupTransportService
# load up the three files
-adb shell "rm /data/data/com.android.backuptest/files/* ; \
- mkdir /data/data/com.android.backuptest ; \
- mkdir /data/data/com.android.backuptest/files ; \
- mkdir /data/data/com.android.backuptest/shared_prefs ; \
- echo -n \"<map><int name=\\\"pref\\\" value=\\\"1\\\" /></map>\" > /data/data/com.android.backuptest/shared_prefs/raw.xml ; \
- echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
- echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
- echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
- echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+a shell \
+ "rm /data/data/com.android.backuptest/files/file.txt ; \
+ rm /data/data/com.android.backuptest/files/another_file.txt ; \
+ rm /data/data/com.android.backuptest/files/empty.txt ; \
+ mkdir /data/data/com.android.backuptest ; \
+ mkdir /data/data/com.android.backuptest/files ; \
+ mkdir /data/data/com.android.backuptest/shared_prefs ; \
+ echo -n \"<map><int name=\\\"pref\\\" value=\\\"1\\\" /></map>\" \
+ > /data/data/com.android.backuptest/shared_prefs/raw.xml ; \
+ echo -n first file > /data/data/com.android.backuptest/files/file.txt ; \
+ echo -n asdf > /data/data/com.android.backuptest/files/another_file.txt ; \
+ echo -n "" > /data/data/com.android.backuptest/files/empty.txt ; \
+ date >> /data/data/com.android.backuptest/files/3.txt ; \
"
+# echo -n 3 > /data/data/com.android.backuptest/files/3.txt ; \
# say that the data has changed
-adb shell bmgr backup com.android.backuptest
+a shell bmgr backup com.android.backuptest
# run the backup
-adb shell bmgr run
-
-
+a shell bmgr run
diff --git a/tests/backup/test_backup_common.sh b/tests/backup/test_backup_common.sh
new file mode 100755
index 0000000..61ec833
--- /dev/null
+++ b/tests/backup/test_backup_common.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+export ADB_OPTS="$@"
+
+# run adb with options
+function a { $DRY_RUN adb $ADB_OPTS "$@"; }
+
+# restart adb as root and wait for it to come back again
+function adb_root
+{
+ root_status=$(a root)
+ if [ "$root_status" != "adbd is already running as root" ]; then
+ echo -n "Restarting adb as root..."
+ sleep 2
+ a 'wait-for-device'
+ echo done.
+ fi
+}
+
diff --git a/tests/backup/test_restore.sh b/tests/backup/test_restore.sh
index ccf29cf..46b46e4 100755
--- a/tests/backup/test_restore.sh
+++ b/tests/backup/test_restore.sh
@@ -1,53 +1,111 @@
#!/bin/bash
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# uncomment for debugging
+#export DRY_RUN="echo"
+source test_backup_common.sh
+
+[ -z "$BUGREPORT_DIR" ] && BUGREPORT_DIR="$HOME/backup/bugreports"
+
function check_file
{
- data=$(adb shell cat /data/data/com.android.backuptest/$1)
+ data=$(a shell cat /data/data/com.android.backuptest/$1)
if [ "$data" = "$2" ] ; then
echo "$1 has correct value [$2]"
+ return 0
else
echo $1 is INCORRECT
echo " value: [$data]"
echo " expected: [$2]"
+ return 1
+ fi
+}
+
+function check_exists
+{
+ # return 0 if file exists, 1 otherwise
+ data=$(a shell "ls $@ 2> /dev/null >/dev/null && echo -n exists")
+ if [ "$data" = "exists" ]; then
+ return 0
+ else
+ return 1
fi
}
+# run adb as root so we can poke at com.android.backuptest's data
+adb_root
+
# delete the old data
echo --- Previous files
-adb shell "ls -l /data/data/com.android.backuptest/files"
-adb shell "rm /data/data/com.android.backuptest/files/*"
+a shell "ls -l /data/data/com.android.backuptest/files"
+a shell "rm /data/data/com.android.backuptest/files/*"
echo --- Previous shared_prefs
-adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
-adb shell "rm /data/data/com.android.backuptest/shared_prefs/*"
+a shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+a shell "rm /data/data/com.android.backuptest/shared_prefs/*"
echo --- Erased files and shared_prefs
-adb shell "ls -l /data/data/com.android.backuptest/files"
-adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+a shell "ls -l /data/data/com.android.backuptest/files"
+a shell "ls -l /data/data/com.android.backuptest/shared_prefs"
echo ---
echo
echo
-echo
+
+# FIXME: there's probably a smarter way to do this
+# FIXME: if we can get the android ID, that's probably the safest thing to do
+# pick the most recent set and restore from it
+restore_set=$(a shell bmgr list sets | head -n1 | awk '{print $1}')
# run the restore
-adb shell bmgr restore 0
+echo "Restoring from set [$restore_set]"
+a shell bmgr restore "$restore_set"
echo
echo
-echo
# check the results
-check_file files/file.txt "first file"
-check_file files/another_file.txt "asdf"
-check_file files/3.txt "3"
-check_file files/empty.txt ""
-check_file shared_prefs/raw.xml '<map><int name="pref" value="1" /></map>'
+export need_bug=0
+
+# make sure files have the expected contents
+check_file files/file.txt "first file" || need_bug=1
+check_file files/another_file.txt "asdf" || need_bug=1
+#check_file files/3.txt "3" || need_bug=1
+check_file files/empty.txt "" || need_bug=1
+check_file shared_prefs/raw.xml '<map><int name="pref" value="1" /></map>' || need_bug=1
+
+# make sure that missing files weren't somehow created
+check_exists files/file_doesnt_exist.txt && need_bug=1
+check_exists files/no_files_here.txt && need_bug=1
+
+if [ \( "$need_bug" -ne 0 \) -a -d "$BUGREPORT_DIR" ]; then
+ dev_id=$(a get-serialno)
+ filename="${dev_id}_`date +%s`"
+ echo "Grabbing bugreport; filename is $filename"
+ a bugreport > "$BUGREPORT_DIR/$filename.txt"
+fi
-echo
-echo
echo
echo --- Restored files
-adb shell "ls -l /data/data/com.android.backuptest/files"
+a shell "ls -l /data/data/com.android.backuptest/files"
echo --- Restored shared_prefs
-adb shell "ls -l /data/data/com.android.backuptest/shared_prefs"
+a shell "ls -l /data/data/com.android.backuptest/shared_prefs"
echo ---
echo
+
+echo "Last 3 timestamps in 3.txt:"
+a shell cat /data/data/com.android.backuptest/files/3.txt | tail -n 3
+
+exit $need_bug
+
diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
index 719e758..aebd68c 100644
--- a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java
@@ -46,7 +46,7 @@ public class HardwareServicePermissionTest extends TestCase {
*/
public void testVibrate() throws RemoteException {
try {
- mHardwareService.vibrate(2000);
+ mHardwareService.vibrate(2000, new Binder());
fail("vibrate did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
@@ -77,7 +77,7 @@ public class HardwareServicePermissionTest extends TestCase {
*/
public void testCancelVibrate() throws RemoteException {
try {
- mHardwareService.cancelVibrate();
+ mHardwareService.cancelVibrate(new Binder());
fail("cancelVibrate did not throw SecurityException as expected");
} catch (SecurityException e) {
// expected
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index dbcef6d..b00d8b0 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -1819,6 +1819,19 @@ void AaptAssets::print() const
AaptDir::print();
}
+sp<AaptDir> AaptAssets::resDir(const String8& name)
+{
+ const Vector<sp<AaptDir> >& dirs = mDirs;
+ const size_t N = dirs.size();
+ for (size_t i=0; i<N; i++) {
+ const sp<AaptDir>& d = dirs.itemAt(i);
+ if (d->getLeaf() == name) {
+ return d;
+ }
+ }
+ return NULL;
+}
+
bool
valid_symbol_name(const String8& symbol)
{
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 63afe5c..865efd1 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -15,7 +15,7 @@
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
#include "Bundle.h"
#include "SourcePos.h"
@@ -131,7 +131,9 @@ public:
{
//printf("new AaptFile created %s\n", (const char*)sourceFile);
}
- virtual ~AaptFile() { }
+ virtual ~AaptFile() {
+ free(mData);
+ }
const String8& getPath() const { return mPath; }
const AaptGroupEntry& getGroupEntry() const { return mGroupEntry; }
@@ -447,7 +449,13 @@ private:
AaptSymbolEntry mDefSymbol;
};
-class ResourceTypeSet;
+class ResourceTypeSet : public RefBase,
+ public KeyedVector<String8,sp<AaptGroup> >
+{
+public:
+ ResourceTypeSet();
+};
+
/**
* Asset hierarchy being operated on.
@@ -455,8 +463,8 @@ class ResourceTypeSet;
class AaptAssets : public AaptDir
{
public:
- AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false) { }
- virtual ~AaptAssets() { }
+ AaptAssets() : AaptDir(String8(), String8()), mHaveIncludedAssets(false), mRes(NULL) { }
+ virtual ~AaptAssets() { delete mRes; }
const String8& getPackage() const { return mPackage; }
void setPackage(const String8& package) { mPackage = package; mSymbolsPrivatePackage = package; }
@@ -474,6 +482,8 @@ public:
const sp<AaptFile>& file,
const String8& resType);
+ void addGroupEntry(const AaptGroupEntry& entry) { mGroupEntries.add(entry); }
+
ssize_t slurpFromArgs(Bundle* bundle);
virtual ssize_t slurpFullTree(Bundle* bundle,
@@ -498,13 +508,14 @@ public:
void print() const;
inline const Vector<sp<AaptDir> >& resDirs() { return mDirs; }
+ sp<AaptDir> resDir(const String8& name);
inline sp<AaptAssets> getOverlay() { return mOverlay; }
inline void setOverlay(sp<AaptAssets>& overlay) { mOverlay = overlay; }
inline KeyedVector<String8, sp<ResourceTypeSet> >* getResources() { return mRes; }
inline void
- setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { mRes = res; }
+ setResources(KeyedVector<String8, sp<ResourceTypeSet> >* res) { delete mRes; mRes = res; }
private:
String8 mPackage;
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index fdc859c..2d8973d 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -17,7 +17,10 @@ LOCAL_SRC_FILES := \
ResourceTable.cpp \
Images.cpp \
Resource.cpp \
- SourcePos.cpp
+ SourcePos.cpp \
+ ZipEntry.cpp \
+ ZipFile.cpp
+
LOCAL_CFLAGS += -Wno-format-y2k
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index a6fedf3..234e5b2 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -7,7 +7,10 @@
#define __BUNDLE_H
#include <stdlib.h>
-#include <utils.h> // android
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <utils/String8.h>
#include <utils/Vector.h>
@@ -36,7 +39,7 @@ public:
mRequireLocalization(false), mPseudolocalize(false),
mValues(false),
mCompressionMethod(0), mOutputAPKFile(NULL),
- mAssetSourceDir(NULL),
+ mAssetSourceDir(NULL), mProguardFile(NULL),
mAndroidManifestFile(NULL), mPublicOutputFile(NULL),
mRClassDir(NULL), mResourceIntermediatesDir(NULL),
mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL),
@@ -85,6 +88,8 @@ public:
*/
const char* getAssetSourceDir() const { return mAssetSourceDir; }
void setAssetSourceDir(const char* dir) { mAssetSourceDir = dir; }
+ const char* getProguardFile() const { return mProguardFile; }
+ void setProguardFile(const char* file) { mProguardFile = file; }
const android::Vector<const char*>& getResourceSourceDirs() const { return mResourceSourceDirs; }
void addResourceSourceDir(const char* dir) { mResourceSourceDirs.insertAt(dir,0); }
const char* getAndroidManifestFile() const { return mAndroidManifestFile; }
@@ -158,6 +163,7 @@ private:
int mCompressionMethod;
const char* mOutputAPKFile;
const char* mAssetSourceDir;
+ const char* mProguardFile;
const char* mAndroidManifestFile;
const char* mPublicOutputFile;
const char* mRClassDir;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 5f80ade..f2cdf75 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -8,8 +8,10 @@
#include "ResourceTable.h"
#include "XMLNode.h"
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <fcntl.h>
#include <errno.h>
@@ -231,7 +233,7 @@ static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes)
return -1;
}
-static String8 getAttribute(const ResXMLTree& tree, const char* ns,
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
const char* attr, String8* outError)
{
ssize_t idx = tree.indexOfAttribute(ns, attr);
@@ -330,9 +332,11 @@ enum {
TARGET_SDK_VERSION_ATTR = 0x01010270,
TEST_ONLY_ATTR = 0x01010272,
DENSITY_ATTR = 0x0101026c,
+ GL_ES_VERSION_ATTR = 0x01010281,
SMALL_SCREEN_ATTR = 0x01010284,
NORMAL_SCREEN_ATTR = 0x01010285,
LARGE_SCREEN_ATTR = 0x01010286,
+ REQUIRED_ATTR = 0x0101028e,
};
const char *getComponentName(String8 &pkgName, String8 &componentName) {
@@ -501,8 +505,25 @@ int doDump(Bundle* bundle)
bool withinActivity = false;
bool isMainActivity = false;
bool isLauncherActivity = false;
+ bool isSearchable = false;
bool withinApplication = false;
bool withinReceiver = false;
+ bool withinService = false;
+ bool withinIntentFilter = false;
+ bool hasMainActivity = false;
+ bool hasOtherActivities = false;
+ bool hasOtherReceivers = false;
+ bool hasOtherServices = false;
+ bool hasWallpaperService = false;
+ bool hasImeService = false;
+ bool hasWidgetReceivers = false;
+ bool hasIntentFilter = false;
+ bool actMainActivity = false;
+ bool actWidgetReceivers = false;
+ bool actImeService = false;
+ bool actWallpaperService = false;
+ bool specCameraFeature = false;
+ bool hasCameraPermission = false;
int targetSdk = 0;
int smallScreen = 1;
int normalScreen = 1;
@@ -512,9 +533,48 @@ int doDump(Bundle* bundle)
String8 activityLabel;
String8 activityIcon;
String8 receiverName;
+ String8 serviceName;
while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
if (code == ResXMLTree::END_TAG) {
depth--;
+ if (depth < 2) {
+ withinApplication = false;
+ } else if (depth < 3) {
+ if (withinActivity && isMainActivity && isLauncherActivity) {
+ const char *aName = getComponentName(pkg, activityName);
+ if (aName != NULL) {
+ printf("launchable activity name='%s'", aName);
+ }
+ printf("label='%s' icon='%s'\n",
+ activityLabel.string(),
+ activityIcon.string());
+ }
+ if (!hasIntentFilter) {
+ hasOtherActivities |= withinActivity;
+ hasOtherReceivers |= withinReceiver;
+ hasOtherServices |= withinService;
+ }
+ withinActivity = false;
+ withinService = false;
+ withinReceiver = false;
+ hasIntentFilter = false;
+ isMainActivity = isLauncherActivity = false;
+ } else if (depth < 4) {
+ if (withinIntentFilter) {
+ if (withinActivity) {
+ hasMainActivity |= actMainActivity;
+ hasOtherActivities |= !actMainActivity;
+ } else if (withinReceiver) {
+ hasWidgetReceivers |= actWidgetReceivers;
+ hasOtherReceivers |= !actWidgetReceivers;
+ } else if (withinService) {
+ hasImeService |= actImeService;
+ hasWallpaperService |= actWallpaperService;
+ hasOtherServices |= (!actImeService && !actWallpaperService);
+ }
+ }
+ withinIntentFilter = false;
+ }
continue;
}
if (code != ResXMLTree::START_TAG) {
@@ -522,7 +582,7 @@ int doDump(Bundle* bundle)
}
depth++;
String8 tag(tree.getElementName(&len));
- //printf("Depth %d tag %s\n", depth, tag.string());
+ //printf("Depth %d, %s\n", depth, tag.string());
if (depth == 1) {
if (tag != "manifest") {
fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
@@ -650,10 +710,41 @@ int doDump(Bundle* bundle)
NORMAL_SCREEN_ATTR, NULL, 1);
largeScreen = getIntegerAttribute(tree,
LARGE_SCREEN_ATTR, NULL, 1);
+ } else if (tag == "uses-feature") {
+ String8 name = getAttribute(tree, NAME_ATTR, &error);
+ if (error == "") {
+ int req = getIntegerAttribute(tree,
+ REQUIRED_ATTR, NULL, 1);
+ if (name == "android.hardware.camera") {
+ specCameraFeature = true;
+ }
+ printf("uses-feature%s:'%s'\n",
+ req ? "" : "-not-required", name.string());
+ } else {
+ int vers = getIntegerAttribute(tree,
+ GL_ES_VERSION_ATTR, &error);
+ if (error == "") {
+ printf("uses-gl-es:'0x%x'\n", vers);
+ }
+ }
+ } else if (tag == "uses-permission") {
+ String8 name = getAttribute(tree, NAME_ATTR, &error);
+ if (error == "") {
+ if (name == "android.permission.CAMERA") {
+ hasCameraPermission = true;
+ }
+ printf("uses-permission:'%s'\n", name.string());
+ } else {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n",
+ error.string());
+ goto bail;
+ }
}
} else if (depth == 3 && withinApplication) {
withinActivity = false;
withinReceiver = false;
+ withinService = false;
+ hasIntentFilter = false;
if(tag == "activity") {
withinActivity = true;
activityName = getAttribute(tree, NAME_ATTR, &error);
@@ -679,7 +770,10 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library: %s\n", error.string());
goto bail;
}
- printf("uses-library:'%s'\n", libraryName.string());
+ int req = getIntegerAttribute(tree,
+ REQUIRED_ATTR, NULL, 1);
+ printf("uses-library%s:'%s'\n",
+ req ? "" : "-not-required", libraryName.string());
} else if (tag == "receiver") {
withinReceiver = true;
receiverName = getAttribute(tree, NAME_ATTR, &error);
@@ -688,76 +782,97 @@ int doDump(Bundle* bundle)
fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
goto bail;
}
+ } else if (tag == "service") {
+ withinService = true;
+ serviceName = getAttribute(tree, NAME_ATTR, &error);
+
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:name' attribute for service: %s\n", error.string());
+ goto bail;
+ }
}
- } else if (depth == 5) {
- if (withinActivity) {
- if (tag == "action") {
- //printf("LOG: action tag\n");
- String8 action = getAttribute(tree, NAME_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
- goto bail;
- }
+ } else if ((depth == 4) && (tag == "intent-filter")) {
+ hasIntentFilter = true;
+ withinIntentFilter = true;
+ actMainActivity = actWidgetReceivers = actImeService = actWallpaperService = false;
+ } else if ((depth == 5) && withinIntentFilter){
+ String8 action;
+ if (tag == "action") {
+ action = getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string());
+ goto bail;
+ }
+ if (withinActivity) {
if (action == "android.intent.action.MAIN") {
isMainActivity = true;
- //printf("LOG: isMainActivity==true\n");
+ actMainActivity = true;
}
- } else if (tag == "category") {
- String8 category = getAttribute(tree, NAME_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
- goto bail;
+ } else if (withinReceiver) {
+ if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
+ actWidgetReceivers = true;
}
- if (category == "android.intent.category.LAUNCHER") {
- isLauncherActivity = true;
- //printf("LOG: isLauncherActivity==true\n");
+ } else if (withinService) {
+ if (action == "android.view.InputMethod") {
+ actImeService = true;
+ } else if (action == "android.service.wallpaper.WallpaperService") {
+ actWallpaperService = true;
}
}
- } else if (withinReceiver) {
- if (tag == "action") {
- String8 action = getAttribute(tree, NAME_ATTR, &error);
- if (error != "") {
- fprintf(stderr, "ERROR getting 'android:name' attribute for receiver: %s\n", error.string());
- goto bail;
- }
- if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
- const char *rName = getComponentName(pkg, receiverName);
- if (rName != NULL) {
- printf("gadget-receiver:'%s/%s'\n", pkg.string(), rName);
- }
- }
+ if (action == "android.intent.action.SEARCH") {
+ isSearchable = true;
}
}
- }
-
- if (depth < 2) {
- withinApplication = false;
- }
- if (depth < 3) {
- //if (withinActivity) printf("LOG: withinActivity==false\n");
- withinActivity = false;
- withinReceiver = false;
- }
- if (depth < 5) {
- //if (isMainActivity) printf("LOG: isMainActivity==false\n");
- //if (isLauncherActivity) printf("LOG: isLauncherActivity==false\n");
- isMainActivity = false;
- isLauncherActivity = false;
- }
-
- if (withinActivity && isMainActivity && isLauncherActivity) {
- printf("launchable activity:");
- const char *aName = getComponentName(pkg, activityName);
- if (aName != NULL) {
- printf(" name='%s'", aName);
+ if (tag == "category") {
+ String8 category = getAttribute(tree, NAME_ATTR, &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string());
+ goto bail;
+ }
+ if (withinActivity) {
+ if (category == "android.intent.category.LAUNCHER") {
+ isLauncherActivity = true;
+ }
+ }
}
- printf("label='%s' icon='%s'\n",
- activityLabel.string(),
- activityIcon.string());
}
}
+
+ if (!specCameraFeature && hasCameraPermission) {
+ // For applications that have not explicitly stated their
+ // camera feature requirements, but have requested the camera
+ // permission, we are going to give them compatibility treatment
+ // of requiring the equivalent to original android devices.
+ printf("uses-feature:'android.hardware.camera'\n");
+ printf("uses-feature:'android.hardware.camera.autofocus'\n");
+ }
+ if (hasMainActivity) {
+ printf("main\n");
+ }
+ if (hasWidgetReceivers) {
+ printf("app-widget\n");
+ }
+ if (hasImeService) {
+ printf("ime\n");
+ }
+ if (hasWallpaperService) {
+ printf("wallpaper\n");
+ }
+ if (hasOtherActivities) {
+ printf("other-activities\n");
+ }
+ if (isSearchable) {
+ printf("search\n");
+ }
+ if (hasOtherReceivers) {
+ printf("other-receivers\n");
+ }
+ if (hasOtherServices) {
+ printf("other-services\n");
+ }
+
// Determine default values for any unspecified screen sizes,
// based on the target SDK of the package. As of 4 (donut)
// the screen size support was introduced, so all default to
@@ -776,7 +891,7 @@ int doDump(Bundle* bundle)
if (normalScreen != 0) printf(" 'normal'");
if (largeScreen != 0) printf(" 'large'");
printf("\n");
-
+
printf("locales:");
Vector<String8> locales;
res.getLocales(&locales);
@@ -789,7 +904,7 @@ int doDump(Bundle* bundle)
printf(" '%s'", localeStr);
}
printf("\n");
-
+
Vector<ResTable_config> configs;
res.getConfigurations(&configs);
SortedVector<int> densities;
@@ -799,14 +914,14 @@ int doDump(Bundle* bundle)
if (dens == 0) dens = 160;
densities.add(dens);
}
-
+
printf("densities:");
const size_t ND = densities.size();
for (size_t i=0; i<ND; i++) {
printf(" '%d'", densities[i]);
}
printf("\n");
-
+
AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib");
if (dir != NULL) {
if (dir->getFileCount() > 0) {
@@ -1043,6 +1158,12 @@ int doPackage(Bundle* bundle)
}
}
+ // Write out the ProGuard file
+ err = writeProguardFile(bundle, assets);
+ if (err < 0) {
+ goto bail;
+ }
+
// Write the apk
if (outputAPKFile) {
err = writeAPK(bundle, assets, String8(outputAPKFile));
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 0a4c68b..f2414dd 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -44,6 +44,9 @@ struct image_info
}
free(allocRows);
}
+ free(info9Patch.xDivs);
+ free(info9Patch.yDivs);
+ free(info9Patch.colors);
}
png_uint_32 width;
@@ -833,6 +836,7 @@ static void write_png(const char* imageName,
int i;
png_unknown_chunk unknowns[1];
+ unknowns[0].data = NULL;
png_bytepp outRows = (png_bytepp) malloc((int) imageInfo.height * png_sizeof(png_bytep));
if (outRows == (png_bytepp) 0) {
@@ -939,6 +943,7 @@ static void write_png(const char* imageName,
free(outRows[i]);
}
free(outRows);
+ free(unknowns[0].data);
png_get_IHDR(write_ptr, write_info, &width, &height,
&bit_depth, &color_type, &interlace_type,
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 12a0445..e61010c 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -6,8 +6,10 @@
#include "Main.h"
#include "Bundle.h"
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <stdlib.h>
#include <getopt.h>
@@ -57,9 +59,9 @@ void usage(void)
" [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n"
" [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
" [--max-sdk-version VAL] [--app-version VAL] \\\n"
- " [--app-version-name TEXT] \\\n"
+ " [--app-version-name TEXT]\\\n"
" [-I base-package [-I base-package ...]] \\\n"
- " [-A asset-source-dir] [-P public-definitions-file] \\\n"
+ " [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] "
" [-F apk-file] [-J R-file-dir] \\\n"
" [raw-files-dir [raw-files-dir] ...]\n"
@@ -107,6 +109,7 @@ void usage(void)
" -z require localization of resource attributes marked with\n"
" localization=\"suggested\"\n"
" -A additional directory in which to find raw asset files\n"
+ " -G A file to output proguard options into.\n"
" -F specify the apk file to output\n"
" -I add an existing package to base include set\n"
" -J specify where to output R.java resource constant definitions\n"
@@ -272,6 +275,17 @@ int main(int argc, char* const argv[])
convertPath(argv[0]);
bundle.setAssetSourceDir(argv[0]);
break;
+ case 'G':
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '-G' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ convertPath(argv[0]);
+ bundle.setProguardFile(argv[0]);
+ break;
case 'I':
argc--;
argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index 65c0a8a..3ba4f39 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -6,10 +6,13 @@
#ifndef __MAIN_H
#define __MAIN_H
-#include <utils.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include "Bundle.h"
#include "AaptAssets.h"
-#include <utils/ZipFile.h>
+#include "ZipFile.h"
extern int doVersion(Bundle* bundle);
extern int doList(Bundle* bundle);
@@ -30,6 +33,8 @@ extern android::status_t buildResources(Bundle* bundle,
extern android::status_t writeResourceSymbols(Bundle* bundle,
const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
+extern android::status_t writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets);
+
extern bool isValidResourceType(const String8& type);
ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
@@ -38,4 +43,7 @@ extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
int dumpResources(Bundle* bundle);
+String8 getAttribute(const ResXMLTree& tree, const char* ns,
+ const char* attr, String8* outError);
+
#endif // __MAIN_H
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index eb7d6f5..999a5cf 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -7,8 +7,10 @@
#include "AaptAssets.h"
#include "ResourceTable.h"
-#include <utils.h>
-#include <utils/ZipFile.h>
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Errors.h>
#include <sys/types.h>
#include <dirent.h>
@@ -166,7 +168,7 @@ status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
delete zip; // close the file so we can remove it in Win32
zip = NULL;
if (unlink(outputFile.string()) != 0) {
- fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string());
+ fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
}
}
@@ -179,7 +181,7 @@ bail:
printf("Removing %s due to earlier failures\n", outputFile.string());
}
if (unlink(outputFile.string()) != 0) {
- fprintf(stderr, "WARNING: could not unlink '%s'\n", outputFile.string());
+ fprintf(stderr, "warning: could not unlink '%s'\n", outputFile.string());
}
}
@@ -281,7 +283,7 @@ bool processFile(Bundle* bundle, ZipFile* zip,
if (fileNameLen > excludeExtensionLen
&& (0 == strcmp(storageName.string() + (fileNameLen - excludeExtensionLen),
kExcludeExtension))) {
- fprintf(stderr, "WARNING: '%s' not added to Zip\n", storageName.string());
+ fprintf(stderr, "warning: '%s' not added to Zip\n", storageName.string());
return true;
}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 027e3ab..10849921 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -45,13 +45,6 @@ static String8 parseResourceName(const String8& leaf)
}
}
-class ResourceTypeSet : public RefBase,
- public KeyedVector<String8,sp<AaptGroup> >
-{
-public:
- ResourceTypeSet();
-};
-
ResourceTypeSet::ResourceTypeSet()
:RefBase(),
KeyedVector<String8,sp<AaptGroup> >()
@@ -181,7 +174,7 @@ static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNec
static status_t parsePackage(const sp<AaptAssets>& assets, const sp<AaptGroup>& grp)
{
if (grp->getFiles().size() != 1) {
- fprintf(stderr, "WARNING: Multiple AndroidManifest.xml files found, using %s\n",
+ fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
grp->getFiles().valueAt(0)->getPrintableSource().string());
}
@@ -279,15 +272,16 @@ static status_t preProcessImages(Bundle* bundle, const sp<AaptAssets>& assets,
ResourceDirIterator it(set, String8("drawable"));
Vector<sp<AaptFile> > newNameFiles;
Vector<String8> newNamePaths;
+ bool hasErrors = false;
ssize_t res;
while ((res=it.next()) == NO_ERROR) {
res = preProcessImage(bundle, assets, it.getFile(), NULL);
- if (res != NO_ERROR) {
- return res;
+ if (res < NO_ERROR) {
+ hasErrors = true;
}
}
- return NO_ERROR;
+ return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
status_t postProcessImages(const sp<AaptAssets>& assets,
@@ -295,15 +289,16 @@ status_t postProcessImages(const sp<AaptAssets>& assets,
const sp<ResourceTypeSet>& set)
{
ResourceDirIterator it(set, String8("drawable"));
+ bool hasErrors = false;
ssize_t res;
while ((res=it.next()) == NO_ERROR) {
res = postProcessImage(assets, table, it.getFile());
- if (res != NO_ERROR) {
- return res;
+ if (res < NO_ERROR) {
+ hasErrors = true;
}
}
- return res < NO_ERROR ? res : (status_t)NO_ERROR;
+ return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
}
static void collect_files(const sp<AaptDir>& dir,
@@ -426,17 +421,22 @@ static void checkForIds(const String8& path, ResXMLParser& parser)
if (code == ResXMLTree::START_TAG) {
ssize_t index = parser.indexOfAttribute(NULL, "id");
if (index >= 0) {
- fprintf(stderr, "%s:%d: WARNING: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
+ fprintf(stderr, "%s:%d: warning: found plain 'id' attribute; did you mean the new 'android:id' name?\n",
path.string(), parser.getLineNumber());
}
}
}
}
-static bool applyFileOverlay(const sp<AaptAssets>& assets,
+static bool applyFileOverlay(Bundle *bundle,
+ const sp<AaptAssets>& assets,
const sp<ResourceTypeSet>& baseSet,
const char *resType)
{
+ if (bundle->getVerbose()) {
+ printf("applyFileOverlay for %s\n", resType);
+ }
+
// Replace any base level files in this category with any found from the overlay
// Also add any found only in the overlay.
sp<AaptAssets> overlay = assets->getOverlay();
@@ -455,6 +455,9 @@ static bool applyFileOverlay(const sp<AaptAssets>& assets,
// non-overlay "baseset".
size_t overlayCount = overlaySet->size();
for (size_t overlayIndex=0; overlayIndex<overlayCount; overlayIndex++) {
+ if (bundle->getVerbose()) {
+ printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
+ }
size_t baseIndex = baseSet->indexOfKey(overlaySet->keyAt(overlayIndex));
if (baseIndex < UNKNOWN_ERROR) {
// look for same flavor. For a given file (strings.xml, for example)
@@ -462,30 +465,57 @@ static bool applyFileOverlay(const sp<AaptAssets>& assets,
// the same flavor.
sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
sp<AaptGroup> baseGroup = baseSet->valueAt(baseIndex);
-
- DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
- baseGroup->getFiles();
- DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
+
+ DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
overlayGroup->getFiles();
+ if (bundle->getVerbose()) {
+ DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > baseFiles =
+ baseGroup->getFiles();
+ for (size_t i=0; i < baseFiles.size(); i++) {
+ printf("baseFile %d has flavor %s\n", i,
+ baseFiles.keyAt(i).toString().string());
+ }
+ for (size_t i=0; i < overlayFiles.size(); i++) {
+ printf("overlayFile %d has flavor %s\n", i,
+ overlayFiles.keyAt(i).toString().string());
+ }
+ }
+
size_t overlayGroupSize = overlayFiles.size();
- for (size_t overlayGroupIndex = 0;
- overlayGroupIndex<overlayGroupSize;
+ for (size_t overlayGroupIndex = 0;
+ overlayGroupIndex<overlayGroupSize;
overlayGroupIndex++) {
- size_t baseFileIndex =
- baseFiles.indexOfKey(overlayFiles.keyAt(overlayGroupIndex));
+ size_t baseFileIndex =
+ baseGroup->getFiles().indexOfKey(overlayFiles.
+ keyAt(overlayGroupIndex));
if(baseFileIndex < UNKNOWN_ERROR) {
+ if (bundle->getVerbose()) {
+ printf("found a match (%d) for overlay file %s, for flavor %s\n",
+ baseFileIndex,
+ overlayGroup->getLeaf().string(),
+ overlayFiles.keyAt(overlayGroupIndex).toString().string());
+ }
baseGroup->removeFile(baseFileIndex);
} else {
// didn't find a match fall through and add it..
}
baseGroup->addFile(overlayFiles.valueAt(overlayGroupIndex));
+ assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
}
} else {
// this group doesn't exist (a file that's only in the overlay)
- fprintf(stderr, "aapt: error: "
- "*** Resource file '%s' exists only in an overlay\n",
- overlaySet->keyAt(overlayIndex).string());
- return false;
+ baseSet->add(overlaySet->keyAt(overlayIndex),
+ overlaySet->valueAt(overlayIndex));
+ // make sure all flavors are defined in the resources.
+ sp<AaptGroup> overlayGroup = overlaySet->valueAt(overlayIndex);
+ DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> > overlayFiles =
+ overlayGroup->getFiles();
+ size_t overlayGroupSize = overlayFiles.size();
+ for (size_t overlayGroupIndex = 0;
+ overlayGroupIndex<overlayGroupSize;
+ overlayGroupIndex++) {
+ assets->addGroupEntry(overlayFiles.keyAt(overlayGroupIndex));
+ }
}
}
// this overlay didn't have resources for this type
@@ -619,13 +649,13 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
current = current->getOverlay();
}
// apply the overlay files to the base set
- if (!applyFileOverlay(assets, drawables, "drawable") ||
- !applyFileOverlay(assets, layouts, "layout") ||
- !applyFileOverlay(assets, anims, "anim") ||
- !applyFileOverlay(assets, xmls, "xml") ||
- !applyFileOverlay(assets, raws, "raw") ||
- !applyFileOverlay(assets, colors, "color") ||
- !applyFileOverlay(assets, menus, "menu")) {
+ if (!applyFileOverlay(bundle, assets, drawables, "drawable") ||
+ !applyFileOverlay(bundle, assets, layouts, "layout") ||
+ !applyFileOverlay(bundle, assets, anims, "anim") ||
+ !applyFileOverlay(bundle, assets, xmls, "xml") ||
+ !applyFileOverlay(bundle, assets, raws, "raw") ||
+ !applyFileOverlay(bundle, assets, colors, "color") ||
+ !applyFileOverlay(bundle, assets, menus, "menu")) {
return UNKNOWN_ERROR;
}
@@ -1118,6 +1148,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile());
}
table.writePublicDefinitions(String16(assets->getPackage()), fp);
+ fclose(fp);
}
NOISY(
@@ -1135,7 +1166,6 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
return err;
}
}
-
return err;
}
@@ -1234,10 +1264,16 @@ static status_t writeLayoutClasses(
NA = idents.size();
+ bool deprecated = false;
+
String16 comment = symbols->getComment(realClassName);
fprintf(fp, "%s/** ", indentStr);
if (comment.size() > 0) {
- fprintf(fp, "%s\n", String8(comment).string());
+ String8 cmt(comment);
+ fprintf(fp, "%s\n", cmt.string());
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
+ }
} else {
fprintf(fp, "Attributes that can be used with a %s.\n", nclassName.string());
}
@@ -1313,6 +1349,10 @@ static status_t writeLayoutClasses(
}
fprintf(fp, "%s */\n", getIndentSpace(indent));
+ if (deprecated) {
+ fprintf(fp, "%s@Deprecated\n", indentStr);
+ }
+
fprintf(fp,
"%spublic static final int[] %s = {\n"
"%s",
@@ -1361,11 +1401,17 @@ static status_t writeLayoutClasses(
//printf("%s:%s/%s: 0x%08x\n", String8(package16).string(),
// String8(attr16).string(), String8(name16).string(), typeSpecFlags);
const bool pub = (typeSpecFlags&ResTable_typeSpec::SPEC_PUBLIC) != 0;
-
+
+ bool deprecated = false;
+
fprintf(fp, "%s/**\n", indentStr);
if (comment.size() > 0) {
+ String8 cmt(comment);
fprintf(fp, "%s <p>\n%s @attr description\n", indentStr, indentStr);
- fprintf(fp, "%s %s\n", indentStr, String8(comment).string());
+ fprintf(fp, "%s %s\n", indentStr, cmt.string());
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
+ }
} else {
fprintf(fp,
"%s <p>This symbol is the offset where the {@link %s.R.attr#%s}\n"
@@ -1377,7 +1423,11 @@ static status_t writeLayoutClasses(
indentStr, nclassName.string());
}
if (typeComment.size() > 0) {
- fprintf(fp, "\n\n%s %s\n", indentStr, String8(typeComment).string());
+ String8 cmt(typeComment);
+ fprintf(fp, "\n\n%s %s\n", indentStr, cmt.string());
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
+ }
}
if (comment.size() > 0) {
if (pub) {
@@ -1395,6 +1445,9 @@ static status_t writeLayoutClasses(
fprintf(fp, "%s @attr name %s:%s\n", indentStr,
"android", String8(name).string());
fprintf(fp, "%s*/\n", indentStr);
+ if (deprecated) {
+ fprintf(fp, "%s@Deprecated\n", indentStr);
+ }
fprintf(fp,
"%spublic static final int %s_%s = %d;\n",
indentStr, nclassName.string(),
@@ -1436,11 +1489,16 @@ static status_t writeSymbolClass(
}
String16 comment(sym.comment);
bool haveComment = false;
+ bool deprecated = false;
if (comment.size() > 0) {
haveComment = true;
+ String8 cmt(comment);
fprintf(fp,
"%s/** %s\n",
- getIndentSpace(indent), String8(comment).string());
+ getIndentSpace(indent), cmt.string());
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
+ }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
@@ -1448,20 +1506,25 @@ static status_t writeSymbolClass(
}
String16 typeComment(sym.typeComment);
if (typeComment.size() > 0) {
+ String8 cmt(typeComment);
if (!haveComment) {
haveComment = true;
fprintf(fp,
- "%s/** %s\n",
- getIndentSpace(indent), String8(typeComment).string());
+ "%s/** %s\n", getIndentSpace(indent), cmt.string());
} else {
fprintf(fp,
- "%s %s\n",
- getIndentSpace(indent), String8(typeComment).string());
+ "%s %s\n", getIndentSpace(indent), cmt.string());
+ }
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
}
}
if (haveComment) {
fprintf(fp,"%s */\n", getIndentSpace(indent));
}
+ if (deprecated) {
+ fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
+ }
fprintf(fp, "%spublic static final int %s=0x%08x;\n",
getIndentSpace(indent),
String8(name).string(), (int)sym.int32Val);
@@ -1480,17 +1543,25 @@ static status_t writeSymbolClass(
return UNKNOWN_ERROR;
}
String16 comment(sym.comment);
+ bool deprecated = false;
if (comment.size() > 0) {
+ String8 cmt(comment);
fprintf(fp,
"%s/** %s\n"
"%s */\n",
- getIndentSpace(indent), String8(comment).string(),
+ getIndentSpace(indent), cmt.string(),
getIndentSpace(indent));
+ if (strstr(cmt.string(), "@deprecated") != NULL) {
+ deprecated = true;
+ }
} else if (sym.isPublic && !includePrivate) {
sym.sourcePos.warning("No comment for public symbol %s:%s/%s",
assets->getPackage().string(), className.string(),
String8(sym.name).string());
}
+ if (deprecated) {
+ fprintf(fp, "%s@Deprecated\n", getIndentSpace(indent));
+ }
fprintf(fp, "%spublic static final String %s=\"%s\";\n",
getIndentSpace(indent),
String8(name).string(), sym.stringVal.string());
@@ -1585,3 +1656,230 @@ status_t writeResourceSymbols(Bundle* bundle, const sp<AaptAssets>& assets,
return NO_ERROR;
}
+
+
+
+class ProguardKeepSet
+{
+public:
+ // { rule --> { file locations } }
+ KeyedVector<String8, SortedVector<String8> > rules;
+
+ void add(const String8& rule, const String8& where);
+};
+
+void ProguardKeepSet::add(const String8& rule, const String8& where)
+{
+ ssize_t index = rules.indexOfKey(rule);
+ if (index < 0) {
+ index = rules.add(rule, SortedVector<String8>());
+ }
+ rules.editValueAt(index).add(where);
+}
+
+status_t
+writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+{
+ status_t err;
+ ResXMLTree tree;
+ size_t len;
+ ResXMLTree::event_code_t code;
+ int depth = 0;
+ bool inApplication = false;
+ String8 error;
+ sp<AaptGroup> assGroup;
+ sp<AaptFile> assFile;
+ String8 pkg;
+
+ // First, look for a package file to parse. This is required to
+ // be able to generate the resource information.
+ assGroup = assets->getFiles().valueFor(String8("AndroidManifest.xml"));
+ if (assGroup == NULL) {
+ fprintf(stderr, "ERROR: No AndroidManifest.xml file found.\n");
+ return -1;
+ }
+
+ if (assGroup->getFiles().size() != 1) {
+ fprintf(stderr, "warning: Multiple AndroidManifest.xml files found, using %s\n",
+ assGroup->getFiles().valueAt(0)->getPrintableSource().string());
+ }
+
+ assFile = assGroup->getFiles().valueAt(0);
+
+ err = parseXMLResource(assFile, &tree);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ tree.restart();
+
+ while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG) {
+ if (/* name == "Application" && */ depth == 2) {
+ inApplication = false;
+ }
+ depth--;
+ continue;
+ }
+ if (code != ResXMLTree::START_TAG) {
+ continue;
+ }
+ depth++;
+ String8 tag(tree.getElementName(&len));
+ // printf("Depth %d tag %s\n", depth, tag.string());
+ if (depth == 1) {
+ if (tag != "manifest") {
+ fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
+ return -1;
+ }
+ pkg = getAttribute(tree, NULL, "package", NULL);
+ } else if (depth == 2 && tag == "application") {
+ inApplication = true;
+ }
+ if (inApplication) {
+ if (tag == "application" || tag == "activity" || tag == "service" || tag == "receiver"
+ || tag == "provider") {
+ String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+ "name", &error);
+ if (error != "") {
+ fprintf(stderr, "ERROR: %s\n", error.string());
+ return -1;
+ }
+ // asdf --> package.asdf
+ // .asdf .a.b --> package.asdf package.a.b
+ // asdf.adsf --> asdf.asdf
+ String8 rule("-keep class ");
+ const char* p = name.string();
+ const char* q = strchr(p, '.');
+ if (p == q) {
+ rule += pkg;
+ rule += name;
+ } else if (q == NULL) {
+ rule += pkg;
+ rule += ".";
+ rule += name;
+ } else {
+ rule += name;
+ }
+
+ String8 location = tag;
+ location += " ";
+ location += assFile->getSourceFile();
+ char lineno[20];
+ sprintf(lineno, ":%d", tree.getLineNumber());
+ location += lineno;
+
+ keep->add(rule, location);
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t
+writeProguardForLayout(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile)
+{
+ status_t err;
+ ResXMLTree tree;
+ size_t len;
+ ResXMLTree::event_code_t code;
+
+ err = parseXMLResource(layoutFile, &tree);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ tree.restart();
+
+ while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code != ResXMLTree::START_TAG) {
+ continue;
+ }
+ String8 tag(tree.getElementName(&len));
+
+ // If there is no '.', we'll assume that it's one of the built in names.
+ if (strchr(tag.string(), '.')) {
+ String8 rule("-keep class ");
+ rule += tag;
+ rule += " { <init>(...); }";
+
+ String8 location("view ");
+ location += layoutFile->getSourceFile();
+ char lineno[20];
+ sprintf(lineno, ":%d", tree.getLineNumber());
+ location += lineno;
+
+ keep->add(rule, location);
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t
+writeProguardForLayouts(ProguardKeepSet* keep, const sp<AaptAssets>& assets)
+{
+ status_t err;
+ sp<AaptDir> layout = assets->resDir(String8("layout"));
+
+ if (layout != NULL) {
+ const KeyedVector<String8,sp<AaptGroup> > groups = layout->getFiles();
+ const size_t N = groups.size();
+ for (size_t i=0; i<N; i++) {
+ const sp<AaptGroup>& group = groups.valueAt(i);
+ const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
+ const size_t M = files.size();
+ for (size_t j=0; j<M; j++) {
+ err = writeProguardForLayout(keep, files.valueAt(j));
+ if (err < 0) {
+ return err;
+ }
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t
+writeProguardFile(Bundle* bundle, const sp<AaptAssets>& assets)
+{
+ status_t err = -1;
+
+ if (!bundle->getProguardFile()) {
+ return NO_ERROR;
+ }
+
+ ProguardKeepSet keep;
+
+ err = writeProguardForAndroidManifest(&keep, assets);
+ if (err < 0) {
+ return err;
+ }
+
+ err = writeProguardForLayouts(&keep, assets);
+ if (err < 0) {
+ return err;
+ }
+
+ FILE* fp = fopen(bundle->getProguardFile(), "w+");
+ if (fp == NULL) {
+ fprintf(stderr, "ERROR: Unable to open class file %s: %s\n",
+ bundle->getProguardFile(), strerror(errno));
+ return UNKNOWN_ERROR;
+ }
+
+ const KeyedVector<String8, SortedVector<String8> >& rules = keep.rules;
+ const size_t N = rules.size();
+ for (size_t i=0; i<N; i++) {
+ const SortedVector<String8>& locations = rules.valueAt(i);
+ const size_t M = locations.size();
+ for (size_t j=0; j<M; j++) {
+ fprintf(fp, "# %s\n", locations.itemAt(j).string());
+ }
+ fprintf(fp, "%s\n\n", rules.keyAt(i).string());
+ }
+ fclose(fp);
+
+ return err;
+}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index b004664..3cf6a71 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -663,6 +663,7 @@ status_t compileResourceFile(Bundle* bundle,
const String16 public16("public");
const String16 public_padding16("public-padding");
const String16 private_symbols16("private-symbols");
+ const String16 add_resource16("add-resource");
const String16 skip16("skip");
const String16 eat_comment16("eat-comment");
@@ -960,6 +961,36 @@ status_t compileResourceFile(Bundle* bundle,
}
continue;
+ } else if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+ SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
+
+ String16 typeName;
+ ssize_t typeIdx = block.indexOfAttribute(NULL, "type");
+ if (typeIdx < 0) {
+ srcPos.error("A 'type' attribute is required for <add-resource>\n");
+ hasErrors = localHasErrors = true;
+ }
+ typeName = String16(block.getAttributeStringValue(typeIdx, &len));
+
+ String16 name;
+ ssize_t nameIdx = block.indexOfAttribute(NULL, "name");
+ if (nameIdx < 0) {
+ srcPos.error("A 'name' attribute is required for <add-resource>\n");
+ hasErrors = localHasErrors = true;
+ }
+ name = String16(block.getAttributeStringValue(nameIdx, &len));
+
+ outTable->canAddEntry(srcPos, myPackage, typeName, name);
+
+ while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
+ if (code == ResXMLTree::END_TAG) {
+ if (strcmp16(block.getElementName(&len), add_resource16.string()) == 0) {
+ break;
+ }
+ }
+ }
+ continue;
+
} else if (strcmp16(block.getElementName(&len), declare_styleable16.string()) == 0) {
SourcePos srcPos(in->getPrintableSource(), block.getLineNumber());
@@ -1557,9 +1588,21 @@ status_t ResourceTable::startBag(const SourcePos& sourcePos,
}
#endif
if (overlay && !hasBagOrEntry(package, type, name)) {
- sourcePos.error("Can't add new bags in an overlay. See '%s'\n",
- String8(name).string());
- return UNKNOWN_ERROR;
+ bool canAdd = false;
+ sp<Package> p = mPackages.valueFor(package);
+ if (p != NULL) {
+ sp<Type> t = p->getTypes().valueFor(type);
+ if (t != NULL) {
+ if (t->getCanAddEntries().indexOf(name) >= 0) {
+ canAdd = true;
+ }
+ }
+ }
+ if (!canAdd) {
+ sourcePos.error("Resource does not already exist in overlay at '%s'; use <add-resource> to add.\n",
+ String8(name).string());
+ return UNKNOWN_ERROR;
+ }
}
sp<Entry> e = getEntry(package, type, name, sourcePos, overlay, params);
if (e == NULL) {
@@ -1724,6 +1767,15 @@ bool ResourceTable::appendTypeComment(const String16& package,
return false;
}
+void ResourceTable::canAddEntry(const SourcePos& pos,
+ const String16& package, const String16& type, const String16& name)
+{
+ sp<Type> t = getType(package, type, pos);
+ if (t != NULL) {
+ t->canAddEntry(name);
+ }
+}
+
size_t ResourceTable::size() const {
return mPackages.size();
}
@@ -2312,13 +2364,12 @@ ResourceTable::validateLocalizations(void)
String8 region(config.string(), 2);
if (configSet.find(region) == configSet.end()) {
if (configSet.count(defaultLocale) == 0) {
- fprintf(stdout, "aapt: error: "
+ fprintf(stdout, "aapt: warning: "
"*** string '%s' has no default or required localization "
"for '%s' in %s\n",
String8(nameIter->first).string(),
config.string(),
mBundle->getResourceSourceDirs()[0]);
- err = UNKNOWN_ERROR;
}
}
}
@@ -3215,6 +3266,11 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos,
return NO_ERROR;
}
+void ResourceTable::Type::canAddEntry(const String16& name)
+{
+ mCanAddEntries.add(name);
+}
+
sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
const SourcePos& sourcePos,
const ResTable_config* config,
@@ -3224,9 +3280,10 @@ sp<ResourceTable::Entry> ResourceTable::Type::getEntry(const String16& entry,
int pos = -1;
sp<ConfigList> c = mConfigs.valueFor(entry);
if (c == NULL) {
- if (overlay == true) {
- sourcePos.error("Resource %s appears in overlay but not"
- " in the base package.\n", String8(entry).string());
+ if (overlay == true && mCanAddEntries.indexOf(entry) < 0) {
+ sourcePos.error("Resource at %s appears in overlay but not"
+ " in the base package; use <add-resource> to add.\n",
+ String8(entry).string());
return NULL;
}
c = new ConfigList(entry, sourcePos);
@@ -3554,26 +3611,26 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
}
if (p == NULL) {
- fprintf(stderr, "WARNING: Package not found for resource #%08x\n", resID);
+ fprintf(stderr, "warning: Package not found for resource #%08x\n", resID);
return NULL;
}
int tid = Res_GETTYPE(resID);
if (tid < 0 || tid >= (int)p->getOrderedTypes().size()) {
- fprintf(stderr, "WARNING: Type not found for resource #%08x\n", resID);
+ fprintf(stderr, "warning: Type not found for resource #%08x\n", resID);
return NULL;
}
sp<Type> t = p->getOrderedTypes()[tid];
int eid = Res_GETENTRY(resID);
if (eid < 0 || eid >= (int)t->getOrderedConfigs().size()) {
- fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+ fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
return NULL;
}
sp<ConfigList> c = t->getOrderedConfigs()[eid];
if (c == NULL) {
- fprintf(stderr, "WARNING: Entry not found for resource #%08x\n", resID);
+ fprintf(stderr, "warning: Entry not found for resource #%08x\n", resID);
return NULL;
}
@@ -3581,7 +3638,7 @@ sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
if (config) cdesc = *config;
sp<Entry> e = c->getEntries().valueFor(cdesc);
if (c == NULL) {
- fprintf(stderr, "WARNING: Entry configuration not found for resource #%08x\n", resID);
+ fprintf(stderr, "warning: Entry configuration not found for resource #%08x\n", resID);
return NULL;
}
@@ -3599,7 +3656,7 @@ const ResourceTable::Item* ResourceTable::getItem(uint32_t resID, uint32_t attrI
for (size_t i=0; i<N; i++) {
const Item& it = e->getBag().valueAt(i);
if (it.bagKeyId == 0) {
- fprintf(stderr, "WARNING: ID not yet assigned to '%s' in bag '%s'\n",
+ fprintf(stderr, "warning: ID not yet assigned to '%s' in bag '%s'\n",
String8(e->getName()).string(),
String8(e->getBag().keyAt(i)).string());
}
@@ -3627,7 +3684,7 @@ bool ResourceTable::getItemValue(
break;
}
}
- fprintf(stderr, "WARNING: Circular reference detected in key '%s' of bag '%s'\n",
+ fprintf(stderr, "warning: Circular reference detected in key '%s' of bag '%s'\n",
String8(e->getName()).string(),
String8(e->getBag().keyAt(i)).string());
return false;
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index ec4331a..caa01b3 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -132,6 +132,9 @@ public:
const String16& name,
const String16& comment);
+ void canAddEntry(const SourcePos& pos,
+ const String16& package, const String16& type, const String16& name);
+
size_t size() const;
size_t numLocalResources() const;
bool hasResources() const;
@@ -413,7 +416,9 @@ public:
status_t addPublic(const SourcePos& pos,
const String16& name,
const uint32_t ident);
-
+
+ void canAddEntry(const String16& name);
+
String16 getName() const { return mName; }
sp<Entry> getEntry(const String16& entry,
const SourcePos& pos,
@@ -435,6 +440,8 @@ public:
const DefaultKeyedVector<String16, sp<ConfigList> >& getConfigs() const { return mConfigs; }
const Vector<sp<ConfigList> >& getOrderedConfigs() const { return mOrderedConfigs; }
+ const SortedVector<String16>& getCanAddEntries() const { return mCanAddEntries; }
+
const SourcePos& getPos() const { return mPos; }
private:
String16 mName;
@@ -443,6 +450,7 @@ public:
SortedVector<ConfigDescription> mUniqueConfigs;
DefaultKeyedVector<String16, sp<ConfigList> > mConfigs;
Vector<sp<ConfigList> > mOrderedConfigs;
+ SortedVector<String16> mCanAddEntries;
int32_t mPublicIndex;
int32_t mIndex;
SourcePos mPos;
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index 2761d18..e2a921c 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -86,7 +86,7 @@ ErrorPos::operator=(const ErrorPos& rhs)
void
ErrorPos::print(FILE* to) const
{
- const char* type = fatal ? "ERROR" : "WARNING";
+ const char* type = fatal ? "error:" : "warning:";
if (this->line >= 0) {
fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 2a85bc7..d4d2a45 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -219,8 +219,13 @@ moveon:
}
spanStack.pop();
- if (empty) {
- fprintf(stderr, "%s:%d: WARNING: empty '%s' span found in text '%s'\n",
+ /*
+ * This warning seems to be just an irritation to most people,
+ * since it is typically introduced by translators who then never
+ * see the warning.
+ */
+ if (0 && empty) {
+ fprintf(stderr, "%s:%d: warning: empty '%s' span found in text '%s'\n",
fileName, inXml->getLineNumber(),
String8(spanTag).string(), String8(*outString).string());
@@ -486,6 +491,7 @@ XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2
XMLNode::XMLNode(const String8& filename)
: mFilename(filename)
{
+ memset(&mCharsValue, 0, sizeof(mCharsValue));
}
XMLNode::type XMLNode::getType() const
diff --git a/tools/aapt/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
new file mode 100644
index 0000000..a0b54c2
--- /dev/null
+++ b/tools/aapt/ZipEntry.cpp
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// Access to entries in a Zip archive.
+//
+
+#define LOG_TAG "zip"
+
+#include "ZipEntry.h"
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Initialize a new ZipEntry structure from a FILE* positioned at a
+ * CentralDirectoryEntry.
+ *
+ * On exit, the file pointer will be at the start of the next CDE or
+ * at the EOCD.
+ */
+status_t ZipEntry::initFromCDE(FILE* fp)
+{
+ status_t result;
+ long posn;
+ bool hasDD;
+
+ //LOGV("initFromCDE ---\n");
+
+ /* read the CDE */
+ result = mCDE.read(fp);
+ if (result != NO_ERROR) {
+ LOGD("mCDE.read failed\n");
+ return result;
+ }
+
+ //mCDE.dump();
+
+ /* using the info in the CDE, go load up the LFH */
+ posn = ftell(fp);
+ if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
+ LOGD("local header seek failed (%ld)\n",
+ mCDE.mLocalHeaderRelOffset);
+ return UNKNOWN_ERROR;
+ }
+
+ result = mLFH.read(fp);
+ if (result != NO_ERROR) {
+ LOGD("mLFH.read failed\n");
+ return result;
+ }
+
+ if (fseek(fp, posn, SEEK_SET) != 0)
+ return UNKNOWN_ERROR;
+
+ //mLFH.dump();
+
+ /*
+ * We *might* need to read the Data Descriptor at this point and
+ * integrate it into the LFH. If this bit is set, the CRC-32,
+ * compressed size, and uncompressed size will be zero. In practice
+ * these seem to be rare.
+ */
+ hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
+ if (hasDD) {
+ // do something clever
+ //LOGD("+++ has data descriptor\n");
+ }
+
+ /*
+ * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
+ * flag is set, because the LFH is incomplete. (Not a problem, since we
+ * prefer the CDE values.)
+ */
+ if (!hasDD && !compareHeaders()) {
+ LOGW("warning: header mismatch\n");
+ // keep going?
+ }
+
+ /*
+ * If the mVersionToExtract is greater than 20, we may have an
+ * issue unpacking the record -- could be encrypted, compressed
+ * with something we don't support, or use Zip64 extensions. We
+ * can defer worrying about that to when we're extracting data.
+ */
+
+ return NO_ERROR;
+}
+
+/*
+ * Initialize a new entry. Pass in the file name and an optional comment.
+ *
+ * Initializes the CDE and the LFH.
+ */
+void ZipEntry::initNew(const char* fileName, const char* comment)
+{
+ assert(fileName != NULL && *fileName != '\0'); // name required
+
+ /* most fields are properly initialized by constructor */
+ mCDE.mVersionMadeBy = kDefaultMadeBy;
+ mCDE.mVersionToExtract = kDefaultVersion;
+ mCDE.mCompressionMethod = kCompressStored;
+ mCDE.mFileNameLength = strlen(fileName);
+ if (comment != NULL)
+ mCDE.mFileCommentLength = strlen(comment);
+ mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
+
+ if (mCDE.mFileNameLength > 0) {
+ mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+ strcpy((char*) mCDE.mFileName, fileName);
+ }
+ if (mCDE.mFileCommentLength > 0) {
+ /* TODO: stop assuming null-terminated ASCII here? */
+ mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+ strcpy((char*) mCDE.mFileComment, comment);
+ }
+
+ copyCDEtoLFH();
+}
+
+/*
+ * Initialize a new entry, starting with the ZipEntry from a different
+ * archive.
+ *
+ * Initializes the CDE and the LFH.
+ */
+status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
+ const ZipEntry* pEntry)
+{
+ /*
+ * Copy everything in the CDE over, then fix up the hairy bits.
+ */
+ memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
+
+ if (mCDE.mFileNameLength > 0) {
+ mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+ if (mCDE.mFileName == NULL)
+ return NO_MEMORY;
+ strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
+ }
+ if (mCDE.mFileCommentLength > 0) {
+ mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+ if (mCDE.mFileComment == NULL)
+ return NO_MEMORY;
+ strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
+ }
+ if (mCDE.mExtraFieldLength > 0) {
+ /* we null-terminate this, though it may not be a string */
+ mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
+ if (mCDE.mExtraField == NULL)
+ return NO_MEMORY;
+ memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
+ mCDE.mExtraFieldLength+1);
+ }
+
+ /* construct the LFH from the CDE */
+ copyCDEtoLFH();
+
+ /*
+ * The LFH "extra" field is independent of the CDE "extra", so we
+ * handle it here.
+ */
+ assert(mLFH.mExtraField == NULL);
+ mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
+ if (mLFH.mExtraFieldLength > 0) {
+ mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
+ if (mLFH.mExtraField == NULL)
+ return NO_MEMORY;
+ memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
+ mLFH.mExtraFieldLength+1);
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Insert pad bytes in the LFH by tweaking the "extra" field. This will
+ * potentially confuse something that put "extra" data in here earlier,
+ * but I can't find an actual problem.
+ */
+status_t ZipEntry::addPadding(int padding)
+{
+ if (padding <= 0)
+ return INVALID_OPERATION;
+
+ //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+ // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
+
+ if (mLFH.mExtraFieldLength > 0) {
+ /* extend existing field */
+ unsigned char* newExtra;
+
+ newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
+ if (newExtra == NULL)
+ return NO_MEMORY;
+ memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
+ memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
+
+ delete[] mLFH.mExtraField;
+ mLFH.mExtraField = newExtra;
+ mLFH.mExtraFieldLength += padding;
+ } else {
+ /* create new field */
+ mLFH.mExtraField = new unsigned char[padding];
+ memset(mLFH.mExtraField, 0, padding);
+ mLFH.mExtraFieldLength = padding;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Set the fields in the LFH equal to the corresponding fields in the CDE.
+ *
+ * This does not touch the LFH "extra" field.
+ */
+void ZipEntry::copyCDEtoLFH(void)
+{
+ mLFH.mVersionToExtract = mCDE.mVersionToExtract;
+ mLFH.mGPBitFlag = mCDE.mGPBitFlag;
+ mLFH.mCompressionMethod = mCDE.mCompressionMethod;
+ mLFH.mLastModFileTime = mCDE.mLastModFileTime;
+ mLFH.mLastModFileDate = mCDE.mLastModFileDate;
+ mLFH.mCRC32 = mCDE.mCRC32;
+ mLFH.mCompressedSize = mCDE.mCompressedSize;
+ mLFH.mUncompressedSize = mCDE.mUncompressedSize;
+ mLFH.mFileNameLength = mCDE.mFileNameLength;
+ // the "extra field" is independent
+
+ delete[] mLFH.mFileName;
+ if (mLFH.mFileNameLength > 0) {
+ mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
+ strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
+ } else {
+ mLFH.mFileName = NULL;
+ }
+}
+
+/*
+ * Set some information about a file after we add it.
+ */
+void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+ int compressionMethod)
+{
+ mCDE.mCompressionMethod = compressionMethod;
+ mCDE.mCRC32 = crc32;
+ mCDE.mCompressedSize = compLen;
+ mCDE.mUncompressedSize = uncompLen;
+ mCDE.mCompressionMethod = compressionMethod;
+ if (compressionMethod == kCompressDeflated) {
+ mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
+ }
+ copyCDEtoLFH();
+}
+
+/*
+ * See if the data in mCDE and mLFH match up. This is mostly useful for
+ * debugging these classes, but it can be used to identify damaged
+ * archives.
+ *
+ * Returns "false" if they differ.
+ */
+bool ZipEntry::compareHeaders(void) const
+{
+ if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
+ LOGV("cmp: VersionToExtract\n");
+ return false;
+ }
+ if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
+ LOGV("cmp: GPBitFlag\n");
+ return false;
+ }
+ if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
+ LOGV("cmp: CompressionMethod\n");
+ return false;
+ }
+ if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
+ LOGV("cmp: LastModFileTime\n");
+ return false;
+ }
+ if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
+ LOGV("cmp: LastModFileDate\n");
+ return false;
+ }
+ if (mCDE.mCRC32 != mLFH.mCRC32) {
+ LOGV("cmp: CRC32\n");
+ return false;
+ }
+ if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
+ LOGV("cmp: CompressedSize\n");
+ return false;
+ }
+ if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
+ LOGV("cmp: UncompressedSize\n");
+ return false;
+ }
+ if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
+ LOGV("cmp: FileNameLength\n");
+ return false;
+ }
+#if 0 // this seems to be used for padding, not real data
+ if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
+ LOGV("cmp: ExtraFieldLength\n");
+ return false;
+ }
+#endif
+ if (mCDE.mFileName != NULL) {
+ if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
+ LOGV("cmp: FileName\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ * Convert the DOS date/time stamp into a UNIX time stamp.
+ */
+time_t ZipEntry::getModWhen(void) const
+{
+ struct tm parts;
+
+ parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
+ parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
+ parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
+ parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
+ parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
+ parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
+ parts.tm_wday = parts.tm_yday = 0;
+ parts.tm_isdst = -1; // DST info "not available"
+
+ return mktime(&parts);
+}
+
+/*
+ * Set the CDE/LFH timestamp from UNIX time.
+ */
+void ZipEntry::setModWhen(time_t when)
+{
+#ifdef HAVE_LOCALTIME_R
+ struct tm tmResult;
+#endif
+ time_t even;
+ unsigned short zdate, ztime;
+
+ struct tm* ptm;
+
+ /* round up to an even number of seconds */
+ even = (time_t)(((unsigned long)(when) + 1) & (~1));
+
+ /* expand */
+#ifdef HAVE_LOCALTIME_R
+ ptm = localtime_r(&even, &tmResult);
+#else
+ ptm = localtime(&even);
+#endif
+
+ int year;
+ year = ptm->tm_year;
+ if (year < 80)
+ year = 80;
+
+ zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
+ ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+
+ mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
+ mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
+}
+
+
+/*
+ * ===========================================================================
+ * ZipEntry::LocalFileHeader
+ * ===========================================================================
+ */
+
+/*
+ * Read a local file header.
+ *
+ * On entry, "fp" points to the signature at the start of the header.
+ * On exit, "fp" points to the start of data.
+ */
+status_t ZipEntry::LocalFileHeader::read(FILE* fp)
+{
+ status_t result = NO_ERROR;
+ unsigned char buf[kLFHLen];
+
+ assert(mFileName == NULL);
+ assert(mExtraField == NULL);
+
+ if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+ LOGD("whoops: didn't find expected signature\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
+ mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
+ mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
+ mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
+ mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
+ mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
+ mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
+ mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
+ mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
+ mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
+
+ // TODO: validate sizes
+
+ /* grab filename */
+ if (mFileNameLength != 0) {
+ mFileName = new unsigned char[mFileNameLength+1];
+ if (mFileName == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileName[mFileNameLength] = '\0';
+ }
+
+ /* grab extra field */
+ if (mExtraFieldLength != 0) {
+ mExtraField = new unsigned char[mExtraFieldLength+1];
+ if (mExtraField == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mExtraField[mExtraFieldLength] = '\0';
+ }
+
+bail:
+ return result;
+}
+
+/*
+ * Write a local file header.
+ */
+status_t ZipEntry::LocalFileHeader::write(FILE* fp)
+{
+ unsigned char buf[kLFHLen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
+ ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
+ ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
+ ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
+ ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
+ ZipEntry::putLongLE(&buf[0x0e], mCRC32);
+ ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
+ ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
+ ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
+ ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
+
+ if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
+ return UNKNOWN_ERROR;
+
+ /* write filename */
+ if (mFileNameLength != 0) {
+ if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write "extra field" */
+ if (mExtraFieldLength != 0) {
+ if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+
+/*
+ * Dump the contents of a LocalFileHeader object.
+ */
+void ZipEntry::LocalFileHeader::dump(void) const
+{
+ LOGD(" LocalFileHeader contents:\n");
+ LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
+ mVersionToExtract, mGPBitFlag, mCompressionMethod);
+ LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ mLastModFileTime, mLastModFileDate, mCRC32);
+ LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ mCompressedSize, mUncompressedSize);
+ LOGD(" filenameLen=%u extraLen=%u\n",
+ mFileNameLength, mExtraFieldLength);
+ if (mFileName != NULL)
+ LOGD(" filename: '%s'\n", mFileName);
+}
+
+
+/*
+ * ===========================================================================
+ * ZipEntry::CentralDirEntry
+ * ===========================================================================
+ */
+
+/*
+ * Read the central dir entry that appears next in the file.
+ *
+ * On entry, "fp" should be positioned on the signature bytes for the
+ * entry. On exit, "fp" will point at the signature word for the next
+ * entry or for the EOCD.
+ */
+status_t ZipEntry::CentralDirEntry::read(FILE* fp)
+{
+ status_t result = NO_ERROR;
+ unsigned char buf[kCDELen];
+
+ /* no re-use */
+ assert(mFileName == NULL);
+ assert(mExtraField == NULL);
+ assert(mFileComment == NULL);
+
+ if (fread(buf, 1, kCDELen, fp) != kCDELen) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+ LOGD("Whoops: didn't find expected signature\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
+ mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
+ mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
+ mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
+ mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
+ mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
+ mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
+ mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
+ mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
+ mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
+ mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
+ mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
+ mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
+ mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
+ mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
+ mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
+
+ // TODO: validate sizes and offsets
+
+ /* grab filename */
+ if (mFileNameLength != 0) {
+ mFileName = new unsigned char[mFileNameLength+1];
+ if (mFileName == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileName[mFileNameLength] = '\0';
+ }
+
+ /* read "extra field" */
+ if (mExtraFieldLength != 0) {
+ mExtraField = new unsigned char[mExtraFieldLength+1];
+ if (mExtraField == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mExtraField[mExtraFieldLength] = '\0';
+ }
+
+
+ /* grab comment, if any */
+ if (mFileCommentLength != 0) {
+ mFileComment = new unsigned char[mFileCommentLength+1];
+ if (mFileComment == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+ {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileComment[mFileCommentLength] = '\0';
+ }
+
+bail:
+ return result;
+}
+
+/*
+ * Write a central dir entry.
+ */
+status_t ZipEntry::CentralDirEntry::write(FILE* fp)
+{
+ unsigned char buf[kCDELen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
+ ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
+ ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
+ ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
+ ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
+ ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
+ ZipEntry::putLongLE(&buf[0x10], mCRC32);
+ ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
+ ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
+ ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
+ ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
+ ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
+ ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
+ ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
+ ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
+ ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
+
+ if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
+ return UNKNOWN_ERROR;
+
+ /* write filename */
+ if (mFileNameLength != 0) {
+ if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write "extra field" */
+ if (mExtraFieldLength != 0) {
+ if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write comment */
+ if (mFileCommentLength != 0) {
+ if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Dump the contents of a CentralDirEntry object.
+ */
+void ZipEntry::CentralDirEntry::dump(void) const
+{
+ LOGD(" CentralDirEntry contents:\n");
+ LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+ mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
+ LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ mLastModFileTime, mLastModFileDate, mCRC32);
+ LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ mCompressedSize, mUncompressedSize);
+ LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
+ mFileNameLength, mExtraFieldLength, mFileCommentLength);
+ LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+ mDiskNumberStart, mInternalAttrs, mExternalAttrs,
+ mLocalHeaderRelOffset);
+
+ if (mFileName != NULL)
+ LOGD(" filename: '%s'\n", mFileName);
+ if (mFileComment != NULL)
+ LOGD(" comment: '%s'\n", mFileComment);
+}
+
diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h
new file mode 100644
index 0000000..7f721b4
--- /dev/null
+++ b/tools/aapt/ZipEntry.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// Zip archive entries.
+//
+// The ZipEntry class is tightly meshed with the ZipFile class.
+//
+#ifndef __LIBS_ZIPENTRY_H
+#define __LIBS_ZIPENTRY_H
+
+#include <utils/Errors.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace android {
+
+class ZipFile;
+
+/*
+ * ZipEntry objects represent a single entry in a Zip archive.
+ *
+ * You can use one of these to get or set information about an entry, but
+ * there are no functions here for accessing the data itself. (We could
+ * tuck a pointer to the ZipFile in here for convenience, but that raises
+ * the likelihood of using ZipEntry objects after discarding the ZipFile.)
+ *
+ * File information is stored in two places: next to the file data (the Local
+ * File Header, and possibly a Data Descriptor), and at the end of the file
+ * (the Central Directory Entry). The two must be kept in sync.
+ */
+class ZipEntry {
+public:
+ friend class ZipFile;
+
+ ZipEntry(void)
+ : mDeleted(false), mMarked(false)
+ {}
+ ~ZipEntry(void) {}
+
+ /*
+ * Returns "true" if the data is compressed.
+ */
+ bool isCompressed(void) const {
+ return mCDE.mCompressionMethod != kCompressStored;
+ }
+ int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
+
+ /*
+ * Return the uncompressed length.
+ */
+ off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
+
+ /*
+ * Return the compressed length. For uncompressed data, this returns
+ * the same thing as getUncompresesdLen().
+ */
+ off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
+
+ /*
+ * Return the absolute file offset of the start of the compressed or
+ * uncompressed data.
+ */
+ off_t getFileOffset(void) const {
+ return mCDE.mLocalHeaderRelOffset +
+ LocalFileHeader::kLFHLen +
+ mLFH.mFileNameLength +
+ mLFH.mExtraFieldLength;
+ }
+
+ /*
+ * Return the data CRC.
+ */
+ unsigned long getCRC32(void) const { return mCDE.mCRC32; }
+
+ /*
+ * Return file modification time in UNIX seconds-since-epoch.
+ */
+ time_t getModWhen(void) const;
+
+ /*
+ * Return the archived file name.
+ */
+ const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
+
+ /*
+ * Application-defined "mark". Can be useful when synchronizing the
+ * contents of an archive with contents on disk.
+ */
+ bool getMarked(void) const { return mMarked; }
+ void setMarked(bool val) { mMarked = val; }
+
+ /*
+ * Some basic functions for raw data manipulation. "LE" means
+ * Little Endian.
+ */
+ static inline unsigned short getShortLE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8);
+ }
+ static inline unsigned long getLongLE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ }
+ static inline void putShortLE(unsigned char* buf, short val) {
+ buf[0] = (unsigned char) val;
+ buf[1] = (unsigned char) (val >> 8);
+ }
+ static inline void putLongLE(unsigned char* buf, long val) {
+ buf[0] = (unsigned char) val;
+ buf[1] = (unsigned char) (val >> 8);
+ buf[2] = (unsigned char) (val >> 16);
+ buf[3] = (unsigned char) (val >> 24);
+ }
+
+ /* defined for Zip archives */
+ enum {
+ kCompressStored = 0, // no compression
+ // shrunk = 1,
+ // reduced 1 = 2,
+ // reduced 2 = 3,
+ // reduced 3 = 4,
+ // reduced 4 = 5,
+ // imploded = 6,
+ // tokenized = 7,
+ kCompressDeflated = 8, // standard deflate
+ // Deflate64 = 9,
+ // lib imploded = 10,
+ // reserved = 11,
+ // bzip2 = 12,
+ };
+
+ /*
+ * Deletion flag. If set, the entry will be removed on the next
+ * call to "flush".
+ */
+ bool getDeleted(void) const { return mDeleted; }
+
+protected:
+ /*
+ * Initialize the structure from the file, which is pointing at
+ * our Central Directory entry.
+ */
+ status_t initFromCDE(FILE* fp);
+
+ /*
+ * Initialize the structure for a new file. We need the filename
+ * and comment so that we can properly size the LFH area. The
+ * filename is mandatory, the comment is optional.
+ */
+ void initNew(const char* fileName, const char* comment);
+
+ /*
+ * Initialize the structure with the contents of a ZipEntry from
+ * another file.
+ */
+ status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+
+ /*
+ * Add some pad bytes to the LFH. We do this by adding or resizing
+ * the "extra" field.
+ */
+ status_t addPadding(int padding);
+
+ /*
+ * Set information about the data for this entry.
+ */
+ void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+ int compressionMethod);
+
+ /*
+ * Set the modification date.
+ */
+ void setModWhen(time_t when);
+
+ /*
+ * Return the offset of the local file header.
+ */
+ off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+ /*
+ * Set the offset of the local file header, relative to the start of
+ * the current file.
+ */
+ void setLFHOffset(off_t offset) {
+ mCDE.mLocalHeaderRelOffset = (long) offset;
+ }
+
+ /* mark for deletion; used by ZipFile::remove() */
+ void setDeleted(void) { mDeleted = true; }
+
+private:
+ /* these are private and not defined */
+ ZipEntry(const ZipEntry& src);
+ ZipEntry& operator=(const ZipEntry& src);
+
+ /* returns "true" if the CDE and the LFH agree */
+ bool compareHeaders(void) const;
+ void copyCDEtoLFH(void);
+
+ bool mDeleted; // set if entry is pending deletion
+ bool mMarked; // app-defined marker
+
+ /*
+ * Every entry in the Zip archive starts off with one of these.
+ */
+ class LocalFileHeader {
+ public:
+ LocalFileHeader(void) :
+ mVersionToExtract(0),
+ mGPBitFlag(0),
+ mCompressionMethod(0),
+ mLastModFileTime(0),
+ mLastModFileDate(0),
+ mCRC32(0),
+ mCompressedSize(0),
+ mUncompressedSize(0),
+ mFileNameLength(0),
+ mExtraFieldLength(0),
+ mFileName(NULL),
+ mExtraField(NULL)
+ {}
+ virtual ~LocalFileHeader(void) {
+ delete[] mFileName;
+ delete[] mExtraField;
+ }
+
+ status_t read(FILE* fp);
+ status_t write(FILE* fp);
+
+ // unsigned long mSignature;
+ unsigned short mVersionToExtract;
+ unsigned short mGPBitFlag;
+ unsigned short mCompressionMethod;
+ unsigned short mLastModFileTime;
+ unsigned short mLastModFileDate;
+ unsigned long mCRC32;
+ unsigned long mCompressedSize;
+ unsigned long mUncompressedSize;
+ unsigned short mFileNameLength;
+ unsigned short mExtraFieldLength;
+ unsigned char* mFileName;
+ unsigned char* mExtraField;
+
+ enum {
+ kSignature = 0x04034b50,
+ kLFHLen = 30, // LocalFileHdr len, excl. var fields
+ };
+
+ void dump(void) const;
+ };
+
+ /*
+ * Every entry in the Zip archive has one of these in the "central
+ * directory" at the end of the file.
+ */
+ class CentralDirEntry {
+ public:
+ CentralDirEntry(void) :
+ mVersionMadeBy(0),
+ mVersionToExtract(0),
+ mGPBitFlag(0),
+ mCompressionMethod(0),
+ mLastModFileTime(0),
+ mLastModFileDate(0),
+ mCRC32(0),
+ mCompressedSize(0),
+ mUncompressedSize(0),
+ mFileNameLength(0),
+ mExtraFieldLength(0),
+ mFileCommentLength(0),
+ mDiskNumberStart(0),
+ mInternalAttrs(0),
+ mExternalAttrs(0),
+ mLocalHeaderRelOffset(0),
+ mFileName(NULL),
+ mExtraField(NULL),
+ mFileComment(NULL)
+ {}
+ virtual ~CentralDirEntry(void) {
+ delete[] mFileName;
+ delete[] mExtraField;
+ delete[] mFileComment;
+ }
+
+ status_t read(FILE* fp);
+ status_t write(FILE* fp);
+
+ // unsigned long mSignature;
+ unsigned short mVersionMadeBy;
+ unsigned short mVersionToExtract;
+ unsigned short mGPBitFlag;
+ unsigned short mCompressionMethod;
+ unsigned short mLastModFileTime;
+ unsigned short mLastModFileDate;
+ unsigned long mCRC32;
+ unsigned long mCompressedSize;
+ unsigned long mUncompressedSize;
+ unsigned short mFileNameLength;
+ unsigned short mExtraFieldLength;
+ unsigned short mFileCommentLength;
+ unsigned short mDiskNumberStart;
+ unsigned short mInternalAttrs;
+ unsigned long mExternalAttrs;
+ unsigned long mLocalHeaderRelOffset;
+ unsigned char* mFileName;
+ unsigned char* mExtraField;
+ unsigned char* mFileComment;
+
+ void dump(void) const;
+
+ enum {
+ kSignature = 0x02014b50,
+ kCDELen = 46, // CentralDirEnt len, excl. var fields
+ };
+ };
+
+ enum {
+ //kDataDescriptorSignature = 0x08074b50, // currently unused
+ kDataDescriptorLen = 16, // four 32-bit fields
+
+ kDefaultVersion = 20, // need deflate, nothing much else
+ kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3
+ kUsesDataDescr = 0x0008, // GPBitFlag bit 3
+ };
+
+ LocalFileHeader mLFH;
+ CentralDirEntry mCDE;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
new file mode 100644
index 0000000..62c9383
--- /dev/null
+++ b/tools/aapt/ZipFile.cpp
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// Access to Zip archives.
+//
+
+#define LOG_TAG "zip"
+
+#include <utils/ZipUtils.h>
+#include <utils/Log.h>
+
+#include "ZipFile.h"
+
+#include <zlib.h>
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+#include <memory.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Some environments require the "b", some choke on it.
+ */
+#define FILE_OPEN_RO "rb"
+#define FILE_OPEN_RW "r+b"
+#define FILE_OPEN_RW_CREATE "w+b"
+
+/* should live somewhere else? */
+static status_t errnoToStatus(int err)
+{
+ if (err == ENOENT)
+ return NAME_NOT_FOUND;
+ else if (err == EACCES)
+ return PERMISSION_DENIED;
+ else
+ return UNKNOWN_ERROR;
+}
+
+/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::open(const char* zipFileName, int flags)
+{
+ bool newArchive = false;
+
+ assert(mZipFp == NULL); // no reopen
+
+ if ((flags & kOpenTruncate))
+ flags |= kOpenCreate; // trunc implies create
+
+ if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+ return INVALID_OPERATION; // not both
+ if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+ return INVALID_OPERATION; // not neither
+ if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+ return INVALID_OPERATION; // create requires write
+
+ if (flags & kOpenTruncate) {
+ newArchive = true;
+ } else {
+ newArchive = (access(zipFileName, F_OK) != 0);
+ if (!(flags & kOpenCreate) && newArchive) {
+ /* not creating, must already exist */
+ LOGD("File %s does not exist", zipFileName);
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ /* open the file */
+ const char* openflags;
+ if (flags & kOpenReadWrite) {
+ if (newArchive)
+ openflags = FILE_OPEN_RW_CREATE;
+ else
+ openflags = FILE_OPEN_RW;
+ } else {
+ openflags = FILE_OPEN_RO;
+ }
+ mZipFp = fopen(zipFileName, openflags);
+ if (mZipFp == NULL) {
+ int err = errno;
+ LOGD("fopen failed: %d\n", err);
+ return errnoToStatus(err);
+ }
+
+ status_t result;
+ if (!newArchive) {
+ /*
+ * Load the central directory. If that fails, then this probably
+ * isn't a Zip archive.
+ */
+ result = readCentralDir();
+ } else {
+ /*
+ * Newly-created. The EndOfCentralDir constructor actually
+ * sets everything to be the way we want it (all zeroes). We
+ * set mNeedCDRewrite so that we create *something* if the
+ * caller doesn't add any files. (We could also just unlink
+ * the file if it's brand new and nothing was added, but that's
+ * probably doing more than we really should -- the user might
+ * have a need for empty zip files.)
+ */
+ mNeedCDRewrite = true;
+ result = NO_ERROR;
+ }
+
+ if (flags & kOpenReadOnly)
+ mReadOnly = true;
+ else
+ assert(!mReadOnly);
+
+ return result;
+}
+
+/*
+ * Return the Nth entry in the archive.
+ */
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
+{
+ if (idx < 0 || idx >= (int) mEntries.size())
+ return NULL;
+
+ return mEntries[idx];
+}
+
+/*
+ * Find an entry by name.
+ */
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+{
+ /*
+ * Do a stupid linear string-compare search.
+ *
+ * There are various ways to speed this up, especially since it's rare
+ * to intermingle changes to the archive with "get by name" calls. We
+ * don't want to sort the mEntries vector itself, however, because
+ * it's used to recreate the Central Directory.
+ *
+ * (Hash table works, parallel list of pointers in sorted order is good.)
+ */
+ int idx;
+
+ for (idx = mEntries.size()-1; idx >= 0; idx--) {
+ ZipEntry* pEntry = mEntries[idx];
+ if (!pEntry->getDeleted() &&
+ strcmp(fileName, pEntry->getFileName()) == 0)
+ {
+ return pEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Empty the mEntries vector.
+ */
+void ZipFile::discardEntries(void)
+{
+ int count = mEntries.size();
+
+ while (--count >= 0)
+ delete mEntries[count];
+
+ mEntries.clear();
+}
+
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end. In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards. The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly. If the wrong value ends up in the EOCD
+ * area, we're hosed. This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+status_t ZipFile::readCentralDir(void)
+{
+ status_t result = NO_ERROR;
+ unsigned char* buf = NULL;
+ off_t fileLength, seekStart;
+ long readAmount;
+ int i;
+
+ fseek(mZipFp, 0, SEEK_END);
+ fileLength = ftell(mZipFp);
+ rewind(mZipFp);
+
+ /* too small to be a ZIP archive? */
+ if (fileLength < EndOfCentralDir::kEOCDLen) {
+ LOGD("Length is %ld -- too small\n", (long)fileLength);
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
+ if (buf == NULL) {
+ LOGD("Failure allocating %d bytes for EOCD search",
+ EndOfCentralDir::kMaxEOCDSearch);
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
+ seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
+ readAmount = EndOfCentralDir::kMaxEOCDSearch;
+ } else {
+ seekStart = 0;
+ readAmount = (long) fileLength;
+ }
+ if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
+ LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /* read the last part of the file into the buffer */
+ if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
+ LOGD("short file? wanted %ld\n", readAmount);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /* find the end-of-central-dir magic */
+ for (i = readAmount - 4; i >= 0; i--) {
+ if (buf[i] == 0x50 &&
+ ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
+ {
+ LOGV("+++ Found EOCD at buf+%d\n", i);
+ break;
+ }
+ }
+ if (i < 0) {
+ LOGD("EOCD not found, not Zip\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ /* extract eocd values */
+ result = mEOCD.readBuf(buf + i, readAmount - i);
+ if (result != NO_ERROR) {
+ LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+ goto bail;
+ }
+ //mEOCD.dump();
+
+ if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
+ mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
+ {
+ LOGD("Archive spanning not supported\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ /*
+ * So far so good. "mCentralDirSize" is the size in bytes of the
+ * central directory, so we can just seek back that far to find it.
+ * We can also seek forward mCentralDirOffset bytes from the
+ * start of the file.
+ *
+ * We're not guaranteed to have the rest of the central dir in the
+ * buffer, nor are we guaranteed that the central dir will have any
+ * sort of convenient size. We need to skip to the start of it and
+ * read the header, then the other goodies.
+ *
+ * The only thing we really need right now is the file comment, which
+ * we're hoping to preserve.
+ */
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ LOGD("Failure seeking to central dir offset %ld\n",
+ mEOCD.mCentralDirOffset);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * Loop through and read the central dir entries.
+ */
+ LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+ int entry;
+ for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
+ ZipEntry* pEntry = new ZipEntry;
+
+ result = pEntry->initFromCDE(mZipFp);
+ if (result != NO_ERROR) {
+ LOGD("initFromCDE failed\n");
+ delete pEntry;
+ goto bail;
+ }
+
+ mEntries.add(pEntry);
+ }
+
+
+ /*
+ * If all went well, we should now be back at the EOCD.
+ */
+ {
+ unsigned char checkBuf[4];
+ if (fread(checkBuf, 1, 4, mZipFp) != 4) {
+ LOGD("EOCD check read failed\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+ if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
+ LOGD("EOCD read check failed\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ LOGV("+++ EOCD read check passed\n");
+ }
+
+bail:
+ delete[] buf;
+ return result;
+}
+
+
+/*
+ * Add a new file to the archive.
+ *
+ * This requires creating and populating a ZipEntry structure, and copying
+ * the data into the file at the appropriate position. The "appropriate
+ * position" is the current location of the central directory, which we
+ * casually overwrite (we can put it back later).
+ *
+ * If we were concerned about safety, we would want to make all changes
+ * in a temp file and then overwrite the original after everything was
+ * safely written. Not really a concern for us.
+ */
+status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
+ const char* storageName, int sourceType, int compressionMethod,
+ ZipEntry** ppEntry)
+{
+ ZipEntry* pEntry = NULL;
+ status_t result = NO_ERROR;
+ long lfhPosn, startPosn, endPosn, uncompressedLen;
+ FILE* inputFp = NULL;
+ unsigned long crc;
+ time_t modWhen;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+
+ assert(compressionMethod == ZipEntry::kCompressDeflated ||
+ compressionMethod == ZipEntry::kCompressStored);
+
+ /* make sure we're in a reasonable state */
+ assert(mZipFp != NULL);
+ assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+ /* make sure it doesn't already exist */
+ if (getEntryByName(storageName) != NULL)
+ return ALREADY_EXISTS;
+
+ if (!data) {
+ inputFp = fopen(fileName, FILE_OPEN_RO);
+ if (inputFp == NULL)
+ return errnoToStatus(errno);
+ }
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ pEntry = new ZipEntry;
+ pEntry->initNew(storageName, NULL);
+
+ /*
+ * From here on out, failures are more interesting.
+ */
+ mNeedCDRewrite = true;
+
+ /*
+ * Write the LFH, even though it's still mostly blank. We need it
+ * as a place-holder. In theory the LFH isn't necessary, but in
+ * practice some utilities demand it.
+ */
+ lfhPosn = ftell(mZipFp);
+ pEntry->mLFH.write(mZipFp);
+ startPosn = ftell(mZipFp);
+
+ /*
+ * Copy the data in, possibly compressing it as we go.
+ */
+ if (sourceType == ZipEntry::kCompressStored) {
+ if (compressionMethod == ZipEntry::kCompressDeflated) {
+ bool failed = false;
+ result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
+ if (result != NO_ERROR) {
+ LOGD("compression failed, storing\n");
+ failed = true;
+ } else {
+ /*
+ * Make sure it has compressed "enough". This probably ought
+ * to be set through an API call, but I don't expect our
+ * criteria to change over time.
+ */
+ long src = inputFp ? ftell(inputFp) : size;
+ long dst = ftell(mZipFp) - startPosn;
+ if (dst + (dst / 10) > src) {
+ LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+ src, dst);
+ failed = true;
+ }
+ }
+
+ if (failed) {
+ compressionMethod = ZipEntry::kCompressStored;
+ if (inputFp) rewind(inputFp);
+ fseek(mZipFp, startPosn, SEEK_SET);
+ /* fall through to kCompressStored case */
+ }
+ }
+ /* handle "no compression" request, or failed compression from above */
+ if (compressionMethod == ZipEntry::kCompressStored) {
+ if (inputFp) {
+ result = copyFpToFp(mZipFp, inputFp, &crc);
+ } else {
+ result = copyDataToFp(mZipFp, data, size, &crc);
+ }
+ if (result != NO_ERROR) {
+ // don't need to truncate; happens in CDE rewrite
+ LOGD("failed copying data in\n");
+ goto bail;
+ }
+ }
+
+ // currently seeked to end of file
+ uncompressedLen = inputFp ? ftell(inputFp) : size;
+ } else if (sourceType == ZipEntry::kCompressDeflated) {
+ /* we should support uncompressed-from-compressed, but it's not
+ * important right now */
+ assert(compressionMethod == ZipEntry::kCompressDeflated);
+
+ bool scanResult;
+ int method;
+ long compressedLen;
+
+ scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
+ &compressedLen, &crc);
+ if (!scanResult || method != ZipEntry::kCompressDeflated) {
+ LOGD("this isn't a deflated gzip file?");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
+ if (result != NO_ERROR) {
+ LOGD("failed copying gzip data in\n");
+ goto bail;
+ }
+ } else {
+ assert(false);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * We could write the "Data Descriptor", but there doesn't seem to
+ * be any point since we're going to go back and write the LFH.
+ *
+ * Update file offsets.
+ */
+ endPosn = ftell(mZipFp); // seeked to end of compressed data
+
+ /*
+ * Success! Fill out new values.
+ */
+ pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
+ compressionMethod);
+ modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
+ pEntry->setModWhen(modWhen);
+ pEntry->setLFHOffset(lfhPosn);
+ mEOCD.mNumEntries++;
+ mEOCD.mTotalNumEntries++;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+ mEOCD.mCentralDirOffset = endPosn;
+
+ /*
+ * Go back and write the LFH.
+ */
+ if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ pEntry->mLFH.write(mZipFp);
+
+ /*
+ * Add pEntry to the list.
+ */
+ mEntries.add(pEntry);
+ if (ppEntry != NULL)
+ *ppEntry = pEntry;
+ pEntry = NULL;
+
+bail:
+ if (inputFp != NULL)
+ fclose(inputFp);
+ delete pEntry;
+ return result;
+}
+
+/*
+ * Add an entry by copying it from another zip file. If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+ int padding, ZipEntry** ppEntry)
+{
+ ZipEntry* pEntry = NULL;
+ status_t result;
+ long lfhPosn, endPosn;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+
+ /* make sure we're in a reasonable state */
+ assert(mZipFp != NULL);
+ assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ pEntry = new ZipEntry;
+ if (pEntry == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
+ if (result != NO_ERROR)
+ goto bail;
+ if (padding != 0) {
+ result = pEntry->addPadding(padding);
+ if (result != NO_ERROR)
+ goto bail;
+ }
+
+ /*
+ * From here on out, failures are more interesting.
+ */
+ mNeedCDRewrite = true;
+
+ /*
+ * Write the LFH. Since we're not recompressing the data, we already
+ * have all of the fields filled out.
+ */
+ lfhPosn = ftell(mZipFp);
+ pEntry->mLFH.write(mZipFp);
+
+ /*
+ * Copy the data over.
+ *
+ * If the "has data descriptor" flag is set, we want to copy the DD
+ * fields as well. This is a fixed-size area immediately following
+ * the data.
+ */
+ if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
+ {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ off_t copyLen;
+ copyLen = pSourceEntry->getCompressedLen();
+ if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
+ copyLen += ZipEntry::kDataDescriptorLen;
+
+ if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
+ != NO_ERROR)
+ {
+ LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * Update file offsets.
+ */
+ endPosn = ftell(mZipFp);
+
+ /*
+ * Success! Fill out new values.
+ */
+ pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
+ mEOCD.mNumEntries++;
+ mEOCD.mTotalNumEntries++;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+ mEOCD.mCentralDirOffset = endPosn;
+
+ /*
+ * Add pEntry to the list.
+ */
+ mEntries.add(pEntry);
+ if (ppEntry != NULL)
+ *ppEntry = pEntry;
+ pEntry = NULL;
+
+ result = NO_ERROR;
+
+bail:
+ delete pEntry;
+ return result;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data.
+ */
+status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
+{
+ unsigned char tmpBuf[32768];
+ size_t count;
+
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+
+ while (1) {
+ count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
+ if (ferror(srcFp) || ferror(dstFp))
+ return errnoToStatus(errno);
+ if (count == 0)
+ break;
+
+ *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+ if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+ LOGD("fwrite %d bytes failed\n", (int) count);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "dstFp" will be seeked immediately past the data.
+ */
+status_t ZipFile::copyDataToFp(FILE* dstFp,
+ const void* data, size_t size, unsigned long* pCRC32)
+{
+ size_t count;
+
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+ if (size > 0) {
+ *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
+ if (fwrite(data, 1, size, dstFp) != size) {
+ LOGD("fwrite %d bytes failed\n", (int) size);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Copy some of the bytes in "src" to "dst".
+ *
+ * If "pCRC32" is NULL, the CRC will not be computed.
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data just written.
+ */
+status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+ unsigned long* pCRC32)
+{
+ unsigned char tmpBuf[32768];
+ size_t count;
+
+ if (pCRC32 != NULL)
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+
+ while (length) {
+ long readSize;
+
+ readSize = sizeof(tmpBuf);
+ if (readSize > length)
+ readSize = length;
+
+ count = fread(tmpBuf, 1, readSize, srcFp);
+ if ((long) count != readSize) { // error or unexpected EOF
+ LOGD("fread %d bytes failed\n", (int) readSize);
+ return UNKNOWN_ERROR;
+ }
+
+ if (pCRC32 != NULL)
+ *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+ if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+ LOGD("fwrite %d bytes failed\n", (int) count);
+ return UNKNOWN_ERROR;
+ }
+
+ length -= readSize;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Compress all of the data in "srcFp" and write it to "dstFp".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the compressed data.
+ */
+status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
+ const void* data, size_t size, unsigned long* pCRC32)
+{
+ status_t result = NO_ERROR;
+ const size_t kBufSize = 32768;
+ unsigned char* inBuf = NULL;
+ unsigned char* outBuf = NULL;
+ z_stream zstream;
+ bool atEof = false; // no feof() aviailable yet
+ unsigned long crc;
+ int zerr;
+
+ /*
+ * Create an input buffer and an output buffer.
+ */
+ inBuf = new unsigned char[kBufSize];
+ outBuf = new unsigned char[kBufSize];
+ if (inBuf == NULL || outBuf == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = outBuf;
+ zstream.avail_out = kBufSize;
+ zstream.data_type = Z_UNKNOWN;
+
+ zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (zerr != Z_OK) {
+ result = UNKNOWN_ERROR;
+ if (zerr == Z_VERSION_ERROR) {
+ LOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ crc = crc32(0L, Z_NULL, 0);
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ size_t getSize;
+ int flush;
+
+ /* only read if the input buffer is empty */
+ if (zstream.avail_in == 0 && !atEof) {
+ LOGV("+++ reading %d bytes\n", (int)kBufSize);
+ if (data) {
+ getSize = size > kBufSize ? kBufSize : size;
+ memcpy(inBuf, data, getSize);
+ data = ((const char*)data) + getSize;
+ size -= getSize;
+ } else {
+ getSize = fread(inBuf, 1, kBufSize, srcFp);
+ if (ferror(srcFp)) {
+ LOGD("deflate read failed (errno=%d)\n", errno);
+ goto z_bail;
+ }
+ }
+ if (getSize < kBufSize) {
+ LOGV("+++ got %d bytes, EOF reached\n",
+ (int)getSize);
+ atEof = true;
+ }
+
+ crc = crc32(crc, inBuf, getSize);
+
+ zstream.next_in = inBuf;
+ zstream.avail_in = getSize;
+ }
+
+ if (atEof)
+ flush = Z_FINISH; /* tell zlib that we're done */
+ else
+ flush = Z_NO_FLUSH; /* more to come! */
+
+ zerr = deflate(&zstream, flush);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+ result = UNKNOWN_ERROR;
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+ {
+ LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+ if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
+ (size_t)(zstream.next_out - outBuf))
+ {
+ LOGD("write %d failed in deflate\n",
+ (int) (zstream.next_out - outBuf));
+ goto z_bail;
+ }
+
+ zstream.next_out = outBuf;
+ zstream.avail_out = kBufSize;
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ *pCRC32 = crc;
+
+z_bail:
+ deflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] inBuf;
+ delete[] outBuf;
+
+ return result;
+}
+
+/*
+ * Mark an entry as deleted.
+ *
+ * We will eventually need to crunch the file down, but if several files
+ * are being removed (perhaps as part of an "update" process) we can make
+ * things considerably faster by deferring the removal to "flush" time.
+ */
+status_t ZipFile::remove(ZipEntry* pEntry)
+{
+ /*
+ * Should verify that pEntry is actually part of this archive, and
+ * not some stray ZipEntry from a different file.
+ */
+
+ /* mark entry as deleted, and mark archive as dirty */
+ pEntry->setDeleted();
+ mNeedCDRewrite = true;
+ return NO_ERROR;
+}
+
+/*
+ * Flush any pending writes.
+ *
+ * In particular, this will crunch out deleted entries, and write the
+ * Central Directory and EOCD if we have stomped on them.
+ */
+status_t ZipFile::flush(void)
+{
+ status_t result = NO_ERROR;
+ long eocdPosn;
+ int i, count;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+ if (!mNeedCDRewrite)
+ return NO_ERROR;
+
+ assert(mZipFp != NULL);
+
+ result = crunchArchive();
+ if (result != NO_ERROR)
+ return result;
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
+ return UNKNOWN_ERROR;
+
+ count = mEntries.size();
+ for (i = 0; i < count; i++) {
+ ZipEntry* pEntry = mEntries[i];
+ pEntry->mCDE.write(mZipFp);
+ }
+
+ eocdPosn = ftell(mZipFp);
+ mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
+
+ mEOCD.write(mZipFp);
+
+ /*
+ * If we had some stuff bloat up during compression and get replaced
+ * with plain files, or if we deleted some entries, there's a lot
+ * of wasted space at the end of the file. Remove it now.
+ */
+ if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
+ LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+ // not fatal
+ }
+
+ /* should we clear the "newly added" flag in all entries now? */
+
+ mNeedCDRewrite = false;
+ return NO_ERROR;
+}
+
+/*
+ * Crunch deleted files out of an archive by shifting the later files down.
+ *
+ * Because we're not using a temp file, we do the operation inside the
+ * current file.
+ */
+status_t ZipFile::crunchArchive(void)
+{
+ status_t result = NO_ERROR;
+ int i, count;
+ long delCount, adjust;
+
+#if 0
+ printf("CONTENTS:\n");
+ for (i = 0; i < (int) mEntries.size(); i++) {
+ printf(" %d: lfhOff=%ld del=%d\n",
+ i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
+ }
+ printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
+#endif
+
+ /*
+ * Roll through the set of files, shifting them as appropriate. We
+ * could probably get a slight performance improvement by sliding
+ * multiple files down at once (because we could use larger reads
+ * when operating on batches of small files), but it's not that useful.
+ */
+ count = mEntries.size();
+ delCount = adjust = 0;
+ for (i = 0; i < count; i++) {
+ ZipEntry* pEntry = mEntries[i];
+ long span;
+
+ if (pEntry->getLFHOffset() != 0) {
+ long nextOffset;
+
+ /* Get the length of this entry by finding the offset
+ * of the next entry. Directory entries don't have
+ * file offsets, so we need to find the next non-directory
+ * entry.
+ */
+ nextOffset = 0;
+ for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
+ nextOffset = mEntries[ii]->getLFHOffset();
+ if (nextOffset == 0)
+ nextOffset = mEOCD.mCentralDirOffset;
+ span = nextOffset - pEntry->getLFHOffset();
+
+ assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
+ } else {
+ /* This is a directory entry. It doesn't have
+ * any actual file contents, so there's no need to
+ * move anything.
+ */
+ span = 0;
+ }
+
+ //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
+ // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
+
+ if (pEntry->getDeleted()) {
+ adjust += span;
+ delCount++;
+
+ delete pEntry;
+ mEntries.removeAt(i);
+
+ /* adjust loop control */
+ count--;
+ i--;
+ } else if (span != 0 && adjust > 0) {
+ /* shuffle this entry back */
+ //printf("+++ Shuffling '%s' back %ld\n",
+ // pEntry->getFileName(), adjust);
+ result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
+ pEntry->getLFHOffset(), span);
+ if (result != NO_ERROR) {
+ /* this is why you use a temp file */
+ LOGE("error during crunch - archive is toast\n");
+ return result;
+ }
+
+ pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
+ }
+ }
+
+ /*
+ * Fix EOCD info. We have to wait until the end to do some of this
+ * because we use mCentralDirOffset to determine "span" for the
+ * last entry.
+ */
+ mEOCD.mCentralDirOffset -= adjust;
+ mEOCD.mNumEntries -= delCount;
+ mEOCD.mTotalNumEntries -= delCount;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+
+ assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
+ assert(mEOCD.mNumEntries == count);
+
+ return result;
+}
+
+/*
+ * Works like memmove(), but on pieces of a file.
+ */
+status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
+{
+ if (dst == src || n <= 0)
+ return NO_ERROR;
+
+ unsigned char readBuf[32768];
+
+ if (dst < src) {
+ /* shift stuff toward start of file; must read from start */
+ while (n != 0) {
+ size_t getSize = sizeof(readBuf);
+ if (getSize > n)
+ getSize = n;
+
+ if (fseek(fp, (long) src, SEEK_SET) != 0) {
+ LOGD("filemove src seek %ld failed\n", (long) src);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fread(readBuf, 1, getSize, fp) != getSize) {
+ LOGD("filemove read %ld off=%ld failed\n",
+ (long) getSize, (long) src);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fseek(fp, (long) dst, SEEK_SET) != 0) {
+ LOGD("filemove dst seek %ld failed\n", (long) dst);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fwrite(readBuf, 1, getSize, fp) != getSize) {
+ LOGD("filemove write %ld off=%ld failed\n",
+ (long) getSize, (long) dst);
+ return UNKNOWN_ERROR;
+ }
+
+ src += getSize;
+ dst += getSize;
+ n -= getSize;
+ }
+ } else {
+ /* shift stuff toward end of file; must read from end */
+ assert(false); // write this someday, maybe
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+
+/*
+ * Get the modification time from a file descriptor.
+ */
+time_t ZipFile::getModTime(int fd)
+{
+ struct stat sb;
+
+ if (fstat(fd, &sb) < 0) {
+ LOGD("HEY: fstat on fd %d failed\n", fd);
+ return (time_t) -1;
+ }
+
+ return sb.st_mtime;
+}
+
+
+#if 0 /* this is a bad idea */
+/*
+ * Get a copy of the Zip file descriptor.
+ *
+ * We don't allow this if the file was opened read-write because we tend
+ * to leave the file contents in an uncertain state between calls to
+ * flush(). The duplicated file descriptor should only be valid for reads.
+ */
+int ZipFile::getZipFd(void) const
+{
+ if (!mReadOnly)
+ return INVALID_OPERATION;
+ assert(mZipFp != NULL);
+
+ int fd;
+ fd = dup(fileno(mZipFp));
+ if (fd < 0) {
+ LOGD("didn't work, errno=%d\n", errno);
+ }
+
+ return fd;
+}
+#endif
+
+
+#if 0
+/*
+ * Expand data.
+ */
+bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
+{
+ return false;
+}
+#endif
+
+// free the memory when you're done
+void* ZipFile::uncompress(const ZipEntry* entry)
+{
+ size_t unlen = entry->getUncompressedLen();
+ size_t clen = entry->getCompressedLen();
+
+ void* buf = malloc(unlen);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ fseek(mZipFp, 0, SEEK_SET);
+
+ off_t offset = entry->getFileOffset();
+ if (fseek(mZipFp, offset, SEEK_SET) != 0) {
+ goto bail;
+ }
+
+ switch (entry->getCompressionMethod())
+ {
+ case ZipEntry::kCompressStored: {
+ ssize_t amt = fread(buf, 1, unlen, mZipFp);
+ if (amt != (ssize_t)unlen) {
+ goto bail;
+ }
+#if 0
+ printf("data...\n");
+ const unsigned char* p = (unsigned char*)buf;
+ const unsigned char* end = p+unlen;
+ for (int i=0; i<32 && p < end; i++) {
+ printf("0x%08x ", (int)(offset+(i*0x10)));
+ for (int j=0; j<0x10 && p < end; j++) {
+ printf(" %02x", *p);
+ p++;
+ }
+ printf("\n");
+ }
+#endif
+
+ }
+ break;
+ case ZipEntry::kCompressDeflated: {
+ if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
+ goto bail;
+ }
+ }
+ break;
+ default:
+ goto bail;
+ }
+ return buf;
+
+bail:
+ free(buf);
+ return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ * ZipFile::EndOfCentralDir
+ * ===========================================================================
+ */
+
+/*
+ * Read the end-of-central-dir fields.
+ *
+ * "buf" should be positioned at the EOCD signature, and should contain
+ * the entire EOCD area including the comment.
+ */
+status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
+{
+ /* don't allow re-use */
+ assert(mComment == NULL);
+
+ if (len < kEOCDLen) {
+ /* looks like ZIP file got truncated */
+ LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+ kEOCDLen, len);
+ return INVALID_OPERATION;
+ }
+
+ /* this should probably be an assert() */
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
+ return UNKNOWN_ERROR;
+
+ mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
+ mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
+ mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
+ mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
+ mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
+ mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
+ mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
+
+ // TODO: validate mCentralDirOffset
+
+ if (mCommentLen > 0) {
+ if (kEOCDLen + mCommentLen > len) {
+ LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+ kEOCDLen, mCommentLen, len);
+ return UNKNOWN_ERROR;
+ }
+ mComment = new unsigned char[mCommentLen];
+ memcpy(mComment, buf + kEOCDLen, mCommentLen);
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Write an end-of-central-directory section.
+ */
+status_t ZipFile::EndOfCentralDir::write(FILE* fp)
+{
+ unsigned char buf[kEOCDLen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
+ ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
+ ZipEntry::putShortLE(&buf[0x08], mNumEntries);
+ ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
+ ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
+ ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
+ ZipEntry::putShortLE(&buf[0x14], mCommentLen);
+
+ if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
+ return UNKNOWN_ERROR;
+ if (mCommentLen > 0) {
+ assert(mComment != NULL);
+ if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Dump the contents of an EndOfCentralDir object.
+ */
+void ZipFile::EndOfCentralDir::dump(void) const
+{
+ LOGD(" EndOfCentralDir contents:\n");
+ LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+ mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
+ LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+ mCentralDirSize, mCentralDirOffset, mCommentLen);
+}
+
diff --git a/tools/aapt/ZipFile.h b/tools/aapt/ZipFile.h
new file mode 100644
index 0000000..dbbd072
--- /dev/null
+++ b/tools/aapt/ZipFile.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2006 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.
+ */
+
+//
+// General-purpose Zip archive access. This class allows both reading and
+// writing to Zip archives, including deletion of existing entries.
+//
+#ifndef __LIBS_ZIPFILE_H
+#define __LIBS_ZIPFILE_H
+
+#include <utils/Vector.h>
+#include <utils/Errors.h>
+#include <stdio.h>
+
+#include "ZipEntry.h"
+
+namespace android {
+
+/*
+ * Manipulate a Zip archive.
+ *
+ * Some changes will not be visible in the until until "flush" is called.
+ *
+ * The correct way to update a file archive is to make all changes to a
+ * copy of the archive in a temporary file, and then unlink/rename over
+ * the original after everything completes. Because we're only interested
+ * in using this for packaging, we don't worry about such things. Crashing
+ * after making changes and before flush() completes could leave us with
+ * an unusable Zip archive.
+ */
+class ZipFile {
+public:
+ ZipFile(void)
+ : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
+ {}
+ ~ZipFile(void) {
+ if (!mReadOnly)
+ flush();
+ if (mZipFp != NULL)
+ fclose(mZipFp);
+ discardEntries();
+ }
+
+ /*
+ * Open a new or existing archive.
+ */
+ typedef enum {
+ kOpenReadOnly = 0x01,
+ kOpenReadWrite = 0x02,
+ kOpenCreate = 0x04, // create if it doesn't exist
+ kOpenTruncate = 0x08, // if it exists, empty it
+ };
+ status_t open(const char* zipFileName, int flags);
+
+ /*
+ * Add a file to the end of the archive. Specify whether you want the
+ * library to try to store it compressed.
+ *
+ * If "storageName" is specified, the archive will use that instead
+ * of "fileName".
+ *
+ * If there is already an entry with the same name, the call fails.
+ * Existing entries with the same name must be removed first.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const char* fileName, int compressionMethod,
+ ZipEntry** ppEntry)
+ {
+ return add(fileName, fileName, compressionMethod, ppEntry);
+ }
+ status_t add(const char* fileName, const char* storageName,
+ int compressionMethod, ZipEntry** ppEntry)
+ {
+ return addCommon(fileName, NULL, 0, storageName,
+ ZipEntry::kCompressStored,
+ compressionMethod, ppEntry);
+ }
+
+ /*
+ * Add a file that is already compressed with gzip.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t addGzip(const char* fileName, const char* storageName,
+ ZipEntry** ppEntry)
+ {
+ return addCommon(fileName, NULL, 0, storageName,
+ ZipEntry::kCompressDeflated,
+ ZipEntry::kCompressDeflated, ppEntry);
+ }
+
+ /*
+ * Add a file from an in-memory data buffer.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const void* data, size_t size, const char* storageName,
+ int compressionMethod, ZipEntry** ppEntry)
+ {
+ return addCommon(NULL, data, size, storageName,
+ ZipEntry::kCompressStored,
+ compressionMethod, ppEntry);
+ }
+
+ /*
+ * Add an entry by copying it from another zip file. If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+ int padding, ZipEntry** ppEntry);
+
+ /*
+ * Mark an entry as having been removed. It is not actually deleted
+ * from the archive or our internal data structures until flush() is
+ * called.
+ */
+ status_t remove(ZipEntry* pEntry);
+
+ /*
+ * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
+ */
+ status_t flush(void);
+
+ /*
+ * Expand the data into the buffer provided. The buffer must hold
+ * at least <uncompressed len> bytes. Variation expands directly
+ * to a file.
+ *
+ * Returns "false" if an error was encountered in the compressed data.
+ */
+ //bool uncompress(const ZipEntry* pEntry, void* buf) const;
+ //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
+ void* uncompress(const ZipEntry* pEntry);
+
+ /*
+ * Get an entry, by name. Returns NULL if not found.
+ *
+ * Does not return entries pending deletion.
+ */
+ ZipEntry* getEntryByName(const char* fileName) const;
+
+ /*
+ * Get the Nth entry in the archive.
+ *
+ * This will return an entry that is pending deletion.
+ */
+ int getNumEntries(void) const { return mEntries.size(); }
+ ZipEntry* getEntryByIndex(int idx) const;
+
+private:
+ /* these are private and not defined */
+ ZipFile(const ZipFile& src);
+ ZipFile& operator=(const ZipFile& src);
+
+ class EndOfCentralDir {
+ public:
+ EndOfCentralDir(void) :
+ mDiskNumber(0),
+ mDiskWithCentralDir(0),
+ mNumEntries(0),
+ mTotalNumEntries(0),
+ mCentralDirSize(0),
+ mCentralDirOffset(0),
+ mCommentLen(0),
+ mComment(NULL)
+ {}
+ virtual ~EndOfCentralDir(void) {
+ delete[] mComment;
+ }
+
+ status_t readBuf(const unsigned char* buf, int len);
+ status_t write(FILE* fp);
+
+ //unsigned long mSignature;
+ unsigned short mDiskNumber;
+ unsigned short mDiskWithCentralDir;
+ unsigned short mNumEntries;
+ unsigned short mTotalNumEntries;
+ unsigned long mCentralDirSize;
+ unsigned long mCentralDirOffset; // offset from first disk
+ unsigned short mCommentLen;
+ unsigned char* mComment;
+
+ enum {
+ kSignature = 0x06054b50,
+ kEOCDLen = 22, // EndOfCentralDir len, excl. comment
+
+ kMaxCommentLen = 65535, // longest possible in ushort
+ kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
+
+ };
+
+ void dump(void) const;
+ };
+
+
+ /* read all entries in the central dir */
+ status_t readCentralDir(void);
+
+ /* crunch deleted entries out */
+ status_t crunchArchive(void);
+
+ /* clean up mEntries */
+ void discardEntries(void);
+
+ /* common handler for all "add" functions */
+ status_t addCommon(const char* fileName, const void* data, size_t size,
+ const char* storageName, int sourceType, int compressionMethod,
+ ZipEntry** ppEntry);
+
+ /* copy all of "srcFp" into "dstFp" */
+ status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
+ /* copy all of "data" into "dstFp" */
+ status_t copyDataToFp(FILE* dstFp,
+ const void* data, size_t size, unsigned long* pCRC32);
+ /* copy some of "srcFp" into "dstFp" */
+ status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+ unsigned long* pCRC32);
+ /* like memmove(), but on parts of a single file */
+ status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
+ /* compress all of "srcFp" into "dstFp", using Deflate */
+ status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
+ const void* data, size_t size, unsigned long* pCRC32);
+
+ /* get modification date from a file descriptor */
+ time_t getModTime(int fd);
+
+ /*
+ * We use stdio FILE*, which gives us buffering but makes dealing
+ * with files >2GB awkward. Until we support Zip64, we're fine.
+ */
+ FILE* mZipFp; // Zip file pointer
+
+ /* one of these per file */
+ EndOfCentralDir mEOCD;
+
+ /* did we open this read-only? */
+ bool mReadOnly;
+
+ /* set this when we trash the central dir */
+ bool mNeedCDRewrite;
+
+ /*
+ * One ZipEntry per entry in the zip file. I'm using pointers instead
+ * of objects because it's easier than making operator= work for the
+ * classes and sub-classes.
+ */
+ Vector<ZipEntry*> mEntries;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPFILE_H
diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp
index 85ca5da..752ef7c 100755
--- a/tools/aidl/AST.cpp
+++ b/tools/aidl/AST.cpp
@@ -845,23 +845,6 @@ Document::Write(FILE* to)
fprintf(to, "package %s;\n", this->package.c_str());
}
- // gather the types for the import statements
- set<Type*> types;
- N = this->classes.size();
- for (i=0; i<N; i++) {
- Class* c = this->classes[i];
- c->GatherTypes(&types);
- }
-
- set<Type*>::iterator it;
- for (it=types.begin(); it!=types.end(); it++) {
- Type* t = *it;
- string pkg = t->Package();
- if (pkg.length() != 0 && pkg != this->package) {
- fprintf(to, "import %s;\n", t->ImportType().c_str());
- }
- }
-
N = this->classes.size();
for (i=0; i<N; i++) {
Class* c = this->classes[i];
diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp
index 622b691..0f18132 100644
--- a/tools/aidl/generate_java.cpp
+++ b/tools/aidl/generate_java.cpp
@@ -1,6 +1,7 @@
#include "generate_java.h"
#include "AST.h"
#include "Type.h"
+#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -133,7 +134,7 @@ StubClass::make_as_interface(Type *interfaceType)
Method* m = new Method;
m->comment = "/**\n * Cast an IBinder object into an ";
- m->comment += interfaceType->Name();
+ m->comment += interfaceType->QualifiedName();
m->comment += " interface,\n";
m->comment += " * generating a proxy if needed.\n */";
m->modifiers = PUBLIC | STATIC;
@@ -323,7 +324,7 @@ generate_method(const method_type* method, Class* interface,
transactCodeName += method->name.data;
char transactCodeValue[50];
- sprintf(transactCodeValue, "(IBinder.FIRST_CALL_TRANSACTION + %d)", index);
+ sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
Field* transactCode = new Field(STATIC | FINAL,
new Variable(INT_TYPE, transactCodeName));
@@ -517,7 +518,7 @@ generate_method(const method_type* method, Class* interface,
new LiteralExpression("Stub." + transactCodeName),
_data, _reply ? _reply : NULL_VALUE,
new LiteralExpression(
- oneway ? "IBinder.FLAG_ONEWAY" : "0"));
+ oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
tryStatement->statements->Add(call);
// throw back exceptions.
diff --git a/tools/aidl/options.h b/tools/aidl/options.h
index e9bf0f7..d88d988 100644
--- a/tools/aidl/options.h
+++ b/tools/aidl/options.h
@@ -1,6 +1,7 @@
#ifndef DEVICE_TOOLS_AIDL_H
#define DEVICE_TOOLS_AIDL_H
+#include <string.h>
#include <string>
#include <vector>
diff --git a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
index df1876d..c562650 100644
--- a/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
+++ b/tools/layoutlib/api/src/com/android/layoutlib/api/ILayoutBridge.java
@@ -24,35 +24,40 @@ import java.util.Map;
* <p/>
* <p/>{@link #getApiLevel()} gives the ability to know which methods are available.
* <p/>
+ * Changes in API level 4:
+ * <ul>
+ * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, boolean, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * </ul>
* Changes in API level 3:
* <ul>
- * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
- * <li> deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, int, float, float, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
* </ul>
* Changes in API level 2:
* <ul>
- * <li>{@link #getApiLevel()}</li>
- * <li>{@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
+ * <li>new API Level method: {@link #getApiLevel()}</li>
+ * <li>new render method: {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}</li>
* <li>deprecated {@link #computeLayout(IXmlPullParser, Object, int, int, String, Map, Map, IProjectCallback, ILayoutLog)}</li>
* </ul>
*/
public interface ILayoutBridge {
-
- final int API_CURRENT = 3;
+
+ final int API_CURRENT = 4;
/**
* Returns the API level of the layout library.
* While no methods will ever be removed, some may become deprecated, and some new ones
* will appear.
* <p/>If calling this method throws an {@link AbstractMethodError}, then the API level
- * should be considered to be 1.
+ * should be considered to be 1.
*/
int getApiLevel();
/**
* Initializes the Bridge object.
* @param fontOsLocation the location of the fonts.
- * @param enumValueMap map attrName => { map enumFlagName => Integer value }.
+ * @param enumValueMap map attrName => { map enumFlagName => Integer value }.
* @return true if success.
* @since 1
*/
@@ -65,6 +70,43 @@ public interface ILayoutBridge {
* @param projectKey An Object identifying the project. This is used for the cache mechanism.
* @param screenWidth the screen width
* @param screenHeight the screen height
+ * @param renderFullSize if true, the rendering will render the full size needed by the
+ * layout. This size is never smaller than <var>screenWidth</var> x <var>screenHeight</var>.
+ * @param density the density factor for the screen.
+ * @param xdpi the screen actual dpi in X
+ * @param ydpi the screen actual dpi in Y
+ * @param themeName The name of the theme to use.
+ * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme.
+ * @param projectResources the resources of the project. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the
+ * map contains (String, {@link IResourceValue}) pairs where the key is the resource name,
+ * and the value is the resource value.
+ * @param frameworkResources the framework resources. The map contains (String, map) pairs
+ * where the string is the type of the resource reference used in the layout file, and the map
+ * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the
+ * value is the resource value.
+ * @param projectCallback The {@link IProjectCallback} object to get information from
+ * the project.
+ * @param logger the object responsible for displaying warning/errors to the user.
+ * @return an {@link ILayoutResult} object that contains the result of the layout.
+ * @since 4
+ */
+ ILayoutResult computeLayout(IXmlPullParser layoutDescription,
+ Object projectKey,
+ int screenWidth, int screenHeight, boolean renderFullSize,
+ int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map<String, Map<String, IResourceValue>> projectResources,
+ Map<String, Map<String, IResourceValue>> frameworkResources,
+ IProjectCallback projectCallback, ILayoutLog logger);
+
+ /**
+ * Computes and renders a layout
+ * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the
+ * layout file.
+ * @param projectKey An Object identifying the project. This is used for the cache mechanism.
+ * @param screenWidth the screen width
+ * @param screenHeight the screen height
* @param density the density factor for the screen.
* @param xdpi the screen actual dpi in X
* @param ydpi the screen actual dpi in Y
@@ -84,6 +126,7 @@ public interface ILayoutBridge {
* @return an {@link ILayoutResult} object that contains the result of the layout.
* @since 3
*/
+ @Deprecated
ILayoutResult computeLayout(IXmlPullParser layoutDescription,
Object projectKey,
int screenWidth, int screenHeight, int density, float xdpi, float ydpi,
@@ -155,7 +198,7 @@ public interface ILayoutBridge {
Map<String, Map<String, IResourceValue>> projectResources,
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback projectCallback, ILayoutLog logger);
-
+
/**
* Clears the resource cache for a specific project.
* <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused
diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java
index 42e4dfa..3b66188 100644
--- a/tools/layoutlib/bridge/src/android/webkit/WebView.java
+++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java
@@ -240,13 +240,6 @@ public class WebView extends MockView {
return null;
}
- public static synchronized PluginList getPluginList() {
- return null;
- }
-
- public void refreshPlugins(boolean reloadOpenPages) {
- }
-
public View getZoomControls() {
return null;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 145a045..55fe4ac 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -312,6 +312,7 @@ public final class Bridge implements ILayoutBridge {
}
/*
+ * For compatilibty purposes, we implement the old deprecated version of computeLayout.
* (non-Javadoc)
* @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
*/
@@ -321,6 +322,23 @@ public final class Bridge implements ILayoutBridge {
Map<String, Map<String, IResourceValue>> projectResources,
Map<String, Map<String, IResourceValue>> frameworkResources,
IProjectCallback customViewLoader, ILayoutLog logger) {
+ return computeLayout(layoutDescription, projectKey,
+ screenWidth, screenHeight, false /* renderFullSize */,
+ density, xdpi, ydpi, themeName, isProjectTheme,
+ projectResources, frameworkResources, customViewLoader, logger);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, boolean, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
+ */
+ public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey,
+ int screenWidth, int screenHeight, boolean renderFullSize,
+ int density, float xdpi, float ydpi,
+ String themeName, boolean isProjectTheme,
+ Map<String, Map<String, IResourceValue>> projectResources,
+ Map<String, Map<String, IResourceValue>> frameworkResources,
+ IProjectCallback customViewLoader, ILayoutLog logger) {
if (logger == null) {
logger = sDefaultLogger;
}
@@ -393,15 +411,39 @@ public final class Bridge implements ILayoutBridge {
root.setBackgroundDrawable(d);
}
- int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
- int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
- MeasureSpec.EXACTLY);
-
// measure the views
+ int w_spec, h_spec;
+
+ if (renderFullSize) {
+ // measure the full size needed by the layout.
+ w_spec = MeasureSpec.makeMeasureSpec(screenWidth,
+ MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
+ h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
+ MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size
+ view.measure(w_spec, h_spec);
+
+ int neededWidth = root.getChildAt(0).getMeasuredWidth();
+ if (neededWidth > screenWidth) {
+ screenWidth = neededWidth;
+ }
+
+ int neededHeight = root.getChildAt(0).getMeasuredHeight();
+ if (neededHeight > screenHeight - screenOffset) {
+ screenHeight = neededHeight + screenOffset;
+ }
+ }
+
+ // remeasure with only the size we need
+ // This must always be done before the call to layout
+ w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
+ h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
+ MeasureSpec.EXACTLY);
view.measure(w_spec, h_spec);
+
+ // now do the layout.
view.layout(0, screenOffset, screenWidth, screenHeight);
- // draw them
+ // draw the views
Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger);
root.draw(canvas);
@@ -1014,6 +1056,10 @@ public final class Bridge implements ILayoutBridge {
// pass for now.
}
+ public void setWallpaperPosition(IBinder window, float x, float y) {
+ // pass for now.
+ }
+
public IBinder asBinder() {
// pass for now.
return null;
@@ -1041,12 +1087,12 @@ public final class Bridge implements ILayoutBridge {
}
@SuppressWarnings("unused")
- public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException {
+ public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
// pass for now.
}
@SuppressWarnings("unused")
- public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException {
+ public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException {
// pass for now.
}
@@ -1067,6 +1113,11 @@ public final class Bridge implements ILayoutBridge {
// pass for now.
}
+ @SuppressWarnings("unused")
+ public void dispatchWallpaperOffsets(float x, float y) {
+ // pass for now.
+ }
+
public IBinder asBinder() {
// pass for now.
return null;
diff --git a/tools/localize/Perforce.cpp b/tools/localize/Perforce.cpp
index 1c644ed..ae11231 100644
--- a/tools/localize/Perforce.cpp
+++ b/tools/localize/Perforce.cpp
@@ -6,7 +6,10 @@
#include <sstream>
#include <sys/types.h>
#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
#include <sys/wait.h>
+#include <cstdio>
using namespace std;
diff --git a/tools/localize/SourcePos.cpp b/tools/localize/SourcePos.cpp
index 2533f0a..184bfe0 100644
--- a/tools/localize/SourcePos.cpp
+++ b/tools/localize/SourcePos.cpp
@@ -3,6 +3,7 @@
#include <stdarg.h>
#include <cstdio>
#include <set>
+#include <cstdio>
using namespace std;
diff --git a/tools/localize/XMLHandler.h b/tools/localize/XMLHandler.h
index 1130710..324385f 100644
--- a/tools/localize/XMLHandler.h
+++ b/tools/localize/XMLHandler.h
@@ -3,6 +3,7 @@
#include "SourcePos.h"
+#include <algorithm>
#include <string>
#include <vector>
#include <map>
diff --git a/tools/localize/file_utils.cpp b/tools/localize/file_utils.cpp
index 293e50e..775ce2f 100644
--- a/tools/localize/file_utils.cpp
+++ b/tools/localize/file_utils.cpp
@@ -8,6 +8,9 @@
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <cstdio>
#include "log.h"
using namespace android;
diff --git a/tools/localize/file_utils.h b/tools/localize/file_utils.h
index 3b3fa21..7706587 100644
--- a/tools/localize/file_utils.h
+++ b/tools/localize/file_utils.h
@@ -4,6 +4,7 @@
#include "ValuesFile.h"
#include "Configuration.h"
#include <string>
+#include <cstdio>
using namespace std;
diff --git a/tools/localize/localize.cpp b/tools/localize/localize.cpp
index c0d84cc..68c03b6 100644
--- a/tools/localize/localize.cpp
+++ b/tools/localize/localize.cpp
@@ -15,6 +15,7 @@
#include <sstream>
#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
using namespace std;
diff --git a/tools/localize/localize_test.cpp b/tools/localize/localize_test.cpp
index 678cad8..1d0ac9a 100644
--- a/tools/localize/localize_test.cpp
+++ b/tools/localize/localize_test.cpp
@@ -1,3 +1,4 @@
+#include <cstdio>
#include "XLIFFFile.h"
#include "ValuesFile.h"
#include "localize.h"
diff --git a/tools/localize/merge_res_and_xliff_test.cpp b/tools/localize/merge_res_and_xliff_test.cpp
index e4ab562..6fe2629 100644
--- a/tools/localize/merge_res_and_xliff_test.cpp
+++ b/tools/localize/merge_res_and_xliff_test.cpp
@@ -1,3 +1,4 @@
+#include <cstdio>
#include "merge_res_and_xliff.h"
#include <stdio.h>
diff --git a/vpn/java/android/net/vpn/VpnManager.java b/vpn/java/android/net/vpn/VpnManager.java
index f71bbea..6df612e 100644
--- a/vpn/java/android/net/vpn/VpnManager.java
+++ b/vpn/java/android/net/vpn/VpnManager.java
@@ -45,18 +45,25 @@ public class VpnManager {
/** Key to the error code of a connectivity broadcast event. */
public static final String BROADCAST_ERROR_CODE = "err";
/** Error code to indicate an error from authentication. */
- public static final int VPN_ERROR_AUTH = 1;
+ public static final int VPN_ERROR_AUTH = 51;
/** Error code to indicate the connection attempt failed. */
- public static final int VPN_ERROR_CONNECTION_FAILED = 2;
+ public static final int VPN_ERROR_CONNECTION_FAILED = 101;
/** Error code to indicate the server is not known. */
- public static final int VPN_ERROR_UNKNOWN_SERVER = 3;
+ public static final int VPN_ERROR_UNKNOWN_SERVER = 102;
/** Error code to indicate an error from challenge response. */
- public static final int VPN_ERROR_CHALLENGE = 4;
+ public static final int VPN_ERROR_CHALLENGE = 5;
/** Error code to indicate an error of remote server hanging up. */
- public static final int VPN_ERROR_REMOTE_HUNG_UP = 5;
+ public static final int VPN_ERROR_REMOTE_HUNG_UP = 7;
+ /** Error code to indicate an error of remote PPP server hanging up. */
+ public static final int VPN_ERROR_REMOTE_PPP_HUNG_UP = 48;
+ /** Error code to indicate a PPP negotiation error. */
+ public static final int VPN_ERROR_PPP_NEGOTIATION_FAILED = 42;
/** Error code to indicate an error of losing connectivity. */
- public static final int VPN_ERROR_CONNECTION_LOST = 6;
- private static final int VPN_ERROR_NO_ERROR = 0;
+ public static final int VPN_ERROR_CONNECTION_LOST = 103;
+ /** Largest error code used by VPN. */
+ public static final int VPN_ERROR_LARGEST = 200;
+ /** Error code to indicate a successful connection. */
+ public static final int VPN_ERROR_NO_ERROR = 0;
public static final String PROFILES_PATH = "/data/misc/vpn/profiles";
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index fa328e8..73dbb6f 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -58,7 +58,7 @@ interface IWifiManager
int getNumAllowedChannels();
- boolean setNumAllowedChannels(int numChannels);
+ boolean setNumAllowedChannels(int numChannels, boolean persist);
int[] getValidChannelCounts();
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 74f4284..27755ed 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -565,14 +565,15 @@ public class WifiManager {
* for some reason.
* @param numChannels the number of allowed channels. Must be greater than 0
* and less than or equal to 16.
+ * @param persist {@code true} if you want this remembered
* @return {@code true} if the operation succeeds, {@code false} otherwise, e.g.,
* {@code numChannels} is out of range.
*
* @hide pending API council
*/
- public boolean setNumAllowedChannels(int numChannels) {
+ public boolean setNumAllowedChannels(int numChannels, boolean persist) {
try {
- return mService.setNumAllowedChannels(numChannels);
+ return mService.setNumAllowedChannels(numChannels, persist);
} catch (RemoteException e) {
return false;
}
@@ -959,7 +960,7 @@ public class WifiManager {
*
* If this MulticastLock is not reference-counted, the first call to
* {@code release} (after the radio was multicast locked using
- * {@linke #acquire}) will unlock the multicast, and subsequent calls
+ * {@link #acquire}) will unlock the multicast, and subsequent calls
* will be ignored.
*
* Note that if any other Wifi Multicast Locks are still outstanding
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 0799f5f..c3c519f 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -79,6 +79,8 @@ public class WifiNative {
public native static int getRssiCommand();
+ public native static int getRssiApproxCommand();
+
public native static int getLinkSpeedCommand();
public native static String getMacAddressCommand();
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 3aa31bf..fa24a98 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -39,6 +39,7 @@ import android.util.Log;
import android.util.Config;
import android.app.Notification;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothA2dp;
import android.content.ContentResolver;
@@ -49,6 +50,7 @@ import com.android.internal.app.IBatteryStats;
import java.util.List;
import java.util.ArrayList;
+import java.util.Set;
import java.net.UnknownHostException;
/**
@@ -183,6 +185,10 @@ public class WifiStateTracker extends NetworkStateTracker {
private boolean mUseStaticIp = false;
private int mReconnectCount;
+ // used to store the (non-persisted) num determined during device boot
+ // (from mcc or other phone info) before the driver is started.
+ private int mNumAllowedChannels = 0;
+
// Variables relating to the 'available networks' notification
/**
@@ -238,7 +244,6 @@ public class WifiStateTracker extends NetworkStateTracker {
private SettingsObserver mSettingsObserver;
private boolean mIsScanModeActive;
- private boolean mIsScanModeSetDueToAHiddenNetwork;
private boolean mEnableRssiPolling;
// Wi-Fi run states:
@@ -311,7 +316,6 @@ public class WifiStateTracker extends NetworkStateTracker {
mScanResults = new ArrayList<ScanResult>();
// Allocate DHCP info object once, and fill it in on each request
mDhcpInfo = new DhcpInfo();
- mIsScanModeSetDueToAHiddenNetwork = false;
mRunState = RUN_STATE_STARTING;
// Setting is in seconds
@@ -379,6 +383,14 @@ public class WifiStateTracker extends NetworkStateTracker {
}
/**
+ * Return the name of our WLAN network interface.
+ * @return the name of our interface.
+ */
+ public String getInterfaceName() {
+ return mInterfaceName;
+ }
+
+ /**
* Return the system properties name associated with the tcp buffer sizes
* for this network.
*/
@@ -576,9 +588,12 @@ public class WifiStateTracker extends NetworkStateTracker {
try {
return setNumAllowedChannels(
Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
+ Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS));
} catch (Settings.SettingNotFoundException e) {
- // if setting doesn't exist, stick with the driver default
+ if (mNumAllowedChannels != 0) {
+ WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels);
+ }
+ // otherwise, use the driver default
}
return true;
}
@@ -592,6 +607,7 @@ public class WifiStateTracker extends NetworkStateTracker {
* {@code numChannels} is outside the valid range.
*/
public synchronized boolean setNumAllowedChannels(int numChannels) {
+ mNumAllowedChannels = numChannels;
return WifiNative.setNumAllowedChannelsCommand(numChannels);
}
@@ -631,10 +647,10 @@ public class WifiStateTracker extends NetworkStateTracker {
private void checkIsBluetoothPlaying() {
boolean isBluetoothPlaying = false;
- List<String> connected = mBluetoothA2dp.listConnectedSinks();
+ Set<BluetoothDevice> connected = mBluetoothA2dp.getConnectedSinks();
- for (String address : connected) {
- if (mBluetoothA2dp.getSinkState(address) == BluetoothA2dp.STATE_PLAYING) {
+ for (BluetoothDevice device : connected) {
+ if (mBluetoothA2dp.getSinkState(device) == BluetoothA2dp.STATE_PLAYING) {
isBluetoothPlaying = true;
break;
}
@@ -790,7 +806,7 @@ public class WifiStateTracker extends NetworkStateTracker {
WifiNative.closeSupplicantConnection();
}
if (died) {
- resetInterface();
+ resetInterface(false);
}
// When supplicant dies, kill the DHCP thread
if (mDhcpTarget != null) {
@@ -1023,17 +1039,12 @@ public class WifiStateTracker extends NetworkStateTracker {
* On receiving the first scan results after connecting to
* the supplicant, switch scan mode over to passive.
*/
- if (!mIsScanModeSetDueToAHiddenNetwork) {
- // This is the only place at the moment where we set
- // the scan mode NOT due to a hidden network. This is
- // what the second parameter value (false) stands for.
- setScanMode(false, false);
- }
+ setScanMode(false);
break;
case EVENT_POLL_INTERVAL:
if (mWifiInfo.getSupplicantState() != SupplicantState.UNINITIALIZED) {
- requestPolledInfo(mWifiInfo);
+ requestPolledInfo(mWifiInfo, true);
checkPollTimer();
}
break;
@@ -1164,9 +1175,7 @@ public class WifiStateTracker extends NetworkStateTracker {
return disabledNetwork;
}
- public synchronized void setScanMode(
- boolean isScanModeActive, boolean setDueToAHiddenNetwork) {
- mIsScanModeSetDueToAHiddenNetwork = setDueToAHiddenNetwork;
+ public synchronized void setScanMode(boolean isScanModeActive) {
if (mIsScanModeActive != isScanModeActive) {
WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive);
}
@@ -1206,7 +1215,7 @@ public class WifiStateTracker extends NetworkStateTracker {
cancelDisconnect();
}
mDisconnectExpected = false;
- resetInterface();
+ resetInterface(true);
setDetailedState(newState);
sendNetworkStateChangeBroadcast(mLastBssid);
mWifiInfo.setBSSID(null);
@@ -1219,7 +1228,7 @@ public class WifiStateTracker extends NetworkStateTracker {
* Resets the Wi-Fi interface by clearing any state, resetting any sockets
* using the interface, stopping DHCP, and disabling the interface.
*/
- public void resetInterface() {
+ public void resetInterface(boolean reenable) {
mHaveIpAddress = false;
mObtainingIpAddress = false;
mWifiInfo.setIpAddress(0);
@@ -1240,6 +1249,9 @@ public class WifiStateTracker extends NetworkStateTracker {
}
NetworkUtils.disableInterface(mInterfaceName);
+ if (reenable) {
+ NetworkUtils.enableInterface(mInterfaceName);
+ }
}
/**
@@ -1278,7 +1290,7 @@ public class WifiStateTracker extends NetworkStateTracker {
*/
public WifiInfo requestConnectionInfo() {
requestConnectionStatus(mWifiInfo);
- requestPolledInfo(mWifiInfo);
+ requestPolledInfo(mWifiInfo, false);
return mWifiInfo;
}
@@ -1333,10 +1345,14 @@ public class WifiStateTracker extends NetworkStateTracker {
* Get the dynamic information that is not reported via events.
* @param info the object into which the information should be captured.
*/
- private synchronized void requestPolledInfo(WifiInfo info)
+ private synchronized void requestPolledInfo(WifiInfo info, boolean polling)
{
int newRssi = WifiNative.getRssiCommand();
- if (newRssi != -1 && -200 < newRssi && newRssi < 100) { // screen out invalid values
+ if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+ /* some implementations avoid negative values by adding 256
+ * so we need to adjust for that here.
+ */
+ if (newRssi > 0) newRssi -= 256;
info.setRssi(newRssi);
/*
* Rather then sending the raw RSSI out every time it
@@ -1453,6 +1469,7 @@ public class WifiStateTracker extends NetworkStateTracker {
public synchronized boolean restart() {
if (mRunState == RUN_STATE_STOPPED) {
mRunState = RUN_STATE_STARTING;
+ resetInterface(true);
return WifiNative.startDriverCommand();
} else if (mRunState == RUN_STATE_STOPPING) {
mRunState = RUN_STATE_STARTING;
@@ -1879,7 +1896,7 @@ public class WifiStateTracker extends NetworkStateTracker {
oDns2 != mDhcpInfo.dns2));
if (changed) {
- resetInterface();
+ resetInterface(true);
configureInterface();
if (mUseStaticIp) {
mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);